Support delete to end of line and delete line.
This commit is contained in:
16
.idea/workspace.xml
generated
16
.idea/workspace.xml
generated
@@ -34,24 +34,10 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.junie/guidelines.md" beforeDir="false" afterPath="$PROJECT_DIR$/.junie/guidelines.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Command.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Command.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Command.h" beforeDir="false" afterPath="$PROJECT_DIR$/Command.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Editor.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Editor.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Editor.h" beforeDir="false" afterPath="$PROJECT_DIR$/Editor.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/GUIFrontend.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIFrontend.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/GUIFrontend.h" beforeDir="false" afterPath="$PROJECT_DIR$/GUIFrontend.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/GUIInputHandler.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIInputHandler.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/GUIInputHandler.h" beforeDir="false" afterPath="$PROJECT_DIR$/GUIInputHandler.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/GUIRenderer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIRenderer.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/KKeymap.cc" beforeDir="false" afterPath="$PROJECT_DIR$/KKeymap.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/KKeymap.h" beforeDir="false" afterPath="$PROJECT_DIR$/KKeymap.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/TerminalFrontend.cc" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalFrontend.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/TerminalInputHandler.cc" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalInputHandler.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/TerminalInputHandler.h" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalInputHandler.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/TerminalRenderer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalRenderer.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/TerminalRenderer.h" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalRenderer.h" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -156,7 +142,7 @@
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1764457173148</updated>
|
||||
<workItem from="1764457174208" duration="22207000" />
|
||||
<workItem from="1764457174208" duration="22512000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
76
Command.cc
76
Command.cc
@@ -720,6 +720,80 @@ cmd_delete_char(CommandContext &ctx)
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
cmd_kill_to_eol(CommandContext &ctx)
|
||||
{
|
||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||
if (!buf) {
|
||||
ctx.editor.SetStatus("No buffer to edit");
|
||||
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;
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
if (y >= rows.size())
|
||||
break;
|
||||
if (x < rows[y].size()) {
|
||||
// delete from cursor to end of line
|
||||
rows[y].erase(x);
|
||||
} else if (y + 1 < rows.size()) {
|
||||
// at EOL: delete the newline (join with next line)
|
||||
rows[y] += rows[y + 1];
|
||||
rows.erase(rows.begin() + static_cast<std::ptrdiff_t>(y + 1));
|
||||
} else {
|
||||
// nothing to delete
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf->SetDirty(true);
|
||||
ensure_cursor_visible(ctx.editor, *buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
cmd_kill_line(CommandContext &ctx)
|
||||
{
|
||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||
if (!buf) {
|
||||
ctx.editor.SetStatus("No buffer to edit");
|
||||
return false;
|
||||
}
|
||||
ensure_at_least_one_line(*buf);
|
||||
auto &rows = buf->Rows();
|
||||
std::size_t y = buf->Cury();
|
||||
std::size_t x = buf->Curx();
|
||||
(void)x; // cursor x will be reset to 0
|
||||
int repeat = ctx.count > 0 ? ctx.count : 1;
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
if (rows.empty())
|
||||
break;
|
||||
if (rows.size() == 1) {
|
||||
// last remaining line: clear its contents
|
||||
rows[0].clear();
|
||||
y = 0;
|
||||
} else if (y < rows.size()) {
|
||||
// erase current line; keep y pointing at the next line
|
||||
rows.erase(rows.begin() + static_cast<std::ptrdiff_t>(y));
|
||||
if (y >= rows.size()) {
|
||||
// deleted last line; move to previous
|
||||
y = rows.size() - 1;
|
||||
}
|
||||
} else {
|
||||
// out of range
|
||||
y = rows.empty() ? 0 : rows.size() - 1;
|
||||
}
|
||||
}
|
||||
buf->SetCursor(0, y);
|
||||
buf->SetDirty(true);
|
||||
ensure_cursor_visible(ctx.editor, *buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// --- Navigation ---
|
||||
// (helper removed)
|
||||
|
||||
@@ -1208,6 +1282,8 @@ InstallDefaultCommands()
|
||||
CommandRegistry::Register({CommandId::Newline, "newline", "Insert newline at cursor", cmd_newline});
|
||||
CommandRegistry::Register({CommandId::Backspace, "backspace", "Delete char before cursor", cmd_backspace});
|
||||
CommandRegistry::Register({CommandId::DeleteChar, "delete-char", "Delete char at cursor", cmd_delete_char});
|
||||
CommandRegistry::Register({CommandId::KillToEOL, "kill-to-eol", "Delete to end of line", cmd_kill_to_eol});
|
||||
CommandRegistry::Register({CommandId::KillLine, "kill-line", "Delete entire line", cmd_kill_line});
|
||||
// Navigation
|
||||
CommandRegistry::Register({CommandId::MoveLeft, "left", "Move cursor left", cmd_move_left});
|
||||
CommandRegistry::Register({CommandId::MoveRight, "right", "Move cursor right", cmd_move_right});
|
||||
|
||||
@@ -29,6 +29,8 @@ enum class CommandId {
|
||||
Newline, // insert a newline at cursor
|
||||
Backspace, // delete char before cursor (may join lines)
|
||||
DeleteChar, // delete char at cursor (may join lines)
|
||||
KillToEOL, // delete from cursor to end of line; if at EOL, delete newline
|
||||
KillLine, // delete the entire current line (including newline)
|
||||
// Navigation (basic)
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
|
||||
@@ -62,6 +62,33 @@ map_key(const SDL_Keycode key, const SDL_Keymod mod, bool &k_prefix, MappedInput
|
||||
break;
|
||||
}
|
||||
|
||||
// If we are in k-prefix, the very next key must be interpreted via the
|
||||
// C-k keymap first, even if Control is held (e.g., C-k C-d).
|
||||
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<int>(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<char>(ascii_key) : '?';
|
||||
std::string arg(1, c);
|
||||
out = {true, CommandId::UnknownKCommand, arg, 0};
|
||||
return true;
|
||||
}
|
||||
out.hasCommand = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_ctrl) {
|
||||
if (key == SDLK_k || key == SDLK_KP_EQUALS) {
|
||||
k_prefix = true;
|
||||
@@ -91,30 +118,7 @@ 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<int>(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<char>(ascii_key) : '?';
|
||||
std::string arg(1, c);
|
||||
out = {true, CommandId::UnknownKCommand, arg, 0};
|
||||
return true;
|
||||
}
|
||||
out.hasCommand = false;
|
||||
return true;
|
||||
}
|
||||
// k_prefix handled earlier
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
||||
|
||||
if (ctrl) {
|
||||
switch (k) {
|
||||
case 'd':
|
||||
out = CommandId::KillLine;
|
||||
return true; // C-k C-d
|
||||
case 'x':
|
||||
out = CommandId::SaveAndQuit;
|
||||
return true; // C-k C-x
|
||||
@@ -20,6 +23,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
||||
}
|
||||
} else {
|
||||
switch (k) {
|
||||
case 'd':
|
||||
out = CommandId::KillToEOL;
|
||||
return true; // C-k d
|
||||
case 's':
|
||||
out = CommandId::Save;
|
||||
return true; // C-k s
|
||||
@@ -45,6 +51,9 @@ KLookupCtrlCommand(const int ascii_key, CommandId &out) -> bool
|
||||
{
|
||||
const int k = KLowerAscii(ascii_key);
|
||||
switch (k) {
|
||||
case 'd':
|
||||
out = CommandId::DeleteChar; // C-d
|
||||
return true;
|
||||
case 'n':
|
||||
out = CommandId::MoveDown;
|
||||
return true;
|
||||
|
||||
@@ -105,6 +105,28 @@ map_key_to_command(const int ch, bool &k_prefix, bool &esc_meta, MappedInput &ou
|
||||
return true;
|
||||
}
|
||||
// Generic Control-chord lookup (after handling special prefixes/cancel)
|
||||
// IMPORTANT: if we're in k-prefix, the very next key must be interpreted
|
||||
// via the C-k keymap first, even if it's a Control chord like C-d.
|
||||
if (k_prefix) {
|
||||
k_prefix = false; // consume the prefix for this one key
|
||||
bool ctrl = false;
|
||||
int ascii_key = ch;
|
||||
if (ch >= 1 && ch <= 26) {
|
||||
ctrl = true;
|
||||
ascii_key = 'a' + (ch - 1);
|
||||
}
|
||||
ascii_key = KLowerAscii(ascii_key);
|
||||
CommandId id;
|
||||
if (KLookupKCommand(ascii_key, ctrl, id)) {
|
||||
out = {true, id, "", 0};
|
||||
} else {
|
||||
char c = (ascii_key >= 0x20 && ascii_key <= 0x7e) ? static_cast<char>(ascii_key) : '?';
|
||||
std::string arg(1, c);
|
||||
out = {true, CommandId::UnknownKCommand, arg, 0};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ch >= 1 && ch <= 26) {
|
||||
int ascii_key = 'a' + (ch - 1);
|
||||
CommandId id;
|
||||
@@ -143,29 +165,7 @@ 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);
|
||||
|
||||
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<char>(ascii_key) : '?';
|
||||
std::string arg(1, c);
|
||||
out = {true, CommandId::UnknownKCommand, arg, 0};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// k_prefix handled earlier
|
||||
|
||||
// Printable ASCII
|
||||
if (ch >= 0x20 && ch <= 0x7E) {
|
||||
|
||||
Reference in New Issue
Block a user