mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
3e0536eb29
3
.gitignore
vendored
3
.gitignore
vendored
@ -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/
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
18
runtime/autoload/provider.vim
Normal file
18
runtime/autoload/provider.vim
Normal file
@ -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
|
@ -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)}
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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'|
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
--------------
|
||||
|
||||
Let's understand what a Vim-like program does by analyzing the workflow of
|
||||
a typical editing session:
|
||||
Following describes how Nvim processes input.
|
||||
|
||||
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:
|
||||
|
@ -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);
|
||||
|
@ -793,6 +793,7 @@ static digr_T digraphdefault[] =
|
||||
{ '/', '-', 0x2020 },
|
||||
{ '/', '=', 0x2021 },
|
||||
{ '.', '.', 0x2025 },
|
||||
{ ',', '.', 0x2026 },
|
||||
{ '%', '0', 0x2030 },
|
||||
{ '1', '\'', 0x2032 },
|
||||
{ '2', '\'', 0x2033 },
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -89,27 +89,28 @@ 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
|
||||
|
@ -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 },
|
||||
|
@ -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')
|
||||
|
@ -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.
|
||||
|
@ -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, ...)
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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']
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -163,6 +163,7 @@ enum {
|
||||
EXPAND_SYNTIME,
|
||||
EXPAND_USER_ADDR_TYPE,
|
||||
EXPAND_PACKADD,
|
||||
EXPAND_MESSAGES,
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user