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>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="">
|
<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.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$/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.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.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>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -156,7 +142,7 @@
|
|||||||
<option name="number" value="Default" />
|
<option name="number" value="Default" />
|
||||||
<option name="presentableId" value="Default" />
|
<option name="presentableId" value="Default" />
|
||||||
<updated>1764457173148</updated>
|
<updated>1764457173148</updated>
|
||||||
<workItem from="1764457174208" duration="22207000" />
|
<workItem from="1764457174208" duration="22512000" />
|
||||||
</task>
|
</task>
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</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 ---
|
// --- Navigation ---
|
||||||
// (helper removed)
|
// (helper removed)
|
||||||
|
|
||||||
@@ -1208,6 +1282,8 @@ InstallDefaultCommands()
|
|||||||
CommandRegistry::Register({CommandId::Newline, "newline", "Insert newline at cursor", cmd_newline});
|
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::Backspace, "backspace", "Delete char before cursor", cmd_backspace});
|
||||||
CommandRegistry::Register({CommandId::DeleteChar, "delete-char", "Delete char at cursor", cmd_delete_char});
|
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
|
// Navigation
|
||||||
CommandRegistry::Register({CommandId::MoveLeft, "left", "Move cursor left", cmd_move_left});
|
CommandRegistry::Register({CommandId::MoveLeft, "left", "Move cursor left", cmd_move_left});
|
||||||
CommandRegistry::Register({CommandId::MoveRight, "right", "Move cursor right", cmd_move_right});
|
CommandRegistry::Register({CommandId::MoveRight, "right", "Move cursor right", cmd_move_right});
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ enum class CommandId {
|
|||||||
Newline, // insert a newline at cursor
|
Newline, // insert a newline at cursor
|
||||||
Backspace, // delete char before cursor (may join lines)
|
Backspace, // delete char before cursor (may join lines)
|
||||||
DeleteChar, // delete char at 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)
|
// Navigation (basic)
|
||||||
MoveLeft,
|
MoveLeft,
|
||||||
MoveRight,
|
MoveRight,
|
||||||
|
|||||||
@@ -62,6 +62,33 @@ map_key(const SDL_Keycode key, const SDL_Keymod mod, bool &k_prefix, MappedInput
|
|||||||
break;
|
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 (is_ctrl) {
|
||||||
if (key == SDLK_k || key == SDLK_KP_EQUALS) {
|
if (key == SDLK_k || key == SDLK_KP_EQUALS) {
|
||||||
k_prefix = true;
|
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 handled earlier
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
|||||||
|
|
||||||
if (ctrl) {
|
if (ctrl) {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
|
case 'd':
|
||||||
|
out = CommandId::KillLine;
|
||||||
|
return true; // C-k C-d
|
||||||
case 'x':
|
case 'x':
|
||||||
out = CommandId::SaveAndQuit;
|
out = CommandId::SaveAndQuit;
|
||||||
return true; // C-k C-x
|
return true; // C-k C-x
|
||||||
@@ -20,6 +23,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
|
case 'd':
|
||||||
|
out = CommandId::KillToEOL;
|
||||||
|
return true; // C-k d
|
||||||
case 's':
|
case 's':
|
||||||
out = CommandId::Save;
|
out = CommandId::Save;
|
||||||
return true; // C-k s
|
return true; // C-k s
|
||||||
@@ -45,6 +51,9 @@ KLookupCtrlCommand(const int ascii_key, CommandId &out) -> bool
|
|||||||
{
|
{
|
||||||
const int k = KLowerAscii(ascii_key);
|
const int k = KLowerAscii(ascii_key);
|
||||||
switch (k) {
|
switch (k) {
|
||||||
|
case 'd':
|
||||||
|
out = CommandId::DeleteChar; // C-d
|
||||||
|
return true;
|
||||||
case 'n':
|
case 'n':
|
||||||
out = CommandId::MoveDown;
|
out = CommandId::MoveDown;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -105,6 +105,28 @@ map_key_to_command(const int ch, bool &k_prefix, bool &esc_meta, MappedInput &ou
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Generic Control-chord lookup (after handling special prefixes/cancel)
|
// 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) {
|
if (ch >= 1 && ch <= 26) {
|
||||||
int ascii_key = 'a' + (ch - 1);
|
int ascii_key = 'a' + (ch - 1);
|
||||||
CommandId id;
|
CommandId id;
|
||||||
@@ -143,29 +165,7 @@ map_key_to_command(const int ch, bool &k_prefix, bool &esc_meta, MappedInput &ou
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k_prefix) {
|
// k_prefix handled earlier
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printable ASCII
|
// Printable ASCII
|
||||||
if (ch >= 0x20 && ch <= 0x7E) {
|
if (ch >= 0x20 && ch <= 0x7E) {
|
||||||
|
|||||||
Reference in New Issue
Block a user