aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorckelsel <ckelsel@hotmail.com>2017-07-28 08:38:08 +0800
committerckelsel <ckelsel@hotmail.com>2017-07-28 08:38:08 +0800
commit3e0536eb295309c728acca386ec35756b7e034f6 (patch)
treef4ef3ef657245cf6d96a3d777ae63c6fda5b11d0
parent31c018244daa12caab3af357a368279a1f55d28c (diff)
parente6d54407ba8ce580fbd81cb9389eb9ce4483597b (diff)
downloadrneovim-3e0536eb295309c728acca386ec35756b7e034f6.tar.gz
rneovim-3e0536eb295309c728acca386ec35756b7e034f6.tar.bz2
rneovim-3e0536eb295309c728acca386ec35756b7e034f6.zip
Merge remote-tracking branch 'upstream/master'
-rw-r--r--.gitignore3
-rw-r--r--CMakeLists.txt8
-rw-r--r--runtime/CMakeLists.txt24
-rw-r--r--runtime/autoload/provider.vim18
-rw-r--r--runtime/autoload/provider/clipboard.vim15
-rw-r--r--runtime/autoload/provider/pythonx.vim16
-rw-r--r--runtime/doc/digraph.txt3
-rw-r--r--runtime/doc/eval.txt2
-rw-r--r--runtime/doc/map.txt1
-rw-r--r--src/nvim/README.md39
-rw-r--r--src/nvim/buffer.c24
-rw-r--r--src/nvim/digraph.c1
-rw-r--r--src/nvim/eval.c16
-rw-r--r--src/nvim/ex_cmds.c14
-rw-r--r--src/nvim/ex_docmd.c32
-rw-r--r--src/nvim/ex_eval.c19
-rw-r--r--src/nvim/ex_eval.h27
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua3
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/log.c47
-rw-r--r--src/nvim/log.h4
-rw-r--r--src/nvim/memory.c2
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/msgpack_rpc/channel.c43
-rw-r--r--src/nvim/normal.c68
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/screen.c4
-rw-r--r--src/nvim/testdir/runtest.vim12
-rw-r--r--src/nvim/testdir/test_cmdline.vim5
-rw-r--r--src/nvim/testdir/test_goto.vim281
-rw-r--r--src/nvim/tui/tui.c8
-rw-r--r--src/nvim/ui.c25
-rw-r--r--src/nvim/ui_bridge.c28
-rw-r--r--src/nvim/version.c16
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c22
38 files changed, 652 insertions, 193 deletions
diff --git a/.gitignore b/.gitignore
index fb506305b6..3a8994a5f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,9 @@ tags
/src/nvim/po/vim.pot
/src/nvim/po/*.ck
+# generated by tests with $NVIM_LOG_FILE set.
+/.nvimlog
+
# Files generated by scripts/vim-patch.sh
/.vim-src/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 141fa81e16..099f6e3787 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -240,6 +240,14 @@ if(HAS_DIAG_COLOR_FLAG)
add_definitions(-fdiagnostics-color=auto)
endif()
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.5")
+ # Array-bounds testing is broken in some GCC versions before 4.8.5.
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56273
+ add_definitions(-Wno-array-bounds)
+ endif()
+endif()
+
option(TRAVIS_CI_BUILD "Travis/QuickBuild CI. Extra flags will be set." OFF)
if(TRAVIS_CI_BUILD)
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index 69498dc1a1..a9efc90b87 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -100,20 +100,6 @@ add_custom_target(
${GENERATED_PACKAGE_TAGS}
)
-# Optional targets for nvim.desktop file and icon.
-find_program(XDG_MENU_PRG xdg-desktop-menu)
-find_program(XDG_ICON_PRG xdg-icon-resource)
-if(XDG_MENU_PRG)
- add_custom_target(desktop-file
- COMMAND xdg-desktop-menu install --novendor ${PROJECT_SOURCE_DIR}/runtime/nvim.desktop)
- # add_dependencies(runtime desktop-file)
-endif()
-if(XDG_ICON_PRG)
- add_custom_target(desktop-icon
- COMMAND xdg-icon-resource install --novendor --size 128 ${PROJECT_SOURCE_DIR}/runtime/nvim.png)
- # add_dependencies(runtime desktop-icon)
-endif()
-
# CMake is painful here. It will create the destination using the user's
# current umask, and we don't want that. And we don't just want to install
# the target directory, as it will mess with existing permissions. So this
@@ -128,6 +114,16 @@ install_helper(
FILES ${GENERATED_SYN_VIM}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/syntax/vim)
+if(NOT APPLE)
+ install_helper(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.desktop
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
+
+ install_helper(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps)
+endif()
+
file(GLOB_RECURSE RUNTIME_PROGRAMS
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.awk *.sh *.bat)
diff --git a/runtime/autoload/provider.vim b/runtime/autoload/provider.vim
new file mode 100644
index 0000000000..a4d5241b57
--- /dev/null
+++ b/runtime/autoload/provider.vim
@@ -0,0 +1,18 @@
+" Common functionality for providers
+
+let s:stderr = {}
+
+function! provider#stderr_collector(chan_id, data, event) dict
+ let stderr = get(s:stderr, a:chan_id, [''])
+ let stderr[-1] .= a:data[0]
+ call extend(stderr, a:data[1:])
+ let s:stderr[a:chan_id] = stderr
+endfunction
+
+function! provider#clear_stderr(chan_id)
+ silent! call delete(s:stderr, a:chan_id)
+endfunction
+
+function! provider#get_stderr(chan_id)
+ return get(s:stderr, a:chan_id, [])
+endfunction
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 86006497d9..8eb694e9fa 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -6,7 +6,7 @@ let s:paste = {}
" When caching is enabled, store the jobid of the xclip/xsel process keeping
" ownership of the selection, so we know how long the cache is valid.
-let s:selection = { 'owner': 0, 'data': [] }
+let s:selection = { 'owner': 0, 'data': [], 'on_stderr': function('provider#stderr_collector') }
function! s:selection.on_exit(jobid, data, event) abort
" At this point this nvim instance might already have launched
@@ -14,12 +14,13 @@ function! s:selection.on_exit(jobid, data, event) abort
if self.owner == a:jobid
let self.owner = 0
endif
-endfunction
-
-function! s:selection.on_stderr(jobid, data, event) abort
- echohl WarningMsg
- echomsg 'clipboard: error invoking '.get(self.argv, 0, '?').': '.join(a:data)
- echohl None
+ if a:data != 0
+ let stderr = provider#get_stderr(a:jobid)
+ echohl WarningMsg
+ echomsg 'clipboard: error invoking '.get(self.argv, 0, '?').': '.join(stderr)
+ echohl None
+ endif
+ call provider#clear_stderr(a:jobid)
endfunction
let s:selections = { '*': s:selection, '+': copy(s:selection)}
diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim
index 2f64c22c71..7285ed43ea 100644
--- a/runtime/autoload/provider/pythonx.vim
+++ b/runtime/autoload/provider/pythonx.vim
@@ -5,17 +5,7 @@ endif
let s:loaded_pythonx_provider = 1
-let s:stderr = {}
-let s:job_opts = {'rpc': v:true}
-
-" TODO(bfredl): this logic is common and should be builtin
-function! s:job_opts.on_stderr(chan_id, data, event)
- let stderr = get(s:stderr, a:chan_id, [''])
- let last = remove(stderr, -1)
- let a:data[0] = last.a:data[0]
- call extend(stderr, a:data)
- let s:stderr[a:chan_id] = stderr
-endfunction
+let s:job_opts = {'rpc': v:true, 'on_stderr': function('provider#stderr_collector')}
function! provider#pythonx#Require(host) abort
let ver = (a:host.orig_name ==# 'python') ? 2 : 3
@@ -38,9 +28,11 @@ function! provider#pythonx#Require(host) abort
catch
echomsg v:throwpoint
echomsg v:exception
- for row in get(s:stderr, channel_id, [])
+ for row in provider#get_stderr(channel_id)
echomsg row
endfor
+ finally
+ call provider#clear_stderr(channel_id)
endtry
throw remote#host#LoadErrorForHost(a:host.orig_name,
\ '$NVIM_PYTHON_LOG_FILE')
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index 89351c5b4f..43f8ccab06 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -143,7 +143,7 @@ a standard meaning:
Two 2 Hook
Nine 9 Horn
- Equals = Cyrillic (= used as second char)
+ Equals = Cyrillic (= used as second char)
Asterisk * Greek
Percent sign % Greek/Cyrillic special
Plus + smalls: Arabic, capitals: Hebrew
@@ -922,6 +922,7 @@ char digraph hex dec official name ~
† /- 2020 8224 DAGGER
‡ /= 2021 8225 DOUBLE DAGGER
‥ .. 2025 8229 TWO DOT LEADER
+… ,. 2026 8230 HORIZONTAL ELLIPSIS
‰ %0 2030 8240 PER MILLE SIGN
′ 1' 2032 8242 PRIME
″ 2' 2033 8243 DOUBLE PRIME
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 8882043162..3e75a42a62 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -4046,7 +4046,9 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
locale locale names (as output of locale -a)
mapping mapping name
menu menus
+ messages |:messages| suboptions
option options
+ packadd optional package |pack-add| names
shellcmd Shell command
sign |:sign| suboptions
syntax syntax file names |'syntax'|
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index bfcf621cb8..944f7474be 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -1202,6 +1202,7 @@ completion can be enabled:
-complete=locale locale names (as output of locale -a)
-complete=mapping mapping name
-complete=menu menus
+ -complete=messages |:messages| suboptions
-complete=option options
-complete=packadd optional package |pack-add| names
-complete=shellcmd Shell command
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 3032913500..1c1c3c364e 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -1,14 +1,26 @@
-## Source code overview
+Nvim core source
+================
-This document is an overview of how Nvim works internally, focusing on parts
-that are different from Vim. Since Nvim inherited from Vim, some information in
-[its README](https://raw.githubusercontent.com/vim/vim/master/src/README.txt)
-still applies.
+Module-specific details are documented at the top of each module (`terminal.c`,
+`screen.c`, ...).
-For module-specific details, read the source code. Some files are extensively
-commented at the top (e.g. terminal.c, screen.c).
+See `:help development` for more guidelines.
-### Source file name conventions
+Logs
+----
+
+Low-level log messages sink to `$NVIM_LOG_FILE`.
+
+You can use `LOG_CALLSTACK()` anywhere in the source to log the current
+stacktrace. (Currently Linux-only.)
+
+UI events are logged at level 0 (`DEBUG_LOG_LEVEL`).
+
+ rm -rf build/
+ make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
+
+Filename conventions
+--------------------
The source files use extensions to hint about their purpose.
@@ -19,10 +31,12 @@ The source files use extensions to hint about their purpose.
- `*.h.generated.h` - exported functions’ declarations.
- `*.c.generated.h` - static functions’ declarations.
-### Top-level program loops
+Nvim lifecycle
+--------------
+
+Following describes how Nvim processes input.
-Let's understand what a Vim-like program does by analyzing the workflow of
-a typical editing session:
+Consider a typical Vim-like editing session:
01. Vim dispays the welcome screen
02. User types: `:`
@@ -154,7 +168,8 @@ modes managed by the `state_enter` loop:
- insert mode: `insert_{enter,check,execute}()`(`edit.c`)
- terminal mode: `terminal_{enter,execute}()`(`terminal.c`)
-### Async event support
+Async event support
+-------------------
One of the features Nvim added is the support for handling arbitrary
asynchronous events, which can include:
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 90a564bb6a..724a8578ac 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1203,8 +1203,8 @@ do_buffer (
*/
while (buf == curbuf
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
- && (firstwin != lastwin || first_tabpage->tp_next != NULL)) {
- if (win_close(curwin, FALSE) == FAIL)
+ && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) {
+ if (win_close(curwin, false) == FAIL)
break;
}
@@ -4428,15 +4428,17 @@ do_arg_all (
continue;
}
}
- /* don't close last window */
- if (firstwin == lastwin
- && (first_tabpage->tp_next == NULL || !had_tab))
- use_firstwin = TRUE;
- else {
+ // don't close last window
+ if (ONE_WINDOW
+ && (first_tabpage->tp_next == NULL || !had_tab)) {
+ use_firstwin = true;
+ } else {
win_close(wp, !P_HID(buf) && !bufIsChanged(buf));
- /* check if autocommands removed the next window */
- if (!win_valid(wpnext))
- wpnext = firstwin; /* start all over... */
+ // check if autocommands removed the next window
+ if (!win_valid(wpnext)) {
+ // start all over...
+ wpnext = firstwin;
+ }
}
}
}
@@ -4593,7 +4595,7 @@ void ex_buffer_all(exarg_T *eap)
- tabline_height()
: wp->w_width != Columns)
|| (had_tab > 0 && wp != firstwin))
- && firstwin != lastwin
+ && !ONE_WINDOW
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
) {
win_close(wp, FALSE);
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 32e71f61f7..bfb1b94738 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -793,6 +793,7 @@ static digr_T digraphdefault[] =
{ '/', '-', 0x2020 },
{ '/', '=', 0x2021 },
{ '.', '.', 0x2025 },
+ { ',', '.', 0x2026 },
{ '%', '0', 0x2030 },
{ '1', '\'', 0x2032 },
{ '2', '\'', 0x2033 },
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 08b3d1dbd7..3ec8deb666 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -15141,7 +15141,8 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
const char *error = NULL;
- uint64_t id = channel_connect(tcp, address, 50, &error);
+ eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
+ uint64_t id = channel_connect(tcp, address, 50, (char *)IObuff, &error);
if (error) {
EMSG2(_("connection failed: %s"), error);
@@ -22449,8 +22450,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
if (data->rpc) {
- // the rpc channel takes over the in and out streams
- channel_from_process(proc, data->id);
+ eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
+ // RPC channel takes over the in/out streams.
+ channel_from_process(proc, data->id, (char *)IObuff);
} else {
wstream_init(proc->in, 0);
if (proc->out) {
@@ -22775,3 +22777,11 @@ bool eval_has_provider(const char *name)
return false;
}
+
+/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
+void eval_format_source_name_line(char *buf, size_t bufsize)
+{
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR,
+ (sourcing_name ? sourcing_name : (char_u *)"?"),
+ (sourcing_name ? sourcing_lnum : 0));
+}
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 0987cb3915..a555fb77e8 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2802,16 +2802,18 @@ void ex_z(exarg_T *eap)
int j;
linenr_T lnum = eap->line2;
- /* Vi compatible: ":z!" uses display height, without a count uses
- * 'scroll' */
- if (eap->forceit)
+ // Vi compatible: ":z!" uses display height, without a count uses
+ // 'scroll'
+ if (eap->forceit) {
bigness = curwin->w_height;
- else if (firstwin == lastwin)
+ } else if (ONE_WINDOW) {
bigness = curwin->w_p_scr * 2;
- else
+ } else {
bigness = curwin->w_height - 3;
- if (bigness < 1)
+ }
+ if (bigness < 1) {
bigness = 1;
+ }
x = eap->arg;
kind = x;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5d7246581c..d7821fc636 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -844,8 +844,6 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
break;
case ET_INTERRUPT:
break;
- default:
- p = vim_strsave((char_u *)_(e_internal));
}
saved_sourcing_name = sourcing_name;
@@ -3440,6 +3438,11 @@ const char * set_one_cmd_context(
xp->xp_pattern = (char_u *)arg;
break;
+ case CMD_messages:
+ xp->xp_context = EXPAND_MESSAGES;
+ xp->xp_pattern = (char_u *)arg;
+ break;
+
case CMD_history:
xp->xp_context = EXPAND_HISTORY;
xp->xp_pattern = (char_u *)arg;
@@ -4874,6 +4877,7 @@ static struct {
#endif
{ EXPAND_MAPPINGS, "mapping" },
{ EXPAND_MENUS, "menu" },
+ { EXPAND_MESSAGES, "messages" },
{ EXPAND_OWNSYNTAX, "syntax" },
{ EXPAND_SYNTIME, "syntime" },
{ EXPAND_SETTINGS, "option" },
@@ -5976,7 +5980,7 @@ static void ex_quit(exarg_T *eap)
// specified. Example:
// :h|wincmd w|1q - don't quit
// :h|wincmd w|q - quit
- if (only_one_window() && (firstwin == lastwin || eap->addr_count == 0)) {
+ if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0)) {
getout(0);
}
/* close window; may free buffer */
@@ -6174,12 +6178,14 @@ static void ex_tabonly(exarg_T *eap)
*/
void tabpage_close(int forceit)
{
- /* First close all the windows but the current one. If that worked then
- * close the last window in this tab, that will close it. */
- if (lastwin != firstwin)
- close_others(TRUE, forceit);
- if (lastwin == firstwin)
+ // First close all the windows but the current one. If that worked then
+ // close the last window in this tab, that will close it.
+ if (!ONE_WINDOW) {
+ close_others(true, forceit);
+ }
+ if (ONE_WINDOW) {
ex_win_close(forceit, curwin, NULL);
+ }
}
/*
@@ -9593,6 +9599,16 @@ char_u *get_behave_arg(expand_T *xp, int idx)
return NULL;
}
+// Function given to ExpandGeneric() to obtain the possible arguments of the
+// ":messages {clear}" command.
+char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx == 0) {
+ return (char_u *)"clear";
+ }
+ return NULL;
+}
+
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 5d664b94a8..c029df2f13 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -374,10 +374,9 @@ int do_intthrow(struct condstack *cstack)
return TRUE;
}
-/*
- * Get an exception message that is to be stored in current_exception->value.
- */
-char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should_free)
+// Get an exception message that is to be stored in current_exception->value.
+char_u *get_exception_string(void *value, except_type_T type, char_u *cmdname,
+ int *should_free)
{
char_u *ret, *mesg;
char_u *p, *val;
@@ -435,13 +434,11 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should
}
-/*
- * Throw a new exception. Return FAIL when out of memory or it was tried to
- * throw an illegal user exception. "value" is the exception string for a
- * user or interrupt exception, or points to a message list in case of an
- * error exception.
- */
-static int throw_exception(void *value, int type, char_u *cmdname)
+// Throw a new exception. Return FAIL when out of memory or it was tried to
+// throw an illegal user exception. "value" is the exception string for a
+// user or interrupt exception, or points to a message list in case of an
+// error exception.
+static int throw_exception(void *value, except_type_T type, char_u *cmdname)
{
except_T *excp;
int should_free;
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index f61e01d25b..d5f8737bf3 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -89,28 +89,29 @@ struct msglist {
struct msglist *next; /* next of several messages in a row */
};
+// The exception types.
+typedef enum
+{
+ ET_USER, // exception caused by ":throw" command
+ ET_ERROR, // error exception
+ ET_INTERRUPT // interrupt exception triggered by Ctrl-C
+} except_type_T;
+
/*
* Structure describing an exception.
* (don't use "struct exception", it's used by the math library).
*/
typedef struct vim_exception except_T;
struct vim_exception {
- int type; /* exception type */
- char_u *value; /* exception value */
- struct msglist *messages; /* message(s) causing error exception */
- char_u *throw_name; /* name of the throw point */
- linenr_T throw_lnum; /* line number of the throw point */
- except_T *caught; /* next exception on the caught stack */
+ except_type_T type; // exception type
+ char_u *value; // exception value
+ struct msglist *messages; // message(s) causing error exception
+ char_u *throw_name; // name of the throw point
+ linenr_T throw_lnum; // line number of the throw point
+ except_T *caught; // next exception on the caught stack
};
/*
- * The exception types.
- */
-#define ET_USER 0 /* exception caused by ":throw" command */
-#define ET_ERROR 1 /* error exception */
-#define ET_INTERRUPT 2 /* interrupt exception triggered by Ctrl-C */
-
-/*
* Structure to save the error/interrupt/exception state between calls to
* enter_cleanup() and leave_cleanup(). Must be allocated as an automatic
* variable by the (common) caller of these functions.
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 0ba6c79a71..1d81a39dfe 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4050,6 +4050,7 @@ ExpandFromContext (
} tab[] = {
{ EXPAND_COMMANDS, get_command_name, false, true },
{ EXPAND_BEHAVE, get_behave_arg, true, true },
+ { EXPAND_MESSAGES, get_messages_arg, true, true },
{ EXPAND_HISTORY, get_history_arg, true, true },
{ EXPAND_USER_COMMANDS, get_user_commands, false, true },
{ EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index acdb25ca67..d2b90db707 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -119,7 +119,7 @@ for i = 1, #events do
write_signature(bridge_output, ev, 'UI *ui')
bridge_output:write('\n{\n')
bridge_output:write(send)
- bridge_output:write(' UI_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
+ bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
end
end
@@ -128,6 +128,7 @@ for i = 1, #events do
call_output:write('\n{\n')
if ev.remote_only then
write_arglist(call_output, ev, false)
+ call_output:write(' UI_LOG('..ev.name..', 0);\n')
call_output:write(' ui_event("'..ev.name..'", args);\n')
else
call_output:write(' UI_CALL')
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 6d1bd1de12..f08812600f 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -494,6 +494,7 @@ EXTERN int updating_screen INIT(= FALSE);
EXTERN win_T *firstwin; /* first window */
EXTERN win_T *lastwin; /* last window */
EXTERN win_T *prevwin INIT(= NULL); /* previous window */
+# define ONE_WINDOW (firstwin == lastwin)
/*
* When using this macro "break" only breaks out of the inner loop. Use "goto"
* to break out of the tabpage loop.
diff --git a/src/nvim/log.c b/src/nvim/log.c
index f1dbe61dda..252fe5438d 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -173,6 +173,53 @@ FILE *open_log_file(void)
return stderr;
}
+#if defined(__linux__)
+# include <execinfo.h>
+void log_callstack(const char *const func_name, const int line_num)
+{
+ void *trace[100];
+ int trace_size = backtrace(trace, ARRAY_SIZE(trace));
+
+ char exepath[MAXPATHL] = { 0 };
+ size_t exepathlen = MAXPATHL;
+ if (os_exepath(exepath, &exepathlen) != 0) {
+ abort();
+ }
+ assert(24 + exepathlen < IOSIZE); // Must fit in `cmdbuf` below.
+
+ do_log(DEBUG_LOG_LEVEL, func_name, line_num, true, "trace:");
+
+ char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace))];
+ snprintf(cmdbuf, sizeof(cmdbuf), "addr2line -e %s -f -p", exepath);
+ for (int i = 1; i < trace_size; i++) {
+ char buf[20]; // 64-bit pointer 0xNNNNNNNNNNNNNNNN with leading space.
+ snprintf(buf, sizeof(buf), " %p", trace[i]);
+ xstrlcat(cmdbuf, buf, sizeof(cmdbuf));
+ }
+ // Now we have a command string like:
+ // addr2line -e /path/to/exe -f -p 0x123 0x456 ...
+
+ log_lock();
+ FILE *log_file = open_log_file();
+ if (log_file == NULL) {
+ goto end;
+ }
+
+ FILE *fp = popen(cmdbuf, "r");
+ char linebuf[IOSIZE];
+ while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) {
+ fprintf(log_file, " %s", linebuf);
+ }
+ pclose(fp);
+
+ if (log_file != stderr && log_file != stdout) {
+ fclose(log_file);
+ }
+end:
+ log_unlock();
+}
+#endif
+
static bool do_log_to_file(FILE *log_file, int log_level,
const char *func_name, int line_num, bool eol,
const char* fmt, ...)
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 221f0bbaf6..2bd18f5776 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -61,6 +61,10 @@
__VA_ARGS__)
#endif
+#if defined(__linux__)
+# define LOG_CALLSTACK() log_callstack(__func__, __LINE__)
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "log.h.generated.h"
#endif
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 74c58fb203..328b96fd5c 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -585,7 +585,7 @@ void free_all_mem(void)
p_ea = false;
if (first_tabpage->tp_next != NULL)
do_cmdline_cmd("tabonly!");
- if (firstwin != lastwin)
+ if (!ONE_WINDOW)
do_cmdline_cmd("only!");
/* Free all spell info. */
diff --git a/src/nvim/move.c b/src/nvim/move.c
index d5be4cb8c3..81d46a7f17 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1763,7 +1763,7 @@ int onepage(int dir, long count)
loff.fill = 0;
if (dir == FORWARD) {
- if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) {
+ if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
/* Vi compatible scrolling */
if (p_window <= 2)
++curwin->w_topline;
@@ -1797,7 +1797,7 @@ int onepage(int dir, long count)
max_topfill();
continue;
}
- if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) {
+ if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
/* Vi compatible scrolling (sort of) */
if (p_window <= 2)
--curwin->w_topline;
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 68ac35bc4e..6fd1af1ba6 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -117,12 +117,15 @@ void channel_teardown(void)
/// Creates an API channel by starting a process and connecting to its
/// stdin/stdout. stderr is handled by the job infrastructure.
///
-/// @param argv The argument vector for the process. [consumed]
-/// @return The channel id (> 0), on success.
-/// 0, on error.
-uint64_t channel_from_process(Process *proc, uint64_t id)
+/// @param proc process object
+/// @param id (optional) channel id
+/// @param source description of source function, rplugin name, TCP addr, etc
+///
+/// @return Channel id (> 0), on success. 0, on error.
+uint64_t channel_from_process(Process *proc, uint64_t id, char *source)
{
- Channel *channel = register_channel(kChannelTypeProc, id, proc->events);
+ Channel *channel = register_channel(kChannelTypeProc, id, proc->events,
+ source);
incref(channel); // process channels are only closed by the exit_cb
channel->data.proc = proc;
@@ -138,7 +141,8 @@ uint64_t channel_from_process(Process *proc, uint64_t id)
/// @param watcher The SocketWatcher ready to accept the connection
void channel_from_connection(SocketWatcher *watcher)
{
- Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeSocket, 0, NULL,
+ watcher->addr);
socket_watcher_accept(watcher, &channel->data.stream);
incref(channel); // close channel only after the stream is closed
channel->data.stream.internal_close_cb = close_cb;
@@ -148,8 +152,9 @@ void channel_from_connection(SocketWatcher *watcher)
rstream_start(&channel->data.stream, receive_msgpack, channel);
}
-uint64_t channel_connect(bool tcp, const char *address,
- int timeout, const char **error)
+/// @param source description of source function, rplugin name, TCP addr, etc
+uint64_t channel_connect(bool tcp, const char *address, int timeout,
+ char *source, const char **error)
{
if (!tcp) {
char *path = fix_fname(address);
@@ -161,7 +166,7 @@ uint64_t channel_connect(bool tcp, const char *address,
xfree(path);
}
- Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeSocket, 0, NULL, source);
if (!socket_connect(&main_loop, &channel->data.stream,
tcp, address, timeout, error)) {
decref(channel);
@@ -329,11 +334,10 @@ bool channel_close(uint64_t id)
return true;
}
-/// Creates an API channel from stdin/stdout. This is used when embedding
-/// Neovim
+/// Creates an API channel from stdin/stdout. Used to embed Nvim.
void channel_from_stdio(void)
{
- Channel *channel = register_channel(kChannelTypeStdio, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeStdio, 0, NULL, NULL);
incref(channel); // stdio channels are only closed on exit
// read stream
rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE);
@@ -346,7 +350,7 @@ void channel_from_stdio(void)
/// when an instance connects to its own named pipe.
uint64_t channel_create_internal(void)
{
- Channel *channel = register_channel(kChannelTypeInternal, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeInternal, 0, NULL, NULL);
incref(channel); // internal channel lives until process exit
return channel->id;
}
@@ -745,9 +749,12 @@ static void close_cb(Stream *stream, void *data)
decref(data);
}
+/// @param source description of source function, rplugin name, TCP addr, etc
static Channel *register_channel(ChannelType type, uint64_t id,
- MultiQueue *events)
+ MultiQueue *events, char *source)
{
+ // Jobs and channels share the same id namespace.
+ assert(id == 0 || !pmap_get(uint64_t)(channels, id));
Channel *rv = xmalloc(sizeof(Channel));
rv->events = events ? events : multiqueue_new_child(main_loop.events);
rv->type = type;
@@ -761,6 +768,14 @@ static Channel *register_channel(ChannelType type, uint64_t id,
kv_init(rv->call_stack);
kv_init(rv->delayed_notifications);
pmap_put(uint64_t)(channels, rv->id, rv);
+
+ ILOG("new channel %" PRIu64 " (%s): %s", rv->id,
+ (type == kChannelTypeProc ? "proc"
+ : (type == kChannelTypeSocket ? "socket"
+ : (type == kChannelTypeStdio ? "stdio"
+ : (type == kChannelTypeInternal ? "internal" : "?")))),
+ (source ? source : "?"));
+
return rv;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index d891c74fd2..c1676780d8 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3657,6 +3657,39 @@ nv_gd (
}
}
+// Return true if line[offset] is not inside a C-style comment or string, false
+// otherwise.
+static bool is_ident(char_u *line, int offset)
+{
+ bool incomment = false;
+ int instring = 0;
+ int prev = 0;
+
+ for (int i = 0; i < offset && line[i] != NUL; i++) {
+ if (instring != 0) {
+ if (prev != '\\' && line[i] == instring) {
+ instring = 0;
+ }
+ } else if ((line[i] == '"' || line[i] == '\'') && !incomment) {
+ instring = line[i];
+ } else {
+ if (incomment) {
+ if (prev == '*' && line[i] == '/') {
+ incomment = false;
+ }
+ } else if (prev == '/' && line[i] == '*') {
+ incomment = true;
+ } else if (prev == '/' && line[i] == '/') {
+ return false;
+ }
+ }
+
+ prev = line[i];
+ }
+
+ return incomment == false && instring == 0;
+}
+
/*
* Search for variable declaration of "ptr[len]".
* When "locally" is true in the current function ("gd"), otherwise in the
@@ -3683,6 +3716,7 @@ find_decl (
bool retval = true;
bool incll;
int searchflags = flags_arg;
+ bool valid;
pat = xmalloc(len + 7);
@@ -3717,6 +3751,7 @@ find_decl (
/* Search forward for the identifier, ignore comment lines. */
clearpos(&found_pos);
for (;; ) {
+ valid = false;
t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD,
pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL);
if (curwin->w_cursor.lnum >= old_pos.lnum)
@@ -3747,20 +3782,35 @@ find_decl (
curwin->w_cursor.col = 0;
continue;
}
- if (!locally) /* global search: use first match found */
+ valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col);
+
+ // If the current position is not a valid identifier and a previous match is
+ // present, favor that one instead.
+ if (!valid && found_pos.lnum != 0) {
+ curwin->w_cursor = found_pos;
break;
- if (curwin->w_cursor.lnum >= par_pos.lnum) {
- /* If we previously found a valid position, use it. */
- if (found_pos.lnum != 0)
+ }
+ // global search: use first match found
+ if (valid && !locally) {
+ break;
+ }
+ if (valid && curwin->w_cursor.lnum >= par_pos.lnum) {
+ // If we previously found a valid position, use it.
+ if (found_pos.lnum != 0) {
curwin->w_cursor = found_pos;
+ }
break;
}
- // For finding a local variable and the match is before the "{" search
- // to find a later match. For K&R style function declarations this
- // skips the function header without types. Remove SEARCH_START from
- // flags to avoid getting stuck at one position.
- found_pos = curwin->w_cursor;
+ // For finding a local variable and the match is before the "{" or
+ // inside a comment, continue searching. For K&R style function
+ // declarations this skips the function header without types.
+ if (!valid) {
+ clearpos(&found_pos);
+ } else {
+ found_pos = curwin->w_cursor;
+ }
+ // Remove SEARCH_START from flags to avoid getting stuck at one position.
searchflags &= ~SEARCH_START;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 75b56b4eb4..a6ae022e79 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4078,7 +4078,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
}
/* Change window height NOW */
- if (lastwin != firstwin) {
+ if (!ONE_WINDOW) {
if (pp == &p_wh && curwin->w_height < p_wh)
win_setheight((int)p_wh);
if (pp == &p_hh && curbuf->b_help && curwin->w_height < p_hh)
@@ -4107,7 +4107,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
}
/* Change window width NOW */
- if (lastwin != firstwin && curwin->w_width < p_wiw)
+ if (!ONE_WINDOW && curwin->w_width < p_wiw)
win_setwidth((int)p_wiw);
}
/* 'winminwidth' */
@@ -5239,7 +5239,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
void comp_col(void)
{
- int last_has_status = (p_ls == 2 || (p_ls == 1 && firstwin != lastwin));
+ int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW));
sc_col = 0;
ru_col = 0;
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index f17075f0c4..4997209556 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -1878,7 +1878,7 @@ win_found:
* If there is only one window and it is the quickfix window, create a
* new one above the quickfix window.
*/
- if (((firstwin == lastwin) && bt_quickfix(curbuf)) || !usable_win) {
+ if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) {
flags = WSP_ABOVE;
if (ll_ref != NULL)
flags |= WSP_NEWLOC;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index a8353153fd..bcc996679b 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -991,7 +991,7 @@ static void win_update(win_T *wp)
* first. */
if (mid_start == 0) {
mid_end = wp->w_height;
- if (lastwin == firstwin) {
+ if (ONE_WINDOW) {
/* Clear the screen when it was not done by win_del_lines() or
* win_ins_lines() above, "screen_cleared" is FALSE or MAYBE
* then. */
@@ -7160,7 +7160,7 @@ static int fillchar_status(int *attr, win_T *wp)
* window differs, or the fillchars differ, or this is not the
* current window */
if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
- || !is_curwin || firstwin == lastwin)
+ || !is_curwin || ONE_WINDOW)
|| (fill_stl != fill_stlnc))) {
return fill;
}
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 117ba52eb6..39ffabc024 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -79,7 +79,11 @@ let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
func RunTheTest(test)
echo 'Executing ' . a:test
if exists("*SetUp")
- call SetUp()
+ try
+ call SetUp()
+ catch
+ call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
endif
call add(s:messages, 'Executing ' . a:test)
@@ -94,7 +98,11 @@ func RunTheTest(test)
endtry
if exists("*TearDown")
- call TearDown()
+ try
+ call TearDown()
+ catch
+ call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
endif
" Close any extra windows and make the current one not modified.
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 0c9d1297d6..f7a6aba6e7 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -130,6 +130,11 @@ func Test_getcompletion()
let l = getcompletion('dark', 'highlight')
call assert_equal([], l)
+ let l = getcompletion('', 'messages')
+ call assert_true(index(l, 'clear') >= 0)
+ let l = getcompletion('not', 'messages')
+ call assert_equal([], l)
+
if has('cscope')
let l = getcompletion('', 'cscope')
let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show']
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index b6ac5720c3..2573401707 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -1,22 +1,277 @@
" Test commands that jump somewhere.
-func Test_geeDEE()
+" Create a new buffer using "lines" and place the cursor on the word after the
+" first occurrence of return and invoke "cmd". The cursor should now be
+" positioned at the given line and col.
+func XTest_goto_decl(cmd, lines, line, col)
new
- call setline(1, ["Filename x;", "", "int Filename", "int func() {", "Filename y;"])
- /y;/
- normal gD
- call assert_equal(1, line('.'))
+ call setline(1, a:lines)
+ /return/
+ normal! W
+ execute 'norm! ' . a:cmd
+ call assert_equal(a:line, line('.'))
+ call assert_equal(a:col, col('.'))
quit!
endfunc
-func Test_gee_dee()
- new
- call setline(1, ["int x;", "", "int func(int x)", "{", " return x;", "}"])
- /return/
- normal $hgd
- call assert_equal(3, line('.'))
- call assert_equal(14, col('.'))
- quit!
+func Test_gD()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 5)
+endfunc
+
+func Test_gD_too()
+ let lines = [
+ \ 'Filename x;',
+ \ '',
+ \ 'int Filename',
+ \ 'int func() {',
+ \ ' Filename x;',
+ \ ' return x;',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 10)
+endfunc
+
+func Test_gD_comment()
+ let lines = [
+ \ '/* int x; */',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_inline_comment()
+ let lines = [
+ \ 'int y /* , x */;',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_string()
+ let lines = [
+ \ 'char *s[] = "x";',
+ \ 'int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_string_same_line()
+ let lines = [
+ \ 'char *s[] = "x", int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 22)
+endfunc
+
+func Test_gD_char()
+ let lines = [
+ \ "char c = 'x';",
+ \ 'int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gd()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 14)
+endfunc
+
+func Test_gd_not_local()
+ let lines = [
+ \ 'int func1(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ '',
+ \ 'int func2(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gd_kr_style()
+ let lines = [
+ \ 'int func(x)',
+ \ ' int x;',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 2, 7)
+endfunc
+
+func Test_gd_missing_braces()
+ let lines = [
+ \ 'def func1(a)',
+ \ ' a + 1',
+ \ 'end',
+ \ '',
+ \ 'a = 1',
+ \ '',
+ \ 'def func2()',
+ \ ' return a',
+ \ 'end',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 11)
+endfunc
+
+func Test_gd_comment()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* int x; */',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gd_comment_in_string()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s ="//"; int x;',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 3, 22)
+endfunc
+
+func Test_gd_string_in_comment()
+ set comments=
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* " */ int x;',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 3, 15)
+ set comments&
+endfunc
+
+func Test_gd_inline_comment()
+ let lines = [
+ \ 'int func(/* x is an int */ int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 32)
+endfunc
+
+func Test_gd_inline_comment_only()
+ let lines = [
+ \ 'int func(void) /* one lonely x */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gd_inline_comment_body()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' int y /* , x */;',
+ \ '',
+ \ ' for (/* int x = 0 */; y < 2; y++);',
+ \ '',
+ \ ' int x = 0;',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 7, 7)
+endfunc
+
+func Test_gd_trailing_multiline_comment()
+ let lines = [
+ \ 'int func(int x) /* x is an int */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
+endfunc
+
+func Test_gd_trailing_comment()
+ let lines = [
+ \ 'int func(int x) // x is an int',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
+endfunc
+
+func Test_gd_string()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s = "x";',
+ \ ' int x = 1;',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gd_string_only()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s = "x";',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 5, 10)
endfunc
" Check that setting 'cursorline' does not change curswant
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index ab78a72909..0975b87ea3 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1007,7 +1007,7 @@ static void tui_flush(UI *ui)
size_t nrevents = loop_size(data->loop);
if (nrevents > TOO_MANY_EVENTS) {
- ILOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
+ WLOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
// Back-pressure: UI events may accumulate much faster than the terminal
// device can serve them. Even if SIGINT/CTRL-C is received, user must still
// wait for the TUI event-queue to drain, and if there are ~millions of
@@ -1690,7 +1690,7 @@ static const char *tui_get_stty_erase(void)
if (tcgetattr(input_global_fd(), &t) != -1) {
stty_erase[0] = (char)t.c_cc[VERASE];
stty_erase[1] = '\0';
- ILOG("stty/termios:erase=%s", stty_erase);
+ DLOG("stty/termios:erase=%s", stty_erase);
}
#endif
return stty_erase;
@@ -1707,12 +1707,12 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value,
}
if (strequal(name, "key_backspace")) {
- ILOG("libtermkey:kbs=%s", value);
+ DLOG("libtermkey:kbs=%s", value);
if (stty_erase[0] != 0) {
return stty_erase;
}
} else if (strequal(name, "key_dc")) {
- ILOG("libtermkey:kdch1=%s", value);
+ DLOG("libtermkey:kdch1=%s", value);
// Vim: "If <BS> and <DEL> are now the same, redefine <DEL>."
if (value != NULL && strequal(stty_erase, value)) {
return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 0a2154438f..a60c061949 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -8,6 +8,7 @@
#include <limits.h>
#include "nvim/vim.h"
+#include "nvim/log.h"
#include "nvim/ui.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
@@ -59,6 +60,27 @@ static int busy = 0;
static int height, width;
static int old_mode_idx = -1;
+#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
+# define UI_LOG(funname, ...)
+#else
+static size_t uilog_seen = 0;
+static char uilog_last_event[1024] = { 0 };
+# define UI_LOG(funname, ...) \
+ do { \
+ if (strequal(uilog_last_event, STR(funname))) { \
+ uilog_seen++; \
+ } else { \
+ if (uilog_seen > 0) { \
+ do_log(DEBUG_LOG_LEVEL, "ui", 0, true, \
+ "%s (+%zu times...)", uilog_last_event, uilog_seen); \
+ } \
+ DLOG("ui: " STR(funname)); \
+ uilog_seen = 0; \
+ xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
+ } \
+ } while (0)
+#endif
+
// UI_CALL invokes a function on all registered UI instances. The functions can
// have 0-5 arguments (configurable by SELECT_NTH).
//
@@ -67,6 +89,7 @@ static int old_mode_idx = -1;
# define UI_CALL(funname, ...) \
do { \
flush_cursor_update(); \
+ UI_LOG(funname, 0); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
UI_CALL_MORE(funname, __VA_ARGS__); \
@@ -76,6 +99,7 @@ static int old_mode_idx = -1;
# define UI_CALL(...) \
do { \
flush_cursor_update(); \
+ UI_LOG(__VA_ARGS__, 0); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
@@ -85,6 +109,7 @@ static int old_mode_idx = -1;
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
+// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 0165db7c0c..5585886612 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -24,30 +24,10 @@
#define UI(b) (((UIBridgeData *)b)->ui)
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
-static size_t uilog_seen = 0;
-static argv_callback uilog_event = NULL;
-#define UI_CALL(ui, name, argc, ...) \
- do { \
- if (uilog_event == ui_bridge_##name##_event) { \
- uilog_seen++; \
- } else { \
- if (uilog_seen > 0) { \
- DLOG("UI bridge: ...%zu times", uilog_seen); \
- } \
- DLOG("UI bridge: " STR(name)); \
- uilog_seen = 0; \
- uilog_event = ui_bridge_##name##_event; \
- } \
- ((UIBridgeData *)ui)->scheduler( \
- event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \
- } while (0)
-#else
// Schedule a function call on the UI bridge thread.
-#define UI_CALL(ui, name, argc, ...) \
+#define UI_BRIDGE_CALL(ui, name, argc, ...) \
((UIBridgeData *)ui)->scheduler( \
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
-#endif
#define INT2PTR(i) ((void *)(intptr_t)i)
#define PTR2INT(p) ((Integer)(intptr_t)p)
@@ -128,7 +108,7 @@ static void ui_bridge_stop(UI *b)
{
UIBridgeData *bridge = (UIBridgeData *)b;
bool stopped = bridge->stopped = false;
- UI_CALL(b, stop, 1, b);
+ UI_BRIDGE_CALL(b, stop, 1, b);
for (;;) {
uv_mutex_lock(&bridge->mutex);
stopped = bridge->stopped;
@@ -154,7 +134,7 @@ static void ui_bridge_highlight_set(UI *b, HlAttrs attrs)
{
HlAttrs *a = xmalloc(sizeof(HlAttrs));
*a = attrs;
- UI_CALL(b, highlight_set, 2, b, a);
+ UI_BRIDGE_CALL(b, highlight_set, 2, b, a);
}
static void ui_bridge_highlight_set_event(void **argv)
{
@@ -167,7 +147,7 @@ static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;
uv_mutex_lock(&data->mutex);
- UI_CALL(b, suspend, 1, b);
+ UI_BRIDGE_CALL(b, suspend, 1, b);
data->ready = false;
// suspend the main thread until CONTINUE is called by the UI thread
while (!data->ready) {
diff --git a/src/nvim/version.c b/src/nvim/version.c
index d372779c75..b585c3ad9a 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -641,7 +641,7 @@ static const int included_patches[] = {
// 91,
// 90,
// 89 NA
- // 88,
+ 88,
// 87 NA
// 86,
85,
@@ -654,20 +654,20 @@ static const int included_patches[] = {
78,
// 77 NA
// 76 NA
- // 75,
+ 75,
// 74,
- // 73,
+ 73,
// 72 NA
// 71 NA
// 70 NA
- // 69,
+ 69,
68,
// 67 NA
66,
// 65 NA
64,
// 63,
- // 62,
+ 62,
// 61 NA
60,
// 59 NA
@@ -695,7 +695,7 @@ static const int included_patches[] = {
37,
// 36 NA
35,
- // 34,
+ 34,
33,
32,
31,
@@ -704,9 +704,9 @@ static const int included_patches[] = {
// 28 NA
// 27 NA
26,
- // 25,
+ 25,
// 24 NA
- // 23,
+ 23,
// 22 NA
// 21,
// 20,
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index c71ca411ea..62ffc7433e 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -163,6 +163,7 @@ enum {
EXPAND_SYNTIME,
EXPAND_USER_ADDR_TYPE,
EXPAND_PACKADD,
+ EXPAND_MESSAGES,
};
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 29f5412ba0..faf5bceb56 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -193,7 +193,7 @@ newwindow:
/* cursor to previous window with wrap around */
case 'W':
CHECK_CMDWIN
- if (firstwin == lastwin && Prenum != 1) /* just one window */
+ if (ONE_WINDOW && Prenum != 1) /* just one window */
beep_flush();
else {
if (Prenum) { /* go to specified window */
@@ -574,7 +574,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
oldwin = curwin;
/* add a status line when p_ls == 1 and splitting the first window */
- if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) {
+ if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) {
if (oldwin->w_height <= p_wmh && new_wp == NULL) {
EMSG(_(e_noroom));
return FAIL;
@@ -1182,7 +1182,7 @@ static void win_exchange(long Prenum)
win_T *wp2;
int temp;
- if (lastwin == firstwin) { /* just one window */
+ if (ONE_WINDOW) { /* just one window */
beep_flush();
return;
}
@@ -1271,7 +1271,7 @@ static void win_rotate(int upwards, int count)
frame_T *frp;
int n;
- if (firstwin == lastwin) { /* nothing to do */
+ if (ONE_WINDOW) { /* nothing to do */
beep_flush();
return;
}
@@ -1343,7 +1343,7 @@ static void win_totop(int size, int flags)
int dir;
int height = curwin->w_height;
- if (lastwin == firstwin) {
+ if (ONE_WINDOW) {
beep_flush();
return;
}
@@ -1728,7 +1728,7 @@ void close_windows(buf_T *buf, int keep_curwin)
++RedrawingDisabled;
- for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) {
+ for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW; ) {
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
if (win_close(wp, false) == FAIL) {
@@ -1810,7 +1810,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf,
tabpage_T *prev_curtab)
FUNC_ATTR_NONNULL_ARG(1)
{
- if (firstwin != lastwin) {
+ if (!ONE_WINDOW) {
return false;
}
buf_T *old_curbuf = curbuf;
@@ -2194,7 +2194,7 @@ winframe_remove (
/*
* If there is only one window there is nothing to remove.
*/
- if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin)
+ if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
return NULL;
/*
@@ -2331,7 +2331,7 @@ win_altframe (
frame_T *frp;
int b;
- if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin)
+ if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
/* Last window in this tab page, will go to next tab page. */
return alt_tabpage()->tp_curwin->w_frame;
@@ -2851,7 +2851,7 @@ close_others (
win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
}
- if (message && lastwin != firstwin)
+ if (message && !ONE_WINDOW)
EMSG(_("E445: Other window contains changes"));
}
@@ -5173,7 +5173,7 @@ last_status (
{
/* Don't make a difference between horizontal or vertical split. */
last_status_rec(topframe, (p_ls == 2
- || (p_ls == 1 && (morewin || lastwin != firstwin))));
+ || (p_ls == 1 && (morewin || !ONE_WINDOW))));
}
static void last_status_rec(frame_T *fr, int statusline)