Merge #7221 from justinmk/ev-focusgained

tui: schedule event instead of <FocusGained> pseudokey
This commit is contained in:
Justin M. Keyes 2017-09-06 07:25:01 +02:00 committed by GitHub
commit 51808a244e
19 changed files with 359 additions and 257 deletions

View File

@ -215,6 +215,7 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error)
#undef UI_EXT_OPTION #undef UI_EXT_OPTION
} }
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
static void push_call(UI *ui, char *name, Array args) static void push_call(UI *ui, char *name, Array args)
{ {
Array call = ARRAY_DICT_INIT; Array call = ARRAY_DICT_INIT;

View File

@ -255,12 +255,11 @@ free_vim_args:
return rv; return rv;
} }
/// Execute lua code. Parameters might be passed, they are available inside /// Execute lua code. Parameters (if any) are available as `...` inside the
/// the chunk as `...`. The chunk can return a value. /// chunk. The chunk can return a value.
/// ///
/// To evaluate an expression, it must be prefixed with "return ". For /// Only statements are executed. To evaluate an expression, prefix it
/// instance, to call a lua function with arguments sent in and get its /// with `return`: return my_function(...)
/// return value back, use the code "return my_function(...)".
/// ///
/// @param code lua code to execute /// @param code lua code to execute
/// @param args Arguments to the code /// @param args Arguments to the code
@ -423,29 +422,18 @@ void nvim_del_var(String name, Error *err)
dict_set_var(&globvardict, name, NIL, true, false, err); dict_set_var(&globvardict, name, NIL, true, false, err);
} }
/// Sets a global variable
///
/// @deprecated /// @deprecated
/// /// @see nvim_set_var
/// @param name Variable name
/// @param value Variable value
/// @param[out] err Error details, if any
/// @return Old value or nil if there was no previous value. /// @return Old value or nil if there was no previous value.
/// /// @warning May return nil if there was no previous value
/// @warning It may return nil if there was no previous value /// OR if previous value was `v:null`.
/// or if previous value was `v:null`.
Object vim_set_var(String name, Object value, Error *err) Object vim_set_var(String name, Object value, Error *err)
{ {
return dict_set_var(&globvardict, name, value, false, true, err); return dict_set_var(&globvardict, name, value, false, true, err);
} }
/// Removes a global variable
///
/// @deprecated /// @deprecated
/// /// @see nvim_del_var
/// @param name Variable name
/// @param[out] err Error details, if any
/// @return Old value
Object vim_del_var(String name, Error *err) Object vim_del_var(String name, Error *err)
{ {
return dict_set_var(&globvardict, name, NIL, true, true, err); return dict_set_var(&globvardict, name, NIL, true, true, err);

41
src/nvim/aucmd.c Normal file
View File

@ -0,0 +1,41 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include "nvim/os/os.h"
#include "nvim/fileio.h"
#include "nvim/vim.h"
#include "nvim/main.h"
#include "nvim/ui.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "aucmd.c.generated.h"
#endif
static void focusgained_event(void **argv)
{
bool *gainedp = argv[0];
do_autocmd_focusgained(*gainedp);
xfree(gainedp);
}
void aucmd_schedule_focusgained(bool gained)
{
bool *gainedp = xmalloc(sizeof(*gainedp));
*gainedp = gained;
loop_schedule_deferred(&main_loop,
event_create(focusgained_event, 1, gainedp));
}
static void do_autocmd_focusgained(bool gained)
FUNC_ATTR_NONNULL_ALL
{
static bool recursive = false;
if (recursive) {
return; // disallow recursion
}
recursive = true;
apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST),
NULL, NULL, false, curbuf);
recursive = false;
}

9
src/nvim/aucmd.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef NVIM_AUCMD_H
#define NVIM_AUCMD_H
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "aucmd.h.generated.h"
#endif
#endif // NVIM_AUCMD_H

View File

@ -974,14 +974,6 @@ static int insert_handle_key(InsertState *s)
multiqueue_process_events(main_loop.events); multiqueue_process_events(main_loop.events);
break; break;
case K_FOCUSGAINED: // Neovim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
break;
case K_FOCUSLOST: // Neovim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
break;
case K_HOME: // <Home> case K_HOME: // <Home>
case K_KHOME: case K_KHOME:
case K_S_HOME: case K_S_HOME:
@ -3167,8 +3159,7 @@ static bool ins_compl_prep(int c)
/* Ignore end of Select mode mapping and mouse scroll buttons. */ /* Ignore end of Select mode mapping and mouse scroll buttons. */
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
|| c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT) {
|| c == K_FOCUSGAINED || c == K_FOCUSLOST) {
return retval; return retval;
} }

View File

@ -59,7 +59,14 @@ void loop_poll_events(Loop *loop, int ms)
multiqueue_process_events(loop->fast_events); multiqueue_process_events(loop->fast_events);
} }
// Schedule an event from another thread /// Schedules an event from another thread.
///
/// @note Event is queued into `fast_events`, which is processed outside of the
/// primary `events` queue by loop_poll_events(). For `main_loop`, that
/// means `fast_events` is NOT processed in an "editor mode"
/// (VimState.execute), so redraw and other side-effects are likely to be
/// skipped.
/// @see loop_schedule_deferred
void loop_schedule(Loop *loop, Event event) void loop_schedule(Loop *loop, Event event)
{ {
uv_mutex_lock(&loop->mutex); uv_mutex_lock(&loop->mutex);
@ -68,6 +75,24 @@ void loop_schedule(Loop *loop, Event event)
uv_mutex_unlock(&loop->mutex); uv_mutex_unlock(&loop->mutex);
} }
/// Schedules an event from another thread. Unlike loop_schedule(), the event
/// is forwarded to `Loop.events`, instead of being processed immediately.
///
/// @see loop_schedule
void loop_schedule_deferred(Loop *loop, Event event)
{
Event *eventp = xmalloc(sizeof(*eventp));
*eventp = event;
loop_schedule(loop, event_create(loop_deferred_event, 2, loop, eventp));
}
static void loop_deferred_event(void **argv)
{
Loop *loop = argv[0];
Event *eventp = argv[1];
multiqueue_put_event(loop->events, *eventp);
xfree(eventp);
}
void loop_on_put(MultiQueue *queue, void *data) void loop_on_put(MultiQueue *queue, void *data)
{ {
Loop *loop = data; Loop *loop = data;

View File

@ -16,10 +16,28 @@ KLIST_INIT(WatcherPtr, WatcherPtr, _noop)
typedef struct loop { typedef struct loop {
uv_loop_t uv; uv_loop_t uv;
MultiQueue *events, *fast_events, *thread_events; MultiQueue *events;
MultiQueue *thread_events;
// Immediate events:
// "Events that should be processed after exiting uv_run() (to avoid
// recursion), but before returning from loop_poll_events()."
// 502aee690c980fcb3cfcb3f211dcfad06103db46
// Practical consequence: these events are processed by
// state_enter()..os_inchar()
// whereas "regular" (main_loop.events) events are processed by
// state_enter()..VimState.execute()
// But state_enter()..os_inchar() can be "too early" if you want the event
// to trigger UI updates and other user-activity-related side-effects.
MultiQueue *fast_events;
// used by process/job-control subsystem
klist_t(WatcherPtr) *children; klist_t(WatcherPtr) *children;
uv_signal_t children_watcher; uv_signal_t children_watcher;
uv_timer_t children_kill_timer, poll_timer; uv_timer_t children_kill_timer;
// generic timer, used by loop_poll_events()
uv_timer_t poll_timer;
size_t children_stop_requests; size_t children_stop_requests;
uv_async_t async; uv_async_t async;
uv_mutex_t mutex; uv_mutex_t mutex;

View File

@ -1620,14 +1620,6 @@ static int command_line_handle_key(CommandLineState *s)
} }
return command_line_not_changed(s); return command_line_not_changed(s);
case K_FOCUSGAINED: // Neovim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
return command_line_not_changed(s);
case K_FOCUSLOST: // Neovim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
return command_line_not_changed(s);
default: default:
// Normal character with no special meaning. Just set mod_mask // Normal character with no special meaning. Just set mod_mask
// to 0x0 so that typing Shift-Space in the GUI doesn't enter // to 0x0 so that typing Shift-Space in the GUI doesn't enter

View File

@ -140,155 +140,153 @@ static char_u modifier_keys_table[] =
}; };
static struct key_name_entry { static struct key_name_entry {
int key; /* Special key code or ascii value */ int key; // Special key code or ascii value
char_u *name; /* Name of key */ char *name; // Name of key
} key_names_table[] = } key_names_table[] =
{ {
{' ', (char_u *)"Space"}, { ' ', "Space" },
{TAB, (char_u *)"Tab"}, { TAB, "Tab" },
{K_TAB, (char_u *)"Tab"}, { K_TAB, "Tab" },
{NL, (char_u *)"NL"}, { NL, "NL" },
{NL, (char_u *)"NewLine"}, /* Alternative name */ { NL, "NewLine" }, // Alternative name
{NL, (char_u *)"LineFeed"}, /* Alternative name */ { NL, "LineFeed" }, // Alternative name
{NL, (char_u *)"LF"}, /* Alternative name */ { NL, "LF" }, // Alternative name
{CAR, (char_u *)"CR"}, { CAR, "CR" },
{CAR, (char_u *)"Return"}, /* Alternative name */ { CAR, "Return" }, // Alternative name
{CAR, (char_u *)"Enter"}, /* Alternative name */ { CAR, "Enter" }, // Alternative name
{K_BS, (char_u *)"BS"}, { K_BS, "BS" },
{K_BS, (char_u *)"BackSpace"}, /* Alternative name */ { K_BS, "BackSpace" }, // Alternative name
{ESC, (char_u *)"Esc"}, { ESC, "Esc" },
{CSI, (char_u *)"CSI"}, { CSI, "CSI" },
{K_CSI, (char_u *)"xCSI"}, { K_CSI, "xCSI" },
{'|', (char_u *)"Bar"}, { '|', "Bar" },
{'\\', (char_u *)"Bslash"}, { '\\', "Bslash" },
{K_DEL, (char_u *)"Del"}, { K_DEL, "Del" },
{K_DEL, (char_u *)"Delete"}, /* Alternative name */ { K_DEL, "Delete" }, // Alternative name
{K_KDEL, (char_u *)"kDel"}, { K_KDEL, "kDel" },
{K_UP, (char_u *)"Up"}, { K_UP, "Up" },
{K_DOWN, (char_u *)"Down"}, { K_DOWN, "Down" },
{K_LEFT, (char_u *)"Left"}, { K_LEFT, "Left" },
{K_RIGHT, (char_u *)"Right"}, { K_RIGHT, "Right" },
{K_XUP, (char_u *)"xUp"}, { K_XUP, "xUp" },
{K_XDOWN, (char_u *)"xDown"}, { K_XDOWN, "xDown" },
{K_XLEFT, (char_u *)"xLeft"}, { K_XLEFT, "xLeft" },
{K_XRIGHT, (char_u *)"xRight"}, { K_XRIGHT, "xRight" },
{K_F1, (char_u *)"F1"}, { K_F1, "F1" },
{K_F2, (char_u *)"F2"}, { K_F2, "F2" },
{K_F3, (char_u *)"F3"}, { K_F3, "F3" },
{K_F4, (char_u *)"F4"}, { K_F4, "F4" },
{K_F5, (char_u *)"F5"}, { K_F5, "F5" },
{K_F6, (char_u *)"F6"}, { K_F6, "F6" },
{K_F7, (char_u *)"F7"}, { K_F7, "F7" },
{K_F8, (char_u *)"F8"}, { K_F8, "F8" },
{K_F9, (char_u *)"F9"}, { K_F9, "F9" },
{K_F10, (char_u *)"F10"}, { K_F10, "F10" },
{K_F11, (char_u *)"F11"}, { K_F11, "F11" },
{K_F12, (char_u *)"F12"}, { K_F12, "F12" },
{K_F13, (char_u *)"F13"}, { K_F13, "F13" },
{K_F14, (char_u *)"F14"}, { K_F14, "F14" },
{K_F15, (char_u *)"F15"}, { K_F15, "F15" },
{K_F16, (char_u *)"F16"}, { K_F16, "F16" },
{K_F17, (char_u *)"F17"}, { K_F17, "F17" },
{K_F18, (char_u *)"F18"}, { K_F18, "F18" },
{K_F19, (char_u *)"F19"}, { K_F19, "F19" },
{K_F20, (char_u *)"F20"}, { K_F20, "F20" },
{K_F21, (char_u *)"F21"}, { K_F21, "F21" },
{K_F22, (char_u *)"F22"}, { K_F22, "F22" },
{K_F23, (char_u *)"F23"}, { K_F23, "F23" },
{K_F24, (char_u *)"F24"}, { K_F24, "F24" },
{K_F25, (char_u *)"F25"}, { K_F25, "F25" },
{K_F26, (char_u *)"F26"}, { K_F26, "F26" },
{K_F27, (char_u *)"F27"}, { K_F27, "F27" },
{K_F28, (char_u *)"F28"}, { K_F28, "F28" },
{K_F29, (char_u *)"F29"}, { K_F29, "F29" },
{K_F30, (char_u *)"F30"}, { K_F30, "F30" },
{K_F31, (char_u *)"F31"}, { K_F31, "F31" },
{K_F32, (char_u *)"F32"}, { K_F32, "F32" },
{K_F33, (char_u *)"F33"}, { K_F33, "F33" },
{K_F34, (char_u *)"F34"}, { K_F34, "F34" },
{K_F35, (char_u *)"F35"}, { K_F35, "F35" },
{K_F36, (char_u *)"F36"}, { K_F36, "F36" },
{K_F37, (char_u *)"F37"}, { K_F37, "F37" },
{K_XF1, (char_u *)"xF1"}, { K_XF1, "xF1" },
{K_XF2, (char_u *)"xF2"}, { K_XF2, "xF2" },
{K_XF3, (char_u *)"xF3"}, { K_XF3, "xF3" },
{K_XF4, (char_u *)"xF4"}, { K_XF4, "xF4" },
{K_HELP, (char_u *)"Help"}, { K_HELP, "Help" },
{K_UNDO, (char_u *)"Undo"}, { K_UNDO, "Undo" },
{K_INS, (char_u *)"Insert"}, { K_INS, "Insert" },
{K_INS, (char_u *)"Ins"}, /* Alternative name */ { K_INS, "Ins" }, // Alternative name
{K_KINS, (char_u *)"kInsert"}, { K_KINS, "kInsert" },
{K_HOME, (char_u *)"Home"}, { K_HOME, "Home" },
{K_KHOME, (char_u *)"kHome"}, { K_KHOME, "kHome" },
{K_XHOME, (char_u *)"xHome"}, { K_XHOME, "xHome" },
{K_ZHOME, (char_u *)"zHome"}, { K_ZHOME, "zHome" },
{K_END, (char_u *)"End"}, { K_END, "End" },
{K_KEND, (char_u *)"kEnd"}, { K_KEND, "kEnd" },
{K_XEND, (char_u *)"xEnd"}, { K_XEND, "xEnd" },
{K_ZEND, (char_u *)"zEnd"}, { K_ZEND, "zEnd" },
{K_PAGEUP, (char_u *)"PageUp"}, { K_PAGEUP, "PageUp" },
{K_PAGEDOWN, (char_u *)"PageDown"}, { K_PAGEDOWN, "PageDown" },
{K_KPAGEUP, (char_u *)"kPageUp"}, { K_KPAGEUP, "kPageUp" },
{K_KPAGEDOWN, (char_u *)"kPageDown"}, { K_KPAGEDOWN, "kPageDown" },
{K_KPLUS, (char_u *)"kPlus"}, { K_KPLUS, "kPlus" },
{K_KMINUS, (char_u *)"kMinus"}, { K_KMINUS, "kMinus" },
{K_KDIVIDE, (char_u *)"kDivide"}, { K_KDIVIDE, "kDivide" },
{K_KMULTIPLY, (char_u *)"kMultiply"}, { K_KMULTIPLY, "kMultiply" },
{K_KENTER, (char_u *)"kEnter"}, { K_KENTER, "kEnter" },
{K_KPOINT, (char_u *)"kPoint"}, { K_KPOINT, "kPoint" },
{K_K0, (char_u *)"k0"}, { K_K0, "k0" },
{K_K1, (char_u *)"k1"}, { K_K1, "k1" },
{K_K2, (char_u *)"k2"}, { K_K2, "k2" },
{K_K3, (char_u *)"k3"}, { K_K3, "k3" },
{K_K4, (char_u *)"k4"}, { K_K4, "k4" },
{K_K5, (char_u *)"k5"}, { K_K5, "k5" },
{K_K6, (char_u *)"k6"}, { K_K6, "k6" },
{K_K7, (char_u *)"k7"}, { K_K7, "k7" },
{K_K8, (char_u *)"k8"}, { K_K8, "k8" },
{K_K9, (char_u *)"k9"}, { K_K9, "k9" },
{'<', (char_u *)"lt"}, { '<', "lt" },
{K_MOUSE, (char_u *)"Mouse"}, { K_MOUSE, "Mouse" },
{K_LEFTMOUSE, (char_u *)"LeftMouse"}, { K_LEFTMOUSE, "LeftMouse" },
{K_LEFTMOUSE_NM, (char_u *)"LeftMouseNM"}, { K_LEFTMOUSE_NM, "LeftMouseNM" },
{K_LEFTDRAG, (char_u *)"LeftDrag"}, { K_LEFTDRAG, "LeftDrag" },
{K_LEFTRELEASE, (char_u *)"LeftRelease"}, { K_LEFTRELEASE, "LeftRelease" },
{K_LEFTRELEASE_NM, (char_u *)"LeftReleaseNM"}, { K_LEFTRELEASE_NM, "LeftReleaseNM" },
{K_MIDDLEMOUSE, (char_u *)"MiddleMouse"}, { K_MIDDLEMOUSE, "MiddleMouse" },
{K_MIDDLEDRAG, (char_u *)"MiddleDrag"}, { K_MIDDLEDRAG, "MiddleDrag" },
{K_MIDDLERELEASE, (char_u *)"MiddleRelease"}, { K_MIDDLERELEASE, "MiddleRelease" },
{K_RIGHTMOUSE, (char_u *)"RightMouse"}, { K_RIGHTMOUSE, "RightMouse" },
{K_RIGHTDRAG, (char_u *)"RightDrag"}, { K_RIGHTDRAG, "RightDrag" },
{K_RIGHTRELEASE, (char_u *)"RightRelease"}, { K_RIGHTRELEASE, "RightRelease" },
{K_MOUSEDOWN, (char_u *)"ScrollWheelUp"}, { K_MOUSEDOWN, "ScrollWheelUp" },
{K_MOUSEUP, (char_u *)"ScrollWheelDown"}, { K_MOUSEUP, "ScrollWheelDown" },
{K_MOUSELEFT, (char_u *)"ScrollWheelRight"}, { K_MOUSELEFT, "ScrollWheelRight" },
{K_MOUSERIGHT, (char_u *)"ScrollWheelLeft"}, { K_MOUSERIGHT, "ScrollWheelLeft" },
{K_MOUSEDOWN, (char_u *)"MouseDown"}, /* OBSOLETE: Use */ { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use
{K_MOUSEUP, (char_u *)"MouseUp"}, /* ScrollWheelXXX instead */ { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead
{K_X1MOUSE, (char_u *)"X1Mouse"}, { K_X1MOUSE, "X1Mouse" },
{K_X1DRAG, (char_u *)"X1Drag"}, { K_X1DRAG, "X1Drag" },
{K_X1RELEASE, (char_u *)"X1Release"}, { K_X1RELEASE, "X1Release" },
{K_X2MOUSE, (char_u *)"X2Mouse"}, { K_X2MOUSE, "X2Mouse" },
{K_X2DRAG, (char_u *)"X2Drag"}, { K_X2DRAG, "X2Drag" },
{K_X2RELEASE, (char_u *)"X2Release"}, { K_X2RELEASE, "X2Release" },
{K_DROP, (char_u *)"Drop"}, { K_DROP, "Drop" },
{K_ZERO, (char_u *)"Nul"}, { K_ZERO, "Nul" },
{K_SNR, (char_u *)"SNR"}, { K_SNR, "SNR" },
{K_PLUG, (char_u *)"Plug"}, { K_PLUG, "Plug" },
{K_PASTE, (char_u *)"Paste"}, { K_PASTE, "Paste" },
{K_FOCUSGAINED, (char_u *)"FocusGained"}, { 0, NULL }
{K_FOCUSLOST, (char_u *)"FocusLost"},
{0, NULL}
}; };
static struct mousetable { static struct mousetable {
@ -721,7 +719,7 @@ int find_special_key_in_table(int c)
*/ */
int get_special_key_code(const char_u *name) int get_special_key_code(const char_u *name)
{ {
char_u *table_name; char *table_name;
int i, j; int i, j;
for (i = 0; key_names_table[i].name != NULL; i++) { for (i = 0; key_names_table[i].name != NULL; i++) {

View File

@ -428,8 +428,6 @@ enum key_extra {
#define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN) #define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN)
#define K_DROP TERMCAP2KEY(KS_EXTRA, KE_DROP) #define K_DROP TERMCAP2KEY(KS_EXTRA, KE_DROP)
#define K_FOCUSGAINED TERMCAP2KEY(KS_EXTRA, KE_FOCUSGAINED)
#define K_FOCUSLOST TERMCAP2KEY(KS_EXTRA, KE_FOCUSLOST)
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT) #define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE) #define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE)

View File

@ -95,8 +95,12 @@ void log_unlock(void)
uv_mutex_unlock(&mutex); uv_mutex_unlock(&mutex);
} }
bool do_log(int log_level, const char *func_name, int line_num, bool eol, /// @param context description of a shared context or subsystem
const char* fmt, ...) FUNC_ATTR_UNUSED /// @param func_name function name, or NULL
/// @param line_num source line number, or -1
bool do_log(int log_level, const char *context, const char *func_name,
int line_num, bool eol, const char *fmt, ...)
FUNC_ATTR_UNUSED
{ {
if (log_level < MIN_LOG_LEVEL) { if (log_level < MIN_LOG_LEVEL) {
return false; return false;
@ -112,8 +116,8 @@ bool do_log(int log_level, const char *func_name, int line_num, bool eol,
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol, ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num,
fmt, args); eol, fmt, args);
va_end(args); va_end(args);
if (log_file != stderr && log_file != stdout) { if (log_file != stderr && log_file != stdout) {
@ -151,7 +155,7 @@ FILE *open_log_file(void)
static bool opening_log_file = false; static bool opening_log_file = false;
// check if it's a recursive call // check if it's a recursive call
if (opening_log_file) { if (opening_log_file) {
do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Cannot LOG() recursively."); "Cannot LOG() recursively.");
return stderr; return stderr;
} }
@ -171,7 +175,7 @@ FILE *open_log_file(void)
// - LOG() is called before early_init() // - LOG() is called before early_init()
// - Directory does not exist // - Directory does not exist
// - File is not writable // - File is not writable
do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Logging to stderr, failed to open $" LOG_FILE_ENV ": %s", "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s",
log_file_path); log_file_path);
return stderr; return stderr;
@ -201,7 +205,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name,
// Now we have a command string like: // Now we have a command string like:
// addr2line -e /path/to/exe -f -p 0x123 0x456 ... // addr2line -e /path/to/exe -f -p 0x123 0x456 ...
do_log_to_file(log_file, DEBUG_LOG_LEVEL, func_name, line_num, true, do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true,
"trace:"); "trace:");
FILE *fp = popen(cmdbuf, "r"); FILE *fp = popen(cmdbuf, "r");
char linebuf[IOSIZE]; char linebuf[IOSIZE];
@ -230,22 +234,23 @@ end:
} }
#endif #endif
static bool do_log_to_file(FILE *log_file, int log_level, static bool do_log_to_file(FILE *log_file, int log_level, const char *context,
const char *func_name, int line_num, bool eol, const char *func_name, int line_num, bool eol,
const char* fmt, ...) const char* fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
bool ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol, bool ret = v_do_log_to_file(log_file, log_level, context, func_name,
fmt, args); line_num, eol, fmt, args);
va_end(args); va_end(args);
return ret; return ret;
} }
static bool v_do_log_to_file(FILE *log_file, int log_level, static bool v_do_log_to_file(FILE *log_file, int log_level,
const char *func_name, int line_num, bool eol, const char *context, const char *func_name,
const char* fmt, va_list args) int line_num, bool eol, const char *fmt,
va_list args)
{ {
static const char *log_levels[] = { static const char *log_levels[] = {
[DEBUG_LOG_LEVEL] = "DEBUG", [DEBUG_LOG_LEVEL] = "DEBUG",
@ -268,8 +273,15 @@ static bool v_do_log_to_file(FILE *log_file, int log_level,
// print the log message prefixed by the current timestamp and pid // print the log message prefixed by the current timestamp and pid
int64_t pid = os_get_pid(); int64_t pid = os_get_pid();
if (fprintf(log_file, "%s %s %" PRId64 "/%s:%d: ", date_time, int rv = (line_num == -1 || func_name == NULL)
log_levels[log_level], pid, func_name, line_num) < 0) { ? fprintf(log_file, "%s %s %" PRId64 " %s", date_time,
log_levels[log_level], pid,
(context == NULL ? "?:" : context))
: fprintf(log_file, "%s %s %" PRId64 " %s%s:%d: ", date_time,
log_levels[log_level], pid,
(context == NULL ? "" : context),
func_name, line_num);
if (rv < 0) {
return false; return false;
} }
if (vfprintf(log_file, fmt, args) < 0) { if (vfprintf(log_file, fmt, args) < 0) {

View File

@ -22,42 +22,42 @@
# define MIN_LOG_LEVEL INFO_LOG_LEVEL # define MIN_LOG_LEVEL INFO_LOG_LEVEL
#endif #endif
#define LOG(level, ...) do_log((level), __func__, __LINE__, true, \ #define LOG(level, ...) do_log((level), NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
# undef DLOG # undef DLOG
# undef DLOGN # undef DLOGN
# define DLOG(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, true, \ # define DLOG(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define DLOGN(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, false, \ # define DLOGN(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif
#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL #if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
# undef ILOG # undef ILOG
# undef ILOGN # undef ILOGN
# define ILOG(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, true, \ # define ILOG(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define ILOGN(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, false, \ # define ILOGN(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif
#if MIN_LOG_LEVEL <= WARN_LOG_LEVEL #if MIN_LOG_LEVEL <= WARN_LOG_LEVEL
# undef WLOG # undef WLOG
# undef WLOGN # undef WLOGN
# define WLOG(...) do_log(WARN_LOG_LEVEL, __func__, __LINE__, true, \ # define WLOG(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define WLOGN(...) do_log(WARN_LOG_LEVEL, __func__, __LINE__, false, \ # define WLOGN(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif
#if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL #if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL
# undef ELOG # undef ELOG
# undef ELOGN # undef ELOGN
# define ELOG(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, true, \ # define ELOG(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define ELOGN(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, false, \ # define ELOGN(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif

View File

@ -188,12 +188,11 @@ uint64_t channel_connect(bool tcp, const char *address, int timeout,
return channel->id; return channel->id;
} }
/// Sends event/arguments to channel /// Publishes an event to a channel.
/// ///
/// @param id The channel id. If 0, the event will be sent to all /// @param id Channel id. 0 means "broadcast to all subscribed channels"
/// channels that have subscribed to the event type /// @param name Event name (application-defined)
/// @param name The event name, an arbitrary string /// @param args Array of event arguments
/// @param args Array with event arguments
/// @return True if the event was sent successfully, false otherwise. /// @return True if the event was sent successfully, false otherwise.
bool channel_send_event(uint64_t id, const char *name, Array args) bool channel_send_event(uint64_t id, const char *name, Array args)
{ {
@ -215,7 +214,6 @@ bool channel_send_event(uint64_t id, const char *name, Array args)
send_event(channel, name, args); send_event(channel, name, args);
} }
} else { } else {
// TODO(tarruda): Implement event broadcasting in vimscript
broadcast_event(name, args); broadcast_event(name, args);
} }

View File

@ -345,8 +345,6 @@ static const struct nv_cmd {
{ K_F8, farsi_f8, 0, 0 }, { K_F8, farsi_f8, 0, 0 },
{ K_F9, farsi_f9, 0, 0 }, { K_F9, farsi_f9, 0, 0 },
{ K_EVENT, nv_event, NV_KEEPREG, 0 }, { K_EVENT, nv_event, NV_KEEPREG, 0 },
{ K_FOCUSGAINED, nv_focusgained, NV_KEEPREG, 0 },
{ K_FOCUSLOST, nv_focuslost, NV_KEEPREG, 0 },
}; };
/* Number of commands in nv_cmds[]. */ /* Number of commands in nv_cmds[]. */
@ -7961,18 +7959,6 @@ static void nv_event(cmdarg_T *cap)
finish_op = false; finish_op = false;
} }
/// Trigger FocusGained event.
static void nv_focusgained(cmdarg_T *cap)
{
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
}
/// Trigger FocusLost event.
static void nv_focuslost(cmdarg_T *cap)
{
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
}
/* /*
* Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos". * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
*/ */

View File

@ -26,10 +26,11 @@ void state_enter(VimState *s)
int check_result = s->check ? s->check(s) : 1; int check_result = s->check ? s->check(s) : 1;
if (!check_result) { if (!check_result) {
break; break; // Terminate this state.
} else if (check_result == -1) { } else if (check_result == -1) {
continue; continue; // check() again.
} }
// Execute this state.
int key; int key;
@ -48,11 +49,13 @@ getkey:
ui_flush(); ui_flush();
// Call `os_inchar` directly to block for events or user input without // Call `os_inchar` directly to block for events or user input without
// consuming anything from `input_buffer`(os/input.c) or calling the // consuming anything from `input_buffer`(os/input.c) or calling the
// mapping engine. If an event was put into the queue, we send K_EVENT // mapping engine.
// directly.
(void)os_inchar(NULL, 0, -1, 0); (void)os_inchar(NULL, 0, -1, 0);
input_disable_events(); input_disable_events();
key = !multiqueue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); // If an event was put into the queue, we send K_EVENT directly.
key = !multiqueue_empty(main_loop.events)
? K_EVENT
: safe_vgetc();
} }
if (key == K_EVENT) { if (key == K_EVENT) {

View File

@ -432,14 +432,6 @@ static int terminal_execute(VimState *state, int key)
TerminalState *s = (TerminalState *)state; TerminalState *s = (TerminalState *)state;
switch (key) { switch (key) {
case K_FOCUSGAINED: // nvim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
break;
case K_FOCUSLOST: // nvim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
break;
// Temporary fix until paste events gets implemented // Temporary fix until paste events gets implemented
case K_PASTE: case K_PASTE:
break; break;

View File

@ -8,6 +8,7 @@
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/main.h" #include "nvim/main.h"
#include "nvim/aucmd.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/event/rstream.h" #include "nvim/event/rstream.h"
@ -280,9 +281,9 @@ static void timer_cb(TimeWatcher *watcher, void *data)
/// Handle focus events. /// Handle focus events.
/// ///
/// If the upcoming sequence of bytes in the input stream matches either the /// If the upcoming sequence of bytes in the input stream matches the termcode
/// escape code for focus gained `<ESC>[I` or focus lost `<ESC>[O` then consume /// for "focus gained" or "focus lost", consume that sequence and schedule an
/// that sequence and push the appropriate event into the input queue /// event on the main loop.
/// ///
/// @param input the input stream /// @param input the input stream
/// @return true iff handle_focus_event consumed some input /// @return true iff handle_focus_event consumed some input
@ -294,11 +295,7 @@ static bool handle_focus_event(TermInput *input)
// Advance past the sequence // Advance past the sequence
bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
rbuffer_consumed(input->read_stream.buffer, 3); rbuffer_consumed(input->read_stream.buffer, 3);
if (focus_gained) { aucmd_schedule_focusgained(focus_gained);
enqueue_input(input, FOCUSGAINED_KEY, sizeof(FOCUSGAINED_KEY) - 1);
} else {
enqueue_input(input, FOCUSLOST_KEY, sizeof(FOCUSLOST_KEY) - 1);
}
return true; return true;
} }
return false; return false;

View File

@ -71,10 +71,10 @@ static char uilog_last_event[1024] = { 0 };
uilog_seen++; \ uilog_seen++; \
} else { \ } else { \
if (uilog_seen > 0) { \ if (uilog_seen > 0) { \
do_log(DEBUG_LOG_LEVEL, "ui", 0, true, \ do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, \
"%s (+%zu times...)", uilog_last_event, uilog_seen); \ "%s (+%zu times...)", uilog_last_event, uilog_seen); \
} \ } \
DLOG("ui: " STR(funname)); \ do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, STR(funname)); \
uilog_seen = 0; \ uilog_seen = 0; \
xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \ xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
} \ } \

View File

@ -1,5 +1,6 @@
-- Some sanity checks for the TUI using the builtin terminal emulator -- TUI acceptance tests.
-- as a simple way to send keys and assert screen state. -- Uses :terminal as a way to send keys and assert screen state.
local global_helpers = require('test.helpers')
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers') local thelpers = require('test.functional.terminal.helpers')
local feed_data = thelpers.feed_data local feed_data = thelpers.feed_data
@ -194,7 +195,7 @@ describe('tui with non-tty file descriptors', function()
end) end)
end) end)
describe('tui focus event handling', function() describe('tui FocusGained/FocusLost', function()
local screen local screen
before_each(function() before_each(function()
@ -206,7 +207,8 @@ describe('tui focus event handling', function()
feed_data("\034\016") -- CTRL-\ CTRL-N feed_data("\034\016") -- CTRL-\ CTRL-N
end) end)
it('can handle focus events in normal mode', function() it('in normal-mode', function()
retry(2, 3 * screen.timeout, function()
feed_data('\027[I') feed_data('\027[I')
screen:expect([[ screen:expect([[
{1: } | {1: } |
@ -228,11 +230,13 @@ describe('tui focus event handling', function()
lost | lost |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]]) ]])
end)
end) end)
it('can handle focus events in insert mode', function() it('in insert-mode', function()
feed_command('set noshowmode') feed_command('set noshowmode')
feed_data('i') feed_data('i')
retry(2, 3 * screen.timeout, function()
feed_data('\027[I') feed_data('\027[I')
screen:expect([[ screen:expect([[
{1: } | {1: } |
@ -253,9 +257,12 @@ describe('tui focus event handling', function()
lost | lost |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]]) ]])
end)
end) end)
it('can handle focus events in cmdline mode', function() -- During cmdline-mode we ignore :echo invoked by timers/events.
-- See commit: 5cc87d4dabd02167117be7a978b5c8faaa975419.
it('in cmdline-mode does NOT :echo', function()
feed_data(':') feed_data(':')
feed_data('\027[I') feed_data('\027[I')
screen:expect([[ screen:expect([[
@ -264,7 +271,7 @@ describe('tui focus event handling', function()
{4:~ }| {4:~ }|
{4:~ }| {4:~ }|
{5:[No Name] }| {5:[No Name] }|
g{1:a}ined | :{1: } |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]]) ]])
feed_data('\027[O') feed_data('\027[O')
@ -274,17 +281,46 @@ describe('tui focus event handling', function()
{4:~ }| {4:~ }|
{4:~ }| {4:~ }|
{5:[No Name] }| {5:[No Name] }|
l{1:o}st | :{1: } |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]]) ]])
end) end)
it('can handle focus events in terminal mode', function() it('in cmdline-mode', function()
-- Set up autocmds that modify the buffer, instead of just calling :echo.
-- This is how we can test handling of focus gained/lost during cmdline-mode.
-- See commit: 5cc87d4dabd02167117be7a978b5c8faaa975419.
feed_data(":autocmd!\n")
feed_data(":autocmd FocusLost * call append(line('$'), 'lost')\n")
feed_data(":autocmd FocusGained * call append(line('$'), 'gained')\n")
-- Enter cmdline-mode.
feed_data(':')
screen:sleep(1)
-- Send focus lost/gained termcodes.
feed_data('\027[O')
feed_data('\027[I')
screen:sleep(1)
-- Exit cmdline-mode. Redraws from timers/events are blocked during
-- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode.
feed_data('\n')
screen:expect([[
{1: } |
lost |
gained |
{4:~ }|
{5:[No Name] [+] }|
: |
{3:-- TERMINAL --} |
]])
end)
it('in terminal-mode', function()
feed_data(':set shell='..nvim_dir..'/shell-test\n') feed_data(':set shell='..nvim_dir..'/shell-test\n')
feed_data(':set noshowmode laststatus=0\n') feed_data(':set noshowmode laststatus=0\n')
retry(2, 3 * screen.timeout, function() retry(2, 3 * screen.timeout, function()
feed_data(':terminal\n') feed_data(':terminal\n')
screen:sleep(1)
feed_data('\027[I') feed_data('\027[I')
screen:expect([[ screen:expect([[
{1:r}eady $ | {1:r}eady $ |
@ -311,13 +347,30 @@ describe('tui focus event handling', function()
feed_data(':bwipeout!\n') feed_data(':bwipeout!\n')
end) end)
end) end)
it('in press-enter prompt', function()
feed_data(":echom 'msg1'|echom 'msg2'|echom 'msg3'|echom 'msg4'|echom 'msg5'\n")
-- Execute :messages to provoke the press-enter prompt.
feed_data(":messages\n")
feed_data('\027[I')
feed_data('\027[I')
screen:expect([[
msg1 |
msg2 |
msg3 |
msg4 |
msg5 |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
]])
end)
end) end)
-- These tests require `thelpers` because --headless/--embed -- These tests require `thelpers` because --headless/--embed
-- does not initialize the TUI. -- does not initialize the TUI.
describe("tui 't_Co' (terminal colors)", function() describe("tui 't_Co' (terminal colors)", function()
local screen local screen
local is_freebsd = (helpers.eval("system('uname') =~? 'FreeBSD'") == 1) local is_freebsd = (string.lower(global_helpers.uname()) == 'freebsd')
local function assert_term_colors(term, colorterm, maxcolors) local function assert_term_colors(term, colorterm, maxcolors)
helpers.clear({env={TERM=term}, args={}}) helpers.clear({env={TERM=term}, args={}})