Add UndoSystem implementation and refactor UndoNode for simplicity.

This commit is contained in:
2025-11-30 00:04:29 -08:00
parent e91a32dd90
commit 35ffe6d11c
20 changed files with 13950 additions and 1479 deletions

8
.idea/kte.iml generated
View File

@@ -1,2 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />
<module classpath="CMake" type="CPP_MODULE" version="4">
<component name="FacetManager">
<facet type="Python" name="Python facet">
<configuration sdkName="Python 3.14 (kte)" />
</facet>
</component>
</module>

3
.idea/misc.xml generated
View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.14 (kte)" />
</component>
<component name="CMakePythonSetting">
<option name="pythonIntegrationState" value="YES" />
</component>

61
.idea/workspace.xml generated
View File

@@ -23,8 +23,6 @@
<component name="CMakeRunConfigurationManager">
<generated>
<config projectName="kte" targetName="kte" />
<config projectName="kte" targetName="imgui" />
<config projectName="kte" targetName="kge" />
</generated>
</component>
<component name="CMakeSettings" AUTO_RELOAD="true">
@@ -33,9 +31,27 @@
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations.&#10;&#10;This uses either a GapBuffer or PieceTable depending on the compilation.">
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Handle end-of-file newline semantics and improve scroll alignment logic.">
<change afterPath="$PROJECT_DIR$/UndoSystem.cc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/UndoSystem.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/kte.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/kte.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.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$/Buffer.h" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Command.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Command.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Command.h" beforeDir="false" afterPath="$PROJECT_DIR$/Command.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/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$/UndoNode.cc" beforeDir="false" afterPath="$PROJECT_DIR$/UndoNode.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/UndoNode.h" beforeDir="false" afterPath="$PROJECT_DIR$/UndoNode.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/UndoTree.cc" beforeDir="false" afterPath="$PROJECT_DIR$/UndoTree.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/UndoTree.h" beforeDir="false" afterPath="$PROJECT_DIR$/UndoTree.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmake/packaging.cmake" beforeDir="false" afterPath="$PROJECT_DIR$/cmake/packaging.cmake" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fonts/b612_mono.h" beforeDir="false" afterPath="$PROJECT_DIR$/fonts/b612_mono.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fonts/brassmono.h" beforeDir="false" afterPath="$PROJECT_DIR$/fonts/brassmono.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.cc" beforeDir="false" afterPath="$PROJECT_DIR$/main.cc" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -103,37 +119,27 @@
"nodejs_package_manager_path": "npm",
"onboarding.tips.debug.path": "/Users/kyle/src/kte/main.cpp",
"rearrange.code.on.save": "true",
"settings.editor.selected.configurable": "CMakeSettings",
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
"to.speed.mode.migration.done": "true",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager" selected="CMake Application.kge">
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/docs" />
</key>
</component>
<component name="RunManager">
<configuration default="true" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" 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">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="kge" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="kge" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="kte" RUN_TARGET_NAME="kge">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="kte" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="kte" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="kte" RUN_TARGET_NAME="kte">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<list>
<item itemvalue="CMake Application.imgui" />
<item itemvalue="CMake Application.kge" />
<item itemvalue="CMake Application.kte" />
</list>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
@@ -142,7 +148,7 @@
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1764457173148</updated>
<workItem from="1764457174208" duration="27972000" />
<workItem from="1764457174208" duration="31043000" />
</task>
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
<option name="closed" value="true" />
@@ -160,7 +166,15 @@
<option name="project" value="LOCAL" />
<updated>1764486011231</updated>
</task>
<option name="localTasksCounter" value="3" />
<task id="LOCAL-00003" summary="Handle end-of-file newline semantics and improve scroll alignment logic.">
<option name="closed" value="true" />
<created>1764486876984</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1764486876984</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -175,7 +189,8 @@
<MESSAGE value="Refactoring" />
<MESSAGE value="Add undo/redo infrastructure and buffer management additions." />
<MESSAGE value="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations.&#10;&#10;This uses either a GapBuffer or PieceTable depending on the compilation." />
<option name="LAST_COMMIT_MESSAGE" value="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations.&#10;&#10;This uses either a GapBuffer or PieceTable depending on the compilation." />
<MESSAGE value="Handle end-of-file newline semantics and improve scroll alignment logic." />
<option name="LAST_COMMIT_MESSAGE" value="Handle end-of-file newline semantics and improve scroll alignment logic." />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

346
Buffer.cc
View File

@@ -1,11 +1,18 @@
#include "Buffer.h"
#include "UndoSystem.h"
#include "UndoTree.h"
#include <fstream>
#include <sstream>
#include <filesystem>
Buffer::Buffer() = default;
Buffer::Buffer()
{
// Initialize undo system per buffer
undo_tree_ = std::make_unique<UndoTree>();
undo_sys_ = std::make_unique<UndoSystem>(*this, *undo_tree_);
}
Buffer::Buffer(const std::string &path)
@@ -15,78 +22,133 @@ Buffer::Buffer(const std::string &path)
}
// Copy constructor/assignment: perform a deep copy of core fields; reinitialize undo for the new buffer.
Buffer::Buffer(const Buffer &other)
{
curx_ = other.curx_;
cury_ = other.cury_;
rx_ = other.rx_;
nrows_ = other.nrows_;
rowoffs_ = other.rowoffs_;
coloffs_ = other.coloffs_;
rows_ = other.rows_;
filename_ = other.filename_;
is_file_backed_ = other.is_file_backed_;
dirty_ = other.dirty_;
mark_set_ = other.mark_set_;
mark_curx_ = other.mark_curx_;
mark_cury_ = other.mark_cury_;
// Fresh undo system for the copy
undo_tree_ = std::make_unique<UndoTree>();
undo_sys_ = std::make_unique<UndoSystem>(*this, *undo_tree_);
}
Buffer &
Buffer::operator=(const Buffer &other)
{
if (this == &other)
return *this;
curx_ = other.curx_;
cury_ = other.cury_;
rx_ = other.rx_;
nrows_ = other.nrows_;
rowoffs_ = other.rowoffs_;
coloffs_ = other.coloffs_;
rows_ = other.rows_;
filename_ = other.filename_;
is_file_backed_ = other.is_file_backed_;
dirty_ = other.dirty_;
mark_set_ = other.mark_set_;
mark_curx_ = other.mark_curx_;
mark_cury_ = other.mark_cury_;
// Recreate undo system for this instance
undo_tree_ = std::make_unique<UndoTree>();
undo_sys_ = std::make_unique<UndoSystem>(*this, *undo_tree_);
return *this;
}
bool
Buffer::OpenFromFile(const std::string &path, std::string &err)
{
// If the file doesn't exist, initialize an empty, non-file-backed buffer
// with the provided filename. Do not touch the filesystem until Save/SaveAs.
if (!std::filesystem::exists(path)) {
rows_.clear();
nrows_ = 0;
filename_ = path;
is_file_backed_ = false;
dirty_ = false;
if (!std::filesystem::exists(path)) {
rows_.clear();
nrows_ = 0;
filename_ = path;
is_file_backed_ = false;
dirty_ = false;
// Reset cursor/viewport state
curx_ = cury_ = rx_ = 0;
rowoffs_ = coloffs_ = 0;
mark_set_ = false;
mark_curx_ = mark_cury_ = 0;
// Reset cursor/viewport state
curx_ = cury_ = rx_ = 0;
rowoffs_ = coloffs_ = 0;
mark_set_ = false;
mark_curx_ = mark_cury_ = 0;
return true;
}
return true;
}
std::ifstream in(path, std::ios::in | std::ios::binary);
if (!in) {
err = "Failed to open file: " + path;
return false;
}
// Detect if file ends with a newline so we can preserve a final empty line
// in our in-memory representation (mg-style semantics).
bool ends_with_nl = false;
{
in.seekg(0, std::ios::end);
std::streamoff sz = in.tellg();
if (sz > 0) {
in.seekg(-1, std::ios::end);
char last = 0;
in.read(&last, 1);
ends_with_nl = (last == '\n');
} else {
in.clear();
}
// Rewind to start for line-by-line read
in.clear();
in.seekg(0, std::ios::beg);
}
rows_.clear();
std::string line;
while (std::getline(in, line)) {
// std::getline strips the '\n', keep raw line content only
// Handle potential Windows CRLF: strip trailing '\r'
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
rows_.emplace_back(line);
}
// Detect if file ends with a newline so we can preserve a final empty line
// in our in-memory representation (mg-style semantics).
bool ends_with_nl = false;
{
in.seekg(0, std::ios::end);
std::streamoff sz = in.tellg();
if (sz > 0) {
in.seekg(-1, std::ios::end);
char last = 0;
in.read(&last, 1);
ends_with_nl = (last == '\n');
} else {
in.clear();
}
// Rewind to start for line-by-line read
in.clear();
in.seekg(0, std::ios::beg);
}
// If the file ended with a newline and we didn't already get an
// empty final row from getline (e.g., when the last textual line
// had content followed by '\n'), append an empty row to represent
// the cursor position past the last newline.
if (ends_with_nl) {
if (rows_.empty() || !rows_.back().empty()) {
rows_.emplace_back(std::string());
}
}
rows_.clear();
std::string line;
while (std::getline(in, line)) {
// std::getline strips the '\n', keep raw line content only
// Handle potential Windows CRLF: strip trailing '\r'
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
rows_.emplace_back(line);
}
nrows_ = rows_.size();
// If the file ended with a newline and we didn't already get an
// empty final row from getline (e.g., when the last textual line
// had content followed by '\n'), append an empty row to represent
// the cursor position past the last newline.
if (ends_with_nl) {
if (rows_.empty() || !rows_.back().empty()) {
rows_.emplace_back(std::string());
}
}
nrows_ = rows_.size();
filename_ = path;
is_file_backed_ = true;
dirty_ = false;
// Reset/initialize undo system for this loaded file
if (!undo_tree_)
undo_tree_ = std::make_unique<UndoTree>();
if (!undo_sys_)
undo_sys_ = std::make_unique<UndoSystem>(*this, *undo_tree_);
// Clear any existing history for a fresh load
undo_sys_->clear();
// Reset cursor/viewport state
curx_ = cury_ = rx_ = 0;
rowoffs_ = coloffs_ = 0;
@@ -109,14 +171,15 @@ Buffer::Save(std::string &err) const
err = "Failed to open for write: " + filename_;
return false;
}
for (std::size_t i = 0; i < rows_.size(); ++i) {
const char *d = rows_[i].Data();
std::size_t n = rows_[i].Size();
if (d && n) out.write(d, static_cast<std::streamsize>(n));
if (i + 1 < rows_.size()) {
out.put('\n');
}
}
for (std::size_t i = 0; i < rows_.size(); ++i) {
const char *d = rows_[i].Data();
std::size_t n = rows_[i].Size();
if (d && n)
out.write(d, static_cast<std::streamsize>(n));
if (i + 1 < rows_.size()) {
out.put('\n');
}
}
if (!out.good()) {
err = "Write error";
return false;
@@ -136,14 +199,15 @@ Buffer::SaveAs(const std::string &path, std::string &err)
err = "Failed to open for write: " + path;
return false;
}
for (std::size_t i = 0; i < rows_.size(); ++i) {
const char *d = rows_[i].Data();
std::size_t n = rows_[i].Size();
if (d && n) out.write(d, static_cast<std::streamsize>(n));
if (i + 1 < rows_.size()) {
out.put('\n');
}
}
for (std::size_t i = 0; i < rows_.size(); ++i) {
const char *d = rows_[i].Data();
std::size_t n = rows_[i].Size();
if (d && n)
out.write(d, static_cast<std::streamsize>(n));
if (i + 1 < rows_.size()) {
out.put('\n');
}
}
if (!out.good()) {
err = "Write error";
return false;
@@ -167,3 +231,147 @@ Buffer::AsString() const
ss << ">: " << rows_.size() << " lines";
return ss.str();
}
// --- Raw editing APIs (no undo recording, cursor untouched) ---
void
Buffer::insert_text(int row, int col, std::string_view text)
{
if (row < 0)
row = 0;
if (static_cast<std::size_t>(row) > rows_.size())
row = static_cast<int>(rows_.size());
if (rows_.empty())
rows_.emplace_back("");
if (static_cast<std::size_t>(row) >= rows_.size())
rows_.emplace_back("");
std::size_t y = static_cast<std::size_t>(row);
std::size_t x = static_cast<std::size_t>(col);
if (x > rows_[y].size())
x = rows_[y].size();
std::string remain(text);
while (true) {
auto pos = remain.find('\n');
if (pos == std::string::npos) {
rows_[y].insert(x, remain);
break;
}
// Insert up to newline
std::string seg = remain.substr(0, pos);
rows_[y].insert(x, seg);
x += seg.size();
// Split line at x
std::string tail = rows_[y].substr(x);
rows_[y].erase(x);
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1), tail);
y += 1;
x = 0;
remain.erase(0, pos + 1);
}
// Do not set dirty here; UndoSystem will manage state/dirty externally
}
void
Buffer::delete_text(int row, int col, std::size_t len)
{
if (rows_.empty() || len == 0)
return;
if (row < 0)
row = 0;
if (static_cast<std::size_t>(row) >= rows_.size())
return;
std::size_t y = static_cast<std::size_t>(row);
std::size_t x = std::min<std::size_t>(static_cast<std::size_t>(col), rows_[y].size());
std::size_t remaining = len;
while (remaining > 0 && y < rows_.size()) {
auto &line = rows_[y];
std::size_t in_line = std::min<std::size_t>(remaining, line.size() - std::min(x, line.size()));
if (x < line.size() && in_line > 0) {
line.erase(x, in_line);
remaining -= in_line;
}
if (remaining == 0)
break;
// If at or beyond end of line and there is a next line, join it (deleting the implied '\n')
if (y + 1 < rows_.size()) {
line += rows_[y + 1];
rows_.erase(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1));
// deleting the newline consumes one virtual character
if (remaining > 0) {
// Treat the newline as one deletion unit if len spans it
// We already joined, so nothing else to do here.
}
} else {
break;
}
}
}
void
Buffer::split_line(int row, int col)
{
if (row < 0)
row = 0;
if (static_cast<std::size_t>(row) >= rows_.size())
rows_.resize(static_cast<std::size_t>(row) + 1);
std::size_t y = static_cast<std::size_t>(row);
std::size_t x = std::min<std::size_t>(static_cast<std::size_t>(col), rows_[y].size());
std::string tail = rows_[y].substr(x);
rows_[y].erase(x);
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1), tail);
}
void
Buffer::join_lines(int row)
{
if (row < 0)
row = 0;
std::size_t y = static_cast<std::size_t>(row);
if (y + 1 >= rows_.size())
return;
rows_[y] += rows_[y + 1];
rows_.erase(rows_.begin() + static_cast<std::ptrdiff_t>(y + 1));
}
void
Buffer::insert_row(int row, std::string_view text)
{
if (row < 0)
row = 0;
if (static_cast<std::size_t>(row) > rows_.size())
row = static_cast<int>(rows_.size());
rows_.insert(rows_.begin() + static_cast<std::ptrdiff_t>(row), std::string(text));
}
void
Buffer::delete_row(int row)
{
if (row < 0)
row = 0;
if (static_cast<std::size_t>(row) >= rows_.size())
return;
rows_.erase(rows_.begin() + static_cast<std::ptrdiff_t>(row));
}
// Undo system accessors
UndoSystem *
Buffer::Undo()
{
return undo_sys_.get();
}
const UndoSystem *
Buffer::Undo() const
{
return undo_sys_.get();
}

108
Buffer.h
View File

@@ -7,13 +7,23 @@
#include <cstddef>
#include <string>
#include <vector>
#include <string_view>
#include "AppendBuffer.h"
#include "UndoSystem.h"
class Buffer {
public:
Buffer();
Buffer(const Buffer &other);
Buffer &operator=(const Buffer &other);
Buffer(Buffer &&) noexcept = default;
Buffer &operator=(Buffer &&) noexcept = default;
explicit Buffer(const std::string &path);
// File operations
@@ -64,31 +74,59 @@ public:
public:
Line() = default;
Line(const char *s)
{
assign_from(s ? std::string(s) : std::string());
}
Line(const std::string &s)
{
assign_from(s);
}
Line(const Line &other) = default;
Line &operator=(const Line &other) = default;
Line(Line &&other) noexcept = default;
Line &operator=(Line &&other) noexcept = default;
// capacity helpers
void Clear() { buf_.Clear(); }
void Clear()
{
buf_.Clear();
}
// size/access
[[nodiscard]] std::size_t size() const { return buf_.Size(); }
[[nodiscard]] bool empty() const { return size() == 0; }
[[nodiscard]] std::size_t size() const
{
return buf_.Size();
}
[[nodiscard]] bool empty() const
{
return size() == 0;
}
// read-only raw view
[[nodiscard]] const char *Data() const { return buf_.Data(); }
[[nodiscard]] std::size_t Size() const { return buf_.Size(); }
[[nodiscard]] const char *Data() const
{
return buf_.Data();
}
[[nodiscard]] std::size_t Size() const
{
return buf_.Size();
}
// element access (read-only)
[[nodiscard]] char operator[](std::size_t i) const
@@ -97,60 +135,77 @@ public:
return (i < buf_.Size() && d) ? d[i] : '\0';
}
// conversions
operator std::string() const { return std::string(buf_.Data() ? buf_.Data() : "", buf_.Size()); }
operator std::string() const
{
return std::string(buf_.Data() ? buf_.Data() : "", buf_.Size());
}
// string-like API used by command/renderer layers (implemented via materialization for now)
std::string substr(std::size_t pos) const
{
const std::size_t n = buf_.Size();
if (pos >= n) return std::string();
if (pos >= n)
return std::string();
return std::string(buf_.Data() + pos, n - pos);
}
std::string substr(std::size_t pos, std::size_t len) const
{
const std::size_t n = buf_.Size();
if (pos >= n) return std::string();
if (pos >= n)
return std::string();
const std::size_t take = (pos + len > n) ? (n - pos) : len;
return std::string(buf_.Data() + pos, take);
}
void erase(std::size_t pos)
{
// erase to end
material_edit([&](std::string &s) {
if (pos < s.size()) s.erase(pos);
if (pos < s.size())
s.erase(pos);
});
}
void erase(std::size_t pos, std::size_t len)
{
material_edit([&](std::string &s) {
if (pos < s.size()) s.erase(pos, len);
if (pos < s.size())
s.erase(pos, len);
});
}
void insert(std::size_t pos, const std::string &seg)
{
material_edit([&](std::string &s) {
if (pos > s.size()) pos = s.size();
if (pos > s.size())
pos = s.size();
s.insert(pos, seg);
});
}
Line &operator+=(const Line &other)
{
buf_.Append(other.buf_.Data(), other.buf_.Size());
return *this;
}
Line &operator+=(const std::string &s)
{
buf_.Append(s.data(), s.size());
return *this;
}
Line &operator=(const std::string &s)
{
assign_from(s);
@@ -161,10 +216,12 @@ public:
void assign_from(const std::string &s)
{
buf_.Clear();
if (!s.empty()) buf_.Append(s.data(), s.size());
if (!s.empty())
buf_.Append(s.data(), s.size());
}
template <typename F>
template<typename F>
void material_edit(F fn)
{
std::string tmp = static_cast<std::string>(*this);
@@ -172,9 +229,11 @@ public:
assign_from(tmp);
}
AppendBuffer buf_;
};
[[nodiscard]] const std::vector<Line> &Rows() const
{
return rows_;
@@ -266,6 +325,25 @@ public:
[[nodiscard]] std::string AsString() const;
// Raw, low-level editing APIs used by UndoSystem apply().
// These must NOT trigger undo recording. They also do not move the cursor.
void insert_text(int row, int col, std::string_view text);
void delete_text(int row, int col, std::size_t len);
void split_line(int row, int col);
void join_lines(int row);
void insert_row(int row, std::string_view text);
void delete_row(int row);
// Undo system accessors (created per-buffer)
UndoSystem *Undo();
const UndoSystem *Undo() const;
private:
// State mirroring original C struct (without undo_tree)
std::size_t curx_ = 0, cury_ = 0; // cursor position in characters
@@ -278,6 +356,10 @@ private:
bool dirty_ = false;
bool mark_set_ = false;
std::size_t mark_curx_ = 0, mark_cury_ = 0;
// Per-buffer undo state
std::unique_ptr<struct UndoTree> undo_tree_;
std::unique_ptr<UndoSystem> undo_sys_;
};
#endif // KTE_BUFFER_H

View File

@@ -55,8 +55,9 @@ set(COMMON_SOURCES
TerminalInputHandler.cc
TerminalRenderer.cc
TerminalFrontend.cc
# UndoNode.cc
# UndoTree.cc
UndoNode.cc
UndoTree.cc
UndoSystem.cc
)
set(COMMON_HEADERS
@@ -73,8 +74,9 @@ set(COMMON_HEADERS
TerminalRenderer.h
Frontend.h
TerminalFrontend.h
# UndoNode.h
# UndoTree.h
UndoNode.h
UndoTree.h
UndoSystem.h
)
# kte (terminal-first) executable

View File

@@ -3,6 +3,7 @@
#include "Command.h"
#include "Editor.h"
#include "Buffer.h"
#include "UndoSystem.h"
// Note: Command layer must remain UI-agnostic. Do not include frontend/IO headers here.
@@ -424,6 +425,8 @@ cmd_save(CommandContext &ctx)
}
buf->SetDirty(false);
ctx.editor.SetStatus("Saved " + buf->Filename());
if (auto *u = buf->Undo())
u->mark_saved();
return true;
}
@@ -446,6 +449,8 @@ cmd_save_as(CommandContext &ctx)
return false;
}
ctx.editor.SetStatus("Saved as " + ctx.arg);
if (auto *u = buf->Undo())
u->mark_saved();
return true;
}
@@ -691,8 +696,10 @@ cmd_buffer_close(CommandContext &ctx)
if (ctx.editor.BufferCount() == 0)
return true;
std::size_t idx = ctx.editor.CurrentBufferIndex();
const Buffer *b = ctx.editor.CurrentBuffer();
Buffer *b = ctx.editor.CurrentBuffer();
std::string name = b ? buffer_display_name(*b) : std::string("");
if (b && b->Undo())
b->Undo()->discard_pending();
ctx.editor.CloseBuffer(idx);
if (ctx.editor.BufferCount() == 0) {
// Open a fresh empty buffer
@@ -716,6 +723,11 @@ cmd_insert_text(CommandContext &ctx)
ctx.editor.SetStatus("No buffer to edit");
return false;
}
// Start/extend an insert batch for undo
if (auto *u = buf->Undo()) {
u->Begin(UndoType::Insert);
u->Append(std::string_view(ctx.arg));
}
// If a prompt is active, edit prompt text
if (ctx.editor.PromptActive()) {
// Special-case: buffer switch prompt supports Tab-completion
@@ -916,15 +928,19 @@ cmd_newline(CommandContext &ctx)
ctx.editor.SetStatus("No buffer to edit");
return false;
}
// Start a newline batch for undo at current cursor
if (auto *u = buf->Undo()) {
u->Begin(UndoType::Newline);
}
ensure_at_least_one_line(*buf);
auto &rows = buf->Rows();
std::size_t y = buf->Cury();
std::size_t x = buf->Curx();
int repeat = ctx.count > 0 ? ctx.count : 1;
for (int i = 0; i < repeat; ++i) {
if (y >= rows.size())
rows.resize(y + 1);
auto &line = rows[y];
if (y >= rows.size())
rows.resize(y + 1);
auto &line = rows[y];
std::string tail;
if (x < line.size()) {
tail = line.substr(x);
@@ -1038,6 +1054,38 @@ cmd_delete_char(CommandContext &ctx)
}
// --- Undo/Redo ---
static bool
cmd_undo(CommandContext &ctx)
{
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo()) {
u->undo();
// Keep cursor within buffer bounds
ensure_cursor_visible(ctx.editor, *buf);
return true;
}
return false;
}
static bool
cmd_redo(CommandContext &ctx)
{
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo()) {
u->redo();
ensure_cursor_visible(ctx.editor, *buf);
return true;
}
return false;
}
static bool
cmd_kill_to_eol(CommandContext &ctx)
{
@@ -1103,7 +1151,7 @@ cmd_kill_line(CommandContext &ctx)
if (rows.size() == 1) {
// last remaining line: clear its contents
killed_total += rows[0];
rows[0].Clear();
rows[0].Clear();
y = 0;
} else if (y < rows.size()) {
// erase current line; keep y pointing at the next line
@@ -1299,6 +1347,8 @@ cmd_move_left(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
// If a prompt is active and it's search, go to previous match
if (ctx.editor.PromptActive() && ctx.editor.CurrentPromptKind() == Editor::PromptKind::Search) {
auto matches = search_compute_matches(*buf, ctx.editor.SearchQuery());
@@ -1353,6 +1403,8 @@ cmd_move_right(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
if (ctx.editor.PromptActive() && ctx.editor.CurrentPromptKind() == Editor::PromptKind::Search) {
auto matches = search_compute_matches(*buf, ctx.editor.SearchQuery());
if (!matches.empty()) {
@@ -1406,6 +1458,8 @@ cmd_move_up(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
if ((ctx.editor.PromptActive() && ctx.editor.CurrentPromptKind() == Editor::PromptKind::Search) || ctx.editor.
SearchActive()) {
// Up == previous match
@@ -1444,6 +1498,8 @@ cmd_move_down(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
if ((ctx.editor.PromptActive() && ctx.editor.CurrentPromptKind() == Editor::PromptKind::Search) || ctx.editor.
SearchActive()) {
// Down == next match
@@ -1483,6 +1539,8 @@ cmd_move_home(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
ensure_at_least_one_line(*buf);
std::size_t y = buf->Cury();
buf->SetCursor(0, y);
@@ -1497,6 +1555,8 @@ cmd_move_end(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
ensure_at_least_one_line(*buf);
auto &rows = buf->Rows();
std::size_t y = buf->Cury();
@@ -1513,6 +1573,8 @@ cmd_page_up(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
ensure_at_least_one_line(*buf);
auto &rows = buf->Rows();
int repeat = ctx.count > 0 ? ctx.count : 1;
@@ -1553,6 +1615,8 @@ cmd_page_down(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
ensure_at_least_one_line(*buf);
auto &rows = buf->Rows();
int repeat = ctx.count > 0 ? ctx.count : 1;
@@ -1597,6 +1661,8 @@ cmd_word_prev(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
ensure_at_least_one_line(*buf);
auto &rows = buf->Rows();
std::size_t y = buf->Cury();
@@ -1652,6 +1718,8 @@ cmd_word_next(CommandContext &ctx)
Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf)
return false;
if (auto *u = buf->Undo())
u->commit();
ensure_at_least_one_line(*buf);
auto &rows = buf->Rows();
std::size_t y = buf->Cury();
@@ -1817,6 +1885,9 @@ InstallDefaultCommands()
CommandRegistry::Register({
CommandId::MoveCursorTo, "move-cursor-to", "Move cursor to y:x", cmd_move_cursor_to
});
// Undo/Redo
CommandRegistry::Register({CommandId::Undo, "undo", "Undo last edit", cmd_undo});
CommandRegistry::Register({CommandId::Redo, "redo", "Redo edit", cmd_redo});
}

View File

@@ -58,6 +58,9 @@ enum class CommandId {
WordNext,
// Direct cursor placement
MoveCursorTo, // arg: "y:x" (zero-based row:col)
// Undo/Redo
Undo,
Redo,
// Meta
UnknownKCommand, // arg: single character that was not recognized after C-k
};

View File

@@ -82,14 +82,14 @@ GUIRenderer::Draw(Editor &ed)
vis_rows = 1;
long last_row = first_row + vis_rows - 1;
// A) If user scrolled (scroll_y changed), and cursor outside, move cursor to nearest visible row
// Skip this when we just forced a scroll alignment this frame (programmatic change).
if (!forced_scroll && prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
long cyr = static_cast<long>(cy);
if (cyr < first_row || cyr > last_row) {
long new_row = (cyr < first_row) ? first_row : last_row;
if (new_row < 0)
new_row = 0;
// A) If user scrolled (scroll_y changed), and cursor outside, move cursor to nearest visible row
// Skip this when we just forced a scroll alignment this frame (programmatic change).
if (!forced_scroll && prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
long cyr = static_cast<long>(cy);
if (cyr < first_row || cyr > last_row) {
long new_row = (cyr < first_row) ? first_row : last_row;
if (new_row < 0)
new_row = 0;
if (new_row >= static_cast<long>(lines.size()))
new_row = static_cast<long>(lines.empty() ? 0 : (lines.size() - 1));
// Clamp column to line length

View File

@@ -4,11 +4,11 @@
auto
KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
{
// Normalize to lowercase letter if applicable
int k = KLowerAscii(ascii_key);
// For k-prefix, preserve case to allow distinct mappings (e.g., 'U' vs 'u').
const int k_lower = KLowerAscii(ascii_key);
if (ctrl) {
switch (k) {
switch (k_lower) {
case 'd':
out = CommandId::KillLine;
return true; // C-k C-d
@@ -22,7 +22,7 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
break;
}
} else {
switch (k) {
switch (k_lower) {
case 'j':
out = CommandId::JumpToMark;
return true; // C-k j
@@ -59,9 +59,17 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
case 'p':
out = CommandId::BufferNext;
return true; // C-k p (switch to next buffer)
case 'u':
out = CommandId::Undo;
return true; // C-k u (undo)
default:
break;
}
// Case-sensitive bindings after k-prefix
if (ascii_key == 'U') {
out = CommandId::Redo; // C-k U (redo)
return true;
}
}
return false;
}

View File

@@ -1,15 +1,2 @@
// Placeholder translation unit for UndoNode struct definition.
#include "UndoNode.h"
void
UndoNode::DeleteNext() const
{
const UndoNode *node = next_;
const UndoNode *next = nullptr;
while (node != nullptr) {
next = node->Next();
delete node;
node = next;
}
}

View File

@@ -2,81 +2,25 @@
#define KTE_UNDONODE_H
#include <cstddef>
#include <cstddef>
#include <cstdint>
#include <string>
enum UndoKind {
UNDO_INSERT,
UNDO_DELETE,
enum class UndoType : uint8_t {
Insert,
Delete,
Paste,
Newline,
DeleteRow,
};
class UndoNode {
public:
explicit UndoNode(const UndoKind kind, const size_t row, const size_t col)
: kind_(kind), row_(row), col_(col) {}
~UndoNode() = default;
[[nodiscard]] UndoKind Kind() const
{
return kind_;
}
[[nodiscard]] UndoNode *Next() const
{
return next_;
}
void Next(UndoNode *next)
{
next_ = next;
}
[[nodiscard]] UndoNode *Child() const
{
return child_;
}
void Child(UndoNode *child)
{
child_ = child;
}
void SetRowCol(const std::size_t row, const std::size_t col)
{
this->row_ = row;
this->col_ = col;
}
[[nodiscard]] std::size_t Row() const
{
return row_;
}
[[nodiscard]] std::size_t Col() const
{
return col_;
}
void DeleteNext() const;
private:
[[maybe_unused]] UndoKind kind_;
[[maybe_unused]] UndoNode *next_{nullptr};
[[maybe_unused]] UndoNode *child_{nullptr};
[[maybe_unused]] std::size_t row_{}, col_{};
struct UndoNode {
UndoType type{};
int row{};
int col{};
std::string text;
UndoNode *child = nullptr; // next in current timeline
UndoNode *next = nullptr; // redo branch
};

263
UndoSystem.cc Normal file
View File

@@ -0,0 +1,263 @@
#include "UndoSystem.h"
#include "Buffer.h"
UndoSystem::UndoSystem(Buffer &owner, UndoTree &tree)
: buf_(owner), tree_(tree) {}
void
UndoSystem::Begin(UndoType type)
{
// Reuse pending if batching conditions are met
const int row = static_cast<int>(buf_.Cury());
const int col = static_cast<int>(buf_.Curx());
if (tree_.pending && tree_.pending->type == type && tree_.pending->row == row) {
std::size_t expected = static_cast<std::size_t>(tree_.pending->col) + tree_.pending->text.size();
if (expected == static_cast<std::size_t>(col)) {
return; // keep batching
}
}
// Otherwise commit any existing batch and start a new node
commit();
auto *node = new UndoNode();
node->type = type;
node->row = row;
node->col = col;
node->child = nullptr;
node->next = nullptr;
tree_.pending = node;
}
void
UndoSystem::Append(char ch)
{
if (!tree_.pending)
return;
tree_.pending->text.push_back(ch);
}
void
UndoSystem::Append(std::string_view text)
{
if (!tree_.pending)
return;
tree_.pending->text.append(text.data(), text.size());
}
void
UndoSystem::commit()
{
if (!tree_.pending)
return;
// If we have redo branches from current, discard them (non-linear behavior)
if (tree_.current && tree_.current->child) {
free_node(tree_.current->child);
tree_.current->child = nullptr;
// We diverged; saved snapshot cannot be on discarded branch anymore
if (tree_.saved) {
// If saved is not equal to current, keep it; if it was on discarded branch we cannot easily detect now.
// For simplicity, leave saved as-is; dirty flag uses pointer equality.
}
}
// Attach pending as next state
if (!tree_.root) {
tree_.root = tree_.pending;
tree_.current = tree_.pending;
} else if (!tree_.current) {
// Should not happen if root exists, but handle gracefully
tree_.current = tree_.pending;
} else {
// Attach as primary child (head of redo list)
tree_.pending->next = nullptr;
tree_.current->child = tree_.pending;
tree_.current = tree_.pending;
}
tree_.pending = nullptr;
update_dirty_flag();
}
void
UndoSystem::undo()
{
// Close any pending batch
commit();
if (!tree_.current)
return;
UndoNode *parent = find_parent(tree_.root, tree_.current);
UndoNode *node = tree_.current;
// Apply inverse of current node
apply(node, -1);
tree_.current = parent;
update_dirty_flag();
}
void
UndoSystem::redo()
{
// Redo next child along current timeline
if (tree_.pending) {
// If app added pending edits, finalize them before redo chain
commit();
}
UndoNode *next = nullptr;
if (!tree_.current) {
next = tree_.root; // if nothing yet, try applying first node
} else {
next = tree_.current->child;
}
if (!next)
return;
apply(next, +1);
tree_.current = next;
update_dirty_flag();
}
void
UndoSystem::mark_saved()
{
tree_.saved = tree_.current;
update_dirty_flag();
}
void
UndoSystem::discard_pending()
{
if (tree_.pending) {
delete tree_.pending;
tree_.pending = nullptr;
}
}
void
UndoSystem::clear()
{
if (tree_.root) {
free_node(tree_.root);
}
if (tree_.pending) {
delete tree_.pending;
}
tree_.root = tree_.current = tree_.saved = tree_.pending = nullptr;
update_dirty_flag();
}
void
UndoSystem::apply(const UndoNode *node, int direction)
{
if (!node)
return;
switch (node->type) {
case UndoType::Insert:
case UndoType::Paste:
if (direction > 0) {
buf_.insert_text(node->row, node->col, node->text);
} else {
buf_.delete_text(node->row, node->col, node->text.size());
}
break;
case UndoType::Delete:
if (direction > 0) {
buf_.delete_text(node->row, node->col, node->text.size());
} else {
buf_.insert_text(node->row, node->col, node->text);
}
break;
case UndoType::Newline:
if (direction > 0) {
buf_.split_line(node->row, node->col);
} else {
buf_.join_lines(node->row);
}
break;
case UndoType::DeleteRow:
if (direction > 0) {
buf_.delete_row(node->row);
} else {
buf_.insert_row(node->row, node->text);
}
break;
}
}
void
UndoSystem::free_node(UndoNode *node)
{
if (!node)
return;
// Free child subtree(s) and sibling branches
if (node->child) {
// Free entire redo list starting at child, including each subtree
UndoNode *branch = node->child;
while (branch) {
UndoNode *next = branch->next;
free_node(branch);
branch = next;
}
node->child = nullptr;
}
delete node;
}
void
UndoSystem::free_branch(UndoNode *node)
{
// Free a branch list (node and its next siblings) including their subtrees
while (node) {
UndoNode *next = node->next;
free_node(node);
node = next;
}
}
static bool
dfs_find_parent(UndoNode *cur, UndoNode *target, UndoNode *&out_parent)
{
if (!cur)
return false;
for (UndoNode *child = cur->child; child != nullptr; child = child->next) {
if (child == target) {
out_parent = cur;
return true;
}
if (dfs_find_parent(child, target, out_parent))
return true;
}
return false;
}
UndoNode *
UndoSystem::find_parent(UndoNode *from, UndoNode *target)
{
if (!from || !target)
return nullptr;
if (from == target)
return nullptr;
UndoNode *parent = nullptr;
dfs_find_parent(from, target, parent);
return parent;
}
void
UndoSystem::update_dirty_flag()
{
// dirty if current != saved
bool dirty = (tree_.current != tree_.saved);
buf_.SetDirty(dirty);
}

45
UndoSystem.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef KTE_UNDOSYSTEM_H
#define KTE_UNDOSYSTEM_H
#include <string_view>
#include "UndoTree.h"
class Buffer;
class UndoSystem {
public:
explicit UndoSystem(Buffer &owner, UndoTree &tree);
void Begin(UndoType type);
void Append(char ch);
void Append(std::string_view text);
void commit();
void undo();
void redo();
void mark_saved();
void discard_pending();
void clear();
private:
void apply(const UndoNode *node, int direction); // +1 redo, -1 undo
void free_node(UndoNode *node);
void free_branch(UndoNode *node); // frees redo siblings only
UndoNode *find_parent(UndoNode *from, UndoNode *target);
void update_dirty_flag();
private:
Buffer &buf_;
UndoTree &tree_;
};
#endif // KTE_UNDOSYSTEM_H

View File

@@ -1,47 +1,3 @@
// Placeholder translation unit for UndoTree struct definition.
// Undo logic is implemented in UndoSystem.
#include "UndoTree.h"
#include <cassert>
void
UndoTree::Begin(const UndoKind kind, const size_t row, const size_t col)
{
if (this->pending != nullptr) {
if (this->pending->Kind() == kind) {
return;
}
this->Commit();
}
assert(this->pending == nullptr);
this->pending = new UndoNode(kind, row, col);
assert(this->pending != nullptr);
}
void
UndoTree::Commit()
{
if (this->pending == nullptr) {
return;
}
if (this->root == nullptr) {
assert(this->current == nullptr);
this->root = this->pending;
this->current = this->pending;
this->pending = nullptr;
return;
}
assert(this->current != nullptr);
if (this->current->Next() != nullptr) {
this->current->DeleteNext();
}
this->current->Next(this->pending);
this->current = this->pending;
this->pending = nullptr;
}

View File

@@ -2,18 +2,13 @@
#define KTE_UNDOTREE_H
#include "UndoNode.h"
#include <memory>
class UndoTree {
UndoTree() : root{nullptr}, current{nullptr}, pending{nullptr} {}
void Begin(UndoKind kind, size_t row, size_t col);
void Commit();
private:
UndoNode *root{nullptr};
UndoNode *current{nullptr};
UndoNode *pending{nullptr};
struct UndoTree {
UndoNode *root = nullptr; // first edit ever
UndoNode *current = nullptr; // current state of buffer
UndoNode *saved = nullptr; // points to node matching last save (for dirty flag)
UndoNode *pending = nullptr; // in-progress batch (detached)
};

View File

@@ -3,7 +3,7 @@ include(InstallRequiredSystemLibraries)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CPACK_DEBIAN_PACKAGE_DEBUG ON)
endif()
endif ()
set(CPACK_PACKAGE_VENDOR "Shimmering Clarity")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "kyle's editor")
@@ -14,11 +14,11 @@ set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
###################
### DEBIANESQUE ###
###################
if(${BUILD_GUI})
if (${BUILD_GUI})
set(CPACK_COMPONENTS_ALL gui nox)
else()
else ()
set(CPACK_COMPONENTS_ALL nox)
endif()
endif ()
set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
@@ -31,25 +31,25 @@ set(CPACK_PACKAGE_nox_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION})
set(CPACK_PACKAGE_nox_PACKAGE_NAME "kte")
set(CPACK_DEBIAN_nox_PACKAGE_NAME "ke")
if(BUILD_GUI)
set(CPACK_PACKAGE_gui_PACKAGE_NAME "kte")
set(CPACK_DEBIAN_gui_PACKAGE_NAME "kte")
if (BUILD_GUI)
set(CPACK_PACKAGE_gui_PACKAGE_NAME "kge")
set(CPACK_DEBIAN_gui_PACKAGE_NAME "kge")
set(CPACK_PACKAGE_gui_DESCRIPTION_SUMMARY " graphical front-end for kyle's editor")
set(CPACK_PACKAGE_gui_DESCRIPTION "graphical front-end for ${CPACK_PACKAGE_DESCRIPTION} ")
endif()
endif ()
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
if(LINUX)
if (LINUX)
set(CPACK_GENERATOR "DEB;STGZ;TGZ")
elseif(APPLE)
elseif (APPLE)
set(CPACK_GENERATOR "productbuild;TGZ")
elseif(MSVC OR MSYS OR MINGW)
elseif (MSVC OR MSYS OR MINGW)
set(CPACK_GENERATOR "NSIS;ZIP")
else()
else ()
set(CPACK_GENERATOR "ZIP")
endif()
endif ()
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP ")
set(CPACK_SOURCE_IGNORE_FILES

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -97,11 +97,12 @@ main(int argc, const char *argv[])
} else if (req_term) {
use_gui = false;
} else {
// Default depends on build target: kge defaults to GUI, kte to terminal
// Default depends on build target: kge defaults to GUI, kte to terminal
#if defined(KTE_DEFAULT_GUI)
use_gui = true;
use_gui = true;
#else
use_gui = false;
use_gui = false;
#endif
}
#endif