diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index e543175..e912af8 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -35,10 +35,15 @@
-
+
+
+
+
+
+
@@ -143,7 +148,7 @@
1764457173148
-
+
diff --git a/Command.cc b/Command.cc
index 55ea888..5afa93e 100644
--- a/Command.cc
+++ b/Command.cc
@@ -365,9 +365,23 @@ cmd_refresh(CommandContext &ctx)
static bool
cmd_kprefix(CommandContext &ctx)
{
- // Show k-command mode hint in status
- ctx.editor.SetStatus("C-k _");
- return true;
+ // Show k-command mode hint in status
+ ctx.editor.SetStatus("C-k _");
+ return true;
+}
+
+
+static bool
+cmd_unknown_kcommand(CommandContext &ctx)
+{
+ char ch = '?';
+ if (!ctx.arg.empty()) {
+ ch = ctx.arg[0];
+ }
+ char buf[64];
+ std::snprintf(buf, sizeof(buf), "unknown k-command %c", ch);
+ ctx.editor.SetStatus(buf);
+ return true;
}
@@ -887,57 +901,75 @@ cmd_move_end(CommandContext &ctx)
static bool
cmd_page_up(CommandContext &ctx)
{
- Buffer *buf = ctx.editor.CurrentBuffer();
- if (!buf)
- return false;
- ensure_at_least_one_line(*buf);
- auto &rows = buf->Rows();
- std::size_t y = buf->Cury();
- std::size_t x = buf->Curx();
- int repeat = ctx.count > 0 ? ctx.count : 1;
- std::size_t content_rows = ctx.editor.Rows() > 0 ? ctx.editor.Rows() - 1 : 0;
- if (content_rows == 0)
- content_rows = 1;
- while (repeat-- > 0) {
- if (y > content_rows)
- y -= content_rows;
- else
- y = 0;
- if (x > rows[y].size())
- x = rows[y].size();
- }
- buf->SetCursor(x, y);
- ensure_cursor_visible(ctx.editor, *buf);
- return true;
+ Buffer *buf = ctx.editor.CurrentBuffer();
+ if (!buf)
+ return false;
+ ensure_at_least_one_line(*buf);
+ auto &rows = buf->Rows();
+ int repeat = ctx.count > 0 ? ctx.count : 1;
+ std::size_t content_rows = ctx.editor.Rows() > 0 ? ctx.editor.Rows() - 1 : 0;
+ if (content_rows == 0)
+ content_rows = 1;
+
+ // Base on current top-of-screen (row offset)
+ std::size_t rowoffs = buf->Rowoffs();
+ while (repeat-- > 0) {
+ if (rowoffs >= content_rows)
+ rowoffs -= content_rows;
+ else
+ rowoffs = 0;
+ }
+ // Clamp to valid range
+ if (rows.size() > content_rows) {
+ std::size_t max_top = rows.size() - content_rows;
+ if (rowoffs > max_top) rowoffs = max_top;
+ } else {
+ rowoffs = 0;
+ }
+ // Move cursor to first visible line, column 0
+ std::size_t y = rowoffs;
+ if (y >= rows.size()) y = rows.empty() ? 0 : rows.size() - 1;
+ buf->SetOffsets(rowoffs, 0);
+ buf->SetCursor(0, y);
+ ensure_cursor_visible(ctx.editor, *buf);
+ return true;
}
static bool
cmd_page_down(CommandContext &ctx)
{
- Buffer *buf = ctx.editor.CurrentBuffer();
- if (!buf)
- return false;
- ensure_at_least_one_line(*buf);
- auto &rows = buf->Rows();
- std::size_t y = buf->Cury();
- std::size_t x = buf->Curx();
- int repeat = ctx.count > 0 ? ctx.count : 1;
- std::size_t content_rows = ctx.editor.Rows() > 0 ? ctx.editor.Rows() - 1 : 0;
- if (content_rows == 0)
- content_rows = 1;
- while (repeat-- > 0) {
- std::size_t max_down = rows.empty() ? 0 : (rows.size() - 1 - y);
- if (content_rows < max_down)
- y += content_rows;
- else
- y += max_down;
- if (x > rows[y].size())
- x = rows[y].size();
- }
- buf->SetCursor(x, y);
- ensure_cursor_visible(ctx.editor, *buf);
- return true;
+ Buffer *buf = ctx.editor.CurrentBuffer();
+ if (!buf)
+ return false;
+ ensure_at_least_one_line(*buf);
+ auto &rows = buf->Rows();
+ int repeat = ctx.count > 0 ? ctx.count : 1;
+ std::size_t content_rows = ctx.editor.Rows() > 0 ? ctx.editor.Rows() - 1 : 0;
+ if (content_rows == 0)
+ content_rows = 1;
+
+ std::size_t rowoffs = buf->Rowoffs();
+ // Compute maximum top offset
+ std::size_t max_top = 0;
+ if (!rows.empty()) {
+ if (rows.size() > content_rows)
+ max_top = rows.size() - content_rows;
+ else
+ max_top = 0;
+ }
+ while (repeat-- > 0) {
+ if (rowoffs + content_rows <= max_top)
+ rowoffs += content_rows;
+ else
+ rowoffs = max_top;
+ }
+ // Move cursor to first visible line, column 0
+ std::size_t y = std::min(rowoffs, rows.empty() ? 0 : rows.size() - 1);
+ buf->SetOffsets(rowoffs, 0);
+ buf->SetCursor(0, y);
+ ensure_cursor_visible(ctx.editor, *buf);
+ return true;
}
@@ -1115,11 +1147,13 @@ InstallDefaultCommands()
CommandRegistry::Register({CommandId::Save, "save", "Save current buffer", cmd_save});
CommandRegistry::Register({CommandId::SaveAs, "save-as", "Save current buffer as...", cmd_save_as});
CommandRegistry::Register({CommandId::Quit, "quit", "Quit editor (request)", cmd_quit});
- CommandRegistry::Register({CommandId::SaveAndQuit, "save-quit", "Save and quit (request)", cmd_save_and_quit});
- CommandRegistry::Register({CommandId::Refresh, "refresh", "Force redraw", cmd_refresh});
- CommandRegistry::Register(
- {CommandId::KPrefix, "k-prefix", "Entering k-command prefix (show hint)", cmd_kprefix});
- CommandRegistry::Register({CommandId::FindStart, "find-start", "Begin incremental search", cmd_find_start});
+ CommandRegistry::Register({CommandId::SaveAndQuit, "save-quit", "Save and quit (request)", cmd_save_and_quit});
+ CommandRegistry::Register({CommandId::Refresh, "refresh", "Force redraw", cmd_refresh});
+ CommandRegistry::Register(
+ {CommandId::KPrefix, "k-prefix", "Entering k-command prefix (show hint)", cmd_kprefix});
+ CommandRegistry::Register({CommandId::UnknownKCommand, "unknown-k", "Unknown k-command (status)",
+ cmd_unknown_kcommand});
+ CommandRegistry::Register({CommandId::FindStart, "find-start", "Begin incremental search", cmd_find_start});
CommandRegistry::Register({
CommandId::OpenFileStart, "open-file-start", "Begin open-file prompt", cmd_open_file_start
});
diff --git a/Command.h b/Command.h
index 65b4123..a9196fa 100644
--- a/Command.h
+++ b/Command.h
@@ -41,6 +41,8 @@ enum class CommandId {
WordNext,
// Direct cursor placement
MoveCursorTo, // arg: "y:x" (zero-based row:col)
+ // Meta
+ UnknownKCommand, // arg: single character that was not recognized after C-k
};
diff --git a/GUIFrontend.cc b/GUIFrontend.cc
index 474a78c..4688cb7 100644
--- a/GUIFrontend.cc
+++ b/GUIFrontend.cc
@@ -19,6 +19,7 @@ static const char *kGlslVersion = "#version 150"; // GL 3.2 core (macOS compatib
bool
GUIFrontend::Init(Editor &ed)
{
+ (void)ed; // editor dimensions will be initialized during the first Step() frame
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
return false;
}
@@ -57,12 +58,11 @@ GUIFrontend::Init(Editor &ed)
if (!ImGui_ImplOpenGL3_Init(kGlslVersion))
return false;
- // Initialize editor reported dimensions to pixels for now
- int w, h;
- SDL_GetWindowSize(window_, &w, &h);
- width_ = w;
- height_ = h;
- ed.SetDimensions(static_cast(height_), static_cast(width_));
+ // Cache initial window size; logical rows/cols will be computed in Step() once a valid ImGui frame exists
+ int w, h;
+ SDL_GetWindowSize(window_, &w, &h);
+ width_ = w;
+ height_ = h;
// Initialize GUI font from embedded default
LoadGuiFont_(nullptr, 16.f);
@@ -81,14 +81,12 @@ GUIFrontend::Step(Editor &ed, bool &running)
case SDL_QUIT:
running = false;
break;
- case SDL_WINDOWEVENT:
- if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
- width_ = e.window.data1;
- height_ = e.window.data2;
- ed.SetDimensions(static_cast(height_),
- static_cast(width_));
- }
- break;
+ case SDL_WINDOWEVENT:
+ if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
+ width_ = e.window.data1;
+ height_ = e.window.data2;
+ }
+ break;
default:
break;
}
@@ -96,7 +94,7 @@ GUIFrontend::Step(Editor &ed, bool &running)
input_.ProcessSDLEvent(e);
}
- // Execute pending mapped inputs (drain queue)
+ // Execute pending mapped inputs (drain queue)
for (;;) {
MappedInput mi;
if (!input_.Poll(mi))
@@ -109,10 +107,43 @@ GUIFrontend::Step(Editor &ed, bool &running)
}
}
- // Start a new ImGui frame
- ImGui_ImplOpenGL3_NewFrame();
- ImGui_ImplSDL2_NewFrame(window_);
- ImGui::NewFrame();
+ // Start a new ImGui frame
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplSDL2_NewFrame(window_);
+ ImGui::NewFrame();
+
+ // Update editor logical rows/cols using current ImGui metrics and display size
+ {
+ ImGuiIO &io = ImGui::GetIO();
+ float line_h = ImGui::GetTextLineHeightWithSpacing();
+ float ch_w = ImGui::CalcTextSize("M").x;
+ if (line_h <= 0.0f) line_h = 16.0f;
+ if (ch_w <= 0.0f) ch_w = 8.0f;
+ // Prefer ImGui IO display size; fall back to cached SDL window size
+ float disp_w = io.DisplaySize.x > 0 ? io.DisplaySize.x : static_cast(width_);
+ float disp_h = io.DisplaySize.y > 0 ? io.DisplaySize.y : static_cast(height_);
+
+ // Account for the GUI window padding and the status bar height used in GUIRenderer
+ const ImGuiStyle &style = ImGui::GetStyle();
+ float pad_x = style.WindowPadding.x;
+ float pad_y = style.WindowPadding.y;
+ // Status bar reserves one frame height (with spacing) inside the window
+ float status_h = ImGui::GetFrameHeightWithSpacing();
+
+ float avail_w = std::max(0.0f, disp_w - 2.0f * pad_x);
+ float avail_h = std::max(0.0f, disp_h - 2.0f * pad_y - status_h);
+
+ // Visible content rows inside the scroll child
+ std::size_t content_rows = static_cast(std::floor(avail_h / line_h));
+ // Editor::Rows includes the status line; add 1 back for it.
+ std::size_t rows = std::max(1, content_rows + 1);
+ std::size_t cols = static_cast(std::max(1.0f, std::floor(avail_w / ch_w)));
+
+ // Only update if changed to avoid churn
+ if (rows != ed.Rows() || cols != ed.Cols()) {
+ ed.SetDimensions(rows, cols);
+ }
+ }
// No runtime font UI; always use embedded font.
diff --git a/GUIInputHandler.cc b/GUIInputHandler.cc
index 30c1e91..85a266b 100644
--- a/GUIInputHandler.cc
+++ b/GUIInputHandler.cc
@@ -62,6 +62,18 @@ map_key(const SDL_Keycode key, const SDL_Keymod mod, bool &k_prefix, MappedInput
k_prefix = true;
out = {true, CommandId::KPrefix, "", 0};
return true;
+ case SDLK_n: // C-n: down
+ out = {true, CommandId::MoveDown, "", 0};
+ return true;
+ case SDLK_p: // C-p: up
+ out = {true, CommandId::MoveUp, "", 0};
+ return true;
+ case SDLK_f: // C-f: right
+ out = {true, CommandId::MoveRight, "", 0};
+ return true;
+ case SDLK_b: // C-b: left
+ out = {true, CommandId::MoveLeft, "", 0};
+ return true;
case SDLK_a:
out = {true, CommandId::MoveHome, "", 0};
return true;
@@ -103,25 +115,30 @@ map_key(const SDL_Keycode key, const SDL_Keymod mod, bool &k_prefix, MappedInput
}
}
- if (k_prefix) {
- k_prefix = false;
- // Normalize SDL key to ASCII where possible
- int ascii_key = 0;
- if (key >= SDLK_SPACE && key <= SDLK_z) {
- ascii_key = static_cast(key);
- }
- bool ctrl2 = (mod & KMOD_CTRL) != 0;
- if (ascii_key != 0) {
- ascii_key = KLowerAscii(ascii_key);
- CommandId id;
- if (KLookupKCommand(ascii_key, ctrl2, id)) {
- out = {true, id, "", 0};
- return true;
- }
- }
- out.hasCommand = false;
- return true;
- }
+ if (k_prefix) {
+ k_prefix = false;
+ // Normalize SDL key to ASCII where possible
+ int ascii_key = 0;
+ if (key >= SDLK_SPACE && key <= SDLK_z) {
+ ascii_key = static_cast(key);
+ }
+ bool ctrl2 = (mod & KMOD_CTRL) != 0;
+ if (ascii_key != 0) {
+ ascii_key = KLowerAscii(ascii_key);
+ CommandId id;
+ if (KLookupKCommand(ascii_key, ctrl2, id)) {
+ out = {true, id, "", 0};
+ return true;
+ }
+ // Unknown k-command: report the typed character
+ char c = (ascii_key >= 0x20 && ascii_key <= 0x7e) ? static_cast(ascii_key) : '?';
+ std::string arg(1, c);
+ out = {true, CommandId::UnknownKCommand, arg, 0};
+ return true;
+ }
+ out.hasCommand = false;
+ return true;
+ }
return false;
}
diff --git a/GUIRenderer.cc b/GUIRenderer.cc
index 42f3e19..d115b05 100644
--- a/GUIRenderer.cc
+++ b/GUIRenderer.cc
@@ -56,40 +56,77 @@ GUIRenderer::Draw(Editor &ed)
const float line_h = ImGui::GetTextLineHeight();
const float row_h = ImGui::GetTextLineHeightWithSpacing();
const float space_w = ImGui::CalcTextSize(" ").x;
- // When the user scrolls and the cursor is off-screen, move it to the nearest visible row
- {
- static float prev_scroll_y = -1.0f;
- float child_h = ImGui::GetWindowHeight(); // child window height
- long first_row = static_cast(scroll_y / row_h);
- long vis_rows = static_cast(child_h / row_h);
- if (vis_rows < 1)
- vis_rows = 1;
- long last_row = first_row + vis_rows - 1;
- if (prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
- long cyr = static_cast(cy);
- if (cyr < first_row || cyr > last_row) {
- long new_row = (cyr < first_row) ? first_row : last_row;
- if (new_row < 0)
- new_row = 0;
- if (new_row >= static_cast(lines.size())) {
- new_row = static_cast(lines.empty() ? 0 : (lines.size() - 1));
- }
- // Clamp column to line length
- std::size_t new_col = 0;
- if (!lines.empty()) {
- const std::string &l = lines[static_cast(new_row)];
- new_col = std::min(cx, l.size());
- }
- char tmp2[64];
- std::snprintf(tmp2, sizeof(tmp2), "%ld:%zu", new_row, new_col);
- Execute(ed, CommandId::MoveCursorTo, std::string(tmp2));
- // refresh local variables after move
- cy = buf->Cury();
- cx = buf->Curx();
- }
- }
- prev_scroll_y = scroll_y;
- }
+ // If the command layer requested a specific top-of-screen (via Buffer::Rowoffs),
+ // force the ImGui scroll to match so paging aligns the first visible row.
+ bool forced_scroll = false;
+ {
+ std::size_t desired_top = buf->Rowoffs();
+ long current_top = static_cast(scroll_y / row_h);
+ if (static_cast(desired_top) != current_top) {
+ ImGui::SetScrollY(static_cast(desired_top) * row_h);
+ scroll_y = ImGui::GetScrollY();
+ forced_scroll = true;
+ }
+ }
+ // Synchronize cursor and scrolling.
+ // A) When the user scrolls and the cursor goes off-screen, move the cursor to the nearest visible row.
+ // B) When the cursor moves (via keyboard commands), scroll it back into view.
+ {
+ static float prev_scroll_y = -1.0f;
+ static long prev_cursor_y = -1;
+ // Compute visible row range using the child window height
+ float child_h = ImGui::GetWindowHeight();
+ long first_row = static_cast(scroll_y / row_h);
+ long vis_rows = static_cast(child_h / row_h);
+ if (vis_rows < 1) vis_rows = 1;
+ long last_row = first_row + vis_rows - 1;
+
+ // A) If user scrolled (scroll_y changed), and cursor outside, move cursor to nearest visible row
+ if (prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
+ long cyr = static_cast(cy);
+ if (cyr < first_row || cyr > last_row) {
+ long new_row = (cyr < first_row) ? first_row : last_row;
+ if (new_row < 0) new_row = 0;
+ if (new_row >= static_cast(lines.size()))
+ new_row = static_cast(lines.empty() ? 0 : (lines.size() - 1));
+ // Clamp column to line length
+ std::size_t new_col = 0;
+ if (!lines.empty()) {
+ const std::string &l = lines[static_cast(new_row)];
+ new_col = std::min(cx, l.size());
+ }
+ char tmp2[64];
+ std::snprintf(tmp2, sizeof(tmp2), "%ld:%zu", new_row, new_col);
+ Execute(ed, CommandId::MoveCursorTo, std::string(tmp2));
+ cy = buf->Cury();
+ cx = buf->Curx();
+ cyr = static_cast(cy);
+ // Update visible range again in case content changed
+ first_row = static_cast(ImGui::GetScrollY() / row_h);
+ last_row = first_row + vis_rows - 1;
+ }
+ }
+
+ // B) If cursor moved since last frame and is outside the visible region, scroll to reveal it
+ // Skip this when we just forced a top-of-screen alignment this frame.
+ if (!forced_scroll && prev_cursor_y >= 0 && static_cast(cy) != prev_cursor_y) {
+ long cyr = static_cast(cy);
+ if (cyr < first_row || cyr > last_row) {
+ float target = (static_cast(cyr) - std::max(0L, vis_rows / 2)) * row_h;
+ float max_y = ImGui::GetScrollMaxY();
+ if (target < 0.f) target = 0.f;
+ if (max_y >= 0.f && target > max_y) target = max_y;
+ ImGui::SetScrollY(target);
+ // refresh local variables
+ scroll_y = ImGui::GetScrollY();
+ first_row = static_cast(scroll_y / row_h);
+ last_row = first_row + vis_rows - 1;
+ }
+ }
+
+ prev_scroll_y = ImGui::GetScrollY();
+ prev_cursor_y = static_cast(cy);
+ }
// Handle mouse click before rendering to avoid dependent on drawn items
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
ImVec2 mp = ImGui::GetIO().MousePos;
diff --git a/TerminalFrontend.cc b/TerminalFrontend.cc
index 5b12f2b..e0630e0 100644
--- a/TerminalFrontend.cc
+++ b/TerminalFrontend.cc
@@ -9,15 +9,21 @@
bool
TerminalFrontend::Init(Editor &ed)
{
- initscr();
- cbreak();
- noecho();
- keypad(stdscr, TRUE);
- nodelay(stdscr, TRUE);
- curs_set(1);
- // Enable mouse support if available
- mouseinterval(0);
- mousemask(ALL_MOUSE_EVENTS, nullptr);
+ initscr();
+ cbreak();
+ noecho();
+ keypad(stdscr, TRUE);
+ // Enable 8-bit meta key sequences (Alt/ESC-prefix handling in terminals)
+ meta(stdscr, TRUE);
+ // Make ESC key sequences resolve quickly so ESC+ works as meta
+#ifdef set_escdelay
+ set_escdelay(50);
+#endif
+ nodelay(stdscr, TRUE);
+ curs_set(1);
+ // Enable mouse support if available
+ mouseinterval(0);
+ mousemask(ALL_MOUSE_EVENTS, nullptr);
int r = 0, c = 0;
getmaxyx(stdscr, r, c);
diff --git a/TerminalInputHandler.cc b/TerminalInputHandler.cc
index 2ce38c3..74159cc 100644
--- a/TerminalInputHandler.cc
+++ b/TerminalInputHandler.cc
@@ -103,6 +103,23 @@ map_key_to_command(const int ch, bool &k_prefix, bool &esc_meta, MappedInput &ou
out = {true, CommandId::FindStart, "", 0};
return true;
}
+ // Emacs-style movement aliases
+ if (ch == CTRL('N')) { // C-n: down
+ out = {true, CommandId::MoveDown, "", 0};
+ return true;
+ }
+ if (ch == CTRL('P')) { // C-p: up
+ out = {true, CommandId::MoveUp, "", 0};
+ return true;
+ }
+ if (ch == CTRL('F')) { // C-f: right/forward
+ out = {true, CommandId::MoveRight, "", 0};
+ return true;
+ }
+ if (ch == CTRL('B')) { // C-b: left/back
+ out = {true, CommandId::MoveLeft, "", 0};
+ return true;
+ }
if (ch == CTRL('A')) {
out = {true, CommandId::MoveHome, "", 0};
return true;
@@ -144,26 +161,29 @@ map_key_to_command(const int ch, bool &k_prefix, bool &esc_meta, MappedInput &ou
return true;
}
- if (k_prefix) {
- k_prefix = false; // single next key only
- // Determine if this is a control chord (e.g., C-x) and normalize
- bool ctrl = false;
- int ascii_key = ch;
- if (ch >= 1 && ch <= 26) {
- ctrl = true;
- ascii_key = 'a' + (ch - 1);
- }
- // For letters, normalize to lowercase ASCII
- ascii_key = KLowerAscii(ascii_key);
+ if (k_prefix) {
+ k_prefix = false; // single next key only
+ // Determine if this is a control chord (e.g., C-x) and normalize
+ bool ctrl = false;
+ int ascii_key = ch;
+ if (ch >= 1 && ch <= 26) {
+ ctrl = true;
+ ascii_key = 'a' + (ch - 1);
+ }
+ // For letters, normalize to lowercase ASCII
+ ascii_key = KLowerAscii(ascii_key);
- CommandId id;
- if (KLookupKCommand(ascii_key, ctrl, id)) {
- out = {true, id, "", 0};
- } else {
- out.hasCommand = false; // unknown chord after C-k
- }
- return true;
- }
+ CommandId id;
+ if (KLookupKCommand(ascii_key, ctrl, id)) {
+ out = {true, id, "", 0};
+ } else {
+ // Show unknown k-command message with the typed character
+ char c = (ascii_key >= 0x20 && ascii_key <= 0x7e) ? static_cast(ascii_key) : '?';
+ std::string arg(1, c);
+ out = {true, CommandId::UnknownKCommand, arg, 0};
+ }
+ return true;
+ }
// Printable ASCII
if (ch >= 0x20 && ch <= 0x7E) {
diff --git a/TerminalRenderer.cc b/TerminalRenderer.cc
index 3ec5467..14383eb 100644
--- a/TerminalRenderer.cc
+++ b/TerminalRenderer.cc
@@ -32,7 +32,8 @@ TerminalRenderer::Draw(Editor &ed)
const Buffer *buf = ed.CurrentBuffer();
int content_rows = rows - 1; // last line is status
- if (buf) {
+ int saved_cur_y = -1, saved_cur_x = -1; // logical cursor position within content area
+ if (buf) {
const auto &lines = buf->Rows();
std::size_t rowoffs = buf->Rowoffs();
std::size_t coloffs = buf->Coloffs();
@@ -138,12 +139,15 @@ TerminalRenderer::Draw(Editor &ed)
std::size_t rx = buf->Rx(); // render x computed by command layer
int cur_y = static_cast(cy) - static_cast(buf->Rowoffs());
int cur_x = static_cast(rx) - static_cast(buf->Coloffs());
- if (cur_y >= 0 && cur_y < content_rows && cur_x >= 0 && cur_x < cols) {
- move(cur_y, cur_x);
- }
- } else {
- mvaddstr(0, 0, "[no buffer]");
- }
+ if (cur_y >= 0 && cur_y < content_rows && cur_x >= 0 && cur_x < cols) {
+ // remember where to leave the terminal cursor after status is drawn
+ saved_cur_y = cur_y;
+ saved_cur_x = cur_x;
+ move(cur_y, cur_x);
+ }
+ } else {
+ mvaddstr(0, 0, "[no buffer]");
+ }
// Status line (inverse) — left: app/version/buffer/dirty, middle: message, right: cursor/mark
move(rows - 1, 0);
@@ -237,5 +241,11 @@ TerminalRenderer::Draw(Editor &ed)
attroff(A_REVERSE);
+ // Restore terminal cursor to the content position so a visible caret
+ // remains in the editing area (not on the status line).
+ if (saved_cur_y >= 0 && saved_cur_x >= 0) {
+ move(saved_cur_y, saved_cur_x);
+ }
+
refresh();
}
diff --git a/main.cc b/main.cc
index 31b549c..855a1b7 100644
--- a/main.cc
+++ b/main.cc
@@ -33,8 +33,6 @@ main(int argc, const char *argv[])
{
Editor editor;
- std::cout << "v" << KTE_VERSION_STR << std::endl;
-
// CLI parsing using getopt_long
bool req_gui = false;
bool req_term = false;