Basic new file work, some graphics glitches fixed.
This commit is contained in:
22
.idea/workspace.xml
generated
22
.idea/workspace.xml
generated
@@ -23,6 +23,7 @@
|
|||||||
<generated>
|
<generated>
|
||||||
<config projectName="kte" targetName="kte" />
|
<config projectName="kte" targetName="kte" />
|
||||||
<config projectName="kte" targetName="imgui" />
|
<config projectName="kte" targetName="imgui" />
|
||||||
|
<config projectName="kte" targetName="kge" />
|
||||||
</generated>
|
</generated>
|
||||||
</component>
|
</component>
|
||||||
<component name="CMakeSettings">
|
<component name="CMakeSettings">
|
||||||
@@ -32,11 +33,17 @@
|
|||||||
</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 afterPath="$PROJECT_DIR$/KKeymap.cpp" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/KKeymap.h" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/Buffer.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cpp" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/Command.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/Command.cpp" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/Command.h" beforeDir="false" afterPath="$PROJECT_DIR$/Command.h" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/GUIInputHandler.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/GUIInputHandler.cpp" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/GUIRenderer.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/GUIRenderer.cpp" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/GUIRenderer.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/GUIRenderer.cpp" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/TerminalInputHandler.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalInputHandler.cpp" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/main.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/main.cpp" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/TerminalRenderer.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalRenderer.cpp" 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" />
|
||||||
@@ -50,6 +57,9 @@
|
|||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="HighlightingSettingsPerFile">
|
||||||
|
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
||||||
|
</component>
|
||||||
<component name="ProjectApplicationVersion">
|
<component name="ProjectApplicationVersion">
|
||||||
<option name="ide" value="CLion" />
|
<option name="ide" value="CLion" />
|
||||||
<option name="majorVersion" value="2025" />
|
<option name="majorVersion" value="2025" />
|
||||||
@@ -106,6 +116,11 @@
|
|||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
<configuration name="kge" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="kge" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="kte" RUN_TARGET_NAME="kge">
|
||||||
|
<method v="2">
|
||||||
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
<configuration name="kte" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="kte" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="kte" RUN_TARGET_NAME="kte">
|
<configuration name="kte" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="kte" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="kte" RUN_TARGET_NAME="kte">
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
@@ -113,6 +128,7 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
<list>
|
<list>
|
||||||
<item itemvalue="CMake Application.imgui" />
|
<item itemvalue="CMake Application.imgui" />
|
||||||
|
<item itemvalue="CMake Application.kge" />
|
||||||
<item itemvalue="CMake Application.kte" />
|
<item itemvalue="CMake Application.kte" />
|
||||||
</list>
|
</list>
|
||||||
</component>
|
</component>
|
||||||
@@ -123,7 +139,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="9674000" />
|
<workItem from="1764457174208" duration="10626000" />
|
||||||
</task>
|
</task>
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ set(COMMON_SOURCES
|
|||||||
Buffer.cpp
|
Buffer.cpp
|
||||||
Editor.cpp
|
Editor.cpp
|
||||||
Command.cpp
|
Command.cpp
|
||||||
|
KKeymap.cpp
|
||||||
TerminalInputHandler.cpp
|
TerminalInputHandler.cpp
|
||||||
TerminalRenderer.cpp
|
TerminalRenderer.cpp
|
||||||
TerminalFrontend.cpp
|
TerminalFrontend.cpp
|
||||||
@@ -62,6 +63,7 @@ set(COMMON_HEADERS
|
|||||||
Editor.h
|
Editor.h
|
||||||
AppendBuffer.h
|
AppendBuffer.h
|
||||||
Command.h
|
Command.h
|
||||||
|
KKeymap.h
|
||||||
InputHandler.h
|
InputHandler.h
|
||||||
TerminalInputHandler.h
|
TerminalInputHandler.h
|
||||||
Renderer.h
|
Renderer.h
|
||||||
|
|||||||
169
Command.cpp
169
Command.cpp
@@ -1,18 +1,20 @@
|
|||||||
#include "Command.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
#include "Editor.h"
|
#include "Editor.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
|
|
||||||
|
|
||||||
// Keep buffer viewport offsets so that the cursor stays within the visible
|
// Keep buffer viewport offsets so that the cursor stays within the visible
|
||||||
// window based on the editor's current dimensions. The bottom row is reserved
|
// window based on the editor's current dimensions. The bottom row is reserved
|
||||||
// for the status line.
|
// for the status line.
|
||||||
static void ensure_cursor_visible(Editor &ed, Buffer &buf)
|
static void
|
||||||
|
ensure_cursor_visible(const Editor &ed, Buffer &buf)
|
||||||
{
|
{
|
||||||
std::size_t rows = ed.Rows();
|
const std::size_t rows = ed.Rows();
|
||||||
std::size_t cols = ed.Cols();
|
const std::size_t cols = ed.Cols();
|
||||||
if (rows == 0 || cols == 0) return;
|
if (rows == 0 || cols == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
std::size_t content_rows = rows > 0 ? rows - 1 : 0; // last row = status
|
std::size_t content_rows = rows > 0 ? rows - 1 : 0; // last row = status
|
||||||
std::size_t cury = buf.Cury();
|
std::size_t cury = buf.Cury();
|
||||||
@@ -31,7 +33,8 @@ static void ensure_cursor_visible(Editor &ed, Buffer &buf)
|
|||||||
const auto total_rows = buf.Rows().size();
|
const auto total_rows = buf.Rows().size();
|
||||||
if (content_rows < total_rows) {
|
if (content_rows < total_rows) {
|
||||||
std::size_t max_rowoffs = total_rows - content_rows;
|
std::size_t max_rowoffs = total_rows - content_rows;
|
||||||
if (rowoffs > max_rowoffs) rowoffs = max_rowoffs;
|
if (rowoffs > max_rowoffs)
|
||||||
|
rowoffs = max_rowoffs;
|
||||||
} else {
|
} else {
|
||||||
rowoffs = 0;
|
rowoffs = 0;
|
||||||
}
|
}
|
||||||
@@ -46,7 +49,9 @@ static void ensure_cursor_visible(Editor &ed, Buffer &buf)
|
|||||||
buf.SetOffsets(rowoffs, coloffs);
|
buf.SetOffsets(rowoffs, coloffs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ensure_at_least_one_line(Buffer &buf)
|
|
||||||
|
static void
|
||||||
|
ensure_at_least_one_line(Buffer &buf)
|
||||||
{
|
{
|
||||||
if (buf.Rows().empty()) {
|
if (buf.Rows().empty()) {
|
||||||
buf.Rows().push_back("");
|
buf.Rows().push_back("");
|
||||||
@@ -54,10 +59,12 @@ static void ensure_at_least_one_line(Buffer &buf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// (helper removed)
|
// (helper removed)
|
||||||
|
|
||||||
// --- File/Session commands ---
|
// --- File/Session commands ---
|
||||||
static bool cmd_save(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_save(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
@@ -65,7 +72,18 @@ static bool cmd_save(CommandContext &ctx)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string err;
|
std::string err;
|
||||||
|
// Allow saving directly to a filename if buffer was opened with a
|
||||||
|
// non-existent path (not yet file-backed but has a filename).
|
||||||
if (!buf->IsFileBacked()) {
|
if (!buf->IsFileBacked()) {
|
||||||
|
if (!buf->Filename().empty()) {
|
||||||
|
if (!buf->SaveAs(buf->Filename(), err)) {
|
||||||
|
ctx.editor.SetStatus(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf->SetDirty(false);
|
||||||
|
ctx.editor.SetStatus("Saved " + buf->Filename());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
ctx.editor.SetStatus("Buffer is not file-backed; use save-as");
|
ctx.editor.SetStatus("Buffer is not file-backed; use save-as");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -79,7 +97,8 @@ static bool cmd_save(CommandContext &ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool cmd_save_as(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_save_as(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
@@ -100,7 +119,8 @@ static bool cmd_save_as(CommandContext &ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool cmd_quit(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_quit(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
// Placeholder: actual app loop should react to this status or a future flag
|
// Placeholder: actual app loop should react to this status or a future flag
|
||||||
ctx.editor.SetStatus("Quit requested");
|
ctx.editor.SetStatus("Quit requested");
|
||||||
@@ -108,7 +128,8 @@ static bool cmd_quit(CommandContext &ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool cmd_save_and_quit(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_save_and_quit(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
// Try save current buffer (if any), then mark quit requested.
|
// Try save current buffer (if any), then mark quit requested.
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
@@ -121,6 +142,13 @@ static bool cmd_save_and_quit(CommandContext &ctx)
|
|||||||
ctx.editor.SetStatus(err);
|
ctx.editor.SetStatus(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (!buf->Filename().empty()) {
|
||||||
|
if (buf->SaveAs(buf->Filename(), err)) {
|
||||||
|
buf->SetDirty(false);
|
||||||
|
} else {
|
||||||
|
ctx.editor.SetStatus(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.editor.SetStatus("Buffer not file-backed; use save-as before quitting");
|
ctx.editor.SetStatus("Buffer not file-backed; use save-as before quitting");
|
||||||
return false;
|
return false;
|
||||||
@@ -130,14 +158,26 @@ static bool cmd_save_and_quit(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_refresh(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_refresh(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
// Placeholder: renderer will handle this in Milestone 3
|
// Placeholder: renderer will handle this in Milestone 3
|
||||||
ctx.editor.SetStatus("Refresh requested");
|
ctx.editor.SetStatus("Refresh requested");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_find_start(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_kprefix(CommandContext &ctx)
|
||||||
|
{
|
||||||
|
// Show k-command mode hint in status
|
||||||
|
ctx.editor.SetStatus("C-k _");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
cmd_find_start(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
// Placeholder for incremental search start
|
// Placeholder for incremental search start
|
||||||
ctx.editor.SetStatus("Find (incremental) start");
|
ctx.editor.SetStatus("Find (incremental) start");
|
||||||
@@ -146,7 +186,8 @@ static bool cmd_find_start(CommandContext &ctx)
|
|||||||
|
|
||||||
|
|
||||||
// --- Editing ---
|
// --- Editing ---
|
||||||
static bool cmd_insert_text(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_insert_text(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
@@ -176,7 +217,9 @@ static bool cmd_insert_text(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_newline(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_newline(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
@@ -189,7 +232,8 @@ static bool cmd_newline(CommandContext &ctx)
|
|||||||
std::size_t x = buf->Curx();
|
std::size_t x = buf->Curx();
|
||||||
int repeat = ctx.count > 0 ? ctx.count : 1;
|
int repeat = ctx.count > 0 ? ctx.count : 1;
|
||||||
for (int i = 0; i < repeat; ++i) {
|
for (int i = 0; i < repeat; ++i) {
|
||||||
if (y >= rows.size()) rows.resize(y + 1);
|
if (y >= rows.size())
|
||||||
|
rows.resize(y + 1);
|
||||||
std::string &line = rows[y];
|
std::string &line = rows[y];
|
||||||
std::string tail;
|
std::string tail;
|
||||||
if (x < line.size()) {
|
if (x < line.size()) {
|
||||||
@@ -206,7 +250,9 @@ static bool cmd_newline(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_backspace(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_backspace(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
@@ -240,7 +286,9 @@ static bool cmd_backspace(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_delete_char(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_delete_char(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
@@ -253,7 +301,8 @@ static bool cmd_delete_char(CommandContext &ctx)
|
|||||||
std::size_t x = buf->Curx();
|
std::size_t x = buf->Curx();
|
||||||
int repeat = ctx.count > 0 ? ctx.count : 1;
|
int repeat = ctx.count > 0 ? ctx.count : 1;
|
||||||
for (int i = 0; i < repeat; ++i) {
|
for (int i = 0; i < repeat; ++i) {
|
||||||
if (y >= rows.size()) break;
|
if (y >= rows.size())
|
||||||
|
break;
|
||||||
if (x < rows[y].size()) {
|
if (x < rows[y].size()) {
|
||||||
rows[y].erase(x, 1);
|
rows[y].erase(x, 1);
|
||||||
} else if (y + 1 < rows.size()) {
|
} else if (y + 1 < rows.size()) {
|
||||||
@@ -269,13 +318,16 @@ static bool cmd_delete_char(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Navigation ---
|
// --- Navigation ---
|
||||||
// (helper removed)
|
// (helper removed)
|
||||||
|
|
||||||
static bool cmd_move_left(CommandContext &ctx)
|
static bool
|
||||||
|
cmd_move_left(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) return false;
|
if (!buf)
|
||||||
|
return false;
|
||||||
ensure_at_least_one_line(*buf);
|
ensure_at_least_one_line(*buf);
|
||||||
auto &rows = buf->Rows();
|
auto &rows = buf->Rows();
|
||||||
std::size_t y = buf->Cury();
|
std::size_t y = buf->Cury();
|
||||||
@@ -294,10 +346,13 @@ static bool cmd_move_left(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_move_right(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_move_right(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) return false;
|
if (!buf)
|
||||||
|
return false;
|
||||||
ensure_at_least_one_line(*buf);
|
ensure_at_least_one_line(*buf);
|
||||||
auto &rows = buf->Rows();
|
auto &rows = buf->Rows();
|
||||||
std::size_t y = buf->Cury();
|
std::size_t y = buf->Cury();
|
||||||
@@ -316,45 +371,58 @@ static bool cmd_move_right(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_move_up(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_move_up(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) return false;
|
if (!buf)
|
||||||
|
return false;
|
||||||
ensure_at_least_one_line(*buf);
|
ensure_at_least_one_line(*buf);
|
||||||
auto &rows = buf->Rows();
|
auto &rows = buf->Rows();
|
||||||
std::size_t y = buf->Cury();
|
std::size_t y = buf->Cury();
|
||||||
std::size_t x = buf->Curx();
|
std::size_t x = buf->Curx();
|
||||||
int repeat = ctx.count > 0 ? ctx.count : 1;
|
int repeat = ctx.count > 0 ? ctx.count : 1;
|
||||||
if (repeat > static_cast<int>(y)) repeat = static_cast<int>(y);
|
if (repeat > static_cast<int>(y))
|
||||||
|
repeat = static_cast<int>(y);
|
||||||
y -= static_cast<std::size_t>(repeat);
|
y -= static_cast<std::size_t>(repeat);
|
||||||
if (x > rows[y].size()) x = rows[y].size();
|
if (x > rows[y].size())
|
||||||
|
x = rows[y].size();
|
||||||
buf->SetCursor(x, y);
|
buf->SetCursor(x, y);
|
||||||
ensure_cursor_visible(ctx.editor, *buf);
|
ensure_cursor_visible(ctx.editor, *buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_move_down(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_move_down(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) return false;
|
if (!buf)
|
||||||
|
return false;
|
||||||
ensure_at_least_one_line(*buf);
|
ensure_at_least_one_line(*buf);
|
||||||
auto &rows = buf->Rows();
|
auto &rows = buf->Rows();
|
||||||
std::size_t y = buf->Cury();
|
std::size_t y = buf->Cury();
|
||||||
std::size_t x = buf->Curx();
|
std::size_t x = buf->Curx();
|
||||||
int repeat = ctx.count > 0 ? ctx.count : 1;
|
int repeat = ctx.count > 0 ? ctx.count : 1;
|
||||||
std::size_t max_down = rows.size() - 1 - y;
|
std::size_t max_down = rows.size() - 1 - y;
|
||||||
if (repeat > static_cast<int>(max_down)) repeat = static_cast<int>(max_down);
|
if (repeat > static_cast<int>(max_down))
|
||||||
|
repeat = static_cast<int>(max_down);
|
||||||
y += static_cast<std::size_t>(repeat);
|
y += static_cast<std::size_t>(repeat);
|
||||||
if (x > rows[y].size()) x = rows[y].size();
|
if (x > rows[y].size())
|
||||||
|
x = rows[y].size();
|
||||||
buf->SetCursor(x, y);
|
buf->SetCursor(x, y);
|
||||||
ensure_cursor_visible(ctx.editor, *buf);
|
ensure_cursor_visible(ctx.editor, *buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_move_home(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_move_home(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) return false;
|
if (!buf)
|
||||||
|
return false;
|
||||||
ensure_at_least_one_line(*buf);
|
ensure_at_least_one_line(*buf);
|
||||||
std::size_t y = buf->Cury();
|
std::size_t y = buf->Cury();
|
||||||
buf->SetCursor(0, y);
|
buf->SetCursor(0, y);
|
||||||
@@ -362,10 +430,13 @@ static bool cmd_move_home(CommandContext &ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_move_end(CommandContext &ctx)
|
|
||||||
|
static bool
|
||||||
|
cmd_move_end(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
Buffer *buf = ctx.editor.CurrentBuffer();
|
Buffer *buf = ctx.editor.CurrentBuffer();
|
||||||
if (!buf) return false;
|
if (!buf)
|
||||||
|
return false;
|
||||||
ensure_at_least_one_line(*buf);
|
ensure_at_least_one_line(*buf);
|
||||||
auto &rows = buf->Rows();
|
auto &rows = buf->Rows();
|
||||||
std::size_t y = buf->Cury();
|
std::size_t y = buf->Cury();
|
||||||
@@ -404,7 +475,9 @@ const Command *
|
|||||||
CommandRegistry::FindById(CommandId id)
|
CommandRegistry::FindById(CommandId id)
|
||||||
{
|
{
|
||||||
auto &v = storage_();
|
auto &v = storage_();
|
||||||
auto it = std::find_if(v.begin(), v.end(), [&](const Command &c) { return c.id == id; });
|
auto it = std::find_if(v.begin(), v.end(), [&](const Command &c) {
|
||||||
|
return c.id == id;
|
||||||
|
});
|
||||||
return it == v.end() ? nullptr : &*it;
|
return it == v.end() ? nullptr : &*it;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +486,9 @@ const Command *
|
|||||||
CommandRegistry::FindByName(const std::string &name)
|
CommandRegistry::FindByName(const std::string &name)
|
||||||
{
|
{
|
||||||
auto &v = storage_();
|
auto &v = storage_();
|
||||||
auto it = std::find_if(v.begin(), v.end(), [&](const Command &c) { return c.name == name; });
|
auto it = std::find_if(v.begin(), v.end(), [&](const Command &c) {
|
||||||
|
return c.name == name;
|
||||||
|
});
|
||||||
return it == v.end() ? nullptr : &*it;
|
return it == v.end() ? nullptr : &*it;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,9 +508,12 @@ InstallDefaultCommands()
|
|||||||
CommandRegistry::Register({CommandId::Quit, "quit", "Quit editor (request)", cmd_quit});
|
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::SaveAndQuit, "save-quit", "Save and quit (request)", cmd_save_and_quit});
|
||||||
CommandRegistry::Register({CommandId::Refresh, "refresh", "Force redraw", cmd_refresh});
|
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::FindStart, "find-start", "Begin incremental search", cmd_find_start});
|
||||||
// Editing
|
// Editing
|
||||||
CommandRegistry::Register({CommandId::InsertText, "insert", "Insert text at cursor (no newlines)", cmd_insert_text});
|
CommandRegistry::Register({
|
||||||
|
CommandId::InsertText, "insert", "Insert text at cursor (no newlines)", cmd_insert_text
|
||||||
|
});
|
||||||
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});
|
||||||
@@ -449,18 +527,23 @@ InstallDefaultCommands()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Execute(Editor &ed, CommandId id, const std::string &arg, int count)
|
bool
|
||||||
|
Execute(Editor &ed, CommandId id, const std::string &arg, int count)
|
||||||
{
|
{
|
||||||
const Command *cmd = CommandRegistry::FindById(id);
|
const Command *cmd = CommandRegistry::FindById(id);
|
||||||
if (!cmd) return false;
|
if (!cmd)
|
||||||
|
return false;
|
||||||
CommandContext ctx{ed, arg, count};
|
CommandContext ctx{ed, arg, count};
|
||||||
return cmd->handler ? cmd->handler(ctx) : false;
|
return cmd->handler ? cmd->handler(ctx) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Execute(Editor &ed, const std::string &name, const std::string &arg, int count)
|
|
||||||
|
bool
|
||||||
|
Execute(Editor &ed, const std::string &name, const std::string &arg, int count)
|
||||||
{
|
{
|
||||||
const Command *cmd = CommandRegistry::FindByName(name);
|
const Command *cmd = CommandRegistry::FindByName(name);
|
||||||
if (!cmd) return false;
|
if (!cmd)
|
||||||
|
return false;
|
||||||
CommandContext ctx{ed, arg, count};
|
CommandContext ctx{ed, arg, count};
|
||||||
return cmd->handler ? cmd->handler(ctx) : false;
|
return cmd->handler ? cmd->handler(ctx) : false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ enum class CommandId {
|
|||||||
Quit,
|
Quit,
|
||||||
SaveAndQuit,
|
SaveAndQuit,
|
||||||
Refresh, // force redraw
|
Refresh, // force redraw
|
||||||
|
KPrefix, // show "C-k _" prompt in status when entering k-command
|
||||||
FindStart, // begin incremental search (placeholder)
|
FindStart, // begin incremental search (placeholder)
|
||||||
// Editing
|
// Editing
|
||||||
InsertText, // arg: text to insert at cursor (UTF-8, no newlines)
|
InsertText, // arg: text to insert at cursor (UTF-8, no newlines)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "GUIInputHandler.h"
|
#include "GUIInputHandler.h"
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include "KKeymap.h"
|
||||||
|
|
||||||
static bool map_key(SDL_Keycode key, SDL_Keymod mod, bool &k_prefix, MappedInput &out)
|
static bool map_key(SDL_Keycode key, SDL_Keymod mod, bool &k_prefix, MappedInput &out)
|
||||||
{
|
{
|
||||||
@@ -26,7 +27,7 @@ static bool map_key(SDL_Keycode key, SDL_Keymod mod, bool &k_prefix, MappedInput
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
case SDLK_k: case SDLK_KP_EQUALS: // treat Ctrl-K
|
case SDLK_k: case SDLK_KP_EQUALS: // treat Ctrl-K
|
||||||
k_prefix = true;
|
k_prefix = true;
|
||||||
out = {true, CommandId::Refresh, "", 0};
|
out = {true, CommandId::KPrefix, "", 0};
|
||||||
return true;
|
return true;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
k_prefix = false;
|
k_prefix = false;
|
||||||
@@ -42,11 +43,19 @@ static bool map_key(SDL_Keycode key, SDL_Keymod mod, bool &k_prefix, MappedInput
|
|||||||
|
|
||||||
if (k_prefix) {
|
if (k_prefix) {
|
||||||
k_prefix = false;
|
k_prefix = false;
|
||||||
switch (key) {
|
// Normalize SDL key to ASCII where possible
|
||||||
case SDLK_s: out = {true, CommandId::Save, "", 0}; return true;
|
int ascii_key = 0;
|
||||||
case SDLK_x: out = {true, CommandId::SaveAndQuit, "", 0}; return true;
|
if (key >= SDLK_SPACE && key <= SDLK_z) {
|
||||||
case SDLK_q: out = {true, CommandId::Quit, "", 0}; return true;
|
ascii_key = static_cast<int>(key);
|
||||||
default: break;
|
}
|
||||||
|
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;
|
out.hasCommand = false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
void GUIRenderer::Draw(const Editor &ed)
|
void GUIRenderer::Draw(const Editor &ed)
|
||||||
{
|
{
|
||||||
@@ -35,20 +36,58 @@ void GUIRenderer::Draw(const Editor &ed)
|
|||||||
// Reserve space for status bar at bottom
|
// Reserve space for status bar at bottom
|
||||||
ImGui::BeginChild("scroll", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), false, ImGuiWindowFlags_HorizontalScrollbar);
|
ImGui::BeginChild("scroll", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
std::size_t rowoffs = buf->Rowoffs();
|
std::size_t rowoffs = buf->Rowoffs();
|
||||||
|
std::size_t cy = buf->Cury();
|
||||||
|
std::size_t cx = buf->Curx();
|
||||||
|
const float line_h = ImGui::GetTextLineHeight();
|
||||||
|
const float space_w = ImGui::CalcTextSize(" ").x;
|
||||||
for (std::size_t i = rowoffs; i < lines.size(); ++i) {
|
for (std::size_t i = rowoffs; i < lines.size(); ++i) {
|
||||||
ImGui::TextUnformatted(lines[i].c_str());
|
// Capture the screen position before drawing the line
|
||||||
|
ImVec2 line_pos = ImGui::GetCursorScreenPos();
|
||||||
|
const std::string &line = lines[i];
|
||||||
|
ImGui::TextUnformatted(line.c_str());
|
||||||
|
|
||||||
|
// Draw a visible cursor indicator on the current line
|
||||||
|
if (i == cy) {
|
||||||
|
// Compute X offset by measuring text width up to cursor column
|
||||||
|
std::size_t px_count = std::min(cx, line.size());
|
||||||
|
ImVec2 pre_sz = ImGui::CalcTextSize(line.c_str(), line.c_str() + static_cast<long>(px_count));
|
||||||
|
ImVec2 p0 = ImVec2(line_pos.x + pre_sz.x, line_pos.y);
|
||||||
|
ImVec2 p1 = ImVec2(p0.x + space_w, p0.y + line_h);
|
||||||
|
ImU32 col = IM_COL32(200, 200, 255, 128); // soft highlight
|
||||||
|
ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, col);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
// Status bar
|
// Status bar spanning full width
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
const char *fname = (buf->IsFileBacked()) ? buf->Filename().c_str() : "(new)";
|
const char *fname = (buf->IsFileBacked()) ? buf->Filename().c_str() : "(new)";
|
||||||
bool dirty = buf->Dirty();
|
bool dirty = buf->Dirty();
|
||||||
ImGui::Text("%s%s %zux%zu %s",
|
char status[1024];
|
||||||
|
snprintf(status, sizeof(status), " %s%s %zux%zu %s ",
|
||||||
fname,
|
fname,
|
||||||
dirty ? "*" : "",
|
dirty ? "*" : "",
|
||||||
ed.Rows(), ed.Cols(),
|
ed.Rows(), ed.Cols(),
|
||||||
ed.Status().c_str());
|
ed.Status().c_str());
|
||||||
|
|
||||||
|
// Compute full content width and draw a filled background rectangle
|
||||||
|
ImVec2 win_pos = ImGui::GetWindowPos();
|
||||||
|
ImVec2 cr_min = ImGui::GetWindowContentRegionMin();
|
||||||
|
ImVec2 cr_max = ImGui::GetWindowContentRegionMax();
|
||||||
|
float x0 = win_pos.x + cr_min.x;
|
||||||
|
float x1 = win_pos.x + cr_max.x;
|
||||||
|
ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||||
|
float bar_h = ImGui::GetFrameHeight();
|
||||||
|
ImVec2 p0(x0, cursor.y);
|
||||||
|
ImVec2 p1(x1, cursor.y + bar_h);
|
||||||
|
ImU32 bg_col = ImGui::GetColorU32(ImGuiCol_HeaderActive);
|
||||||
|
ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, bg_col);
|
||||||
|
// Place status text within the bar
|
||||||
|
ImVec2 text_sz = ImGui::CalcTextSize(status);
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(p0.x + 6.f, p0.y + (bar_h - text_sz.y) * 0.5f));
|
||||||
|
ImGui::TextUnformatted(status);
|
||||||
|
// Advance cursor to after the bar to keep layout consistent
|
||||||
|
ImGui::Dummy(ImVec2(x1 - x0, bar_h));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|||||||
24
KKeymap.cpp
Normal file
24
KKeymap.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "KKeymap.h"
|
||||||
|
|
||||||
|
auto
|
||||||
|
KLookupKCommand(const int ascii_key, bool ctrl, CommandId &out) -> bool
|
||||||
|
{
|
||||||
|
// Normalize to lowercase letter if applicable
|
||||||
|
int k = KLowerAscii(ascii_key);
|
||||||
|
|
||||||
|
if (ctrl) {
|
||||||
|
switch (k) {
|
||||||
|
case 'x': out = CommandId::SaveAndQuit; return true; // C-k C-x
|
||||||
|
case 'q': out = CommandId::Quit; return true; // C-k C-q (quit immediately)
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (k) {
|
||||||
|
case 's': out = CommandId::Save; return true; // C-k s
|
||||||
|
case 'x': out = CommandId::SaveAndQuit; return true; // C-k x
|
||||||
|
case 'q': out = CommandId::Quit; return true; // C-k q
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
26
KKeymap.h
Normal file
26
KKeymap.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* KKeymap.h - mapping for k-command (C-k prefix) keys to CommandId
|
||||||
|
*/
|
||||||
|
#ifndef KTE_KKEYMAP_H
|
||||||
|
#define KTE_KKEYMAP_H
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
// Lookup the command to execute after a C-k prefix.
|
||||||
|
// Parameters:
|
||||||
|
// - ascii_key: ASCII code of the key, preferably lowercased if it's a letter.
|
||||||
|
// - ctrl: whether Control modifier was held for this key (e.g., C-k C-x).
|
||||||
|
// Returns true and sets out if a mapping exists; false otherwise.
|
||||||
|
bool KLookupKCommand(int ascii_key, bool ctrl, CommandId &out);
|
||||||
|
|
||||||
|
// Utility: normalize an int keycode to lowercased ASCII if it's in printable range.
|
||||||
|
inline int
|
||||||
|
KLowerAscii(const int key)
|
||||||
|
{
|
||||||
|
if (key >= 'A' && key <= 'Z')
|
||||||
|
return key + ('a' - 'A');
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KTE_KKEYMAP_H
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "TerminalInputHandler.h"
|
#include "TerminalInputHandler.h"
|
||||||
|
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
#include "KKeymap.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int CTRL(char c) { return c & 0x1F; }
|
constexpr int CTRL(char c) { return c & 0x1F; }
|
||||||
@@ -35,7 +36,7 @@ static bool map_key_to_command(int ch, bool &k_prefix, MappedInput &out)
|
|||||||
// Control keys
|
// Control keys
|
||||||
if (ch == CTRL('K')) { // C-k prefix
|
if (ch == CTRL('K')) { // C-k prefix
|
||||||
k_prefix = true;
|
k_prefix = true;
|
||||||
out = {true, CommandId::Refresh, "", 0};
|
out = {true, CommandId::KPrefix, "", 0};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ch == CTRL('G')) { // cancel
|
if (ch == CTRL('G')) { // cancel
|
||||||
@@ -53,14 +54,22 @@ static bool map_key_to_command(int ch, bool &k_prefix, MappedInput &out)
|
|||||||
|
|
||||||
if (k_prefix) {
|
if (k_prefix) {
|
||||||
k_prefix = false; // single next key only
|
k_prefix = false; // single next key only
|
||||||
switch (ch) {
|
// Determine if this is a control chord (e.g., C-x) and normalize
|
||||||
case 's': case 'S': out = {true, CommandId::Save, "", 0}; return true;
|
bool ctrl = false;
|
||||||
case 'x': case 'X': out = {true, CommandId::SaveAndQuit, "", 0}; return true;
|
int ascii_key = ch;
|
||||||
case 'q': case 'Q': out = {true, CommandId::Quit, "", 0}; return true;
|
if (ch >= 1 && ch <= 26) {
|
||||||
default: break;
|
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
|
||||||
}
|
}
|
||||||
if (ch == CTRL('Q')) { out = {true, CommandId::Quit, "", 0}; return true; }
|
|
||||||
out.hasCommand = false; // unknown chord
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,27 @@ void TerminalRenderer::Draw(const Editor &ed)
|
|||||||
clrtoeol();
|
clrtoeol();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place cursor (best-effort; tabs etc. not handled yet)
|
// Draw a visible cursor cell by inverting the character at the cursor
|
||||||
|
// position (or a space at EOL). This makes the cursor obvious even when
|
||||||
|
// the terminal's native cursor is hidden or not prominent.
|
||||||
std::size_t cy = buf->Cury();
|
std::size_t cy = buf->Cury();
|
||||||
std::size_t cx = buf->Curx();
|
std::size_t cx = buf->Curx();
|
||||||
int cur_y = static_cast<int>(cy - buf->Rowoffs());
|
int cur_y = static_cast<int>(cy - buf->Rowoffs());
|
||||||
int cur_x = static_cast<int>(cx - buf->Coloffs());
|
int cur_x = static_cast<int>(cx - buf->Coloffs());
|
||||||
if (cur_y >= 0 && cur_y < content_rows && cur_x >= 0 && cur_x < cols) {
|
if (cur_y >= 0 && cur_y < content_rows && cur_x >= 0 && cur_x < cols) {
|
||||||
|
// Determine the character under the cursor (if any)
|
||||||
|
char ch = ' ';
|
||||||
|
if (cy < lines.size()) {
|
||||||
|
const std::string &cline = lines[cy];
|
||||||
|
if (cx < cline.size()) {
|
||||||
|
ch = cline[static_cast<long>(cx)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
move(cur_y, cur_x);
|
||||||
|
attron(A_REVERSE);
|
||||||
|
addch(static_cast<unsigned char>(ch));
|
||||||
|
attroff(A_REVERSE);
|
||||||
|
// Also place the terminal cursor at the same spot
|
||||||
move(cur_y, cur_x);
|
move(cur_y, cur_x);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user