Compare commits
	
		
			No commits in common. "57597bf8a2538a2d897778582aad8cccbf945432" and "83764d1e1b7df02d788c323467fa44d993703e3a" have entirely different histories.
		
	
	
		
			57597bf8a2
			...
			83764d1e1b
		
	
		
							
								
								
									
										10
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								NEWS
									
									
									
									
									
								
							| @ -1,14 +1,6 @@ | |||||||
| Unreleased | Unreleased | ||||||
| 
 | 
 | ||||||
|  * xC: all behaviour.* configuration options have been renamed to general.*, |  * xC: improved backlog helper integration capabilities | ||||||
|    with the exception of editor_command/editor, backlog_helper/pager, |  | ||||||
|    and backlog_helper_strip_formatting/pager_strip_formatting |  | ||||||
| 
 |  | ||||||
|  * xC: replaced behaviour.save_on_quit with general.autosave |  | ||||||
| 
 |  | ||||||
|  * xC: improved pager integration capabilities |  | ||||||
| 
 |  | ||||||
|  * xC: normalized editline's history behaviour, making it a viable frontend |  | ||||||
| 
 | 
 | ||||||
|  * xC: made it show WALLOPS messages, as PRIVMSG for the server buffer |  * xC: made it show WALLOPS messages, as PRIVMSG for the server buffer | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -77,6 +77,9 @@ Runtime dependencies: openssl + | |||||||
| Additionally for 'xC': curses, libffi, lua >= 5.3 (optional), | Additionally for 'xC': curses, libffi, lua >= 5.3 (optional), | ||||||
|                        readline >= 6.0 or libedit >= 2013-07-12 |                        readline >= 6.0 or libedit >= 2013-07-12 | ||||||
| 
 | 
 | ||||||
|  | Avoid libedit if you can, in general it works but at the moment history is | ||||||
|  | acting up and I have no clue about fixing it. | ||||||
|  | 
 | ||||||
|  $ git clone --recursive https://git.janouch.name/p/xK.git |  $ git clone --recursive https://git.janouch.name/p/xK.git | ||||||
|  $ mkdir xK/build |  $ mkdir xK/build | ||||||
|  $ cd xK/build |  $ cd xK/build | ||||||
| @ -158,9 +161,9 @@ black-on-white and white-on-black terminals, or anything wild in between. | |||||||
| Assuming that your build supports Lua plugins, and that you have a decent, | Assuming that your build supports Lua plugins, and that you have a decent, | ||||||
| properly set-up terminal emulator, it suffices to run: | properly set-up terminal emulator, it suffices to run: | ||||||
| 
 | 
 | ||||||
|  /set general.pager = Press Tab here and change +Gb to +Gb1d |  /set behaviour.backlog_helper = Press Tab here and change +Gb to +Gb1d | ||||||
|  /set general.date_change_line = "%a %e %b %Y" |  /set behaviour.date_change_line = "%a %e %b %Y" | ||||||
|  /set general.plugin_autoload += "fancy-prompt.lua" |  /set behaviour.plugin_autoload += "fancy-prompt.lua" | ||||||
|  /set attributes.userhost = "\x1b[38;5;109m" |  /set attributes.userhost = "\x1b[38;5;109m" | ||||||
|  /set attributes.join = "\x1b[38;5;108m" |  /set attributes.join = "\x1b[38;5;108m" | ||||||
|  /set attributes.part = "\x1b[38;5;138m" |  /set attributes.part = "\x1b[38;5;138m" | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| -- | -- | ||||||
| -- fancy-prompt.lua: the fancy multiline prompt you probably want | -- fancy-prompt.lua: the fancy multiline prompt you probably want | ||||||
| -- | -- | ||||||
| -- Copyright (c) 2016 - 2022, Přemysl Eric Janouch <p@janouch.name> | -- Copyright (c) 2016, Přemysl Eric Janouch <p@janouch.name> | ||||||
| -- | -- | ||||||
| -- Permission to use, copy, modify, and/or distribute this software for any | -- Permission to use, copy, modify, and/or distribute this software for any | ||||||
| -- purpose with or without fee is hereby granted. | -- purpose with or without fee is hereby granted. | ||||||
| @ -40,7 +40,7 @@ xC.hook_prompt (function (hook) | |||||||
| 		if buffer == current then | 		if buffer == current then | ||||||
| 			current_n = i | 			current_n = i | ||||||
| 		elseif buffer.new_messages_count ~= buffer.new_unimportant_count then | 		elseif buffer.new_messages_count ~= buffer.new_unimportant_count then | ||||||
| 			active = active .. "," | 			if active ~= "" then active = active .. "," end | ||||||
| 			if buffer.highlighted then | 			if buffer.highlighted then | ||||||
| 				active = active .. "!" | 				active = active .. "!" | ||||||
| 				bg_color = "224" | 				bg_color = "224" | ||||||
| @ -48,6 +48,7 @@ xC.hook_prompt (function (hook) | |||||||
| 			active = active .. i | 			active = active .. i | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
|  | 	if active ~= "" then active = "(" .. active .. ")" end | ||||||
| 	local x = current_n .. ":" .. current.name | 	local x = current_n .. ":" .. current.name | ||||||
| 	if chan and chan.users_len ~= 0 then | 	if chan and chan.users_len ~= 0 then | ||||||
| 		local params = "" | 		local params = "" | ||||||
| @ -55,34 +56,25 @@ xC.hook_prompt (function (hook) | |||||||
| 			params = params .. " +" .. mode .. " " .. param | 			params = params .. " +" .. mode .. " " .. param | ||||||
| 		end | 		end | ||||||
| 		local modes = chan.no_param_modes .. params:sub (3) | 		local modes = chan.no_param_modes .. params:sub (3) | ||||||
| 		if modes ~= "" then | 		if modes ~= "" then x = x .. "(+" .. modes .. ")" end | ||||||
| 			x = x .. "(+" .. modes .. ")" |  | ||||||
| 		end |  | ||||||
| 		x = x .. "{" .. chan.users_len .. "}" | 		x = x .. "{" .. chan.users_len .. "}" | ||||||
| 	end | 	end | ||||||
| 	if current.hide_unimportant then | 	if current.hide_unimportant then x = x .. "<H>" end | ||||||
| 		x = x .. "<H>" | 
 | ||||||
| 	end | 	local lines, cols = xC.get_screen_size () | ||||||
| 	if active ~= "" then | 	x = x .. " " .. active .. string.rep (" ", cols) | ||||||
| 		x = x .. " (" .. active:sub (2) .. ")" |  | ||||||
| 	end |  | ||||||
| 
 | 
 | ||||||
| 	-- Readline 7.0.003 seems to be broken and completely corrupts the prompt. | 	-- Readline 7.0.003 seems to be broken and completely corrupts the prompt. | ||||||
| 	-- However 8.0.004 seems to be fine with these, as is libedit 20191231-3.1. | 	-- However 8.0.004 seems to be fine with these, as is libedit 20191231-3.1. | ||||||
| 	--x = x:gsub("[\128-\255]", "?") | 	--x = x:gsub("[\128-\255]", "?") | ||||||
| 
 | 
 | ||||||
| 	-- Align to the terminal's width and apply formatting, including the hack. | 	-- Cut off extra characters and apply formatting, including the hack. | ||||||
| 	local lines, cols = xC.get_screen_size () | 	-- FIXME: this doesn't count with full-width or zero-width characters. | ||||||
| 	local trailing, width = " ", xC.measure (x) | 	--   We might want to export wcwidth() above term_from_utf8 somehow. | ||||||
| 	while cols > 0 and width >= cols do | 	local overflow = utf8.offset (x, cols - 1) | ||||||
| 		x = x:sub (1, utf8.offset (x, -1) - 1) | 	if overflow then x = x:sub (1, overflow) end | ||||||
| 		trailing, width = ">", xC.measure (x) |  | ||||||
| 	end |  | ||||||
| 
 |  | ||||||
| 	x = "\x01\x1b[0;4;1;38;5;16m\x1b[48;5;" .. bg_color .. "m\x02" .. | 	x = "\x01\x1b[0;4;1;38;5;16m\x1b[48;5;" .. bg_color .. "m\x02" .. | ||||||
| 		x .. string.rep (" ", cols - width - 1) .. | 		x ..  "\x01\x1b[0;4;1;7;38;5;" .. bg_color .. "m\x02 \x01\x1b[0;1m\x02" | ||||||
| 		"\x01\x1b[0;4;1;7;38;5;" .. bg_color .. "m\x02" .. |  | ||||||
| 		trailing .. "\x01\x1b[0;1m\x02" |  | ||||||
| 
 | 
 | ||||||
| 	local user_prefix = function (chan, user) | 	local user_prefix = function (chan, user) | ||||||
| 		for i, chan_user in ipairs (chan.users) do | 		for i, chan_user in ipairs (chan.users) do | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								xC.adoc
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								xC.adoc
									
									
									
									
									
								
							| @ -105,7 +105,7 @@ _~/.config/xC/xC.conf_:: | |||||||
| 	as the */set* command, to make changes in it. | 	as the */set* command, to make changes in it. | ||||||
| 
 | 
 | ||||||
| _~/.local/share/xC/logs/_:: | _~/.local/share/xC/logs/_:: | ||||||
| 	When enabled by *general.logging*, log files are stored here. | 	When enabled by *behaviour.logging*, log files are stored here. | ||||||
| 
 | 
 | ||||||
| _~/.local/share/xC/plugins/_:: | _~/.local/share/xC/plugins/_:: | ||||||
| _/usr/local/share/xC/plugins/_:: | _/usr/local/share/xC/plugins/_:: | ||||||
| @ -114,7 +114,8 @@ _/usr/share/xC/plugins/_:: | |||||||
| 
 | 
 | ||||||
| Bugs | Bugs | ||||||
| ---- | ---- | ||||||
| The editline (libedit) frontend may exhibit some unexpected behaviour. | The editline (libedit) frontend is more of a proof of concept that mostly seems | ||||||
|  | to work but exhibits bugs that are not our fault. | ||||||
| 
 | 
 | ||||||
| Reporting bugs | Reporting bugs | ||||||
| -------------- | -------------- | ||||||
|  | |||||||
							
								
								
									
										334
									
								
								xC.c
									
									
									
									
									
								
							
							
						
						
									
										334
									
								
								xC.c
									
									
									
									
									
								
							| @ -259,7 +259,7 @@ struct input_vtable | |||||||
| 	XX (get_line) XX (clear_line) XX (insert)                                  \ | 	XX (get_line) XX (clear_line) XX (insert)                                  \ | ||||||
| 	XX (on_tty_resized) XX (on_tty_readable) | 	XX (on_tty_resized) XX (on_tty_readable) | ||||||
| 
 | 
 | ||||||
| // ~~~ GNU Readline ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // --- GNU Readline ------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| #ifdef HAVE_READLINE | #ifdef HAVE_READLINE | ||||||
| 
 | 
 | ||||||
| @ -728,7 +728,7 @@ input_rl_new (void) | |||||||
| #define input_new input_rl_new | #define input_new input_rl_new | ||||||
| #endif // HAVE_READLINE
 | #endif // HAVE_READLINE
 | ||||||
| 
 | 
 | ||||||
| // ~~~ BSD Editline ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // --- BSD Editline ------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| #ifdef HAVE_EDITLINE | #ifdef HAVE_EDITLINE | ||||||
| 
 | 
 | ||||||
| @ -761,7 +761,6 @@ struct input_el | |||||||
| { | { | ||||||
| 	struct input super;                 ///< Parent class
 | 	struct input super;                 ///< Parent class
 | ||||||
| 	EditLine *editline;                 ///< The EditLine object
 | 	EditLine *editline;                 ///< The EditLine object
 | ||||||
| 	FILE *null;                         ///< Output redirect
 |  | ||||||
| 
 | 
 | ||||||
| 	bool active;                        ///< Are we a thing?
 | 	bool active;                        ///< Are we a thing?
 | ||||||
| 	char *prompt;                       ///< The prompt we use
 | 	char *prompt;                       ///< The prompt we use
 | ||||||
| @ -779,12 +778,12 @@ input_el__redisplay (void *input) | |||||||
| 	// See rl_redisplay(), however NetBSD editline's map.c v1.54 breaks VREPRINT
 | 	// See rl_redisplay(), however NetBSD editline's map.c v1.54 breaks VREPRINT
 | ||||||
| 	// so we bind redisplay somewhere else in app_editline_init()
 | 	// so we bind redisplay somewhere else in app_editline_init()
 | ||||||
| 	struct input_el *self = input; | 	struct input_el *self = input; | ||||||
| 	wchar_t x[] = { L'q' & 31, 0 }; | 	char x[] = { 'q' & 31, 0 }; | ||||||
| 	el_wpush (self->editline, x); | 	el_push (self->editline, x); | ||||||
| 
 | 
 | ||||||
| 	// We have to do this or it gets stuck and nothing is done
 | 	// We have to do this or it gets stuck and nothing is done
 | ||||||
| 	int dummy_count = 0; | 	int count = 0; | ||||||
| 	(void) el_wgets (self->editline, &dummy_count); | 	(void) el_wgets (self->editline, &count); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static char * | static char * | ||||||
| @ -1027,50 +1026,18 @@ input_el__restore (struct input_el *self) | |||||||
| 
 | 
 | ||||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| // XXX: Editline keeping its own history position (look for "eventno" there).
 |  | ||||||
| //   Invoking ed-next-history through our bind from app_editline_init() seems
 |  | ||||||
| //   like the only viable hack to get consistent history behaviour.
 |  | ||||||
| static void |  | ||||||
| input_el__bottom (struct input_el *self) |  | ||||||
| { |  | ||||||
| 	// First, we need to redirect output to avoid ringing the terminal bell.
 |  | ||||||
| 	FILE *out = NULL; |  | ||||||
| 	el_wget (self->editline, EL_GETFP, 1, &out); |  | ||||||
| 	el_wset (self->editline, EL_SETFP, 1, self->null); |  | ||||||
| 
 |  | ||||||
| 	// Invoke hist_get() to make the history pointer's cursor match "eventno".
 |  | ||||||
| 	int down = 1, dummy_count = 0; |  | ||||||
| 	el_wpush (self->editline, L"\x1bn"); |  | ||||||
| 	(void) el_wgets (self->editline, &dummy_count); |  | ||||||
| 
 |  | ||||||
| 	// It doesn't seem like we can just retrieve the position.
 |  | ||||||
| 	HistEventW ev; |  | ||||||
| 	while (!history_w (self->current->history, &ev, H_PREV)) |  | ||||||
| 		down++; |  | ||||||
| 	while (down--) |  | ||||||
| 	{ |  | ||||||
| 		el_wpush (self->editline, L"\x1bn"); |  | ||||||
| 		(void) el_wgets (self->editline, &dummy_count); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	el_wset (self->editline, EL_SETFP, 1, out); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void | static void | ||||||
| input_el_buffer_switch (void *input, input_buffer_t input_buffer) | input_el_buffer_switch (void *input, input_buffer_t input_buffer) | ||||||
| { | { | ||||||
| 	struct input_el *self = input; | 	struct input_el *self = input; | ||||||
| 	struct input_el_buffer *buffer = input_buffer; | 	struct input_el_buffer *buffer = input_buffer; | ||||||
| 	if (!self->active) |  | ||||||
| 		return; |  | ||||||
| 
 | 
 | ||||||
| 	if (self->current) | 	if (self->current) | ||||||
| 		input_el__save_buffer (self, self->current); | 		input_el__save_buffer (self, self->current); | ||||||
| 
 | 
 | ||||||
| 	self->current = buffer; |  | ||||||
| 	el_wset (self->editline, EL_HIST, history, buffer->history); |  | ||||||
| 	input_el__bottom (self); |  | ||||||
| 	input_el__restore_buffer (self, buffer); | 	input_el__restore_buffer (self, buffer); | ||||||
|  | 	el_wset (self->editline, EL_HIST, history, buffer->history); | ||||||
|  | 	self->current = buffer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -1144,7 +1111,7 @@ input_el_on_tty_readable (void *input) | |||||||
| 	if (!buf || count-- <= 0) | 	if (!buf || count-- <= 0) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	if (count == 0 && buf[0] == ('D' - 0x40) /* hardcoded VEOF in el_wgets() */) | 	if (count == 0 && buf[0] == ('D' - 0x40) /* hardcoded VEOF in editline */) | ||||||
| 	{ | 	{ | ||||||
| 		el_deletestr (self->editline, 1); | 		el_deletestr (self->editline, 1); | ||||||
| 		input_el__redisplay (self); | 		input_el__redisplay (self); | ||||||
| @ -1164,7 +1131,6 @@ input_el_destroy (void *input) | |||||||
| 		free (iter->help); | 		free (iter->help); | ||||||
| 		ffi_closure_free (iter); | 		ffi_closure_free (iter); | ||||||
| 	} | 	} | ||||||
| 	fclose (self->null); |  | ||||||
| 	free (self->prompt); | 	free (self->prompt); | ||||||
| 	free (self); | 	free (self); | ||||||
| } | } | ||||||
| @ -1178,7 +1144,6 @@ input_el_new (void) | |||||||
| { | { | ||||||
| 	struct input_el *self = xcalloc (1, sizeof *self); | 	struct input_el *self = xcalloc (1, sizeof *self); | ||||||
| 	self->super.vtable = &input_el_vtable; | 	self->super.vtable = &input_el_vtable; | ||||||
| 	self->null = fopen ("/dev/null", "w"); |  | ||||||
| 	return &self->super; | 	return &self->super; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1195,7 +1160,7 @@ input_el_new (void) | |||||||
| //
 | //
 | ||||||
| // The only exception is IRC identifiers.
 | // The only exception is IRC identifiers.
 | ||||||
| 
 | 
 | ||||||
| // ~~~ Scripting support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| // We need a few reference countable objects with support for both strong
 | // We need a few reference countable objects with support for both strong
 | ||||||
| // and weak references (mainly used for scripted plugins).
 | // and weak references (mainly used for scripted plugins).
 | ||||||
| @ -1307,7 +1272,7 @@ struct ispect_field | |||||||
| 	{ #field, offsetof (struct object, field), ISPECT_STR_MAP,                 \ | 	{ #field, offsetof (struct object, field), ISPECT_STR_MAP,                 \ | ||||||
| 	  ISPECT_REF, g_##ref_type##_ispect, is_list }, | 	  ISPECT_REF, g_##ref_type##_ispect, is_list }, | ||||||
| 
 | 
 | ||||||
| // ~~~ Chat ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| struct user_channel | struct user_channel | ||||||
| { | { | ||||||
| @ -1466,7 +1431,7 @@ channel_destroy (struct channel *self) | |||||||
| 
 | 
 | ||||||
| REF_COUNTABLE_METHODS (channel) | REF_COUNTABLE_METHODS (channel) | ||||||
| 
 | 
 | ||||||
| // ~~~ Buffers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| enum formatter_item_type | enum formatter_item_type | ||||||
| { | { | ||||||
| @ -1479,21 +1444,10 @@ enum formatter_item_type | |||||||
| 	FORMATTER_ITEM_IGNORE_ATTR          ///< Un/set attribute ignoration
 | 	FORMATTER_ITEM_IGNORE_ATTR          ///< Un/set attribute ignoration
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum |  | ||||||
| { |  | ||||||
| 	TEXT_BOLD        = 1 << 0, |  | ||||||
| 	TEXT_ITALIC      = 1 << 1, |  | ||||||
| 	TEXT_UNDERLINE   = 1 << 2, |  | ||||||
| 	TEXT_INVERSE     = 1 << 3, |  | ||||||
| 	TEXT_BLINK       = 1 << 4, |  | ||||||
| 	TEXT_CROSSED_OUT = 1 << 5, |  | ||||||
| 	TEXT_MONOSPACE   = 1 << 6 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct formatter_item | struct formatter_item | ||||||
| { | { | ||||||
| 	enum formatter_item_type type : 16; ///< Type of this item
 | 	enum formatter_item_type type : 16; ///< Type of this item
 | ||||||
| 	int attribute                 : 16; ///< Attribute ID or a TEXT_* mask
 | 	int attribute                 : 16; ///< Attribute ID
 | ||||||
| 	int color;                          ///< Colour
 | 	int color;                          ///< Colour
 | ||||||
| 	char *text;                         ///< String
 | 	char *text;                         ///< String
 | ||||||
| }; | }; | ||||||
| @ -1673,7 +1627,7 @@ buffer_destroy (struct buffer *self) | |||||||
| REF_COUNTABLE_METHODS (buffer) | REF_COUNTABLE_METHODS (buffer) | ||||||
| #define buffer_ref do_not_use_dangerous | #define buffer_ref do_not_use_dangerous | ||||||
| 
 | 
 | ||||||
| // ~~~ Server ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| // The only real purpose of this is to abstract away TLS
 | // The only real purpose of this is to abstract away TLS
 | ||||||
| struct transport | struct transport | ||||||
| @ -1949,7 +1903,7 @@ server_destroy (struct server *self) | |||||||
| REF_COUNTABLE_METHODS (server) | REF_COUNTABLE_METHODS (server) | ||||||
| #define server_ref do_not_use_dangerous | #define server_ref do_not_use_dangerous | ||||||
| 
 | 
 | ||||||
| // ~~~ Scripting ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| struct plugin | struct plugin | ||||||
| { | { | ||||||
| @ -2068,7 +2022,7 @@ struct completion_hook | |||||||
| 		struct completion *data, const char *word, struct strv *output); | 		struct completion *data, const char *word, struct strv *output); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // ~~~ Main context ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| struct app_context | struct app_context | ||||||
| { | { | ||||||
| @ -2131,7 +2085,7 @@ struct app_context | |||||||
| 	bool in_bracketed_paste;            ///< User is pasting some content
 | 	bool in_bracketed_paste;            ///< User is pasting some content
 | ||||||
| 	struct str input_buffer;            ///< Buffered pasted content
 | 	struct str input_buffer;            ///< Buffered pasted content
 | ||||||
| 
 | 
 | ||||||
| 	bool running_pager;                 ///< Running a pager for buffer history
 | 	bool running_backlog_helper;        ///< Running a backlog helper
 | ||||||
| 	bool running_editor;                ///< Running editor for the input
 | 	bool running_editor;                ///< Running editor for the input
 | ||||||
| 	char *editor_filename;              ///< The file being edited by user
 | 	char *editor_filename;              ///< The file being edited by user
 | ||||||
| 	int terminal_suspended;             ///< Terminal suspension level
 | 	int terminal_suspended;             ///< Terminal suspension level
 | ||||||
| @ -2466,65 +2420,18 @@ static struct config_schema g_config_server[] = | |||||||
| 	{} | 	{} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct config_schema g_config_general[] = | static struct config_schema g_config_behaviour[] = | ||||||
| { | { | ||||||
| 	{ .name      = "autosave", |  | ||||||
| 	  .comment   = "Save configuration automatically after each change", |  | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, |  | ||||||
| 	  .default_  = "on" }, |  | ||||||
| 	{ .name      = "debug_mode", |  | ||||||
| 	  .comment   = "Produce some debugging output", |  | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, |  | ||||||
| 	  .default_  = "off", |  | ||||||
| 	  .on_change = on_config_debug_mode_change }, |  | ||||||
| 	{ .name      = "logging", |  | ||||||
| 	  .comment   = "Log buffer contents to file", |  | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, |  | ||||||
| 	  .default_  = "off", |  | ||||||
| 	  .on_change = on_config_logging_change }, |  | ||||||
| 	{ .name      = "plugin_autoload", |  | ||||||
| 	  .comment   = "Plugins to automatically load on start", |  | ||||||
| 	  .type      = CONFIG_ITEM_STRING_ARRAY, |  | ||||||
| 	  .validate  = config_validate_nonjunk_string }, |  | ||||||
| 
 |  | ||||||
| 	// Buffer history:
 |  | ||||||
| 	{ .name      = "backlog_limit", |  | ||||||
| 	  .comment   = "Maximum number of lines stored in the backlog", |  | ||||||
| 	  .type      = CONFIG_ITEM_INTEGER, |  | ||||||
| 	  .validate  = config_validate_nonnegative, |  | ||||||
| 	  .default_  = "1000", |  | ||||||
| 	  .on_change = on_config_backlog_limit_change }, |  | ||||||
| 	{ .name      = "pager", |  | ||||||
| 	  .comment   = "Shell command to page buffer history (args: name [path])", |  | ||||||
| 	  .type      = CONFIG_ITEM_STRING, |  | ||||||
| 	  .default_  = "`name=$(echo \"$1\" | sed 's/[%?:.]/\\\\&/g'); " |  | ||||||
| 		"prompt='?f%F:'$name'. ?db- page %db?L of %D. .(?eEND:?PB%PB\\%..)'; " |  | ||||||
| 		"LESSSECURE=1 less +Gb -Ps\"$prompt\" \"${2:--R}\"`" }, |  | ||||||
| 	{ .name      = "pager_strip_formatting", |  | ||||||
| 	  .comment   = "Strip terminal formatting from pager input", |  | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, |  | ||||||
| 	  .default_  = "off" }, |  | ||||||
| 
 |  | ||||||
| 	// Output adjustments:
 |  | ||||||
| 	{ .name      = "beep_on_highlight", |  | ||||||
| 	  .comment   = "Ring the bell when highlighted or on a new invisible PM", |  | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, |  | ||||||
| 	  .default_  = "on", |  | ||||||
| 	  .on_change = on_config_beep_on_highlight_change }, |  | ||||||
| 	{ .name      = "date_change_line", |  | ||||||
| 	  .comment   = "Input to strftime(3) for the date change line", |  | ||||||
| 	  .type      = CONFIG_ITEM_STRING, |  | ||||||
| 	  .default_  = "\"%F\"" }, |  | ||||||
| 	{ .name      = "isolate_buffers", | 	{ .name      = "isolate_buffers", | ||||||
| 	  .comment   = "Don't leak messages from the server and global buffers", | 	  .comment   = "Don't leak messages from the server and global buffers", | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
| 	  .default_  = "off", | 	  .default_  = "off", | ||||||
| 	  .on_change = on_config_isolate_buffers_change }, | 	  .on_change = on_config_isolate_buffers_change }, | ||||||
| 	{ .name      = "read_marker_char", | 	{ .name      = "beep_on_highlight", | ||||||
| 	  .comment   = "The character to use for the read marker line", | 	  .comment   = "Beep when highlighted or on a new invisible PM", | ||||||
| 	  .type      = CONFIG_ITEM_STRING, | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
| 	  .default_  = "\"-\"", | 	  .default_  = "on", | ||||||
| 	  .validate  = config_validate_nonjunk_string }, | 	  .on_change = on_config_beep_on_highlight_change }, | ||||||
| 	{ .name      = "show_all_prefixes", | 	{ .name      = "show_all_prefixes", | ||||||
| 	  .comment   = "Show all prefixes in front of nicknames", | 	  .comment   = "Show all prefixes in front of nicknames", | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
| @ -2535,9 +2442,7 @@ static struct config_schema g_config_general[] = | |||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
| 	  .default_  = "on", | 	  .default_  = "on", | ||||||
| 	  .on_change = on_config_word_wrapping_change }, | 	  .on_change = on_config_word_wrapping_change }, | ||||||
| 
 | 	{ .name      = "editor_command", | ||||||
| 	// User input:
 |  | ||||||
| 	{ .name      = "editor", |  | ||||||
| 	  .comment   = "VIM: \"vim +%Bgo %F\", Emacs: \"emacs -nw +%L:%C %F\", " | 	  .comment   = "VIM: \"vim +%Bgo %F\", Emacs: \"emacs -nw +%L:%C %F\", " | ||||||
| 		"nano/micro/kakoune: \"nano/micro/kak +%L:%C %F\"", | 		"nano/micro/kakoune: \"nano/micro/kak +%L:%C %F\"", | ||||||
| 	  .type      = CONFIG_ITEM_STRING }, | 	  .type      = CONFIG_ITEM_STRING }, | ||||||
| @ -2545,8 +2450,58 @@ static struct config_schema g_config_general[] = | |||||||
| 	  .comment   = "Normalize newlines and quote the command prefix in pastes", | 	  .comment   = "Normalize newlines and quote the command prefix in pastes", | ||||||
| 	  .type      = CONFIG_ITEM_BOOLEAN, | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
| 	  .default_  = "on" }, | 	  .default_  = "on" }, | ||||||
|  | 	{ .name      = "date_change_line", | ||||||
|  | 	  .comment   = "Input to strftime(3) for the date change line", | ||||||
|  | 	  .type      = CONFIG_ITEM_STRING, | ||||||
|  | 	  .default_  = "\"%F\"" }, | ||||||
|  | 	{ .name      = "read_marker_char", | ||||||
|  | 	  .comment   = "The character to use for the read marker line", | ||||||
|  | 	  .type      = CONFIG_ITEM_STRING, | ||||||
|  | 	  .default_  = "\"-\"", | ||||||
|  | 	  .validate  = config_validate_nonjunk_string }, | ||||||
|  | 	{ .name      = "logging", | ||||||
|  | 	  .comment   = "Log buffer contents to file", | ||||||
|  | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
|  | 	  .default_  = "off", | ||||||
|  | 	  .on_change = on_config_logging_change }, | ||||||
|  | 	{ .name      = "save_on_quit", | ||||||
|  | 	  .comment   = "Save configuration before quitting", | ||||||
|  | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
|  | 	  .default_  = "on" }, | ||||||
|  | 	{ .name      = "debug_mode", | ||||||
|  | 	  .comment   = "Produce some debugging output", | ||||||
|  | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
|  | 	  .default_  = "off", | ||||||
|  | 	  .on_change = on_config_debug_mode_change }, | ||||||
|  | 
 | ||||||
|  | 	{ .name      = "backlog_limit", | ||||||
|  | 	  .comment   = "Maximum number of lines stored in the backlog", | ||||||
|  | 	  .type      = CONFIG_ITEM_INTEGER, | ||||||
|  | 	  .validate  = config_validate_nonnegative, | ||||||
|  | 	  .default_  = "1000", | ||||||
|  | 	  .on_change = on_config_backlog_limit_change }, | ||||||
|  | 	{ .name      = "backlog_helper", | ||||||
|  | 	  .comment   = "Shell command to page buffer history (args: name [path])", | ||||||
|  | 	  .type      = CONFIG_ITEM_STRING, | ||||||
|  | 	  .default_  = "`name=$(echo \"$1\" | sed 's/[%?:.]/\\\\&/g'); " | ||||||
|  | 		"prompt='?f%F:'$name'. ?db- page %db?L of %D. .(?eEND:?PB%PB\\%..)'; " | ||||||
|  | 		"LESSSECURE=1 less +Gb -Ps\"$prompt\" \"${2:--R}\"`" }, | ||||||
|  | 	{ .name      = "backlog_helper_strip_formatting", | ||||||
|  | 	  .comment   = "Strip formatting from backlog helper input", | ||||||
|  | 	  .type      = CONFIG_ITEM_BOOLEAN, | ||||||
|  | 	  .default_  = "off" }, | ||||||
|  | 
 | ||||||
|  | 	{ .name      = "reconnect_delay_growing", | ||||||
|  | 	  .comment   = "Growing factor for reconnect delay", | ||||||
|  | 	  .type      = CONFIG_ITEM_INTEGER, | ||||||
|  | 	  .validate  = config_validate_nonnegative, | ||||||
|  | 	  .default_  = "2" }, | ||||||
|  | 	{ .name      = "reconnect_delay_max", | ||||||
|  | 	  .comment   = "Maximum reconnect delay in seconds", | ||||||
|  | 	  .type      = CONFIG_ITEM_INTEGER, | ||||||
|  | 	  .validate  = config_validate_nonnegative, | ||||||
|  | 	  .default_  = "600" }, | ||||||
| 
 | 
 | ||||||
| 	// Pan-server configuration:
 |  | ||||||
| 	{ .name      = "autoaway_message", | 	{ .name      = "autoaway_message", | ||||||
| 	  .comment   = "Automated away message", | 	  .comment   = "Automated away message", | ||||||
| 	  .type      = CONFIG_ITEM_STRING, | 	  .type      = CONFIG_ITEM_STRING, | ||||||
| @ -2556,16 +2511,11 @@ static struct config_schema g_config_general[] = | |||||||
| 	  .type      = CONFIG_ITEM_INTEGER, | 	  .type      = CONFIG_ITEM_INTEGER, | ||||||
| 	  .validate  = config_validate_nonnegative, | 	  .validate  = config_validate_nonnegative, | ||||||
| 	  .default_  = "1800" }, | 	  .default_  = "1800" }, | ||||||
| 	{ .name      = "reconnect_delay_growing", | 
 | ||||||
| 	  .comment   = "Growth factor for the reconnect delay", | 	{ .name      = "plugin_autoload", | ||||||
| 	  .type      = CONFIG_ITEM_INTEGER, | 	  .comment   = "Plugins to automatically load on start", | ||||||
| 	  .validate  = config_validate_nonnegative, | 	  .type      = CONFIG_ITEM_STRING_ARRAY, | ||||||
| 	  .default_  = "2" }, | 	  .validate  = config_validate_nonjunk_string }, | ||||||
| 	{ .name      = "reconnect_delay_max", |  | ||||||
| 	  .comment   = "Maximum reconnect delay in seconds", |  | ||||||
| 	  .type      = CONFIG_ITEM_INTEGER, |  | ||||||
| 	  .validate  = config_validate_nonnegative, |  | ||||||
| 	  .default_  = "600" }, |  | ||||||
| 	{} | 	{} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -2581,9 +2531,9 @@ static struct config_schema g_config_attributes[] = | |||||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| load_config_general (struct config_item *subtree, void *user_data) | load_config_behaviour (struct config_item *subtree, void *user_data) | ||||||
| { | { | ||||||
| 	config_schema_apply_to_object (g_config_general, subtree, user_data); | 	config_schema_apply_to_object (g_config_behaviour,  subtree, user_data); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -2600,7 +2550,7 @@ register_config_modules (struct app_context *ctx) | |||||||
| 	config_register_module (config, "servers",    NULL, NULL); | 	config_register_module (config, "servers",    NULL, NULL); | ||||||
| 	config_register_module (config, "aliases",    NULL, NULL); | 	config_register_module (config, "aliases",    NULL, NULL); | ||||||
| 	config_register_module (config, "plugins",    NULL, NULL); | 	config_register_module (config, "plugins",    NULL, NULL); | ||||||
| 	config_register_module (config, "general",    load_config_general,    ctx); | 	config_register_module (config, "behaviour",  load_config_behaviour,  ctx); | ||||||
| 	config_register_module (config, "attributes", load_config_attributes, ctx); | 	config_register_module (config, "attributes", load_config_attributes, ctx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -2835,13 +2785,24 @@ init_colors (struct app_context *ctx) | |||||||
| 		(config_item_get (ctx->config.root, "attributes", NULL)); | 		(config_item_get (ctx->config.root, "attributes", NULL)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ~~~ Attribute printer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| // A little tool that tries to make the most of the terminal's capabilities
 | // A little tool that tries to make the most of the terminal's capabilities
 | ||||||
| // to set up text attributes.  It mostly targets just terminal emulators as that
 | // to set up text attributes.  It mostly targets just terminal emulators as that
 | ||||||
| // is what people are using these days.  At least no stupid ncurses limits us
 | // is what people are using these days.  At least no stupid ncurses limits us
 | ||||||
| // with colour pairs.
 | // with colour pairs.
 | ||||||
| 
 | 
 | ||||||
|  | enum | ||||||
|  | { | ||||||
|  | 	TEXT_BOLD        = 1 << 0, | ||||||
|  | 	TEXT_ITALIC      = 1 << 1, | ||||||
|  | 	TEXT_UNDERLINE   = 1 << 2, | ||||||
|  | 	TEXT_INVERSE     = 1 << 3, | ||||||
|  | 	TEXT_BLINK       = 1 << 4, | ||||||
|  | 	TEXT_CROSSED_OUT = 1 << 5, | ||||||
|  | 	TEXT_MONOSPACE   = 1 << 6 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| struct attr_printer | struct attr_printer | ||||||
| { | { | ||||||
| 	char **attrs;                       ///< Named attributes
 | 	char **attrs;                       ///< Named attributes
 | ||||||
| @ -3815,7 +3776,7 @@ explode_text (struct exploder *self, const char *text) | |||||||
| 		if (!strchr ("\a\b\x0e\x0f\x1b" /* BEL BS SO SI ESC */, *p)) | 		if (!strchr ("\a\b\x0e\x0f\x1b" /* BEL BS SO SI ESC */, *p)) | ||||||
| 			str_append_c (&filtered, *p); | 			str_append_c (&filtered, *p); | ||||||
| 
 | 
 | ||||||
| 	size_t term_len = 0, processed = 0, len; | 	size_t term_len = 0; | ||||||
| 	char *term = iconv_xstrdup (self->ctx->term_from_utf8, | 	char *term = iconv_xstrdup (self->ctx->term_from_utf8, | ||||||
| 		filtered.str, filtered.len + 1, &term_len); | 		filtered.str, filtered.len + 1, &term_len); | ||||||
| 	str_free (&filtered); | 	str_free (&filtered); | ||||||
| @ -3824,10 +3785,11 @@ explode_text (struct exploder *self, const char *text) | |||||||
| 	memset (&ps, 0, sizeof ps); | 	memset (&ps, 0, sizeof ps); | ||||||
| 
 | 
 | ||||||
| 	wchar_t wch; | 	wchar_t wch; | ||||||
|  | 	size_t len, processed = 0; | ||||||
| 	while ((len = mbrtowc (&wch, term + processed, term_len - processed, &ps))) | 	while ((len = mbrtowc (&wch, term + processed, term_len - processed, &ps))) | ||||||
| 	{ | 	{ | ||||||
| 		hard_assert (len != (size_t) -2 && len != (size_t) -1); | 		hard_assert (len != (size_t) -2 && len != (size_t) -1); | ||||||
| 		hard_assert ((processed += len) <= term_len); | 		processed += len; | ||||||
| 
 | 
 | ||||||
| 		struct line_char *c = line_char_new (wch); | 		struct line_char *c = line_char_new (wch); | ||||||
| 		c->attrs = self->attrs; | 		c->attrs = self->attrs; | ||||||
| @ -3962,7 +3924,7 @@ buffer_update_time (struct app_context *ctx, time_t now, FILE *stream, | |||||||
| 
 | 
 | ||||||
| 	char buf[64] = ""; | 	char buf[64] = ""; | ||||||
| 	const char *format = | 	const char *format = | ||||||
| 		get_config_string (ctx->config.root, "general.date_change_line"); | 		get_config_string (ctx->config.root, "behaviour.date_change_line"); | ||||||
| 	if (!strftime (buf, sizeof buf, format, ¤t)) | 	if (!strftime (buf, sizeof buf, format, ¤t)) | ||||||
| 	{ | 	{ | ||||||
| 		print_error ("%s: %s", "strftime", strerror (errno)); | 		print_error ("%s: %s", "strftime", strerror (errno)); | ||||||
| @ -4339,8 +4301,8 @@ buffer_print_read_marker (struct app_context *ctx, FILE *stream, int flush_opts) | |||||||
| { | { | ||||||
| 	struct formatter f = formatter_make (ctx, NULL); | 	struct formatter f = formatter_make (ctx, NULL); | ||||||
| 	const int timestamp_width = 8;  // hardcoded to %T right now, simple
 | 	const int timestamp_width = 8;  // hardcoded to %T right now, simple
 | ||||||
| 	const char *marker_char = | 	const char *marker_char = get_config_string (ctx->config.root, | ||||||
| 		get_config_string (ctx->config.root, "general.read_marker_char"); | 		"behaviour.read_marker_char"); | ||||||
| 
 | 
 | ||||||
| 	// We could turn this off on FLUSH_OPT_NOWRAP, however our default pager
 | 	// We could turn this off on FLUSH_OPT_NOWRAP, however our default pager
 | ||||||
| 	// wraps lines for us even if we don't do it ourselves, and thus there's
 | 	// wraps lines for us even if we don't do it ourselves, and thus there's
 | ||||||
| @ -4950,8 +4912,8 @@ static int64_t | |||||||
| irc_get_reconnect_delay (struct server *s) | irc_get_reconnect_delay (struct server *s) | ||||||
| { | { | ||||||
| 	int64_t delay = get_config_integer (s->config, "reconnect_delay"); | 	int64_t delay = get_config_integer (s->config, "reconnect_delay"); | ||||||
| 	int64_t delay_factor = get_config_integer | 	int64_t delay_factor = get_config_integer (s->ctx->config.root, | ||||||
| 		(s->ctx->config.root, "general.reconnect_delay_growing"); | 		"behaviour.reconnect_delay_growing"); | ||||||
| 	for (unsigned i = 0; i < s->reconnect_attempt; i++) | 	for (unsigned i = 0; i < s->reconnect_attempt; i++) | ||||||
| 	{ | 	{ | ||||||
| 		if (delay_factor && delay > INT64_MAX / delay_factor) | 		if (delay_factor && delay > INT64_MAX / delay_factor) | ||||||
| @ -4959,8 +4921,8 @@ irc_get_reconnect_delay (struct server *s) | |||||||
| 		delay *= delay_factor; | 		delay *= delay_factor; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	int64_t delay_max = | 	int64_t delay_max = get_config_integer (s->ctx->config.root, | ||||||
| 		get_config_integer (s->ctx->config.root, "general.reconnect_delay_max"); | 		"behaviour.reconnect_delay_max"); | ||||||
| 	return MIN (delay, delay_max); | 	return MIN (delay, delay_max); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -10565,33 +10527,6 @@ lua_plugin_get_screen_size (lua_State *L) | |||||||
| 	return 2; | 	return 2; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int |  | ||||||
| lua_plugin_measure (lua_State *L) |  | ||||||
| { |  | ||||||
| 	struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1)); |  | ||||||
| 	const char *line = lua_plugin_check_utf8 (L, 1); |  | ||||||
| 
 |  | ||||||
| 	size_t term_len = 0, processed = 0, width = 0, len; |  | ||||||
| 	char *term = iconv_xstrdup (plugin->ctx->term_from_utf8, |  | ||||||
| 		(char *) line, strlen (line) + 1, &term_len); |  | ||||||
| 
 |  | ||||||
| 	mbstate_t ps; |  | ||||||
| 	memset (&ps, 0, sizeof ps); |  | ||||||
| 
 |  | ||||||
| 	wchar_t wch; |  | ||||||
| 	while ((len = mbrtowc (&wch, term + processed, term_len - processed, &ps))) |  | ||||||
| 	{ |  | ||||||
| 		hard_assert (len != (size_t) -2 && len != (size_t) -1); |  | ||||||
| 		hard_assert ((processed += len) <= term_len); |  | ||||||
| 
 |  | ||||||
| 		int wch_width = wcwidth (wch); |  | ||||||
| 		width += MAX (0, wch_width); |  | ||||||
| 	} |  | ||||||
| 	free (term); |  | ||||||
| 	lua_pushinteger (L, width); |  | ||||||
| 	return 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int | static int | ||||||
| lua_ctx_gc (lua_State *L) | lua_ctx_gc (lua_State *L) | ||||||
| { | { | ||||||
| @ -10612,7 +10547,6 @@ static luaL_Reg lua_plugin_library[] = | |||||||
| 	// And these are methods:
 | 	// And these are methods:
 | ||||||
| 
 | 
 | ||||||
| 	{ "get_screen_size", lua_plugin_get_screen_size }, | 	{ "get_screen_size", lua_plugin_get_screen_size }, | ||||||
| 	{ "measure",         lua_plugin_measure         }, |  | ||||||
| 	{ "__gc",            lua_ctx_gc                 }, | 	{ "__gc",            lua_ctx_gc                 }, | ||||||
| 	{ NULL,              NULL                       }, | 	{ NULL,              NULL                       }, | ||||||
| }; | }; | ||||||
| @ -11031,8 +10965,8 @@ plugin_unload (struct app_context *ctx, const char *name) | |||||||
| static void | static void | ||||||
| load_plugins (struct app_context *ctx) | load_plugins (struct app_context *ctx) | ||||||
| { | { | ||||||
| 	const char *plugins = | 	const char *plugins = get_config_string | ||||||
| 		get_config_string (ctx->config.root, "general.plugin_autoload"); | 		(ctx->config.root, "behaviour.plugin_autoload"); | ||||||
| 	if (plugins) | 	if (plugins) | ||||||
| 	{ | 	{ | ||||||
| 		struct strv v = strv_make (); | 		struct strv v = strv_make (); | ||||||
| @ -11398,8 +11332,6 @@ static bool | |||||||
| handle_command_set_assign | handle_command_set_assign | ||||||
| 	(struct app_context *ctx, struct strv *all, char *arguments) | 	(struct app_context *ctx, struct strv *all, char *arguments) | ||||||
| { | { | ||||||
| 	hard_assert (all->len > 0); |  | ||||||
| 
 |  | ||||||
| 	char *op = cut_word (&arguments); | 	char *op = cut_word (&arguments); | ||||||
| 	bool add = false; | 	bool add = false; | ||||||
| 	bool remove = false; | 	bool remove = false; | ||||||
| @ -11434,9 +11366,6 @@ handle_command_set_assign | |||||||
| 		free (key); | 		free (key); | ||||||
| 	} | 	} | ||||||
| 	config_item_destroy (new_); | 	config_item_destroy (new_); | ||||||
| 
 |  | ||||||
| 	if (get_config_boolean (ctx->config.root, "general.autosave")) |  | ||||||
| 		save_configuration (ctx); |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -13277,7 +13206,8 @@ static struct strv | |||||||
| build_editor_command (struct app_context *ctx, const char *filename) | build_editor_command (struct app_context *ctx, const char *filename) | ||||||
| { | { | ||||||
| 	struct strv argv = strv_make (); | 	struct strv argv = strv_make (); | ||||||
| 	const char *editor = get_config_string (ctx->config.root, "general.editor"); | 	const char *editor = get_config_string | ||||||
|  | 		(ctx->config.root, "behaviour.editor_command"); | ||||||
| 	if (!editor) | 	if (!editor) | ||||||
| 	{ | 	{ | ||||||
| 		const char *command; | 		const char *command; | ||||||
| @ -13285,8 +13215,8 @@ build_editor_command (struct app_context *ctx, const char *filename) | |||||||
| 		 && !(command = getenv ("EDITOR"))) | 		 && !(command = getenv ("EDITOR"))) | ||||||
| 			command = "vi"; | 			command = "vi"; | ||||||
| 
 | 
 | ||||||
| 		// Although most visual editors support a "+LINE" argument
 | 		// Although most visual editors support a "+LINE" argument (every
 | ||||||
| 		// (every editor mentioned in the default value of general.editor,
 | 		// editor mentioned in the default value of behaviour.editor_command,
 | ||||||
| 		// plus vi, mcedit, vis, ...), it isn't particularly useful by itself.
 | 		// plus vi, mcedit, vis, ...), it isn't particularly useful by itself.
 | ||||||
| 		// We need to be able to specify the column number.
 | 		// We need to be able to specify the column number.
 | ||||||
| 		//
 | 		//
 | ||||||
| @ -13446,27 +13376,28 @@ input_editor_cleanup (struct app_context *ctx) | |||||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| launch_pager (struct app_context *ctx, | launch_backlog_helper (struct app_context *ctx, int backlog_fd, | ||||||
| 	int fd, const char *name, const char *path) | 	const char *name, const char *path) | ||||||
| { | { | ||||||
| 	hard_assert (!ctx->running_pager); | 	hard_assert (!ctx->running_backlog_helper); | ||||||
| 	switch (spawn_helper_child (ctx)) | 	switch (spawn_helper_child (ctx)) | ||||||
| 	{ | 	{ | ||||||
| 	case 0: | 	case 0: | ||||||
| 		dup2 (fd, STDIN_FILENO); | 		dup2 (backlog_fd, STDIN_FILENO); | ||||||
| 		char *localized_name = | 		char *localized_name = | ||||||
| 			iconv_xstrdup (ctx->term_from_utf8, (char *) name, -1, NULL); | 			iconv_xstrdup (ctx->term_from_utf8, (char *) name, -1, NULL); | ||||||
| 		execl ("/bin/sh", "/bin/sh", "-c", | 		execl ("/bin/sh", "/bin/sh", "-c", | ||||||
| 			get_config_string (ctx->config.root, "general.pager"), | 			get_config_string (ctx->config.root, "behaviour.backlog_helper"), | ||||||
| 			PROGRAM_NAME, localized_name, path, NULL); | 			PROGRAM_NAME, localized_name, path, NULL); | ||||||
| 		print_error ("%s: %s", "Failed to launch pager", strerror (errno)); | 		print_error ("%s: %s", | ||||||
|  | 			"Failed to launch backlog helper", strerror (errno)); | ||||||
| 		_exit (EXIT_FAILURE); | 		_exit (EXIT_FAILURE); | ||||||
| 	case -1: | 	case -1: | ||||||
| 		log_global_error (ctx, "#s: #l", | 		log_global_error (ctx, "#s: #l", | ||||||
| 			"Failed to launch pager", strerror (errno)); | 			"Failed to launch backlog helper", strerror (errno)); | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		ctx->running_pager = true; | 		ctx->running_backlog_helper = true; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -13482,7 +13413,7 @@ display_backlog (struct app_context *ctx, int flush_opts) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!get_config_boolean (ctx->config.root, | 	if (!get_config_boolean (ctx->config.root, | ||||||
| 		"general.pager_strip_formatting")) | 		"behaviour.backlog_helper_strip_formatting")) | ||||||
| 		flush_opts |= FLUSH_OPT_RAW; | 		flush_opts |= FLUSH_OPT_RAW; | ||||||
| 
 | 
 | ||||||
| 	struct buffer *buffer = ctx->current_buffer; | 	struct buffer *buffer = ctx->current_buffer; | ||||||
| @ -13502,7 +13433,7 @@ display_backlog (struct app_context *ctx, int flush_opts) | |||||||
| 
 | 
 | ||||||
| 	rewind (backlog); | 	rewind (backlog); | ||||||
| 	set_cloexec (fileno (backlog)); | 	set_cloexec (fileno (backlog)); | ||||||
| 	launch_pager (ctx, fileno (backlog), buffer->name, NULL); | 	launch_backlog_helper (ctx, fileno (backlog), buffer->name, NULL); | ||||||
| 	fclose (backlog); | 	fclose (backlog); | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| @ -13546,7 +13477,7 @@ on_display_full_log (int count, int key, void *user_data) | |||||||
| 		(void) fflush (buffer->log_file); | 		(void) fflush (buffer->log_file); | ||||||
| 
 | 
 | ||||||
| 	set_cloexec (fileno (full_log)); | 	set_cloexec (fileno (full_log)); | ||||||
| 	launch_pager (ctx, fileno (full_log), buffer->name, path); | 	launch_backlog_helper (ctx, fileno (full_log), buffer->name, path); | ||||||
| 	fclose (full_log); | 	fclose (full_log); | ||||||
| 	free (path); | 	free (path); | ||||||
| 	return true; | 	return true; | ||||||
| @ -13943,10 +13874,13 @@ on_editline_return (EditLine *editline, int key) | |||||||
| 	wchar_t *line = calloc (sizeof *info->buffer, len + 1); | 	wchar_t *line = calloc (sizeof *info->buffer, len + 1); | ||||||
| 	memcpy (line, info->buffer, sizeof *info->buffer * len); | 	memcpy (line, info->buffer, sizeof *info->buffer * len); | ||||||
| 
 | 
 | ||||||
|  | 	// XXX: Editline seems to remember its position in history,
 | ||||||
|  | 	//   so it's not going to work as you'd expect it to
 | ||||||
| 	if (*line) | 	if (*line) | ||||||
| 	{ | 	{ | ||||||
| 		HistEventW ev; | 		HistEventW ev; | ||||||
| 		history_w (self->current->history, &ev, H_ENTER, line); | 		history_w (self->current->history, &ev, H_ENTER, line); | ||||||
|  | 		print_debug ("history: %d %ls", ev.num, ev.str); | ||||||
| 	} | 	} | ||||||
| 	free (line); | 	free (line); | ||||||
| 
 | 
 | ||||||
| @ -13958,7 +13892,6 @@ on_editline_return (EditLine *editline, int key) | |||||||
| 
 | 
 | ||||||
| 	el_cursor (editline, len - point); | 	el_cursor (editline, len - point); | ||||||
| 	el_wdeletestr (editline, len); | 	el_wdeletestr (editline, len); | ||||||
| 	input_el__bottom (self); |  | ||||||
| 	return CC_REFRESH; | 	return CC_REFRESH; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -14007,7 +13940,7 @@ static const char *g_first_time_help[] = | |||||||
| 	"", | 	"", | ||||||
| 	"To get a list of all commands, type \x02/help\x02.  To obtain", | 	"To get a list of all commands, type \x02/help\x02.  To obtain", | ||||||
| 	"more information on a command or option, simply add it as", | 	"more information on a command or option, simply add it as", | ||||||
| 	"a parameter, e.g. \x02/help set\x02 or \x02/help general.logging\x02.", | 	"a parameter, e.g. \x02/help set\x02 or \x02/help behaviour.logging\x02.", | ||||||
| 	"", | 	"", | ||||||
| 	"To switch between buffers, press \x02" | 	"To switch between buffers, press \x02" | ||||||
| 		"F5/Ctrl-P\x02 or \x02" "F6/Ctrl-N\x02.", | 		"F5/Ctrl-P\x02 or \x02" "F6/Ctrl-N\x02.", | ||||||
| @ -14214,8 +14147,8 @@ try_reap_child (struct app_context *ctx) | |||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ctx->running_pager) | 	if (ctx->running_backlog_helper) | ||||||
| 		ctx->running_pager = false; | 		ctx->running_backlog_helper = false; | ||||||
| 	else if (!ctx->running_editor) | 	else if (!ctx->running_editor) | ||||||
| 	{ | 	{ | ||||||
| 		log_global_debug (ctx, "An unknown child has died"); | 		log_global_debug (ctx, "An unknown child has died"); | ||||||
| @ -14332,7 +14265,7 @@ done: | |||||||
| static bool | static bool | ||||||
| insert_paste (struct app_context *ctx, char *paste, size_t len) | insert_paste (struct app_context *ctx, char *paste, size_t len) | ||||||
| { | { | ||||||
| 	if (!get_config_boolean (ctx->config.root, "general.process_pasted_text")) | 	if (!get_config_boolean (ctx->config.root, "behaviour.process_pasted_text")) | ||||||
| 		return CALL_ (ctx->input, insert, paste); | 		return CALL_ (ctx->input, insert, paste); | ||||||
| 
 | 
 | ||||||
| 	// Without ICRNL, which Editline keeps but Readline doesn't,
 | 	// Without ICRNL, which Editline keeps but Readline doesn't,
 | ||||||
| @ -14419,7 +14352,7 @@ reset_autoaway (struct app_context *ctx) | |||||||
| 
 | 
 | ||||||
| 	// And potentially start a new auto-away timer
 | 	// And potentially start a new auto-away timer
 | ||||||
| 	int64_t delay = get_config_integer | 	int64_t delay = get_config_integer | ||||||
| 		(ctx->config.root, "general.autoaway_delay"); | 		(ctx->config.root, "behaviour.autoaway_delay"); | ||||||
| 	if (delay) | 	if (delay) | ||||||
| 		poller_timer_set (&ctx->autoaway_tmr, delay * 1000); | 		poller_timer_set (&ctx->autoaway_tmr, delay * 1000); | ||||||
| } | } | ||||||
| @ -14429,7 +14362,7 @@ on_autoaway_timer (struct app_context *ctx) | |||||||
| { | { | ||||||
| 	// An empty message would unset any away status, so let's ignore that
 | 	// An empty message would unset any away status, so let's ignore that
 | ||||||
| 	const char *message = get_config_string | 	const char *message = get_config_string | ||||||
| 		(ctx->config.root, "general.autoaway_message"); | 		(ctx->config.root, "behaviour.autoaway_message"); | ||||||
| 	if (!message || !*message) | 	if (!message || !*message) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| @ -14786,6 +14719,9 @@ main (int argc, char *argv[]) | |||||||
| 
 | 
 | ||||||
| 	CALL (ctx.input, stop); | 	CALL (ctx.input, stop); | ||||||
| 
 | 
 | ||||||
|  | 	if (get_config_boolean (ctx.config.root, "behaviour.save_on_quit")) | ||||||
|  | 		save_configuration (&ctx); | ||||||
|  | 
 | ||||||
| 	app_context_free (&ctx); | 	app_context_free (&ctx); | ||||||
| 	toggle_bracketed_paste (false); | 	toggle_bracketed_paste (false); | ||||||
| 	free_terminal (); | 	free_terminal (); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user