Remove Editor/ since I wasn't maintaining or trying to develop it.

This commit is contained in:
Andreas Kling 2019-02-02 04:28:45 +01:00
parent 76f53b40f4
commit 7e64154d3f
Notes: sideshowbarker 2024-07-19 15:54:04 +09:00
19 changed files with 0 additions and 1030 deletions

3
Editor/.gitignore vendored
View file

@ -1,3 +0,0 @@
*.o
cuki
log

View file

@ -1,72 +0,0 @@
#include "Document.h"
#include "FileReader.h"
OwnPtr<Document> Document::create_from_file(const std::string& path)
{
auto document = make<Document>(path);
FileReader reader(path);
while (reader.can_read()) {
auto line = reader.read_line();
document->m_lines.push_back(make<Line>(line));
}
return document;
}
void Document::dump()
{
fprintf(stderr, "Document{%p}\n", this);
for (size_t i = 0; i < line_count(); ++i) {
fprintf(stderr, "[%02zu] %s\n", i, line(i).data().c_str());
}
}
bool Document::backspace_at(Position)
{
return false;
}
bool Document::erase_at(Position position, int count)
{
ASSERT(position.is_valid());
ASSERT(position.line() < line_count());
if (count == 0)
return false;
line(position.line()).erase(position.column(), count);
return true;
}
bool Document::newline_at(Position position)
{
ASSERT(position.is_valid());
ASSERT(position.line() < line_count());
auto& line = this->line(position.line());
if (position.column() > line.length())
return false;
if (position.column() == line.length()) {
m_lines.insert(m_lines.begin() + position.line() + 1, make<Line>(""));
return true;
}
auto chop = line.truncate(position.column());
m_lines.insert(m_lines.begin() + position.line() + 1, make<Line>(chop));
return true;
}
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;
auto& line = this->line(position.line());
if (position.column() > line.length())
return false;
line.insert(position.column(), text);
return true;
}

View file

@ -1,32 +0,0 @@
#pragma once
#include "cuki.h"
#include "Line.h"
#include "Position.h"
#include "OwnPtr.h"
#include <string>
class Document {
public:
explicit Document(const std::string& path) : m_path(path) { }
~Document() { }
std::string path() const { return m_path; }
Line& line(size_t index) { return *m_lines[index]; }
const Line& line(size_t index) const { return *m_lines[index]; }
size_t line_count() const { return m_lines.size(); }
static OwnPtr<Document> create_from_file(const std::string& path);
bool insert_at(Position, const std::string&);
bool newline_at(Position);
bool backspace_at(Position);
bool erase_at(Position, int count);
void dump();
private:
std::deque<OwnPtr<Line>> m_lines;
std::string m_path;
};

View file

@ -1,427 +0,0 @@
#include "Editor.h"
#include "Document.h"
#include "InsertOperation.h"
#define _XOPEN_SOURCE_EXTENDED
#include <ncurses.h>
#include <stdarg.h>
static int statusbar_attributes;
static int ruler_attributes;
Editor::Editor()
{
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>&& 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->line(current_document_line).data().size();
const char* line_data = m_document->line(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 'I': move_to_start_of_line(); set_mode(EditingDocument); break;
case 'A': move_to_end_of_line(); set_mode(EditingDocument); break;
case '0': move_to_start_of_line(); break;
case '$': move_to_end_of_line(); break;
case 'a': move_right(); set_mode(EditingDocument); break;
case 'x': erase_right(); break;
case 'X': erase_left(); break;
case '\\': set_mode(EditingCommand); break;
}
}
}
return 0;
}
void Editor::write_to_file()
{
FILE* fp = fopen(m_document->path().c_str(), "w");
if (!fp) {
set_status_text("Failed to open %s for writing", m_document->path().c_str());
return;
}
size_t bytes = 0;
for (size_t i = 0; i < m_document->line_count(); ++i) {
fwrite(m_document->line(i).data().c_str(), sizeof(char), m_document->line(i).length(), fp);
bytes += m_document->line(i).length();
if (i != m_document->line_count() - 1) {
fputc('\n', fp);
++bytes;
}
}
fclose(fp);
set_status_text("Wrote %zu bytes across %zu lines", bytes, m_document->line_count());
}
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;
coalesce_current_line();
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::coalesce_current_line()
{
m_document->line(m_cursor.line()).coalesce();
}
void Editor::move_up()
{
if (m_cursor.line() == 0)
return;
coalesce_current_line();
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();
}
void Editor::move_to_end_of_line()
{
m_cursor.move_to(m_cursor.line(), m_document->line(m_cursor.line()).length());
update_scroll_position_if_needed();
}
void Editor::move_to_start_of_line()
{
m_cursor.move_to(m_cursor.line(), 0);
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->line(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<EraseOperation>(1);
m_document->backspace_at(m_cursor);
} else {
auto op = make<InsertOperation>(ch);
run(std::move(op));
}
}
void Editor::run(OwnPtr<Operation>&& 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);
if (text[0] == '\n') {
m_document->newline_at(m_cursor);
m_cursor.move_to(m_cursor.line() + 1, 0);
return true;
}
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::erase_left()
{
if (m_cursor.column() == 0)
return;
m_document->erase_at(m_cursor, -1);
m_cursor.move_by(0, -1);
}
Line& Editor::current_line()
{
return m_document->line(m_cursor.line());
}
const Line& Editor::current_line() const
{
return m_document->line(m_cursor.line());
}
void Editor::erase_right()
{
if (m_cursor.column() == current_line().length())
return;
m_document->erase_at(m_cursor, 1);
}
void Editor::set_status_text(const std::string& text)
{
m_status_text = text;
}
void Editor::set_status_text(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char buf[128];
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
m_status_text = buf;
}
void Editor::exec_command()
{
if (m_command == "q") {
m_should_quit = true;
return;
}
if (m_command == "w") {
write_to_file();
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);
}

View file

@ -1,85 +0,0 @@
#pragma once
#include "OwnPtr.h"
#include "Position.h"
#include "UndoStack.h"
#include <string>
class Document;
class Line;
class Editor {
public:
Editor();
~Editor();
void set_document(OwnPtr<Document>&&);
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&);
void set_status_text(const char* fmt, ...);
bool insert_text_at_cursor(const std::string&);
bool remove_text_at_cursor(const std::string&);
void run(OwnPtr<Operation>&&);
private:
Line& current_line();
const Line& current_line() const;
void move_left();
void move_down();
void move_up();
void move_right();
void move_to_end_of_line();
void move_to_start_of_line();
void erase_left();
void erase_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();
void coalesce_current_line();
void write_to_file();
OwnPtr<Document> 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 };
};

View file

@ -1,40 +0,0 @@
#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::read_line() 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;
}

View file

@ -1,17 +0,0 @@
#pragma once
#include <string>
#include <stdio.h>
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 };
};

View file

@ -1,26 +0,0 @@
#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);
}

View file

@ -1,17 +0,0 @@
#pragma once
#include "Operation.h"
#include <string>
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;
};

View file

@ -1,132 +0,0 @@
#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));
}
std::string Line::truncate(size_t length)
{
coalesce();
auto remainder = data().substr(0, length);
auto chop = data().substr(length, data().length() - length);
m_chunks.clear();
m_chunks.push_back(Chunk{ remainder });
return chop;
}
void Line::insert(size_t index, const std::string& text)
{
if (index == 0) {
prepend(text);
return;
}
if (index == length()) {
append(text);
return;
}
auto chunk_address = chunk_index_for_position(index);
auto chunk_index = std::get<0>(chunk_address);
auto& chunk = m_chunks[chunk_index];
auto index_in_chunk = std::get<1>(chunk_address);
static FILE* f = fopen("log", "a");
fprintf(f, "#Column:%zu, Chunk:%zu, Index:%zu\n", index, chunk_index, index_in_chunk);
auto left_string = chunk.data().substr(0, index_in_chunk);
auto right_string = chunk.data().substr(index_in_chunk, chunk.length() - index_in_chunk);
fprintf(f, "#{\"%s\", \"%s\", \"%s\"}\n", left_string.c_str(), text.c_str(), right_string.c_str());
Chunk left_chunk { left_string };
Chunk mid_chunk { text };
Chunk right_chunk { right_string };
auto iterator = m_chunks.begin() + chunk_index;
m_chunks.erase(iterator);
iterator = m_chunks.begin() + chunk_index;
// Note reverse insertion order!
iterator = m_chunks.insert(iterator, right_chunk);
iterator = m_chunks.insert(iterator, mid_chunk);
iterator = m_chunks.insert(iterator, left_chunk);
fflush(f);
}
std::tuple<size_t, size_t> Line::chunk_index_for_position(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);
}
void Line::coalesce()
{
if (m_chunks.size() <= 1)
return;
auto contents = data();
m_chunks.clear();
m_chunks.push_back(Chunk{ contents });
}
void Line::erase(size_t column, int count)
{
coalesce();
auto str = data();
if (count < 0)
str.erase(str.begin() + column + count, str.begin() + column);
else
str.erase(str.begin() + column, str.begin() + column + count);
m_chunks.clear();
m_chunks.push_back(Chunk{ str });
}

View file

@ -1,46 +0,0 @@
#pragma once
#include "cuki.h"
#include <deque>
#include <string>
#include <tuple>
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&);
std::string truncate(size_t length);
void coalesce();
void erase(size_t column, int count);
private:
void append(const std::string&);
void prepend(const std::string&);
std::tuple<size_t, size_t> chunk_index_for_position(size_t);
std::deque<Chunk> m_chunks;
};

View file

@ -1,26 +0,0 @@
BINARY = cuki
OBJS = \
Document.o \
Line.o \
FileReader.o \
Editor.o \
main.o \
InsertOperation.o \
Operation.o \
UndoStack.o
CXX = g++
CXXFLAGS = -O2 -std=c++1z -W -Wall -g -I../AK
LIBS = -lncurses
$(BINARY): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
all: $(BINARY)
clean:
rm -f $(BINARY) $(OBJS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@

View file

@ -1,9 +0,0 @@
#include "Operation.h"
Operation::Operation()
{
}
Operation::~Operation()
{
}

View file

@ -1,14 +0,0 @@
#pragma once
class Editor;
class Operation {
public:
virtual ~Operation();
virtual bool apply(Editor&) = 0;
virtual bool unapply(Editor&) = 0;
protected:
Operation();
};

View file

@ -1,27 +0,0 @@
#pragma once
#include <unistd.h>
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 };
};

View file

@ -1,14 +0,0 @@
#include "cuki.h"
#include "UndoStack.h"
void UndoStack::push(OwnPtr<Operation>&& op)
{
m_stack.push(std::move(op));
}
OwnPtr<Operation> UndoStack::pop()
{
OwnPtr<Operation> op = std::move(m_stack.top());
m_stack.pop();
return op;
}

View file

@ -1,18 +0,0 @@
#pragma once
#include <stack>
#include "Operation.h"
#include "OwnPtr.h"
class UndoStack {
public:
UndoStack() { }
void push(OwnPtr<Operation>&&);
OwnPtr<Operation> pop();
bool is_empty() const { return m_stack.empty(); }
private:
std::stack<OwnPtr<Operation>> m_stack;
};

View file

@ -1,6 +0,0 @@
// cuki text editor
#include "Assertions.h"
#include "Noncopyable.h"
// eof

View file

@ -1,19 +0,0 @@
#include "Document.h"
#include "Editor.h"
#include <stdio.h>
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();
}