Remove packaging.cmake, deprecate test_undo setup, and add new testing infrastructure.

- Delete `packaging.cmake` to streamline build system.
- Deprecate `test_undo` in CMake setup; condition builds on `BUILD_TESTS`.
- Introduce `TestFrontend`, `TestRenderer`, and `TestInputHandler` for structured testing.
- Update `GUIInputHandler` and `Command` for enhanced buffer save handling and overwrite confirmation.
- Enhance kill ring operations and new prompt workflows in `Editor`.
This commit is contained in:
2025-11-30 03:18:50 -08:00
parent 8c8e4e59a4
commit 091bfa8095
10 changed files with 266 additions and 131 deletions

47
.idea/workspace.xml generated
View File

@@ -25,7 +25,6 @@
<config projectName="kte" targetName="kte" />
<config projectName="kte" targetName="imgui" />
<config projectName="kte" targetName="kge" />
<config projectName="kte" targetName="test_undo" />
</generated>
</component>
<component name="CMakeSettings" AUTO_RELOAD="true">
@@ -34,26 +33,17 @@
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Add non-linear undo/redo design documentation and improve `UndoSystem` with backspace batching and GUI integration fixes.">
<change afterPath="$PROJECT_DIR$/TestFrontend.cc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/TestInputHandler.cc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/TestRenderer.cc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/docs/TestFrontend.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/test_undo.cc" afterDir="false" />
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Add `TestFrontend` documentation and `UndoSystem` buffer reference update.&#10;&#10;- Document `TestFrontend` for programmatic testing, including examples and usage details.&#10;- Add `UpdateBufferReference` to `UndoSystem` to support updating buffer associations.">
<change afterPath="$PROJECT_DIR$/TestFrontend.h" afterDir="false" />
<change afterPath="$PROJECT_DIR$/TestInputHandler.h" afterDir="false" />
<change afterPath="$PROJECT_DIR$/TestRenderer.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Buffer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Buffer.h" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Command.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Command.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Command.h" beforeDir="false" afterPath="$PROJECT_DIR$/Command.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/GUIFrontend.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIFrontend.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/KKeymap.cc" beforeDir="false" afterPath="$PROJECT_DIR$/KKeymap.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Editor.h" beforeDir="false" afterPath="$PROJECT_DIR$/Editor.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/GUIInputHandler.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIInputHandler.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/TerminalInputHandler.cc" beforeDir="false" afterPath="$PROJECT_DIR$/TerminalInputHandler.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/UndoSystem.cc" beforeDir="false" afterPath="$PROJECT_DIR$/UndoSystem.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/UndoSystem.h" beforeDir="false" afterPath="$PROJECT_DIR$/UndoSystem.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/ke.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/ke.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/undo-state.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/undo-state.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fonts/brassmono.h" beforeDir="false" afterPath="$PROJECT_DIR$/fonts/brassmono.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmake/packaging.cmake" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -147,7 +137,7 @@
<recent name="$PROJECT_DIR$/docs" />
</key>
</component>
<component name="RunManager" selected="CMake Application.test_undo">
<component name="RunManager" selected="CMake Application.imgui">
<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" />
@@ -168,16 +158,10 @@
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="test_undo" 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="test_undo" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="kte" RUN_TARGET_NAME="test_undo">
<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" />
<item itemvalue="CMake Application.test_undo" />
</list>
</component>
<component name="TaskManager">
@@ -187,7 +171,7 @@
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1764457173148</updated>
<workItem from="1764457174208" duration="41399000" />
<workItem from="1764457174208" duration="42780000" />
</task>
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
<option name="closed" value="true" />
@@ -229,7 +213,15 @@
<option name="project" value="LOCAL" />
<updated>1764496151303</updated>
</task>
<option name="localTasksCounter" value="6" />
<task id="LOCAL-00006" summary="Add `TestFrontend` documentation and `UndoSystem` buffer reference update.&#10;&#10;- Document `TestFrontend` for programmatic testing, including examples and usage details.&#10;- Add `UpdateBufferReference` to `UndoSystem` to support updating buffer associations.">
<option name="closed" value="true" />
<created>1764500200942</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1764500200942</updated>
</task>
<option name="localTasksCounter" value="7" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -248,7 +240,8 @@
<MESSAGE value="Enable installation targets." />
<MESSAGE value="Add `UndoSystem` implementation and refactor `UndoNode` for simplicity." />
<MESSAGE value="Add non-linear undo/redo design documentation and improve `UndoSystem` with backspace batching and GUI integration fixes." />
<option name="LAST_COMMIT_MESSAGE" value="Add non-linear undo/redo design documentation and improve `UndoSystem` with backspace batching and GUI integration fixes." />
<MESSAGE value="Add `TestFrontend` documentation and `UndoSystem` buffer reference update.&#10;&#10;- Document `TestFrontend` for programmatic testing, including examples and usage details.&#10;- Add `UpdateBufferReference` to `UndoSystem` to support updating buffer associations." />
<option name="LAST_COMMIT_MESSAGE" value="Add `TestFrontend` documentation and `UndoSystem` buffer reference update.&#10;&#10;- Document `TestFrontend` for programmatic testing, including examples and usage details.&#10;- Add `UpdateBufferReference` to `UndoSystem` to support updating buffer associations." />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

View File

@@ -4,11 +4,12 @@ project(kte)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 17)
set(KTE_VERSION "0.0.1")
set(KTE_VERSION "0.1.0")
# Default to terminal-only build to avoid SDL/OpenGL dependency by default.
# Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available.
set(BUILD_GUI OFF CACHE BOOL "Enable building the graphical version.")
set(BUILD_TESTS OFF CACHE BOOL "Enable building test programs.")
option(KTE_USE_PIECE_TABLE "Use PieceTable instead of GapBuffer implementation" OFF)
set(KTE_FONT_SIZE "18.0" CACHE STRING "Default font size for GUI")
@@ -103,19 +104,22 @@ install(TARGETS kte
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# test_undo executable for testing undo/redo system
add_executable(test_undo
test_undo.cc
${COMMON_SOURCES}
${COMMON_HEADERS}
)
if (BUILD_TESTS)
# test_undo executable for testing undo/redo system
add_executable(test_undo
test_undo.cc
${COMMON_SOURCES}
${COMMON_HEADERS}
)
if (KTE_USE_PIECE_TABLE)
target_compile_definitions(test_undo PRIVATE KTE_USE_PIECE_TABLE=1)
if (KTE_USE_PIECE_TABLE)
target_compile_definitions(test_undo PRIVATE KTE_USE_PIECE_TABLE=1)
endif ()
target_link_libraries(test_undo ${CURSES_LIBRARIES})
endif ()
target_link_libraries(test_undo ${CURSES_LIBRARIES})
if (${BUILD_GUI})
target_sources(kte PRIVATE
Font.h
@@ -142,7 +146,27 @@ if (${BUILD_GUI})
target_compile_definitions(kge PRIVATE KTE_BUILD_GUI=1 KTE_DEFAULT_GUI=1 KTE_FONT_SIZE=${KTE_FONT_SIZE})
target_link_libraries(kge ${CURSES_LIBRARIES} imgui)
install(TARGETS kge
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# On macOS, build kge as a proper .app bundle
if (APPLE)
# Configure Info.plist with version and identifiers
set(KGE_BUNDLE_ID "dev.kyle.kge")
configure_file(
${CMAKE_CURRENT_LIST_DIR}/cmake/Info.plist.in
${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist
@ONLY)
set_target_properties(kge PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID}
MACOSX_BUNDLE_BUNDLE_NAME "kge"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist")
install(TARGETS kge
BUNDLE DESTINATION .
)
else()
install(TARGETS kge
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
endif ()

View File

@@ -1,4 +1,5 @@
#include <algorithm>
#include <filesystem>
#include "Command.h"
#include "Editor.h"
@@ -418,13 +419,22 @@ cmd_save(CommandContext &ctx)
// non-existent path (not yet file-backed but has a filename).
if (!buf->IsFileBacked()) {
if (!buf->Filename().empty()) {
if (!buf->SaveAs(buf->Filename(), err)) {
ctx.editor.SetStatus(err);
return false;
// If first-time save to an existing path, confirm overwrite
if (std::filesystem::exists(buf->Filename())) {
ctx.editor.StartPrompt(Editor::PromptKind::Confirm, "Overwrite", "");
ctx.editor.SetPendingOverwritePath(buf->Filename());
ctx.editor.SetStatus(
std::string("Overwrite existing file '") + buf->Filename() + "'? (y/N)");
return true;
} else {
if (!buf->SaveAs(buf->Filename(), err)) {
ctx.editor.SetStatus(err);
return false;
}
buf->SetDirty(false);
ctx.editor.SetStatus("Saved " + buf->Filename());
return true;
}
buf->SetDirty(false);
ctx.editor.SetStatus("Saved " + buf->Filename());
return true;
}
// If buffer has no name, prompt for a filename
ctx.editor.StartPrompt(Editor::PromptKind::SaveAs, "Save as", "");
@@ -933,16 +943,52 @@ cmd_newline(CommandContext &ctx)
if (!buf) {
ctx.editor.SetStatus("No buffer to save");
} else {
// If this is a first-time save (unnamed/non-file-backed) and the
// target exists, ask for confirmation before overwriting.
if (!buf->IsFileBacked() && std::filesystem::exists(value)) {
ctx.editor.StartPrompt(Editor::PromptKind::Confirm, "Overwrite", "");
ctx.editor.SetPendingOverwritePath(value);
ctx.editor.SetStatus(
std::string("Overwrite existing file '") + value + "'? (y/N)");
} else {
std::string err;
if (!buf->SaveAs(value, err)) {
ctx.editor.SetStatus(err);
} else {
buf->SetDirty(false);
ctx.editor.SetStatus("Saved as " + value);
if (auto *u = buf->Undo())
u->mark_saved();
}
}
}
}
} else if (kind == Editor::PromptKind::Confirm) {
// Confirmation for potentially destructive operations (e.g., overwrite on save-as)
Buffer *buf = ctx.editor.CurrentBuffer();
const std::string target = ctx.editor.PendingOverwritePath();
if (!target.empty() && buf) {
bool yes = false;
if (!value.empty()) {
char c = value[0];
yes = (c == 'y' || c == 'Y');
}
if (yes) {
std::string err;
if (!buf->SaveAs(value, err)) {
if (!buf->SaveAs(target, err)) {
ctx.editor.SetStatus(err);
} else {
buf->SetDirty(false);
ctx.editor.SetStatus("Saved as " + value);
ctx.editor.SetStatus("Saved as " + target);
if (auto *u = buf->Undo())
u->mark_saved();
}
} else {
ctx.editor.SetStatus("Save canceled");
}
ctx.editor.ClearPendingOverwritePath();
} else {
ctx.editor.SetStatus("Nothing to confirm");
}
}
return true;
@@ -1924,7 +1970,7 @@ cmd_delete_word_prev(CommandContext &ctx)
ensure_cursor_visible(ctx.editor, *buf);
if (!killed_total.empty()) {
if (ctx.editor.KillChain())
ctx.editor.KillRingAppend(killed_total);
ctx.editor.KillRingPrepend(killed_total);
else
ctx.editor.KillRingPush(killed_total);
ctx.editor.SetKillChain(true);

View File

@@ -99,6 +99,18 @@ public:
}
void KillRingPrepend(const std::string &text)
{
if (text.empty())
return;
if (kill_ring_.empty()) {
KillRingPush(text);
} else {
kill_ring_.front() = text + kill_ring_.front();
}
}
[[nodiscard]] std::string KillRingHead() const
{
return kill_ring_.empty() ? std::string() : kill_ring_.front();
@@ -349,6 +361,25 @@ public:
}
// --- Overwrite confirmation (save-as on existing file) ---
void SetPendingOverwritePath(const std::string &path)
{
pending_overwrite_path_ = path;
}
void ClearPendingOverwritePath()
{
pending_overwrite_path_.clear();
}
[[nodiscard]] const std::string &PendingOverwritePath() const
{
return pending_overwrite_path_;
}
[[nodiscard]] const std::string &PromptLabel() const
{
return prompt_label_;
@@ -441,6 +472,7 @@ private:
PromptKind prompt_kind_ = PromptKind::None;
std::string prompt_label_;
std::string prompt_text_;
std::string pending_overwrite_path_;
};
#endif // KTE_EDITOR_H

View File

@@ -1,5 +1,6 @@
#include <SDL.h>
#include <cstdio>
#include <ncurses.h>
#include "GUIInputHandler.h"
#include "KKeymap.h"
@@ -26,7 +27,10 @@ map_key(const SDL_Keycode key,
// If previous key was ESC, interpret this as Meta via ESC keymap
if (esc_meta) {
int ascii_key = 0;
if (key >= SDLK_a && key <= SDLK_z) {
if (key == SDLK_BACKSPACE) {
// ESC BACKSPACE: map to DeleteWordPrev using ncurses KEY_BACKSPACE constant
ascii_key = KEY_BACKSPACE;
} else if (key >= SDLK_a && key <= SDLK_z) {
ascii_key = static_cast<int>('a' + (key - SDLK_a));
} else if (key == SDLK_COMMA) {
ascii_key = '<';
@@ -98,14 +102,7 @@ map_key(const SDL_Keycode key,
return true;
case SDLK_ESCAPE:
k_prefix = false;
esc_meta = true; // next key will be treated as Meta
// Cancel any universal argument collection
uarg_active = false;
uarg_collecting = false;
uarg_negative = false;
uarg_had_digits = false;
uarg_value = 0;
uarg_text.clear();
esc_meta = true; // next key will be treated as Meta
out.hasCommand = false; // no immediate command for bare ESC in GUI
return true;
default:
@@ -222,7 +219,10 @@ map_key(const SDL_Keycode key,
// Alt/Meta bindings (ESC f/b equivalent)
if (is_alt) {
int ascii_key = 0;
if (key >= SDLK_a && key <= SDLK_z) {
if (key == SDLK_BACKSPACE) {
// Alt BACKSPACE: map to DeleteWordPrev using ncurses KEY_BACKSPACE constant
ascii_key = KEY_BACKSPACE;
} else if (key >= SDLK_a && key <= SDLK_z) {
ascii_key = static_cast<int>('a' + (key - SDLK_a));
} else if (key == SDLK_COMMA) {
ascii_key = '<';

View File

@@ -85,15 +85,8 @@ map_key_to_command(const int ch,
// ESC as cancel of prefix; many terminals send meta sequences as ESC+...
if (ch == 27) {
// ESC
k_prefix = false;
esc_meta = true; // next key will be considered meta-modified
// Cancel any universal argument collection
uarg_active = false;
uarg_collecting = false;
uarg_negative = false;
uarg_had_digits = false;
uarg_value = 0;
uarg_text.clear();
k_prefix = false;
esc_meta = true; // next key will be considered meta-modified
out.hasCommand = false; // no command yet
return true;
}

41
TestFrontend.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* TestFrontend.h - headless frontend for testing with programmable input
*/
#ifndef KTE_TEST_FRONTEND_H
#define KTE_TEST_FRONTEND_H
#include "Frontend.h"
#include "TestInputHandler.h"
#include "TestRenderer.h"
class TestFrontend final : public Frontend {
public:
TestFrontend() = default;
~TestFrontend() override = default;
bool Init(Editor &ed) override;
void Step(Editor &ed, bool &running) override;
void Shutdown() override;
TestInputHandler &Input()
{
return input_;
}
TestRenderer &Renderer()
{
return renderer_;
}
private:
TestInputHandler input_{};
TestRenderer renderer_{};
};
#endif // KTE_TEST_FRONTEND_H

33
TestInputHandler.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* TestInputHandler.h - programmable input handler for testing
*/
#ifndef KTE_TEST_INPUT_HANDLER_H
#define KTE_TEST_INPUT_HANDLER_H
#include "InputHandler.h"
#include <queue>
class TestInputHandler : public InputHandler {
public:
TestInputHandler() = default;
~TestInputHandler() override = default;
bool Poll(MappedInput &out) override;
void QueueCommand(CommandId id, const std::string &arg = "", int count = 0);
void QueueText(const std::string &text);
bool IsEmpty() const
{
return queue_.empty();
}
private:
std::queue<MappedInput> queue_;
};
#endif // KTE_TEST_INPUT_HANDLER_H

35
TestRenderer.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* TestRenderer.h - minimal renderer for testing (no actual display)
*/
#ifndef KTE_TEST_RENDERER_H
#define KTE_TEST_RENDERER_H
#include "Renderer.h"
#include <cstddef>
class TestRenderer : public Renderer {
public:
TestRenderer() = default;
~TestRenderer() override = default;
void Draw(Editor &ed) override;
std::size_t GetDrawCount() const
{
return draw_count_;
}
void ResetDrawCount()
{
draw_count_ = 0;
}
private:
std::size_t draw_count_ = 0;
};
#endif // KTE_TEST_RENDERER_H

View File

@@ -1,62 +0,0 @@
# Packaging support
include(InstallRequiredSystemLibraries)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CPACK_DEBIAN_PACKAGE_DEBUG ON)
endif ()
set(CPACK_PACKAGE_VENDOR "Shimmering Clarity")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "kyle's editor")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
###################
### DEBIANESQUE ###
###################
if (${BUILD_GUI})
set(CPACK_COMPONENTS_ALL gui nox)
else ()
set(CPACK_COMPONENTS_ALL nox)
endif ()
set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
set(CPACK_DEBIAN_PACKAGE_SECTION universe/editors)
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "K. Isom")
set(CPACK_PACKAGE_nox_DESCRIPTION_SUMMARY "kyle's editor")
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 "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 ()
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
if (LINUX)
set(CPACK_GENERATOR "DEB;STGZ;TGZ")
elseif (APPLE)
set(CPACK_GENERATOR "productbuild;TGZ")
elseif (MSVC OR MSYS OR MINGW)
set(CPACK_GENERATOR "NSIS;ZIP")
else ()
set(CPACK_GENERATOR "ZIP")
endif ()
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP ")
set(CPACK_SOURCE_IGNORE_FILES
/.git
/.idea
/dist
/.*build.*)
include(CPack)
cpack_add_component(gui DEPENDS nox)