From 18aa993466ab857b2a56612dabd243992e987998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Wed, 10 Sep 2014 11:46:59 +0200 Subject: Add comments where appropriate --- config.def.h | 74 ++++++++++++++++------- editor.h | 26 +++++++- register.c | 10 +--- register.h | 10 ++-- syntax.h | 12 ++-- text-motions.c | 2 +- text-motions.h | 19 ++++-- text-objects.h | 13 +++- text.c | 4 +- text.h | 2 +- util.h | 4 +- vis.c | 183 ++++++++++++++++++++++++++++++++++++++++----------------- window.c | 18 +++--- window.h | 18 +++++- 14 files changed, 272 insertions(+), 123 deletions(-) diff --git a/config.def.h b/config.def.h index df35f2a..49a87fd 100644 --- a/config.def.h +++ b/config.def.h @@ -1,3 +1,6 @@ +/** start by reading from the top of vis.c up until config.h is included */ + +/* macros used to specify keys for key bindings */ #define ESC 0x1B #define NONE(k) { .str = { k }, .code = 0 } #define KEY(k) { .str = { '\0' }, .code = KEY_##k } @@ -9,6 +12,16 @@ { { NONE(127) }, (func), { .name = (arg) } }, \ { { CONTROL('B') }, (func), { .name = (arg) } } +/* a mode contains a set of key bindings which are currently valid. + * + * each mode can specify one parent mode which is consultated if a given key + * is not found in the current mode. hence the modes form a tree which is + * searched from the current mode up towards the root mode until a valid binding + * is found. + * + * if no binding is found, mode->input(...) is called and the user entered + * keys are passed as argument. this is used to change the document content. + */ static Mode vis_modes[]; enum { @@ -31,26 +44,6 @@ enum { VIS_MODE_REPLACE, }; -/* operators */ -static void op_change(OperatorContext *c); -static void op_yank(OperatorContext *c); -static void op_paste(OperatorContext *c); -static void op_delete(OperatorContext *c); - -enum { - OP_DELETE, - OP_CHANGE, - OP_YANK, - OP_PASTE, -}; - -static Operator ops[] = { - [OP_DELETE] = { op_delete, false }, - [OP_CHANGE] = { op_change, false }, - [OP_YANK] = { op_yank, false }, - [OP_PASTE] = { op_paste, true }, -}; - /* command recognized at the ':'-prompt, matched using a greedy top to bottom, * regex search. make sure the longer commands are listed before the shorter ones * e.g. 'sp' before 's' and 'wq' before 'w'. @@ -333,7 +326,7 @@ static KeyBinding vis_marks_set[] = { }; static KeyBinding vis_normal[] = { - { { CONTROL('w'), NONE('c') }, split, { .s = NULL } }, + { { CONTROL('w'), NONE('s') }, split, { NULL } }, { { CONTROL('w'), NONE('j') }, call, { .f = editor_window_next } }, { { CONTROL('w'), NONE('k') }, call, { .f = editor_window_prev } }, { { CONTROL('F') }, cursor, { .m = window_page_up } }, @@ -456,6 +449,45 @@ static void vis_replace_input(const char *str, size_t len) { editor_replace_key(vis, str, len); } +/* + * the tree of modes currently looks like this. the double line between OPERATOR-OPTION + * and OPERATOR is only in effect once an operator is detected. that is when entering the + * OPERATOR mode its parent is set to OPERATOR-OPTION which makes {INNER-,}TEXTOBJ + * reachable. once the operator is processed (i.e. the OPERATOR mode is left) its parent + * mode is reset back to MOVE. + * + * + * BASIC + * (arrow keys etc.) + * / | + * /-------------------/ | + * READLINE MARK + * / \ (` [a-z]) + * / \ | + * / \ | + * INSERT-REGISTER PROMPT MARK-LINE + * (Ctrl+R [a-z]) (history etc) (' [a-z]) + * | | + * | | + * INSERT MOVE + * | (h,j,k,l ...) + * | | \-----------------\ + * | | | + * REPLACE OPERATOR ======\\ INNER-TEXTOBJ + * (d,c,y,p ..) || (i [wsp[]()b<>{}B"'`] ) + * | || | + * | || | + * REGISTER || TEXTOBJ + * (" [a-z]) || (a [wsp[]()b<>{}B"'`] ) + * /-----------/ | \\ + * / | \\ + * VISUAL MARK-SET \\ OPERATOR-OPTION + * (m [a-z]) \\ (v,V) + * | \\ // + * | \\======// + * NORMAL + */ + static Mode vis_modes[] = { [VIS_MODE_BASIC] = { .name = "BASIC", diff --git a/editor.h b/editor.h index 1aa98ff..109ea32 100644 --- a/editor.h +++ b/editor.h @@ -108,6 +108,9 @@ void editor_free(Editor*); void editor_resize(Editor*, int width, int height); void editor_draw(Editor*); void editor_update(Editor*); + +/* these function operate on the currently focused window but make sure + * that all windows which show the affected region are redrawn too. */ void editor_insert_key(Editor*, const char *c, size_t len); void editor_replace_key(Editor*, const char *c, size_t len); void editor_backspace_key(Editor*); @@ -115,18 +118,33 @@ void editor_delete_key(Editor*); void editor_insert(Editor*, size_t pos, const char *data, size_t len); void editor_delete(Editor*, size_t pos, size_t len); -// TODO comment +/* load a set of syntax highlighting definitions which will be associated + * to the underlying window based on the file type loaded. + * + * both *syntaxes and *colors must point to a NULL terminated array. + * it the i-th syntax definition does not specifiy a fore ground color + * then the i-th entry of the color array will be used instead + */ bool editor_syntax_load(Editor*, Syntax *syntaxes, Color *colors); void editor_syntax_unload(Editor*); +/* creates a new window, and loads the given file. if filename is NULL + * an unamed / empty buffer is created */ bool editor_window_new(Editor*, const char *filename); void editor_window_close(Editor *vis); +/* if filename is non NULL it is equivalent to window_new call above. + * if however filename is NULL a new window is created and linked to the + * same underlying text as the currently selected one. changes will + * thus be visible in both windows. */ void editor_window_split(Editor*, const char *filename); void editor_window_vsplit(Editor*, const char *filename); +/* focus the next / previous window */ void editor_window_next(Editor*); void editor_window_prev(Editor*); - +/* return the content of the command prompt in a malloc(3)-ed string + * which the call site has to free. */ char *editor_prompt_get(Editor *vis); +/* replace the current command line content with the one given */ void editor_prompt_set(Editor *vis, const char *line); void editor_prompt_show(Editor *vis, const char *title); void editor_prompt_hide(Editor *vis); @@ -134,9 +152,13 @@ void editor_prompt_hide(Editor *vis); void editor_statusbar_set(Editor*, editor_statusbar_t); +// TODO cleanup this mess only 1 function should suffice? move perform +// lazy initialization on first call /* library initialization code, should be run at startup */ void editor_init(void); short editor_color_reserve(short fg, short bg); +/* look up a curses color pair for the given combination of fore and + * background color */ short editor_color_get(short fg, short bg); #endif diff --git a/register.c b/register.c index bcce594..db43d71 100644 --- a/register.c +++ b/register.c @@ -6,7 +6,7 @@ #define REG_SIZE 1024 -bool register_alloc(Register *reg, size_t size) { +static bool register_alloc(Register *reg, size_t size) { if (size < REG_SIZE) size = REG_SIZE; if (reg->size < size) { @@ -30,14 +30,6 @@ void register_free(Register *reg) { reg->size = 0; } -bool register_store(Register *reg, const char *data, size_t len) { - if (!register_alloc(reg, len)) - return false; - memcpy(reg->data, data, len); - reg->len = len; - return true; -} - bool register_put(Register *reg, Text *txt, Filerange *range) { size_t len = range->end - range->start; if (!register_alloc(reg, len)) diff --git a/register.h b/register.h index 51fcd25..506b86a 100644 --- a/register.h +++ b/register.h @@ -6,15 +6,13 @@ #include "text.h" typedef struct { - char *data; - size_t len; - size_t size; - bool linewise; + char *data; /* NULL if empty */ + size_t len; /* current length of data */ + size_t size; /* maximal capacity of the register */ + bool linewise; /* place register content on a new line when inserting? */ } Register; -bool register_alloc(Register *reg, size_t size); void register_free(Register *reg); -bool register_store(Register *reg, const char *data, size_t len); bool register_put(Register *reg, Text *txt, Filerange *range); bool register_append(Register *reg, Text *txt, Filerange *range); diff --git a/syntax.h b/syntax.h index a945389..accae9f 100644 --- a/syntax.h +++ b/syntax.h @@ -3,7 +3,7 @@ #include -#define SYNTAX_REGEX_RULES 10 +#define SYNTAX_RULES 10 /* maximal number of syntax rules per file type */ typedef struct { short fg, bg; /* fore and background color */ @@ -18,11 +18,11 @@ typedef struct { } SyntaxRule; typedef struct Syntax Syntax; -struct Syntax { /* a syntax definition */ - char *name; /* syntax name */ - char *file; /* apply to files matching this regex */ - regex_t file_regex; /* compiled file name regex */ - SyntaxRule rules[SYNTAX_REGEX_RULES]; /* all rules for this file type */ +struct Syntax { /* a syntax definition */ + char *name; /* syntax name */ + char *file; /* apply to files matching this regex */ + regex_t file_regex; /* compiled file name regex */ + SyntaxRule rules[SYNTAX_RULES]; /* all rules for this file type */ }; #endif diff --git a/text-motions.c b/text-motions.c index b2785d8..c453cab 100644 --- a/text-motions.c +++ b/text-motions.c @@ -89,7 +89,7 @@ size_t text_line_finish(Text *txt, size_t pos) { Iterator it = text_iterator_get(txt, text_line_end(txt, pos)); do text_iterator_byte_prev(&it, NULL); while (text_iterator_byte_get(&it, &c) && c != '\n' && c != '\r' && isspace(c)); - if (!isutf8(c)) + if (!ISUTF8(c)) text_iterator_char_prev(&it, NULL); return it.pos; } diff --git a/text-motions.h b/text-motions.h index 81b5b61..84f6ecc 100644 --- a/text-motions.h +++ b/text-motions.h @@ -1,15 +1,22 @@ #ifndef TEXT_MOTIONS_H #define TEXT_MOTIONS_H +/* these function all take a position in bytes from the start of the file, + * perform a certain movement and return the new postion. if the movement + * is not possible the original position is returned unchanged. */ + #include #include "text.h" size_t text_begin(Text*, size_t pos); size_t text_end(Text*, size_t pos); +/* move to start of next / previous UTF-8 character */ size_t text_char_next(Text*, size_t pos); size_t text_char_prev(Text*, size_t pos); +/* find the given substring either in forward or backward direction. + * does not wrap around at file start / end. */ size_t text_find_char_next(Text*, size_t pos, const char *s, size_t len); size_t text_find_char_prev(Text*, size_t pos, const char *s, size_t len); @@ -25,9 +32,9 @@ size_t text_line_finish(Text*, size_t pos); size_t text_line_end(Text*, size_t pos); size_t text_line_next(Text*, size_t pos); /* - * A word consists of a sequence of non-blank characters, separated with white space. - * TODO?: An empty line is also considered to be a word. This is equivalant to a WORD - * in vim terminology. + * A word consists of a sequence of non-blank characters, separated with white + * space. TODO?: An empty line is also considered to be a word. + * This is equivalant to a WORD in vim terminology. */ size_t text_word_end_next(Text*, size_t pos); size_t text_word_end_prev(Text*, size_t pos); @@ -35,8 +42,8 @@ size_t text_word_start_next(Text*, size_t pos); size_t text_word_start_prev(Text*, size_t pos); /* * These are variants of the above with the additional possibility to implement - * a custom word detection logic. Can be used to implment the equivalent of a what - * vim reconizes as a word (lowercase). + * a custom word detection logic. Can be used to implment the equivalent of a + * what vim reconizes as a word (lowercase). */ size_t text_word_boundry_end_next(Text*, size_t pos, int (*isboundry)(int)); size_t text_word_boundry_end_prev(Text*, size_t pos, int (*isboundry)(int)); @@ -65,6 +72,8 @@ size_t text_section_prev(Text*, size_t pos); /* search coresponding '(', ')', '{', '}', '[', ']', '>', '<', '"', ''' */ size_t text_bracket_match(Text*, size_t pos); +/* search the given regex pattern in either forward or backward direction, + * starting from pos. does wrap around if no match was found. */ size_t text_search_forward(Text *txt, size_t pos, Regex *regex); size_t text_search_backward(Text *txt, size_t pos, Regex *regex); diff --git a/text-objects.h b/text-objects.h index 5b3895d..a1e30f9 100644 --- a/text-objects.h +++ b/text-objects.h @@ -1,18 +1,25 @@ #ifndef TEXT_OBJECTS_H #define TEXT_OBJECTS_H +/* these functions all take a file position. if this position is part of the + * respective text-object, a corresponding range is returned. if there is no + * such text-object at the given location, an empty range is returned. + */ + #include #include "text.h" -/* word which happens to be at pos, includes trailing white spaces. if at pos happens to - * be a whitespace include all neighbouring leading whitespaces and the following word. */ +/* word which happens to be at pos, includes trailing white spaces. if at pos + * happens to be a whitespace include all neighbouring leading whitespaces + * and the following word. */ Filerange text_object_word(Text*, size_t pos); Filerange text_object_word_boundry(Text*, size_t pos, int (*isboundry)(int)); Filerange text_object_line(Text*, size_t pos); Filerange text_object_sentence(Text*, size_t pos); Filerange text_object_paragraph(Text*, size_t pos); -/* the delimiters themself are not included in the range */ +/* these are inner text objects i.e. the delimiters themself are not + * included in the range */ Filerange text_object_square_bracket(Text*, size_t pos); Filerange text_object_curly_bracket(Text*, size_t pos); Filerange text_object_angle_bracket(Text*, size_t pos); diff --git a/text.c b/text.c index dac2b41..9879f98 100644 --- a/text.c +++ b/text.c @@ -871,7 +871,7 @@ bool text_iterator_byte_prev(Iterator *it, char *b) { bool text_iterator_char_next(Iterator *it, char *c) { while (text_iterator_byte_next(it, NULL)) { - if (isutf8(*it->text)) { + if (ISUTF8(*it->text)) { if (c) *c = *it->text; return true; @@ -882,7 +882,7 @@ bool text_iterator_char_next(Iterator *it, char *c) { bool text_iterator_char_prev(Iterator *it, char *c) { while (text_iterator_byte_prev(it, NULL)) { - if (isutf8(*it->text)) { + if (ISUTF8(*it->text)) { if (c) *c = *it->text; return true; diff --git a/text.h b/text.h index 1b4bc2b..e133af0 100644 --- a/text.h +++ b/text.h @@ -7,7 +7,7 @@ typedef size_t Filepos; typedef struct { - size_t start, end; /* range in bytes from start of file */ + size_t start, end; /* range in bytes from start of the file */ } Filerange; typedef struct Text Text; diff --git a/util.h b/util.h index b2446a7..f2e3d65 100644 --- a/util.h +++ b/util.h @@ -6,7 +6,7 @@ #define MAX(a, b) ((a) < (b) ? (b) : (a)) /* is c the start of a utf8 sequence? */ -#define isutf8(c) (((c)&0xC0)!=0x80) -#define ISASCII(ch) ((unsigned char)ch < 0x80) +#define ISUTF8(c) (((c)&0xC0)!=0x80) +#define ISASCII(ch) ((unsigned char)ch < 0x80) #endif diff --git a/vis.c b/vis.c index 3afa2fa..3aefc9a 100644 --- a/vis.c +++ b/vis.c @@ -28,13 +28,13 @@ typedef union { bool b; size_t i; const char *s; - size_t (*m)(Win*); - void (*f)(Editor*); + size_t (*m)(Win*); /* cursor movement based on window content */ + void (*f)(Editor*); /* generic editor commands */ } Arg; typedef struct { - char str[6]; - int code; + char str[6]; /* UTF8 character or terminal escape code */ + int code; /* curses KEY_* constant */ } Key; typedef struct { @@ -45,29 +45,30 @@ typedef struct { typedef struct Mode Mode; struct Mode { - Mode *parent; - KeyBinding *bindings; - const char *name; - bool common_prefix; - void (*enter)(Mode *old); - void (*leave)(Mode *new); - bool (*unknown)(Key *key0, Key *key1); /* unknown key for this mode, return value determines whether parent modes will be checked */ - void (*input)(const char *str, size_t len); /* unknown key for this an all parent modes */ - void (*idle)(void); + Mode *parent; /* if no match is found in this mode, search will continue there */ + KeyBinding *bindings; /* NULL terminated array of keybindings for this mode */ + const char *name; /* descriptive, user facing name of the mode */ + bool common_prefix; /* whether the first key in this mode is always the same */ + void (*enter)(Mode *old); /* called right before the mode becomes active */ + void (*leave)(Mode *new); /* called right before the mode becomes inactive */ + bool (*unknown)(Key*, Key*); /* called whenever a key is not found in this mode, + the return value determines whether parent modes will be searched */ + void (*input)(const char*, size_t); /* called whenever a key is not found in this mode and all its parent modes */ + void (*idle)(void); /* called whenever a certain idle time i.e. without any user input elapsed */ }; typedef struct { - char *name; - Mode *mode; - editor_statusbar_t statusbar; + char *name; /* is used to match against argv[0] to enable this config */ + Mode *mode; /* default mode in which the editor should start in */ + editor_statusbar_t statusbar; /* routine which is called whenever the cursor is moved within a window */ } Config; typedef struct { - int count; - Register *reg; - Filerange range; - size_t pos; - bool linewise; + int count; /* how many times should the command be executed? */ + Register *reg; /* always non-NULL, set to a default register */ + Filerange range; /* which part of the file should be affected by the operator */ + size_t pos; /* at which byte from the start of the file should the operation start? */ + bool linewise; /* should the changes always affect whole lines? */ } OperatorContext; typedef struct { @@ -76,9 +77,9 @@ typedef struct { } Operator; typedef struct { - size_t (*cmd)(const Arg*); - size_t (*win)(Win*); - size_t (*txt)(Text*, size_t pos); + size_t (*cmd)(const Arg*); /* a custom movement based on user input from vis.c */ + size_t (*win)(Win*); /* a movement based on current window content from window.h */ + size_t (*txt)(Text*, size_t pos); /* a movement form text-motions.h */ enum { LINEWISE = 1 << 0, CHARWISE = 1 << 1, @@ -89,14 +90,14 @@ typedef struct { } Movement; typedef struct { - Filerange (*range)(Text*, size_t pos); + Filerange (*range)(Text*, size_t pos); /* a text object from text-objects.h */ enum { INNER, OUTER, } type; } TextObject; -typedef struct { +typedef struct { /** collects all information until an operator is executed */ int count; bool linewise; Operator *op; @@ -108,31 +109,43 @@ typedef struct { Arg arg; } Action; -typedef struct { - const char *name; - bool (*cmd)(const char *argv[]); - regex_t regex; +typedef struct { /* command definitions for the ':'-prompt */ + const char *name; /* regular expression pattern to match command */ + bool (*cmd)(const char *argv[]); /* command logic called with a NULL terminated array + * of arguments. argv[0] will be the command name, + * as matched by the regex. */ + regex_t regex; /* compiled form of the pattern in 'name' */ } Command; -/* global variables */ -static Editor *vis; /* global editor instance, keeps track of all windows etc. */ +/** global variables */ +static Editor *vis; /* global editor instance, keeps track of all windows etc. */ static Mode *mode; /* currently active mode, used to search for keybindings */ static Mode *mode_prev; /* mode which was active previously */ static Action action; /* current action which is in progress */ static Action action_prev; /* last operator action used by the repeat '.' key */ -/* movements which can be used besides the one in text-motions.h and window.h */ -static size_t search_forward(const Arg *arg); -static size_t search_backward(const Arg *arg); -static size_t mark_goto(const Arg *arg); -static size_t mark_line_goto(const Arg *arg); -static size_t to(const Arg *arg); -static size_t till(const Arg *arg); -static size_t to_left(const Arg *arg); -static size_t till_left(const Arg *arg); -static size_t line(const Arg *arg); -static size_t column(const Arg *arg); +/** operators */ +static void op_change(OperatorContext *c); +static void op_yank(OperatorContext *c); +static void op_paste(OperatorContext *c); +static void op_delete(OperatorContext *c); + +/* these can be passed as int argument to operator(&(const Arg){ .i = OP_*}) */ +enum { + OP_DELETE, + OP_CHANGE, + OP_YANK, + OP_PASTE, +}; +static Operator ops[] = { + [OP_DELETE] = { op_delete, false }, + [OP_CHANGE] = { op_change, false }, + [OP_YANK] = { op_yank, false }, + [OP_PASTE] = { op_paste, true }, +}; + +/* these can be passed as int argument to movement(&(const Arg){ .i = MOVE_* }) */ enum { MOVE_LINE_UP, MOVE_LINE_DOWN, @@ -165,6 +178,27 @@ enum { MOVE_SEARCH_BACKWARD, }; +/** movements which can be used besides the one in text-motions.h and window.h */ +/* search again for the last used search pattern */ +static size_t search_forward(const Arg *arg); +static size_t search_backward(const Arg *arg); +/* goto action.mark */ +static size_t mark_goto(const Arg *arg); +/* goto first non-blank char on line pointed by action.mark */ +static size_t mark_line_goto(const Arg *arg); +/* goto to next occurence of action.key to the right */ +static size_t to(const Arg *arg); +/* goto to position before next occurence of action.key to the right */ +static size_t till(const Arg *arg); +/* goto to next occurence of action.key to the left */ +static size_t to_left(const Arg *arg); +/* goto to position after next occurence of action.key to the left */ +static size_t till_left(const Arg *arg); +/* goto line number action.count, or if zero to end of file */ +static size_t line(const Arg *arg); +/* goto to byte action.count on current line */ +static size_t column(const Arg *arg); + static Movement moves[] = { [MOVE_LINE_UP] = { .win = window_line_up }, [MOVE_LINE_DOWN] = { .win = window_line_down }, @@ -197,7 +231,7 @@ static Movement moves[] = { [MOVE_SEARCH_BACKWARD] = { .cmd = search_backward, .type = LINEWISE }, }; -/* available text objects */ +/* these can be passed as int argument to textobj(&(const Arg){ .i = TEXT_OBJ_* }) */ enum { TEXT_OBJ_WORD, TEXT_OBJ_LINE_UP, @@ -248,46 +282,84 @@ static TextObject *moves_linewise[] = { [MOVE_LINE_DOWN] = &textobjs[TEXT_OBJ_LINE_DOWN], }; -/* functions to be called from keybindings */ +/** functions to be called from keybindings */ +/* switch to mode indicated by arg->i */ static void switchmode(const Arg *arg); +/* set mark indicated by arg->i to current cursor position */ static void mark_set(const Arg *arg); +/* insert arg->s at the current cursor position */ static void insert(const Arg *arg); +/* insert a tab or the needed amount of spaces at the current cursor position */ static void insert_tab(const Arg *arg); +/* inserts a newline (either \n or \n\r depending on file type) */ static void insert_newline(const Arg *arg); +/* split current window horizontally (default) or vertically (if arg->b is set) */ static void split(const Arg *arg); -static void quit(const Arg *arg); +/* perform last action i.e. action_prev again */ static void repeat(const Arg *arg); +/* adjust action.count by arg->i */ static void count(const Arg *arg); +/* force operator to linewise (if arg->b is set) */ static void linewise(const Arg *arg); +/* make the current action use the operator indicated by arg->i */ static void operator(const Arg *arg); +/* blocks to read a key and performs movement indicated by arg->i which + * should be one of MOVE_{RIGHT,LEFT}_{TO,TILL} */ static void movement_key(const Arg *arg); +/* perform the movement as indicated by arg->i */ static void movement(const Arg *arg); +/* let the current operator affect the range indicated by the text object arg->i */ static void textobj(const Arg *arg); +/* use register indicated by arg->i for the current operator */ static void reg(const Arg *arg); +/* perform a movement to mark arg->i */ static void mark(const Arg *arg); +/* perform a movement to the first non-blank on the line pointed by mark arg->i */ static void mark_line(const Arg *arg); +/* {un,re}do last action, redraw window */ static void undo(const Arg *arg); static void redo(const Arg *arg); +/* either part of multiplier or a movement to begin of line */ static void zero(const Arg *arg); +/* delete from the current cursor position to the start of the previous word */ static void delete_word(const Arg *arg); +/* insert register content indicated by arg->i at current cursor position */ static void insert_register(const Arg *arg); +/* show a user prompt to get input with title arg->s */ static void prompt(const Arg *arg); +/* evaluate user input at prompt, perform search or execute a command */ static void prompt_enter(const Arg *arg); +/* cycle through past user inputs */ static void prompt_up(const Arg *arg); static void prompt_down(const Arg *arg); +/* blocks to read 3 consecutive digits and inserts the corresponding byte value */ static void insert_verbatim(const Arg *arg); +/* cursor movement based on the current window content as indicated by arg->m + * which should point to a function from window.h */ static void cursor(const Arg *arg); +/* call editor function as indicated by arg->f */ static void call(const Arg *arg); +static void quit(const Arg *arg); -/* commands to enter at the ':'-prompt */ +/** commands to enter at the ':'-prompt */ +/* goto line indicated by argv[0] */ static bool cmd_gotoline(const char *argv[]); +/* for each argument create a new window and open the corresponding file */ static bool cmd_open(const char *argv[]); +/* close the current window, if argv[0] contains a '!' discard modifications */ static bool cmd_quit(const char *argv[]); +/* for each argument try to insert the file content at current cursor postion */ static bool cmd_read(const char *argv[]); static bool cmd_substitute(const char *argv[]); +/* if no argument are given, split the current window horizontally, + * otherwise open the file */ static bool cmd_split(const char *argv[]); +/* if no argument are given, split the current window vertically, + * otherwise open the file */ static bool cmd_vsplit(const char *argv[]); +/* save the file displayed in the current window and close it */ static bool cmd_wq(const char *argv[]); +/* save the file displayed in the current window to the name given */ static bool cmd_write(const char *argv[]); static void action_reset(Action *a); @@ -299,7 +371,7 @@ static Key getkey(void); static void action_do(Action *a); static bool exec_command(char *cmdline); -/* operator implementations of type: void (*op)(OperatorContext*) */ +/** operator implementations of type: void (*op)(OperatorContext*) */ static void op_delete(OperatorContext *c) { size_t len = c->range.end - c->range.start; @@ -327,7 +399,7 @@ static void op_paste(OperatorContext *c) { window_cursor_to(vis->win->win, pos + c->reg->len); } -/* movement implementations of type: size_t (*move)(const Arg*) */ +/** movement implementations of type: size_t (*move)(const Arg*) */ static size_t search_forward(const Arg *arg) { size_t pos = window_cursor_get(vis->win->win); @@ -388,7 +460,7 @@ static size_t column(const Arg *arg) { return it.pos; } -/* key bindings functions of type: void (*func)(const Arg*) */ +/** key bindings functions of type: void (*func)(const Arg*) */ static void repeat(const Arg *arg) { action = action_prev; @@ -542,7 +614,10 @@ static void quit(const Arg *arg) { } static void split(const Arg *arg) { - editor_window_split(vis, arg->s); + if (arg->b) + editor_window_vsplit(vis, arg->s); + else + editor_window_split(vis, arg->s); } static void cursor(const Arg *arg) { @@ -570,7 +645,7 @@ static void switchmode(const Arg *arg) { switchmode_to(&vis_modes[arg->i]); } -/* action processing, executed the operator / movement / text object */ +/** action processing: execut the operator / movement / text object */ static void action_do(Action *a) { Text *txt = vis->win->text; @@ -684,9 +759,7 @@ static void switchmode_to(Mode *new_mode) { } - - -/* ':'-command implementations */ +/** ':'-command implementations */ static bool cmd_gotoline(const char *argv[]) { action.count = strtoul(argv[0], NULL, 10); diff --git a/window.c b/window.c index a2209de..bdb5ce3 100644 --- a/window.c +++ b/window.c @@ -68,12 +68,10 @@ struct Win { /* window showing part of a file */ Line *bottomline; /* bottom of screen, might be unused if lastline < bottomline */ Filerange sel; /* selected text range in bytes from start of file */ Cursor cursor; /* current window cursor position */ - void (*cursor_moved)(Win*, void *); - void *cursor_moved_data; - - Line *line; // TODO: rename to something more descriptive, these are the current drawing pos - int col; - + void (*cursor_moved)(Win*, void *); /* registered callback, fires whenever the cursor moved */ + void *cursor_moved_data; /* user supplied data, passed as second argument to the above callback */ + Line *line; /* used while drawing window content, line where next char will be drawn */ + int col; /* used while drawing window content, column where next char will be drawn */ Syntax *syntax; /* syntax highlighting definitions for this window or NULL */ int tabwidth; /* how many spaces should be used to display a tab character */ }; @@ -92,6 +90,7 @@ void window_selection_clear(Win *win) { window_cursor_update(win); } +/* reset internal window data structures (cell matrix, line offsets etc.) */ static void window_clear(Win *win) { size_t line_size = sizeof(Line) + win->width*sizeof(Cell); win->topline = win->lines; @@ -129,6 +128,7 @@ Filerange window_selection_get(Win *win) { Filerange window_viewport_get(Win *win) { return (Filerange){ .start = win->start, .end = win->end }; } + /* try to add another character to the window, return whether there was space left */ static bool window_addch(Win *win, Char *c) { if (!win->line) @@ -249,7 +249,7 @@ void window_cursor_getxy(Win *win, size_t *lineno, size_t *col) { } /* place the cursor according to the screen coordinates in win->{row,col} and - * update the statusbar. if a selection is active, redraw the window to reflect + * fire user callback. if a selection is active, redraw the window to reflect * its changes. */ static size_t window_cursor_update(Win *win) { Cursor *cursor = &win->cursor; @@ -341,7 +341,7 @@ void window_draw(Win *win) { /* current selection */ Filerange sel = window_selection_get(win); /* matched tokens for each syntax rule */ - regmatch_t match[SYNTAX_REGEX_RULES][1]; + regmatch_t match[SYNTAX_RULES][1]; if (win->syntax) { for (int i = 0; i < LENGTH(win->syntax->rules); i++) { SyntaxRule *rule = &win->syntax->rules[i]; @@ -394,7 +394,7 @@ void window_draw(Win *win) { /* ok, we encountered an invalid multibyte sequence, * replace it with the Unicode Replacement Character * (FFFD) and skip until the start of the next utf8 char */ - for (len = 1; rem > len && !isutf8(cur[len]); len++); + for (len = 1; rem > len && !ISUTF8(cur[len]); len++); c = (Char){ .c = "\xEF\xBF\xBD", .wchar = 0xFFFD, .len = len }; } else if (len == (size_t)-2) { /* not enough bytes available to convert to a diff --git a/window.h b/window.h index 2f5d5ee..fc9cd43 100644 --- a/window.h +++ b/window.h @@ -23,7 +23,8 @@ void window_draw(Win*); /* flush all changes made to the ncurses windows to the screen */ void window_update(Win*); -/* cursor movements, also updates selection if one is active, returns new cursor postion */ +/* cursor movements which also update selection if one is active. + * they return new cursor postion */ size_t window_page_down(Win*); size_t window_page_up(Win*); size_t window_char_next(Win*); @@ -31,17 +32,32 @@ size_t window_char_prev(Win*); size_t window_line_down(Win*); size_t window_line_up(Win*); +/* get cursor position in bytes from start of the file */ size_t window_cursor_get(Win*); +/* get cursor position in terms of screen coordinates */ void window_cursor_getxy(Win*, size_t *lineno, size_t *col); +/* moves window viewport in direction until pos is visible. should only be + * used for short distances between current cursor position and destination */ void window_scroll_to(Win*, size_t pos); +/* move cursor to a given position. changes the viewport to make sure that + * position is visible. if the position is in the middle of a line, try to + * adjust the viewport in such a way that the whole line is displayed */ void window_cursor_to(Win*, size_t pos); +/* start selected area at current cursor position. further cursor movements will + * affect the selected region. */ void window_selection_start(Win*); void window_selection_end(Win*); +/* returns the currently selected text region, is either empty or well defined, + * i.e. sel.start <= sel.end */ Filerange window_selection_get(Win*); +/* clear selection and redraw window */ void window_selection_clear(Win*); +/* get the currently displayed area in bytes from the start of the file */ Filerange window_viewport_get(Win*); +/* associate a set of syntax highlighting rules to this window. */ void window_syntax_set(Win*, Syntax*); Syntax *window_syntax_get(Win*); +/* register a user defined function which will be called whenever the cursor has moved */ void window_cursor_watch(Win *win, void (*cursor_moved)(Win*, void*), void *data); #endif -- cgit v1.2.3