9 Commits

Author SHA1 Message Date
f9128a336d better support for smaller screens
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
editor_prompt should replace the status line when active
2025-11-30 20:25:53 -08:00
f8d0e9213f bump version
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
Also add some TODOs.
2025-11-30 19:57:05 -08:00
68286ecb7c Add icon to share/icons for Linux. 2025-11-30 19:55:03 -08:00
44807d0f40 clang-tidy was zealous on macOS. 2025-11-30 19:47:43 -08:00
41f37478c1 bump cmake version
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
2025-11-30 19:09:35 -08:00
d582979eb3 release maybe 2025-11-30 19:08:34 -08:00
2b194c7910 Actually add the screenshot. 2025-11-30 18:55:55 -08:00
6498213378 updating readme and roadmap 2025-11-30 18:54:24 -08:00
1a37a92534 add screenshot to README 2025-11-30 18:45:11 -08:00
11 changed files with 368 additions and 296 deletions

View File

@@ -9,6 +9,9 @@ on:
permissions: permissions:
contents: write contents: write
env:
BUILD_TYPE: Release
jobs: jobs:
homebrew: homebrew:
name: Bump Homebrew formula name: Bump Homebrew formula
@@ -34,4 +37,115 @@ jobs:
Created by https://github.com/mislav/bump-homebrew-formula-action Created by https://github.com/mislav/bump-homebrew-formula-action
env: env:
COMMITTER_TOKEN: ${{ secrets.GH_CPAT }} COMMITTER_TOKEN: ${{ secrets.GH_CPAT }}
linux-build:
name: Build Linux ${{ matrix.arch }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
- arch: arm64
runner: ubuntu-24.04-arm64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake pkg-config \
libncurses5-dev libncursesw5-dev \
libsdl2-dev libfreetype6-dev mesa-common-dev
- name: Configure (CMake, GUI ON)
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_GUI=ON
- name: Build
run: |
cmake --build build --config ${BUILD_TYPE} -j
- name: Prepare dist
run: |
mkdir -p dist/linux-${{ matrix.arch }}
cp build/kte dist/linux-${{ matrix.arch }}/
cp build/kge dist/linux-${{ matrix.arch }}/
strip dist/linux-${{ matrix.arch }}/kte || true
strip dist/linux-${{ matrix.arch }}/kge || true
- name: Upload artifact (linux-${{ matrix.arch }})
uses: actions/upload-artifact@v4
with:
name: linux-${{ matrix.arch }}
path: dist/linux-${{ matrix.arch }}/*
macos-build:
name: Build macOS arm64 (.app)
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install deps (brew)
run: |
brew update
brew install cmake ncurses sdl2 freetype
- name: Configure (CMake, GUI ON, arm64)
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_GUI=ON -DCMAKE_OSX_ARCHITECTURES=arm64
- name: Build
run: |
cmake --build build --config ${BUILD_TYPE} -j
- name: Zip kge.app
run: |
mkdir -p dist/macos-arm64
cd build
ditto -c -k --sequesterRsrc --keepParent kge.app ../dist/macos-arm64/kge.app.zip
- name: Upload artifact (macos-arm64)
uses: actions/upload-artifact@v4
with:
name: macos-arm64
path: dist/macos-arm64/kge.app.zip
release:
name: Create GitHub Release
needs: [ linux-build, macos-build ]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: dist
- name: Reshape artifact layout
run: |
ls -R dist
# Actions download-artifact places each named artifact in a subfolder
# Move into the expected dist structure for GoReleaser
mkdir -p dist/linux-amd64 dist/linux-arm64 dist/macos-arm64
if [ -d dist/linux-amd64/linux-amd64 ]; then mv dist/linux-amd64/linux-amd64/* dist/linux-amd64/; fi
if [ -d dist/linux-arm64/linux-arm64 ]; then mv dist/linux-arm64/linux-arm64/* dist/linux-arm64/; fi
if [ -d dist/macos-arm64/macos-arm64 ]; then mv dist/macos-arm64/macos-arm64/* dist/macos-arm64/; fi
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22.x'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean --config .goreleaser.yaml
env:
GITHUB_TOKEN: ${{ secrets.GH_CPAT }}

69
.goreleaser.yaml Normal file
View File

@@ -0,0 +1,69 @@
# GoReleaser configuration for kte/kge (C++ project)
# We use GoReleaser only for releasing: changelog, checksums, and uploading
# prebuilt artifacts that are produced by the CI workflow.
version: 2
project_name: kte
before:
hooks:
# No build here; artifacts are produced by the CI jobs and placed into dist/
- echo "GoReleaser: using prebuilt artifacts from dist/"
builds:
# No Go builds; this is a C++ project.
- id: noop
skip: true
checksum:
name_template: "checksums.txt"
algorithm: sha256
release:
# Rely on GITHUB_TOKEN from the workflow.
draft: false
prerelease: auto
mode: replace
footer: |
Built with CMake. See README for platform dependencies.
extra_files:
# Linux binaries (amd64, arm64)
- glob: dist/linux-amd64/kte
- glob: dist/linux-amd64/kge
- glob: dist/linux-arm64/kte
- glob: dist/linux-arm64/kge
# macOS Apple Silicon app bundle (zipped)
- glob: dist/macos-arm64/kge.app.zip
changelog:
sort: asc
use: github
filters:
exclude:
- '^docs: '
- '^chore: '
- '^ci: '
announce:
skip: true
signs:
# No signing by default.
- artifacts: none
archives:
# We are uploading raw binaries / zip created by CI, so no archives here.
- id: none
formats: [binary]
builds: [noop]
blobs: []
brews: []
snapcrafts: []
nfpm: []
publishers: []

37
.idea/workspace.xml generated
View File

@@ -33,10 +33,9 @@
</configurations> </configurations>
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup."> <list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Actually add the screenshot.">
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.github/workflows/release.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.github/workflows/release.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/kte.1" beforeDir="false" afterPath="$PROJECT_DIR$/docs/kte.1" 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" />
@@ -51,6 +50,12 @@
<option name="myRunOnSave" value="true" /> <option name="myRunOnSave" value="true" />
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="PUSH_TAGS">
<GitPushTagMode>
<option name="argument" value="--tags" />
<option name="title" value="All" />
</GitPushTagMode>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="UPDATE_TYPE" value="REBASE" /> <option name="UPDATE_TYPE" value="REBASE" />
</component> </component>
@@ -164,7 +169,7 @@
<workItem from="1764539556448" duration="156000" /> <workItem from="1764539556448" duration="156000" />
<workItem from="1764539725338" duration="1075000" /> <workItem from="1764539725338" duration="1075000" />
<workItem from="1764542392763" duration="3512000" /> <workItem from="1764542392763" duration="3512000" />
<workItem from="1764548345516" duration="8203000" /> <workItem from="1764548345516" duration="9962000" />
</task> </task>
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions."> <task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -262,7 +267,23 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1764556512864</updated> <updated>1764556512864</updated>
</task> </task>
<option name="localTasksCounter" value="13" /> <task id="LOCAL-00013" summary="Add buffer position display and documentation improvements.&#10;&#10;- Display buffer position prefix &quot;[x/N]&quot; in GUI and terminal renderers.&#10;- Improve `kte` and `kge` man pages with frontend usage details and project homepage.&#10;- Update README with GUI invocation instructions.&#10;- Bump version to 1.0.0.">
<option name="closed" value="true" />
<created>1764556854788</created>
<option name="number" value="00013" />
<option name="presentableId" value="LOCAL-00013" />
<option name="project" value="LOCAL" />
<updated>1764556854788</updated>
</task>
<task id="LOCAL-00014" summary="Actually add the screenshot.">
<option name="closed" value="true" />
<created>1764557759844</created>
<option name="number" value="00014" />
<option name="presentableId" value="LOCAL-00014" />
<option name="project" value="LOCAL" />
<updated>1764557759844</updated>
</task>
<option name="localTasksCounter" value="15" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -288,7 +309,9 @@
<MESSAGE value="Refactor code for consistency and enhanced functionality.&#10;&#10;- Normalize path handling for buffer operations, supporting tilde expansion and absolute paths.&#10;- Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes.&#10;- Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`.&#10;- Refine keybindings and enhance existing commands for improved command flow.&#10;- Adjust GUI and terminal renderers to display total line counts alongside filenames.&#10;- Update coding style to align with project guidelines." /> <MESSAGE value="Refactor code for consistency and enhanced functionality.&#10;&#10;- Normalize path handling for buffer operations, supporting tilde expansion and absolute paths.&#10;- Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes.&#10;- Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`.&#10;- Refine keybindings and enhance existing commands for improved command flow.&#10;- Adjust GUI and terminal renderers to display total line counts alongside filenames.&#10;- Update coding style to align with project guidelines." />
<MESSAGE value="Add horizontal scrolling support and refactor mouse click handling in GUI.&#10;&#10;- Introduce horizontal scrolling with column offset synchronization in GUI.&#10;- Refactor mouse click handling for improved accuracy and viewport alignment.&#10;- Enhance tab expansion and cursor rendering logic for better user experience.&#10;- Replace redundant variable declarations in `Buffer` for cleaner code." /> <MESSAGE value="Add horizontal scrolling support and refactor mouse click handling in GUI.&#10;&#10;- Introduce horizontal scrolling with column offset synchronization in GUI.&#10;- Refactor mouse click handling for improved accuracy and viewport alignment.&#10;- Enhance tab expansion and cursor rendering logic for better user experience.&#10;- Replace redundant variable declarations in `Buffer` for cleaner code." />
<MESSAGE value="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup." /> <MESSAGE value="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup." />
<option name="LAST_COMMIT_MESSAGE" value="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup." /> <MESSAGE value="Add buffer position display and documentation improvements.&#10;&#10;- Display buffer position prefix &quot;[x/N]&quot; in GUI and terminal renderers.&#10;- Improve `kte` and `kge` man pages with frontend usage details and project homepage.&#10;- Update README with GUI invocation instructions.&#10;- Bump version to 1.0.0." />
<MESSAGE value="Actually add the screenshot." />
<option name="LAST_COMMIT_MESSAGE" value="Actually add the screenshot." />
</component> </component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />

View File

@@ -5,6 +5,7 @@
#define KTE_BUFFER_H #define KTE_BUFFER_H
#include <cstddef> #include <cstddef>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <string_view> #include <string_view>

View File

@@ -4,7 +4,7 @@ project(kte)
include(GNUInstallDirs) include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(KTE_VERSION "1.0.0") set(KTE_VERSION "1.0.3")
# Default to terminal-only build to avoid SDL/OpenGL dependency by default. # Default to terminal-only build to avoid SDL/OpenGL dependency by default.
# Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available. # Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available.
@@ -180,9 +180,20 @@ if (${BUILD_GUI})
MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE}
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist") MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist")
add_dependencies(kge kte)
add_custom_command(TARGET kge POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:kte>
$<TARGET_FILE_DIR:kge>/kte
COMMENT "Copying kte binary into kge.app bundle")
install(TARGETS kge install(TARGETS kge
BUNDLE DESTINATION . BUNDLE DESTINATION .
) )
install(TARGETS kte
RUNTIME DESTINATION kge.app/Contents/MacOS
)
else () else ()
install(TARGETS kge install(TARGETS kge
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
@@ -190,4 +201,5 @@ if (${BUILD_GUI})
endif () endif ()
# Install kge man page only when GUI is built # Install kge man page only when GUI is built
install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
install(FILES kge.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons)
endif () endif ()

View File

@@ -2,6 +2,7 @@
#include <cstdio> #include <cstdio>
#include <filesystem> #include <filesystem>
#include <limits> #include <limits>
#include <cstdlib>
#include <string> #include <string>
#include <imgui.h> #include <imgui.h>
@@ -280,28 +281,59 @@ GUIRenderer::Draw(Editor &ed)
} }
ImGui::EndChild(); ImGui::EndChild();
// Status bar spanning full width // Status bar spanning full width
ImGui::Separator(); ImGui::Separator();
// Build three segments: left (app/version/buffer/dirty), middle (message), right (cursor/mark) // Compute full content width and draw a filled background rectangle
// Compute full content width and draw a filled background rectangle ImVec2 win_pos = ImGui::GetWindowPos();
ImVec2 win_pos = ImGui::GetWindowPos(); ImVec2 cr_min = ImGui::GetWindowContentRegionMin();
ImVec2 cr_min = ImGui::GetWindowContentRegionMin(); ImVec2 cr_max = ImGui::GetWindowContentRegionMax();
ImVec2 cr_max = ImGui::GetWindowContentRegionMax(); float x0 = win_pos.x + cr_min.x;
float x0 = win_pos.x + cr_min.x; float x1 = win_pos.x + cr_max.x;
float x1 = win_pos.x + cr_max.x; ImVec2 cursor = ImGui::GetCursorScreenPos();
ImVec2 cursor = ImGui::GetCursorScreenPos(); float bar_h = ImGui::GetFrameHeight();
float bar_h = ImGui::GetFrameHeight(); ImVec2 p0(x0, cursor.y);
ImVec2 p0(x0, cursor.y); ImVec2 p1(x1, cursor.y + bar_h);
ImVec2 p1(x1, cursor.y + bar_h); ImU32 bg_col = ImGui::GetColorU32(ImGuiCol_HeaderActive);
ImU32 bg_col = ImGui::GetColorU32(ImGuiCol_HeaderActive); ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, bg_col);
ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, bg_col); // If a prompt is active, replace the entire status bar with the prompt text
// Build left text if (ed.PromptActive()) {
std::string left; std::string msg = ed.PromptLabel();
left.reserve(256); if (!msg.empty()) msg += ": ";
left += "kge"; // GUI app name std::string ptext = ed.PromptText();
left += " "; auto kind = ed.CurrentPromptKind();
left += KTE_VERSION_STR; if (kind == Editor::PromptKind::OpenFile || kind == Editor::PromptKind::SaveAs ||
kind == Editor::PromptKind::Chdir) {
const char *home_c = std::getenv("HOME");
if (home_c && *home_c) {
std::string home(home_c);
if (ptext.rfind(home, 0) == 0) {
std::string rest = ptext.substr(home.size());
if (rest.empty())
ptext = "~";
else if (!rest.empty() && (rest[0] == '/' || rest[0] == '\\'))
ptext = std::string("~") + rest;
}
}
}
msg += ptext;
float pad = 6.f;
ImVec2 msg_sz = ImGui::CalcTextSize(msg.c_str());
float left_x = p0.x + pad;
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::PopClipRect();
// Advance cursor to after the bar to keep layout consistent
ImGui::Dummy(ImVec2(x1 - x0, bar_h));
} else {
// Build left text
std::string left;
left.reserve(256);
left += "kge"; // GUI app name
left += " ";
left += KTE_VERSION_STR;
std::string fname; std::string fname;
try { try {
fname = ed.DisplayNameFor(*buf); fname = ed.DisplayNameFor(*buf);
@@ -400,8 +432,9 @@ GUIRenderer::Draw(Editor &ed)
ImGui::PopClipRect(); ImGui::PopClipRect();
} }
} }
// Advance cursor to after the bar to keep layout consistent // Advance cursor to after the bar to keep layout consistent
ImGui::Dummy(ImVec2(x1 - x0, bar_h)); ImGui::Dummy(ImVec2(x1 - x0, bar_h));
}
} }
ImGui::End(); ImGui::End();

161
README.md
View File

@@ -1,4 +1,6 @@
kte Kyle's Text Editor kte - Kyle's Text Editor
![Editor screenshot](./docs/screenshot.jpg)
Vision Vision
------- -------
@@ -11,7 +13,9 @@ can learn and keep in your head.
I am experimenting with using Jetbrains Junie to assist in I am experimenting with using Jetbrains Junie to assist in
development, largely as a way to learn the effective use of agentic development, largely as a way to learn the effective use of agentic
coding. coding. I worked with the agent by feeding it notes that I've been
taking about text editors for the last few years, as well as the
sources from the original ke editor that is all handwritten C.
Project Goals Project Goals
------------- -------------
@@ -26,98 +30,6 @@ Project Goals
so a GUI can grow independently of the TUI. so a GUI can grow independently of the TUI.
- Minimize dependencies; the GUI layer remains optional and isolated. - Minimize dependencies; the GUI layer remains optional and isolated.
User Experience (intended)
--------------------------
- Terminal first: instant startup, responsive editing, no surprises
over SSH.
- Optional GUI: an ImGuibased window with tabs, menus, and
palette—sharing the same editor core and command model.
- Discoverable command model: WordStar/VDE style with a `C-k` prefix,
Emacslike incremental search, and context help.
- Sensible defaults with a simple config file for remaps and theme
selection.
- Respect the file system: no magic project files; autosave and
crashrecovery journals are optin and visible.
Core Features (roadmapped)
--------------------------
- Buffers and windows
- Multiple file buffers; fast switching, closing, and reopening.
- Split views (horizontal/vertical) in TUI and tiled panels in
GUI.
- Editing primitives
- Gap buffer (primary) with an alternative piece table for
largeedit scenarios.
- Kill/yank ring, word/sentence/paragraph motions, and rectangle
ops.
- Undo/redo with grouped edits and timetravel scrubbing.
- Search and replace
- Incremental search (C-s) and regex search (C-r) with live
highlighting.
- Multifile grep with a quickfix list; replace with confirm.
- Files and projects
- Robust encoding/lineending detection; safe writes (atomic where
possible).
- File tree sidebar (GUI) and quickopen palette.
- Lightweight session restore.
- Language niceties (optin, no runtime servers required)
- Syntax highlighting via fast, tabledriven lexers.
- Basic indentation rules per language; trailing whitespace/EOF
newline helpers.
- Extensibility (later)
- Command palette actions backed by the core command model.
- Small C++ plugin ABI and a scripting shim for configtime
customization.
Interfaces
----------
- CLI: the primary interface. `kte [files]` starts in the terminal,
adopting your `$TERM` capabilities. Terminal mode is implemented
using ncurses.
- GUI: an optional ImGuibased frontend that embeds the same editor
core.
Man pages
---------
- Terminal editor: `docs/kte.1` (view locally with `man -l docs/kte.1`)
- GUI frontend: `docs/kge.1` (view locally with `man -l docs/kge.1`)
The `ke` keybinding reference remains the canonical source for
commands while kte evolves: see `docs/ke.md`.
Architecture (intended)
-----------------------
- Core model
- Buffer: file I/O, cursor/mark, viewport state, and edit
operations.
- GapBuffer: fast inmemory text structure for typical edits.
- PieceTable: alternative representation for heavy insert/delete
workflows.
- Controller layer
- InputHandler interface with `TerminalInputHandler` and
`GUIInputHandler` implementations.
- Command: normalized operations (save, kill, yank, move, search,
etc.).
- View layer
- Renderer interface with `TerminalRenderer` and `GUIRenderer`
implementations.
- Editor: toplevel state managing buffers, messaging, and global
flags.
Performance and Reliability Targets
-----------------------------------
- Submillisecond keystroke to screen update on typical files in TUI.
- Sustain fluid editing on multimegabyte files; graceful degradation
on very large files.
- Atomic/safe writes; autosave and crashrecovery journals are
explicit and transparent.
Keybindings Keybindings
----------- -----------
kte maintains kes command model while internals evolve. Highlights (subject to refinement): kte maintains kes command model while internals evolve. Highlights (subject to refinement):
@@ -146,26 +58,26 @@ Dependencies by platform
------------------------ ------------------------
- macOS (Homebrew) - macOS (Homebrew)
- Terminal (default): - Terminal (default):
- `brew install ncurses` - `brew install ncurses`
- Optional GUI (enable with `-DBUILD_GUI=ON`): - Optional GUI (enable with `-DBUILD_GUI=ON`):
- `brew install sdl2 freetype` - `brew install sdl2 freetype`
- OpenGL is provided by the system framework on macOS; no package needed. - OpenGL is provided by the system framework on macOS; no package needed.
- Debian/Ubuntu - Debian/Ubuntu
- Terminal (default): - Terminal (default):
- `sudo apt-get install -y libncurses5-dev libncursesw5-dev` - `sudo apt-get install -y libncurses5-dev libncursesw5-dev`
- Optional GUI (enable with `-DBUILD_GUI=ON`): - Optional GUI (enable with `-DBUILD_GUI=ON`):
- `sudo apt-get install -y libsdl2-dev libfreetype6-dev mesa-common-dev` - `sudo apt-get install -y libsdl2-dev libfreetype6-dev mesa-common-dev`
- The `mesa-common-dev` package provides OpenGL headers/libs (`libGL`). - The `mesa-common-dev` package provides OpenGL headers/libs (`libGL`).
- NixOS/Nix - NixOS/Nix
- Terminal (default): - Terminal (default):
- Ad-hoc shell: `nix-shell -p cmake gcc ncurses` - Ad-hoc shell: `nix-shell -p cmake gcc ncurses`
- Optional GUI (enable with `-DBUILD_GUI=ON`): - Optional GUI (enable with `-DBUILD_GUI=ON`):
- Ad-hoc shell: `nix-shell -p cmake gcc ncurses SDL2 freetype libGL` - Ad-hoc shell: `nix-shell -p cmake gcc ncurses SDL2 freetype libGL`
- With flakes/devshell (example `flake.nix` inputs not provided): include - With flakes/devshell (example `flake.nix` inputs not provided): include
`ncurses` for TUI, and `SDL2`, `freetype`, `libGL` for GUI in your devShell. `ncurses` for TUI, and `SDL2`, `freetype`, `libGL` for GUI in your devShell.
Notes Notes
----- -----
@@ -212,30 +124,5 @@ cmake --build cmake-build-debug
Status Status
------ ------
- The project is under active evolution toward the above architecture - This project is a hobby text editor meant to be my personal editor. I
and UX. The terminal interface now uses ncurses for input and do not warrant its suitability for anyone else.
rendering. GUI work will follow as a thin, optional layer. ke
compatibility remains a primary constraint while internals modernize.
Roadmap (high level)
--------------------
1. Solidify core buffer model (gap buffer), file I/O, and
kecompatible commands.
2. Introduce structured undo/redo and search/replace with
highlighting.
3. Stabilize terminal renderer and input handling across common
terminals. (initial ncurses implementation landed)
4. Add piece table as an alternative backend with runtime selection
per buffer.
5. Optional GUI frontend using ImGui; shared command palette.
6. Language niceties (syntax highlighting, indentation rules) behind a
zerodeps, fast path.
7. Session restore, autosave/journaling, and safe write guarantees.
8. Extensibility hooks with a small, stable API.
References
----------
- [ke](https://git.wntrmute.dev/kyle/ke) manual and keybinding
reference: `ke.md`
- Inspirations: Antirez kilo, WordStar/VDE, Emacs, and `mg(1)`

View File

@@ -1,116 +1,10 @@
kte ROADMAP — from skeleton to a working editor ROADMAP / TODO:
Scope for “working editor” v0.1 - [ ] Search + Replace
- [ ] Regex search + replace
- Runs in a terminal; opens files passed on the CLI or an empty buffer. - [ ] The undo system should actually work
- Basic navigation, insert/delete, newline handling. - [ ] Able to mark buffers as read-only
- Status line and message area; shows filename, dirty flag, cursor position. - [ ] Built-in help text
- Save file(s) to disk safely; quit/confirm on dirty buffers. - [ ] Shorten paths in the homedir with ~
- Core ke key chords: C-g (cancel), C-k s/x/q/C-q, C-l, basic arrows, Enter/Backspace, C-s (simple find). - [ ] When the filename is longer than the message window, scoot left to
to keep it in view
Guiding principles
- Keep the core small and understandable; evolve incrementally.
- Separate model (Buffer/Editor), control (Input/Command), and view (Renderer).
- Favor terminal first; GUI hooks arrive later behind interfaces.
✓ Milestone 0 — Wire up a minimal app shell
1. main.cpp
- Replace demo printing with real startup using `Editor`.
- Parse CLI args; open each path into a buffer (create empty if none). ✓ when `kte file1 file2` loads buffers and
exits cleanly.
2. Editor integration
- Ensure `Editor` can open/switch/close buffers and hold status messages.
- Add a temporary “headless loop” to prove open/save calls work.
✓ Milestone 1 — Command model
1. Command vocabulary
- Flesh out `Command.h/.cpp`: enums/struct for operations and data (e.g., InsertChar, MoveCursor, Save, Quit,
FindNext, etc.).
- Provide a dispatcher entry point callable from the input layer to mutate `Editor`/`Buffer`.
- Definition of done: commands exist for minimal edit/navigation/save/quit; no rendering yet.
✓ Milestone 2 — Terminal input
1. Input interfaces
- Add `InputHandler.h` interface plus `TerminalInputHandler` implementation.
- Terminal input via ncurses (`getch`, `keypad`, nonblocking with `nodelay`), basic key decoding (arrows, Ctrl, ESC
sequences).
2. Keymap
- Map ke chords to `Command` (C-k prefix handling, C-g cancel, C-l refresh, C-k s/x/q/C-q, C-s find start, text
input → InsertChar).
3. Event loop
- Introduce the core loop in main: read key → translate to `Command` → dispatch → trigger render.
✓ Milestone 3 — Terminal renderer
1. View interfaces
- Add `Renderer.h` with `TerminalRenderer` implementation (ncursesbased).
2. Minimal draw
- Render viewport lines from current buffer; draw status bar (filename, dirty, row:col, message).
- Handle scrolling when cursor moves past edges; support window resize (SIGWINCH).
3. Cursor
- Place terminal cursor at logical buffer location (account for tabs later; start with plain text).
Milestone 4 — Buffer fundamentals to support editing
1. GapBuffer
- Ensure `GapBuffer` supports insert char, backspace, delete, newline, and efficient cursor moves.
2. Buffer API
- File I/O (open/save), dirty tracking, encoding/line ending kept simple (UTF8, LF) for v0.1.
- Cursor state, mark (optional later), and viewport bookkeeping.
3. Basic motions
- Left/Right/Up/Down, Home/End, PageUp/PageDown; word f/b (optional in v0.1).
Milestone 5 — Core editing loop complete
1. Tighten loop timing
- Ensure keystroke→update→render latency is reliably low; avoid unnecessary redraws.
2. Status/messages
- `Editor::SetStatus()` shows transient messages; C-l forces full refresh.
3. Prompts
- Minimal prompt line for saveas/confirm quit; blocking read in prompt mode is acceptable for v0.1.
Milestone 6 — Search (minimal)
1. Incremental search (C-s)
- Simple forward substring search with live highlight of current match; arrow keys navigate matches while in search
mode (kestyle quirk acceptable).
- ESC/C-g exits search; Enter confirms and leaves cursor on match.
Milestone 7 — Safety and polish for v0.1
1. Safe writes
- Write to temp file then rename; preserve permissions where possible.
2. Dirty/quit logic
- Confirm on quit when any buffer is dirty; `C-k C-q` bypasses confirmation.
3. Resize/terminal quirks
- Handle small terminals gracefully; no crashes on narrow widths.
4. Basic tests
- Unit tests for `GapBuffer`, Buffer open/save roundtrip, and command mapping.
Out of scope for v0.1 (tracked, not blocking)
- Undo/redo, regex search, kill ring, word motions, tabs/render width, syntax highlighting, piece table selection, GUI.
Implementation notes (files to add)
- Input: `InputHandler.h`, `TerminalInputHandler.cc/h` (ncurses).
- Rendering: `Renderer.h`, `TerminalRenderer.cc/h` (ncurses).
- Prompt helpers: minimal utility for line input in raw mode.
- Platform: small termios wrapper; SIGWINCH handler.
Acceptance checklist for v0.1
- Start: `./kte [files]` opens files or an empty buffer.
- Edit: insert text, backspace, newlines; move cursor; content scrolls.
- Save: `C-k s` writes file atomically; dirty flag clears; status shows bytes written.
- Quit: `C-k q` confirms if dirty; `C-k C-q` exits without confirm; `C-k x` save+exit.
- Refresh: `C-l` redraws.
- Search: `C-s` finds next while typing; ESC cancels.
Next concrete step
- Stabilize cursor placement and scrolling logic; add resize handling and begin minimal prompt for saveas.

View File

@@ -1,6 +1,7 @@
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
#include <filesystem> #include <filesystem>
#include <cstdlib>
#include <ncurses.h> #include <ncurses.h>
#include <string> #include <string>
@@ -149,13 +150,50 @@ TerminalRenderer::Draw(Editor &ed)
mvaddstr(0, 0, "[no buffer]"); mvaddstr(0, 0, "[no buffer]");
} }
// Status line (inverse) — left: app/version/buffer/dirty, middle: message, right: cursor/mark // Status line (inverse)
move(rows - 1, 0); move(rows - 1, 0);
attron(A_REVERSE); attron(A_REVERSE);
// Fill the status line with spaces first // Fill the status line with spaces first
for (int i = 0; i < cols; ++i) for (int i = 0; i < cols; ++i)
addch(' '); addch(' ');
// 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 ptext = ed.PromptText();
auto kind = ed.CurrentPromptKind();
if (kind == Editor::PromptKind::OpenFile || kind == Editor::PromptKind::SaveAs ||
kind == Editor::PromptKind::Chdir) {
const char *home_c = std::getenv("HOME");
if (home_c && *home_c) {
std::string home(home_c);
// Ensure we match only at the start
if (ptext.rfind(home, 0) == 0) {
std::string rest = ptext.substr(home.size());
if (rest.empty())
ptext = "~";
else if (rest[0] == '/' || rest[0] == '\\')
ptext = std::string("~") + rest;
}
}
}
msg += ptext;
// Draw left-aligned, clipped to width
if (!msg.empty())
mvaddnstr(rows - 1, 0, msg.c_str(), std::max(0, cols));
// End status rendering for prompt mode
attroff(A_REVERSE);
// Restore logical cursor position in content area
if (saved_cur_y >= 0 && saved_cur_x >= 0)
move(saved_cur_y, saved_cur_x);
return;
}
// Build left segment // Build left segment
std::string left; std::string left;
@@ -243,10 +281,10 @@ TerminalRenderer::Draw(Editor &ed)
if (llen > 0) if (llen > 0)
mvaddnstr(rows - 1, 0, left.c_str(), llen); mvaddnstr(rows - 1, 0, left.c_str(), llen);
// Draw right, flush to end // Draw right, flush to end
int rstart = std::max(0, cols - rlen); int rstart = std::max(0, cols - rlen);
if (rlen > 0) if (rlen > 0)
mvaddnstr(rows - 1, rstart, right.c_str(), rlen); mvaddnstr(rows - 1, rstart, right.c_str(), rlen);
// Middle message // Middle message
const std::string &msg = ed.Status(); const std::string &msg = ed.Status();
@@ -262,7 +300,7 @@ TerminalRenderer::Draw(Editor &ed)
} }
} }
attroff(A_REVERSE); attroff(A_REVERSE);
// Restore terminal cursor to the content position so a visible caret // Restore terminal cursor to the content position so a visible caret
// remains in the editing area (not on the status line). // remains in the editing area (not on the status line).

View File

@@ -1,10 +1,11 @@
#ifndef KTE_UNDONODE_H #ifndef KTE_UNDONODE_H
#define KTE_UNDONODE_H #define KTE_UNDONODE_H
#include <cstdint>
#include <string> #include <string>
enum class UndoType : uint8_t { enum class UndoType : std::uint8_t {
Insert, Insert,
Delete, Delete,
Paste, Paste,

BIN
docs/screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB