diff --git a/AK/String.h b/AK/AKString.h similarity index 100% rename from AK/String.h rename to AK/AKString.h diff --git a/AK/Bitmap.h b/AK/Bitmap.h index c2f6993f761..cd89ff2996a 100644 --- a/AK/Bitmap.h +++ b/AK/Bitmap.h @@ -1,6 +1,6 @@ #pragma once -#include "StdLib.h" +#include "StdLibExtras.h" #include "Types.h" #include "kmalloc.h" #include "Assertions.h" diff --git a/AK/Buffer.h b/AK/Buffer.h index efed0313b44..2010bf16136 100644 --- a/AK/Buffer.h +++ b/AK/Buffer.h @@ -3,7 +3,7 @@ #include "Assertions.h" #include "Retainable.h" #include "RetainPtr.h" -#include "StdLib.h" +#include "StdLibExtras.h" #include "kmalloc.h" namespace AK { diff --git a/AK/ByteBuffer.h b/AK/ByteBuffer.h index c576ad2b747..b005deefe77 100644 --- a/AK/ByteBuffer.h +++ b/AK/ByteBuffer.h @@ -2,7 +2,7 @@ #include "Buffer.h" #include "Types.h" -#include "StdLib.h" +#include "StdLibExtras.h" namespace AK { diff --git a/AK/DoublyLinkedList.h b/AK/DoublyLinkedList.h index 60288ef2628..6c7f8dd6b53 100644 --- a/AK/DoublyLinkedList.h +++ b/AK/DoublyLinkedList.h @@ -1,6 +1,6 @@ #pragma once -#include "StdLib.h" +#include "StdLibExtras.h" namespace AK { diff --git a/AK/FileSystemPath.h b/AK/FileSystemPath.h index 980f836226f..d56625dcaf4 100644 --- a/AK/FileSystemPath.h +++ b/AK/FileSystemPath.h @@ -1,6 +1,6 @@ #pragma once -#include "String.h" +#include "AKString.h" namespace AK { diff --git a/AK/Function.h b/AK/Function.h index fe501805177..d1aab89d451 100644 --- a/AK/Function.h +++ b/AK/Function.h @@ -27,7 +27,7 @@ #include "Assertions.h" #include "OwnPtr.h" -#include "StdLib.h" +#include "StdLibExtras.h" namespace AK { diff --git a/AK/HashMap.h b/AK/HashMap.h index 0035732a16a..b1a68abb5b7 100644 --- a/AK/HashMap.h +++ b/AK/HashMap.h @@ -1,7 +1,7 @@ #pragma once #include "HashTable.h" -#include "StdLib.h" +#include "StdLibExtras.h" #include "kstdio.h" namespace AK { diff --git a/AK/HashTable.h b/AK/HashTable.h index 91df234be4f..68e2c164a2f 100644 --- a/AK/HashTable.h +++ b/AK/HashTable.h @@ -3,7 +3,7 @@ #include "Assertions.h" #include "DoublyLinkedList.h" #include "Traits.h" -#include "StdLib.h" +#include "StdLibExtras.h" #include "kstdio.h" //#define HASHTABLE_DEBUG diff --git a/AK/Makefile b/AK/Makefile index b706a03eef2..1d6353827cb 100644 --- a/AK/Makefile +++ b/AK/Makefile @@ -5,7 +5,7 @@ CXXFLAGS = -std=c++17 -O0 -W -Wall -ggdb3 all: $(PROGRAM) -test.o: Vector.h String.h StringImpl.h MappedFile.h HashTable.h SinglyLinkedList.h Traits.h HashMap.h TemporaryFile.h Buffer.h FileSystemPath.h StringBuilder.h +test.o: Vector.h AKString.h StringImpl.h MappedFile.h HashTable.h SinglyLinkedList.h Traits.h HashMap.h TemporaryFile.h Buffer.h FileSystemPath.h StringBuilder.h .cpp.o: $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/AK/MappedFile.h b/AK/MappedFile.h index d93908c8545..4fbc1872dd3 100644 --- a/AK/MappedFile.h +++ b/AK/MappedFile.h @@ -1,6 +1,6 @@ #pragma once -#include "String.h" +#include "AKString.h" namespace AK { diff --git a/AK/Noncopyable.h b/AK/Noncopyable.h new file mode 100644 index 00000000000..ff7df2346b7 --- /dev/null +++ b/AK/Noncopyable.h @@ -0,0 +1,6 @@ +#pragma once + +#define AK_MAKE_NONCOPYABLE(c) \ +private: \ + c(const c&) = delete; \ + c& operator=(const c&) = delete; diff --git a/AK/OwnPtr.h b/AK/OwnPtr.h index 24c92642a04..c6b55cdb3a4 100644 --- a/AK/OwnPtr.h +++ b/AK/OwnPtr.h @@ -1,6 +1,6 @@ #pragma once -#include "StdLib.h" +#include "StdLibExtras.h" #include "Types.h" #include "Traits.h" diff --git a/AK/StdLib.h b/AK/StdLibExtras.h similarity index 100% rename from AK/StdLib.h rename to AK/StdLibExtras.h diff --git a/AK/String.cpp b/AK/String.cpp index bf8339e4494..fd4b34f06ed 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -1,5 +1,5 @@ -#include "String.h" -#include "StdLib.h" +#include "AKString.h" +#include "StdLibExtras.h" namespace AK { diff --git a/AK/StringBuilder.h b/AK/StringBuilder.h index 2eb8b1880ea..ca38cc26c2b 100644 --- a/AK/StringBuilder.h +++ b/AK/StringBuilder.h @@ -1,6 +1,6 @@ #pragma once -#include "String.h" +#include "AKString.h" #include "Vector.h" namespace AK { diff --git a/AK/StringImpl.cpp b/AK/StringImpl.cpp index 8fbf11f6928..53719e35496 100644 --- a/AK/StringImpl.cpp +++ b/AK/StringImpl.cpp @@ -1,5 +1,5 @@ #include "StringImpl.h" -#include "StdLib.h" +#include "StdLibExtras.h" #include "kmalloc.h" namespace AK { diff --git a/AK/TemporaryFile.h b/AK/TemporaryFile.h index bcc88b7652b..4596a214d05 100644 --- a/AK/TemporaryFile.h +++ b/AK/TemporaryFile.h @@ -1,6 +1,6 @@ #pragma once -#include "String.h" +#include "AKString.h" #include namespace AK { diff --git a/AK/test.cpp b/AK/test.cpp index 5d2bcbca145..49f057c7603 100644 --- a/AK/test.cpp +++ b/AK/test.cpp @@ -1,4 +1,4 @@ -#include "String.h" +#include "AKString.h" //#include "StringBuilder.h" #include "Vector.h" #include diff --git a/Editor/Document.cpp b/Editor/Document.cpp new file mode 100644 index 00000000000..26d3b24f543 --- /dev/null +++ b/Editor/Document.cpp @@ -0,0 +1,46 @@ +#include "Document.h" +#include "FileReader.h" + +OwnPtr Document::create_from_file(const std::string& path) +{ + auto document = make(); + + FileReader reader(path); + while (reader.can_read()) { + auto line = reader.read_line(); + document->m_lines.push_back(Line(line)); + } + + return document; +} + +void Document::dump() +{ + fprintf(stderr, "Document{%p}\n", this); + for (size_t i = 0; i < m_lines.size(); ++i) { + fprintf(stderr, "[%02zu] %s\n", i, m_lines[i].data().c_str()); + } +} + +bool Document::backspace_at(Position position) +{ + return false; +} + +bool Document::insert_at(Position position, const std::string& text) +{ + static FILE* f = fopen("log", "a"); + fprintf(f, "@%zu,%zu: +%s\n", position.line(), position.column(), text.c_str()); + fflush(f); + ASSERT(position.is_valid()); + if (!position.is_valid()) + return false; + ASSERT(position.line() < line_count()); + if (position.line() >= line_count()) + return false; + Line& line = m_lines[position.line()]; + if (position.column() > line.length()) + return false; + line.insert(position.column(), text); + return true; +} diff --git a/Editor/Document.h b/Editor/Document.h new file mode 100644 index 00000000000..92430381808 --- /dev/null +++ b/Editor/Document.h @@ -0,0 +1,27 @@ +#pragma once + +#include "cuki.h" +#include "Line.h" +#include "Position.h" +#include "OwnPtr.h" +#include + +class Document { +public: + Document() { } + ~Document() { } + + const std::deque& lines() const { return m_lines; } + std::deque& lines() { return m_lines; } + size_t line_count() const { return m_lines.size(); } + + static OwnPtr create_from_file(const std::string& path); + + bool insert_at(Position, const std::string&); + bool backspace_at(Position); + + void dump(); + +private: + std::deque m_lines; +}; diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp new file mode 100644 index 00000000000..0ad3b3edd03 --- /dev/null +++ b/Editor/Editor.cpp @@ -0,0 +1,336 @@ +#include "Editor.h" +#include "Document.h" +#include "InsertOperation.h" + +#define _XOPEN_SOURCE_EXTENDED +#include +#include + +static int statusbar_attributes; +static int ruler_attributes; + +Editor::Editor() +{ + setlocale(LC_ALL, ""); + initscr(); + start_color(); + use_default_colors(); + + init_pair(1, COLOR_WHITE, COLOR_BLUE); + init_pair(2, COLOR_BLUE, -1); + + statusbar_attributes = COLOR_PAIR(1); + ruler_attributes = COLOR_PAIR(2); + + raw(); + keypad(stdscr, true); + timeout(10); + noecho(); + refresh(); +} + +Editor::~Editor() +{ + //move(2, 2); + //printw("*** Press any key to exit! ***"); + //getch(); + endwin(); +} + +void Editor::set_document(OwnPtr&& document) +{ + m_document = std::move(document); + m_cursor.move_to(0, 0); + m_scroll_position.move_to(0, 0); +} + +void Editor::redraw() +{ + clear(); + if (!m_document) + return; + + size_t window_height = getmaxy(stdscr); + size_t window_width = getmaxx(stdscr); + + for (size_t row = 0; row < window_height - 1; ++row) { + size_t current_document_line = m_scroll_position.line() + row; + size_t current_document_column = m_scroll_position.column(); + + move(row, 0); + + if (current_document_line >= m_document->line_count()) { + printw("~"); + } else { + attron(ruler_attributes); + printw("%3d ", current_document_line); + attroff(ruler_attributes); + m_ruler_width = 4; + size_t line_length = m_document->lines()[current_document_line].data().size(); + const char* line_data = m_document->lines()[current_document_line].data().c_str(); + if (m_scroll_position.column() < line_length) + addnstr(line_data + m_scroll_position.column(), window_width - m_ruler_width); + } + } + + draw_status_bar(); + + draw_cursor(); + refresh(); +} + +void Editor::draw_cursor() +{ + ssize_t cursor_row_on_screen = m_cursor.line() - m_scroll_position.line(); + ssize_t cursor_column_on_screen = m_cursor.column() - m_scroll_position.column(); + + move(cursor_row_on_screen, cursor_column_on_screen + m_ruler_width); + +} + +void Editor::draw_status_bar() +{ + int old_background = getbkgd(stdscr); + bkgdset(' ' | statusbar_attributes); + + size_t window_height = getmaxy(stdscr); + size_t window_width = getmaxx(stdscr); + + move(window_height - 1, 0); + clrtoeol(); + + if (is_editing_document()) { + attron(A_STANDOUT); + printw("* Editing *"); + attroff(A_STANDOUT); + } else if (is_editing_command()) { + printw("\\%s", m_command.c_str()); + } else { + attron(A_BOLD); + addstr("~(^_^)~ "); + if (m_status_text.size() > 0) { + addstr(m_status_text.c_str()); + } + attroff(A_BOLD); + } + + move(window_height - 1, window_width - 20); + printw("%zu, %zu", m_scroll_position.line(), m_scroll_position.column()); + + move(window_height - 1, window_width - 8); + printw("%zu, %zu", m_cursor.line(), m_cursor.column()); + attroff(statusbar_attributes); + + bkgdset(old_background); +} + +int Editor::exec() +{ + while (!m_should_quit) { + redraw(); + int ch = getch(); + if (ch == ERR) { + continue; + fprintf(stderr, "getch() returned ERR\n"); + break; + } + + if (is_editing_document() || is_editing_command()) { + if (ch == 27) + set_mode(Idle); + else { + if (is_editing_document()) + handle_document_key_press(ch); + else + handle_command_key_press(ch); + } + } else { + switch (ch) { + case 'h': move_left(); break; + case 'j': move_down(); break; + case 'k': move_up(); break; + case 'l': move_right(); break; + case 'i': set_mode(EditingDocument); break; + case 'q': m_should_quit = true; break; + case '\\': set_mode(EditingCommand); break; + } + } + } + return 0; +} + +void Editor::move_left() +{ + if (m_cursor.column() == 0) + return; + m_cursor.move_by(0, -1); + update_scroll_position_if_needed(); +} + +void Editor::move_down() +{ + if (m_cursor.line() >= max_line()) + return; + m_cursor.move_by(1, 0); + if (m_cursor.column() > max_column()) + m_cursor.set_column(max_column()); + + update_scroll_position_if_needed(); +} + +void Editor::move_up() +{ + if (m_cursor.line() == 0) + return; + m_cursor.move_by(-1, 0); + if (m_cursor.column() > max_column()) + m_cursor.set_column(max_column()); + + update_scroll_position_if_needed(); +} + +void Editor::move_right() +{ + if (m_cursor.column() >= max_column()) + return; + m_cursor.move_by(0, 1); + update_scroll_position_if_needed(); +} + +size_t Editor::max_line() const +{ + return m_document->line_count() - 1; +} + +size_t Editor::max_column() const +{ + return m_document->lines()[m_cursor.line()].data().size(); +} + +void Editor::update_scroll_position_if_needed() +{ + ssize_t max_row = getmaxy(stdscr) - 2; + ssize_t max_column = getmaxx(stdscr) - 1 - m_ruler_width; + + ssize_t cursor_row_on_screen = m_cursor.line() - m_scroll_position.line(); + ssize_t cursor_column_on_screen = m_cursor.column() - m_scroll_position.column(); + + // FIXME: Need to move by more than just 1 step sometimes! + + if (cursor_row_on_screen < 0) { + m_scroll_position.move_by(-1, 0); + } + + if (cursor_row_on_screen > max_row) { + m_scroll_position.move_by(1, 0); + } + + if (cursor_column_on_screen < 0) { + m_scroll_position.move_by(0, -1); + } + + if (cursor_column_on_screen > max_column) { + m_scroll_position.move_by(0, 1); + } +} + +void Editor::set_mode(Mode m) +{ + if (m_mode == m) + return; + m_mode = m; + m_command = ""; +} + +static bool is_backspace(int ch) +{ + return ch == 8 || ch == 127; +} + +static bool is_newline(int ch) +{ + return ch == 10 || ch == 13; +} + +void Editor::handle_command_key_press(int ch) +{ + if (is_backspace(ch)) { + if (m_command.size() > 0) + m_command.pop_back(); + else + set_mode(Idle); + return; + } + if (is_newline(ch)) { + if (m_command.size() > 0) + exec_command(); + set_mode(Idle); + return; + } + m_command.push_back(ch); +} + +void Editor::handle_document_key_press(int ch) +{ + if (is_backspace(ch)) { + //auto op = make(1); + m_document->backspace_at(m_cursor); + } else { + auto op = make(ch); + run(std::move(op)); + } +} + +void Editor::run(OwnPtr&& op) +{ + ASSERT(op); + op->apply(*this); + m_undo_stack.push(std::move(op)); +} + +void Editor::insert_at_cursor(int ch) +{ + std::string s; + s += ch; + m_document->insert_at(m_cursor, s); + m_cursor.move_by(0, 1); +} + +bool Editor::insert_text_at_cursor(const std::string& text) +{ + ASSERT(text.size() == 1); + m_document->insert_at(m_cursor, text); + m_cursor.move_by(0, text.size()); + return true; +} + +bool Editor::remove_text_at_cursor(const std::string& text) +{ + // FIXME: Implement + ASSERT(false); + return false; +} + +void Editor::set_status_text(const std::string& text) +{ + m_status_text = text; +} + +void Editor::exec_command() +{ + if (m_command == "q") { + m_should_quit = true; + return; + } + + if (m_command == "about") { + set_status_text("cuki editor!"); + return; + } + + std::string s; + s = "Invalid command: '"; + s += m_command; + s += "'"; + set_status_text(s); +} diff --git a/Editor/Editor.h b/Editor/Editor.h new file mode 100644 index 00000000000..ec1f53ff11e --- /dev/null +++ b/Editor/Editor.h @@ -0,0 +1,74 @@ +#pragma once + +#include "OwnPtr.h" +#include "Position.h" +#include "UndoStack.h" +#include + +class Document; + +class Editor { +public: + Editor(); + ~Editor(); + + void set_document(OwnPtr&&); + void redraw(); + + int exec(); + + enum Mode { + Idle, + EditingCommand, + EditingDocument, + }; + + void set_mode(Mode); + Mode mode() const { return m_mode; } + bool is_editing_document() const { return m_mode == EditingDocument; } + bool is_editing_command() const { return m_mode == EditingCommand; } + bool is_idle() const { return m_mode == Idle; } + + void set_status_text(const std::string&); + + bool insert_text_at_cursor(const std::string&); + bool remove_text_at_cursor(const std::string&); + + void run(OwnPtr&&); + +private: + void move_left(); + void move_down(); + void move_up(); + void move_right(); + + size_t max_line() const; + size_t max_column() const; + + void update_scroll_position_if_needed(); + + void draw_status_bar(); + void draw_cursor(); + + void handle_document_key_press(int ch); + void handle_command_key_press(int ch); + + void insert_at_cursor(int ch); + + void exec_command(); + + OwnPtr m_document; + + UndoStack m_undo_stack; + + // Document relative + Position m_scroll_position; + Position m_cursor; + + std::string m_command; + std::string m_status_text; + + bool m_should_quit { false }; + size_t m_ruler_width { 0 }; + Mode m_mode { Idle }; +}; diff --git a/Editor/FileReader.cpp b/Editor/FileReader.cpp new file mode 100644 index 00000000000..0788d06b02b --- /dev/null +++ b/Editor/FileReader.cpp @@ -0,0 +1,40 @@ +#include "FileReader.h" + +FileReader::FileReader(const std::string& path) + : m_path(path) +{ + m_file = fopen(path.c_str(), "r"); +} + +FileReader::~FileReader() +{ + if (m_file) + fclose(m_file); + m_file = nullptr; +} + +bool FileReader::can_read() const +{ + return m_file && !feof(m_file); +} + +std::string FileReader::read_line() +{ + if (!m_file) { + fprintf(stderr, "Error: FileReader::readLine() called on invalid FileReader for '%s'\n", m_path.c_str()); + return std::string(); + } + + std::string line; + + while (can_read()) { + int ch = fgetc(m_file); + if (ch == EOF) + break; + if (ch == '\n') + break; + line += ch; + } + + return line; +} diff --git a/Editor/FileReader.h b/Editor/FileReader.h new file mode 100644 index 00000000000..54fde0780eb --- /dev/null +++ b/Editor/FileReader.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class FileReader { +public: + explicit FileReader(const std::string& path); + ~FileReader(); + + bool can_read() const; + std::string read_line(); + +private: + std::string m_path; + FILE* m_file { nullptr }; +}; diff --git a/Editor/InsertOperation.cpp b/Editor/InsertOperation.cpp new file mode 100644 index 00000000000..cd7de9956b8 --- /dev/null +++ b/Editor/InsertOperation.cpp @@ -0,0 +1,26 @@ +#include "InsertOperation.h" +#include "Editor.h" + +InsertOperation::InsertOperation(const std::string& text) + : m_text(text) +{ +} + +InsertOperation::InsertOperation(char ch) + : m_text(&ch, 1) +{ +} + +InsertOperation::~InsertOperation() +{ +} + +bool InsertOperation::apply(Editor& editor) +{ + return editor.insert_text_at_cursor(m_text); +} + +bool InsertOperation::unapply(Editor& editor) +{ + return editor.remove_text_at_cursor(m_text); +} diff --git a/Editor/InsertOperation.h b/Editor/InsertOperation.h new file mode 100644 index 00000000000..97096ed3124 --- /dev/null +++ b/Editor/InsertOperation.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Operation.h" +#include + +class InsertOperation final : public Operation { +public: + explicit InsertOperation(const std::string&); + explicit InsertOperation(char); + ~InsertOperation(); + + virtual bool apply(Editor&) override; + virtual bool unapply(Editor&) override; + +private: + std::string m_text; +}; diff --git a/Editor/Line.cpp b/Editor/Line.cpp new file mode 100644 index 00000000000..431bf1d04fa --- /dev/null +++ b/Editor/Line.cpp @@ -0,0 +1,95 @@ +#include "Line.h" + +Chunk::Chunk(const std::string& str) + : m_data(str) +{ +} + +Chunk::~Chunk() +{ +} + +Line::Line(const std::string& str) +{ + m_chunks.push_back(Chunk(str)); +} + +Line::Line(Line&& other) + : m_chunks(std::move(other.m_chunks)) +{ +} + +Line::~Line() +{ +} + +std::string Line::data() const +{ + std::string str; + for (auto& chunk : m_chunks) + str += chunk.data(); + return str; +} + +void Line::append(const std::string& text) +{ + m_chunks.push_back(Chunk(text)); +} + +void Line::prepend(const std::string& text) +{ + m_chunks.push_front(Chunk(text)); +} + +void Line::insert(size_t index, const std::string& text) +{ + if (index == 0) { + prepend(text); + return; + } + + if (index == length()) { + append(text); + return; + } + + auto chunkAddress = chunkIndexForPosition(index); + auto chunkIndex = std::get<0>(chunkAddress); + auto& chunk = m_chunks[chunkIndex]; + auto indexInChunk = std::get<1>(chunkAddress); + + static FILE* f = fopen("log", "a"); + fprintf(f, "#Column:%zu, Chunk:%zu, Index:%zu\n", index, chunkIndex, indexInChunk); + + auto leftString = chunk.data().substr(0, indexInChunk); + auto rightString = chunk.data().substr(indexInChunk, chunk.length() - indexInChunk); + + fprintf(f, "#{\"%s\", \"%s\", \"%s\"}\n", leftString.c_str(), text.c_str(), rightString.c_str()); + fflush(f); + + Chunk leftChunk { leftString }; + Chunk midChunk { text }; + Chunk rightChunk { rightString }; + + auto iterator = m_chunks.begin() + chunkIndex; + m_chunks.erase(iterator); + iterator = m_chunks.begin() + chunkIndex; + + // Note reverse insertion order! + m_chunks.insert(iterator, rightChunk); + m_chunks.insert(iterator, midChunk); + m_chunks.insert(iterator, leftChunk); +} + +std::tuple Line::chunkIndexForPosition(size_t position) +{ + ASSERT(position < length()); + size_t seen { 0 }; + for (size_t i = 0; i < m_chunks.size(); ++i) { + if (position < seen + m_chunks[i].length()) + return std::make_tuple(i, position - seen); + seen += m_chunks[i].length(); + } + ASSERT(false); + return std::make_tuple(0, 0); +} diff --git a/Editor/Line.h b/Editor/Line.h new file mode 100644 index 00000000000..d8e65fc2438 --- /dev/null +++ b/Editor/Line.h @@ -0,0 +1,40 @@ +#pragma once + +#include "cuki.h" +#include +#include +#include + +class Chunk { +public: + explicit Chunk(const std::string&); + ~Chunk(); + + const std::string& data() const { return m_data; } + size_t length() const { return m_data.size(); } + +private: + std::string m_data; +}; + +class Line { + AK_MAKE_NONCOPYABLE(Line); +public: + Line() { } + Line(const std::string&); + Line(Line&&); + ~Line(); + + std::string data() const; + size_t length() const { return data().size(); } + + void insert(size_t index, const std::string&); + +private: + void append(const std::string&); + void prepend(const std::string&); + + std::tuple chunkIndexForPosition(size_t); + + std::deque m_chunks; +}; diff --git a/Editor/Makefile b/Editor/Makefile new file mode 100644 index 00000000000..b67c24746c1 --- /dev/null +++ b/Editor/Makefile @@ -0,0 +1,25 @@ +BINARY = cuki + +OBJS = \ + Document.o \ + Line.o \ + FileReader.o \ + Editor.o \ + main.o \ + InsertOperation.o \ + Operation.o \ + UndoStack.o + +CXXFLAGS = -Os -std=c++1z -W -Wall -g -I../AK +LDFLAGS = -lncurses + +$(BINARY): $(OBJS) + $(CXX) $(LDFLAGS) -o $@ $(OBJS) + +all: $(BINARY) + +clean: + rm -f $(BINARY) $(OBJS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/Editor/Operation.cpp b/Editor/Operation.cpp new file mode 100644 index 00000000000..0363ee1ef47 --- /dev/null +++ b/Editor/Operation.cpp @@ -0,0 +1,9 @@ +#include "Operation.h" + +Operation::Operation() +{ +} + +Operation::~Operation() +{ +} diff --git a/Editor/Operation.h b/Editor/Operation.h new file mode 100644 index 00000000000..03fdf0aac5b --- /dev/null +++ b/Editor/Operation.h @@ -0,0 +1,14 @@ +#pragma once + +class Editor; + +class Operation { +public: + virtual ~Operation(); + + virtual bool apply(Editor&) = 0; + virtual bool unapply(Editor&) = 0; + +protected: + Operation(); +}; diff --git a/Editor/Position.h b/Editor/Position.h new file mode 100644 index 00000000000..6037e5e8227 --- /dev/null +++ b/Editor/Position.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +class Position { +public: + Position() { } + Position(size_t line, size_t column) : m_line(line), m_column(column) { } + + size_t line() const { return m_line; } + size_t column() const { return m_column; } + + void set_line(size_t l) { m_line = l; } + void set_column(size_t c) { m_column = c; } + + void move_to(size_t l, size_t c) { m_line = l; m_column = c; } + + void move_by(ssize_t l, ssize_t c) { m_line += l; m_column += c; } + + bool is_valid() const { return m_line != InvalidValue && m_column != InvalidValue; } + +private: + static const size_t InvalidValue = 0xFFFFFFFF; + + size_t m_line { InvalidValue }; + size_t m_column { InvalidValue }; +}; diff --git a/Editor/UndoStack.cpp b/Editor/UndoStack.cpp new file mode 100644 index 00000000000..45867d2c79a --- /dev/null +++ b/Editor/UndoStack.cpp @@ -0,0 +1,14 @@ +#include "cuki.h" +#include "UndoStack.h" + +void UndoStack::push(OwnPtr&& op) +{ + m_stack.push(std::move(op)); +} + +OwnPtr UndoStack::pop() +{ + OwnPtr op = std::move(m_stack.top()); + m_stack.pop(); + return op; +} diff --git a/Editor/UndoStack.h b/Editor/UndoStack.h new file mode 100644 index 00000000000..5938dca2160 --- /dev/null +++ b/Editor/UndoStack.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "Operation.h" +#include "OwnPtr.h" + +class UndoStack { +public: + UndoStack() { } + + void push(OwnPtr&&); + OwnPtr pop(); + + bool is_empty() const { return m_stack.empty(); } + +private: + std::stack> m_stack; +}; diff --git a/Editor/cuki.h b/Editor/cuki.h new file mode 100644 index 00000000000..80b218bfa00 --- /dev/null +++ b/Editor/cuki.h @@ -0,0 +1,6 @@ +// cuki text editor + +#include "Assertions.h" +#include "Noncopyable.h" + +// eof diff --git a/Editor/main.cpp b/Editor/main.cpp new file mode 100644 index 00000000000..1e9f5a8d416 --- /dev/null +++ b/Editor/main.cpp @@ -0,0 +1,19 @@ +#include "Document.h" +#include "Editor.h" +#include + +int main(int c, char** v) +{ + std::string file_to_open = "cuki.h"; + if (c > 1) { + file_to_open = v[1]; + } + auto document = Document::create_from_file(file_to_open); + if (!document) { + fprintf(stderr, "Failed to open file.\n"); + return 1; + } + Editor editor; + editor.set_document(std::move(document)); + return editor.exec(); +} diff --git a/Kernel/ELFImage.h b/Kernel/ELFImage.h index 24bf890a3ba..a4decc0ab35 100644 --- a/Kernel/ELFImage.h +++ b/Kernel/ELFImage.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include "elf.h" #include "types.h" diff --git a/Kernel/FIFO.cpp b/Kernel/FIFO.cpp index 14589d4c10b..9b65dc132b2 100644 --- a/Kernel/FIFO.cpp +++ b/Kernel/FIFO.cpp @@ -1,5 +1,5 @@ #include "FIFO.h" -#include +#include //#define FIFO_DEBUG diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index 03bb3bbcfe2..166486ca97b 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include class Process; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 9ffa7ded493..1a92f1ea7ce 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -12,7 +12,7 @@ #include "i8253.h" #include "RTC.h" #include "ProcFileSystem.h" -#include +#include #include #include "Syscall.h" #include "Scheduler.h" diff --git a/Kernel/Process.h b/Kernel/Process.h index 26d612d00ed..3e7038b6ab5 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include class FileDescriptor; diff --git a/Kernel/VirtualConsole.cpp b/Kernel/VirtualConsole.cpp index 3e5e85bea76..379f55421da 100644 --- a/Kernel/VirtualConsole.cpp +++ b/Kernel/VirtualConsole.cpp @@ -4,7 +4,7 @@ #include "IO.h" #include "StdLib.h" #include "Keyboard.h" -#include +#include static byte* s_vga_buffer; static VirtualConsole* s_consoles[6]; diff --git a/Kernel/system.h b/Kernel/system.h index 3d2f26b46b0..262d1d6d595 100644 --- a/Kernel/system.h +++ b/Kernel/system.h @@ -2,7 +2,7 @@ #include "types.h" #include -#include +#include struct KSym { dword address; diff --git a/LibC/grp.cpp b/LibC/grp.cpp index e48772627ff..c4f2af5f993 100644 --- a/LibC/grp.cpp +++ b/LibC/grp.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include extern "C" { diff --git a/LibC/pwd.cpp b/LibC/pwd.cpp index 681bd93c2d9..38f3020b26c 100644 --- a/LibC/pwd.cpp +++ b/LibC/pwd.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include extern "C" { diff --git a/LibC/termcap.cpp b/LibC/termcap.cpp index 1fff6aea389..8bffe27d3fc 100644 --- a/LibC/termcap.cpp +++ b/LibC/termcap.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include //#define TERMCAP_DEBUG diff --git a/Userland/kill.cpp b/Userland/kill.cpp index 7da64292700..5708c7a8d05 100644 --- a/Userland/kill.cpp +++ b/Userland/kill.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include static unsigned parseUInt(const String& str, bool& ok) { diff --git a/Userland/ls.cpp b/Userland/ls.cpp index fa15d8a23b8..741b0a3f1ba 100644 --- a/Userland/ls.cpp +++ b/Userland/ls.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include static int do_dir(const char* path); diff --git a/Userland/sleep.cpp b/Userland/sleep.cpp index 79cffbfc1b4..ba50980723f 100644 --- a/Userland/sleep.cpp +++ b/Userland/sleep.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include static unsigned parseUInt(const String& str, bool& ok) { diff --git a/VirtualFileSystem/Ext2FileSystem.cpp b/VirtualFileSystem/Ext2FileSystem.cpp index 18d0a4576f3..9a07560d644 100644 --- a/VirtualFileSystem/Ext2FileSystem.cpp +++ b/VirtualFileSystem/Ext2FileSystem.cpp @@ -2,7 +2,7 @@ #include "ext2_fs.h" #include "UnixTypes.h" #include -#include +#include #include #include #include diff --git a/VirtualFileSystem/FileBackedDiskDevice.h b/VirtualFileSystem/FileBackedDiskDevice.h index a42991eda8a..9fd630ecad9 100644 --- a/VirtualFileSystem/FileBackedDiskDevice.h +++ b/VirtualFileSystem/FileBackedDiskDevice.h @@ -2,7 +2,7 @@ #include "DiskDevice.h" #include -#include +#include #include #include diff --git a/VirtualFileSystem/FileSystem.h b/VirtualFileSystem/FileSystem.h index af6a1d1c129..d1f8f67611a 100644 --- a/VirtualFileSystem/FileSystem.h +++ b/VirtualFileSystem/FileSystem.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/VirtualFileSystem/FullDevice.cpp b/VirtualFileSystem/FullDevice.cpp index 8670652a3dd..58f014895fa 100644 --- a/VirtualFileSystem/FullDevice.cpp +++ b/VirtualFileSystem/FullDevice.cpp @@ -1,7 +1,7 @@ #include "FullDevice.h" #include "Limits.h" #include -#include +#include #include FullDevice::FullDevice() diff --git a/VirtualFileSystem/NullDevice.cpp b/VirtualFileSystem/NullDevice.cpp index 61df2b0cdef..1ad8c5f7847 100644 --- a/VirtualFileSystem/NullDevice.cpp +++ b/VirtualFileSystem/NullDevice.cpp @@ -1,6 +1,6 @@ #include "NullDevice.h" #include "Limits.h" -#include +#include #include NullDevice::NullDevice() diff --git a/VirtualFileSystem/RandomDevice.cpp b/VirtualFileSystem/RandomDevice.cpp index bcc81a4793e..0bd2559085b 100644 --- a/VirtualFileSystem/RandomDevice.cpp +++ b/VirtualFileSystem/RandomDevice.cpp @@ -1,6 +1,6 @@ #include "RandomDevice.h" #include "Limits.h" -#include +#include RandomDevice::RandomDevice() : CharacterDevice(1, 8) diff --git a/VirtualFileSystem/SyntheticFileSystem.cpp b/VirtualFileSystem/SyntheticFileSystem.cpp index 9de46db5a8d..526d3489f43 100644 --- a/VirtualFileSystem/SyntheticFileSystem.cpp +++ b/VirtualFileSystem/SyntheticFileSystem.cpp @@ -1,7 +1,7 @@ #include "SyntheticFileSystem.h" #include "FileDescriptor.h" #include -#include +#include #ifndef SERENITY typedef int InterruptDisabler; diff --git a/VirtualFileSystem/VirtualFileSystem.h b/VirtualFileSystem/VirtualFileSystem.h index 3c24f943141..36695b03955 100644 --- a/VirtualFileSystem/VirtualFileSystem.h +++ b/VirtualFileSystem/VirtualFileSystem.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include "InodeIdentifier.h" diff --git a/VirtualFileSystem/ZeroDevice.cpp b/VirtualFileSystem/ZeroDevice.cpp index 1057e8c9470..529f87550b0 100644 --- a/VirtualFileSystem/ZeroDevice.cpp +++ b/VirtualFileSystem/ZeroDevice.cpp @@ -1,6 +1,6 @@ #include "ZeroDevice.h" #include "Limits.h" -#include +#include #include ZeroDevice::ZeroDevice()