525 lines
13 KiB
C
525 lines
13 KiB
C
#include "init.h"
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "Editor.h"
|
|
|
|
static int copyMode;
|
|
static Text copy;
|
|
|
|
void Editor_static_initialize(void)
|
|
{
|
|
copyMode = EDITOR_COPY_MODE_NONE;
|
|
copy = NULL;
|
|
}
|
|
|
|
Editor Editor_create(Window window, Text text, int tabWidth, Input input, KeyCommand keyCommand)
|
|
{
|
|
Editor this;
|
|
|
|
this = malloc(sizeof(struct Editor_struct));
|
|
if (this == NULL) {
|
|
fprintf(stderr, "malloc failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
this->view = View_create(window, text, tabWidth);
|
|
this->text = text;
|
|
this->input = input;
|
|
this->keyCommand = keyCommand;
|
|
this->mode = EDITOR_MODE_COMMAND;
|
|
this->filename = NULL;
|
|
|
|
return this;
|
|
}
|
|
|
|
void Editor_destroy(Editor this)
|
|
{
|
|
if (this == NULL) {
|
|
return;
|
|
}
|
|
|
|
View_destroy(this->view);
|
|
free(this->filename);
|
|
free(this);
|
|
}
|
|
|
|
void Editor_updateCursorPosition(Editor this)
|
|
{
|
|
View_updateCursorPosition(this->view);
|
|
}
|
|
|
|
void Editor_paint(Editor this)
|
|
{
|
|
View_paint(this->view);
|
|
}
|
|
|
|
static void runInput(Editor this)
|
|
{
|
|
char *string;
|
|
|
|
string = Line_toString(this->input->line);
|
|
if (strlen(string) < 1) {
|
|
goto finish;
|
|
}
|
|
if (strcmp(string, ":") == 0) {
|
|
goto finish;
|
|
}
|
|
|
|
if (strcmp(string + 1, "q") == 0) {
|
|
exit(EXIT_SUCCESS);
|
|
} else if (strcmp(string + 1, "w") == 0) {
|
|
Editor_save(this);
|
|
} else if (strcmp(string + 1, "wq") == 0) {
|
|
Editor_save(this);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
finish:
|
|
free(string);
|
|
}
|
|
|
|
static void runKeyCommand(Editor this)
|
|
{
|
|
if (this->keyCommand->edit != 0) {
|
|
switch (this->keyCommand->edit) {
|
|
default:
|
|
assert(0);
|
|
case '\f': /* repaint */
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case ':': /* input command line */
|
|
this->mode = EDITOR_MODE_FIELD;
|
|
Input_keyTyped(this->input, ':');
|
|
Input_paint(this->input);
|
|
Input_updateCursorPosition(this->input);
|
|
break;
|
|
case 'i': /* insert */
|
|
this->mode = EDITOR_MODE_INSERT;
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case 'a': /* insert after */
|
|
this->mode = EDITOR_MODE_INSERT;
|
|
if (Text_size(this->text) > 0) {
|
|
if (Line_size(Text_getLine(this->text, this->view->cursorLine)) > 0) {
|
|
this->view->cursorCharacter++;
|
|
}
|
|
}
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case 'I': /* insert at head */
|
|
this->mode = EDITOR_MODE_INSERT;
|
|
this->view->cursorCharacter = 0;
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case 'A': /* insert at tail */
|
|
this->mode = EDITOR_MODE_INSERT;
|
|
if (Text_size(this->text) > 0) {
|
|
this->view->cursorCharacter = Line_size(Text_getLine(this->text, this->view->cursorLine));
|
|
}
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case 'o': /* open line */
|
|
this->mode = EDITOR_MODE_INSERT;
|
|
if (Text_size(this->text) > 0) {
|
|
this->view->cursorLine++;
|
|
}
|
|
this->view->cursorCharacter = 0;
|
|
Text_addLine(this->text, this->view->cursorLine, Line_create());
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case 'O': /* open previous line */
|
|
this->mode = EDITOR_MODE_INSERT;
|
|
this->view->cursorCharacter = 0;
|
|
Text_addLine(this->text, this->view->cursorLine, Line_create());
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
break;
|
|
case 'x': { /* delete character */
|
|
Line line;
|
|
int repeat;
|
|
int i;
|
|
|
|
line = Text_getLine(this->text, this->view->cursorLine);
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
if (repeat > Line_size(line)) {
|
|
repeat = Line_size(line);
|
|
}
|
|
|
|
for (i = 0; i < repeat; i++) {
|
|
int size;
|
|
|
|
Line_removeCharacter(line, this->view->cursorCharacter);
|
|
size = Line_size(line);
|
|
if (size > 0 && this->view->cursorCharacter >= size) {
|
|
this->view->cursorCharacter = size - 1;
|
|
}
|
|
}
|
|
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
} break;
|
|
case 'd': { /* delete line */
|
|
int repeat;
|
|
int i;
|
|
int size;
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
if (repeat > Text_size(this->text) - this->view->cursorLine) {
|
|
repeat = Text_size(this->text) - this->view->cursorLine;
|
|
}
|
|
if (repeat < 1) {
|
|
break;
|
|
}
|
|
Text_destroy(copy);
|
|
copyMode = EDITOR_COPY_MODE_LINES;
|
|
copy = Text_create();
|
|
for (i = 0; i < repeat; i++) {
|
|
Text_addLine(copy, Text_size(copy), Text_removeLine(this->text, this->view->cursorLine));
|
|
size = Text_size(this->text);
|
|
if (size > 0 && this->view->cursorLine >= size) {
|
|
this->view->cursorLine = size - 1;
|
|
}
|
|
}
|
|
this->view->cursorCharacter = 0;
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
} break;
|
|
case 'J': { /* join lines */
|
|
Line line;
|
|
Line nextLine;
|
|
|
|
if (this->view->cursorLine + 1 > Text_size(this->text) - 1) {
|
|
break;
|
|
}
|
|
|
|
line = Text_getLine(this->text, this->view->cursorLine);
|
|
this->view->cursorCharacter = Line_size(line);
|
|
Line_addCharacter(line, Line_size(line), ' ');
|
|
nextLine = Text_getLine(this->text, this->view->cursorLine + 1);
|
|
Line_addLine(line, Line_size(line), nextLine);
|
|
Line_destroy(Text_removeLine(this->text, this->view->cursorLine + 1));
|
|
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
} break;
|
|
case 'y': { /* copy */
|
|
int repeat;
|
|
int i;
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
if (repeat > Text_size(this->text) - this->view->cursorLine) {
|
|
repeat = Text_size(this->text) - this->view->cursorLine;
|
|
}
|
|
if (repeat < 1) {
|
|
break;
|
|
}
|
|
Text_destroy(copy);
|
|
copyMode = EDITOR_COPY_MODE_LINES;
|
|
copy = Text_create();
|
|
for (i = 0; i < repeat; i++) {
|
|
Text_addLine(copy, Text_size(copy), Line_clone(Text_getLine(this->text, this->view->cursorLine + i)));
|
|
}
|
|
} break;
|
|
case 'p': { /* paste */
|
|
int i;
|
|
int size;
|
|
int lastLine;
|
|
|
|
if (copyMode != EDITOR_COPY_MODE_LINES) {
|
|
break;
|
|
}
|
|
for (i = 0; i < Text_size(copy); i++) {
|
|
if (Text_size(this->text) < 1) {
|
|
Text_addLine(this->text, 0, Line_clone(Text_getLine(copy, i)));
|
|
} else {
|
|
Text_addLine(this->text, this->view->cursorLine + 1, Line_clone(Text_getLine(copy, i)));
|
|
this->view->cursorLine++;
|
|
}
|
|
}
|
|
size = Line_size(Text_getLine(this->text, this->view->cursorLine));
|
|
if (size < 1) {
|
|
this->view->cursorCharacter = 0;
|
|
} else if (this->view->cursorCharacter >= size) {
|
|
this->view->cursorCharacter = size - 1;
|
|
}
|
|
lastLine = View_lastLineIndex(this->view);
|
|
if (this->view->cursorLine > lastLine) {
|
|
this->view->origin += this->view->cursorLine - lastLine;
|
|
}
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
} break;
|
|
case 'P': { /* paste above */
|
|
int i;
|
|
int size;
|
|
|
|
if (copyMode != EDITOR_COPY_MODE_LINES) {
|
|
break;
|
|
}
|
|
for (i = 0; i < Text_size(copy); i++) {
|
|
Text_addLine(this->text, this->view->cursorLine, Line_clone(Text_getLine(copy, i)));
|
|
}
|
|
size = Line_size(Text_getLine(this->text, this->view->cursorLine));
|
|
if (size < 1) {
|
|
this->view->cursorCharacter = 0;
|
|
} else if (this->view->cursorCharacter >= size) {
|
|
this->view->cursorCharacter = size - 1;
|
|
}
|
|
if (this->view->cursorLine < this->view->origin) {
|
|
this->view->origin = this->view->cursorLine;
|
|
}
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
} break;
|
|
case 'Z':
|
|
if (this->keyCommand->motion == 'Z') {
|
|
Editor_save(this);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
break;
|
|
}
|
|
} else if (this->keyCommand->motion != 0) {
|
|
switch (this->keyCommand->motion) {
|
|
default:
|
|
assert(0);
|
|
case '0': /* move to head of line */
|
|
View_moveToHead(this->view);
|
|
break;
|
|
case '$': /* move to tail of line */
|
|
View_moveToTail(this->view);
|
|
break;
|
|
case 'G': { /* move to line */
|
|
int index;
|
|
|
|
index = this->keyCommand->count1 - 1;
|
|
if (index == -1) {
|
|
index = Text_size(this->text) - 1;
|
|
}
|
|
if (index >= Text_size(this->text)) {
|
|
index = Text_size(this->text) - 1;
|
|
}
|
|
if (index < 0) {
|
|
index = 0;
|
|
}
|
|
View_moveToLine(this->view, index);
|
|
} break;
|
|
case 'h': { /* move left */
|
|
int repeat;
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
View_moveLeft(this->view, repeat);
|
|
} break;
|
|
case 'j': { /* move down */
|
|
int repeat;
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
View_moveDown(this->view, repeat);
|
|
} break;
|
|
case 'k': { /* move up */
|
|
int repeat;
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
View_moveUp(this->view, repeat);
|
|
} break;
|
|
case 'l': { /* move right */
|
|
int repeat;
|
|
|
|
repeat = this->keyCommand->count1;
|
|
if (repeat == 0) {
|
|
repeat = 1;
|
|
}
|
|
View_moveRight(this->view, repeat);
|
|
} break;
|
|
}
|
|
} else {
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void showKeyCommand(Editor this)
|
|
{
|
|
char edit;
|
|
char motion;
|
|
|
|
if (this->keyCommand->edit == 0) {
|
|
edit = '-';
|
|
} else {
|
|
edit = this->keyCommand->edit;
|
|
}
|
|
if (this->keyCommand->motion == 0) {
|
|
motion = '-';
|
|
} else {
|
|
motion = this->keyCommand->motion;
|
|
}
|
|
|
|
Window_setCursorPosition(this->input->window, 0, 0);
|
|
printf("[%d,%c,%d,%c]", this->keyCommand->count1, edit, this->keyCommand->count2, motion);
|
|
Editor_updateCursorPosition(this);
|
|
}
|
|
#endif
|
|
|
|
void Editor_keyTyped(Editor this, int keyChar)
|
|
{
|
|
switch (this->mode) {
|
|
default:
|
|
assert(0);
|
|
case EDITOR_MODE_COMMAND:
|
|
switch (KeyCommand_read(this->keyCommand, keyChar)) {
|
|
default:
|
|
assert(0);
|
|
case KEY_COMMAND_SWALLOWED:
|
|
return;
|
|
case KEY_COMMAND_FINISHED:
|
|
#ifdef DEBUG
|
|
showKeyCommand(this);
|
|
#endif
|
|
runKeyCommand(this);
|
|
KeyCommand_reset(this->keyCommand);
|
|
return;
|
|
case KEY_COMMAND_CANCELED:
|
|
KeyCommand_reset(this->keyCommand);
|
|
return;
|
|
case KEY_COMMAND_ERROR:
|
|
KeyCommand_reset(this->keyCommand);
|
|
return;
|
|
}
|
|
case EDITOR_MODE_FIELD:
|
|
if (keyChar == 0x1b) {
|
|
this->mode = EDITOR_MODE_COMMAND;
|
|
Input_clear(this->input);
|
|
Input_paint(this->input);
|
|
Editor_updateCursorPosition(this);
|
|
} else if (keyChar == '\n') {
|
|
runInput(this);
|
|
this->mode = EDITOR_MODE_COMMAND;
|
|
Input_clear(this->input);
|
|
Input_paint(this->input);
|
|
Editor_updateCursorPosition(this);
|
|
} else {
|
|
Input_keyTyped(this->input, keyChar);
|
|
Input_paint(this->input);
|
|
Input_updateCursorPosition(this->input);
|
|
}
|
|
break;
|
|
case EDITOR_MODE_INSERT:
|
|
if (Text_size(this->text) <= this->view->cursorLine) {
|
|
Text_addLine(this->text, this->view->cursorLine, Line_create());
|
|
}
|
|
if (keyChar == 0x1b) {
|
|
this->mode = EDITOR_MODE_COMMAND;
|
|
|
|
if (this->view->cursorCharacter > 0) {
|
|
this->view->cursorCharacter--;
|
|
}
|
|
|
|
Editor_updateCursorPosition(this);
|
|
} else if (keyChar == '\n') {
|
|
Line line;
|
|
Line subLine;
|
|
int lastLine;
|
|
|
|
line = Text_getLine(this->text, this->view->cursorLine);
|
|
subLine = Line_removeRange(line, this->view->cursorCharacter, Line_size(line) - this->view->cursorCharacter);
|
|
|
|
this->view->cursorLine++;
|
|
this->view->cursorCharacter = 0;
|
|
Text_addLine(this->text, this->view->cursorLine, subLine);
|
|
|
|
lastLine = View_lastLineIndex(this->view);
|
|
if (this->view->cursorLine > lastLine) {
|
|
this->view->origin += this->view->cursorLine - lastLine;
|
|
Editor_paint(this);
|
|
}
|
|
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
} else if (keyChar == 0x08 || keyChar == 0x7f) {
|
|
Line line;
|
|
int size;
|
|
|
|
line = Text_getLine(this->text, this->view->cursorLine);
|
|
size = Line_size(line);
|
|
if (size > 0) {
|
|
Line_removeCharacter(line, size - 1);
|
|
this->view->cursorCharacter--;
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
}
|
|
} else {
|
|
Line line;
|
|
int lastLine;
|
|
|
|
line = Text_getLine(this->text, this->view->cursorLine);
|
|
Line_addCharacter(line, this->view->cursorCharacter, keyChar);
|
|
this->view->cursorCharacter++;
|
|
|
|
lastLine = View_lastLineIndex(this->view);
|
|
if (this->view->cursorLine > lastLine) {
|
|
this->view->origin += this->view->cursorLine - lastLine;
|
|
}
|
|
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Editor_setFilename(Editor this, char *filename)
|
|
{
|
|
free(this->filename);
|
|
|
|
this->filename = malloc(sizeof(char) * (strlen(filename) + 1));
|
|
if (this->filename == NULL) {
|
|
fprintf(stderr, "malloc failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
strcpy(this->filename, filename);
|
|
}
|
|
|
|
void Editor_load(Editor this)
|
|
{
|
|
if (this->filename == NULL) {
|
|
return;
|
|
}
|
|
|
|
Text_load(this->text, this->filename);
|
|
|
|
Editor_paint(this);
|
|
Editor_updateCursorPosition(this);
|
|
}
|
|
|
|
void Editor_save(Editor this)
|
|
{
|
|
if (this->filename == NULL) {
|
|
return;
|
|
}
|
|
|
|
Text_save(this->text, this->filename);
|
|
}
|