diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2016-04-02 13:20:20 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2016-04-03 21:38:05 +0200 |
| commit | ab2d1ddd0adbeec8061b6560ebedf5a1f7bb5867 (patch) | |
| tree | 0531ad62420a9407bcf21849e592d8104af54484 | |
| parent | 378e3b7c01d07659b85bb77786461e91a1c6d8ab (diff) | |
| download | vis-ab2d1ddd0adbeec8061b6560ebedf5a1f7bb5867.tar.gz vis-ab2d1ddd0adbeec8061b6560ebedf5a1f7bb5867.tar.xz | |
sam: unify vi(m) and sam command line
The following vi commands have been dropped:
- saveas
- xit
- !
The following commands are only recognized in their short form:
- e (edit)
- q (quit)
- s (substitute)
- w (write)
- r (read)
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | config.def.h | 2 | ||||
| -rw-r--r-- | main.c | 8 | ||||
| -rw-r--r-- | sam.c | 238 | ||||
| -rw-r--r-- | vis-cmds.c | 820 | ||||
| -rw-r--r-- | vis-prompt.c | 7 | ||||
| -rw-r--r-- | vis.c | 188 |
7 files changed, 403 insertions, 862 deletions
@@ -2,7 +2,7 @@ SRC = array.c buffer.c libutf.c main.c map.c register.c ring-buffer.c \ sam.c text.c text-motions.c text-objects.c text-regex.c text-util.c \ - ui-curses.c view.c vis.c vis-cmds.c vis-lua.c vis-modes.c vis-motions.c \ + ui-curses.c view.c vis.c vis-lua.c vis-modes.c vis-motions.c \ vis-operators.c vis-prompt.c vis-text-objects.c # conditionally initialized, this is needed for standalone build diff --git a/config.def.h b/config.def.h index 3a09da2..c29ce64 100644 --- a/config.def.h +++ b/config.def.h @@ -268,7 +268,7 @@ static const KeyBinding bindings_visual[] = { { "<C-c>", ALIAS("<Escape>") }, { "v", ALIAS("<Escape>") }, { "V", ACTION(MODE_VISUAL_LINE) }, - { ":", ACTION(PROMPT_SHOW_VISUAL) }, + { ":", ACTION(PROMPT_SHOW) }, { "<C-b>", ALIAS("<PageUp>") }, { "<C-f>", ALIAS("<PageDown>") }, { "<C-u>", ACTION(CURSORS_PREV) }, @@ -228,7 +228,6 @@ enum { VIS_ACTION_JOIN_LINE_BELOW, VIS_ACTION_JOIN_LINES, VIS_ACTION_PROMPT_SHOW, - VIS_ACTION_PROMPT_SHOW_VISUAL, VIS_ACTION_REPEAT, VIS_ACTION_SELECTION_FLIP, VIS_ACTION_SELECTION_RESTORE, @@ -748,7 +747,7 @@ static const KeyAction vis_action[] = { [VIS_ACTION_OPERATOR_FILTER_FMT] = { "vis-operator-filter-format", "Formating operator, filter range through fmt(1)", - operator_filter, { .s = "'<,'>!fmt" } + operator_filter, { .s = "|fmt" } }, [VIS_ACTION_COUNT] = { "vis-count", @@ -825,11 +824,6 @@ static const KeyAction vis_action[] = { "Show editor command line prompt", prompt_show, { .s = ":" } }, - [VIS_ACTION_PROMPT_SHOW_VISUAL] = { - "prompt-show-visual", - "Show editor command line prompt in visual mode", - prompt_show, { .s = ":'<,'>" } - }, [VIS_ACTION_REPEAT] = { "editor-repeat", "Repeat latest editor command", @@ -8,6 +8,7 @@ #include <string.h> #include <stdio.h> #include <ctype.h> +#include <errno.h> #include "sam.h" #include "vis-core.h" #include "buffer.h" @@ -65,51 +66,93 @@ struct CommandDef { CMD_ARGV = 1 << 10, /* whether shell like argument splitted is desired */ } flags; const char *defcmd; /* name of a default target command */ - bool (*func)(Vis*, Win*, Command*, const char *argv[], Filerange*); /* command implementation */ + bool (*func)(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); /* command implementation */ }; -static bool cmd_insert(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_append(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_change(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_delete(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_guard(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_extract(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_select(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_print(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_files(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_pipein(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_pipeout(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_filter(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_substitute(Vis*, Win*, Command*, const char *argv[], Filerange*); -static bool cmd_write(Vis*, Win*, Command*,const char *argv[], Filerange*); -static bool cmd_read(Vis*, Win*, Command*, const char *argv[], Filerange*); - +/* sam commands */ +static bool cmd_insert(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_append(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_change(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_delete(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_guard(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_extract(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_select(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_print(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_files(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_pipein(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_pipeout(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_filter(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_substitute(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_write(Vis*, Win*, Command*,const char *argv[], Cursor*, Filerange*); +static bool cmd_read(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_edit(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_quit(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +/* vi(m) commands */ +static bool cmd_set(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_open(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_bdelete(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_qall(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_split(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_vsplit(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_new(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_vnew(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_wq(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_earlier_later(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_help(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_map(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_unmap(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); +static bool cmd_langmap(Vis*, Win*, Command*, const char *argv[], Cursor*, Filerange*); + +/* command recognized at the ':'-prompt. commands are found using a unique + * prefix match. that is if a command should be available under an abbreviation + * which is a prefix for another command it has to be added as an alias. the + * long human readable name should always come first */ static const CommandDef cmds[] = { - /* name(s), flags, default command, command */ - { { "a" }, CMD_TEXT, NULL, cmd_append }, - { { "c" }, CMD_TEXT, NULL, cmd_change }, - { { "d" }, 0, NULL, cmd_delete }, - { { "g" }, CMD_CMD|CMD_REGEX, "p", cmd_guard }, - { { "i" }, CMD_TEXT, NULL, cmd_insert }, - { { "p" }, 0, NULL, cmd_print }, - { { "s" }, CMD_SHELL, NULL, cmd_substitute }, - { { "v" }, CMD_CMD|CMD_REGEX, "p", cmd_guard }, - { { "x" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, "p", cmd_extract }, - { { "y" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, "p", cmd_extract }, - { { "X" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, NULL, cmd_files }, - { { "Y" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, NULL, cmd_files }, - { { ">" }, CMD_SHELL, NULL, cmd_pipeout }, - { { "<" }, CMD_SHELL, NULL, cmd_pipein }, - { { "|" }, CMD_SHELL, NULL, cmd_filter }, - { { "w" }, CMD_ARGV|CMD_FORCE, NULL, cmd_write }, - { { "r" }, CMD_FILE, NULL, cmd_read }, - { { "{" }, 0, NULL, NULL }, - { { "}" }, 0, NULL, NULL }, - { { NULL }, 0, NULL, NULL }, + /* name(s), flags, default command, implementation */ + { { "a" }, CMD_TEXT, NULL, cmd_append }, + { { "c" }, CMD_TEXT, NULL, cmd_change }, + { { "d" }, 0, NULL, cmd_delete }, + { { "g" }, CMD_CMD|CMD_REGEX, "p", cmd_guard }, + { { "i" }, CMD_TEXT, NULL, cmd_insert }, + { { "p" }, 0, NULL, cmd_print }, + { { "s" }, CMD_SHELL, NULL, cmd_substitute }, + { { "v" }, CMD_CMD|CMD_REGEX, "p", cmd_guard }, + { { "x" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, "p", cmd_extract }, + { { "y" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, "p", cmd_extract }, + { { "X" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, NULL, cmd_files }, + { { "Y" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, NULL, cmd_files }, + { { ">" }, CMD_SHELL, NULL, cmd_pipeout }, + { { "<" }, CMD_SHELL, NULL, cmd_pipein }, + { { "|" }, CMD_SHELL, NULL, cmd_filter }, + { { "w" }, CMD_ARGV|CMD_FORCE, NULL, cmd_write }, + { { "r" }, CMD_FILE, NULL, cmd_read }, + { { "{" }, 0, NULL, NULL }, + { { "}" }, 0, NULL, NULL }, + { { "e" }, CMD_FILE|CMD_FORCE, NULL, cmd_edit }, + { { "q" }, CMD_FORCE, NULL, cmd_quit }, + /* vi(m) related commands */ + { { "bdelete" }, CMD_FORCE, NULL, cmd_bdelete }, + { { "help" }, 0, NULL, cmd_help }, + { { "map" }, CMD_ARGV|CMD_FORCE, NULL, cmd_map }, + { { "map-window" }, CMD_ARGV|CMD_FORCE, NULL, cmd_map }, + { { "unmap" }, CMD_ARGV, NULL, cmd_unmap }, + { { "unmap-window" }, CMD_ARGV, NULL, cmd_unmap }, + { { "langmap" }, CMD_ARGV|CMD_FORCE, NULL, cmd_langmap }, + { { "new" }, CMD_ARGV, NULL, cmd_new }, + { { "open" }, CMD_ARGV, NULL, cmd_open }, + { { "qall" }, CMD_FORCE, NULL, cmd_qall }, + { { "set" }, CMD_ARGV, NULL, cmd_set }, + { { "split" }, CMD_ARGV, NULL, cmd_split }, + { { "vnew" }, CMD_ARGV, NULL, cmd_vnew }, + { { "vsplit" }, CMD_ARGV, NULL, cmd_vsplit }, + { { "wq" }, CMD_ARGV|CMD_FORCE, NULL, cmd_wq }, + { { "earlier" }, CMD_ARGV, NULL, cmd_earlier_later }, + { { "later" }, CMD_ARGV, NULL, cmd_earlier_later }, + { { NULL }, 0, NULL, NULL }, }; static const CommandDef cmddef_select = - { { "s" }, 0, NULL, cmd_select }; + { { "s" }, 0, NULL, cmd_select }; const char *sam_error(enum SamError err) { static const char *error_msg[] = { @@ -670,7 +713,7 @@ static Filerange address_evaluate(Address *addr, File *file, Filerange *range, i return ret; } -static bool sam_execute(Vis *vis, Win *win, Command *cmd, Filerange *range) { +static bool sam_execute(Vis *vis, Win *win, Command *cmd, Cursor *cur, Filerange *range) { bool ret = true; if (cmd->address) *range = address_evaluate(cmd->address, win->file, range, 0); @@ -689,7 +732,7 @@ static bool sam_execute(Vis *vis, Win *win, Command *cmd, Filerange *range) { start = text_mark_set(txt, group.start); end = text_mark_set(txt, group.end); - ret &= sam_execute(vis, win, c, &group); + ret &= sam_execute(vis, win, c, cur, &group); size_t s = text_mark_get(txt, start); /* hack to make delete work, only update if still valid */ @@ -700,7 +743,7 @@ static bool sam_execute(Vis *vis, Win *win, Command *cmd, Filerange *range) { break; } default: - ret = cmd->cmddef->func(vis, win, cmd, cmd->argv, range); + ret = cmd->cmddef->func(vis, win, cmd, cmd->argv, cur, range); break; } return ret; @@ -719,7 +762,7 @@ enum SamError sam_cmd(Vis *vis, const char *s) { } Filerange range = text_range_empty(); - sam_execute(vis, vis->win, cmd, &range); + sam_execute(vis, vis->win, cmd, NULL, &range); bool completed = true; for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) { @@ -735,7 +778,7 @@ enum SamError sam_cmd(Vis *vis, const char *s) { return err; } -static bool cmd_insert(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_insert(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { size_t len = strlen(argv[1]); bool ret = text_insert(win->file->text, range->start, argv[1], len); if (ret) @@ -743,7 +786,7 @@ static bool cmd_insert(Vis *vis, Win *win, Command *cmd, const char *argv[], Fil return ret; } -static bool cmd_append(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_append(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { size_t len = strlen(argv[1]); bool ret = text_insert(win->file->text, range->end, argv[1], len); if (ret) @@ -751,7 +794,7 @@ static bool cmd_append(Vis *vis, Win *win, Command *cmd, const char *argv[], Fil return ret; } -static bool cmd_change(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_change(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Text *txt = win->file->text; size_t len = strlen(argv[1]); bool ret = text_delete(txt, range->start, text_range_size(range)) && @@ -761,24 +804,34 @@ static bool cmd_change(Vis *vis, Win *win, Command *cmd, const char *argv[], Fil return ret; } -static bool cmd_delete(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_delete(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool ret = text_delete(win->file->text, range->start, text_range_size(range)); if (ret) *range = text_range_new(range->start, range->start); return ret; } -static bool cmd_guard(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_guard(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool match = !text_search_range_forward(win->file->text, range->start, text_range_size(range), cmd->regex, 0, NULL, 0); if (match ^ (argv[0][0] == 'v')) - return sam_execute(vis, win, cmd->cmd, range); + return sam_execute(vis, win, cmd->cmd, cur, range); + view_cursors_dispose(cur); return true; } -static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool ret = true; + View *view = win->view; Text *txt = win->file->text; + + Cursor *cursor = NULL; + size_t pos = view_cursor_get(view); + if (!view_cursors_dispose(cur)) { + cursor = view_cursors_new(view, text_size(txt)+1); + view_cursors_dispose(cur); + } + if (cmd->regex) { size_t start = range->start, end = range->end, last_start = EPOS; RegexMatch match[1]; @@ -809,13 +862,15 @@ static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Fi if (text_range_valid(&r)) { Mark mark_start = text_mark_set(txt, start); Mark mark_end = text_mark_set(txt, end); - ret &= sam_execute(vis, win, cmd->cmd, &r); + ret &= sam_execute(vis, win, cmd->cmd, NULL, &r); last_start = start = text_mark_get(txt, mark_start); if (start == EPOS && last_start != r.end) last_start = start = r.end; end = text_mark_get(txt, mark_end); - if (start == EPOS || end == EPOS) - return false; + if (start == EPOS || end == EPOS) { + ret = false; + break; + } } } } else { @@ -826,22 +881,28 @@ static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Fi if (start == end || !text_range_valid(&line)) break; Mark mark_end = text_mark_set(txt, end); - ret &= sam_execute(vis, win, cmd->cmd, &line); + ret &= sam_execute(vis, win, cmd->cmd, NULL, &line); start = text_mark_get(txt, mark_end); - if (start == EPOS) - return false; + if (start == EPOS) { + ret = false; + break; + } } } + + if (cursor && !view_cursors_dispose(cursor)) { + view_cursors_selection_clear(cursor); + view_cursors_to(cursor, pos); + } + return ret; } -static bool cmd_select(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_select(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool ret = true; View *view = win->view; Text *txt = win->file->text; bool multiple_cursors = view_cursors_multiple(view); - size_t pos = view_cursor_get(view); - Cursor *cursor = NULL; for (Cursor *c = view_cursors(view), *next; c; c = next) { next = view_cursors_next(c); Filerange sel; @@ -856,23 +917,14 @@ static bool cmd_select(Vis *vis, Win *win, Command *cmd, const char *argv[], Fil } else { sel = text_range_new(0, text_size(txt)); } - if (!view_cursors_dispose(c)) { - cursor = view_cursors_new(view, text_size(txt)+1); - view_cursors_dispose(c); - } if (text_range_valid(&sel)) - ret &= sam_execute(vis, win, cmd->cmd, &sel); - } - - if (cursor && !view_cursors_dispose(cursor)) { - view_cursors_selection_clear(cursor); - view_cursors_to(cursor, pos); + ret &= sam_execute(vis, win, cmd->cmd, c, &sel); } return ret; } -static bool cmd_print(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_print(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { if (!text_range_valid(range)) return false; View *view = win->view; @@ -880,13 +932,20 @@ static bool cmd_print(Vis *vis, Win *win, Command *cmd, const char *argv[], File size_t pos = range->end; if (range->start != range->end) pos = text_char_prev(txt, pos); - Cursor *cursor = view_cursors_new(view, pos); - if (cursor && range->start != range->end) - view_cursors_selection_set(cursor, range); - return cursor != NULL; + if (cur) + view_cursors_to(cur, pos); + else + cur = view_cursors_new(view, pos); + if (cur) { + if (range->start != range->end) + view_cursors_selection_set(cur, range); + else + view_cursors_selection_clear(cur); + } + return cur != NULL; } -static bool cmd_files(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_files(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool ret = true; for (Win *win = vis->windows; win; win = win->next) { if (win->file->internal) @@ -894,22 +953,22 @@ static bool cmd_files(Vis *vis, Win *win, Command *cmd, const char *argv[], File bool match = !cmd->regex || (win->file->name && text_regex_match(cmd->regex, win->file->name, 0)); if (match ^ (argv[0][0] == 'Y')) - ret &= sam_execute(vis, win, cmd->cmd, range); + ret &= sam_execute(vis, win, cmd->cmd, NULL, range); } return ret; } -static bool cmd_substitute(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_substitute(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Buffer buf; buffer_init(&buf); bool ret = false; if (buffer_put0(&buf, "s") && buffer_append0(&buf, argv[1])) - ret = cmd_filter(vis, win, cmd, (const char*[]){ argv[0], "sed", buf.data, NULL }, range); + ret = cmd_filter(vis, win, cmd, (const char*[]){ argv[0], "sed", buf.data, NULL }, cur, range); buffer_release(&buf); return ret; } -static bool cmd_write(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_write(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { File *file = win->file; Text *text = file->text; if (!argv[1]) @@ -956,12 +1015,12 @@ static bool cmd_write(Vis *vis, Win *win, Command *cmd, const char *argv[], File return true; } -static bool cmd_read(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_read(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Buffer buf; buffer_init(&buf); bool ret = false; if (buffer_put0(&buf, "cat ") && buffer_append0(&buf, argv[1])) - ret = cmd_pipein(vis, win, cmd, (const char*[]){ argv[0], buf.data, NULL }, range); + ret = cmd_pipein(vis, win, cmd, (const char*[]){ argv[0], buf.data, NULL }, cur, range); buffer_release(&buf); return ret; } @@ -979,7 +1038,7 @@ static ssize_t read_stderr(void *context, char *data, size_t len) { return len; } -static bool cmd_filter(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_filter(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Text *txt = win->file->text; Filter filter = { @@ -1010,6 +1069,8 @@ static bool cmd_filter(Vis *vis, Win *win, Command *cmd, const char *argv[], Fil text_delete_range(txt, range); text_snapshot(txt); range->end = filter.pos - text_range_size(range); + if (cur) + view_cursors_to(cur, range->start); } else { /* make sure we have somehting to undo */ text_insert(txt, filter.pos, " ", 1); @@ -1030,22 +1091,27 @@ static bool cmd_filter(Vis *vis, Win *win, Command *cmd, const char *argv[], Fil return !vis->cancel_filter && status == 0; } -static bool cmd_pipein(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_pipein(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Filerange filter_range = text_range_new(range->end, range->end); - bool ret = cmd_filter(vis, win, cmd, (const char*[]){ argv[0], argv[1], NULL }, &filter_range); + bool ret = cmd_filter(vis, win, cmd, (const char*[]){ argv[0], argv[1], NULL }, cur, &filter_range); if (ret) { text_delete_range(win->file->text, range); range->end = range->start + text_range_size(&filter_range); + if (cur) + view_cursors_to(cur, range->start); } return ret; } -static bool cmd_pipeout(Vis *vis, Win *win, Command *cmd, const char *argv[], Filerange *range) { +static bool cmd_pipeout(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Filter filter; buffer_init(&filter.err); int status = vis_pipe(vis, &filter, range, (const char*[]){ argv[1], NULL }, NULL, read_stderr); + if (status == 0 && cur) + view_cursors_to(cur, range->start); + if (vis->cancel_filter) vis_info_show(vis, "Command cancelled"); else if (status == 0) @@ -1059,3 +1125,5 @@ static bool cmd_pipeout(Vis *vis, Win *win, Command *cmd, const char *argv[], Fi return !vis->cancel_filter && status == 0; } + +#include "vis-cmds.c" @@ -1,134 +1,4 @@ -#include <stdbool.h> -#include <string.h> -#include <strings.h> -#include <stdio.h> -#include <limits.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> -#include <ctype.h> -#include <sys/select.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include "vis-core.h" -#include "text-util.h" -#include "text-motions.h" -#include "text-objects.h" -#include "util.h" -#include "sam.h" - -enum CmdOpt { /* option flags for command definitions */ - CMD_OPT_NONE, /* no option (default value) */ - CMD_OPT_FORCE, /* whether the command can be forced by appending '!' */ - CMD_OPT_ARGS, /* whether the command line should be parsed in to space - * separated arguments to placed into argv, otherwise argv[1] - * will contain the remaining command line unmodified */ -}; - -typedef struct { /* command definitions for the ':'-prompt */ - const char *name[3]; /* name and optional alias for the command */ - /* command logic called with a NULL terminated array of arguments. - * argv[0] will be the command name */ - bool (*cmd)(Vis*, Filerange*, enum CmdOpt opt, const char *argv[]); - enum CmdOpt opt; /* command option flags */ -} Command; - -typedef struct { /* used to keep context when dealing with external proceses */ - Vis *vis; /* editor instance */ - Text *txt; /* text into which received data will be inserted */ - size_t pos; /* position at which to insert new data */ - Buffer err; /* used to store everything the process writes to stderr */ -} Filter; - -/** ':'-command implementations */ -/* set various runtime options */ -static bool cmd_set(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* for each argument create a new window and open the corresponding file */ -static bool cmd_open(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* close current window (discard modifications if forced ) and open argv[1], - * if no argv[1] is given re-read to current file from disk */ -static bool cmd_edit(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* close the current window, discard modifications if forced */ -static bool cmd_quit(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* close all windows which show current file, discard modifications if forced */ -static bool cmd_bdelete(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* close all windows, exit editor, discard modifications if forced */ -static bool cmd_qall(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* for each argument try to insert the file content at current cursor postion */ -static bool cmd_read(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -static bool cmd_substitute(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* if no argument are given, split the current window horizontally, - * otherwise open the file */ -static bool cmd_split(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* if no argument are given, split the current window vertically, - * otherwise open the file */ -static bool cmd_vsplit(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* create a new empty window and arrange all windows either horizontally or vertically */ -static bool cmd_new(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -static bool cmd_vnew(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* save the file displayed in the current window and close it */ -static bool cmd_wq(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* save the file displayed in the current window if it was changvis, then close the window */ -static bool cmd_xit(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* save the file displayed in the current window to the name given. - * do not change internal filname association. further :w commands - * without arguments will still write to the old filename */ -static bool cmd_write(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* save the file displayed in the current window to the name given, - * associate the new name with the buffer. further :w commands - * without arguments will write to the new filename */ -static bool cmd_saveas(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* filter range through external program argv[1] */ -static bool cmd_filter(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* write range to external program, display output in a new window */ -static bool cmd_pipe(Vis *vis, Filerange*, enum CmdOpt, const char *argv[]); -/* switch to the previous/next saved state of the text, chronologically */ -static bool cmd_earlier_later(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* dump current key bindings */ -static bool cmd_help(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* change runtime key bindings */ -static bool cmd_map(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -static bool cmd_unmap(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -/* set language specific key bindings */ -static bool cmd_langmap(Vis*, Filerange*, enum CmdOpt, const char *argv[]); -static bool cmd_sam(Vis*, Filerange*, enum CmdOpt, const char *argv[]); - -/* command recognized at the ':'-prompt. commands are found using a unique - * prefix match. that is if a command should be available under an abbreviation - * which is a prefix for another command it has to be added as an alias. the - * long human readable name should always come first */ -static const Command cmds[] = { - /* command name / optional alias, function, options */ - { { "bdelete" }, cmd_bdelete, CMD_OPT_FORCE }, - { { "edit", "e" }, cmd_edit, CMD_OPT_FORCE }, - { { "help" }, cmd_help, CMD_OPT_NONE }, - { { "map", }, cmd_map, CMD_OPT_FORCE|CMD_OPT_ARGS }, - { { "map-window", }, cmd_map, CMD_OPT_FORCE|CMD_OPT_ARGS }, - { { "unmap", }, cmd_unmap, CMD_OPT_ARGS }, - { { "unmap-window", }, cmd_unmap, CMD_OPT_ARGS }, - { { "langmap", }, cmd_langmap, CMD_OPT_FORCE|CMD_OPT_ARGS }, - { { "new" }, cmd_new, CMD_OPT_NONE }, - { { "open" }, cmd_open, CMD_OPT_NONE }, - { { "qall" }, cmd_qall, CMD_OPT_FORCE }, - { { "quit", "q" }, cmd_quit, CMD_OPT_FORCE }, - { { "read", }, cmd_read, CMD_OPT_FORCE }, - { { "saveas" }, cmd_saveas, CMD_OPT_FORCE }, - { { "set", }, cmd_set, CMD_OPT_ARGS }, - { { "split" }, cmd_split, CMD_OPT_NONE }, - { { "substitute", "s" }, cmd_substitute, CMD_OPT_NONE }, - { { "vnew" }, cmd_vnew, CMD_OPT_NONE }, - { { "vsplit", }, cmd_vsplit, CMD_OPT_NONE }, - { { "wq", }, cmd_wq, CMD_OPT_FORCE }, - { { "write", "w" }, cmd_write, CMD_OPT_FORCE }, - { { "xit", }, cmd_xit, CMD_OPT_FORCE }, - { { "earlier" }, cmd_earlier_later, CMD_OPT_NONE }, - { { "later" }, cmd_earlier_later, CMD_OPT_NONE }, - { { "!", }, cmd_filter, CMD_OPT_NONE }, - { { "|", }, cmd_pipe, CMD_OPT_NONE }, - { { "sam" }, cmd_sam, CMD_OPT_NONE }, - { { NULL, }, NULL, CMD_OPT_NONE }, -}; +/* this file is included from sam.c */ static void windows_arrange(Vis *vis, enum UiLayout layout) { vis->ui->arrange(vis->ui, layout); @@ -160,7 +30,7 @@ static bool parse_bool(const char *s, bool *outval) { return false; } -static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char *argv[]) { +static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { typedef struct { const char *names[3]; @@ -217,7 +87,7 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char * return false; } - if (!vis->win) { + if (!win) { vis_info_show(vis, "Need active window for :set command"); return false; } @@ -281,7 +151,7 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char * break; case OPTION_SYNTAX: if (!argv[2]) { - const char *syntax = view_syntax_get(vis->win->view); + const char *syntax = view_syntax_get(win->view); if (syntax) vis_info_show(vis, "Syntax definition in use: `%s'", syntax); else @@ -290,8 +160,8 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char * } if (parse_bool(argv[2], &arg.b) && !arg.b) - return view_syntax_set(vis->win->view, NULL); - if (!view_syntax_set(vis->win->view, argv[2])) { + return view_syntax_set(win->view, NULL); + if (!view_syntax_set(win->view, argv[2])) { vis_info_show(vis, "Unknown syntax definition: `%s'", argv[2]); return false; } @@ -307,7 +177,7 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char * UI_OPTION_SYMBOL_TAB|UI_OPTION_SYMBOL_TAB_FILL, UI_OPTION_SYMBOL_EOL, }; - int flags = view_options_get(vis->win->view); + int flags = view_options_get(win->view); for (const char **args = &argv[2]; *args; args++) { for (int i = 0; i < LENGTH(keys); i++) { if (strcmp(*args, keys[i]) == 0) { @@ -324,37 +194,37 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char * } } } - view_options_set(vis->win->view, flags); + view_options_set(win->view, flags); break; case OPTION_NUMBER: { - enum UiOption opt = view_options_get(vis->win->view); + enum UiOption opt = view_options_get(win->view); if (arg.b) { opt &= ~UI_OPTION_LINE_NUMBERS_RELATIVE; opt |= UI_OPTION_LINE_NUMBERS_ABSOLUTE; } else { opt &= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE; } - view_options_set(vis->win->view, opt); + view_options_set(win->view, opt); break; } case OPTION_NUMBER_RELATIVE: { - enum UiOption opt = view_options_get(vis->win->view); + enum UiOption opt = view_options_get(win->view); if (arg.b) { opt &= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE; opt |= UI_OPTION_LINE_NUMBERS_RELATIVE; } else { opt &= ~UI_OPTION_LINE_NUMBERS_RELATIVE; } - view_options_set(vis->win->view, opt); + view_options_set(win->view, opt); break; } case OPTION_CURSOR_LINE: { - enum UiOption opt = view_options_get(vis->win->view); + enum UiOption opt = view_options_get(win->view); if (arg.b) opt |= UI_OPTION_CURSOR_LINE; else opt &= ~UI_OPTION_CURSOR_LINE; - view_options_set(vis->win->view, opt); + view_options_set(win->view, opt); break; } case OPTION_THEME: @@ -364,7 +234,7 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char * } break; case OPTION_COLOR_COLUMN: - view_colorcolumn_set(vis->win->view, arg.i); + view_colorcolumn_set(win->view, arg.i); break; } @@ -381,6 +251,9 @@ static bool is_file_pattern(const char *pattern) { } static const char *file_open_dialog(Vis *vis, const char *pattern) { + /* FIXME/TODO rewrite this mess */ + return NULL; +#if 0 if (!is_file_pattern(pattern)) return pattern; /* this is a bit of a hack, we temporarily replace the text/view of the active @@ -402,7 +275,7 @@ static const char *file_open_dialog(Vis *vis, const char *pattern) { win->view = view; file->text = txt; - if (cmd_filter(vis, &range, CMD_OPT_NONE, (const char *[]){ "open", vis_open, NULL })) { + if (cmd_filter(vis, win, &range, CMD_OPT_NONE, (const char *[]){ "open", vis_open, NULL })) { size_t len = text_size(txt); if (len >= sizeof(filename)) len = 0; @@ -417,6 +290,7 @@ out: win->view = view_orig; file->text = txt_orig; return filename[0] ? filename : NULL; +#endif } static bool openfiles(Vis *vis, const char **files) { @@ -434,7 +308,7 @@ static bool openfiles(Vis *vis, const char **files) { return true; } -static bool cmd_open(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_open(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { if (!argv[1]) return vis_window_new(vis, NULL); return openfiles(vis, &argv[1]); @@ -444,9 +318,9 @@ static void info_unsaved_changes(Vis *vis) { vis_info_show(vis, "No write since last change (add ! to override)"); } -static bool cmd_edit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - Win *oldwin = vis->win; - if (!(opt & CMD_OPT_FORCE) && !vis_window_closable(oldwin)) { +static bool cmd_edit(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + Win *oldwin = win; + if (cmd->flags != '!' && !vis_window_closable(oldwin)) { info_unsaved_changes(vis); return false; } @@ -467,28 +341,20 @@ static bool has_windows(Vis *vis) { return false; } -static bool cmd_quit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - if (!(opt & CMD_OPT_FORCE) && !vis_window_closable(vis->win)) { +static bool cmd_quit(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + if (cmd->flags != '!' && !vis_window_closable(win)) { info_unsaved_changes(vis); return false; } - vis_window_close(vis->win); + vis_window_close(win); if (!has_windows(vis)) vis_exit(vis, EXIT_SUCCESS); return true; } -static bool cmd_xit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - if (text_modified(vis->win->file->text) && !cmd_write(vis, range, opt, argv)) { - if (!(opt & CMD_OPT_FORCE)) - return false; - } - return cmd_quit(vis, range, opt, argv); -} - -static bool cmd_bdelete(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - Text *txt = vis->win->file->text; - if (text_modified(txt) && !(opt & CMD_OPT_FORCE)) { +static bool cmd_bdelete(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + Text *txt = win->file->text; + if (text_modified(txt) && cmd->flags != '!') { info_unsaved_changes(vis); return false; } @@ -502,10 +368,10 @@ static bool cmd_bdelete(Vis *vis, Filerange *range, enum CmdOpt opt, const char return true; } -static bool cmd_qall(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_qall(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { for (Win *next, *win = vis->windows; win; win = next) { next = win->next; - if (!win->file->internal && (!text_modified(win->file->text) || (opt & CMD_OPT_FORCE))) + if (!win->file->internal && (!text_modified(win->file->text) || cmd->flags == '!')) vis_window_close(win); } if (!has_windows(vis)) { @@ -517,415 +383,46 @@ static bool cmd_qall(Vis *vis, Filerange *range, enum CmdOpt opt, const char *ar } } -static bool cmd_read(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - char cmd[255]; - - if (!argv[1]) { - vis_info_show(vis, "Filename or command expected"); - return false; - } - - bool iscmd = (opt & CMD_OPT_FORCE) || argv[1][0] == '!'; - const char *arg = argv[1]+(argv[1][0] == '!'); - snprintf(cmd, sizeof cmd, "%s%s", iscmd ? "" : "cat ", arg); - - size_t pos = view_cursor_get(vis->win->view); - if (!text_range_valid(range)) - *range = (Filerange){ .start = pos, .end = pos }; - Filerange delete = *range; - range->start = range->end; - - bool ret = cmd_filter(vis, range, opt, (const char*[]){ argv[0], "sh", "-c", cmd, NULL}); - if (ret) - text_delete_range(vis->win->file->text, &delete); - return ret; -} - -static bool cmd_substitute(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - char pattern[255]; - if (!text_range_valid(range)) - *range = text_object_line(vis->win->file->text, view_cursor_get(vis->win->view)); - snprintf(pattern, sizeof pattern, "s%s", argv[1]); - return cmd_filter(vis, range, opt, (const char*[]){ argv[0], "sed", pattern, NULL}); -} - -static bool cmd_split(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - enum UiOption options = view_options_get(vis->win->view); +static bool cmd_split(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + enum UiOption options = view_options_get(win->view); windows_arrange(vis, UI_LAYOUT_HORIZONTAL); if (!argv[1]) - return vis_window_split(vis->win); + return vis_window_split(win); bool ret = openfiles(vis, &argv[1]); - view_options_set(vis->win->view, options); + if (ret) + view_options_set(vis->win->view, options); return ret; } -static bool cmd_vsplit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - enum UiOption options = view_options_get(vis->win->view); +static bool cmd_vsplit(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + enum UiOption options = view_options_get(win->view); windows_arrange(vis, UI_LAYOUT_VERTICAL); if (!argv[1]) - return vis_window_split(vis->win); + return vis_window_split(win); bool ret = openfiles(vis, &argv[1]); - view_options_set(vis->win->view, options); + if (ret) + view_options_set(vis->win->view, options); return ret; } -static bool cmd_new(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_new(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { windows_arrange(vis, UI_LAYOUT_HORIZONTAL); return vis_window_new(vis, NULL); } -static bool cmd_vnew(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_vnew(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { windows_arrange(vis, UI_LAYOUT_VERTICAL); return vis_window_new(vis, NULL); } -static bool cmd_wq(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - if (cmd_write(vis, range, opt, argv)) - return cmd_quit(vis, range, opt, argv); - return false; -} - -static bool cmd_write(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - File *file = vis->win->file; - Text *text = file->text; - if (!text_range_valid(range)) - *range = (Filerange){ .start = 0, .end = text_size(text) }; - if (!argv[1]) - argv[1] = file->name; - if (!argv[1]) { - if (file->is_stdin) { - if (strchr(argv[0], 'q')) { - ssize_t written = text_write_range(text, range, STDOUT_FILENO); - if (written == -1 || (size_t)written != text_range_size(range)) { - vis_info_show(vis, "Can not write to stdout"); - return false; - } - /* make sure the file is marked as saved i.e. not modified */ - text_save_range(text, range, NULL); - return true; - } - vis_info_show(vis, "No filename given, use 'wq' to write to stdout"); - return false; - } - vis_info_show(vis, "Filename expected"); - return false; - } - - if (argv[1][0] == '!') { - argv[1]++; - return cmd_pipe(vis, range, opt, argv); - } - - for (const char **name = &argv[1]; *name; name++) { - struct stat meta; - if (!(opt & CMD_OPT_FORCE) && file->stat.st_mtime && stat(*name, &meta) == 0 && - file->stat.st_mtime < meta.st_mtime) { - vis_info_show(vis, "WARNING: file has been changed since reading it"); - return false; - } - if (!text_save_range(text, range, *name)) { - vis_info_show(vis, "Can't write `%s'", *name); - return false; - } - if (!file->name) { - vis_window_name(vis->win, *name); - file->name = vis->win->file->name; - } - if (strcmp(file->name, *name) == 0) - file->stat = text_stat(text); - if (vis->event && vis->event->file_save) - vis->event->file_save(vis, file); - } - return true; -} - -static bool cmd_saveas(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - if (cmd_write(vis, range, opt, argv)) { - vis_window_name(vis->win, argv[1]); - vis->win->file->stat = text_stat(vis->win->file->text); - return true; - } +static bool cmd_wq(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + if (cmd_write(vis, win, cmd, argv, cur, range)) + return cmd_quit(vis, win, cmd, argv, cur, range); return false; } -int vis_pipe(Vis *vis, void *context, Filerange *range, const char *argv[], - ssize_t (*read_stdout)(void *context, char *data, size_t len), - ssize_t (*read_stderr)(void *context, char *data, size_t len)) { - - /* if an invalid range was given, stdin (i.e. key board input) is passed - * through the external command. */ - Text *text = vis->win->file->text; - View *view = vis->win->view; - int pin[2], pout[2], perr[2], status = -1; - bool interactive = !text_range_valid(range); - size_t pos = view_cursor_get(view); - Filerange rout = *range; - if (interactive) - rout = (Filerange){ .start = pos, .end = pos }; - - if (pipe(pin) == -1) - return -1; - if (pipe(pout) == -1) { - close(pin[0]); - close(pin[1]); - return -1; - } - - if (pipe(perr) == -1) { - close(pin[0]); - close(pin[1]); - close(pout[0]); - close(pout[1]); - return -1; - } - - vis->ui->terminal_save(vis->ui); - pid_t pid = fork(); - - if (pid == -1) { - close(pin[0]); - close(pin[1]); - close(pout[0]); - close(pout[1]); - close(perr[0]); - close(perr[1]); - vis_info_show(vis, "fork failure: %s", strerror(errno)); - return -1; - } else if (pid == 0) { /* child i.e filter */ - if (!interactive) - dup2(pin[0], STDIN_FILENO); - close(pin[0]); - close(pin[1]); - dup2(pout[1], STDOUT_FILENO); - close(pout[1]); - close(pout[0]); - if (!interactive) - dup2(perr[1], STDERR_FILENO); - close(perr[0]); - close(perr[1]); - if (!argv[1]) - execl("/bin/sh", "sh", "-c", argv[0], NULL); - else - execvp(argv[0], (char* const*)argv); - vis_info_show(vis, "exec failure: %s", strerror(errno)); - exit(EXIT_FAILURE); - } - - vis->cancel_filter = false; - - close(pin[0]); - close(pout[1]); - close(perr[1]); - - fcntl(pout[0], F_SETFL, O_NONBLOCK); - fcntl(perr[0], F_SETFL, O_NONBLOCK); - - - fd_set rfds, wfds; - - do { - if (vis->cancel_filter) { - kill(-pid, SIGTERM); - break; - } - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - if (pin[1] != -1) - FD_SET(pin[1], &wfds); - if (pout[0] != -1) - FD_SET(pout[0], &rfds); - if (perr[0] != -1) - FD_SET(perr[0], &rfds); - - if (select(FD_SETSIZE, &rfds, &wfds, NULL, NULL) == -1) { - if (errno == EINTR) - continue; - vis_info_show(vis, "Select failure"); - break; - } - - if (pin[1] != -1 && FD_ISSET(pin[1], &wfds)) { - Filerange junk = rout; - if (junk.end > junk.start + PIPE_BUF) - junk.end = junk.start + PIPE_BUF; - ssize_t len = text_write_range(text, &junk, pin[1]); - if (len > 0) { - rout.start += len; - if (text_range_size(&rout) == 0) { - close(pout[1]); - pout[1] = -1; - } - } else { - close(pin[1]); - pin[1] = -1; - if (len == -1) - vis_info_show(vis, "Error writing to external command"); - } - } - - if (pout[0] != -1 && FD_ISSET(pout[0], &rfds)) { - char buf[BUFSIZ]; - ssize_t len = read(pout[0], buf, sizeof buf); - if (len > 0) { - if (read_stdout) - (*read_stdout)(context, buf, len); - } else if (len == 0) { - close(pout[0]); - pout[0] = -1; - } else if (errno != EINTR && errno != EWOULDBLOCK) { - vis_info_show(vis, "Error reading from filter stdout"); - close(pout[0]); - pout[0] = -1; - } - } - - if (perr[0] != -1 && FD_ISSET(perr[0], &rfds)) { - char buf[BUFSIZ]; - ssize_t len = read(perr[0], buf, sizeof buf); - if (len > 0) { - if (read_stderr) - (*read_stderr)(context, buf, len); - } else if (len == 0) { - close(perr[0]); - perr[0] = -1; - } else if (errno != EINTR && errno != EWOULDBLOCK) { - vis_info_show(vis, "Error reading from filter stderr"); - close(perr[0]); - perr[0] = -1; - } - } - - } while (pin[1] != -1 || pout[0] != -1 || perr[0] != -1); - - if (pin[1] != -1) - close(pin[1]); - if (pout[0] != -1) - close(pout[0]); - if (perr[0] != -1) - close(perr[0]); - - for (pid_t died; (died = waitpid(pid, &status, 0)) != -1 && pid != died;); - - vis->ui->terminal_restore(vis->ui); - - return status; -} - -static ssize_t read_stdout(void *context, char *data, size_t len) { - Filter *filter = context; - text_insert(filter->txt, filter->pos, data, len); - filter->pos += len; - return len; -} - -static ssize_t read_stderr(void *context, char *data, size_t len) { - Filter *filter = context; - buffer_append(&filter->err, data, len); - return len; -} - -static bool cmd_filter(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - Text *txt = vis->win->file->text; - View *view = vis->win->view; - - Filter filter = { - .vis = vis, - .txt = vis->win->file->text, - .pos = range->end != EPOS ? range->end : view_cursor_get(view), - }; - - buffer_init(&filter.err); - - /* The general idea is the following: - * - * 1) take a snapshot - * 2) write [range.start, range.end] to exteneral command - * 3) read the output of the external command and insert it after the range - * 4) depending on the exit status of the external command - * - on success: delete original range - * - on failure: revert to previous snapshot - * - * 2) and 3) happend in small junks - */ - - text_snapshot(txt); - - int status = vis_pipe(vis, &filter, range, &argv[1], read_stdout, read_stderr); - - if (status == 0) { - if (text_range_valid(range)) { - text_delete_range(txt, range); - view_cursor_to(view, range->start); - } - text_snapshot(txt); - } else { - /* make sure we have somehting to undo */ - text_insert(txt, filter.pos, " ", 1); - text_undo(txt); - } - - if (vis->cancel_filter) - vis_info_show(vis, "Command cancelled"); - else if (status == 0) - vis_info_show(vis, "Command succeded"); - else if (filter.err.len > 0) - vis_info_show(vis, "Command failed: %s", filter.err.data); - else - vis_info_show(vis, "Command failed"); - - buffer_release(&filter.err); - - return !vis->cancel_filter && status == 0; -} - -static ssize_t read_stdout_new(void *context, char *data, size_t len) { - Filter *filter = context; - - if (!filter->txt && vis_window_new(filter->vis, NULL)) - filter->txt = filter->vis->win->file->text; - - if (filter->txt) { - text_insert(filter->txt, filter->pos, data, len); - filter->pos += len; - } - return len; -} - -static bool cmd_pipe(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - Text *txt = vis->win->file->text; - if (!text_range_valid(range)) - *range = (Filerange){ .start = 0, .end = text_size(txt) }; - - Filter filter = { - .vis = vis, - .txt = NULL, - .pos = 0, - }; - - buffer_init(&filter.err); - - int status = vis_pipe(vis, &filter, range, &argv[1], read_stdout_new, read_stderr); - - if (vis->cancel_filter) - vis_info_show(vis, "Command cancelled"); - else if (status == 0) - vis_info_show(vis, "Command succeded"); - else if (filter.err.len > 0) - vis_info_show(vis, "Command failed: %s", filter.err.data); - else - vis_info_show(vis, "Command failed"); - - buffer_release(&filter.err); - - if (filter.txt) - text_save(filter.txt, NULL); - - return !vis->cancel_filter && status == 0; -} - -static bool cmd_earlier_later(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - Text *txt = vis->win->file->text; +static bool cmd_earlier_later(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { + Text *txt = win->file->text; char *unit = ""; long count = 1; size_t pos = EPOS; @@ -997,7 +494,7 @@ static bool print_action(const char *key, void *value, void *data) { return text_appendf(txt, " %-30s\t%s\n", key, action->help); } -static bool cmd_help(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_help(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { if (!vis_window_new(vis, NULL)) return false; @@ -1024,7 +521,7 @@ static bool cmd_help(Vis *vis, Filerange *range, enum CmdOpt opt, const char *ar print_mode(&vis_modes[VIS_MODE_INSERT], txt); text_appendf(txt, "\n :-Commands\n\n"); - for (const Command *cmd = cmds; cmd && cmd->name[0]; cmd++) + for (const CommandDef *cmd = cmds; cmd && cmd->name[0]; cmd++) text_appendf(txt, " %s\n", cmd->name[0]); text_appendf(txt, "\n Key binding actions\n\n"); @@ -1051,7 +548,7 @@ static enum VisMode str2vismode(const char *mode) { return VIS_MODE_INVALID; } -static bool cmd_langmap(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_langmap(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { const char *nonlatin = argv[1]; const char *latin = argv[2]; bool mapped = true; @@ -1082,7 +579,7 @@ static bool cmd_langmap(Vis *vis, Filerange *range, enum CmdOpt opt, const char return mapped; } -static bool cmd_map(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_map(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool local = strstr(argv[0], "-") != NULL; enum VisMode mode = str2vismode(argv[1]); const char *lhs = argv[2]; @@ -1120,14 +617,14 @@ static bool cmd_map(Vis *vis, Filerange *range, enum CmdOpt opt, const char *arg bool mapped; if (local) - mapped = vis_window_mode_map(vis->win, mode, lhs, binding); + mapped = vis_window_mode_map(win, mode, lhs, binding); else mapped = vis_mode_map(vis, mode, lhs, binding); - if (!mapped && opt & CMD_OPT_FORCE) { + if (!mapped && cmd->flags == '!') { if (local) { - mapped = vis_window_mode_unmap(vis->win, mode, lhs) && - vis_window_mode_map(vis->win, mode, lhs, binding); + mapped = vis_window_mode_unmap(win, mode, lhs) && + vis_window_mode_map(win, mode, lhs, binding); } else { mapped = vis_mode_unmap(vis, mode, lhs) && vis_mode_map(vis, mode, lhs, binding); @@ -1139,7 +636,7 @@ static bool cmd_map(Vis *vis, Filerange *range, enum CmdOpt opt, const char *arg return mapped; } -static bool cmd_unmap(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { +static bool cmd_unmap(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { bool local = strstr(argv[0], "-") != NULL; enum VisMode mode = str2vismode(argv[1]); const char *lhs = argv[2]; @@ -1150,206 +647,7 @@ static bool cmd_unmap(Vis *vis, Filerange *range, enum CmdOpt opt, const char *a } if (local) - return vis_window_mode_unmap(vis->win, mode, lhs); + return vis_window_mode_unmap(win, mode, lhs); else return vis_mode_unmap(vis, mode, lhs); } - -static bool cmd_sam(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { - enum SamError err = sam_cmd(vis, argv[1]); - if (err != SAM_ERR_OK) - vis_info_show(vis, "%s", sam_error(err)); - return err == SAM_ERR_OK; -} - -static Filepos parse_pos(Win *win, char **cmd) { - size_t pos = EPOS; - View *view = win->view; - Text *txt = win->file->text; - Mark *marks = win->file->marks; - switch (**cmd) { - case '.': - pos = text_line_begin(txt, view_cursor_get(view)); - (*cmd)++; - break; - case '$': - pos = text_size(txt); - (*cmd)++; - break; - case '\'': - (*cmd)++; - if ('a' <= **cmd && **cmd <= 'z') - pos = text_mark_get(txt, marks[**cmd - 'a']); - else if (**cmd == '<') - pos = text_mark_get(txt, marks[VIS_MARK_SELECTION_START]); - else if (**cmd == '>') - pos = text_mark_get(txt, marks[VIS_MARK_SELECTION_END]); - (*cmd)++; - break; - case '/': - (*cmd)++; - char *pattern_end = strchr(*cmd, '/'); - if (!pattern_end) - return EPOS; - *pattern_end++ = '\0'; - Regex *regex = vis_regex(win->vis, *cmd); - if (!regex) - return EPOS; - pos = text_search_forward(txt, view_cursor_get(view), regex); - text_regex_free(regex); - break; - case '+': - case '-': - { - CursorPos curspos = view_cursor_getpos(view); - long long line = curspos.line + strtoll(*cmd, cmd, 10); - if (line < 0) - line = 0; - pos = text_pos_by_lineno(txt, line); - break; - } - default: - if ('0' <= **cmd && **cmd <= '9') - pos = text_pos_by_lineno(txt, strtoul(*cmd, cmd, 10)); - break; - } - - return pos; -} - -static Filerange parse_range(Win *win, char **cmd) { - Filerange r = text_range_empty(); - if (!win) - return r; - Text *txt = win->file->text; - Mark *marks = win->file->marks; - char start = **cmd; - switch (**cmd) { - case '%': - r.start = 0; - r.end = text_size(txt); - (*cmd)++; - break; - case '*': - r.start = text_mark_get(txt, marks[VIS_MARK_SELECTION_START]); - r.end = text_mark_get(txt, marks[VIS_MARK_SELECTION_END]); - (*cmd)++; - break; - default: - r.start = parse_pos(win, cmd); - if (**cmd != ',') { - if (start == '.') - r.end = text_line_next(txt, r.start); - return r; - } - (*cmd)++; - r.end = parse_pos(win, cmd); - break; - } - return r; -} - -static const Command *lookup_cmd(Vis *vis, const char *name) { - if (!vis->cmds) { - if (!(vis->cmds = map_new())) - return NULL; - - for (const Command *cmd = cmds; cmd && cmd->name[0]; cmd++) { - for (const char *const *name = cmd->name; *name; name++) - map_put(vis->cmds, *name, cmd); - } - } - return map_closest(vis->cmds, name); -} - -bool vis_cmd(Vis *vis, const char *cmdline) { - if (!cmdline) - return true; - enum CmdOpt opt = CMD_OPT_NONE; - while (*cmdline == ':') - cmdline++; - size_t len = strlen(cmdline); - char *line = malloc(len+2); - if (!line) - return false; - strncpy(line, cmdline, len+1); - - for (char *end = line + len - 1; end >= line && isspace((unsigned char)*end); end--) - *end = '\0'; - - char *name = line; - - Filerange range = parse_range(vis->win, &name); - if (!text_range_valid(&range)) { - /* if only one position was given, jump to it */ - if (range.start != EPOS && !*name) { - view_cursor_to(vis->win->view, range.start); - free(line); - return true; - } - - if (name != line) { - vis_info_show(vis, "Invalid range\n"); - free(line); - return false; - } - } - /* skip leading white space */ - while (*name == ' ') - name++; - char *param = name; - while (*param && (isalpha((unsigned char)*param) || *param == '-' || *param == '|')) - param++; - - if (*param == '!') { - if (param != name) { - opt |= CMD_OPT_FORCE; - *param = ' '; - } else { - param++; - } - } - - memmove(param+1, param, strlen(param)+1); - *param++ = '\0'; /* separate command name from parameters */ - - const Command *cmd = lookup_cmd(vis, name); - if (!cmd) { - vis_info_show(vis, "Not an editor command"); - free(line); - return false; - } - - char *s = param; - const char *argv[32] = { name }; - for (int i = 1; i < LENGTH(argv); i++) { - while (s && isspace((unsigned char)*s)) - s++; - if (s && !*s) - s = NULL; - argv[i] = s; - if (!(cmd->opt & CMD_OPT_ARGS)) { - /* remove trailing spaces */ - if (s) { - while (*s) s++; - while (*(--s) == ' ') *s = '\0'; - } - s = NULL; - } - if (s) { - while (*s && !isspace((unsigned char)*s)) - s++; - if (*s) - *s++ = '\0'; - } - /* strip out a single '!' argument to make ":q !" work */ - if (argv[i] && !strcmp(argv[i], "!")) { - opt |= CMD_OPT_FORCE; - i--; - } - } - - cmd->cmd(vis, &range, opt, argv); - free(line); - return true; -} diff --git a/vis-prompt.c b/vis-prompt.c index 52a0fbd..3711cc9 100644 --- a/vis-prompt.c +++ b/vis-prompt.c @@ -13,13 +13,8 @@ bool vis_prompt_cmd(Vis *vis, const char *cmd) { return vis_motion(vis, VIS_MOVE_SEARCH_BACKWARD, cmd+1); case '+': case ':': - { register_put0(vis, &vis->registers[VIS_REG_COMMAND], cmd+1); - bool ret = vis_cmd(vis, cmd+1); - if (ret && vis->mode->visual) - ;//vis_mode_switch(vis, VIS_MODE_NORMAL); - return ret; - } + return vis_cmd(vis, cmd+1); default: return false; } @@ -27,6 +27,7 @@ #include "text-objects.h" #include "util.h" #include "vis-core.h" +#include "sam.h" /* enable large file optimization for files larger than: */ #define LARGE_FILE (1 << 25) @@ -587,7 +588,7 @@ void action_do(Vis *vis, Action *a) { vis_mode_switch(vis, VIS_MODE_NORMAL); vis_cmd(vis, a->arg.s); } else { - vis_prompt_show(vis, ":'<,'>!"); + vis_prompt_show(vis, ":|"); } } else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) { mode_set(vis, vis->mode_prev); @@ -1117,6 +1118,191 @@ Regex *vis_regex(Vis *vis, const char *pattern) { return regex; } +int vis_pipe(Vis *vis, void *context, Filerange *range, const char *argv[], + ssize_t (*read_stdout)(void *context, char *data, size_t len), + ssize_t (*read_stderr)(void *context, char *data, size_t len)) { + + /* if an invalid range was given, stdin (i.e. key board input) is passed + * through the external command. */ + Text *text = vis->win->file->text; + View *view = vis->win->view; + int pin[2], pout[2], perr[2], status = -1; + bool interactive = !text_range_valid(range); + size_t pos = view_cursor_get(view); + Filerange rout = *range; + if (interactive) + rout = (Filerange){ .start = pos, .end = pos }; + + if (pipe(pin) == -1) + return -1; + if (pipe(pout) == -1) { + close(pin[0]); + close(pin[1]); + return -1; + } + + if (pipe(perr) == -1) { + close(pin[0]); + close(pin[1]); + close(pout[0]); + close(pout[1]); + return -1; + } + + vis->ui->terminal_save(vis->ui); + pid_t pid = fork(); + + if (pid == -1) { + close(pin[0]); + close(pin[1]); + close(pout[0]); + close(pout[1]); + close(perr[0]); + close(perr[1]); + vis_info_show(vis, "fork failure: %s", strerror(errno)); + return -1; + } else if (pid == 0) { /* child i.e filter */ + if (!interactive) + dup2(pin[0], STDIN_FILENO); + close(pin[0]); + close(pin[1]); + dup2(pout[1], STDOUT_FILENO); + close(pout[1]); + close(pout[0]); + if (!interactive) + dup2(perr[1], STDERR_FILENO); + close(perr[0]); + close(perr[1]); + if (!argv[1]) + execl("/bin/sh", "sh", "-c", argv[0], NULL); + else + execvp(argv[0], (char* const*)argv); + vis_info_show(vis, "exec failure: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + vis->cancel_filter = false; + + close(pin[0]); + close(pout[1]); + close(perr[1]); + + fcntl(pout[0], F_SETFL, O_NONBLOCK); + fcntl(perr[0], F_SETFL, O_NONBLOCK); + + + fd_set rfds, wfds; + + do { + if (vis->cancel_filter) { + kill(-pid, SIGTERM); + break; + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + if (pin[1] != -1) + FD_SET(pin[1], &wfds); + if (pout[0] != -1) + FD_SET(pout[0], &rfds); + if (perr[0] != -1) + FD_SET(perr[0], &rfds); + + if (select(FD_SETSIZE, &rfds, &wfds, NULL, NULL) == -1) { + if (errno == EINTR) + continue; + vis_info_show(vis, "Select failure"); + break; + } + + if (pin[1] != -1 && FD_ISSET(pin[1], &wfds)) { + Filerange junk = rout; + if (junk.end > junk.start + PIPE_BUF) + junk.end = junk.start + PIPE_BUF; + ssize_t len = text_write_range(text, &junk, pin[1]); + if (len > 0) { + rout.start += len; + if (text_range_size(&rout) == 0) { + close(pout[1]); + pout[1] = -1; + } + } else { + close(pin[1]); + pin[1] = -1; + if (len == -1) + vis_info_show(vis, "Error writing to external command"); + } + } + + if (pout[0] != -1 && FD_ISSET(pout[0], &rfds)) { + char buf[BUFSIZ]; + ssize_t len = read(pout[0], buf, sizeof buf); + if (len > 0) { + if (read_stdout) + (*read_stdout)(context, buf, len); + } else if (len == 0) { + close(pout[0]); + pout[0] = -1; + } else if (errno != EINTR && errno != EWOULDBLOCK) { + vis_info_show(vis, "Error reading from filter stdout"); + close(pout[0]); + pout[0] = -1; + } + } + + if (perr[0] != -1 && FD_ISSET(perr[0], &rfds)) { + char buf[BUFSIZ]; + ssize_t len = read(perr[0], buf, sizeof buf); + if (len > 0) { + if (read_stderr) + (*read_stderr)(context, buf, len); + } else if (len == 0) { + close(perr[0]); + perr[0] = -1; + } else if (errno != EINTR && errno != EWOULDBLOCK) { + vis_info_show(vis, "Error reading from filter stderr"); + close(perr[0]); + perr[0] = -1; + } + } + + } while (pin[1] != -1 || pout[0] != -1 || perr[0] != -1); + + if (pin[1] != -1) + close(pin[1]); + if (pout[0] != -1) + close(pout[0]); + if (perr[0] != -1) + close(perr[0]); + + for (pid_t died; (died = waitpid(pid, &status, 0)) != -1 && pid != died;); + + vis->ui->terminal_restore(vis->ui); + + return status; +} + +bool vis_cmd(Vis *vis, const char *cmdline) { + if (!cmdline) + return true; + while (*cmdline == ':') + cmdline++; + size_t len = strlen(cmdline); + char *line = malloc(len+2); + if (!line) + return false; + strncpy(line, cmdline, len+1); + + for (char *end = line + len - 1; end >= line && isspace((unsigned char)*end); end--) + *end = '\0'; + + enum SamError err = sam_cmd(vis, line); + if (err != SAM_ERR_OK) + vis_info_show(vis, "%s", sam_error(err)); + free(line); + return err == SAM_ERR_OK; +} + Text *vis_text(Vis *vis) { return vis->win->file->text; } |
