Add regex search, search/replace, and buffer read-only mode functionality with help text
This commit is contained in:
23
.idea/workspace.xml
generated
23
.idea/workspace.xml
generated
@@ -33,9 +33,13 @@
|
||||
</configurations>
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Fix void crash in kge.">
|
||||
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="add regex and search/replace functionality to editor">
|
||||
<change afterPath="$PROJECT_DIR$/HelpText.cc" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/HelpText.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Buffer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Buffer.h" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" 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.h" beforeDir="false" afterPath="$PROJECT_DIR$/Editor.h" afterDir="false" />
|
||||
@@ -43,7 +47,6 @@
|
||||
<change beforePath="$PROJECT_DIR$/KKeymap.cc" beforeDir="false" afterPath="$PROJECT_DIR$/KKeymap.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/ROADMAP.md" beforeDir="false" afterPath="$PROJECT_DIR$/ROADMAP.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/TerminalRenderer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalRenderer.cc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/default.nix" beforeDir="false" afterPath="$PROJECT_DIR$/default.nix" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -70,6 +73,7 @@
|
||||
<component name="HighlightingSettingsPerFile">
|
||||
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
||||
</component>
|
||||
<component name="OptimizeOnSaveOptions">
|
||||
<option name="myRunOnSave" value="true" />
|
||||
@@ -180,7 +184,7 @@
|
||||
<workItem from="1764539556448" duration="156000" />
|
||||
<workItem from="1764539725338" duration="1075000" />
|
||||
<workItem from="1764542392763" duration="3512000" />
|
||||
<workItem from="1764548345516" duration="16453000" />
|
||||
<workItem from="1764548345516" duration="28341000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
|
||||
<option name="closed" value="true" />
|
||||
@@ -302,7 +306,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1764568264996</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="16" />
|
||||
<task id="LOCAL-00016" summary="add regex and search/replace functionality to editor">
|
||||
<option name="closed" value="true" />
|
||||
<created>1764574397967</created>
|
||||
<option name="number" value="00016" />
|
||||
<option name="presentableId" value="LOCAL-00016" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1764574397967</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="17" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -331,7 +343,8 @@
|
||||
<MESSAGE value="Add buffer position display and documentation improvements. - Display buffer position prefix "[x/N]" in GUI and terminal renderers. - Improve `kte` and `kge` man pages with frontend usage details and project homepage. - Update README with GUI invocation instructions. - Bump version to 1.0.0." />
|
||||
<MESSAGE value="Actually add the screenshot." />
|
||||
<MESSAGE value="Fix void crash in kge." />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Fix void crash in kge." />
|
||||
<MESSAGE value="add regex and search/replace functionality to editor" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="add regex and search/replace functionality to editor" />
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
|
||||
16
Buffer.cc
16
Buffer.cc
@@ -36,6 +36,7 @@ Buffer::Buffer(const Buffer &other)
|
||||
filename_ = other.filename_;
|
||||
is_file_backed_ = other.is_file_backed_;
|
||||
dirty_ = other.dirty_;
|
||||
read_only_ = other.read_only_;
|
||||
mark_set_ = other.mark_set_;
|
||||
mark_curx_ = other.mark_curx_;
|
||||
mark_cury_ = other.mark_cury_;
|
||||
@@ -60,6 +61,7 @@ Buffer::operator=(const Buffer &other)
|
||||
filename_ = other.filename_;
|
||||
is_file_backed_ = other.is_file_backed_;
|
||||
dirty_ = other.dirty_;
|
||||
read_only_ = other.read_only_;
|
||||
mark_set_ = other.mark_set_;
|
||||
mark_curx_ = other.mark_curx_;
|
||||
mark_cury_ = other.mark_cury_;
|
||||
@@ -82,6 +84,7 @@ Buffer::Buffer(Buffer &&other) noexcept
|
||||
filename_(std::move(other.filename_)),
|
||||
is_file_backed_(other.is_file_backed_),
|
||||
dirty_(other.dirty_),
|
||||
read_only_(other.read_only_),
|
||||
mark_set_(other.mark_set_),
|
||||
mark_curx_(other.mark_curx_),
|
||||
mark_cury_(other.mark_cury_),
|
||||
@@ -112,6 +115,7 @@ Buffer::operator=(Buffer &&other) noexcept
|
||||
filename_ = std::move(other.filename_);
|
||||
is_file_backed_ = other.is_file_backed_;
|
||||
dirty_ = other.dirty_;
|
||||
read_only_ = other.read_only_;
|
||||
mark_set_ = other.mark_set_;
|
||||
mark_curx_ = other.mark_curx_;
|
||||
mark_cury_ = other.mark_cury_;
|
||||
@@ -364,9 +368,9 @@ Buffer::insert_text(int row, int col, std::string_view text)
|
||||
rows_[y].insert(x, seg);
|
||||
x += seg.size();
|
||||
// Split line at x
|
||||
std::string tail = rows_[y].substr(x);
|
||||
rows_[y].erase(x);
|
||||
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1), tail);
|
||||
std::string tail = rows_[y].substr(x);
|
||||
rows_[y].erase(x);
|
||||
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1), Line(tail));
|
||||
y += 1;
|
||||
x = 0;
|
||||
remain.erase(0, pos + 1);
|
||||
@@ -426,8 +430,8 @@ Buffer::split_line(int row, const int col)
|
||||
const auto y = static_cast<std::size_t>(row);
|
||||
const auto x = std::min<std::size_t>(static_cast<std::size_t>(col), rows_[y].size());
|
||||
const auto tail = rows_[y].substr(x);
|
||||
rows_[y].erase(x);
|
||||
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1), tail);
|
||||
rows_[y].erase(x);
|
||||
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1), Line(tail));
|
||||
}
|
||||
|
||||
|
||||
@@ -455,7 +459,7 @@ Buffer::insert_row(int row, const std::string_view text)
|
||||
row = 0;
|
||||
if (static_cast<std::size_t>(row) > rows_.size())
|
||||
row = static_cast<int>(rows_.size());
|
||||
rows_.insert(rows_.begin() + row, std::string(text));
|
||||
rows_.insert(rows_.begin() + row, Line(std::string(text)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
51
Buffer.h
51
Buffer.h
@@ -16,7 +16,7 @@
|
||||
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer();
|
||||
Buffer();
|
||||
|
||||
Buffer(const Buffer &other);
|
||||
|
||||
@@ -262,6 +262,14 @@ public:
|
||||
return filename_;
|
||||
}
|
||||
|
||||
// Set a virtual (non file-backed) display name for this buffer, e.g. "+HELP+"
|
||||
// This does not mark the buffer as file-backed.
|
||||
void SetVirtualName(const std::string &name)
|
||||
{
|
||||
filename_ = name;
|
||||
is_file_backed_ = false;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] bool IsFileBacked() const
|
||||
{
|
||||
@@ -269,10 +277,26 @@ public:
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] bool Dirty() const
|
||||
{
|
||||
return dirty_;
|
||||
}
|
||||
[[nodiscard]] bool Dirty() const
|
||||
{
|
||||
return dirty_;
|
||||
}
|
||||
|
||||
// Read-only flag
|
||||
[[nodiscard]] bool IsReadOnly() const
|
||||
{
|
||||
return read_only_;
|
||||
}
|
||||
|
||||
void SetReadOnly(bool ro)
|
||||
{
|
||||
read_only_ = ro;
|
||||
}
|
||||
|
||||
void ToggleReadOnly()
|
||||
{
|
||||
read_only_ = !read_only_;
|
||||
}
|
||||
|
||||
|
||||
void SetCursor(const std::size_t x, const std::size_t y)
|
||||
@@ -356,17 +380,18 @@ public:
|
||||
[[nodiscard]] const UndoSystem *Undo() const;
|
||||
|
||||
private:
|
||||
// State mirroring original C struct (without undo_tree)
|
||||
std::size_t curx_ = 0, cury_ = 0; // cursor position in characters
|
||||
std::size_t rx_ = 0; // render x (tabs expanded)
|
||||
std::size_t nrows_ = 0; // number of rows
|
||||
// State mirroring original C struct (without undo_tree)
|
||||
std::size_t curx_ = 0, cury_ = 0; // cursor position in characters
|
||||
std::size_t rx_ = 0; // render x (tabs expanded)
|
||||
std::size_t nrows_ = 0; // number of rows
|
||||
std::size_t rowoffs_ = 0, coloffs_ = 0; // viewport offsets
|
||||
std::vector<Line> rows_; // buffer rows (without trailing newlines)
|
||||
std::string filename_;
|
||||
bool is_file_backed_ = false;
|
||||
bool dirty_ = false;
|
||||
bool mark_set_ = false;
|
||||
std::size_t mark_curx_ = 0, mark_cury_ = 0;
|
||||
bool is_file_backed_ = false;
|
||||
bool dirty_ = false;
|
||||
bool read_only_ = false;
|
||||
bool mark_set_ = false;
|
||||
std::size_t mark_curx_ = 0, mark_cury_ = 0;
|
||||
|
||||
// Per-buffer undo state
|
||||
std::unique_ptr<struct UndoTree> undo_tree_;
|
||||
|
||||
@@ -8,7 +8,7 @@ set(KTE_VERSION "1.0.4")
|
||||
|
||||
# Default to terminal-only build to avoid SDL/OpenGL dependency by default.
|
||||
# Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available.
|
||||
set(BUILD_GUI OFF CACHE BOOL "Enable building the graphical version.")
|
||||
set(BUILD_GUI ON CACHE BOOL "Enable building the graphical version.")
|
||||
set(BUILD_TESTS OFF CACHE BOOL "Enable building test programs.")
|
||||
option(KTE_USE_PIECE_TABLE "Use PieceTable instead of GapBuffer implementation" ON)
|
||||
set(KTE_FONT_SIZE "18.0" CACHE STRING "Default font size for GUI")
|
||||
@@ -55,6 +55,7 @@ set(COMMON_SOURCES
|
||||
Buffer.cc
|
||||
Editor.cc
|
||||
Command.cc
|
||||
HelpText.cc
|
||||
KKeymap.cc
|
||||
TerminalInputHandler.cc
|
||||
TerminalRenderer.cc
|
||||
@@ -74,6 +75,7 @@ set(COMMON_HEADERS
|
||||
Editor.h
|
||||
AppendBuffer.h
|
||||
Command.h
|
||||
HelpText.h
|
||||
KKeymap.h
|
||||
InputHandler.h
|
||||
TerminalInputHandler.h
|
||||
|
||||
1036
Command.cc
1036
Command.cc
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ enum class CommandId {
|
||||
KPrefix, // show "C-k _" prompt in status when entering k-command
|
||||
FindStart, // begin incremental search (placeholder)
|
||||
RegexFindStart, // begin regex search (C-r)
|
||||
RegexpReplace, // begin regex search & replace (C-t)
|
||||
SearchReplace, // begin search & replace (two-step prompt)
|
||||
OpenFileStart, // begin open-file prompt
|
||||
VisualFilePickerToggle,
|
||||
@@ -72,6 +73,8 @@ enum class CommandId {
|
||||
IndentRegion, // indent region (C-k =)
|
||||
UnindentRegion, // unindent region (C-k -)
|
||||
ReflowParagraph, // reflow paragraph to column width (ESC q)
|
||||
// Read-only buffers
|
||||
ToggleReadOnly, // toggle current buffer read-only (C-k ')
|
||||
// Buffer operations
|
||||
ReloadBuffer, // reload buffer from disk (C-k l)
|
||||
MarkAllAndJumpEnd, // set mark at beginning, jump to end (C-k a)
|
||||
@@ -79,6 +82,8 @@ enum class CommandId {
|
||||
JumpToLine, // prompt for line and jump (C-k g)
|
||||
ShowWorkingDirectory, // Display the current working directory in the editor message.
|
||||
ChangeWorkingDirectory, // Change the editor's current directory.
|
||||
// Help
|
||||
ShowHelp, // open +HELP+ buffer with manual text (C-k h)
|
||||
// Meta
|
||||
UnknownKCommand, // arg: single character that was not recognized after C-k
|
||||
};
|
||||
|
||||
2
Editor.h
2
Editor.h
@@ -306,6 +306,8 @@ public:
|
||||
None = 0,
|
||||
Search,
|
||||
RegexSearch,
|
||||
RegexReplaceFind, // step 1 of Regex Search & Replace: find pattern
|
||||
RegexReplaceWith, // step 2 of Regex Search & Replace: replacement text
|
||||
OpenFile,
|
||||
SaveAs,
|
||||
Confirm,
|
||||
|
||||
@@ -191,7 +191,7 @@ GUIRenderer::Draw(Editor &ed)
|
||||
} else {
|
||||
// Convert pixel X to a render-column target including horizontal col offset
|
||||
// Use our own tab expansion of width 8 to match command layer logic.
|
||||
const std::string &line_clicked = lines[by];
|
||||
std::string line_clicked = static_cast<std::string>(lines[by]);
|
||||
const std::size_t tabw = 8;
|
||||
// We iterate source columns computing absolute rendered column (rx_abs) from 0,
|
||||
// then translate to viewport-space by subtracting Coloffs.
|
||||
@@ -245,7 +245,7 @@ GUIRenderer::Draw(Editor &ed)
|
||||
for (std::size_t i = rowoffs; i < lines.size(); ++i) {
|
||||
// Capture the screen position before drawing the line
|
||||
ImVec2 line_pos = ImGui::GetCursorScreenPos();
|
||||
const std::string &line = lines[i];
|
||||
std::string line = static_cast<std::string>(lines[i]);
|
||||
|
||||
// Expand tabs to spaces with width=8 and apply horizontal scroll offset
|
||||
const std::size_t tabw = 8;
|
||||
@@ -256,8 +256,8 @@ GUIRenderer::Draw(Editor &ed)
|
||||
bool search_mode = ed.SearchActive() && !ed.SearchQuery().empty();
|
||||
std::vector<std::pair<std::size_t, std::size_t>> hl_src_ranges;
|
||||
if (search_mode) {
|
||||
// If we're in RegexSearch mode, compute ranges using regex; otherwise plain substring
|
||||
if (ed.PromptActive() && ed.CurrentPromptKind() == Editor::PromptKind::RegexSearch) {
|
||||
// If we're in RegexSearch or RegexReplaceFind mode, compute ranges using regex; otherwise plain substring
|
||||
if (ed.PromptActive() && (ed.CurrentPromptKind() == Editor::PromptKind::RegexSearch || ed.CurrentPromptKind() == Editor::PromptKind::RegexReplaceFind)) {
|
||||
try {
|
||||
std::regex rx(ed.SearchQuery());
|
||||
for (auto it = std::sregex_iterator(line.begin(), line.end(), rx);
|
||||
@@ -366,8 +366,7 @@ GUIRenderer::Draw(Editor &ed)
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, bg_col);
|
||||
// If a prompt is active, replace the entire status bar with the prompt text
|
||||
if (ed.PromptActive()) {
|
||||
std::string msg = ed.PromptLabel();
|
||||
if (!msg.empty()) msg += ": ";
|
||||
std::string label = ed.PromptLabel();
|
||||
std::string ptext = ed.PromptText();
|
||||
auto kind = ed.CurrentPromptKind();
|
||||
if (kind == Editor::PromptKind::OpenFile || kind == Editor::PromptKind::SaveAs ||
|
||||
@@ -384,14 +383,62 @@ GUIRenderer::Draw(Editor &ed)
|
||||
}
|
||||
}
|
||||
}
|
||||
msg += ptext;
|
||||
|
||||
float pad = 6.f;
|
||||
ImVec2 msg_sz = ImGui::CalcTextSize(msg.c_str());
|
||||
float left_x = p0.x + pad;
|
||||
float right_x = p1.x - pad;
|
||||
float max_px = std::max(0.0f, right_x - left_x);
|
||||
|
||||
std::string prefix;
|
||||
if (!label.empty()) prefix = label + ": ";
|
||||
|
||||
// Compose showing right-end of filename portion when too long for space
|
||||
std::string final_msg;
|
||||
ImVec2 prefix_sz = ImGui::CalcTextSize(prefix.c_str());
|
||||
float avail_px = std::max(0.0f, max_px - prefix_sz.x);
|
||||
if ((kind == Editor::PromptKind::OpenFile || kind == Editor::PromptKind::SaveAs || kind == Editor::PromptKind::Chdir) && avail_px > 0.0f) {
|
||||
// Trim from left until it fits by pixel width
|
||||
std::string tail = ptext;
|
||||
ImVec2 tail_sz = ImGui::CalcTextSize(tail.c_str());
|
||||
if (tail_sz.x > avail_px) {
|
||||
// Remove leading chars until it fits
|
||||
// Use a simple loop; text lengths are small here
|
||||
size_t start = 0;
|
||||
// To avoid O(n^2) worst-case, remove chunks
|
||||
while (start < tail.size()) {
|
||||
// Estimate how many chars to skip based on ratio
|
||||
float ratio = tail_sz.x / avail_px;
|
||||
size_t skip = ratio > 1.5f ? std::min(tail.size() - start, (size_t)std::max<size_t>(1, (size_t)(tail.size() / 4))) : 1;
|
||||
start += skip;
|
||||
std::string candidate = tail.substr(start);
|
||||
ImVec2 cand_sz = ImGui::CalcTextSize(candidate.c_str());
|
||||
if (cand_sz.x <= avail_px) {
|
||||
tail = candidate;
|
||||
tail_sz = cand_sz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ImGui::CalcTextSize(tail.c_str()).x > avail_px && !tail.empty()) {
|
||||
// As a last resort, ensure fit by chopping exactly
|
||||
// binary reduce
|
||||
size_t lo = 0, hi = tail.size();
|
||||
while (lo < hi) {
|
||||
size_t mid = (lo + hi) / 2;
|
||||
std::string cand = tail.substr(mid);
|
||||
if (ImGui::CalcTextSize(cand.c_str()).x <= avail_px) hi = mid; else lo = mid + 1;
|
||||
}
|
||||
tail = tail.substr(lo);
|
||||
}
|
||||
}
|
||||
final_msg = prefix + tail;
|
||||
} else {
|
||||
final_msg = prefix + ptext;
|
||||
}
|
||||
|
||||
ImVec2 msg_sz = ImGui::CalcTextSize(final_msg.c_str());
|
||||
ImGui::PushClipRect(ImVec2(p0.x, p0.y), ImVec2(p1.x, p1.y), true);
|
||||
ImGui::SetCursorScreenPos(ImVec2(left_x, p0.y + (bar_h - msg_sz.y) * 0.5f));
|
||||
ImGui::TextUnformatted(msg.c_str());
|
||||
ImGui::TextUnformatted(final_msg.c_str());
|
||||
ImGui::PopClipRect();
|
||||
// Advance cursor to after the bar to keep layout consistent
|
||||
ImGui::Dummy(ImVec2(x1 - x0, bar_h));
|
||||
|
||||
55
HelpText.cc
Normal file
55
HelpText.cc
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* HelpText.cc - embedded/customizable help content
|
||||
*/
|
||||
|
||||
#include "HelpText.h"
|
||||
|
||||
|
||||
std::string
|
||||
HelpText::Text()
|
||||
{
|
||||
// Customize the help text here. This string will be used by C-k h first.
|
||||
// You can keep it empty to fall back to the manpage or built-in defaults.
|
||||
// Note: keep newline characters as-is; the renderer splits lines on '\n'.
|
||||
|
||||
return std::string(
|
||||
"KTE - Kyle's Text Editor\n\n"
|
||||
"About:\n"
|
||||
" kte is Kyle's Text Editor and is probably ill-suited to everyone else. It was\n"
|
||||
" inspired by Antirez' kilo text editor by way of someone's writeup of the\n"
|
||||
" process of writing a text editor from scratch. It has keybindings inspired by\n"
|
||||
" VDE (and the Wordstar family) and emacs; its spiritual parent is mg(1).\n"
|
||||
"\n"
|
||||
"Core keybindings:\n"
|
||||
" C-k ' Toggle read-only\n"
|
||||
" C-k - Unindent region\n"
|
||||
" C-k = Indent region\n"
|
||||
" C-k C-d Kill entire line\n"
|
||||
" C-k C-q Quit now (no confirm)\n"
|
||||
" C-k a Mark all and jump to end\n"
|
||||
" C-k b Switch buffer\n"
|
||||
" C-k c Close current buffer\n"
|
||||
" C-k d Kill to end of line\n"
|
||||
" C-k e Open file (prompt)\n"
|
||||
" C-k g Jump to line\n"
|
||||
" C-k h Show this help\n"
|
||||
" C-k l Reload buffer from disk\n"
|
||||
" C-k n Previous buffer\n"
|
||||
" C-k o Change working directory (prompt)\n"
|
||||
" C-k p Next buffer\n"
|
||||
" C-k q Quit (confirm if dirty)\n"
|
||||
" C-k r Redo\n"
|
||||
" C-k s Save buffer\n"
|
||||
" C-k u Undo\n"
|
||||
" C-k v Toggle visual file picker (GUI)\n"
|
||||
" C-k w Show working directory\n"
|
||||
" C-k x Save and quit\n"
|
||||
"\n"
|
||||
"ESC/Alt commands:\n"
|
||||
" ESC q Reflow paragraph\n"
|
||||
" ESC BACKSPACE Delete previous word\n"
|
||||
" ESC d Delete next word\n"
|
||||
" Alt-w Copy region to kill ring\n\n"
|
||||
"Buffers:\n +HELP+ is read-only. Press C-k ' to toggle if you need to edit; C-k h restores it.\n"
|
||||
);
|
||||
}
|
||||
17
HelpText.h
Normal file
17
HelpText.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* HelpText.h - embedded/customizable help content
|
||||
*/
|
||||
#ifndef KTE_HELPTEXT_H
|
||||
#define KTE_HELPTEXT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class HelpText {
|
||||
public:
|
||||
// Returns the embedded help text as a single string with newlines.
|
||||
// Project maintainers can customize the returned string below
|
||||
// (in HelpText.cc) without touching the help command logic.
|
||||
static std::string Text();
|
||||
};
|
||||
|
||||
#endif // KTE_HELPTEXT_H
|
||||
18
KKeymap.cc
18
KKeymap.cc
@@ -33,6 +33,10 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
||||
out = CommandId::Redo; // C-k r (redo)
|
||||
return true;
|
||||
}
|
||||
if (ascii_key == '\'') {
|
||||
out = CommandId::ToggleReadOnly; // C-k ' (toggle read-only)
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (k_lower) {
|
||||
case 'a':
|
||||
@@ -59,6 +63,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
||||
case 'g':
|
||||
out = CommandId::JumpToLine;
|
||||
return true;
|
||||
case 'h':
|
||||
out = CommandId::ShowHelp;
|
||||
return true;
|
||||
case 'j':
|
||||
out = CommandId::JumpToMark;
|
||||
return true;
|
||||
@@ -114,7 +121,7 @@ auto
|
||||
KLookupCtrlCommand(const int ascii_key, CommandId &out) -> bool
|
||||
{
|
||||
const int k = KLowerAscii(ascii_key);
|
||||
switch (k) {
|
||||
switch (k) {
|
||||
case 'w':
|
||||
out = CommandId::KillRegion; // C-w
|
||||
return true;
|
||||
@@ -145,9 +152,12 @@ KLookupCtrlCommand(const int ascii_key, CommandId &out) -> bool
|
||||
case 's':
|
||||
out = CommandId::FindStart;
|
||||
return true;
|
||||
case 'r':
|
||||
out = CommandId::RegexFindStart; // C-r regex search
|
||||
return true;
|
||||
case 'r':
|
||||
out = CommandId::RegexFindStart; // C-r regex search
|
||||
return true;
|
||||
case 't':
|
||||
out = CommandId::RegexpReplace; // C-t regex search & replace
|
||||
return true;
|
||||
case 'h':
|
||||
out = CommandId::SearchReplace; // C-h: search & replace
|
||||
return true;
|
||||
|
||||
12
ROADMAP.md
12
ROADMAP.md
@@ -1,10 +1,10 @@
|
||||
ROADMAP / TODO:
|
||||
|
||||
- [x] Search + Replace
|
||||
- [ ] Regex search + replace
|
||||
- [x] Regex search + replace
|
||||
- [ ] The undo system should actually work
|
||||
- [ ] Able to mark buffers as read-only
|
||||
- [ ] Built-in help text
|
||||
- [ ] Shorten paths in the homedir with ~
|
||||
- [ ] When the filename is longer than the message window, scoot left to
|
||||
to keep it in view
|
||||
- [x] Able to mark buffers as read-only
|
||||
- [x] Built-in help text
|
||||
- [x] Shorten paths in the homedir with ~
|
||||
- [x] When the filename is longer than the message window, scoot left to
|
||||
keep it in view
|
||||
|
||||
@@ -50,9 +50,9 @@ TerminalRenderer::Draw(Editor &ed)
|
||||
bool search_mode = ed.SearchActive() && !ed.SearchQuery().empty();
|
||||
std::vector<std::pair<std::size_t, std::size_t>> ranges; // [start, end)
|
||||
if (search_mode && li < lines.size()) {
|
||||
const std::string &sline = lines[li];
|
||||
// If regex search prompt is active, use regex to compute highlight ranges
|
||||
if (ed.PromptActive() && ed.CurrentPromptKind() == Editor::PromptKind::RegexSearch) {
|
||||
std::string sline = static_cast<std::string>(lines[li]);
|
||||
// If regex search prompt is active (RegexSearch or RegexReplaceFind), use regex to compute highlight ranges
|
||||
if (ed.PromptActive() && (ed.CurrentPromptKind() == Editor::PromptKind::RegexSearch || ed.CurrentPromptKind() == Editor::PromptKind::RegexReplaceFind)) {
|
||||
try {
|
||||
std::regex rx(ed.SearchQuery());
|
||||
for (auto it = std::sregex_iterator(sline.begin(), sline.end(), rx);
|
||||
@@ -93,7 +93,7 @@ TerminalRenderer::Draw(Editor &ed)
|
||||
bool cur_on = false;
|
||||
int written = 0;
|
||||
if (li < lines.size()) {
|
||||
const std::string &line = lines[li];
|
||||
std::string line = static_cast<std::string>(lines[li]);
|
||||
src_i = 0;
|
||||
render_col = 0;
|
||||
while (written < cols) {
|
||||
@@ -202,9 +202,7 @@ TerminalRenderer::Draw(Editor &ed)
|
||||
// If a prompt is active, replace the status bar with the full prompt text
|
||||
if (ed.PromptActive()) {
|
||||
// Build prompt text: "Label: text" and shorten HOME path for file-related prompts
|
||||
std::string msg = ed.PromptLabel();
|
||||
if (!msg.empty())
|
||||
msg += ": ";
|
||||
std::string label = ed.PromptLabel();
|
||||
std::string ptext = ed.PromptText();
|
||||
auto kind = ed.CurrentPromptKind();
|
||||
if (kind == Editor::PromptKind::OpenFile || kind == Editor::PromptKind::SaveAs ||
|
||||
@@ -222,7 +220,30 @@ TerminalRenderer::Draw(Editor &ed)
|
||||
}
|
||||
}
|
||||
}
|
||||
msg += ptext;
|
||||
// Prefer keeping the tail of the filename visible when it exceeds the window
|
||||
std::string msg;
|
||||
if (!label.empty()) {
|
||||
msg = label + ": ";
|
||||
}
|
||||
// When dealing with file-related prompts, left-trim the filename text so the tail stays visible
|
||||
if ((kind == Editor::PromptKind::OpenFile || kind == Editor::PromptKind::SaveAs || kind == Editor::PromptKind::Chdir) && cols > 0) {
|
||||
int avail = cols - static_cast<int>(msg.size());
|
||||
if (avail <= 0) {
|
||||
// No room for label; fall back to showing the rightmost portion of the whole string
|
||||
std::string whole = msg + ptext;
|
||||
if ((int)whole.size() > cols)
|
||||
whole = whole.substr(whole.size() - cols);
|
||||
msg = whole;
|
||||
} else {
|
||||
if ((int)ptext.size() > avail) {
|
||||
ptext = ptext.substr(ptext.size() - avail);
|
||||
}
|
||||
msg += ptext;
|
||||
}
|
||||
} else {
|
||||
// Non-file prompts: simple concatenation and clip by terminal
|
||||
msg += ptext;
|
||||
}
|
||||
|
||||
// Draw left-aligned, clipped to width
|
||||
if (!msg.empty())
|
||||
@@ -274,6 +295,9 @@ TerminalRenderer::Draw(Editor &ed)
|
||||
left += fname;
|
||||
if (b && b->Dirty())
|
||||
left += " *";
|
||||
// Append read-only indicator
|
||||
if (b && b->IsReadOnly())
|
||||
left += " [RO]";
|
||||
// Append total line count as "<n>L"
|
||||
if (b) {
|
||||
unsigned long lcount = static_cast<unsigned long>(b->Rows().size());
|
||||
|
||||
Reference in New Issue
Block a user