diff options
| -rw-r--r-- | xC.c | 125 | 
1 files changed, 114 insertions, 11 deletions
@@ -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;  }  | 
