aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xC.c125
1 files changed, 114 insertions, 11 deletions
diff --git a/xC.c b/xC.c
index 1f4ff0f..3de9f26 100644
--- a/xC.c
+++ b/xC.c
@@ -238,8 +238,8 @@ struct input_vtable
/// Bind Alt+key to the given named function
void (*bind_meta) (void *input, char key, const char *fn);
- /// Get the current line input
- char *(*get_line) (void *input);
+ /// Get the current line input and position within
+ char *(*get_line) (void *input, int *position);
/// Clear the current line input
void (*clear_line) (void *input);
/// Insert text at current position
@@ -361,9 +361,10 @@ input_rl_insert (void *input, const char *s)
}
static char *
-input_rl_get_line (void *input)
+input_rl_get_line (void *input, int *position)
{
(void) input;
+ if (position) *position = rl_point;
return rl_copy_text (0, rl_end);
}
@@ -860,10 +861,12 @@ input_el_insert (void *input, const char *s)
}
static char *
-input_el_get_line (void *input)
+input_el_get_line (void *input, int *position)
{
struct input_el *self = input;
const LineInfo *info = el_line (self->editline);
+ int point = info->cursor - info->buffer;
+ if (position) *position = point;
return xstrndup (info->buffer, info->lastchar - info->buffer);
}
@@ -2439,6 +2442,9 @@ static struct config_schema g_config_behaviour[] =
.type = CONFIG_ITEM_BOOLEAN,
.default_ = "on",
.on_change = on_config_word_wrapping_change },
+ { .name = "editor_command",
+ .comment = "VIM: \"vim +%Bgo %F\", Emacs: \"emacs -nw +%L:%C %F\"",
+ .type = CONFIG_ITEM_STRING },
{ .name = "date_change_line",
.comment = "Input to strftime(3) for the date change line",
.type = CONFIG_ITEM_STRING,
@@ -6894,7 +6900,7 @@ irc_handle_join (struct server *s, const struct irc_message *msg)
buffer_add (s->ctx, buffer);
- char *input = CALL (s->ctx->input, get_line);
+ char *input = CALL_ (s->ctx->input, get_line, NULL);
if (!*input)
buffer_activate (s->ctx, buffer);
else
@@ -13152,7 +13158,7 @@ dump_input_to_file (struct app_context *ctx, char *template, struct error **e)
if (fd < 0)
return error_set (e, "%s", strerror (errno));
- char *input = CALL (ctx->input, get_line);
+ char *input = CALL_ (ctx->input, get_line, NULL);
bool success = xwrite (fd, input, strlen (input), e);
free (input);
@@ -13180,6 +13186,103 @@ try_dump_input_to_file (struct app_context *ctx)
return NULL;
}
+static struct strv
+build_editor_command (struct app_context *ctx, const char *filename)
+{
+ struct strv argv = strv_make ();
+ const char *editor = get_config_string
+ (ctx->config.root, "behaviour.editor_command");
+ if (!editor)
+ {
+ const char *command;
+ if (!(command = getenv ("VISUAL"))
+ && !(command = getenv ("EDITOR")))
+ command = "vi";
+
+ strv_append (&argv, command);
+ strv_append (&argv, filename);
+ return argv;
+ }
+
+ int cursor = 0;
+ char *input = CALL_ (ctx->input, get_line, &cursor);
+ hard_assert (cursor >= 0);
+
+ mbstate_t ps;
+ memset (&ps, 0, sizeof ps);
+
+ wchar_t wch;
+ size_t len, processed = 0, line_one_based = 1, column = 0;
+ while (processed < (size_t) cursor
+ && (len = mbrtowc (&wch, input + processed, cursor - processed, &ps))
+ && len != (size_t) -2 && len != (size_t) -1)
+ {
+ // Both VIM and Emacs use the caret notation with columns.
+ // Consciously leaving tabs broken, they're too difficult to handle.
+ int width = wcwidth (wch);
+ if (width < 0)
+ width = 2;
+
+ processed += len;
+ if (wch == '\n')
+ {
+ line_one_based++;
+ column = 0;
+ }
+ else
+ column += width;
+ }
+ free (input);
+
+ // Trivially split the command on spaces and substitute our values
+ struct str argument = str_make ();
+ for (; *editor; editor++)
+ {
+ if (*editor == ' ')
+ {
+ if (argument.len)
+ {
+ strv_append_owned (&argv, str_steal (&argument));
+ argument = str_make ();
+ }
+ continue;
+ }
+ if (*editor != '%' || !editor[1])
+ {
+ str_append_c (&argument, *editor);
+ continue;
+ }
+
+ // None of them are zero-length, thus words don't get lost
+ switch (*++editor)
+ {
+ case 'F':
+ str_append (&argument, filename);
+ break;
+ case 'L':
+ str_append_printf (&argument, "%zu", line_one_based);
+ break;
+ case 'C':
+ str_append_printf (&argument, "%zu", column + 1);
+ break;
+ case 'B':
+ str_append_printf (&argument, "%d", cursor + 1);
+ break;
+ case '%':
+ case ' ':
+ str_append_c (&argument, *editor);
+ break;
+ default:
+ print_warning ("unknown substitution variable");
+ }
+ }
+ if (argument.len)
+ strv_append_owned (&argv, str_steal (&argument));
+ else
+ str_free (&argument);
+ return argv;
+}
+
static bool
on_edit_input (int count, int key, void *user_data)
{
@@ -13191,16 +13294,15 @@ on_edit_input (int count, int key, void *user_data)
if (!(filename = try_dump_input_to_file (ctx)))
return false;
- const char *command;
- if (!(command = getenv ("VISUAL"))
- && !(command = getenv ("EDITOR")))
- command = "vi";
+ struct strv argv = build_editor_command (ctx, filename);
+ if (!argv.len)
+ strv_append (&argv, "true");
hard_assert (!ctx->running_editor);
switch (spawn_helper_child (ctx))
{
case 0:
- execlp (command, command, filename, NULL);
+ execvp (argv.vector[0], argv.vector);
print_error ("%s: %s",
"Failed to launch editor", strerror (errno));
_exit (EXIT_FAILURE);
@@ -13213,6 +13315,7 @@ on_edit_input (int count, int key, void *user_data)
ctx->running_editor = true;
ctx->editor_filename = filename;
}
+ strv_free (&argv);
return true;
}