Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 41f37478c1 | |||
| d582979eb3 | |||
| 2b194c7910 | |||
| 6498213378 | |||
| 1a37a92534 | |||
| fb5976f123 | |||
| e4cd4877cc |
114
.github/workflows/release.yml
vendored
@@ -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
|
||||||
@@ -35,3 +38,114 @@ 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
@@ -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: []
|
||||||
69
.idea/workspace.xml
generated
@@ -33,12 +33,9 @@
|
|||||||
</configurations>
|
</configurations>
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Refactor code for consistency and enhanced functionality. - Normalize path handling for buffer operations, supporting tilde expansion and absolute paths. - Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes. - Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`. - Refine keybindings and enhance existing commands for improved command flow. - Adjust GUI and terminal renderers to display total line counts alongside filenames. - Update coding style to align with project guidelines.">
|
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Actually add the screenshot.">
|
||||||
|
<change beforePath="$PROJECT_DIR$/.github/workflows/release.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.github/workflows/release.yml" 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.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cc" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/GUIRenderer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIRenderer.cc" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/KKeymap.cc" beforeDir="false" afterPath="$PROJECT_DIR$/KKeymap.cc" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/main.cc" beforeDir="false" afterPath="$PROJECT_DIR$/main.cc" 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" />
|
||||||
@@ -53,21 +50,18 @@
|
|||||||
<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>
|
||||||
<component name="HighlightingSettingsPerFile">
|
<component name="HighlightingSettingsPerFile">
|
||||||
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
||||||
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
||||||
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
</component>
|
</component>
|
||||||
<component name="OptimizeOnSaveOptions">
|
<component name="OptimizeOnSaveOptions">
|
||||||
<option name="myRunOnSave" value="true" />
|
<option name="myRunOnSave" value="true" />
|
||||||
@@ -134,6 +128,11 @@
|
|||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="CMake Application.kge">
|
<component name="RunManager" selected="CMake Application.kge">
|
||||||
|
<configuration default="true" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
|
||||||
|
<method v="2">
|
||||||
|
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
<configuration name="imgui" 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="imgui" CONFIG_NAME="Debug">
|
<configuration name="imgui" 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="imgui" CONFIG_NAME="Debug">
|
||||||
<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" />
|
||||||
@@ -170,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="3453000" />
|
<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" />
|
||||||
@@ -252,7 +251,39 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1764550164829</updated>
|
<updated>1764550164829</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="11" />
|
<task id="LOCAL-00011" summary="Add horizontal scrolling support and refactor mouse click handling in GUI. - Introduce horizontal scrolling with column offset synchronization in GUI. - Refactor mouse click handling for improved accuracy and viewport alignment. - Enhance tab expansion and cursor rendering logic for better user experience. - Replace redundant variable declarations in `Buffer` for cleaner code.">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1764551986561</created>
|
||||||
|
<option name="number" value="00011" />
|
||||||
|
<option name="presentableId" value="LOCAL-00011" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1764551986561</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00012" summary="Introduce file picker and GUI configuration with enhancements. - Add visual file picker for GUI with toggle support. - Introduce `GUIConfig` class for loading GUI settings from configuration file. - Refactor window initialization to support dynamic sizing based on configuration. - Add macOS-specific handling for fullscreen behavior. - Improve header inclusion order and minor code cleanup.">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1764556512864</created>
|
||||||
|
<option name="number" value="00012" />
|
||||||
|
<option name="presentableId" value="LOCAL-00012" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1764556512864</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00013" summary="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.">
|
||||||
|
<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">
|
||||||
@@ -276,7 +307,11 @@
|
|||||||
<MESSAGE value="Add man pages for `kge` and `kte` with installation targets in CMake. - Introduce `docs/kge.1` and `docs/kte.1` man pages covering usage, options, keybindings, and examples. - Update `CMakeLists.txt` to install man pages under `${CMAKE_INSTALL_MANDIR}/man1`. - Ensure `kge` man page installation is conditional on GUI being built." />
|
<MESSAGE value="Add man pages for `kge` and `kte` with installation targets in CMake. - Introduce `docs/kge.1` and `docs/kte.1` man pages covering usage, options, keybindings, and examples. - Update `CMakeLists.txt` to install man pages under `${CMAKE_INSTALL_MANDIR}/man1`. - Ensure `kge` man page installation is conditional on GUI being built." />
|
||||||
<MESSAGE value="Add GUI initialization updates and improve navigation commands. - Implement terminal detachment for GUI mode to enable terminal closure post-launch. - Add `+N` support for opening files at specific line numbers and refine cursor positioning. - Introduce `JumpToLine` command for direct navigation by line number. - Enhance mouse wheel handling for line-wise scrolling." />
|
<MESSAGE value="Add GUI initialization updates and improve navigation commands. - Implement terminal detachment for GUI mode to enable terminal closure post-launch. - Add `+N` support for opening files at specific line numbers and refine cursor positioning. - Introduce `JumpToLine` command for direct navigation by line number. - Enhance mouse wheel handling for line-wise scrolling." />
|
||||||
<MESSAGE value="Refactor code for consistency and enhanced functionality. - Normalize path handling for buffer operations, supporting tilde expansion and absolute paths. - Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes. - Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`. - Refine keybindings and enhance existing commands for improved command flow. - Adjust GUI and terminal renderers to display total line counts alongside filenames. - Update coding style to align with project guidelines." />
|
<MESSAGE value="Refactor code for consistency and enhanced functionality. - Normalize path handling for buffer operations, supporting tilde expansion and absolute paths. - Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes. - Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`. - Refine keybindings and enhance existing commands for improved command flow. - Adjust GUI and terminal renderers to display total line counts alongside filenames. - Update coding style to align with project guidelines." />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Refactor code for consistency and enhanced functionality. - Normalize path handling for buffer operations, supporting tilde expansion and absolute paths. - Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes. - Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`. - Refine keybindings and enhance existing commands for improved command flow. - Adjust GUI and terminal renderers to display total line counts alongside filenames. - Update coding style to align with project guidelines." />
|
<MESSAGE value="Add horizontal scrolling support and refactor mouse click handling in GUI. - Introduce horizontal scrolling with column offset synchronization in GUI. - Refactor mouse click handling for improved accuracy and viewport alignment. - Enhance tab expansion and cursor rendering logic for better user experience. - Replace redundant variable declarations in `Buffer` for cleaner code." />
|
||||||
|
<MESSAGE value="Introduce file picker and GUI configuration with enhancements. - Add visual file picker for GUI with toggle support. - Introduce `GUIConfig` class for loading GUI settings from configuration file. - Refactor window initialization to support dynamic sizing based on configuration. - Add macOS-specific handling for fullscreen behavior. - Improve header inclusion order and minor code cleanup." />
|
||||||
|
<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." />
|
||||||
|
<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 />
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "Buffer.h"
|
|
||||||
#include "UndoSystem.h"
|
|
||||||
#include "UndoTree.h"
|
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "Buffer.h"
|
||||||
|
#include "UndoSystem.h"
|
||||||
|
#include "UndoTree.h"
|
||||||
|
|
||||||
|
|
||||||
Buffer::Buffer()
|
Buffer::Buffer()
|
||||||
{
|
{
|
||||||
|
|||||||
1
Buffer.h
@@ -12,6 +12,7 @@
|
|||||||
#include "AppendBuffer.h"
|
#include "AppendBuffer.h"
|
||||||
#include "UndoSystem.h"
|
#include "UndoSystem.h"
|
||||||
|
|
||||||
|
|
||||||
class Buffer {
|
class Buffer {
|
||||||
public:
|
public:
|
||||||
Buffer();
|
Buffer();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ project(kte)
|
|||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(KTE_VERSION "0.9.2")
|
set(KTE_VERSION "1.0.1")
|
||||||
|
|
||||||
# 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.
|
||||||
@@ -128,6 +128,8 @@ endif ()
|
|||||||
if (${BUILD_GUI})
|
if (${BUILD_GUI})
|
||||||
target_sources(kte PRIVATE
|
target_sources(kte PRIVATE
|
||||||
Font.h
|
Font.h
|
||||||
|
GUIConfig.cc
|
||||||
|
GUIConfig.h
|
||||||
GUIRenderer.cc
|
GUIRenderer.cc
|
||||||
GUIRenderer.h
|
GUIRenderer.h
|
||||||
GUIInputHandler.cc
|
GUIInputHandler.cc
|
||||||
@@ -142,6 +144,8 @@ if (${BUILD_GUI})
|
|||||||
main.cc
|
main.cc
|
||||||
${COMMON_SOURCES}
|
${COMMON_SOURCES}
|
||||||
${COMMON_HEADERS}
|
${COMMON_HEADERS}
|
||||||
|
GUIConfig.cc
|
||||||
|
GUIConfig.h
|
||||||
GUIRenderer.cc
|
GUIRenderer.cc
|
||||||
GUIRenderer.h
|
GUIRenderer.h
|
||||||
GUIInputHandler.cc
|
GUIInputHandler.cc
|
||||||
@@ -153,6 +157,15 @@ if (${BUILD_GUI})
|
|||||||
|
|
||||||
# On macOS, build kge as a proper .app bundle
|
# On macOS, build kge as a proper .app bundle
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
# Define the icon file
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE kge.icns)
|
||||||
|
set(kge_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}")
|
||||||
|
|
||||||
|
# Add icon to the target sources and mark it as a resource
|
||||||
|
target_sources(kge PRIVATE ${kge_ICON})
|
||||||
|
set_source_files_properties(${kge_ICON} PROPERTIES
|
||||||
|
MACOSX_PACKAGE_LOCATION Resources)
|
||||||
|
|
||||||
# Configure Info.plist with version and identifiers
|
# Configure Info.plist with version and identifiers
|
||||||
set(KGE_BUNDLE_ID "dev.wntrmute.kge")
|
set(KGE_BUNDLE_ID "dev.wntrmute.kge")
|
||||||
configure_file(
|
configure_file(
|
||||||
@@ -164,11 +177,23 @@ if (${BUILD_GUI})
|
|||||||
MACOSX_BUNDLE TRUE
|
MACOSX_BUNDLE TRUE
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID}
|
MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID}
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "kge"
|
MACOSX_BUNDLE_BUNDLE_NAME "kge"
|
||||||
|
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}
|
||||||
|
|||||||
34
Command.cc
@@ -6,14 +6,13 @@
|
|||||||
#include "Editor.h"
|
#include "Editor.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "UndoSystem.h"
|
#include "UndoSystem.h"
|
||||||
// Note: Command layer must remain UI-agnostic. Do not include frontend/IO headers here.
|
|
||||||
|
|
||||||
|
|
||||||
// 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 std::size_t
|
static std::size_t
|
||||||
compute_render_x(const std::string &line, std::size_t curx, std::size_t tabw)
|
compute_render_x(const std::string &line, const std::size_t curx, const std::size_t tabw)
|
||||||
{
|
{
|
||||||
std::size_t rx = 0;
|
std::size_t rx = 0;
|
||||||
for (std::size_t i = 0; i < curx && i < line.size(); ++i) {
|
for (std::size_t i = 0; i < curx && i < line.size(); ++i) {
|
||||||
@@ -474,7 +473,7 @@ cmd_change_working_directory_start(CommandContext &ctx)
|
|||||||
{
|
{
|
||||||
std::string initial;
|
std::string initial;
|
||||||
try {
|
try {
|
||||||
initial = std::filesystem::current_path().string();
|
initial = std::filesystem::current_path().string() + "/";
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
initial.clear();
|
initial.clear();
|
||||||
}
|
}
|
||||||
@@ -677,6 +676,30 @@ cmd_open_file_start(CommandContext &ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// GUI: toggle visual file picker (no-op in terminal; renderer will consume flag)
|
||||||
|
static bool
|
||||||
|
cmd_visual_file_picker_toggle(CommandContext &ctx)
|
||||||
|
{
|
||||||
|
// Toggle visibility
|
||||||
|
bool show = !ctx.editor.FilePickerVisible();
|
||||||
|
ctx.editor.SetFilePickerVisible(show);
|
||||||
|
if (show) {
|
||||||
|
// Initialize directory to current working directory if empty
|
||||||
|
if (ctx.editor.FilePickerDir().empty()) {
|
||||||
|
try {
|
||||||
|
ctx.editor.SetFilePickerDir(std::filesystem::current_path().string());
|
||||||
|
} catch (...) {
|
||||||
|
ctx.editor.SetFilePickerDir(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.editor.SetStatus("Open File (visual)");
|
||||||
|
} else {
|
||||||
|
ctx.editor.SetStatus("Closed file picker");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
cmd_jump_to_line_start(CommandContext &ctx)
|
cmd_jump_to_line_start(CommandContext &ctx)
|
||||||
{
|
{
|
||||||
@@ -2630,6 +2653,11 @@ InstallDefaultCommands()
|
|||||||
CommandId::MarkAllAndJumpEnd, "mark-all-jump-end", "Set mark at beginning and jump to end",
|
CommandId::MarkAllAndJumpEnd, "mark-all-jump-end", "Set mark at beginning and jump to end",
|
||||||
cmd_mark_all_and_jump_end
|
cmd_mark_all_and_jump_end
|
||||||
});
|
});
|
||||||
|
// GUI
|
||||||
|
CommandRegistry::Register({
|
||||||
|
CommandId::VisualFilePickerToggle, "file-picker-toggle", "Toggle visual file picker",
|
||||||
|
cmd_visual_file_picker_toggle
|
||||||
|
});
|
||||||
// Working directory
|
// Working directory
|
||||||
CommandRegistry::Register({
|
CommandRegistry::Register({
|
||||||
CommandId::ShowWorkingDirectory, "show-working-directory", "Show current working directory",
|
CommandId::ShowWorkingDirectory, "show-working-directory", "Show current working directory",
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ enum class CommandId {
|
|||||||
KPrefix, // show "C-k _" prompt in status when entering k-command
|
KPrefix, // show "C-k _" prompt in status when entering k-command
|
||||||
FindStart, // begin incremental search (placeholder)
|
FindStart, // begin incremental search (placeholder)
|
||||||
OpenFileStart, // begin open-file prompt
|
OpenFileStart, // begin open-file prompt
|
||||||
|
// GUI: visual file picker
|
||||||
|
VisualFilePickerToggle,
|
||||||
// Buffers
|
// Buffers
|
||||||
BufferSwitchStart, // begin buffer switch prompt
|
BufferSwitchStart, // begin buffer switch prompt
|
||||||
BufferClose,
|
BufferClose,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "Editor.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "Editor.h"
|
||||||
|
|
||||||
|
|
||||||
Editor::Editor() = default;
|
Editor::Editor() = default;
|
||||||
|
|
||||||
|
|||||||
29
Editor.h
@@ -441,6 +441,31 @@ public:
|
|||||||
return buffers_;
|
return buffers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- GUI: Visual File Picker state ---
|
||||||
|
void SetFilePickerVisible(bool on)
|
||||||
|
{
|
||||||
|
file_picker_visible_ = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]] bool FilePickerVisible() const
|
||||||
|
{
|
||||||
|
return file_picker_visible_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SetFilePickerDir(const std::string &path)
|
||||||
|
{
|
||||||
|
file_picker_dir_ = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string &FilePickerDir() const
|
||||||
|
{
|
||||||
|
return file_picker_dir_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t rows_ = 0, cols_ = 0;
|
std::size_t rows_ = 0, cols_ = 0;
|
||||||
int mode_ = 0;
|
int mode_ = 0;
|
||||||
@@ -478,6 +503,10 @@ private:
|
|||||||
std::string prompt_label_;
|
std::string prompt_label_;
|
||||||
std::string prompt_text_;
|
std::string prompt_text_;
|
||||||
std::string pending_overwrite_path_;
|
std::string pending_overwrite_path_;
|
||||||
|
|
||||||
|
// GUI-only state (safe no-op in terminal builds)
|
||||||
|
bool file_picker_visible_ = false;
|
||||||
|
std::string file_picker_dir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KTE_EDITOR_H
|
#endif // KTE_EDITOR_H
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#ifndef KTE_FRONTEND_H
|
#ifndef KTE_FRONTEND_H
|
||||||
#define KTE_FRONTEND_H
|
#define KTE_FRONTEND_H
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
|
|
||||||
class Editor;
|
class Editor;
|
||||||
class InputHandler;
|
class InputHandler;
|
||||||
|
|||||||
107
GUIConfig.cc
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "GUIConfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
trim(std::string &s)
|
||||||
|
{
|
||||||
|
auto not_space = [](int ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
};
|
||||||
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space));
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static std::string
|
||||||
|
default_config_path()
|
||||||
|
{
|
||||||
|
const char *home = std::getenv("HOME");
|
||||||
|
if (!home || !*home)
|
||||||
|
return std::string();
|
||||||
|
std::string path(home);
|
||||||
|
path += "/.config/kte/kge.ini";
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GUIConfig
|
||||||
|
GUIConfig::Load()
|
||||||
|
{
|
||||||
|
GUIConfig cfg; // defaults already set
|
||||||
|
std::string path = default_config_path();
|
||||||
|
if (!path.empty()) {
|
||||||
|
cfg.LoadFromFile(path);
|
||||||
|
}
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
GUIConfig::LoadFromFile(const std::string &path)
|
||||||
|
{
|
||||||
|
std::ifstream in(path);
|
||||||
|
if (!in.good())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
// Remove comments starting with '#' or ';'
|
||||||
|
auto hash = line.find('#');
|
||||||
|
if (hash != std::string::npos)
|
||||||
|
line.erase(hash);
|
||||||
|
auto sc = line.find("//");
|
||||||
|
if (sc != std::string::npos)
|
||||||
|
line.erase(sc);
|
||||||
|
// Basic key=value
|
||||||
|
auto eq = line.find('=');
|
||||||
|
if (eq == std::string::npos)
|
||||||
|
continue;
|
||||||
|
std::string key = line.substr(0, eq);
|
||||||
|
std::string val = line.substr(eq + 1);
|
||||||
|
trim(key);
|
||||||
|
trim(val);
|
||||||
|
// Strip trailing semicolon
|
||||||
|
if (!val.empty() && val.back() == ';') {
|
||||||
|
val.pop_back();
|
||||||
|
trim(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lowercase key
|
||||||
|
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) {
|
||||||
|
return (char) std::tolower(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (key == "fullscreen") {
|
||||||
|
fullscreen = (val == "1" || val == "true" || val == "on" || val == "yes");
|
||||||
|
} else if (key == "columns" || key == "cols") {
|
||||||
|
int v = columns;
|
||||||
|
try {
|
||||||
|
v = std::stoi(val);
|
||||||
|
} catch (...) {}
|
||||||
|
if (v > 0)
|
||||||
|
columns = v;
|
||||||
|
} else if (key == "rows") {
|
||||||
|
int v = rows;
|
||||||
|
try {
|
||||||
|
v = std::stoi(val);
|
||||||
|
} catch (...) {}
|
||||||
|
if (v > 0)
|
||||||
|
rows = v;
|
||||||
|
} else if (key == "font_size" || key == "fontsize") {
|
||||||
|
float v = font_size;
|
||||||
|
try {
|
||||||
|
v = std::stof(val);
|
||||||
|
} catch (...) {}
|
||||||
|
if (v > 0.0f)
|
||||||
|
font_size = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
27
GUIConfig.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* GUIConfig - loads simple GUI configuration from $HOME/.config/kte/kge.ini
|
||||||
|
*/
|
||||||
|
#ifndef KTE_GUI_CONFIG_H
|
||||||
|
#define KTE_GUI_CONFIG_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef KTE_FONT_SIZE
|
||||||
|
#define KTE_FONT_SIZE 16.0f
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class GUIConfig {
|
||||||
|
public:
|
||||||
|
bool fullscreen = false;
|
||||||
|
int columns = 80;
|
||||||
|
int rows = 42;
|
||||||
|
float font_size = (float) KTE_FONT_SIZE;
|
||||||
|
|
||||||
|
// Load from default path: $HOME/.config/kte/kge.ini
|
||||||
|
static GUIConfig Load();
|
||||||
|
|
||||||
|
// Load from explicit path. Returns true if file existed and was parsed.
|
||||||
|
bool LoadFromFile(const std::string &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KTE_GUI_CONFIG_H
|
||||||
@@ -1,18 +1,25 @@
|
|||||||
#include <SDL.h>
|
|
||||||
#include <SDL_opengl.h>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <backends/imgui_impl_sdl2.h>
|
|
||||||
#include <backends/imgui_impl_opengl3.h>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_opengl.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <backends/imgui_impl_sdl2.h>
|
||||||
|
#include <backends/imgui_impl_opengl3.h>
|
||||||
|
|
||||||
#include "Editor.h"
|
#include "Editor.h"
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
#include "GUIFrontend.h"
|
#include "GUIFrontend.h"
|
||||||
#include "Font.h" // embedded default font (DefaultFontRegular)
|
#include "Font.h" // embedded default font (DefaultFontRegular)
|
||||||
|
#include "GUIConfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef KTE_FONT_SIZE
|
||||||
|
#define KTE_FONT_SIZE 16.0f
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *kGlslVersion = "#version 150"; // GL 3.2 core (macOS compatible)
|
static const char *kGlslVersion = "#version 150"; // GL 3.2 core (macOS compatible)
|
||||||
|
|
||||||
@@ -24,6 +31,9 @@ GUIFrontend::Init(Editor &ed)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load GUI configuration (fullscreen, columns/rows, font size)
|
||||||
|
const auto [fullscreen, columns, rows, font_size] = GUIConfig::Load();
|
||||||
|
|
||||||
// GL attributes for core profile
|
// GL attributes for core profile
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
@@ -33,14 +43,56 @@ GUIFrontend::Init(Editor &ed)
|
|||||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||||
|
|
||||||
|
// Compute desired window size from config
|
||||||
|
Uint32 win_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
|
|
||||||
|
if (fullscreen) {
|
||||||
|
// "Fullscreen": fill the usable bounds of the primary display.
|
||||||
|
// On macOS, do NOT use true fullscreen so the menu/status bar remains visible.
|
||||||
|
SDL_Rect usable{};
|
||||||
|
if (SDL_GetDisplayUsableBounds(0, &usable) == 0) {
|
||||||
|
width_ = usable.w;
|
||||||
|
height_ = usable.h;
|
||||||
|
}
|
||||||
|
#if !defined(__APPLE__)
|
||||||
|
// Non-macOS: desktop fullscreen uses the current display resolution.
|
||||||
|
win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
// Windowed: width = columns * font_size, height = (rows * 2) * font_size
|
||||||
|
int w = static_cast<int>(columns * font_size);
|
||||||
|
int h = static_cast<int>((rows * 2) * font_size);
|
||||||
|
|
||||||
|
// As a safety, clamp to display usable bounds if retrievable
|
||||||
|
SDL_Rect usable{};
|
||||||
|
if (SDL_GetDisplayUsableBounds(0, &usable) == 0) {
|
||||||
|
w = std::min(w, usable.w);
|
||||||
|
h = std::min(h, usable.h);
|
||||||
|
}
|
||||||
|
width_ = std::max(320, w);
|
||||||
|
height_ = std::max(200, h);
|
||||||
|
}
|
||||||
|
|
||||||
window_ = SDL_CreateWindow(
|
window_ = SDL_CreateWindow(
|
||||||
"kte",
|
"kge - kyle's text editor " KTE_VERSION_STR,
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
width_, height_,
|
width_, height_,
|
||||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
win_flags);
|
||||||
if (!window_)
|
if (!window_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
// macOS: when "fullscreen" is requested, position the window at the
|
||||||
|
// top-left of the usable display area to mimic fullscreen while keeping
|
||||||
|
// the system menu bar visible.
|
||||||
|
if (fullscreen) {
|
||||||
|
SDL_Rect usable{};
|
||||||
|
if (SDL_GetDisplayUsableBounds(0, &usable) == 0) {
|
||||||
|
SDL_SetWindowPosition(window_, usable.x, usable.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
gl_ctx_ = SDL_GL_CreateContext(window_);
|
gl_ctx_ = SDL_GL_CreateContext(window_);
|
||||||
if (!gl_ctx_)
|
if (!gl_ctx_)
|
||||||
return false;
|
return false;
|
||||||
@@ -64,11 +116,23 @@ GUIFrontend::Init(Editor &ed)
|
|||||||
width_ = w;
|
width_ = w;
|
||||||
height_ = h;
|
height_ = h;
|
||||||
|
|
||||||
// Initialize GUI font from embedded default
|
#if defined(__APPLE__)
|
||||||
#ifndef KTE_FONT_SIZE
|
// Workaround: On macOS Retina when starting maximized, we sometimes get a
|
||||||
#define KTE_FONT_SIZE 16.0f
|
// subtle input vs draw alignment mismatch until the first manual resize.
|
||||||
|
// Nudge the window size by 1px and back to trigger a proper internal
|
||||||
|
// recomputation, without visible impact.
|
||||||
|
if (w > 1 && h > 1) {
|
||||||
|
SDL_SetWindowSize(window_, w - 1, h - 1);
|
||||||
|
SDL_SetWindowSize(window_, w, h);
|
||||||
|
// Update cached size in case backend reports immediately
|
||||||
|
SDL_GetWindowSize(window_, &w, &h);
|
||||||
|
width_ = w;
|
||||||
|
height_ = h;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
LoadGuiFont_(nullptr, (float) KTE_FONT_SIZE);
|
|
||||||
|
// Initialize GUI font from embedded default (use configured size or compiled default)
|
||||||
|
LoadGuiFont_(nullptr, (float) font_size);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "GUIInputHandler.h"
|
#include "GUIInputHandler.h"
|
||||||
#include "GUIRenderer.h"
|
#include "GUIRenderer.h"
|
||||||
|
|
||||||
|
|
||||||
struct SDL_Window;
|
struct SDL_Window;
|
||||||
typedef void *SDL_GLContext;
|
typedef void *SDL_GLContext;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include <SDL.h>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "GUIInputHandler.h"
|
#include "GUIInputHandler.h"
|
||||||
#include "KKeymap.h"
|
#include "KKeymap.h"
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
#ifndef KTE_GUI_INPUT_HANDLER_H
|
#ifndef KTE_GUI_INPUT_HANDLER_H
|
||||||
#define KTE_GUI_INPUT_HANDLER_H
|
#define KTE_GUI_INPUT_HANDLER_H
|
||||||
|
|
||||||
#include <queue>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include "InputHandler.h"
|
#include "InputHandler.h"
|
||||||
|
|
||||||
|
|
||||||
union SDL_Event; // fwd decl to avoid including SDL here (SDL defines SDL_Event as a union)
|
union SDL_Event; // fwd decl to avoid including SDL here (SDL defines SDL_Event as a union)
|
||||||
|
|
||||||
class GUIInputHandler final : public InputHandler {
|
class GUIInputHandler final : public InputHandler {
|
||||||
|
|||||||
200
GUIRenderer.cc
@@ -1,29 +1,44 @@
|
|||||||
#include "GUIRenderer.h"
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
#include "Editor.h"
|
#include <filesystem>
|
||||||
#include "Buffer.h"
|
#include <limits>
|
||||||
#include "Command.h"
|
#include <string>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
#include "GUIRenderer.h"
|
||||||
#include <filesystem>
|
#include "Buffer.h"
|
||||||
#include <cmath>
|
#include "Command.h"
|
||||||
#include <limits>
|
#include "Editor.h"
|
||||||
|
|
||||||
|
|
||||||
// Version string expected to be provided by build system as KTE_VERSION_STR
|
// Version string expected to be provided by build system as KTE_VERSION_STR
|
||||||
#ifndef KTE_VERSION_STR
|
#ifndef KTE_VERSION_STR
|
||||||
# define KTE_VERSION_STR "dev"
|
# define KTE_VERSION_STR "dev"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ImGui compatibility: some bundled ImGui versions (or builds without docking)
|
||||||
|
// don't define ImGuiWindowFlags_NoDocking. Treat it as 0 in that case.
|
||||||
|
#ifndef ImGuiWindowFlags_NoDocking
|
||||||
|
# define ImGuiWindowFlags_NoDocking 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
GUIRenderer::Draw(Editor &ed)
|
GUIRenderer::Draw(Editor &ed)
|
||||||
{
|
{
|
||||||
// Make the editor window occupy the entire GUI container/viewport
|
// Make the editor window occupy the entire GUI container/viewport
|
||||||
ImGuiViewport *vp = ImGui::GetMainViewport();
|
ImGuiViewport *vp = ImGui::GetMainViewport();
|
||||||
ImGui::SetNextWindowPos(vp->Pos);
|
// On HiDPI/Retina, snap to integer pixels to prevent any draw vs hit-test
|
||||||
ImGui::SetNextWindowSize(vp->Size);
|
// mismatches that can appear on the very first maximized frame.
|
||||||
|
ImVec2 main_pos = vp->Pos;
|
||||||
|
ImVec2 main_sz = vp->Size;
|
||||||
|
main_pos.x = std::floor(main_pos.x + 0.5f);
|
||||||
|
main_pos.y = std::floor(main_pos.y + 0.5f);
|
||||||
|
main_sz.x = std::floor(main_sz.x + 0.5f);
|
||||||
|
main_sz.y = std::floor(main_sz.y + 0.5f);
|
||||||
|
ImGui::SetNextWindowPos(main_pos);
|
||||||
|
ImGui::SetNextWindowSize(main_sz);
|
||||||
|
|
||||||
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar
|
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar
|
||||||
| ImGuiWindowFlags_NoResize
|
| ImGuiWindowFlags_NoResize
|
||||||
@@ -297,6 +312,18 @@ GUIRenderer::Draw(Editor &ed)
|
|||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
left += " ";
|
left += " ";
|
||||||
|
// Insert buffer position prefix "[x/N] " before filename
|
||||||
|
{
|
||||||
|
std::size_t total = ed.BufferCount();
|
||||||
|
if (total > 0) {
|
||||||
|
std::size_t idx1 = ed.CurrentBufferIndex() + 1; // 1-based for display
|
||||||
|
left += "[";
|
||||||
|
left += std::to_string(static_cast<unsigned long long>(idx1));
|
||||||
|
left += "/";
|
||||||
|
left += std::to_string(static_cast<unsigned long long>(total));
|
||||||
|
left += "] ";
|
||||||
|
}
|
||||||
|
}
|
||||||
left += fname;
|
left += fname;
|
||||||
if (buf->Dirty())
|
if (buf->Dirty())
|
||||||
left += " *";
|
left += " *";
|
||||||
@@ -379,4 +406,153 @@ GUIRenderer::Draw(Editor &ed)
|
|||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
ImGui::PopStyleVar(3);
|
ImGui::PopStyleVar(3);
|
||||||
|
|
||||||
|
// --- Visual File Picker overlay (GUI only) ---
|
||||||
|
if (ed.FilePickerVisible()) {
|
||||||
|
// Centered popup-style window that always fits within the current viewport
|
||||||
|
ImGuiViewport *vp2 = ImGui::GetMainViewport();
|
||||||
|
|
||||||
|
// Desired size, min size, and margins
|
||||||
|
const ImVec2 want(800.0f, 500.0f);
|
||||||
|
const ImVec2 min_sz(240.0f, 160.0f);
|
||||||
|
const float margin = 20.0f; // space from viewport edges
|
||||||
|
|
||||||
|
// Compute the maximum allowed size (viewport minus margins) and make sure it's not negative
|
||||||
|
ImVec2 max_sz(std::max(32.0f, vp2->Size.x - 2.0f * margin),
|
||||||
|
std::max(32.0f, vp2->Size.y - 2.0f * margin));
|
||||||
|
|
||||||
|
// Clamp desired size to [min_sz, max_sz]
|
||||||
|
ImVec2 size(std::min(want.x, max_sz.x), std::min(want.y, max_sz.y));
|
||||||
|
size.x = std::max(size.x, std::min(min_sz.x, max_sz.x));
|
||||||
|
size.y = std::max(size.y, std::min(min_sz.y, max_sz.y));
|
||||||
|
|
||||||
|
// Center within the viewport using the final size
|
||||||
|
ImVec2 pos(vp2->Pos.x + std::max(margin, (vp2->Size.x - size.x) * 0.5f),
|
||||||
|
vp2->Pos.y + std::max(margin, (vp2->Size.y - size.y) * 0.5f));
|
||||||
|
|
||||||
|
// On HiDPI displays (macOS Retina), ensure integer pixel alignment to avoid
|
||||||
|
// potential hit-test vs draw mismatches from sub-pixel positions.
|
||||||
|
pos.x = std::floor(pos.x + 0.5f);
|
||||||
|
pos.y = std::floor(pos.y + 0.5f);
|
||||||
|
size.x = std::floor(size.x + 0.5f);
|
||||||
|
size.y = std::floor(size.y + 0.5f);
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowSize(size, ImGuiCond_Always);
|
||||||
|
ImGuiWindowFlags wflags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||||
|
ImGuiWindowFlags_NoDocking;
|
||||||
|
bool open = true;
|
||||||
|
if (ImGui::Begin("File Picker", &open, wflags)) {
|
||||||
|
// Current directory
|
||||||
|
std::string curdir = ed.FilePickerDir();
|
||||||
|
if (curdir.empty()) {
|
||||||
|
try {
|
||||||
|
curdir = std::filesystem::current_path().string();
|
||||||
|
} catch (...) {
|
||||||
|
curdir = ".";
|
||||||
|
}
|
||||||
|
ed.SetFilePickerDir(curdir);
|
||||||
|
}
|
||||||
|
ImGui::TextUnformatted(curdir.c_str());
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Up")) {
|
||||||
|
try {
|
||||||
|
std::filesystem::path p(curdir);
|
||||||
|
if (p.has_parent_path()) {
|
||||||
|
ed.SetFilePickerDir(p.parent_path().string());
|
||||||
|
}
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Close")) {
|
||||||
|
ed.SetFilePickerVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Header
|
||||||
|
ImGui::TextUnformatted("Name");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Scrollable list
|
||||||
|
ImGui::BeginChild("picker-list", ImVec2(0, 0), true);
|
||||||
|
|
||||||
|
// Build entries: directories first then files, alphabetical
|
||||||
|
struct Entry {
|
||||||
|
std::string name;
|
||||||
|
std::filesystem::path path;
|
||||||
|
bool is_dir;
|
||||||
|
};
|
||||||
|
std::vector<Entry> entries;
|
||||||
|
entries.reserve(256);
|
||||||
|
// Optional parent entry
|
||||||
|
try {
|
||||||
|
std::filesystem::path base(curdir);
|
||||||
|
std::error_code ec;
|
||||||
|
for (auto it = std::filesystem::directory_iterator(base, ec);
|
||||||
|
!ec && it != std::filesystem::directory_iterator(); it.increment(ec)) {
|
||||||
|
const auto &p = it->path();
|
||||||
|
std::string nm;
|
||||||
|
try {
|
||||||
|
nm = p.filename().string();
|
||||||
|
} catch (...) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nm == "." || nm == "..")
|
||||||
|
continue;
|
||||||
|
bool is_dir = false;
|
||||||
|
std::error_code ec2;
|
||||||
|
is_dir = it->is_directory(ec2);
|
||||||
|
entries.push_back({nm, p, is_dir});
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
// ignore listing errors; show empty
|
||||||
|
}
|
||||||
|
std::sort(entries.begin(), entries.end(), [](const Entry &a, const Entry &b) {
|
||||||
|
if (a.is_dir != b.is_dir)
|
||||||
|
return a.is_dir && !b.is_dir;
|
||||||
|
return a.name < b.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Draw rows
|
||||||
|
int idx = 0;
|
||||||
|
for (const auto &e: entries) {
|
||||||
|
ImGui::PushID(idx++); // ensure unique/stable IDs even if names repeat
|
||||||
|
std::string label;
|
||||||
|
label.reserve(e.name.size() + 4);
|
||||||
|
if (e.is_dir)
|
||||||
|
label += "[";
|
||||||
|
label += e.name;
|
||||||
|
if (e.is_dir)
|
||||||
|
label += "]";
|
||||||
|
|
||||||
|
// Render selectable row
|
||||||
|
ImGui::Selectable(label.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick);
|
||||||
|
|
||||||
|
// Activate based strictly on hover + mouse, to avoid any off-by-one due to click routing
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
if (e.is_dir && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||||
|
// Enter directory on double-click
|
||||||
|
ed.SetFilePickerDir(e.path.string());
|
||||||
|
} else if (!e.is_dir && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||||
|
// Open file on single click
|
||||||
|
std::string err;
|
||||||
|
if (!ed.OpenFile(e.path.string(), err)) {
|
||||||
|
ed.SetStatus(std::string("open: ") + err);
|
||||||
|
} else {
|
||||||
|
ed.SetStatus(std::string("Opened: ") + e.name);
|
||||||
|
}
|
||||||
|
ed.SetFilePickerVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
if (!open) {
|
||||||
|
ed.SetFilePickerVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "Renderer.h"
|
#include "Renderer.h"
|
||||||
|
|
||||||
class GUIRenderer : public Renderer {
|
class GUIRenderer final : public Renderer {
|
||||||
public:
|
public:
|
||||||
GUIRenderer() = default;
|
GUIRenderer() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#include "GapBuffer.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "GapBuffer.h"
|
||||||
|
|
||||||
|
|
||||||
GapBuffer::GapBuffer() = default;
|
GapBuffer::GapBuffer() = default;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
|
|
||||||
class GapBuffer {
|
class GapBuffer {
|
||||||
public:
|
public:
|
||||||
GapBuffer();
|
GapBuffer();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
|
||||||
|
|
||||||
// Result of translating raw input into an editor command.
|
// Result of translating raw input into an editor command.
|
||||||
struct MappedInput {
|
struct MappedInput {
|
||||||
bool hasCommand = false;
|
bool hasCommand = false;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "KKeymap.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "KKeymap.h"
|
||||||
|
|
||||||
|
|
||||||
auto
|
auto
|
||||||
KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
||||||
@@ -83,6 +83,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
|
|||||||
case 'u':
|
case 'u':
|
||||||
out = CommandId::Undo;
|
out = CommandId::Undo;
|
||||||
return true;
|
return true;
|
||||||
|
case 'v':
|
||||||
|
out = CommandId::VisualFilePickerToggle;
|
||||||
|
return true;
|
||||||
case 'w':
|
case 'w':
|
||||||
out = CommandId::ShowWorkingDirectory;
|
out = CommandId::ShowWorkingDirectory;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
|
||||||
|
|
||||||
// Lookup the command to execute after a C-k prefix.
|
// Lookup the command to execute after a C-k prefix.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ascii_key: ASCII code of the key, preferably lowercased if it's a letter.
|
// - ascii_key: ASCII code of the key, preferably lowercased if it's a letter.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
class PieceTable {
|
class PieceTable {
|
||||||
public:
|
public:
|
||||||
PieceTable();
|
PieceTable();
|
||||||
|
|||||||
165
README.md
@@ -1,9 +1,11 @@
|
|||||||
kte — Kyle's Text Editor
|
kte - Kyle's Text Editor
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Vision
|
Vision
|
||||||
-------
|
-------
|
||||||
kte will be a small, fast, and understandable text editor with a
|
kte is a small, fast, and understandable text editor with a
|
||||||
terminal<EFBFBD>first UX and an optional ImGui GUI. It modernizes the
|
terminal-first UX and an optional ImGui GUI. It modernizes the
|
||||||
original ke editor while preserving its familiar WordStar/VDE‑style
|
original ke editor while preserving its familiar WordStar/VDE‑style
|
||||||
command model and Emacs‑influenced ergonomics. The focus is on
|
command model and Emacs‑influenced ergonomics. The focus is on
|
||||||
simplicity of design, excellent latency, and pragmatic features you
|
simplicity of design, excellent latency, and pragmatic features you
|
||||||
@@ -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,89 +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 ImGui‑based 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,
|
|
||||||
Emacs‑like 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
|
|
||||||
crash‑recovery journals are opt‑in 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
|
|
||||||
large‑edit scenarios.
|
|
||||||
- Kill/yank ring, word/sentence/paragraph motions, and rectangle
|
|
||||||
ops.
|
|
||||||
- Undo/redo with grouped edits and time‑travel scrubbing.
|
|
||||||
- Search and replace
|
|
||||||
- Incremental search (C-s) and regex search (C-r) with live
|
|
||||||
highlighting.
|
|
||||||
- Multi‑file grep with a quickfix list; replace with confirm.
|
|
||||||
- Files and projects
|
|
||||||
- Robust encoding/line‑ending detection; safe writes (atomic where
|
|
||||||
possible).
|
|
||||||
- File tree sidebar (GUI) and quick‑open palette.
|
|
||||||
- Lightweight session restore.
|
|
||||||
- Language niceties (opt‑in, no runtime servers required)
|
|
||||||
- Syntax highlighting via fast, table‑driven 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 config‑time
|
|
||||||
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 ImGui‑based frontend that embeds the same editor
|
|
||||||
core.
|
|
||||||
|
|
||||||
Architecture (intended)
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
- Core model
|
|
||||||
- Buffer: file I/O, cursor/mark, viewport state, and edit
|
|
||||||
operations.
|
|
||||||
- GapBuffer: fast in‑memory 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: top‑level state managing buffers, messaging, and global
|
|
||||||
flags.
|
|
||||||
|
|
||||||
Performance and Reliability Targets
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
- Sub‑millisecond keystroke to screen update on typical files in TUI.
|
|
||||||
- Sustain fluid editing on multi‑megabyte files; graceful degradation
|
|
||||||
on very large files.
|
|
||||||
- Atomic/safe writes; autosave and crash‑recovery journals are
|
|
||||||
explicit and transparent.
|
|
||||||
|
|
||||||
Keybindings
|
Keybindings
|
||||||
-----------
|
-----------
|
||||||
kte maintains ke’s command model while internals evolve. Highlights (subject to refinement):
|
kte maintains ke’s command model while internals evolve. Highlights (subject to refinement):
|
||||||
@@ -137,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
|
||||||
-----
|
-----
|
||||||
@@ -180,6 +101,15 @@ Run:
|
|||||||
./cmake-build-debug/kte [files]
|
./cmake-build-debug/kte [files]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you configured the GUI, you can also run the GUI-first target (when
|
||||||
|
built as `kge`) or request the GUI from `kte`:
|
||||||
|
|
||||||
|
```
|
||||||
|
./cmake-build-debug/kte --gui [files]
|
||||||
|
# or if built/installed as a separate GUI target
|
||||||
|
./cmake-build-debug/kge [files]
|
||||||
|
```
|
||||||
|
|
||||||
GUI build example
|
GUI build example
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@@ -194,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
|
|
||||||
ke‑compatible 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
|
|
||||||
zero‑deps, 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)`
|
|
||||||
|
|||||||
121
ROADMAP.md
@@ -1,116 +1,7 @@
|
|||||||
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.
|
|
||||||
- Core ke key chords: C-g (cancel), C-k s/x/q/C-q, C-l, basic arrows, Enter/Backspace, C-s (simple find).
|
|
||||||
|
|
||||||
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`, non‑blocking 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 (ncurses‑based).
|
|
||||||
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 (UTF‑8, 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 save‑as/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 (ke‑style 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 round‑trip, 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 save‑as.
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#include <unistd.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "Editor.h"
|
|
||||||
#include "Command.h"
|
|
||||||
#include "TerminalFrontend.h"
|
#include "TerminalFrontend.h"
|
||||||
|
#include "Command.h"
|
||||||
|
#include "Editor.h"
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
#ifndef KTE_TERMINAL_FRONTEND_H
|
#ifndef KTE_TERMINAL_FRONTEND_H
|
||||||
#define KTE_TERMINAL_FRONTEND_H
|
#define KTE_TERMINAL_FRONTEND_H
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
#include "Frontend.h"
|
#include "Frontend.h"
|
||||||
#include "TerminalInputHandler.h"
|
#include "TerminalInputHandler.h"
|
||||||
#include "TerminalRenderer.h"
|
#include "TerminalRenderer.h"
|
||||||
#include <termios.h>
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalFrontend final : public Frontend {
|
class TerminalFrontend final : public Frontend {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include <ncurses.h>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
#include "KKeymap.h"
|
|
||||||
#include "TerminalInputHandler.h"
|
#include "TerminalInputHandler.h"
|
||||||
|
#include "KKeymap.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int
|
constexpr int
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#ifndef KTE_TERMINAL_INPUT_HANDLER_H
|
#ifndef KTE_TERMINAL_INPUT_HANDLER_H
|
||||||
#define KTE_TERMINAL_INPUT_HANDLER_H
|
#define KTE_TERMINAL_INPUT_HANDLER_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "InputHandler.h"
|
#include "InputHandler.h"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
#include "TerminalRenderer.h"
|
|
||||||
|
|
||||||
#include <ncurses.h>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "Editor.h"
|
#include "TerminalRenderer.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
|
#include "Editor.h"
|
||||||
|
|
||||||
// Version string expected to be provided by build system as KTE_VERSION_STR
|
// Version string expected to be provided by build system as KTE_VERSION_STR
|
||||||
#ifndef KTE_VERSION_STR
|
#ifndef KTE_VERSION_STR
|
||||||
# define KTE_VERSION_STR "dev"
|
# define KTE_VERSION_STR "dev"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
TerminalRenderer::TerminalRenderer() = default;
|
TerminalRenderer::TerminalRenderer() = default;
|
||||||
|
|
||||||
TerminalRenderer::~TerminalRenderer() = default;
|
TerminalRenderer::~TerminalRenderer() = default;
|
||||||
@@ -180,6 +180,18 @@ TerminalRenderer::Draw(Editor &ed)
|
|||||||
fname = "[no name]";
|
fname = "[no name]";
|
||||||
}
|
}
|
||||||
left += " ";
|
left += " ";
|
||||||
|
// Insert buffer position prefix "[x/N] " before filename
|
||||||
|
{
|
||||||
|
std::size_t total = ed.BufferCount();
|
||||||
|
if (total > 0) {
|
||||||
|
std::size_t idx1 = ed.CurrentBufferIndex() + 1; // human-friendly 1-based
|
||||||
|
left += "[";
|
||||||
|
left += std::to_string(static_cast<unsigned long long>(idx1));
|
||||||
|
left += "/";
|
||||||
|
left += std::to_string(static_cast<unsigned long long>(total));
|
||||||
|
left += "] ";
|
||||||
|
}
|
||||||
|
}
|
||||||
left += fname;
|
left += fname;
|
||||||
if (b && b->Dirty())
|
if (b && b->Dirty())
|
||||||
left += " *";
|
left += " *";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "TestFrontend.h"
|
#include "TestFrontend.h"
|
||||||
#include "Editor.h"
|
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
#include <iostream>
|
#include "Editor.h"
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
#ifndef KTE_TEST_INPUT_HANDLER_H
|
#ifndef KTE_TEST_INPUT_HANDLER_H
|
||||||
#define KTE_TEST_INPUT_HANDLER_H
|
#define KTE_TEST_INPUT_HANDLER_H
|
||||||
|
|
||||||
#include "InputHandler.h"
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include "InputHandler.h"
|
||||||
|
|
||||||
class TestInputHandler : public InputHandler {
|
|
||||||
|
class TestInputHandler final : public InputHandler {
|
||||||
public:
|
public:
|
||||||
TestInputHandler() = default;
|
TestInputHandler() = default;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ public:
|
|||||||
void QueueText(const std::string &text);
|
void QueueText(const std::string &text);
|
||||||
|
|
||||||
|
|
||||||
bool IsEmpty() const
|
[[nodiscard]] bool IsEmpty() const
|
||||||
{
|
{
|
||||||
return queue_.empty();
|
return queue_.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "TestRenderer.h"
|
#include "TestRenderer.h"
|
||||||
#include "Editor.h"
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
#ifndef KTE_TEST_RENDERER_H
|
#ifndef KTE_TEST_RENDERER_H
|
||||||
#define KTE_TEST_RENDERER_H
|
#define KTE_TEST_RENDERER_H
|
||||||
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "Renderer.h"
|
||||||
|
|
||||||
class TestRenderer : public Renderer {
|
|
||||||
|
class TestRenderer final : public Renderer {
|
||||||
public:
|
public:
|
||||||
TestRenderer() = default;
|
TestRenderer() = default;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ public:
|
|||||||
void Draw(Editor &ed) override;
|
void Draw(Editor &ed) override;
|
||||||
|
|
||||||
|
|
||||||
std::size_t GetDrawCount() const
|
[[nodiscard]] std::size_t GetDrawCount() const
|
||||||
{
|
{
|
||||||
return draw_count_;
|
return draw_count_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef KTE_UNDONODE_H
|
#ifndef KTE_UNDONODE_H
|
||||||
#define KTE_UNDONODE_H
|
#define KTE_UNDONODE_H
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ UndoSystem::Begin(UndoType type)
|
|||||||
if (type == UndoType::Delete) {
|
if (type == UndoType::Delete) {
|
||||||
// Support batching both forward deletes (DeleteChar) and backspace (prepend case)
|
// Support batching both forward deletes (DeleteChar) and backspace (prepend case)
|
||||||
// Forward delete: cursor stays at anchor col; expected == col
|
// Forward delete: cursor stays at anchor col; expected == col
|
||||||
std::size_t anchor = static_cast<std::size_t>(tree_.pending->col);
|
const auto anchor = static_cast<std::size_t>(tree_.pending->col);
|
||||||
if (anchor + tree_.pending->text.size() == static_cast<std::size_t>(col)) {
|
if (anchor + tree_.pending->text.size() == static_cast<std::size_t>(col)) {
|
||||||
pending_prepend_ = false;
|
pending_prepend_ = false;
|
||||||
return; // keep batching forward delete
|
return; // keep batching forward delete
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
#define KTE_UNDOSYSTEM_H
|
#define KTE_UNDOSYSTEM_H
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "UndoTree.h"
|
#include "UndoTree.h"
|
||||||
|
|
||||||
|
|
||||||
class Buffer;
|
class Buffer;
|
||||||
|
|
||||||
class UndoSystem {
|
class UndoSystem {
|
||||||
@@ -39,7 +41,6 @@ private:
|
|||||||
|
|
||||||
void update_dirty_flag();
|
void update_dirty_flag();
|
||||||
|
|
||||||
private:
|
|
||||||
Buffer *buf_;
|
Buffer *buf_;
|
||||||
UndoTree &tree_;
|
UndoTree &tree_;
|
||||||
// Internal hint for Delete batching: whether next Append() should prepend
|
// Internal hint for Delete batching: whether next Append() should prepend
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define KTE_UNDOTREE_H
|
#define KTE_UNDOTREE_H
|
||||||
|
|
||||||
#include "UndoNode.h"
|
#include "UndoNode.h"
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
struct UndoTree {
|
struct UndoTree {
|
||||||
UndoNode *root = nullptr; // first edit ever
|
UndoNode *root = nullptr; // first edit ever
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>kge</string>
|
<string>kge</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>@MACOSX_BUNDLE_ICON_FILE@</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>@KGE_BUNDLE_ID@</string>
|
<string>@KGE_BUNDLE_ID@</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ kge \- Kyle's Graphical Editor (GUI-first)
|
|||||||
is the GUI-first build target of Kyle's Text Editor. It shares the same
|
is the GUI-first build target of Kyle's Text Editor. It shares the same
|
||||||
editor core and command model as
|
editor core and command model as
|
||||||
.BR kte (1),
|
.BR kte (1),
|
||||||
but defaults to the graphical ImGui frontend when available. A terminal
|
and defaults to the graphical ImGui frontend when available. A terminal
|
||||||
(ncurses) frontend is also available and can be requested explicitly.
|
(ncurses) frontend is also available and can be requested explicitly with
|
||||||
|
.B --term
|
||||||
|
or by invoking
|
||||||
|
.BR kte (1).
|
||||||
|
|
||||||
If one or more
|
If one or more
|
||||||
.I files
|
.I files
|
||||||
@@ -199,6 +202,8 @@ Open using the terminal frontend from kge:
|
|||||||
.BR kte (1),
|
.BR kte (1),
|
||||||
.I docs/ke.md
|
.I docs/ke.md
|
||||||
(project keybinding manual)
|
(project keybinding manual)
|
||||||
|
.br
|
||||||
|
Project homepage: https://github.com/wntrmute/kte
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report issues on the project tracker. Some behaviors are inherited from
|
Report issues on the project tracker. Some behaviors are inherited from
|
||||||
ke and may evolve over time; see the manual for notes.
|
ke and may evolve over time; see the manual for notes.
|
||||||
|
|||||||
13
docs/kte.1
@@ -16,8 +16,15 @@ kte \- Kyle's Text Editor (terminal-first)
|
|||||||
.B kte
|
.B kte
|
||||||
is a small, fast, and understandable text editor with a terminal-first
|
is a small, fast, and understandable text editor with a terminal-first
|
||||||
experience. It preserves ke's WordStar/VDE-style command model with
|
experience. It preserves ke's WordStar/VDE-style command model with
|
||||||
Emacs-influenced ergonomics. The core uses ncurses in the terminal and can
|
Emacs-influenced ergonomics. The core uses ncurses in the terminal.
|
||||||
optionally run with a GUI frontend if built.
|
|
||||||
|
By default, .B kte
|
||||||
|
runs in the terminal (ncurses) frontend. If the binary was built with GUI
|
||||||
|
support, the same editor core can be shown with an ImGui-based GUI by passing
|
||||||
|
.B --gui
|
||||||
|
or by invoking the GUI-first target
|
||||||
|
.BR kge (1)
|
||||||
|
when available.
|
||||||
|
|
||||||
If one or more
|
If one or more
|
||||||
.I files
|
.I files
|
||||||
@@ -194,6 +201,8 @@ Force GUI frontend (if available):
|
|||||||
.BR kge (1),
|
.BR kge (1),
|
||||||
.I docs/ke.md
|
.I docs/ke.md
|
||||||
(project keybinding manual)
|
(project keybinding manual)
|
||||||
|
.br
|
||||||
|
Project homepage: https://github.com/wntrmute/kte
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Incremental search currently restarts from the top on each invocation; see
|
Incremental search currently restarts from the top on each invocation; see
|
||||||
\(lqKnown behavior\(rq in the ke manual. Report issues on the project tracker.
|
\(lqKnown behavior\(rq in the ke manual. Report issues on the project tracker.
|
||||||
|
|||||||
BIN
docs/screenshot.jpg
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
kge.iconset/icon_128x128.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
kge.iconset/icon_128x128@2x.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
kge.iconset/icon_16x16.png
Normal file
|
After Width: | Height: | Size: 911 B |
BIN
kge.iconset/icon_16x16@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
kge.iconset/icon_256x256.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
kge.iconset/icon_256x256@2x.png
Normal file
|
After Width: | Height: | Size: 483 KiB |
BIN
kge.iconset/icon_32x32.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
kge.iconset/icon_32x32@2x.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
kge.iconset/icon_512x512.png
Normal file
|
After Width: | Height: | Size: 483 KiB |
BIN
kge.iconset/icon_512x512@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
13
main.cc
@@ -1,15 +1,15 @@
|
|||||||
|
#include <cctype>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <getopt.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
|
||||||
#include <cctype>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <cstdio>
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "Editor.h"
|
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
#include "Editor.h"
|
||||||
#include "Frontend.h"
|
#include "Frontend.h"
|
||||||
#include "TerminalFrontend.h"
|
#include "TerminalFrontend.h"
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
# define KTE_VERSION_STR "devel"
|
# define KTE_VERSION_STR "devel"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
PrintUsage(const char *prog)
|
PrintUsage(const char *prog)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "Buffer.h"
|
||||||
|
#include "Command.h"
|
||||||
#include "Editor.h"
|
#include "Editor.h"
|
||||||
#include "TestFrontend.h"
|
#include "TestFrontend.h"
|
||||||
#include "Command.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||