Refactor indentation for consistent style across codebase.
Some checks failed
Release / Bump Homebrew formula (push) Has been cancelled
Release / Build Linux amd64 (push) Has been cancelled
Release / Build Linux arm64 (push) Has been cancelled
Release / Build macOS arm64 (.app) (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled

This commit is contained in:
2025-12-02 01:37:44 -08:00
parent d15b241140
commit 0bfe75fbf0
14 changed files with 281 additions and 271 deletions

View File

@@ -542,4 +542,4 @@ const UndoSystem *
Buffer::Undo() const
{
return undo_sys_.get();
}
}

View File

@@ -468,4 +468,4 @@ private:
std::unique_ptr<kte::HighlighterEngine> highlighter_;
};
#endif // KTE_BUFFER_H
#endif // KTE_BUFFER_H

View File

@@ -4,7 +4,7 @@ project(kte)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 17)
set(KTE_VERSION "1.2.0")
set(KTE_VERSION "1.2.1")
# Default to terminal-only build to avoid SDL/OpenGL dependency by default.
# Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available.

View File

@@ -3800,4 +3800,4 @@ Execute(Editor &ed, const std::string &name, const std::string &arg, int count)
return false;
CommandContext ctx{ed, arg, count};
return cmd->handler ? cmd->handler(ctx) : false;
}
}

View File

@@ -94,10 +94,10 @@ enum class CommandId {
// Theme by name
ThemeSetByName,
// Background mode (GUI)
BackgroundSet,
// Syntax highlighting
Syntax, // ":syntax on|off|reload"
SetOption, // generic ":set key=value" (v1: filetype=<lang>)
BackgroundSet,
// Syntax highlighting
Syntax, // ":syntax on|off|reload"
SetOption, // generic ":set key=value" (v1: filetype=<lang>)
};
@@ -151,4 +151,4 @@ bool Execute(Editor &ed, CommandId id, const std::string &arg = std::string(), i
bool Execute(Editor &ed, const std::string &name, const std::string &arg = std::string(), int count = 0);
#endif // KTE_COMMAND_H
#endif // KTE_COMMAND_H

View File

@@ -282,4 +282,4 @@ Editor::Reset()
quit_confirm_pending_ = false;
buffers_.clear();
curbuf_ = 0;
}
}

View File

@@ -102,27 +102,27 @@ GUIConfig::LoadFromFile(const std::string &path)
if (v > 0.0f) {
font_size = v;
}
} else if (key == "theme") {
theme = val;
} else if (key == "background" || key == "bg") {
std::string v = val;
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) {
return (char) std::tolower(c);
});
if (v == "light" || v == "dark")
background = v;
} else if (key == "syntax") {
std::string v = val;
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) {
return (char) std::tolower(c);
});
if (v == "1" || v == "on" || v == "true" || v == "yes") {
syntax = true;
} else if (v == "0" || v == "off" || v == "false" || v == "no") {
syntax = false;
}
}
}
} else if (key == "theme") {
theme = val;
} else if (key == "background" || key == "bg") {
std::string v = val;
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) {
return (char) std::tolower(c);
});
if (v == "light" || v == "dark")
background = v;
} else if (key == "syntax") {
std::string v = val;
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) {
return (char) std::tolower(c);
});
if (v == "1" || v == "on" || v == "true" || v == "yes") {
syntax = true;
} else if (v == "0" || v == "off" || v == "false" || v == "no") {
syntax = false;
}
}
}
return true;
}
return true;
}

View File

@@ -12,18 +12,18 @@
class GUIConfig {
public:
bool fullscreen = false;
int columns = 80;
int rows = 42;
float font_size = (float) KTE_FONT_SIZE;
std::string theme = "nord";
// Background mode for themes that support light/dark variants
// Values: "dark" (default), "light"
std::string background = "dark";
bool fullscreen = false;
int columns = 80;
int rows = 42;
float font_size = (float) KTE_FONT_SIZE;
std::string theme = "nord";
// Background mode for themes that support light/dark variants
// Values: "dark" (default), "light"
std::string background = "dark";
// Default syntax highlighting state for GUI (kge): on/off
// Accepts: on/off/true/false/yes/no/1/0 in the ini file.
bool syntax = true; // default: enabled
// Default syntax highlighting state for GUI (kge): on/off
// Accepts: on/off/true/false/yes/no/1/0 in the ini file.
bool syntax = true; // default: enabled
// Load from default path: $HOME/.config/kte/kge.ini
static GUIConfig Load();
@@ -32,4 +32,4 @@ public:
bool LoadFromFile(const std::string &path);
};
#endif // KTE_GUI_CONFIG_H
#endif // KTE_GUI_CONFIG_H

View File

@@ -318,4 +318,4 @@ GUIFrontend::LoadGuiFont_(const char * /*path*/, float size_px)
}
// No runtime font reload or system font resolution in this simplified build.
// No runtime font reload or system font resolution in this simplified build.

View File

@@ -139,29 +139,29 @@ GUIRenderer::Draw(Editor &ed)
vis_rows = 1;
long last_row = first_row + vis_rows - 1;
if (!forced_scroll) {
long cyr = static_cast<long>(cy);
if (cyr < first_row || cyr > last_row) {
float target = (static_cast<float>(cyr) - std::max(0L, vis_rows / 2)) * row_h;
float max_y = ImGui::GetScrollMaxY();
if (target < 0.f)
target = 0.f;
if (max_y >= 0.f && target > max_y)
target = max_y;
ImGui::SetScrollY(target);
// refresh local variables
scroll_y = ImGui::GetScrollY();
first_row = static_cast<long>(scroll_y / row_h);
last_row = first_row + vis_rows - 1;
}
}
// Phase 3: prefetch visible viewport highlights and warm around in background
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
int fr = static_cast<int>(std::max(0L, first_row));
int rc = static_cast<int>(std::max(1L, vis_rows));
buf->Highlighter()->PrefetchViewport(*buf, fr, rc, buf->Version());
}
}
if (!forced_scroll) {
long cyr = static_cast<long>(cy);
if (cyr < first_row || cyr > last_row) {
float target = (static_cast<float>(cyr) - std::max(0L, vis_rows / 2)) * row_h;
float max_y = ImGui::GetScrollMaxY();
if (target < 0.f)
target = 0.f;
if (max_y >= 0.f && target > max_y)
target = max_y;
ImGui::SetScrollY(target);
// refresh local variables
scroll_y = ImGui::GetScrollY();
first_row = static_cast<long>(scroll_y / row_h);
last_row = first_row + vis_rows - 1;
}
}
// Phase 3: prefetch visible viewport highlights and warm around in background
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
int fr = static_cast<int>(std::max(0L, first_row));
int rc = static_cast<int>(std::max(1L, vis_rows));
buf->Highlighter()->PrefetchViewport(*buf, fr, rc, buf->Version());
}
}
// Handle mouse click before rendering to avoid dependent on drawn items
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
ImVec2 mp = ImGui::GetIO().MousePos;
@@ -329,50 +329,56 @@ GUIRenderer::Draw(Editor &ed)
ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, col);
}
}
// Emit entire line to an expanded buffer (tabs -> spaces)
for (std::size_t src = 0; src < line.size(); ++src) {
char c = line[src];
if (c == '\t') {
std::size_t adv = (tabw - (rx_abs_draw % tabw));
expanded.append(adv, ' ');
rx_abs_draw += adv;
} else {
expanded.push_back(c);
rx_abs_draw += 1;
}
}
// Emit entire line to an expanded buffer (tabs -> spaces)
for (std::size_t src = 0; src < line.size(); ++src) {
char c = line[src];
if (c == '\t') {
std::size_t adv = (tabw - (rx_abs_draw % tabw));
expanded.append(adv, ' ');
rx_abs_draw += adv;
} else {
expanded.push_back(c);
rx_abs_draw += 1;
}
}
// Draw syntax-colored runs (text above background highlights)
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
const kte::LineHighlight &lh = buf->Highlighter()->GetLine(*buf, static_cast<int>(i), buf->Version());
// Helper to convert a src column to expanded rx position
auto src_to_rx_full = [&](std::size_t sidx) -> std::size_t {
std::size_t rx = 0;
for (std::size_t k = 0; k < sidx && k < line.size(); ++k) {
rx += (line[k] == '\t') ? (tabw - (rx % tabw)) : 1;
}
return rx;
};
for (const auto &sp: lh.spans) {
std::size_t rx_s = src_to_rx_full(static_cast<std::size_t>(std::max(0, sp.col_start)));
std::size_t rx_e = src_to_rx_full(static_cast<std::size_t>(std::max(sp.col_start, sp.col_end)));
if (rx_e <= coloffs_now)
continue;
std::size_t vx0 = (rx_s > coloffs_now) ? (rx_s - coloffs_now) : 0;
std::size_t vx1 = (rx_e > coloffs_now) ? (rx_e - coloffs_now) : 0;
if (vx0 >= expanded.size()) continue;
vx1 = std::min<std::size_t>(vx1, expanded.size());
if (vx1 <= vx0) continue;
ImU32 col = ImGui::GetColorU32(kte::SyntaxInk(sp.kind));
ImVec2 p = ImVec2(line_pos.x + static_cast<float>(vx0) * space_w, line_pos.y);
ImGui::GetWindowDrawList()->AddText(p, col, expanded.c_str() + vx0, expanded.c_str() + vx1);
}
// We drew text via draw list (no layout advance). Manually advance the cursor to the next line.
ImGui::SetCursorScreenPos(ImVec2(line_pos.x, line_pos.y + line_h));
} else {
// No syntax: draw as one run
ImGui::TextUnformatted(expanded.c_str());
}
// Draw syntax-colored runs (text above background highlights)
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
const kte::LineHighlight &lh = buf->Highlighter()->GetLine(
*buf, static_cast<int>(i), buf->Version());
// Helper to convert a src column to expanded rx position
auto src_to_rx_full = [&](std::size_t sidx) -> std::size_t {
std::size_t rx = 0;
for (std::size_t k = 0; k < sidx && k < line.size(); ++k) {
rx += (line[k] == '\t') ? (tabw - (rx % tabw)) : 1;
}
return rx;
};
for (const auto &sp: lh.spans) {
std::size_t rx_s = src_to_rx_full(
static_cast<std::size_t>(std::max(0, sp.col_start)));
std::size_t rx_e = src_to_rx_full(
static_cast<std::size_t>(std::max(sp.col_start, sp.col_end)));
if (rx_e <= coloffs_now)
continue;
std::size_t vx0 = (rx_s > coloffs_now) ? (rx_s - coloffs_now) : 0;
std::size_t vx1 = (rx_e > coloffs_now) ? (rx_e - coloffs_now) : 0;
if (vx0 >= expanded.size())
continue;
vx1 = std::min<std::size_t>(vx1, expanded.size());
if (vx1 <= vx0)
continue;
ImU32 col = ImGui::GetColorU32(kte::SyntaxInk(sp.kind));
ImVec2 p = ImVec2(line_pos.x + static_cast<float>(vx0) * space_w, line_pos.y);
ImGui::GetWindowDrawList()->AddText(
p, col, expanded.c_str() + vx0, expanded.c_str() + vx1);
}
// We drew text via draw list (no layout advance). Manually advance the cursor to the next line.
ImGui::SetCursorScreenPos(ImVec2(line_pos.x, line_pos.y + line_h));
} else {
// No syntax: draw as one run
ImGui::TextUnformatted(expanded.c_str());
}
// Draw a visible cursor indicator on the current line
if (i == cy) {
@@ -761,4 +767,4 @@ GUIRenderer::Draw(Editor &ed)
ed.SetFilePickerVisible(false);
}
}
}
}

View File

@@ -412,4 +412,4 @@ SyntaxInk(const TokenKind k)
return def;
}
}
} // namespace kte
} // namespace kte

View File

@@ -5,35 +5,33 @@
#include <vector>
namespace kte {
// Token kinds shared between renderers and highlighters
enum class TokenKind {
Default,
Keyword,
Type,
String,
Char,
Comment,
Number,
Preproc,
Constant,
Function,
Operator,
Punctuation,
Identifier,
Whitespace,
Error
Default,
Keyword,
Type,
String,
Char,
Comment,
Number,
Preproc,
Constant,
Function,
Operator,
Punctuation,
Identifier,
Whitespace,
Error
};
struct HighlightSpan {
int col_start{0}; // inclusive, 0-based columns in buffer indices
int col_end{0}; // exclusive
TokenKind kind{TokenKind::Default};
int col_start{0}; // inclusive, 0-based columns in buffer indices
int col_end{0}; // exclusive
TokenKind kind{TokenKind::Default};
};
struct LineHighlight {
std::vector<HighlightSpan> spans;
std::uint64_t version{0}; // buffer version used for this line
std::vector<HighlightSpan> spans;
std::uint64_t version{0}; // buffer version used for this line
};
} // namespace kte
} // namespace kte

View File

@@ -2,9 +2,11 @@ ROADMAP / TODO:
- [x] Search + Replace
- [x] Regex search + replace
- [ ] The undo system should actually work
- [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
- [x] Syntax highlighting
- [ ] The undo system should actually work
- [ ] LSP integration

View File

@@ -42,18 +42,18 @@ TerminalRenderer::Draw(Editor &ed)
std::size_t coloffs = buf->Coloffs();
const int tabw = 8;
// Phase 3: prefetch visible viewport highlights (current terminal area)
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
int fr = static_cast<int>(rowoffs);
int rc = std::max(0, content_rows);
buf->Highlighter()->PrefetchViewport(*buf, fr, rc, buf->Version());
}
// Phase 3: prefetch visible viewport highlights (current terminal area)
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
int fr = static_cast<int>(rowoffs);
int rc = std::max(0, content_rows);
buf->Highlighter()->PrefetchViewport(*buf, fr, rc, buf->Version());
}
for (int r = 0; r < content_rows; ++r) {
move(r, 0);
std::size_t li = rowoffs + static_cast<std::size_t>(r);
std::size_t render_col = 0;
std::size_t src_i = 0;
for (int r = 0; r < content_rows; ++r) {
move(r, 0);
std::size_t li = rowoffs + static_cast<std::size_t>(r);
std::size_t render_col = 0;
std::size_t src_i = 0;
// Compute matches for this line if search highlighting is active
bool search_mode = ed.SearchActive() && !ed.SearchQuery().empty();
std::vector<std::pair<std::size_t, std::size_t> > ranges; // [start, end)
@@ -105,49 +105,53 @@ TerminalRenderer::Draw(Editor &ed)
bool hl_on = false;
bool cur_on = false;
int written = 0;
if (li < lines.size()) {
std::string line = static_cast<std::string>(lines[li]);
src_i = 0;
render_col = 0;
// Syntax highlighting: fetch per-line spans
const kte::LineHighlight *lh_ptr = nullptr;
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->HasHighlighter()) {
lh_ptr = &buf->Highlighter()->GetLine(*buf, static_cast<int>(li), buf->Version());
}
auto token_at = [&](std::size_t src_index) -> kte::TokenKind {
if (!lh_ptr) return kte::TokenKind::Default;
for (const auto &sp: lh_ptr->spans) {
if (static_cast<int>(src_index) >= sp.col_start && static_cast<int>(src_index) < sp.col_end)
return sp.kind;
}
return kte::TokenKind::Default;
};
auto apply_token_attr = [&](kte::TokenKind k) {
// Map to simple attributes; search highlight uses A_STANDOUT which takes precedence below
attrset(A_NORMAL);
switch (k) {
case kte::TokenKind::Keyword:
case kte::TokenKind::Type:
case kte::TokenKind::Constant:
case kte::TokenKind::Function:
attron(A_BOLD);
break;
case kte::TokenKind::Comment:
attron(A_DIM);
break;
case kte::TokenKind::String:
case kte::TokenKind::Char:
case kte::TokenKind::Number:
// standout a bit using A_UNDERLINE if available
attron(A_UNDERLINE);
break;
default:
break;
}
};
while (written < cols) {
char ch = ' ';
bool from_src = false;
if (li < lines.size()) {
std::string line = static_cast<std::string>(lines[li]);
src_i = 0;
render_col = 0;
// Syntax highlighting: fetch per-line spans
const kte::LineHighlight *lh_ptr = nullptr;
if (buf->SyntaxEnabled() && buf->Highlighter() && buf->Highlighter()->
HasHighlighter()) {
lh_ptr = &buf->Highlighter()->GetLine(
*buf, static_cast<int>(li), buf->Version());
}
auto token_at = [&](std::size_t src_index) -> kte::TokenKind {
if (!lh_ptr)
return kte::TokenKind::Default;
for (const auto &sp: lh_ptr->spans) {
if (static_cast<int>(src_index) >= sp.col_start && static_cast<int>(
src_index) < sp.col_end)
return sp.kind;
}
return kte::TokenKind::Default;
};
auto apply_token_attr = [&](kte::TokenKind k) {
// Map to simple attributes; search highlight uses A_STANDOUT which takes precedence below
attrset(A_NORMAL);
switch (k) {
case kte::TokenKind::Keyword:
case kte::TokenKind::Type:
case kte::TokenKind::Constant:
case kte::TokenKind::Function:
attron(A_BOLD);
break;
case kte::TokenKind::Comment:
attron(A_DIM);
break;
case kte::TokenKind::String:
case kte::TokenKind::Char:
case kte::TokenKind::Number:
// standout a bit using A_UNDERLINE if available
attron(A_UNDERLINE);
break;
default:
break;
}
};
while (written < cols) {
char ch = ' ';
bool from_src = false;
if (src_i < line.size()) {
unsigned char c = static_cast<unsigned char>(line[src_i]);
if (c == '\t') {
@@ -166,45 +170,45 @@ TerminalRenderer::Draw(Editor &ed)
next_tab -= to_skip;
}
// Now render visible spaces
while (next_tab > 0 && written < cols) {
bool in_hl = search_mode && is_src_in_hl(src_i);
bool in_cur =
has_current && li == cur_my && src_i >= cur_mx
&& src_i < cur_mend;
// Toggle highlight attributes
int attr = 0;
if (in_hl)
attr |= A_STANDOUT;
if (in_cur)
attr |= A_BOLD;
if ((attr & A_STANDOUT) && !hl_on) {
attron(A_STANDOUT);
hl_on = true;
}
if (!(attr & A_STANDOUT) && hl_on) {
attroff(A_STANDOUT);
hl_on = false;
}
if ((attr & A_BOLD) && !cur_on) {
attron(A_BOLD);
cur_on = true;
}
if (!(attr & A_BOLD) && cur_on) {
attroff(A_BOLD);
cur_on = false;
}
// Apply syntax attribute only if not in search highlight
if (!in_hl) {
apply_token_attr(token_at(src_i));
}
addch(' ');
++written;
++render_col;
--next_tab;
}
++src_i;
continue;
} else {
while (next_tab > 0 && written < cols) {
bool in_hl = search_mode && is_src_in_hl(src_i);
bool in_cur =
has_current && li == cur_my && src_i >= cur_mx
&& src_i < cur_mend;
// Toggle highlight attributes
int attr = 0;
if (in_hl)
attr |= A_STANDOUT;
if (in_cur)
attr |= A_BOLD;
if ((attr & A_STANDOUT) && !hl_on) {
attron(A_STANDOUT);
hl_on = true;
}
if (!(attr & A_STANDOUT) && hl_on) {
attroff(A_STANDOUT);
hl_on = false;
}
if ((attr & A_BOLD) && !cur_on) {
attron(A_BOLD);
cur_on = true;
}
if (!(attr & A_BOLD) && cur_on) {
attroff(A_BOLD);
cur_on = false;
}
// Apply syntax attribute only if not in search highlight
if (!in_hl) {
apply_token_attr(token_at(src_i));
}
addch(' ');
++written;
++render_col;
--next_tab;
}
++src_i;
continue;
} else {
// normal char
if (render_col < coloffs) {
++render_col;
@@ -219,49 +223,49 @@ TerminalRenderer::Draw(Editor &ed)
ch = ' ';
from_src = false;
}
bool in_hl = search_mode && from_src && is_src_in_hl(src_i);
bool in_cur =
has_current && li == cur_my && from_src && src_i >= cur_mx && src_i <
cur_mend;
if (in_hl && !hl_on) {
attron(A_STANDOUT);
hl_on = true;
}
if (!in_hl && hl_on) {
attroff(A_STANDOUT);
hl_on = false;
}
if (in_cur && !cur_on) {
attron(A_BOLD);
cur_on = true;
}
if (!in_cur && cur_on) {
attroff(A_BOLD);
cur_on = false;
}
if (!in_hl && from_src) {
apply_token_attr(token_at(src_i));
}
addch(static_cast<unsigned char>(ch));
++written;
++render_col;
if (from_src)
++src_i;
bool in_hl = search_mode && from_src && is_src_in_hl(src_i);
bool in_cur =
has_current && li == cur_my && from_src && src_i >= cur_mx && src_i <
cur_mend;
if (in_hl && !hl_on) {
attron(A_STANDOUT);
hl_on = true;
}
if (!in_hl && hl_on) {
attroff(A_STANDOUT);
hl_on = false;
}
if (in_cur && !cur_on) {
attron(A_BOLD);
cur_on = true;
}
if (!in_cur && cur_on) {
attroff(A_BOLD);
cur_on = false;
}
if (!in_hl && from_src) {
apply_token_attr(token_at(src_i));
}
addch(static_cast<unsigned char>(ch));
++written;
++render_col;
if (from_src)
++src_i;
if (src_i >= line.size() && written >= cols)
break;
}
}
if (hl_on) {
attroff(A_STANDOUT);
hl_on = false;
}
if (cur_on) {
attroff(A_BOLD);
cur_on = false;
}
attrset(A_NORMAL);
clrtoeol();
}
if (hl_on) {
attroff(A_STANDOUT);
hl_on = false;
}
if (cur_on) {
attroff(A_BOLD);
cur_on = false;
}
attrset(A_NORMAL);
clrtoeol();
}
// Place terminal cursor at logical position accounting for tabs and coloffs
std::size_t cy = buf->Cury();
@@ -464,4 +468,4 @@ TerminalRenderer::Draw(Editor &ed)
}
refresh();
}
}