Merge remote-tracking branch 'upstream/master'

This commit is contained in:
ckelsel 2017-07-28 08:38:08 +08:00
commit 3e0536eb29
38 changed files with 652 additions and 193 deletions

3
.gitignore vendored
View File

@ -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/

View File

@ -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)

View File

@ -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)

View 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

View File

@ -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)}

View File

@ -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')

View File

@ -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

View File

@ -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'|

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -793,6 +793,7 @@ static digr_T digraphdefault[] =
{ '/', '-', 0x2020 },
{ '/', '=', 0x2021 },
{ '.', '.', 0x2025 },
{ ',', '.', 0x2026 },
{ '%', '0', 0x2030 },
{ '1', '\'', 0x2032 },
{ '2', '\'', 0x2033 },

View File

@ -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));
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 },

View File

@ -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')

View File

@ -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.

View File

@ -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, ...)

View File

@ -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

View File

@ -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. */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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.

View File

@ -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']

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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) {

View File

@ -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,

View File

@ -163,6 +163,7 @@ enum {
EXPAND_SYNTIME,
EXPAND_USER_ADDR_TYPE,
EXPAND_PACKADD,
EXPAND_MESSAGES,
};

View File

@ -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)