Move editor to its own subdirectory.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-08-23 21:26:38 +02:00
parent 2d6481a5e7
commit 08461c72fa
25 changed files with 3242 additions and 2479 deletions

View File

@ -11,6 +11,7 @@ libpthread \
dispd \ dispd \
bench \ bench \
carray \ carray \
editor \
ext \ ext \
games \ games \
mbr \ mbr \

View File

@ -311,14 +311,18 @@ Editing Files
You can use the `editor` program to edit files. The editor itself is fairly You can use the `editor` program to edit files. The editor itself is fairly
simple to use. It currently supports these keyboard commands: simple to use. It currently supports these keyboard commands:
* `Ctrl-Q` - Exit * `Ctrl-C` - Copy
* `Ctrl-O` - Open a file
* `Ctrl-S` - Save a file
* `Ctrl-I` - Go to line * `Ctrl-I` - Go to line
* `ESC tabsize <desired-tab-size>` - change tab size * `Ctrl-K` - Cut
* `Ctrl-O` - Open a file
* `Ctrl-Q` - Exit
* `Ctrl-S` - Save a file
* `Ctrl-V` - Paste
* `Ctrl-X` - Cut
* `ESC language <c or c++>` - enable syntax highlighting
* `ESC margin <column>` - add right margin at column * `ESC margin <column>` - add right margin at column
* `ESC popen <command>` - open command output * `ESC popen <command>` - open command output
* `ESC language <c or c++>` - enable syntax highlighting * `ESC tabsize <desired-tab-size>` - change tab size
It is not currently possible to port third party editors because the terminal It is not currently possible to port third party editors because the terminal
implementation is not standards-compliant enough and is seriously lacking. implementation is not standards-compliant enough and is seriously lacking.

2
editor/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
editor
*.o

41
editor/Makefile Normal file
View File

@ -0,0 +1,41 @@
SOFTWARE_MEANT_FOR_SORTIX=1
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CXXFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
BINARY=editor
OBJS=\
command.o \
cursor.o \
display.o \
editor.o \
highlight.o \
input.o \
modal.o \
multibyte.o \
terminal.o \
all: $(BINARY)
.PHONY: all install clean
$(BINARY): $(OBJS)
$(CXX) $(OBJS) -o $(BINARY) $(CXXFLAGS) $(LIBS)
%.o: %.c++
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARY) $(DESTDIR)$(BINDIR)
clean:
rm -f $(BINARY) $(OBJS) *.o

734
editor/command.c++ Normal file
View File

@ -0,0 +1,734 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
command.c++
Editor commands.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#include "command.h++"
#include "cursor.h++"
#include "display.h++"
#include "editor.h++"
#include "multibyte.h++"
#include "terminal.h++"
void editor_type_newline(struct editor* editor)
{
editor->dirty = true;
if ( editor->lines_used == editor->lines_length )
{
size_t new_length = editor->lines_length ? 2 * editor->lines_length : 8;
struct line* new_lines = new struct line[new_length];
for ( size_t i = 0; i < editor->lines_used; i++ )
new_lines[i] = editor->lines[i];
delete[] editor->lines;
editor->lines = new_lines;
editor->lines_length = new_length;
}
for ( size_t i = editor->lines_used-1; editor->cursor_row < i; i-- )
editor->lines[i+1] = editor->lines[i];
editor->lines_used++;
struct line old_line = editor->lines[editor->cursor_row];
size_t keep_length = editor->cursor_column;
size_t move_length = old_line.used - keep_length;
struct line* keep_line = &editor->lines[editor->cursor_row];
struct line* move_line = &editor->lines[editor->cursor_row+1];
keep_line->data = new wchar_t[keep_length];
keep_line->used = keep_length;
keep_line->length = keep_length;
memcpy(keep_line->data, old_line.data + 0, sizeof(wchar_t) * keep_length);
move_line->data = new wchar_t[move_length];
move_line->used = move_length;
move_line->length = move_length;
memcpy(move_line->data, old_line.data + keep_length, sizeof(wchar_t) * move_length);
editor_cursor_set(editor, editor->cursor_row+1, 0);
delete[] old_line.data;
}
void editor_type_delete_selection(struct editor* editor);
void editor_type_combine_with_last(struct editor* editor)
{
if ( !editor->cursor_row )
return;
editor->dirty = true;
struct line* keep_line = &editor->lines[editor->cursor_row-1];
struct line* gone_line = &editor->lines[editor->cursor_row];
wchar_t* keep_line_data = keep_line->data;
wchar_t* gone_line_data = gone_line->data;
size_t new_length = keep_line->used + gone_line->used;
wchar_t* new_data = new wchar_t[new_length];
memcpy(new_data, keep_line_data, sizeof(wchar_t) * keep_line->used);
memcpy(new_data + keep_line->used, gone_line_data, sizeof(wchar_t) * gone_line->used);
editor_cursor_set(editor, editor->cursor_row-1, keep_line->used);
keep_line->data = new_data;
keep_line->used = new_length;
keep_line->length = new_length;
editor->lines_used--;
for ( size_t i = editor->cursor_row + 1; i < editor->lines_used; i++ )
editor->lines[i] = editor->lines[i+1];
free(keep_line_data);
free(gone_line_data);
}
void editor_type_backspace(struct editor* editor)
{
if ( !(editor->select_row == editor->cursor_row &&
editor->select_column == editor->cursor_column) )
{
editor_type_delete_selection(editor);
return;
}
struct line* current_line = &editor->lines[editor->cursor_row];
if ( !editor->cursor_column )
{
editor_type_combine_with_last(editor);
return;
}
editor->dirty = true;
current_line->used--;
for ( size_t i = editor_cursor_column_dec(editor); i < current_line->used; i++ )
current_line->data[i] = current_line->data[i+1];
}
void editor_type_combine_with_next(struct editor* editor)
{
if ( editor->cursor_row + 1 == editor->lines_used )
return;
editor->dirty = true;
struct line* keep_line = &editor->lines[editor->cursor_row];
struct line* gone_line = &editor->lines[editor->cursor_row+1];
wchar_t* keep_line_data = keep_line->data;
wchar_t* gone_line_data = gone_line->data;
size_t new_length = keep_line->used + gone_line->used;
wchar_t* new_data = new wchar_t[new_length];
memcpy(new_data, keep_line_data, sizeof(wchar_t) * keep_line->used);
memcpy(new_data + keep_line->used, gone_line_data, sizeof(wchar_t) * gone_line->used);
editor_cursor_column_set(editor, keep_line->used);
keep_line->data = new_data;
keep_line->used = new_length;
keep_line->length = new_length;
editor->lines_used--;
for ( size_t i = editor->cursor_row + 1; i < editor->lines_used; i++ )
editor->lines[i] = editor->lines[i+1];
free(keep_line_data);
free(gone_line_data);
}
void editor_type_delete(struct editor* editor)
{
if ( !(editor->select_row == editor->cursor_row &&
editor->select_column == editor->cursor_column) )
{
editor_type_delete_selection(editor);
return;
}
struct line* current_line = &editor->lines[editor->cursor_row];
if ( editor->cursor_column == current_line->used )
{
editor_type_combine_with_next(editor);
return;
}
editor->dirty = true;
current_line->used--;
for ( size_t i = editor->cursor_column; i < current_line->used; i++ )
current_line->data[i] = current_line->data[i+1];
}
void editor_type_delete_selection(struct editor* editor)
{
if ( is_row_column_lt(editor->select_row, editor->select_column,
editor->cursor_row, editor->cursor_column) )
{
size_t tmp;
tmp = editor->select_row;
editor->select_row = editor->cursor_row;
editor->cursor_row = tmp;
tmp = editor->select_column;
editor->select_column = editor->cursor_column;
editor->cursor_column = tmp;
}
size_t desired_row = editor->cursor_row;
size_t desired_column = editor->cursor_column;
editor->cursor_row = editor->select_row;
editor->cursor_column = editor->select_column;
while ( !(editor->cursor_row == desired_row &&
editor->cursor_column == desired_column) )
editor_type_backspace(editor);
}
void editor_type_left(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_smallest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
return;
}
if ( editor->cursor_column )
editor_cursor_column_dec(editor);
else if ( editor->cursor_row )
{
editor_cursor_row_dec(editor);
editor_cursor_column_set(editor, editor->lines[editor->cursor_row].used);
}
}
void editor_type_select_left(struct editor* editor)
{
if ( editor->select_column )
editor_select_column_dec(editor);
else if ( editor->select_row )
{
editor_select_row_dec(editor);
editor_select_column_set(editor, editor->lines[editor->select_row].used);
}
}
void editor_type_right(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_biggest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
return;
}
struct line* current_line = &editor->lines[editor->cursor_row];
if ( editor->cursor_column != current_line->used )
editor_cursor_column_inc(editor);
else if ( editor->cursor_row+1 != editor->lines_used )
editor_cursor_row_inc(editor),
editor_cursor_column_set(editor, 0);
}
void editor_type_select_right(struct editor* editor)
{
struct line* current_line = &editor->lines[editor->select_row];
if ( editor->select_column != current_line->used )
editor_select_column_inc(editor);
else if ( editor->select_row+1 != editor->lines_used )
editor_select_row_inc(editor),
editor_select_column_set(editor, 0);
}
void editor_type_up(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_smallest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
}
if ( !editor->cursor_row )
{
editor_cursor_column_set(editor, 0);
return;
}
size_t new_line_len = editor->lines[editor_cursor_row_dec(editor)].used;
if ( new_line_len < editor->cursor_column )
editor_cursor_column_set(editor, new_line_len);
}
void editor_type_select_up(struct editor* editor)
{
if ( !editor->select_row )
{
editor_select_column_set(editor, 0);
return;
}
size_t new_line_len = editor->lines[editor_select_row_dec(editor)].used;
if ( new_line_len < editor->select_column )
editor_select_column_set(editor, new_line_len);
}
void editor_type_down(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_biggest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
}
if ( editor->cursor_row+1 == editor->lines_used )
{
editor_cursor_column_set(editor, editor->lines[editor->cursor_row].used);
return;
}
size_t new_line_len = editor->lines[editor_cursor_row_inc(editor)].used;
if ( new_line_len < editor->cursor_column )
editor_cursor_column_set(editor, new_line_len);
}
void editor_type_select_down(struct editor* editor)
{
if ( editor->select_row+1 == editor->lines_used )
{
editor_select_column_set(editor, editor->lines[editor->select_row].used);
return;
}
size_t new_line_len = editor->lines[editor_select_row_inc(editor)].used;
if ( new_line_len < editor->select_column )
editor_select_column_set(editor, new_line_len);
}
void editor_skip_leading(struct editor* editor)
{
struct line* current_line = &editor->lines[editor->cursor_row];
for ( editor_cursor_column_set(editor, 0);
editor->cursor_column < current_line->used;
editor_cursor_column_inc(editor) )
if ( !iswspace(current_line->data[editor->cursor_column]) )
break;
}
void editor_select_skip_leading(struct editor* editor)
{
struct line* current_line = &editor->lines[editor->select_row];
for ( editor_select_column_set(editor, 0);
editor->select_column < current_line->used;
editor_select_column_inc(editor) )
if ( !iswspace(current_line->data[editor->select_column]) )
break;
}
void editor_type_home(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_smallest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
}
if ( !editor->cursor_column )
{
editor_skip_leading(editor);
return;
}
editor_cursor_column_set(editor, 0);
}
void editor_type_select_home(struct editor* editor)
{
if ( !editor->select_column )
{
editor_select_skip_leading(editor);
return;
}
editor_select_column_set(editor, 0);
}
void editor_skip_ending(struct editor* editor)
{
struct line* current_line = &editor->lines[editor->cursor_row];
for ( editor_cursor_column_set(editor, current_line->used);
editor->cursor_column;
editor_cursor_column_dec(editor) )
if ( !iswspace(current_line->data[editor->cursor_column-1]) )
break;
}
void editor_select_skip_ending(struct editor* editor)
{
struct line* current_line = &editor->lines[editor->select_row];
for ( editor_select_column_set(editor, current_line->used);
editor->select_column;
editor_select_column_dec(editor) )
if ( !iswspace(current_line->data[editor->select_column-1]) )
break;
}
void editor_type_end(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_biggest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
}
struct line* current_line = &editor->lines[editor->cursor_row];
if ( editor->cursor_column == current_line->used )
{
editor_skip_ending(editor);
return;
}
editor_cursor_column_set(editor, current_line->used);
}
void editor_type_select_end(struct editor* editor)
{
struct line* current_line = &editor->lines[editor->select_row];
if ( editor->select_column == current_line->used )
{
editor_select_skip_ending(editor);
return;
}
editor_select_column_set(editor, current_line->used);
}
void editor_type_page_up(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_smallest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
}
if ( editor->cursor_row < editor->viewport_height )
{
editor_cursor_set(editor, 0, 0);
return;
}
size_t new_line = editor->cursor_row - editor->viewport_height;
editor_cursor_row_set(editor, new_line);
size_t new_line_len = editor->lines[new_line].used;
if ( new_line_len < editor->cursor_column )
editor_cursor_column_set(editor, new_line_len);
}
void editor_type_select_page_up(struct editor* editor)
{
if ( editor->select_row < editor->viewport_height )
{
editor_select_set(editor, 0, 0);
return;
}
size_t new_line = editor->select_row - editor->viewport_height;
editor_select_row_set(editor, new_line);
size_t new_line_len = editor->lines[new_line].used;
if ( new_line_len < editor->select_column )
editor_select_column_set(editor, new_line_len);
}
void editor_type_page_down(struct editor* editor)
{
if ( editor_has_selection(editor) )
{
size_t column, row;
row_column_biggest(editor->cursor_row, editor->cursor_column,
editor->select_row, editor->select_column,
&column, &row);
editor_cursor_set(editor, column, row);
}
size_t new_line = editor->cursor_row + editor->viewport_height;
if ( editor->lines_used <= new_line )
{
editor_cursor_row_set(editor, editor->lines_used - 1);
editor_cursor_column_set(editor, editor->lines[editor->cursor_row].used);
return;
}
editor_cursor_row_set(editor, new_line);
size_t new_line_len = editor->lines[new_line].used;
if ( new_line_len < editor->cursor_column )
editor_cursor_column_set(editor, new_line_len);
}
void editor_type_select_page_down(struct editor* editor)
{
size_t new_line = editor->select_row + editor->viewport_height;
if ( editor->lines_used <= new_line )
{
editor_select_row_set(editor, editor->lines_used - 1);
editor_select_column_set(editor, editor->lines[editor->select_row].used);
return;
}
editor_select_row_set(editor, new_line);
size_t new_line_len = editor->lines[new_line].used;
if ( new_line_len < editor->select_column )
editor_select_column_set(editor, new_line_len);
}
void editor_type_edit(struct editor* editor)
{
editor->mode = MODE_EDIT;
}
void editor_type_goto_line(struct editor* editor)
{
editor->mode = MODE_GOTO_LINE;
editor->modal_used = 0;
editor->modal_cursor = 0;
editor->modal_error = false;
}
void editor_type_save(struct editor* editor)
{
editor->mode = MODE_SAVE;
free(editor->modal);
editor->modal = convert_mbs_to_wcs(editor->current_file_name);
editor->modal_used = wcslen(editor->modal);
editor->modal_length = editor->modal_used+1;
editor->modal_cursor = editor->modal_used;
editor->modal_error = false;
}
void editor_type_save_as(struct editor* editor)
{
editor->mode = MODE_SAVE;
editor->modal_used = 0;
editor->modal_cursor = 0;
editor->modal_error = false;
}
void editor_type_open(struct editor* editor)
{
editor->mode = MODE_LOAD;
editor->modal_used = 0;
editor->modal_cursor = 0;
editor->modal_error = false;
}
void editor_type_open_as(struct editor* editor)
{
editor->mode = MODE_LOAD;
free(editor->modal);
editor->modal = convert_mbs_to_wcs(editor->current_file_name);
editor->modal_used = wcslen(editor->modal);
editor->modal_length = editor->modal_used+1;
editor->modal_cursor = editor->modal_used;
editor->modal_error = false;
}
void editor_type_quit(struct editor* editor)
{
editor->mode = editor->dirty ? MODE_ASK_QUIT : MODE_QUIT;
editor->modal_cursor = 0;
editor->modal_used = 0;
editor->modal_error = false;
}
void editor_type_command(struct editor* editor)
{
editor->mode = MODE_COMMAND;
editor->modal_cursor = 0;
editor->modal_used = 0;
editor->modal_error = false;
}
void editor_type_raw_character(struct editor* editor, wchar_t c)
{
struct line* current_line = &editor->lines[editor->cursor_row];
if ( current_line->used == current_line->length )
{
size_t new_length = current_line->length ? 2 * current_line->length : 8;
wchar_t* new_data = new wchar_t[new_length];
for ( size_t i = 0; i < current_line->used; i++ )
new_data[i] = current_line->data[i];
delete[] current_line->data;
current_line->data = new_data;
current_line->length = new_length;
}
editor->dirty = true;
for ( size_t i = current_line->used; editor->cursor_column < i; i-- )
current_line->data[i] = current_line->data[i-1];
current_line->used++;
current_line->data[editor_cursor_column_inc(editor)-1] = c;
}
void editor_type_copy(struct editor* editor)
{
if ( editor->cursor_row == editor->select_row &&
editor->cursor_column == editor->select_column )
return;
delete[] editor->clipboard;
size_t start_row;
size_t start_column;
size_t end_row;
size_t end_column;
if ( is_row_column_lt(editor->select_row, editor->select_column,
editor->cursor_row, editor->cursor_column) )
{
start_row = editor->select_row;
start_column = editor->select_column;
end_row = editor->cursor_row;
end_column = editor->cursor_column;
}
else
{
start_row = editor->cursor_row;
start_column = editor->cursor_column;
end_row = editor->select_row;
end_column = editor->select_column;
}
size_t length = 0;
for ( size_t row = start_row, column = start_column;
is_row_column_lt(row, column, end_row, end_column); )
{
if ( row == end_row )
{
length += end_column - column;
column = end_column;
}
else
{
length += editor->lines[row].used + 1 /*newline*/;
column = 0;
row++;
}
}
editor->clipboard = new wchar_t[length + 1];
size_t offset = 0;
for ( size_t row = start_row, column = start_column;
is_row_column_lt(row, column, end_row, end_column); )
{
struct line* line = &editor->lines[row];
if ( row == end_row )
{
memcpy(editor->clipboard + offset, line->data + column, sizeof(wchar_t) * (end_column - column));
offset += end_column - column;
column = end_column;
}
else
{
memcpy(editor->clipboard + offset, line->data, sizeof(wchar_t) * line->used);
editor->clipboard[offset + line->used] = L'\n';
offset += line->used + 1 /*newline*/;
column = 0;
row++;
}
}
editor->clipboard[length] = L'\0';
}
void editor_type_cut(struct editor* editor)
{
if ( editor->cursor_row == editor->select_row &&
editor->cursor_column == editor->select_column )
return;
editor_type_copy(editor);
editor_type_delete_selection(editor);
}
void editor_type_paste(struct editor* editor)
{
if ( !(editor->cursor_row == editor->select_row &&
editor->cursor_column == editor->select_column) )
editor_type_delete_selection(editor);
for ( size_t i = 0; editor->clipboard && editor->clipboard[i]; i++ )
{
if ( editor->clipboard[i] == L'\n' )
editor_type_newline(editor);
else
editor_type_raw_character(editor, editor->clipboard[i]);
}
}
void editor_type_character(struct editor* editor, wchar_t c)
{
if ( editor->control )
{
switch ( towlower(c) )
{
case L'c': editor_type_copy(editor); break;
case L'i': editor_type_goto_line(editor); break;
case L'k': editor_type_cut(editor); break;
case L'o': editor->shift ?
editor_type_open_as(editor) :
editor_type_open(editor); break;
case L'q': editor_type_quit(editor); break;
case L's': editor->shift ?
editor_type_save_as(editor) :
editor_type_save(editor); break;
case L'v': editor_type_paste(editor); break;
case L'x': editor_type_cut(editor); break;
}
return;
}
if ( editor_has_selection(editor) )
editor_type_delete_selection(editor);
if ( c == L'\n' ) { editor_type_newline(editor); return; }
editor_type_raw_character(editor, c);
}

70
editor/command.h++ Normal file
View File

@ -0,0 +1,70 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
command.h++
Editor commands.
*******************************************************************************/
#ifndef EDITOR_COMMAND_HXX
#define EDITOR_COMMAND_HXX
#include <stddef.h>
struct editor;
void editor_type_newline(struct editor* editor);
void editor_type_combine_with_last(struct editor* editor);
void editor_type_backspace(struct editor* editor);
void editor_type_combine_with_next(struct editor* editor);
void editor_type_delete(struct editor* editor);
void editor_type_delete_selection(struct editor* editor);
void editor_type_left(struct editor* editor);
void editor_type_select_left(struct editor* editor);
void editor_type_right(struct editor* editor);
void editor_type_select_right(struct editor* editor);
void editor_type_up(struct editor* editor);
void editor_type_select_up(struct editor* editor);
void editor_type_down(struct editor* editor);
void editor_type_select_down(struct editor* editor);
void editor_skip_leading(struct editor* editor);
void editor_select_skip_leading(struct editor* editor);
void editor_type_home(struct editor* editor);
void editor_type_select_home(struct editor* editor);
void editor_skip_ending(struct editor* editor);
void editor_select_skip_ending(struct editor* editor);
void editor_type_end(struct editor* editor);
void editor_type_select_end(struct editor* editor);
void editor_type_page_up(struct editor* editor);
void editor_type_select_page_up(struct editor* editor);
void editor_type_page_down(struct editor* editor);
void editor_type_select_page_down(struct editor* editor);
void editor_type_edit(struct editor* editor);
void editor_type_goto_line(struct editor* editor);
void editor_type_save(struct editor* editor);
void editor_type_save_as(struct editor* editor);
void editor_type_open(struct editor* editor);
void editor_type_open_as(struct editor* editor);
void editor_type_quit(struct editor* editor);
void editor_type_command(struct editor* editor);
void editor_type_raw_character(struct editor* editor, wchar_t c);
void editor_type_copy(struct editor* editor);
void editor_type_cut(struct editor* editor);
void editor_type_paste(struct editor* editor);
void editor_type_character(struct editor* editor, wchar_t c);
#endif

132
editor/cursor.c++ Normal file
View File

@ -0,0 +1,132 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
command.c++
Editor cursor.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <assert.h>
#include <stddef.h>
#include "cursor.h++"
#include "display.h++"
#include "editor.h++"
size_t editor_select_column_set(struct editor* editor, size_t x)
{
if ( editor->viewport_width )
{
struct line* line = &editor->lines[editor->select_row];
size_t rx = displayed_string_length(line->data, x, editor->tabsize);
if ( rx < editor->page_x_offset )
editor->page_x_offset = rx;
if ( editor->page_x_offset + editor->viewport_width <= rx )
editor->page_x_offset = rx + 1- editor->viewport_width;
}
return editor->select_column = x;
}
size_t editor_select_row_set(struct editor* editor, size_t y)
{
if ( editor->viewport_height )
{
if ( y < editor->page_y_offset )
editor->page_y_offset = y;
if ( editor->page_y_offset + editor->viewport_height <= y )
editor->page_y_offset = y + 1- editor->viewport_height;
}
return editor->select_row = y;
}
void editor_select_set(struct editor* editor, size_t y, size_t x)
{
editor_select_column_set(editor, x);
editor_select_row_set(editor, y);
}
size_t editor_select_column_dec(struct editor* editor)
{
assert(editor->select_column);
return editor_select_column_set(editor, editor->select_column-1);
}
size_t editor_select_column_inc(struct editor* editor)
{
// TODO: Assert line doesn't overflow!
return editor_select_column_set(editor, editor->select_column+1);
}
size_t editor_select_row_dec(struct editor* editor)
{
assert(editor->select_row);
return editor_select_row_set(editor, editor->select_row-1);
}
size_t editor_select_row_inc(struct editor* editor)
{
// TODO: Assert line doesn't overflow!
return editor_select_row_set(editor, editor->select_row+1);
}
size_t editor_cursor_column_set(struct editor* editor, size_t x)
{
editor_select_column_set(editor, x);
editor_select_row_set(editor, editor->cursor_row);
return editor->cursor_column = x;
}
size_t editor_cursor_row_set(struct editor* editor, size_t y)
{
editor_select_column_set(editor, editor->cursor_column);
editor_select_row_set(editor, y);
return editor->cursor_row = y;
}
void editor_cursor_set(struct editor* editor, size_t y, size_t x)
{
editor_cursor_column_set(editor, x);
editor_cursor_row_set(editor, y);
}
size_t editor_cursor_column_dec(struct editor* editor)
{
assert(editor->cursor_column);
return editor_cursor_column_set(editor, editor->cursor_column-1);
}
size_t editor_cursor_column_inc(struct editor* editor)
{
// TODO: Assert line doesn't overflow!
return editor_cursor_column_set(editor, editor->cursor_column+1);
}
size_t editor_cursor_row_dec(struct editor* editor)
{
assert(editor->cursor_row);
return editor_cursor_row_set(editor, editor->cursor_row-1);
}
size_t editor_cursor_row_inc(struct editor* editor)
{
// TODO: Assert line doesn't overflow!
return editor_cursor_row_set(editor, editor->cursor_row+1);
}

46
editor/cursor.h++ Normal file
View File

@ -0,0 +1,46 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
command.h++
Editor cursor.
*******************************************************************************/
#ifndef EDITOR_CURSOR_HXX
#define EDITOR_CURSOR_HXX
#include <stddef.h>
struct editor;
size_t editor_select_column_set(struct editor* editor, size_t x);
size_t editor_select_row_set(struct editor* editor, size_t y);
void editor_select_set(struct editor* editor, size_t y, size_t x);
size_t editor_select_column_dec(struct editor* editor);
size_t editor_select_column_inc(struct editor* editor);
size_t editor_select_row_dec(struct editor* editor);
size_t editor_select_row_inc(struct editor* editor);
size_t editor_cursor_column_set(struct editor* editor, size_t x);
size_t editor_cursor_row_set(struct editor* editor, size_t y);
void editor_cursor_set(struct editor* editor, size_t y, size_t x);
size_t editor_cursor_column_dec(struct editor* editor);
size_t editor_cursor_column_inc(struct editor* editor);
size_t editor_cursor_row_dec(struct editor* editor);
size_t editor_cursor_row_inc(struct editor* editor);
#endif

233
editor/display.c++ Normal file
View File

@ -0,0 +1,233 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
display.c++
Display handling.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
#include "display.h++"
#include "editor.h++"
#include "multibyte.h++"
#include "terminal.h++"
size_t displayed_string_length(const wchar_t* str, size_t len, size_t tabsize)
{
size_t ret_len = 0;
for ( size_t i = 0; i < len; i++ )
if ( str[i] == L'\t' )
do ret_len++;
while ( ret_len % tabsize );
else
ret_len++;
return ret_len;
}
struct display_char* expand_tabs(const wchar_t* str, size_t len, uint8_t* colors,
size_t colors_len, size_t* ret_len_ptr,
size_t tabsize)
{
size_t ret_len = displayed_string_length(str, len, tabsize);
struct display_char* ret = new struct display_char[ret_len+1];
for ( size_t i = 0, j = 0; i < len; i++ )
{
uint8_t color = i < colors_len ? colors[i] : 7;
if ( str[i] == L'\t' )
do ret[j++] = { L' ', color};
while ( j % tabsize );
else
ret[j++] = { str[i], color };
}
ret[ret_len] = { L'\0', 0 };
if ( ret_len_ptr )
*ret_len_ptr = ret_len;
return ret;
}
void render_editor(struct editor* editor, struct terminal_state* state)
{
if ( state->height < 1 )
return;
// Create the header title bar.
for ( int x = 0; x < state->width; x++ )
state->data[0 * state->width + x] = make_terminal_datum(L' ', 0x70);
// Render the name of the program.
const wchar_t* header_start = editor->dirty ? L" editor *"
: L" editor ";
size_t header_start_len = wcslen(header_start);
for ( size_t i = 0; i < header_start_len; i++ )
if ( i < (size_t) state->width)
state->data[i].character = header_start[i];
// Render the name of the currently open file.
const char* file_name = editor->current_file_name;
if ( !file_name )
file_name = "New File";
wchar_t* wcs_file_name = convert_mbs_to_wcs(file_name);
size_t wcs_file_name_len = wcslen(wcs_file_name);
for ( size_t i = 0; i < wcs_file_name_len; i++ )
if ( header_start_len+i < (size_t) state->width)
state->data[header_start_len+i].character = wcs_file_name[i];
free(wcs_file_name);
// Calculate the dimensions of the viewport.
size_t viewport_top = 1;
editor->viewport_width = (size_t) state->width;
editor->viewport_height = (size_t) state->height - viewport_top;
if ( !editor->viewport_height )
return;
// Decide which page of the file to render and the cursor position on it.
struct line* current_line = &editor->lines[editor->cursor_row];
size_t cursor_x = displayed_string_length(current_line->data,
editor->cursor_column,
editor->tabsize);
size_t cursor_y = editor->cursor_row;
struct line* select_line = &editor->lines[editor->select_row];
size_t select_x = displayed_string_length(select_line->data,
editor->select_column,
editor->tabsize);
size_t select_y = editor->select_row;
size_t page_x_offset = editor->page_x_offset;
size_t page_y_offset = editor->page_y_offset;
bool has_selection = !(editor->cursor_row == editor->select_row &&
editor->cursor_column == editor->select_column);
size_t viewport_select_x = select_x - page_x_offset;
size_t viewport_select_y = select_y - page_y_offset;
// Render this page of text.
for ( size_t y = 0; y < editor->viewport_height; y++ )
{
size_t line_index = page_y_offset + y;
struct terminal_datum* data_line = state->data + (viewport_top + y) * state->width;
struct line* line = line_index < editor->lines_used ?
&editor->lines[line_index] : NULL;
struct color_line* color_line = line_index < editor->color_lines_used ?
&editor->color_lines[line_index] : NULL;
size_t expanded_len;
struct display_char* expanded
= expand_tabs(line ? line->data : L"",
line ? line->used : 0,
color_line ? color_line->data : NULL,
color_line ? color_line->length : 0,
&expanded_len,
editor->tabsize);
const struct display_char* chars = expanded;
size_t chars_length = expanded_len;
if ( chars_length < page_x_offset )
chars = NULL, chars_length = 0;
else
chars += page_x_offset, chars_length -= page_x_offset;
for ( size_t x = 0; x < editor->viewport_width; x++ )
{
size_t column_index = page_x_offset + x;
bool selected = (is_row_column_lt(cursor_y, cursor_x, select_y, select_x) &&
is_row_column_le(cursor_y, cursor_x, line_index, column_index) &&
is_row_column_lt(line_index, column_index, select_y, select_x)) ||
(is_row_column_lt(select_y, select_x, cursor_y, cursor_x) &&
is_row_column_le(select_y, select_x, line_index, column_index) &&
is_row_column_lt(line_index, column_index, cursor_y, cursor_x));
bool at_margin = column_index == editor->margin;
bool is_blank = chars_length <= x;
wchar_t c = is_blank ? L' ' : chars[x].character;
uint8_t color = (is_blank ? 7 : chars[x].color);
data_line[x] = selected && is_blank && at_margin ? make_terminal_datum(L'|', 0x41) :
selected ? make_terminal_datum(c, 0x47) :
is_blank && at_margin ? make_terminal_datum(L'|', 0x01) :
make_terminal_datum(c, color);
}
delete[] expanded;
}
// Set the rest of the terminal state.
state->cursor_x = has_selection ?
editor->viewport_width : viewport_select_x;
state->cursor_y = has_selection ?
editor->viewport_height : viewport_select_y + viewport_top;
state->color = 0x07;
if ( editor->mode == MODE_EDIT )
return;
const char* msg = "";
if ( editor->mode == MODE_SAVE )
msg = "File Name to Write: ";
if ( editor->mode == MODE_LOAD )
msg = "File Name to Read: ";;
if ( editor->mode == MODE_ASK_QUIT )
msg = "Exit without saving changes? (Y/N): ";
if ( editor->mode == MODE_GOTO_LINE )
msg = "Go to line: ";
if ( editor->mode == MODE_COMMAND )
msg = "Enter miscellaneous command: ";
struct terminal_datum* data_line = state->data + (state->height - 1) * state->width;
wchar_t* wcs_msg = convert_mbs_to_wcs(msg);
size_t wcs_msg_len = wcslen(wcs_msg);
for ( size_t i = 0; i < wcs_msg_len; i++ )
if ( i < (size_t) state->width)
data_line[i] = make_terminal_datum(wcs_msg[i], 0x70);
free(wcs_msg);
if ( (size_t) state->width <= wcs_msg_len )
return;
size_t modal_viewport_width = state->width - wcs_msg_len;
size_t modal_viewport_cursor = editor->modal_cursor % modal_viewport_width;
size_t modal_viewport_page = editor->modal_cursor / modal_viewport_width;
size_t modal_viewport_offset = modal_viewport_page * modal_viewport_width;
struct terminal_datum* modal_viewport_data = data_line + wcs_msg_len;
const wchar_t* modal_chars = editor->modal;
size_t modal_chars_length = editor->modal_used;
if ( modal_chars_length < modal_viewport_offset )
modal_chars = NULL,
modal_chars_length = 0;
else
modal_chars += modal_viewport_offset,
modal_chars_length -= modal_viewport_offset;
for ( size_t x = 0; x < modal_viewport_width; x++ )
{
wchar_t c = x < modal_chars_length ? modal_chars[x] : L' ';
uint16_t color = editor->modal_error ? 0x17 : 0x70;
uint16_t tab_color = editor->modal_error ? 0x12 : 0x71;
if ( c == L'\t' )
modal_viewport_data[x] = make_terminal_datum(L'>', tab_color);
else
modal_viewport_data[x] = make_terminal_datum(c, color);
}
state->cursor_x = wcs_msg_len + modal_viewport_cursor;
state->cursor_y = state->height - 1;
state->color = 0x70;
}

44
editor/display.h++ Normal file
View File

@ -0,0 +1,44 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
display.h++
Display handling.
*******************************************************************************/
#ifndef EDITOR_DISPLAY_HXX
#define EDITOR_DISPLAY_HXX
#include <stddef.h>
#include <stdint.h>
struct editor;
struct terminal_state;
struct display_char
{
wchar_t character;
uint8_t color;
};
size_t displayed_string_length(const wchar_t* str, size_t len, size_t tabsize);
struct display_char* expand_tabs(const wchar_t* str, size_t len, uint8_t* colors,
size_t colors_len, size_t* ret_len_ptr,
size_t tabsize);
void render_editor(struct editor* editor, struct terminal_state* state);
#endif

276
editor/editor.c++ Normal file
View File

@ -0,0 +1,276 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
editor.c++
Editor.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <limits.h>
#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <unistd.h>
#include "command.h++"
#include "cursor.h++"
#include "display.h++"
#include "editor.h++"
#include "highlight.h++"
#include "input.h++"
#include "terminal.h++"
void initialize_editor(struct editor* editor)
{
memset(editor, 0, sizeof(*editor));
editor->current_file_name = NULL;
editor->lines = NULL;
editor->lines_used = 0;
editor->lines_length = 0;
editor->cursor_column = 0;
editor->cursor_row = 0;
editor->select_column = 0;
editor->select_row = 0;
editor->page_x_offset = 0;
editor->page_y_offset = 0;
editor->modal = NULL;
editor->modal_used = 0;
editor->modal_length = 0;
editor->modal_cursor = 0;
editor->clipboard = NULL;
editor->tabsize = 8;
editor->margin = SIZE_MAX;
editor->mode = MODE_EDIT;
editor->control = false;
editor->shift = false;
editor->lshift = false;
editor->rshift = false;
editor->dirty = false;
editor->modal_error = false;
editor->highlight_source = false;
editor->lines_used = 1;
editor->lines_length = 1;
editor->lines = new struct line[editor->lines_length];
editor->lines[0].data = NULL;
editor->lines[0].used = 0;
editor->lines[0].length = 0;
editor->color_lines_used = 0;
editor->color_lines_length = 0;
editor->color_lines = NULL;
}
void editor_reset_contents(struct editor* editor)
{
for ( size_t i = 0; i < editor->lines_used; i++ )
delete[] editor->lines[i].data;
delete[] editor->lines;
editor->lines_used = 1;
editor->lines_length = 1;
editor->lines = new struct line[editor->lines_length];
editor->lines[0].data = NULL;
editor->lines[0].used = 0;
editor->lines[0].length = 0;
editor->highlight_source = false;
editor_cursor_set(editor, 0, 0);
}
bool editor_load_file_contents(struct editor* editor, FILE* fp)
{
struct stat st;
if ( fstat(fileno(fp), &st) != 0 || S_ISDIR(st.st_mode) )
return errno = EISDIR, false;
free(editor->current_file_name);
editor->current_file_name = NULL;
editor_reset_contents(editor);
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
bool last_newline = false;
int ic;
while ( (ic = fgetc(fp)) != EOF )
{
if ( last_newline )
{
editor_type_newline(editor);
last_newline = false;
}
char c = (char) ic;
wchar_t wc;
size_t count = mbrtowc(&wc, &c, 1, &ps);
if ( count == (size_t) 0 )
continue;
if ( count == (size_t) -1 )
{
memset(&ps, 0, sizeof(ps));
wc = L'<EFBFBD>';
}
if ( count == (size_t) -2 )
continue;
assert(wc != L'\0');
if ( !(last_newline = wc == L'\n') )
editor_type_raw_character(editor, wc);
}
if ( !mbsinit(&ps) )
editor_type_raw_character(editor, L'<EFBFBD>');
editor_cursor_set(editor, 0, 0);
return true;
}
bool editor_load_file(struct editor* editor, const char* path)
{
if ( FILE* fp = fopen(path, "r") )
{
bool success = editor_load_file_contents(editor, fp);
fclose(fp);
if ( !success )
return false;
editor->dirty = false;
}
else if ( errno == ENOENT )
{
editor_reset_contents(editor);
editor->dirty = true;
}
else
return false;
editor->current_file_name = strdup(path);
editor->highlight_source = should_highlight_path(path);
return true;
}
bool editor_load_popen(struct editor* editor, const char* cmd)
{
FILE* fp = popen(cmd, "r");
if ( !fp )
return false;
bool success = editor_load_file_contents(editor, fp);
pclose(fp);
if ( !success )
return false;
editor->current_file_name = NULL;
editor->dirty = true;
return true;
}
bool editor_save_file(struct editor* editor, const char* path)
{
FILE* fp = fopen(path, "w");
if ( !fp )
return false;
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
for ( size_t i = 0; i < editor->lines_used; i++ )
{
char mb[MB_CUR_MAX];
for ( size_t n = 0; n < editor->lines[i].used; n++ )
{
mbstate_t saved_ps = ps;
wchar_t wc = editor->lines[i].data[n];
size_t count = wcrtomb(mb, wc, &ps);
if ( count == (size_t) -1 )
{
ps = saved_ps;
count = wcrtomb(mb, L'<EFBFBD>', &ps);
assert(count != (size_t) -1);
}
fwrite(mb, sizeof(char), count, fp);
}
size_t count = wcrtomb(mb, L'\n', &ps);
assert(count != (size_t) -1);
fwrite(mb, sizeof(char), count, fp);
}
editor->current_file_name = strdup(path);
editor->dirty = false;
editor->highlight_source = should_highlight_path(path);
return fclose(fp) != EOF;
}
int main(int argc, char* argv[])
{
setlocale(LC_ALL, "");
if ( !isatty(0) )
error(1, errno, "standard input");
if ( !isatty(1) )
error(1, errno, "standard output");
struct editor editor;
initialize_editor(&editor);
if ( 2 <= argc && !editor_load_file(&editor, argv[1]) )
error(1, errno, "`%s'", argv[1]);
struct editor_input editor_input;
editor_input_begin(&editor_input);
struct terminal_state stdout_state;
make_terminal_state(stdout, &stdout_state);
reset_terminal_state(stdout, &stdout_state);
fflush(stdout);
while ( editor.mode != MODE_QUIT )
{
struct terminal_state output_state;
make_terminal_state(stdout, &output_state);
editor_colorize(&editor);
render_editor(&editor, &output_state);
update_terminal(stdout, &output_state, &stdout_state);
free_terminal_state(&output_state);
fflush(stdout);
editor_input_process(&editor_input, &editor);
}
reset_terminal_state(stdout, &stdout_state);
free_terminal_state(&stdout_state);
fflush(stdout);
editor_input_end(&editor_input);
return 0;
}

102
editor/editor.h++ Normal file
View File

@ -0,0 +1,102 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
editor.h++
Editor.
*******************************************************************************/
#ifndef EDITOR_EDITOR_HXX
#define EDITOR_EDITOR_HXX
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
struct line
{
wchar_t* data;
size_t used;
size_t length;
};
struct color_line
{
uint8_t* data;
size_t length;
};
enum editor_mode
{
MODE_QUIT,
MODE_EDIT,
MODE_LOAD,
MODE_SAVE,
MODE_ASK_QUIT,
MODE_GOTO_LINE,
MODE_COMMAND,
};
struct editor
{
char* current_file_name;
struct line* lines;
size_t lines_used;
size_t lines_length;
struct color_line* color_lines;
size_t color_lines_used;
size_t color_lines_length;
size_t cursor_column;
size_t cursor_row;
size_t select_column;
size_t select_row;
size_t viewport_width;
size_t viewport_height;
size_t page_x_offset;
size_t page_y_offset;
wchar_t* modal;
size_t modal_used;
size_t modal_length;
size_t modal_cursor;
wchar_t* clipboard;
size_t tabsize;
size_t margin;
enum editor_mode mode;
bool control;
bool shift;
bool lshift;
bool rshift;
bool dirty;
bool modal_error;
bool highlight_source;
};
__attribute__((unused))
static inline bool editor_has_selection(struct editor* editor)
{
return !(editor->cursor_row == editor->select_row &&
editor->cursor_column == editor->select_column);
}
void initialize_editor(struct editor* editor);
void editor_reset_contents(struct editor* editor);
bool editor_load_file_contents(struct editor* editor, FILE* fp);
bool editor_load_file(struct editor* editor, const char* path);
bool editor_load_popen(struct editor* editor, const char* cmd);
bool editor_save_file(struct editor* editor, const char* path);
#endif

495
editor/highlight.c++ Normal file
View File

@ -0,0 +1,495 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
highlight.c++
Syntax highlighting.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#include "editor.h++"
#include "highlight.h++"
bool should_highlight_path(const char* path)
{
size_t path_length = strlen(path);
if ( 2 <= path_length &&
(!strcmp(path+path_length-2, ".c") ||
!strcmp(path+path_length-2, ".h")) )
return true;
if ( 4 <= path_length &&
(!strcmp(path+path_length-4, ".c++") ||
!strcmp(path+path_length-4, ".h++") ||
!strcmp(path+path_length-4, ".cxx") ||
!strcmp(path+path_length-4, ".hxx") ||
!strcmp(path+path_length-4, ".cpp") ||
!strcmp(path+path_length-4, ".hpp")) )
return true;
return false;
}
size_t recognize_constant(const wchar_t* string, size_t string_length)
{
bool hex = false;
size_t result = 0;
if ( result < string_length && string[result] == L'0' )
{
result++;
if ( result < string_length && (string[result] == L'x' ||
string[result] == L'X') )
{
result++;
hex = true;
}
}
bool floating = false;
bool exponent = false;
while ( result < string_length )
{
if ( (L'0' <= string[result] && string[result] <= L'9') ||
(hex && L'a' <= string[result] && string[result] <= L'f') ||
(hex && L'A' <= string[result] && string[result] <= L'F') )
{
result++;
continue;
}
if ( string[result] == L'.' )
{
if ( hex || floating )
return 0;
floating = true;
result++;
continue;
}
if ( !hex && (string[result] == L'e' || string[result] == L'E') )
{
if ( !result )
return 0;
if ( exponent )
return 0;
floating = true;
result++;
continue;
}
break;
}
if ( result == (hex ? 2 : 0) )
return 0;
if ( floating )
{
if ( result < string_length && (string[result] == L'l' ||
string[result] == L'L') )
result++;
else if ( result < string_length && (string[result] == L'f' ||
string[result] == L'F') )
result++;
}
else
{
if ( result < string_length && (string[result] == L'u' ||
string[result] == L'U') )
result++;
if ( result < string_length && (string[result] == L'l' ||
string[result] == L'L') )
result++;
if ( result < string_length && (string[result] == L'l' ||
string[result] == L'L') )
result++;
}
return result;
}
void editor_colorize(struct editor* editor)
{
if ( editor->color_lines_length != editor->lines_used ||
!editor->highlight_source )
{
for ( size_t i = 0; i < editor->color_lines_used; i++ )
delete[] editor->color_lines[i].data;
delete[] editor->color_lines;
editor->color_lines_used = 0;
editor->color_lines_length = 0;
editor->color_lines = NULL;
}
if ( !editor->highlight_source )
return;
if ( !editor->color_lines )
{
if ( !(editor->color_lines = new struct color_line[editor->lines_used]) )
return;
editor->color_lines_used = editor->lines_used;
editor->color_lines_length = editor->lines_used;
for ( size_t i = 0; i < editor->lines_used; i++ )
editor->color_lines[i].data = NULL,
editor->color_lines[i].length = 0;
}
for ( size_t i = 0; i < editor->lines_used; i++ )
{
if ( editor->color_lines[i].length == editor->lines[i].used )
continue;
if ( !(editor->color_lines[i].data = new uint8_t[editor->lines[i].used]) )
{
for ( size_t n = 0; n < i; i++ )
delete[] editor->color_lines[n].data;
delete[] editor->color_lines;
editor->color_lines_used = 0;
editor->color_lines_length = 0;
editor->color_lines = NULL;
return;
}
editor->color_lines[i].length = editor->lines[i].used;
}
enum
{
STATE_INIT,
STATE_LINE_COMMENT,
STATE_MULTI_LINE_COMMENT,
STATE_PREPROCESSOR,
STATE_SINGLE_QUOTE,
STATE_DOUBLE_QUOTE,
STATE_NUMBER,
STATE_KEYWORD,
STATE_TYPE,
} state = STATE_INIT, prev_state = STATE_INIT;
bool escaped = false;
size_t fixed_state = 0;
size_t multi_expiration = 0;
for ( size_t y = 0; y < editor->lines_used; y++ )
{
struct line* line = &editor->lines[y];
for ( size_t x = 0; x < line->used; x++ )
{
wchar_t pc = x ? line->data[x-1] : '\0';
wchar_t c = line->data[x];
wchar_t nc = x+1 < line->used ? line->data[x+1] : L'\0';
uint8_t color = 7;
// The character makes you leave this state.
if ( !fixed_state && (state == STATE_KEYWORD ||
state == STATE_TYPE ||
state == STATE_NUMBER ) )
state = STATE_INIT;
// The character makes you enter a new state.
if ( !fixed_state && state == STATE_INIT && c == L'#' )
state = STATE_PREPROCESSOR;
// TODO: Detect NULL as a value.
if ( !fixed_state && state == STATE_INIT &&
!(x && (iswalnum(pc) || pc == L'_')) )
{
size_t number_length = recognize_constant(line->data + x,
line->used - x);
if ( number_length )
{
state = STATE_NUMBER;
fixed_state = number_length;
}
}
if ( !fixed_state && state == STATE_INIT && c == L'\'' )
state = STATE_SINGLE_QUOTE, fixed_state = 1, escaped = false;
if ( !fixed_state && state == STATE_INIT && c == L'"' )
state = STATE_DOUBLE_QUOTE, fixed_state = 1, escaped = false;
if ( !fixed_state && (state == STATE_INIT ||
state == STATE_PREPROCESSOR) )
{
if ( c == L'/' && nc == L'/' )
state = STATE_LINE_COMMENT, fixed_state = 2;
else if ( c == L'/' && nc == L'*' )
{
prev_state = state;
multi_expiration = 0;
state = STATE_MULTI_LINE_COMMENT;
fixed_state = 2;
}
}
if ( !fixed_state && state == STATE_INIT )
{
const wchar_t* keywords[] =
{
L"alignas",
L"alignof",
L"and",
L"and_eq",
L"asm",
L"bitand",
L"bitor",
L"break",
L"case",
L"catch",
L"class",
L"compl",
L"const_cast",
L"constexpr",
L"continue",
L"decltype",
L"default",
L"delete",
L"do",
L"dynamic_cast",
L"else",
L"enum",
L"false",
L"final",
L"for",
L"friend",
L"goto",
L"if",
L"namespace",
L"new",
L"not",
L"not_eq",
L"nullptr",
L"operator",
L"or",
L"or_eq",
L"override",
L"private",
L"protected",
L"public",
L"reinterpret_cast",
L"return",
L"sizeof",
L"static_assert",
L"static_cast",
L"struct",
L"switch",
L"template",
L"this",
L"thread_local",
L"throw",
L"true",
L"try",
L"typedef",
L"typeid",
L"typename",
L"union",
L"using",
L"virtual",
L"while",
L"xor",
L"xor_eq",
};
bool cannot_be_keyword = x && (iswalnum(pc) || pc == L'_');
for ( size_t i = 0;
!cannot_be_keyword && i < sizeof(keywords) / sizeof(keywords[0]);
i++ )
{
const wchar_t* keyword = keywords[i];
if ( c != keyword[0] )
continue;
size_t keyword_length = wcslen(keyword);
if ( (x - line->used) < keyword_length )
continue;
if ( wcsncmp(line->data + x, keyword, keyword_length) != 0 )
continue;
if ( keyword_length < line->used - x )
{
wchar_t wc = line->data[x + keyword_length];
if ( iswalnum(wc) || wc == L'_' )
continue;
}
state = STATE_KEYWORD;
fixed_state = keyword_length;
}
}
if ( !fixed_state && state == STATE_INIT )
{
const wchar_t* types[] =
{
L"auto",
L"blkcnt_t",
L"blksize_t",
L"bool",
L"char",
L"char16_t",
L"char32_t",
L"clockid_t",
L"clock_t",
L"const",
L"dev_t",
L"double",
L"explicit",
L"extern",
L"FILE",
L"float",
L"fpos_t",
L"fsblkcnt_t",
L"fsfilcnt_t",
L"gid_t",
L"id_t",
L"inline",
L"ino_t",
L"int",
L"int16_t",
L"int32_t",
L"int64_t",
L"int8_t",
L"intmax_t",
L"intptr_t",
L"locale_t",
L"long",
L"mode_t",
L"mutable",
L"nlink_t",
L"noexcept",
L"off_t",
L"pid_t",
L"ptrdiff_t",
L"register",
L"restrict",
L"short",
L"signed",
L"size_t",
L"ssize_t",
L"static",
L"suseconds_t",
L"thread_local",
L"timer_t",
L"time_t",
L"trace_t",
L"uid_t",
L"uint16_t",
L"uint32_t",
L"uint64_t",
L"uint8_t",
L"uintmax_t",
L"uintptr_t",
L"unsigned",
L"useconds_t",
L"va_list",
L"void",
L"volatile",
L"wchar_t",
};
bool cannot_be_type = x && (iswalnum(pc) || pc == L'_');
for ( size_t i = 0;
!cannot_be_type && i < sizeof(types) / sizeof(types[0]);
i++ )
{
const wchar_t* type = types[i];
if ( c != type[0] )
continue;
size_t type_length = wcslen(type);
if ( (x - line->used) < type_length )
continue;
if ( wcsncmp(line->data + x, type, type_length) != 0 )
continue;
if ( (x - line->used) != type_length &&
(iswalnum(line->data[x+type_length]) ||
line->data[x+type_length] == L'_') )
continue;
state = STATE_TYPE;
fixed_state = type_length;
}
}
// The current state uses a non-default color.
if ( state == STATE_SINGLE_QUOTE ||
state == STATE_DOUBLE_QUOTE ||
state == STATE_NUMBER )
color = 5;
if ( state == STATE_PREPROCESSOR )
color = 3;
if ( state == STATE_LINE_COMMENT ||
state == STATE_MULTI_LINE_COMMENT )
color = 6;
if ( state == STATE_KEYWORD )
color = 1;
if ( state == STATE_TYPE )
color = 2;
// The character is the last character in this state.
if ( !fixed_state )
{
if ( state == STATE_SINGLE_QUOTE && !escaped && c == L'\'' )
state = STATE_INIT, fixed_state = 1;
if ( state == STATE_DOUBLE_QUOTE && !escaped && c == L'"' )
state = STATE_INIT, fixed_state = 1;
}
if ( (state == STATE_SINGLE_QUOTE || state == STATE_DOUBLE_QUOTE) )
{
if ( !escaped && c == L'\\' )
escaped = true;
else if ( escaped )
escaped = false;
}
if ( !fixed_state && state == STATE_MULTI_LINE_COMMENT )
{
if ( multi_expiration == 1 )
state = prev_state, multi_expiration = 0;
else if ( c == L'*' && nc == L'/' )
multi_expiration = 1;
}
if ( state == STATE_PREPROCESSOR )
escaped = c == L'\\' && !nc;
editor->color_lines[y].data[x] = color;
if ( fixed_state )
fixed_state--;
}
if ( state == STATE_LINE_COMMENT ||
state == STATE_PREPROCESSOR ||
state == STATE_SINGLE_QUOTE ||
state == STATE_DOUBLE_QUOTE )
{
if ( state == STATE_PREPROCESSOR && escaped )
escaped = false;
else
state = STATE_INIT;
}
}
}

31
editor/highlight.h++ Normal file
View File

@ -0,0 +1,31 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
highlight.h++
Syntax highlighting.
*******************************************************************************/
#ifndef EDITOR_HIGHLIGHT_HXX
#define EDITOR_HIGHLIGHT_HXX
struct editor;
bool should_highlight_path(const char* path);
void editor_colorize(struct editor* editor);
#endif

196
editor/input.c++ Normal file
View File

@ -0,0 +1,196 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
input.c++
Keyboard input.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#if defined(__sortix__)
#include <sys/keycodes.h>
#include <sys/termmode.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include "command.h++"
#include "editor.h++"
#include "input.h++"
#include "modal.h++"
void editor_codepoint(struct editor* editor, uint32_t codepoint)
{
wchar_t c = (wchar_t) codepoint;
if ( c == L'\b' || c == 127 /* delete */ )
return;
if ( editor->mode == MODE_EDIT )
editor_type_character(editor, c);
else
editor_modal_character(editor, c);
}
#if defined(__sortix__)
void editor_type_kbkey(struct editor* editor, int kbkey)
{
if ( kbkey < 0 )
return;
if ( kbkey == KBKEY_ESC )
{
editor_type_command(editor);
return;
}
if ( editor->control && editor->shift )
{
switch ( kbkey )
{
}
}
else if ( editor->control && !editor->shift )
{
switch ( kbkey )
{
}
}
else if ( !editor->control && editor->shift )
{
switch ( kbkey )
{
case KBKEY_LEFT: editor_type_select_left(editor); break;
case KBKEY_RIGHT: editor_type_select_right(editor); break;
case KBKEY_UP: editor_type_select_up(editor); break;
case KBKEY_DOWN: editor_type_select_down(editor); break;
case KBKEY_HOME: editor_type_select_home(editor); break;
case KBKEY_END: editor_type_select_end(editor); break;
case KBKEY_PGUP: editor_type_select_page_up(editor); break;
case KBKEY_PGDOWN: editor_type_select_page_down(editor); break;
case KBKEY_BKSPC: editor_type_backspace(editor); break;
case KBKEY_DELETE: editor_type_delete(editor); break;
}
}
else if ( !editor->control && !editor->shift )
{
switch ( kbkey )
{
case KBKEY_LEFT: editor_type_left(editor); break;
case KBKEY_RIGHT: editor_type_right(editor); break;
case KBKEY_UP: editor_type_up(editor); break;
case KBKEY_DOWN: editor_type_down(editor); break;
case KBKEY_HOME: editor_type_home(editor); break;
case KBKEY_END: editor_type_end(editor); break;
case KBKEY_PGUP: editor_type_page_up(editor); break;
case KBKEY_PGDOWN: editor_type_page_down(editor); break;
case KBKEY_BKSPC: editor_type_backspace(editor); break;
case KBKEY_DELETE: editor_type_delete(editor); break;
}
}
}
void editor_modal_kbkey(struct editor* editor, int kbkey)
{
if ( editor->control )
return;
if ( kbkey < 0 )
return;
switch ( kbkey )
{
case KBKEY_LEFT: editor_modal_left(editor); break;
case KBKEY_RIGHT: editor_modal_right(editor); break;
case KBKEY_HOME: editor_modal_home(editor); break;
case KBKEY_END: editor_modal_end(editor); break;
case KBKEY_BKSPC: editor_modal_backspace(editor); break;
case KBKEY_DELETE: editor_modal_delete(editor); break;
case KBKEY_ESC: editor_type_edit(editor); break;
}
}
void editor_kbkey(struct editor* editor, int kbkey)
{
int abskbkey = kbkey < 0 ? -kbkey : kbkey;
if ( abskbkey == KBKEY_LCTRL )
{
editor->control = 0 <= kbkey;
return;
}
if ( abskbkey == KBKEY_LSHIFT )
{
editor->lshift = 0 <= kbkey;
editor->shift = editor->lshift || editor->rshift;
return;
}
if ( abskbkey == KBKEY_RSHIFT )
{
editor->rshift = 0 <= kbkey;
editor->shift = editor->lshift || editor->rshift;
return;
}
if ( editor->mode == MODE_EDIT )
editor_type_kbkey(editor, kbkey);
else
editor_modal_kbkey(editor, kbkey);
}
#endif
void editor_input_begin(struct editor_input* editor_input)
{
#if defined(__sortix__)
gettermmode(0, &editor_input->saved_termmode);
settermmode(0, TERMMODE_KBKEY | TERMMODE_UNICODE);
#else
(void) editor_input;
#endif
}
void editor_input_process(struct editor_input* editor_input,
struct editor* editor)
{
#if defined(__sortix__)
(void) editor_input;
uint32_t input;
if ( read(0, &input, sizeof(input)) != sizeof(input) )
return;
if ( int kbkey = KBKEY_DECODE(input) )
editor_kbkey(editor, kbkey);
else
editor_codepoint(editor, input);
#else
(void) editor_input;
(void) editor;
#endif
}
void editor_input_end(struct editor_input* editor_input)
{
#if defined(__sortix__)
settermmode(0, editor_input->saved_termmode);
#else
(void) editor_input;
#endif
}

40
editor/input.h++ Normal file
View File

@ -0,0 +1,40 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
input.h++
Keyboard input.
*******************************************************************************/
#ifndef EDITOR_INPUT_HXX
#define EDITOR_INPUT_HXX
struct editor;
struct editor_input
{
#if defined(__sortix__)
unsigned int saved_termmode;
#endif
};
void editor_input_begin(struct editor_input* editor_input);
void editor_input_process(struct editor_input* editor_input,
struct editor* editor);
void editor_input_end(struct editor_input* editor_input);
#endif

304
editor/modal.c++ Normal file
View File

@ -0,0 +1,304 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
modal.c++
Modal commands.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <ctype.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
#include "command.h++"
#include "cursor.h++"
#include "editor.h++"
#include "modal.h++"
#include "multibyte.h++"
void editor_modal_left(struct editor* editor)
{
if ( editor->modal_cursor )
editor->modal_cursor--;
}
void editor_modal_right(struct editor* editor)
{
if ( editor->modal_cursor != editor->modal_used )
editor->modal_cursor++;
}
void editor_modal_home(struct editor* editor)
{
editor->modal_cursor = 0;
}
void editor_modal_end(struct editor* editor)
{
editor->modal_cursor = editor->modal_used;
}
void editor_modal_backspace(struct editor* editor)
{
if ( !editor->modal_cursor )
return;
editor->modal_error = false;
editor->modal_used--;
for ( size_t i = --editor->modal_cursor; i < editor->modal_used; i++ )
editor->modal[i] = editor->modal[i+1];
}
void editor_modal_delete(struct editor* editor)
{
if ( editor->modal_cursor == editor->modal_used )
return;
editor->modal_error = false;
editor->modal_used--;
for ( size_t i = editor->modal_cursor; i < editor->modal_used; i++ )
editor->modal[i] = editor->modal[i+1];
}
void editor_modal_load(struct editor* editor, const char* path)
{
if ( editor_load_file(editor, path) )
editor_type_edit(editor);
else
editor->modal_error = true;
}
void editor_modal_save(struct editor* editor, const char* path)
{
if ( editor_save_file(editor, path) )
editor_type_edit(editor);
else
editor->modal_error = true;
}
void editor_modal_ask_quit(struct editor* editor, const char* answer)
{
if ( tolower(answer[0]) == 'y' )
editor->mode = MODE_QUIT;
else if ( tolower(answer[0]) == 'n' || !answer[0] )
editor_type_edit(editor);
else
editor->modal_error = true;
}
void editor_modal_goto_line(struct editor* editor, const char* linestr)
{
if ( linestr[0] )
{
bool go_back = false, go_forward = false;
if ( linestr[0] == '+' )
linestr++, go_forward = true;
else if ( linestr[0] == '-' )
linestr++, go_back = true;
if ( !linestr[0] ) { editor->modal_error = true; return; }
const char* linestr_end;
unsigned long line = strtoul(linestr, (char**) &linestr_end, 0);
if ( *linestr_end ) { editor->modal_error = true; return; }
if ( go_back )
{
if ( editor->cursor_row < line )
{
editor->modal_error = true;
return;
}
editor_cursor_row_set(editor, editor->cursor_row - line);
}
else if ( go_forward )
{
if ( editor->lines_used - (editor->cursor_row+1) < line )
{
editor->modal_error = true;
return;
}
editor_cursor_row_set(editor, editor->cursor_row + line);
}
else
{
if ( editor->lines_used+1 <= line )
{
editor->modal_error = true;
return;
}
editor_cursor_row_set(editor, line ? line - 1 : 0);
}
editor_cursor_column_set(editor, 0);
}
editor_type_edit(editor);
}
void editor_modal_margin(struct editor* editor, const char* marginstr)
{
if ( !marginstr[0] )
editor->margin = SIZE_MAX;
else
{
char* end_ptr;
unsigned long margin = strtoul(marginstr, &end_ptr, 0);
if ( *end_ptr ) { editor->modal_error = true; return; }
editor->margin = margin;
}
editor_type_edit(editor);
}
void editor_modal_popen(struct editor* editor, const char* cmd)
{
if ( cmd[0] && editor_load_popen(editor, cmd) )
editor_type_edit(editor);
else
editor->modal_error = true;
}
void editor_modal_tabsize(struct editor* editor, const char* tabsizestr)
{
if ( !tabsizestr[0] )
editor->tabsize = 8;
else
{
char* end_ptr;
unsigned long tabsize = strtoul(tabsizestr, &end_ptr, 0);
if ( !tabsize || *end_ptr || 256 < tabsize )
{
editor->modal_error = true;
return;
}
editor->tabsize = tabsize;
}
editor_type_edit(editor);
}
void editor_modal_language(struct editor* editor, const char* language)
{
if ( !language[0] || !strcmp(language, "none") )
{
editor->highlight_source = false;
return;
}
if ( !strcmp(language, "c") || !strcmp(language, "c++") )
{
editor->highlight_source = true;
return;
}
editor->modal_error = true;
editor_type_edit(editor);
}
bool is_modal_command(const char* cmd, const char* candidate, const char** rest)
{
size_t candidate_len = strlen(candidate);
if ( strncmp(cmd, candidate, candidate_len) == 0 &&
(!cmd[candidate_len] || isspace(cmd[candidate_len])) )
{
*rest = cmd + candidate_len;
while ( **rest && isspace(**rest) )
(*rest)++;
return true;
}
return false;
}
void editor_modal_command(struct editor* editor, const char* cmd)
{
while ( *cmd && isspace(*cmd) )
cmd++;
if ( cmd[0] == ':' )
cmd++;
if ( !cmd[0] ) { editor_type_edit(editor); return; }
if ( !strcmp(cmd, "q") || !strcmp(cmd, "exit") || !strcmp(cmd, "quit") )
editor_type_quit(editor);
else if ( !strcmp(cmd, "q!") )
editor->dirty = false, editor_type_quit(editor);
else if ( !strcmp(cmd, "w") )
editor_type_save(editor);
else if ( !strcmp(cmd, "wq") || !strcmp(cmd, "wq!") )
editor->dirty ? editor_type_save(editor)
: editor_type_quit(editor);
else if ( is_modal_command(cmd, "margin", &cmd) )
editor_modal_margin(editor, cmd);
else if ( is_modal_command(cmd, "popen", &cmd) )
editor_modal_popen(editor, cmd);
else if ( is_modal_command(cmd, "tabsize", &cmd) )
editor_modal_tabsize(editor, cmd);
else if ( is_modal_command(cmd, "language", &cmd) )
editor_modal_language(editor, cmd);
else
editor->modal_error = true;
}
void editor_modal_character(struct editor* editor, wchar_t c)
{
if ( editor->control )
{
switch ( towlower(c) )
{
case L'c': editor_type_edit(editor); break;
}
return;
}
editor->modal_error = false;
if ( c == L'\n' )
{
if ( !editor->modal )
editor->modal = (wchar_t*) malloc(sizeof(wchar_t) * 1);
editor->modal[editor->modal_used] = L'\0';
char* param = convert_wcs_to_mbs(editor->modal);
switch ( editor->mode )
{
case MODE_LOAD: editor_modal_load(editor, param); break;
case MODE_SAVE: editor_modal_save(editor, param); break;
case MODE_ASK_QUIT: editor_modal_ask_quit(editor, param); break;
case MODE_GOTO_LINE: editor_modal_goto_line(editor, param); break;
case MODE_COMMAND: editor_modal_command(editor, param); break;
default: break;
}
free(param);
return;
}
if ( editor->modal_used == editor->modal_length )
{
size_t new_length = editor->modal_length ? 2 * editor->modal_length : 8;
wchar_t* new_data = (wchar_t*) malloc(sizeof(wchar_t) * (new_length + 1));
for ( size_t i = 0; i < editor->modal_used; i++ )
new_data[i] = editor->modal[i];
free(editor->modal);
editor->modal = new_data;
editor->modal_length = new_length;
}
for ( size_t i = editor->modal_used; editor->modal_cursor < i; i-- )
editor->modal[i] = editor->modal[i-1];
editor->modal_used++;
editor->modal[editor->modal_cursor++] = c;
}

48
editor/modal.h++ Normal file
View File

@ -0,0 +1,48 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
modal.h++
Modal commands.
*******************************************************************************/
#ifndef EDITOR_MODAL_HXX
#define EDITOR_MODAL_HXX
struct editor;
void editor_modal_left(struct editor* editor);
void editor_modal_right(struct editor* editor);
void editor_modal_home(struct editor* editor);
void editor_modal_end(struct editor* editor);
void editor_modal_backspace(struct editor* editor);
void editor_modal_delete(struct editor* editor);
void editor_modal_load(struct editor* editor, const char* path);
void editor_modal_save(struct editor* editor, const char* path);
void editor_modal_ask_quit(struct editor* editor, const char* answer);
void editor_modal_goto_line(struct editor* editor, const char* linestr);
void editor_modal_margin(struct editor* editor, const char* marginstr);
void editor_modal_popen(struct editor* editor, const char* cmd);
void editor_modal_tabsize(struct editor* editor, const char* tabsizestr);
void editor_modal_language(struct editor* editor, const char* language);
bool is_modal_command(const char* cmd, const char* candidate, const char** rest);
void editor_modal_command(struct editor* editor, const char* cmd);
void editor_modal_character(struct editor* editor, wchar_t c);
#endif

161
editor/multibyte.c++ Normal file
View File

@ -0,0 +1,161 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
multibyte.c++
Conversion from multibyte strings to wide strings.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "multibyte.h++"
wchar_t* convert_mbs_to_wcs(const char* mbs)
{
if ( !mbs )
mbs = "";
size_t mbs_offset = 0;
size_t mbs_length = strlen(mbs) + 1;
mbstate_t ps;
// Determinal the length of the resulting string.
size_t wcs_length = 0;
memset(&ps, 0, sizeof(ps));
while ( true )
{
wchar_t wc;
size_t count = mbrtowc(&wc, mbs + mbs_offset, mbs_length - mbs_offset, &ps);
if ( count == (size_t) 0 )
break;
wcs_length++;
if ( count == (size_t) -1 )
{
memset(&ps, 0, sizeof(ps));
mbs_offset++; // Attempt to recover.
continue;
}
if ( count == (size_t) -2 )
break;
mbs_offset += count;
}
wchar_t* result = (wchar_t*) malloc(sizeof(wchar_t) * (wcs_length + 1));
if ( !result )
return NULL;
// Create the resulting string.
mbs_offset = 0;
size_t wcs_offset = 0;
memset(&ps, 0, sizeof(ps));
while ( true )
{
wchar_t wc;
size_t count = mbrtowc(&wc, mbs + mbs_offset, mbs_length - mbs_offset, &ps);
if ( count == (size_t) 0 )
break;
assert(mbs_offset < wcs_length);
if ( count == (size_t) -1 )
{
memset(&ps, 0, sizeof(ps));
result[wcs_offset++] = L'<EFBFBD>';
mbs_offset++; // Attempt to recover.
continue;
}
if ( count == (size_t) -2 )
{
result[wcs_offset++] = L'<EFBFBD>';
break;
}
result[wcs_offset++] = wc;
mbs_offset += count;
}
result[wcs_offset] = L'\0';
return result;
}
char* convert_wcs_to_mbs(const wchar_t* wcs)
{
const char* replacement_mb = "<EFBFBD>";
size_t replacement_mblen = strlen(replacement_mb);
if ( !wcs )
wcs = L"";
mbstate_t ps;
// Determinal the length of the resulting string.
size_t wcs_offset = 0;
size_t mbs_length = 0;
memset(&ps, 0, sizeof(ps));
while ( true )
{
wchar_t wc = wcs[wcs_offset++];
char mb[MB_CUR_MAX];
size_t count = wcrtomb(mb, wc, &ps);
if ( count == (size_t) -1 )
{
memset(&ps, 0, sizeof(ps));
mbs_length += replacement_mblen;
continue;
}
mbs_length += count;
if ( wc == L'\0' )
break;
}
char* result = (char*) malloc(sizeof(char) * mbs_length);
if ( !result )
return NULL;
// Create the resulting string.
wcs_offset = 0;
size_t mbs_offset = 0;
memset(&ps, 0, sizeof(ps));
while ( true )
{
wchar_t wc = wcs[wcs_offset++];
char mb[MB_CUR_MAX];
size_t count = wcrtomb(mb, wc, &ps);
if ( count == (size_t) -1 )
{
memset(&ps, 0, sizeof(ps));
assert(replacement_mblen <= mbs_length - mbs_offset);
memcpy(result + mbs_offset, replacement_mb, sizeof(char) * replacement_mblen);
mbs_offset += replacement_mblen;
continue;
}
assert(count <= mbs_length - mbs_offset);
memcpy(result + mbs_offset, mb, sizeof(char) * count);
mbs_offset += count;
if ( wc == L'\0' )
break;
}
return result;
}

31
editor/multibyte.h++ Normal file
View File

@ -0,0 +1,31 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
multibyte.h++
Conversion from multibyte strings to wide strings.
*******************************************************************************/
#ifndef EDITOR_MULTIBYTE_HXX
#define EDITOR_MULTIBYTE_HXX
#include <stddef.h>
wchar_t* convert_mbs_to_wcs(const char* mbs);
char* convert_wcs_to_mbs(const wchar_t* wcs);
#endif

142
editor/terminal.c++ Normal file
View File

@ -0,0 +1,142 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
terminal.c++
Terminal handling.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <termios.h>
#include "terminal.h++"
void update_terminal_color(FILE* fp, uint8_t desired_color,
struct terminal_state* current)
{
uint8_t desired_fg = (desired_color >> 0) % 16;
uint8_t desired_bg = (desired_color >> 4) % 16;
uint8_t current_fg = (current->color >> 0) % 16;
uint8_t current_bg = (current->color >> 4) % 16;
if ( desired_fg != current_fg )
fprintf(fp, "\e[%im", desired_fg + (desired_fg < 8 ? 30 : 90-8) );
if ( desired_bg != current_bg )
fprintf(fp, "\e[%im", desired_bg + (desired_bg < 8 ? 40 : 100-8) );
current->color = desired_color;
}
void update_terminal_cursor(FILE* fp, int x, int y,
struct terminal_state* current)
{
if ( current->cursor_x == x && current->cursor_y == y )
return;
fprintf(fp, "\e[%i;%iH", y + 1, x + 1);
current->cursor_x = x;
current->cursor_y = y;
}
void update_terminal_entry(FILE* fp, struct terminal_datum entry, int x, int y,
struct terminal_state* current)
{
assert(entry.character != L'\0');
size_t index = y * current->width + x;
struct terminal_datum current_entry = current->data[index];
if ( entry.character == current_entry.character &&
entry.vgacolor == current_entry.vgacolor )
return;
update_terminal_cursor(fp, x, y, current);
update_terminal_color(fp, entry.vgacolor, current);
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
char mb[MB_CUR_MAX];
size_t count = wcrtomb(mb, entry.character, &ps);
if ( count == (size_t) -1 )
fputs("<EFBFBD>", fp);
else for ( size_t i = 0; i < count; i++ )
fputc(mb[i], fp);
current->data[index] = entry;
if ( ++current->cursor_x == current->width )
{
current->cursor_x = 0;
current->cursor_y++;
}
}
void update_terminal(FILE* fp,
struct terminal_state* desired,
struct terminal_state* current)
{
// TODO: If terminal size has changed!
for ( int y = 0; y < current->height; y++ )
{
for ( int x = 0; x < current->width; x++ )
{
size_t index = y * desired->width + x;
struct terminal_datum desired_entry = desired->data[index];
update_terminal_entry(fp, desired_entry, x, y, current);
}
}
update_terminal_cursor(fp, desired->cursor_x, desired->cursor_y, current);
update_terminal_color(fp, desired->color, current);
}
void make_terminal_state(FILE* fp, struct terminal_state* state)
{
memset(state, 0, sizeof(*state));
struct winsize terminal_size;
tcgetwinsize(fileno(fp), &terminal_size);
state->width = (int) terminal_size.ws_col;
state->height = (int) terminal_size.ws_row;
size_t data_length = state->width * state->height;
size_t data_size = sizeof(struct terminal_datum) * data_length;
state->data = (struct terminal_datum*) malloc(data_size);
for ( size_t i = 0; i < data_length; i++ )
state->data[i].character = L' ',
state->data[i].vgacolor = 0;
}
void free_terminal_state(struct terminal_state* state)
{
free(state->data);
}
void reset_terminal_state(FILE* fp, struct terminal_state* state)
{
fprintf(fp, "\e[H");
fprintf(fp, "\e[m");
fprintf(fp, "\e[2J");
state->cursor_x = 0;
state->cursor_y = 0;
update_terminal_color(fp, 0x07, state);
for ( int y = 0; y < state->height; y++ )
for ( int x = 0; x < state->width; x++ )
update_terminal_entry(fp, make_terminal_datum(L' ', 0x07), x, y, state);
update_terminal_cursor(fp, 0, 0, state);
}

104
editor/terminal.h++ Normal file
View File

@ -0,0 +1,104 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
terminal.h++
Terminal handling.
*******************************************************************************/
#ifndef EDITOR_TERMINAL_HXX
#define EDITOR_TERMINAL_HXX
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
struct terminal_datum
{
wchar_t character;
uint8_t vgacolor;
};
__attribute__((unused))
static inline
struct terminal_datum make_terminal_datum(wchar_t c, uint8_t cl)
{
struct terminal_datum result = { c, cl };
return result;
}
struct terminal_state
{
int width;
int height;
int cursor_x;
int cursor_y;
uint8_t color;
struct terminal_datum* data;
};
__attribute__((unused))
static inline
bool is_row_column_lt(size_t ra, size_t ca, size_t rb, size_t cb)
{
return ra < rb || (ra == rb && ca < cb);
}
__attribute__((unused))
static inline
bool is_row_column_le(size_t ra, size_t ca, size_t rb, size_t cb)
{
return (ra == rb && ca == cb) || is_row_column_lt(ra, ca, rb, cb);
}
__attribute__((unused))
static inline
void row_column_smallest(size_t ra, size_t ca, size_t rb, size_t cb,
size_t* row, size_t* column)
{
if ( is_row_column_lt(ra, ca, rb, cb) )
*row = ra, *column = ca;
else
*row = rb, *column = cb;
}
__attribute__((unused))
static inline
void row_column_biggest(size_t ra, size_t ca, size_t rb, size_t cb,
size_t* row, size_t* column)
{
if ( is_row_column_lt(ra, ca, rb, cb) )
*row = rb, *column = cb;
else
*row = ra, *column = ca;
}
void update_terminal_color(FILE* fp, uint8_t desired_color,
struct terminal_state* current);
void update_terminal_cursor(FILE* fp, int x, int y,
struct terminal_state* current);
void update_terminal_entry(FILE* fp, struct terminal_datum entry, int x, int y,
struct terminal_state* current);
void update_terminal(FILE* fp,
struct terminal_state* desired,
struct terminal_state* current);
void make_terminal_state(FILE* fp, struct terminal_state* state);
void free_terminal_state(struct terminal_state* state);
void reset_terminal_state(FILE* fp, struct terminal_state* state);
#endif

1
utils/.gitignore vendored
View File

@ -13,7 +13,6 @@ date
dirname dirname
du du
echo echo
editor
env env
expr expr
false false

View File

@ -25,7 +25,6 @@ date \
dirname \ dirname \
du \ du \
echo \ echo \
editor \
env \ env \
expr \ expr \
false \ false \

File diff suppressed because it is too large Load Diff