aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.asan-blacklist4
-rwxr-xr-xsrc/clint.py22
-rw-r--r--src/nvim/CMakeLists.txt35
-rw-r--r--src/nvim/README.md112
-rw-r--r--src/nvim/api/buffer.c55
-rw-r--r--src/nvim/api/private/defs.h2
-rw-r--r--src/nvim/api/private/helpers.c137
-rw-r--r--src/nvim/api/private/helpers.h15
-rw-r--r--src/nvim/api/ui.c73
-rw-r--r--src/nvim/api/ui_events.in.h34
-rw-r--r--src/nvim/api/vim.c730
-rw-r--r--src/nvim/arabic.c1070
-rw-r--r--src/nvim/ascii.h13
-rw-r--r--src/nvim/aucmd.c41
-rw-r--r--src/nvim/aucmd.h9
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/buffer.c287
-rw-r--r--src/nvim/buffer_defs.h3
-rw-r--r--src/nvim/bufhl_defs.h2
-rw-r--r--src/nvim/channel.c756
-rw-r--r--src/nvim/channel.h134
-rw-r--r--src/nvim/charset.c215
-rw-r--r--src/nvim/charset.h16
-rw-r--r--src/nvim/cursor.c29
-rw-r--r--src/nvim/cursor_shape.c9
-rw-r--r--src/nvim/diff.c168
-rw-r--r--src/nvim/digraph.c20
-rw-r--r--src/nvim/edit.c126
-rw-r--r--src/nvim/edit.h11
-rw-r--r--src/nvim/eval.c3171
-rw-r--r--src/nvim/eval.h5
-rw-r--r--src/nvim/eval.lua17
-rw-r--r--src/nvim/eval/decode.c100
-rw-r--r--src/nvim/eval/encode.c94
-rw-r--r--src/nvim/eval/encode.h13
-rw-r--r--src/nvim/eval/typval.c560
-rw-r--r--src/nvim/eval/typval.h382
-rw-r--r--src/nvim/eval/typval_encode.c.h139
-rw-r--r--src/nvim/event/libuv_process.c23
-rw-r--r--src/nvim/event/loop.c49
-rw-r--r--src/nvim/event/loop.h21
-rw-r--r--src/nvim/event/process.c161
-rw-r--r--src/nvim/event/process.h19
-rw-r--r--src/nvim/event/rstream.c30
-rw-r--r--src/nvim/event/socket.c9
-rw-r--r--src/nvim/event/stream.c9
-rw-r--r--src/nvim/event/stream.h8
-rw-r--r--src/nvim/event/wstream.c3
-rw-r--r--src/nvim/ex_cmds.c413
-rw-r--r--src/nvim/ex_cmds.lua24
-rw-r--r--src/nvim/ex_cmds2.c256
-rw-r--r--src/nvim/ex_docmd.c491
-rw-r--r--src/nvim/ex_eval.c360
-rw-r--r--src/nvim/ex_eval.h27
-rw-r--r--src/nvim/ex_getln.c1122
-rw-r--r--src/nvim/farsi.c337
-rw-r--r--src/nvim/file_search.c4
-rw-r--r--src/nvim/fileio.c192
-rw-r--r--src/nvim/fileio.h15
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua16
-rwxr-xr-xsrc/nvim/generators/gen_declarations.lua64
-rw-r--r--src/nvim/generators/gen_options.lua2
-rw-r--r--src/nvim/getchar.c194
-rw-r--r--src/nvim/getchar.h15
-rw-r--r--src/nvim/gettext.h1
-rw-r--r--src/nvim/globals.h123
-rw-r--r--src/nvim/hashtab.c2
-rw-r--r--src/nvim/highlight_defs.h29
-rw-r--r--src/nvim/if_cscope.c371
-rw-r--r--src/nvim/indent_c.c180
-rw-r--r--src/nvim/keymap.c504
-rw-r--r--src/nvim/keymap.h19
-rw-r--r--src/nvim/lib/kbtree.h22
-rw-r--r--src/nvim/lib/kvec.h12
-rw-r--r--src/nvim/lib/ringbuf.h27
-rw-r--r--src/nvim/log.c97
-rw-r--r--src/nvim/log.h27
-rw-r--r--src/nvim/lua/converter.c51
-rw-r--r--src/nvim/lua/executor.c491
-rw-r--r--src/nvim/lua/vim.lua68
-rw-r--r--src/nvim/macros.h32
-rw-r--r--src/nvim/main.c199
-rw-r--r--src/nvim/mark.c18
-rw-r--r--src/nvim/mbyte.c355
-rw-r--r--src/nvim/mbyte.h14
-rw-r--r--src/nvim/memfile.c6
-rw-r--r--src/nvim/memline.c110
-rw-r--r--src/nvim/memory.c4
-rw-r--r--src/nvim/menu.c320
-rw-r--r--src/nvim/menu.h47
-rw-r--r--src/nvim/message.c299
-rw-r--r--src/nvim/message.h9
-rw-r--r--src/nvim/misc1.c38
-rw-r--r--src/nvim/mouse.c117
-rw-r--r--src/nvim/move.c50
-rw-r--r--src/nvim/msgpack_rpc/channel.c447
-rw-r--r--src/nvim/msgpack_rpc/channel.h2
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h36
-rw-r--r--src/nvim/msgpack_rpc/helpers.c9
-rw-r--r--src/nvim/msgpack_rpc/server.c4
-rw-r--r--src/nvim/normal.c239
-rw-r--r--src/nvim/ops.c237
-rw-r--r--src/nvim/option.c533
-rw-r--r--src/nvim/option_defs.h101
-rw-r--r--src/nvim/options.lua82
-rw-r--r--src/nvim/os/env.c4
-rw-r--r--src/nvim/os/fileio.c88
-rw-r--r--src/nvim/os/fs.c119
-rw-r--r--src/nvim/os/input.c4
-rw-r--r--src/nvim/os/lang.c43
-rw-r--r--src/nvim/os/lang.h7
-rw-r--r--src/nvim/os/process.c253
-rw-r--r--src/nvim/os/process.h11
-rw-r--r--src/nvim/os/pty_process_unix.c45
-rw-r--r--src/nvim/os/pty_process_win.c413
-rw-r--r--src/nvim/os/pty_process_win.h28
-rw-r--r--src/nvim/os/shell.c143
-rw-r--r--src/nvim/os/signal.c3
-rw-r--r--src/nvim/os/time.c22
-rw-r--r--src/nvim/os/unix_defs.h3
-rw-r--r--src/nvim/os/win_defs.h20
-rw-r--r--src/nvim/os_unix.c7
-rw-r--r--src/nvim/path.c70
-rw-r--r--src/nvim/po/af.po7118
-rw-r--r--src/nvim/po/ca.po88
-rw-r--r--src/nvim/po/de.po18
-rw-r--r--src/nvim/po/eo.po323
-rw-r--r--src/nvim/po/es.po31
-rw-r--r--src/nvim/po/fi.po59
-rw-r--r--src/nvim/po/fr.po286
-rw-r--r--src/nvim/po/ga.po179
-rw-r--r--src/nvim/po/it.po52
-rw-r--r--src/nvim/po/ja.euc-jp.po147
-rw-r--r--src/nvim/po/ja.po147
-rw-r--r--src/nvim/po/ko.UTF-8.po63
-rw-r--r--src/nvim/po/pl.UTF-8.po2
-rw-r--r--src/nvim/po/pt_BR.po12
-rw-r--r--src/nvim/po/ru.po8
-rw-r--r--src/nvim/po/sk.cp1250.po1
-rw-r--r--src/nvim/po/sk.po1
-rw-r--r--src/nvim/po/uk.po46
-rw-r--r--src/nvim/popupmnu.c10
-rw-r--r--src/nvim/quickfix.c653
-rw-r--r--src/nvim/rbuffer.c2
-rw-r--r--src/nvim/regexp.c1152
-rw-r--r--src/nvim/regexp_defs.h46
-rw-r--r--src/nvim/regexp_nfa.c287
-rw-r--r--src/nvim/screen.c424
-rw-r--r--src/nvim/search.c123
-rw-r--r--src/nvim/shada.c57
-rw-r--r--src/nvim/spell.c102
-rw-r--r--src/nvim/spellfile.c3
-rw-r--r--src/nvim/state.c38
-rw-r--r--src/nvim/strings.c7
-rw-r--r--src/nvim/syntax.c2559
-rw-r--r--src/nvim/syntax.h16
-rw-r--r--src/nvim/syntax_defs.h15
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c154
-rw-r--r--src/nvim/terminal.h2
-rw-r--r--src/nvim/testdir/Makefile129
-rwxr-xr-xsrc/nvim/testdir/runnvim.sh82
-rw-r--r--src/nvim/testdir/runnvim.vim55
-rw-r--r--src/nvim/testdir/runtest.vim123
-rw-r--r--src/nvim/testdir/sautest/autoload/footest.vim2
-rw-r--r--src/nvim/testdir/setup.vim13
-rw-r--r--src/nvim/testdir/shared.vim20
-rw-r--r--src/nvim/testdir/test32.in60
-rw-r--r--src/nvim/testdir/test32.ok15
-rw-r--r--src/nvim/testdir/test53.in74
-rw-r--r--src/nvim/testdir/test53.ok45
-rw-r--r--src/nvim/testdir/test73.in168
-rw-r--r--src/nvim/testdir/test73.ok21
-rw-r--r--src/nvim/testdir/test79.inbin3335 -> 0 bytes
-rw-r--r--src/nvim/testdir/test79.okbin570 -> 0 bytes
-rw-r--r--src/nvim/testdir/test_alot.vim13
-rw-r--r--src/nvim/testdir/test_alot_latin.vim10
-rw-r--r--src/nvim/testdir/test_alot_utf8.vim17
-rw-r--r--src/nvim/testdir/test_arabic.vim472
-rw-r--r--src/nvim/testdir/test_arglist.vim355
-rw-r--r--src/nvim/testdir/test_autochdir.vim19
-rw-r--r--src/nvim/testdir/test_autocmd.vim821
-rw-r--r--src/nvim/testdir/test_breakindent.vim298
-rw-r--r--src/nvim/testdir/test_bufwintabinfo.vim14
-rw-r--r--src/nvim/testdir/test_changedtick.vim57
-rw-r--r--src/nvim/testdir/test_charsearch_utf8.vim22
-rw-r--r--src/nvim/testdir/test_cindent.vim76
-rw-r--r--src/nvim/testdir/test_close_count.vim174
-rw-r--r--src/nvim/testdir/test_cmdline.vim218
-rw-r--r--src/nvim/testdir/test_command_count.vim20
-rw-r--r--src/nvim/testdir/test_curswant.vim23
-rw-r--r--src/nvim/testdir/test_diffmode.vim349
-rw-r--r--src/nvim/testdir/test_display.vim71
-rw-r--r--src/nvim/testdir/test_edit.vim1325
-rw-r--r--src/nvim/testdir/test_erasebackword.vim25
-rw-r--r--src/nvim/testdir/test_exists.vim321
-rw-r--r--src/nvim/testdir/test_expr.vim13
-rw-r--r--src/nvim/testdir/test_expr_utf8.vim4
-rw-r--r--src/nvim/testdir/test_farsi.vim49
-rw-r--r--src/nvim/testdir/test_file_size.vim58
-rw-r--r--src/nvim/testdir/test_fileformat.vim33
-rw-r--r--src/nvim/testdir/test_filetype.vim557
-rw-r--r--src/nvim/testdir/test_find_complete.vim157
-rw-r--r--src/nvim/testdir/test_findfile.vim25
-rw-r--r--src/nvim/testdir/test_fixeol.vim48
-rw-r--r--src/nvim/testdir/test_fold.vim21
-rw-r--r--src/nvim/testdir/test_functions.vim161
-rw-r--r--src/nvim/testdir/test_ga.vim37
-rw-r--r--src/nvim/testdir/test_goto.vim281
-rw-r--r--src/nvim/testdir/test_hardcopy.vim1
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim113
-rw-r--r--src/nvim/testdir/test_hide.vim97
-rw-r--r--src/nvim/testdir/test_history.vim24
-rw-r--r--src/nvim/testdir/test_ins_complete.vim219
-rw-r--r--src/nvim/testdir/test_let.vim27
-rw-r--r--src/nvim/testdir/test_lineending.vim19
-rw-r--r--src/nvim/testdir/test_lispwords.vim82
-rw-r--r--src/nvim/testdir/test_listchars.vim63
-rw-r--r--src/nvim/testdir/test_listdict.vim603
-rw-r--r--src/nvim/testdir/test_listlbr.vim238
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim256
-rw-r--r--src/nvim/testdir/test_makeencoding.py67
-rw-r--r--src/nvim/testdir/test_makeencoding.vim106
-rw-r--r--src/nvim/testdir/test_mapping.vim4
-rw-r--r--src/nvim/testdir/test_matchadd_conceal.vim1
-rw-r--r--src/nvim/testdir/test_mksession.vim155
-rw-r--r--src/nvim/testdir/test_mksession_utf8.vim105
-rw-r--r--src/nvim/testdir/test_nested_function.vim57
-rw-r--r--src/nvim/testdir/test_normal.vim72
-rw-r--r--src/nvim/testdir/test_number.vim254
-rw-r--r--src/nvim/testdir/test_options.vim136
-rw-r--r--src/nvim/testdir/test_popup.vim75
-rw-r--r--src/nvim/testdir/test_profile.vim183
-rw-r--r--src/nvim/testdir/test_put.vim36
-rw-r--r--src/nvim/testdir/test_quickfix.vim941
-rw-r--r--src/nvim/testdir/test_recover.vim63
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim32
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim15
-rw-r--r--src/nvim/testdir/test_retab.vim77
-rw-r--r--src/nvim/testdir/test_scrollbind.vim32
-rw-r--r--src/nvim/testdir/test_search.vim182
-rw-r--r--src/nvim/testdir/test_sha256.vim22
-rw-r--r--src/nvim/testdir/test_signs.vim2
-rw-r--r--src/nvim/testdir/test_source_utf8.vim30
-rw-r--r--src/nvim/testdir/test_spell.vim821
-rw-r--r--src/nvim/testdir/test_startup.vim60
-rw-r--r--src/nvim/testdir/test_startup_utf8.vim64
-rw-r--r--src/nvim/testdir/test_stat.vim66
-rw-r--r--src/nvim/testdir/test_statusline.vim251
-rw-r--r--src/nvim/testdir/test_substitute.vim282
-rw-r--r--src/nvim/testdir/test_syntax.vim308
-rw-r--r--src/nvim/testdir/test_system.vim92
-rw-r--r--src/nvim/testdir/test_tab.vim45
-rw-r--r--src/nvim/testdir/test_tabpage.vim19
-rw-r--r--src/nvim/testdir/test_tagjump.vim37
-rw-r--r--src/nvim/testdir/test_textformat.vim168
-rw-r--r--src/nvim/testdir/test_textobjects.vim127
-rw-r--r--src/nvim/testdir/test_undo.vim51
-rw-r--r--src/nvim/testdir/test_unlet.vim6
-rw-r--r--src/nvim/testdir/test_usercommands.vim104
-rw-r--r--src/nvim/testdir/test_utf8_comparisons.vim95
-rw-r--r--src/nvim/testdir/test_vimscript.vim56
-rw-r--r--src/nvim/testdir/test_virtualedit.vim43
-rw-r--r--src/nvim/testdir/test_visual.vim147
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim124
-rw-r--r--src/nvim/testdir/test_window_cmd.vim56
-rw-r--r--src/nvim/testdir/unix.vim6
-rw-r--r--src/nvim/testdir/view_util.vim30
-rw-r--r--src/nvim/tui/input.c52
-rw-r--r--src/nvim/tui/terminfo.c255
-rw-r--r--src/nvim/tui/terminfo.h10
-rw-r--r--src/nvim/tui/tui.c1281
-rw-r--r--src/nvim/ugrid.c10
-rw-r--r--src/nvim/ugrid.h8
-rw-r--r--src/nvim/ui.c230
-rw-r--r--src/nvim/ui.h19
-rw-r--r--src/nvim/ui_bridge.c45
-rw-r--r--src/nvim/undo.c86
-rw-r--r--src/nvim/undo.h1
-rw-r--r--src/nvim/undo_defs.h6
-rw-r--r--src/nvim/version.c3274
-rw-r--r--src/nvim/version.h8
-rw-r--r--src/nvim/vim.h188
-rw-r--r--src/nvim/viml/parser/expressions.c3102
-rw-r--r--src/nvim/viml/parser/expressions.h389
-rw-r--r--src/nvim/viml/parser/parser.c16
-rw-r--r--src/nvim/viml/parser/parser.h244
-rw-r--r--src/nvim/window.c226
288 files changed, 39396 insertions, 17775 deletions
diff --git a/src/.asan-blacklist b/src/.asan-blacklist
index 928d81bd5a..9d7f721a88 100644
--- a/src/.asan-blacklist
+++ b/src/.asan-blacklist
@@ -1,3 +1,7 @@
# multiqueue.h pointer arithmetic is not accepted by asan
fun:multiqueue_node_data
fun:tv_dict_watcher_node_data
+
+# Allocation in loop_schedule_deferred() is freed by loop_deferred_event(), but
+# this sometimes does not happen during teardown.
+func:loop_schedule_deferred
diff --git a/src/clint.py b/src/clint.py
index 69a061d2ab..79ab91ebe1 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -201,6 +201,7 @@ _ERROR_CATEGORIES = [
'runtime/printf',
'runtime/printf_format',
'runtime/threadsafe_fn',
+ 'runtime/deprecated',
'syntax/parenthesis',
'whitespace/alignment',
'whitespace/blank_line',
@@ -2123,8 +2124,10 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
+ (level_starts[depth][2] == '{')):
if depth not in ignore_error_levels:
error(filename, linenum, 'whitespace/alignment', 2,
- 'Inner expression should be aligned '
- 'as opening brace + 1 (+ 2 in case of {)')
+ ('Inner expression should be aligned '
+ 'as opening brace + 1 (+ 2 in case of {{). '
+ 'Relevant opening is on line {0!r}').format(
+ level_starts[depth][3]))
prev_line_start = pos
elif brace == 'e':
pass
@@ -2141,7 +2144,8 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
ignore_error_levels.add(depth)
line_ended_with_opening = (
pos == len(line) - 2 * (line.endswith(' \\')) - 1)
- level_starts[depth] = (pos, line_ended_with_opening, brace)
+ level_starts[depth] = (pos, line_ended_with_opening, brace,
+ linenum)
if line_ended_with_opening:
depth_line_starts[depth] = (prev_line_start, brace)
else:
@@ -2531,6 +2535,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
r'(?<!\bkhash_t)'
r'(?<!\bkbtree_t)'
r'(?<!\bkbitr_t)'
+ r'(?<!\bPMap)'
r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)'
r' +'
r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line)
@@ -2613,7 +2618,8 @@ def CheckBraces(filename, clean_lines, linenum, error):
func_start_linenum += 1
else:
- if clean_lines.lines[func_start_linenum].endswith('{'):
+ func_start = clean_lines.lines[func_start_linenum]
+ if not func_start.startswith('enum ') and func_start.endswith('{'):
error(filename, func_start_linenum,
'readability/braces', 5,
'Brace starting function body must be placed '
@@ -3198,6 +3204,14 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if match:
error(filename, linenum, 'runtime/printf', 4,
'Use xstrlcat or snprintf instead of %s' % match.group(1))
+ if not Search(r'eval/typval\.[ch]$', filename):
+ match = Search(r'(?:\.|->)'
+ r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?'
+ r'|copylist|lock)'
+ r'|li_(?:next|prev|tv))\b', line)
+ if match:
+ error(filename, linenum, 'runtime/deprecated', 4,
+ 'Accessing list_T internals directly is prohibited')
# Check for suspicious usage of "if" like
# } if (a == b) {
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index c46c0bed6d..606baff619 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -10,6 +10,14 @@ if(USE_GCOV)
endif()
endif()
+if(WIN32)
+ # tell MinGW compiler to enable wmain
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode")
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreFoundation")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -framework CoreFoundation")
+endif()
+
set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
@@ -81,6 +89,8 @@ foreach(subdir
event
eval
lua
+ viml
+ viml/parser
)
if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
continue()
@@ -111,6 +121,9 @@ foreach(sfile ${NVIM_SOURCES})
if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$")
list(APPEND to_remove ${sfile})
endif()
+ if(NOT WIN32 AND ${f} MATCHES "^(pty_process_win.c)$")
+ list(APPEND to_remove ${sfile})
+ endif()
endforeach()
list(REMOVE_ITEM NVIM_SOURCES ${to_remove})
@@ -156,13 +169,13 @@ if(NOT MSVC)
endif()
endif()
-if(DEFINED MIN_LOG_LEVEL)
+if(NOT "${MIN_LOG_LEVEL}" MATCHES "^$")
add_definitions(-DMIN_LOG_LEVEL=${MIN_LOG_LEVEL})
endif()
get_directory_property(gen_cdefs COMPILE_DEFINITIONS)
foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES)
- if(NOT "${gen_cdef}" MATCHES "INCLUDE_GENERATED_DECLARATIONS")
+ if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS")
list(APPEND gen_cflags "-D${gen_cdef}")
endif()
endforeach()
@@ -174,6 +187,10 @@ get_directory_property(gen_includes INCLUDE_DIRECTORIES)
foreach(gen_include ${gen_includes} ${LUA_PREFERRED_INCLUDE_DIRS})
list(APPEND gen_cflags "-I${gen_include}")
endforeach()
+if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT)
+ list(APPEND gen_cflags "-isysroot")
+ list(APPEND gen_cflags "${CMAKE_OSX_SYSROOT}")
+endif()
string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
separate_arguments(C_FLAGS_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS})
separate_arguments(C_FLAGS_${build_type}_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS_${build_type}})
@@ -350,6 +367,10 @@ if(Iconv_LIBRARIES)
list(APPEND NVIM_LINK_LIBRARIES ${Iconv_LIBRARIES})
endif()
+if(WIN32)
+ list(APPEND NVIM_LINK_LIBRARIES ${WINPTY_LIBRARIES})
+endif()
+
# Put these last on the link line, since multiple things may depend on them.
list(APPEND NVIM_LINK_LIBRARIES
${LIBUV_LIBRARIES}
@@ -415,6 +436,7 @@ if(WIN32)
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tee.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tidy.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/win32yank.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
+ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty-agent.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/D3Dcompiler_47.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/libEGL.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
@@ -428,10 +450,13 @@ if(WIN32)
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Network.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Svg.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Widgets.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
+ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/platforms/qwindows.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/platforms/
)
add_dependencies(nvim_runtime_deps external_blobs)
+else()
+ add_custom_target(nvim_runtime_deps) # Stub target to avoid CMP0046.
endif()
add_library(
@@ -526,11 +551,7 @@ endfunction()
set(NO_SINGLE_CHECK_HEADERS
os/win_defs.h
- regexp_defs.h
- syntax_defs.h
- terminal.h
- undo.h
- undo_defs.h
+ os/pty_process_win.h
)
foreach(hfile ${NVIM_HEADERS})
get_test_target(test-includes "${hfile}" relative_path texe)
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 3032913500..d668db0cdc 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -1,14 +1,13 @@
-## Source code overview
+Nvim core
+=========
-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 dev` for guidelines.
-### Source file name conventions
+Filename conventions
+--------------------
The source files use extensions to hint about their purpose.
@@ -19,12 +18,98 @@ 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
+Logs
+----
-Let's understand what a Vim-like program does by analyzing the workflow of
-a typical editing session:
+Low-level log messages sink to `$NVIM_LOG_FILE`.
-01. Vim dispays the welcome screen
+You can use `LOG_CALLSTACK();` anywhere in the source to log the current
+stacktrace. To log in an alternate file, e.g. stderr, use
+`LOG_CALLSTACK_TO_FILE(FILE*)`. (Currently Linux-only.)
+
+UI events are logged at level 0 (`DEBUG_LOG_LEVEL`).
+
+ rm -rf build/
+ make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
+
+Build with ASAN
+---------------
+
+Building Nvim with Clang sanitizers (Address Sanitizer: ASan, Undefined
+Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is
+a good way to catch undefined behavior, leaks and other errors as soon as they
+happen. It's significantly faster than Valgrind.
+
+Requires clang 3.4 or later:
+
+ clang --version
+
+Build Nvim with sanitizer instrumentation:
+
+ CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON"
+
+Create a directory to store logs:
+
+ mkdir -p "$HOME/logs"
+
+Enable the sanitizer(s) via these environment variables:
+
+ # Change to detect_leaks=1 to detect memory leaks (slower).
+ export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan"
+ export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer
+
+ export MSAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer
+ export TSAN_OPTIONS="external_symbolizer_path=/usr/lib/llvm-5.0/bin/llvm-symbolizer log_path=${HOME}/logs/tsan"
+
+Logs will be written to `${HOME}/logs/*san.PID`.
+
+TUI debugging
+-------------
+
+### TUI troubleshoot
+
+Nvim logs its internal terminfo state at 'verbose' level 3. This makes it
+possible to see exactly what terminfo values Nvim is using on any system.
+
+ nvim -V3log
+
+### TUI trace
+
+The ancient `script` command is still the "state of the art" for tracing
+terminal behavior. The libvterm `vterm-dump` utility formats the result for
+human-readability.
+
+Record a Nvim terminal session and format it with `vterm-dump`:
+
+ script foo
+ ./build/bin/nvim -u NONE
+ # Exit the script session with CTRL-d
+
+ # Use `vterm-dump` utility to format the result.
+ ./.deps/usr/bin/vterm-dump foo > bar
+
+Then you can compare `bar` with another session, to debug TUI behavior.
+
+### TUI redraw
+
+Set the 'writedelay' option to see where and when the UI is painted.
+
+ :set writedelay=1
+
+### Terminal reference
+
+- `man terminfo`
+- http://bazaar.launchpad.net/~libvterm/libvterm/trunk/view/head:/doc/seqs.txt
+- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+
+Nvim lifecycle
+--------------
+
+Following describes how Nvim processes input.
+
+Consider a typical Vim-like editing session:
+
+01. Vim displays the welcome screen
02. User types: `:`
03. Vim enters command-line mode
04. User types: `edit README.txt<CR>`
@@ -154,7 +239,8 @@ modes managed by the `state_enter` loop:
- insert mode: `insert_{enter,check,execute}()`(`edit.c`)
- terminal mode: `terminal_{enter,execute}()`(`terminal.c`)
-### Async event support
+Async event support
+-------------------
One of the features Nvim added is the support for handling arbitrary
asynchronous events, which can include:
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 82de8fd4a2..af723639c5 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -399,7 +399,11 @@ void nvim_buf_set_lines(uint64_t channel_id,
// Only adjust marks if we managed to switch to a window that holds
// the buffer, otherwise line numbers will be invalid.
if (save_curbuf.br_buf == NULL) {
- mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra, false);
+ mark_adjust((linenr_T)start,
+ (linenr_T)(end - 1),
+ MAXLNUM,
+ (long)extra,
+ false);
}
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra);
@@ -439,6 +443,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
/// Gets a changed tick of a buffer
///
/// @param[in] buffer Buffer handle.
+/// @param[out] err Error details, if any
///
/// @return `b:changedtick` value.
Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
@@ -453,14 +458,14 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
return buf->b_changedtick;
}
-/// Get a list of dictionaries describing buffer-local mappings
-/// Note that the buffer key in the dictionary will represent the buffer
-/// handle where the mapping is present
+/// Gets a list of dictionaries describing buffer-local mappings.
+/// The "buffer" key in the returned dictionary reflects the buffer
+/// handle where the mapping is present.
///
-/// @param mode The abbreviation for the mode
-/// @param buffer_id Buffer handle
+/// @param mode Mode short-name ("n", "i", "v", ...)
+/// @param buffer Buffer handle
/// @param[out] err Error details, if any
-/// @returns An array of maparg() like dictionaries describing mappings
+/// @returns Array of maparg()-like dictionaries describing mappings
ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
FUNC_API_SINCE(3)
{
@@ -739,31 +744,31 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// Adds a highlight to buffer.
///
-/// This can be used for plugins which dynamically generate highlights to a
-/// buffer (like a semantic highlighter or linter). The function adds a single
+/// Useful for plugins that dynamically generate highlights to a buffer
+/// (like a semantic highlighter or linter). The function adds a single
/// highlight to a buffer. Unlike matchaddpos() highlights follow changes to
/// line numbering (as lines are inserted/removed above the highlighted line),
/// like signs and marks do.
///
-/// "src_id" is useful for batch deletion/updating of a set of highlights. When
-/// called with src_id = 0, an unique source id is generated and returned.
-/// Succesive calls can pass in it as "src_id" to add new highlights to the same
-/// source group. All highlights in the same group can then be cleared with
-/// nvim_buf_clear_highlight. If the highlight never will be manually deleted
-/// pass in -1 for "src_id".
+/// `src_id` is useful for batch deletion/updating of a set of highlights. When
+/// called with `src_id = 0`, an unique source id is generated and returned.
+/// Successive calls can pass that `src_id` to associate new highlights with
+/// the same source group. All highlights in the same group can be cleared
+/// with `nvim_buf_clear_highlight`. If the highlight never will be manually
+/// deleted, pass `src_id = -1`.
///
-/// If "hl_group" is the empty string no highlight is added, but a new src_id
+/// If `hl_group` is the empty string no highlight is added, but a new `src_id`
/// is still returned. This is useful for an external plugin to synchrounously
-/// request an unique src_id at initialization, and later asynchronously add and
-/// clear highlights in response to buffer changes.
+/// request an unique `src_id` at initialization, and later asynchronously add
+/// and clear highlights in response to buffer changes.
///
/// @param buffer Buffer handle
/// @param src_id Source group to use or 0 to use a new group,
/// or -1 for ungrouped highlight
/// @param hl_group Name of the highlight group to use
-/// @param line Line to highlight
-/// @param col_start Start of range of columns to highlight
-/// @param col_end End of range of columns to highlight,
+/// @param line Line to highlight (zero-indexed)
+/// @param col_start Start of (byte-indexed) column range to highlight
+/// @param col_end End of (byte-indexed) column range to highlight,
/// or -1 to highlight to end of line
/// @param[out] err Error details, if any
/// @return The src_id that was used
@@ -793,7 +798,11 @@ Integer nvim_buf_add_highlight(Buffer buffer,
col_end = MAXCOL;
}
- int hlg_id = syn_name2id((char_u *)(hl_group.data ? hl_group.data : ""));
+ int hlg_id = 0;
+ if (hl_group.size > 0) {
+ hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size);
+ }
+
src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1,
(colnr_T)col_start+1, (colnr_T)col_end);
return src_id;
@@ -801,7 +810,7 @@ Integer nvim_buf_add_highlight(Buffer buffer,
/// Clears highlights from a given source group and a range of lines
///
-/// To clear a source group in the entire buffer, pass in 1 and -1 to
+/// To clear a source group in the entire buffer, pass in 0 and -1 to
/// line_start and line_end respectively.
///
/// @param buffer Buffer handle
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 2144c80d6a..390b7e8363 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -47,7 +47,7 @@ typedef enum {
/// Internal call from lua code
#define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1)
-static inline bool is_internal_call(uint64_t channel_id)
+static inline bool is_internal_call(const uint64_t channel_id)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
/// Check whether call is internal
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index d401ae52a0..12a4279dd7 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -12,6 +12,7 @@
#include "nvim/api/private/handle.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/assert.h"
#include "nvim/vim.h"
#include "nvim/buffer.h"
#include "nvim/window.h"
@@ -25,6 +26,7 @@
#include "nvim/version.h"
#include "nvim/lib/kvec.h"
#include "nvim/getchar.h"
+#include "nvim/ui.h"
/// Helper structure for vim_to_object
typedef struct {
@@ -37,7 +39,71 @@ typedef struct {
# include "api/private/ui_events_metadata.generated.h"
#endif
+/// Start block that may cause VimL exceptions while evaluating another code
+///
+/// Used when caller is supposed to be operating when other VimL code is being
+/// processed and that “other VimL code” must not be affected.
+///
+/// @param[out] tstate Location where try state should be saved.
+void try_enter(TryState *const tstate)
+{
+ // TODO(ZyX-I): Check whether try_enter()/try_leave() may use
+ // enter_cleanup()/leave_cleanup(). Or
+ // save_dbg_stuff()/restore_dbg_stuff().
+ *tstate = (TryState) {
+ .current_exception = current_exception,
+ .msg_list = (const struct msglist *const *)msg_list,
+ .private_msg_list = NULL,
+ .trylevel = trylevel,
+ .got_int = got_int,
+ .need_rethrow = need_rethrow,
+ .did_emsg = did_emsg,
+ };
+ msg_list = &tstate->private_msg_list;
+ current_exception = NULL;
+ trylevel = 1;
+ got_int = false;
+ need_rethrow = false;
+ did_emsg = false;
+}
+
+/// End try block, set the error message if any and restore previous state
+///
+/// @warning Return is consistent with most functions (false on error), not with
+/// try_end (true on error).
+///
+/// @param[in] tstate Previous state to restore.
+/// @param[out] err Location where error should be saved.
+///
+/// @return false if error occurred, true otherwise.
+bool try_leave(const TryState *const tstate, Error *const err)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const bool ret = !try_end(err);
+ assert(trylevel == 0);
+ assert(!need_rethrow);
+ assert(!got_int);
+ assert(!did_emsg);
+ assert(msg_list == &tstate->private_msg_list);
+ assert(*msg_list == NULL);
+ assert(current_exception == NULL);
+ msg_list = (struct msglist **)tstate->msg_list;
+ current_exception = tstate->current_exception;
+ trylevel = tstate->trylevel;
+ got_int = tstate->got_int;
+ need_rethrow = tstate->need_rethrow;
+ did_emsg = tstate->did_emsg;
+ return ret;
+}
+
/// Start block that may cause vimscript exceptions
+///
+/// Each try_start() call should be mirrored by try_end() call.
+///
+/// To be used as a replacement of `:try … catch … endtry` in C code, in cases
+/// when error flag could not already be set. If there may be pending error
+/// state at the time try_start() is executed which needs to be preserved,
+/// try_enter()/try_leave() pair should be used instead.
void try_start(void)
{
++trylevel;
@@ -50,7 +116,9 @@ void try_start(void)
/// @return true if an error occurred
bool try_end(Error *err)
{
- --trylevel;
+ // Note: all globals manipulated here should be saved/restored in
+ // try_enter/try_leave.
+ trylevel--;
// Without this it stops processing all subsequent VimL commands and
// generates strange error messages if I e.g. try calling Test() in a
@@ -58,7 +126,7 @@ bool try_end(Error *err)
did_emsg = false;
if (got_int) {
- if (did_throw) {
+ if (current_exception) {
// If we got an interrupt, discard the current exception
discard_current_exception();
}
@@ -77,7 +145,7 @@ bool try_end(Error *err)
if (should_free) {
xfree(msg);
}
- } else if (did_throw) {
+ } else if (current_exception) {
api_set_error(err, kErrorTypeException, "%s", current_exception->value);
discard_current_exception();
}
@@ -95,7 +163,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
if (di == NULL) {
- api_set_error(err, kErrorTypeValidation, "Key not found");
+ api_set_error(err, kErrorTypeValidation, "Key '%s' not found", key.data);
return (Object)OBJECT_INIT;
}
@@ -498,7 +566,7 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
typval_encode_dict_end(edata)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
- TYPVAL_ENCODE_CONV_NIL()
+ TYPVAL_ENCODE_CONV_NIL(val)
#define TYPVAL_ENCODE_SCOPE static
#define TYPVAL_ENCODE_NAME object
@@ -600,6 +668,22 @@ tabpage_T *find_tab_by_handle(Tabpage tabpage, Error *err)
return rv;
}
+/// Allocates a String consisting of a single char. Does not support multibyte
+/// characters. The resulting string is also NUL-terminated, to facilitate
+/// interoperating with code using C strings.
+///
+/// @param char the char to convert
+/// @return the resulting String, if the input char was NUL, an
+/// empty String is returned
+String cchar_to_string(char c)
+{
+ char buf[] = { c, NUL };
+ return (String) {
+ .data = xmemdupz(buf, 1),
+ .size = (c != NUL) ? 1 : 0
+ };
+}
+
/// Copies a C string into a String (binary safe string, characters + length).
/// The resulting string is also NUL-terminated, to facilitate interoperating
/// with code using C strings.
@@ -620,6 +704,23 @@ String cstr_to_string(const char *str)
};
}
+/// Copies buffer to an allocated String.
+/// The resulting string is also NUL-terminated, to facilitate interoperating
+/// with code using C strings.
+///
+/// @param buf the buffer to copy
+/// @param size length of the buffer
+/// @return the resulting String, if the input string was NULL, an
+/// empty String is returned
+String cbuf_to_string(const char *buf, size_t size)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return (String) {
+ .data = xmemdupz(buf, size),
+ .size = size
+ };
+}
+
/// Creates a String using the given C string. Unlike
/// cstr_to_string this function DOES NOT copy the C string.
///
@@ -660,12 +761,8 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
case kObjectTypeWindow:
case kObjectTypeTabpage:
case kObjectTypeInteger:
- if (obj.data.integer > VARNUMBER_MAX
- || obj.data.integer < VARNUMBER_MIN) {
- api_set_error(err, kErrorTypeValidation, "Integer value outside range");
- return false;
- }
-
+ STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T),
+ "Integer size must be <= VimL number size");
tv->v_type = VAR_NUMBER;
tv->vval.v_number = (varnumber_T)obj.data.integer;
break;
@@ -686,22 +783,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
break;
case kObjectTypeArray: {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size);
for (uint32_t i = 0; i < obj.data.array.size; i++) {
Object item = obj.data.array.items[i];
- listitem_T *li = tv_list_item_alloc();
+ typval_T li_tv;
- if (!object_to_vim(item, &li->li_tv, err)) {
- // cleanup
- tv_list_item_free(li);
+ if (!object_to_vim(item, &li_tv, err)) {
tv_list_free(list);
return false;
}
- tv_list_append(list, li);
+ tv_list_append_owned_tv(list, li_tv);
}
- list->lv_refcount++;
+ tv_list_ref(list);
tv->v_type = VAR_LIST;
tv->vval.v_list = list;
@@ -860,6 +955,12 @@ static void init_ui_event_metadata(Dictionary *metadata)
msgpack_rpc_to_object(&unpacked.data, &ui_events);
msgpack_unpacked_destroy(&unpacked);
PUT(*metadata, "ui_events", ui_events);
+ Array ui_options = ARRAY_DICT_INIT;
+ ADD(ui_options, STRING_OBJ(cstr_to_string("rgb")));
+ for (UIExtension i = 0; i < kUIExtCount; i++) {
+ ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i])));
+ }
+ PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options));
}
static void init_error_type_metadata(Dictionary *metadata)
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 159b9d5c2a..0634764f13 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -6,6 +6,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/vim.h"
#include "nvim/memory.h"
+#include "nvim/ex_eval.h"
#include "nvim/lib/kvec.h"
#define OBJECT_OBJ(o) o
@@ -82,6 +83,20 @@
#define api_free_window(value)
#define api_free_tabpage(value)
+/// Structure used for saving state for :try
+///
+/// Used when caller is supposed to be operating when other VimL code is being
+/// processed and that “other VimL code” must not be affected.
+typedef struct {
+ except_T *current_exception;
+ struct msglist *private_msg_list;
+ const struct msglist *const *msg_list;
+ int trylevel;
+ int got_int;
+ int need_rethrow;
+ int did_emsg;
+} TryState;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/helpers.h.generated.h"
#endif
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 573be23d8e..4870c3fb8a 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -43,10 +43,10 @@ void remote_ui_disconnect(uint64_t channel_id)
return;
}
UIData *data = ui->data;
- // destroy pending screen updates
- api_free_array(data->buffer);
+ api_free_array(data->buffer); // Destroy pending screen updates.
pmap_del(uint64_t)(connected_uis, channel_id);
xfree(ui->data);
+ ui->data = NULL; // Flag UI as "stopped".
ui_detach_impl(ui);
xfree(ui);
}
@@ -86,6 +86,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->put = remote_ui_put;
ui->bell = remote_ui_bell;
ui->visual_bell = remote_ui_visual_bell;
+ ui->default_colors_set = remote_ui_default_colors_set;
ui->update_fg = remote_ui_update_fg;
ui->update_bg = remote_ui_update_bg;
ui->update_sp = remote_ui_update_sp;
@@ -93,6 +94,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->suspend = remote_ui_suspend;
ui->set_title = remote_ui_set_title;
ui->set_icon = remote_ui_set_icon;
+ ui->option_set = remote_ui_option_set;
ui->event = remote_ui_event;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
@@ -174,18 +176,6 @@ void nvim_ui_set_option(uint64_t channel_id, String name,
static void ui_set_option(UI *ui, String name, Object value, Error *error)
{
-#define UI_EXT_OPTION(o, e) \
- do { \
- if (strequal(name.data, #o)) { \
- if (value.type != kObjectTypeBoolean) { \
- api_set_error(error, kErrorTypeValidation, #o " must be a Boolean"); \
- return; \
- } \
- ui->ui_ext[(e)] = value.data.boolean; \
- return; \
- } \
- } while (0)
-
if (strequal(name.data, "rgb")) {
if (value.type != kObjectTypeBoolean) {
api_set_error(error, kErrorTypeValidation, "rgb must be a Boolean");
@@ -195,13 +185,21 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error)
return;
}
- UI_EXT_OPTION(ext_cmdline, kUICmdline);
- UI_EXT_OPTION(ext_popupmenu, kUIPopupmenu);
- UI_EXT_OPTION(ext_tabline, kUITabline);
- UI_EXT_OPTION(ext_wildmenu, kUIWildmenu);
+ for (UIExtension i = 0; i < kUIExtCount; i++) {
+ if (strequal(name.data, ui_ext_names[i])) {
+ if (value.type != kObjectTypeBoolean) {
+ snprintf((char *)IObuff, IOSIZE, "%s must be a Boolean",
+ ui_ext_names[i]);
+ api_set_error(error, kErrorTypeValidation, (char *)IObuff);
+ return;
+ }
+ ui->ui_ext[i] = value.data.boolean;
+ return;
+ }
+ }
if (strequal(name.data, "popupmenu_external")) {
- // LEGACY: Deprecated option, use `ui_ext` instead.
+ // LEGACY: Deprecated option, use `ext_cmdline` instead.
if (value.type != kObjectTypeBoolean) {
api_set_error(error, kErrorTypeValidation,
"popupmenu_external must be a Boolean");
@@ -215,6 +213,7 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error)
#undef UI_EXT_OPTION
}
+/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
static void push_call(UI *ui, char *name, Array args)
{
Array call = ARRAY_DICT_INIT;
@@ -241,39 +240,7 @@ static void push_call(UI *ui, char *name, Array args)
static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
{
Array args = ARRAY_DICT_INIT;
- Dictionary hl = ARRAY_DICT_INIT;
-
- if (attrs.bold) {
- PUT(hl, "bold", BOOLEAN_OBJ(true));
- }
-
- if (attrs.underline) {
- PUT(hl, "underline", BOOLEAN_OBJ(true));
- }
-
- if (attrs.undercurl) {
- PUT(hl, "undercurl", BOOLEAN_OBJ(true));
- }
-
- if (attrs.italic) {
- PUT(hl, "italic", BOOLEAN_OBJ(true));
- }
-
- if (attrs.reverse) {
- PUT(hl, "reverse", BOOLEAN_OBJ(true));
- }
-
- if (attrs.foreground != -1) {
- PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground));
- }
-
- if (attrs.background != -1) {
- PUT(hl, "background", INTEGER_OBJ(attrs.background));
- }
-
- if (attrs.special != -1) {
- PUT(hl, "special", INTEGER_OBJ(attrs.special));
- }
+ Dictionary hl = hlattrs2dict(&attrs, ui->rgb);
ADD(args, DICTIONARY_OBJ(hl));
push_call(ui, "highlight_set", args);
@@ -283,7 +250,7 @@ static void remote_ui_flush(UI *ui)
{
UIData *data = ui->data;
if (data->buffer.size > 0) {
- channel_send_event(data->channel_id, "redraw", data->buffer);
+ rpc_send_event(data->channel_id, "redraw", data->buffer);
data->buffer = (Array)ARRAY_DICT_INIT;
}
}
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 1b5d17584f..c599b0ce72 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -47,17 +47,22 @@ void visual_bell(void)
void flush(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
void update_fg(Integer fg)
- FUNC_API_SINCE(3);
+ FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
void update_bg(Integer bg)
- FUNC_API_SINCE(3);
+ FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
void update_sp(Integer sp)
- FUNC_API_SINCE(3);
+ FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
+void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
+ Integer cterm_fg, Integer cterm_bg)
+ FUNC_API_SINCE(4);
void suspend(void)
FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
void set_title(String title)
FUNC_API_SINCE(3);
void set_icon(String icon)
FUNC_API_SINCE(3);
+void option_set(String name, Object value)
+ FUNC_API_SINCE(4);
void popupmenu_show(Array items, Integer selected, Integer row, Integer col)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
@@ -65,7 +70,30 @@ void popupmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void popupmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+
void tabline_update(Tabpage current, Array tabs)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_show(Array content, Integer pos, String firstc, String prompt,
+ Integer indent, Integer level)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_pos(Integer pos, Integer level)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_special_char(String c, Boolean shift, Integer level)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_hide(Integer level)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_block_show(Array lines)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_block_append(Array lines)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cmdline_block_hide(void)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+
+void wildmenu_show(Array items)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void wildmenu_select(Integer selected)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void wildmenu_hide(void)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 80efe86ea3..962081cc23 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -33,6 +33,10 @@
#include "nvim/syntax.h"
#include "nvim/getchar.h"
#include "nvim/os/input.h"
+#include "nvim/os/process.h"
+#include "nvim/viml/parser/expressions.h"
+#include "nvim/viml/parser/parser.h"
+#include "nvim/ui.h"
#define LINE_BUFFER_SIZE 4096
@@ -41,20 +45,63 @@
#endif
/// Executes an ex-command.
-/// On VimL error: Returns the VimL error; v:errmsg is not updated.
+///
+/// On parse error: forwards the Vim error; does not update v:errmsg.
+/// On runtime error: forwards the Vim error; does not update v:errmsg.
///
/// @param command Ex-command string
-/// @param[out] err Error details (including actual VimL error), if any
+/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
FUNC_API_SINCE(1)
{
- // Run the command
try_start();
do_cmdline_cmd(command.data);
update_screen(VALID);
try_end(err);
}
+/// Gets a highlight definition by name.
+///
+/// @param name Highlight group name
+/// @param rgb Export RGB colors
+/// @param[out] err Error details, if any
+/// @return Highlight definition map
+/// @see nvim_get_hl_by_id
+Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
+ FUNC_API_SINCE(3)
+{
+ Dictionary result = ARRAY_DICT_INIT;
+ int id = syn_name2id((const char_u *)name.data);
+
+ if (id == 0) {
+ api_set_error(err, kErrorTypeException, "Invalid highlight name: %s",
+ name.data);
+ return result;
+ }
+ result = nvim_get_hl_by_id(id, rgb, err);
+ return result;
+}
+
+/// Gets a highlight definition by id. |hlID()|
+///
+/// @param hl_id Highlight id as returned by |hlID()|
+/// @param rgb Export RGB colors
+/// @param[out] err Error details, if any
+/// @return Highlight definition map
+/// @see nvim_get_hl_by_name
+Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
+ FUNC_API_SINCE(3)
+{
+ Dictionary dic = ARRAY_DICT_INIT;
+ if (syn_get_final_id((int)hl_id) == 0) {
+ api_set_error(err, kErrorTypeException,
+ "Invalid highlight id: %" PRId64, hl_id);
+ return dic;
+ }
+ int attrcode = syn_id2attr((int)hl_id);
+ return hl_get_attr_by_id(attrcode, rgb, err);
+}
+
/// Passes input keys to Nvim.
/// On VimL error: Does not fail, but updates v:errmsg.
///
@@ -125,7 +172,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
/// On VimL error: Does not fail, but updates v:errmsg.
///
/// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call
-/// is not deferred. This is the most reliable way to emulate real user input.
+/// is not deferred. This is the most reliable way to send real user input.
+///
+/// @note |keycodes| like <CR> are translated, so "<" is special.
+/// To input a literal "<", send <LT>.
///
/// @param keys to be typed
/// @return Number of bytes actually written (can be fewer than
@@ -136,9 +186,13 @@ Integer nvim_input(String keys)
return (Integer)input_enqueue(keys);
}
-/// Replaces terminal codes and key codes (<CR>, <Esc>, ...) in a string with
+/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
/// the internal representation.
///
+/// @param str String to be converted.
+/// @param from_part Legacy Vim parameter. Usually true.
+/// @param do_lt Also translate <lt>. Ignored if `special` is false.
+/// @param special Replace |keycodes|, e.g. <CR> becomes a "\n" char.
/// @see replace_termcodes
/// @see cpoptions
String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
@@ -151,29 +205,54 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
}
char *ptr = NULL;
- // Set 'cpoptions' the way we want it.
- // FLAG_CPO_BSLASH set - backslashes are *not* treated specially
- // FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered
- // FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted
- // The third from end parameter of replace_termcodes() is true so that the
- // <lt> sequence is recognised - needed for a real backslash.
replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
from_part, do_lt, special, CPO_TO_CPO_FLAGS);
return cstr_as_string(ptr);
}
-String nvim_command_output(String str, Error *err)
+/// Executes an ex-command and returns its (non-error) output.
+/// Shell |:!| output is not captured.
+///
+/// On parse error: forwards the Vim error; does not update v:errmsg.
+/// On runtime error: forwards the Vim error; does not update v:errmsg.
+///
+/// @param command Ex-command string
+/// @param[out] err Error details (Vim error), if any
+String nvim_command_output(String command, Error *err)
FUNC_API_SINCE(1)
{
- do_cmdline_cmd("redir => v:command_output");
- nvim_command(str, err);
- do_cmdline_cmd("redir END");
+ const int save_msg_silent = msg_silent;
+ garray_T *const save_capture_ga = capture_ga;
+ garray_T capture_local;
+ ga_init(&capture_local, 1, 80);
+
+ try_start();
+ msg_silent++;
+ capture_ga = &capture_local;
+ do_cmdline_cmd(command.data);
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ try_end(err);
if (ERROR_SET(err)) {
- return (String)STRING_INIT;
+ goto theend;
}
- return cstr_to_string((char *)get_vim_var_str(VV_COMMAND_OUTPUT));
+ if (capture_local.ga_len > 1) {
+ // redir always(?) prepends a newline; remove it.
+ char *s = capture_local.ga_data;
+ assert(s[0] == '\n');
+ memmove(s, s + 1, (size_t)capture_local.ga_len);
+ s[capture_local.ga_len - 1] = '\0';
+ return (String) { // Caller will free the memory.
+ .data = s,
+ .size = (size_t)(capture_local.ga_len - 1),
+ };
+ }
+
+theend:
+ ga_clear(&capture_local);
+ return (String)STRING_INIT;
}
/// Evaluates a VimL expression (:help expression).
@@ -257,12 +336,11 @@ free_vim_args:
return rv;
}
-/// Execute lua code. Parameters might be passed, they are available inside
-/// the chunk as `...`. The chunk can return a value.
+/// Execute lua code. Parameters (if any) are available as `...` inside the
+/// chunk. The chunk can return a value.
///
-/// To evaluate an expression, it must be prefixed with "return ". For
-/// instance, to call a lua function with arguments sent in and get its
-/// return value back, use the code "return my_function(...)".
+/// Only statements are executed. To evaluate an expression, prefix it
+/// with `return`: return my_function(...)
///
/// @param code lua code to execute
/// @param args Arguments to the code
@@ -282,15 +360,15 @@ Object nvim_execute_lua(String code, Array args, Error *err)
/// @param text Some text
/// @param[out] err Error details, if any
/// @return Number of cells
-Integer nvim_strwidth(String str, Error *err)
+Integer nvim_strwidth(String text, Error *err)
FUNC_API_SINCE(1)
{
- if (str.size > INT_MAX) {
+ if (text.size > INT_MAX) {
api_set_error(err, kErrorTypeValidation, "String length is too high");
return 0;
}
- return (Integer) mb_string2cells((char_u *) str.data);
+ return (Integer)mb_string2cells((char_u *)text.data);
}
/// Gets the paths contained in 'runtimepath'.
@@ -425,29 +503,18 @@ void nvim_del_var(String name, Error *err)
dict_set_var(&globvardict, name, NIL, true, false, err);
}
-/// Sets a global variable
-///
/// @deprecated
-///
-/// @param name Variable name
-/// @param value Variable value
-/// @param[out] err Error details, if any
+/// @see nvim_set_var
/// @return Old value or nil if there was no previous value.
-///
-/// @warning It may return nil if there was no previous value
-/// or if previous value was `v:null`.
+/// @warning May return nil if there was no previous value
+/// OR if previous value was `v:null`.
Object vim_set_var(String name, Object value, Error *err)
{
return dict_set_var(&globvardict, name, value, false, true, err);
}
-/// Removes a global variable
-///
/// @deprecated
-///
-/// @param name Variable name
-/// @param[out] err Error details, if any
-/// @return Old value
+/// @see nvim_del_var
Object vim_del_var(String name, Error *err)
{
return dict_set_var(&globvardict, name, NIL, true, true, err);
@@ -486,7 +553,8 @@ void nvim_set_option(String name, Object value, Error *err)
set_option_to(NULL, SREQ_GLOBAL, name, value, err);
}
-/// Writes a message to vim output buffer
+/// Writes a message to the Vim output buffer. Does not append "\n", the
+/// message is buffered (won't display) until a linefeed is written.
///
/// @param str Message
void nvim_out_write(String str)
@@ -495,7 +563,8 @@ void nvim_out_write(String str)
write_msg(str, false);
}
-/// Writes a message to vim error buffer
+/// Writes a message to the Vim error buffer. Does not append "\n", the
+/// message is buffered (won't display) until a linefeed is written.
///
/// @param str Message
void nvim_err_write(String str)
@@ -504,8 +573,8 @@ void nvim_err_write(String str)
write_msg(str, true);
}
-/// Writes a message to vim error buffer. Appends a linefeed to ensure all
-/// contents are written.
+/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
+/// flushed (and displayed).
///
/// @param str Message
/// @see nvim_err_write()
@@ -549,7 +618,7 @@ Buffer nvim_get_current_buf(void)
/// Sets the current buffer
///
-/// @param id Buffer handle
+/// @param buffer Buffer handle
/// @param[out] err Error details, if any
void nvim_set_current_buf(Buffer buffer, Error *err)
FUNC_API_SINCE(1)
@@ -603,7 +672,7 @@ Window nvim_get_current_win(void)
/// Sets the current window
///
-/// @param handle Window handle
+/// @param window Window handle
void nvim_set_current_win(Window window, Error *err)
FUNC_API_SINCE(1)
{
@@ -656,7 +725,7 @@ Tabpage nvim_get_current_tabpage(void)
/// Sets the current tabpage
///
-/// @param handle Tabpage handle
+/// @param tabpage Tabpage handle
/// @param[out] err Error details, if any
void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
FUNC_API_SINCE(1)
@@ -688,7 +757,7 @@ void nvim_subscribe(uint64_t channel_id, String event)
char e[METHOD_MAXLEN + 1];
memcpy(e, event.data, length);
e[length] = NUL;
- channel_subscribe(channel_id, e);
+ rpc_subscribe(channel_id, e);
}
/// Unsubscribes to event broadcasts
@@ -704,7 +773,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
char e[METHOD_MAXLEN + 1];
memcpy(e, event.data, length);
e[length] = NUL;
- channel_unsubscribe(channel_id, e);
+ rpc_unsubscribe(channel_id, e);
}
Integer nvim_get_color_by_name(String name)
@@ -726,9 +795,8 @@ Dictionary nvim_get_color_map(void)
}
-/// Gets the current mode.
-/// mode: Mode string. |mode()|
-/// blocking: true if Nvim is waiting for input.
+/// Gets the current mode. |mode()|
+/// "blocking" is true if Nvim is waiting for input.
///
/// @returns Dictionary { "mode": String, "blocking": Boolean }
Dictionary nvim_get_mode(void)
@@ -744,17 +812,21 @@ Dictionary nvim_get_mode(void)
return rv;
}
-/// Get a list of dictionaries describing global (i.e. non-buffer) mappings
-/// Note that the "buffer" key will be 0 to represent false.
+/// Gets a list of dictionaries describing global (non-buffer) mappings.
+/// The "buffer" key in the returned dictionary is always zero.
///
-/// @param mode The abbreviation for the mode
-/// @returns An array of maparg() like dictionaries describing mappings
+/// @param mode Mode short-name ("n", "i", "v", ...)
+/// @returns Array of maparg()-like dictionaries describing mappings
ArrayOf(Dictionary) nvim_get_keymap(String mode)
FUNC_API_SINCE(3)
{
return keymap_array(mode, NULL);
}
+/// Returns a 2-tuple (Array), where item 0 is the current channel id and item
+/// 1 is the |api-metadata| map (Dictionary).
+///
+/// @returns 2-tuple [{channel-id}, {api-metadata}]
Array nvim_get_api_info(uint64_t channel_id)
FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_REMOTE_ONLY
{
@@ -857,6 +929,460 @@ theend:
return rv;
}
+typedef struct {
+ ExprASTNode **node_p;
+ Object *ret_node_p;
+} ExprASTConvStackItem;
+
+/// @cond DOXYGEN_NOT_A_FUNCTION
+typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
+/// @endcond
+
+/// Parse a VimL expression
+///
+/// @param[in] expr Expression to parse. Is always treated as a single line.
+/// @param[in] flags Flags:
+///
+/// - "m" if multiple expressions in a row are allowed (only
+/// the first one will be parsed),
+/// - "E" if EOC tokens are not allowed (determines whether
+/// they will stop parsing process or be recognized as an
+/// operator/space, though also yielding an error).
+/// - "l" when needing to start parsing with lvalues for
+/// ":let" or ":for".
+///
+/// Common flag sets:
+/// - "m" to parse like for ":echo".
+/// - "E" to parse like for "<C-r>=".
+/// - empty string for ":call".
+/// - "lm" to parse for ":let".
+/// @param[in] highlight If true, return value will also include "highlight"
+/// key containing array of 4-tuples (arrays) (Integer,
+/// Integer, Integer, String), where first three numbers
+/// define the highlighted region and represent line,
+/// starting column and ending column (latter exclusive:
+/// one should highlight region [start_col, end_col)).
+///
+/// @return AST: top-level dictionary with these keys:
+///
+/// "error": Dictionary with error, present only if parser saw some
+/// error. Contains the following keys:
+///
+/// "message": String, error message in printf format, translated.
+/// Must contain exactly one "%.*s".
+/// "arg": String, error message argument.
+///
+/// "len": Amount of bytes successfully parsed. With flags equal to ""
+/// that should be equal to the length of expr string.
+///
+/// @note: “Sucessfully parsed” here means “participated in AST
+/// creation”, not “till the first error”.
+///
+/// "ast": AST, either nil or a dictionary with these keys:
+///
+/// "type": node type, one of the value names from ExprASTNodeType
+/// stringified without "kExprNode" prefix.
+/// "start": a pair [line, column] describing where node is “started”
+/// where "line" is always 0 (will not be 0 if you will be
+/// using nvim_parse_viml() on e.g. ":let", but that is not
+/// present yet). Both elements are Integers.
+/// "len": “length” of the node. This and "start" are there for
+/// debugging purposes primary (debugging parser and providing
+/// debug information).
+/// "children": a list of nodes described in top/"ast". There always
+/// is zero, one or two children, key will not be present
+/// if node has no children. Maximum number of children
+/// may be found in node_maxchildren array.
+///
+/// Local values (present only for certain nodes):
+///
+/// "scope": a single Integer, specifies scope for "Option" and
+/// "PlainIdentifier" nodes. For "Option" it is one of
+/// ExprOptScope values, for "PlainIdentifier" it is one of
+/// ExprVarScope values.
+/// "ident": identifier (without scope, if any), present for "Option",
+/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
+/// "name": Integer, register name (one character) or -1. Only present
+/// for "Register" nodes.
+/// "cmp_type": String, comparison type, one of the value names from
+/// ExprComparisonType, stringified without "kExprCmp"
+/// prefix. Only present for "Comparison" nodes.
+/// "ccs_strategy": String, case comparison strategy, one of the
+/// value names from ExprCaseCompareStrategy,
+/// stringified without "kCCStrategy" prefix. Only
+/// present for "Comparison" nodes.
+/// "augmentation": String, augmentation type for "Assignment" nodes.
+/// Is either an empty string, "Add", "Subtract" or
+/// "Concat" for "=", "+=", "-=" or ".=" respectively.
+/// "invert": Boolean, true if result of comparison needs to be
+/// inverted. Only present for "Comparison" nodes.
+/// "ivalue": Integer, integer value for "Integer" nodes.
+/// "fvalue": Float, floating-point value for "Float" nodes.
+/// "svalue": String, value for "SingleQuotedString" and
+/// "DoubleQuotedString" nodes.
+Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
+ Error *err)
+ FUNC_API_SINCE(4) FUNC_API_ASYNC
+{
+ int pflags = 0;
+ for (size_t i = 0 ; i < flags.size ; i++) {
+ switch (flags.data[i]) {
+ case 'm': { pflags |= kExprFlagsMulti; break; }
+ case 'E': { pflags |= kExprFlagsDisallowEOC; break; }
+ case 'l': { pflags |= kExprFlagsParseLet; break; }
+ case NUL: {
+ api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
+ (unsigned)flags.data[i]);
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+ default: {
+ api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)",
+ flags.data[i], (unsigned)flags.data[i]);
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+ }
+ }
+ ParserLine plines[] = {
+ {
+ .data = expr.data,
+ .size = expr.size,
+ .allocated = false,
+ },
+ { NULL, 0, false },
+ };
+ ParserLine *plines_p = plines;
+ ParserHighlight colors;
+ kvi_init(colors);
+ ParserHighlight *const colors_p = (highlight ? &colors : NULL);
+ ParserState pstate;
+ viml_parser_init(
+ &pstate, parser_simple_get_line, &plines_p, colors_p);
+ ExprAST east = viml_pexpr_parse(&pstate, pflags);
+
+ const size_t ret_size = (
+ 2 // "ast", "len"
+ + (size_t)(east.err.msg != NULL) // "error"
+ + (size_t)highlight // "highlight"
+ + 0);
+ Dictionary ret = {
+ .items = xmalloc(ret_size * sizeof(ret.items[0])),
+ .size = 0,
+ .capacity = ret_size,
+ };
+ ret.items[ret.size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ast"),
+ .value = NIL,
+ };
+ ret.items[ret.size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("len"),
+ .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
+ ? plines[0].size
+ : pstate.pos.col)),
+ };
+ if (east.err.msg != NULL) {
+ Dictionary err_dict = {
+ .items = xmalloc(2 * sizeof(err_dict.items[0])),
+ .size = 2,
+ .capacity = 2,
+ };
+ err_dict.items[0] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("message"),
+ .value = STRING_OBJ(cstr_to_string(east.err.msg)),
+ };
+ if (east.err.arg == NULL) {
+ err_dict.items[1] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("arg"),
+ .value = STRING_OBJ(STRING_INIT),
+ };
+ } else {
+ err_dict.items[1] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("arg"),
+ .value = STRING_OBJ(((String) {
+ .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len),
+ .size = (size_t)east.err.arg_len,
+ })),
+ };
+ }
+ ret.items[ret.size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("error"),
+ .value = DICTIONARY_OBJ(err_dict),
+ };
+ }
+ if (highlight) {
+ Array hl = (Array) {
+ .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])),
+ .capacity = kv_size(colors),
+ .size = kv_size(colors),
+ };
+ for (size_t i = 0 ; i < kv_size(colors) ; i++) {
+ const ParserHighlightChunk chunk = kv_A(colors, i);
+ Array chunk_arr = (Array) {
+ .items = xmalloc(4 * sizeof(chunk_arr.items[0])),
+ .capacity = 4,
+ .size = 4,
+ };
+ chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line);
+ chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col);
+ chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col);
+ chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group));
+ hl.items[i] = ARRAY_OBJ(chunk_arr);
+ }
+ ret.items[ret.size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("highlight"),
+ .value = ARRAY_OBJ(hl),
+ };
+ }
+ kvi_destroy(colors);
+
+ // Walk over the AST, freeing nodes in process.
+ ExprASTConvStack ast_conv_stack;
+ kvi_init(ast_conv_stack);
+ kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
+ .node_p = &east.root,
+ .ret_node_p = &ret.items[0].value,
+ }));
+ while (kv_size(ast_conv_stack)) {
+ ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
+ ExprASTNode *const node = *cur_item.node_p;
+ if (node == NULL) {
+ assert(kv_size(ast_conv_stack) == 1);
+ kv_drop(ast_conv_stack, 1);
+ } else {
+ if (cur_item.ret_node_p->type == kObjectTypeNil) {
+ const size_t ret_node_items_size = (size_t)(
+ 3 // "type", "start" and "len"
+ + (node->children != NULL) // "children"
+ + (node->type == kExprNodeOption
+ || node->type == kExprNodePlainIdentifier) // "scope"
+ + (node->type == kExprNodeOption
+ || node->type == kExprNodePlainIdentifier
+ || node->type == kExprNodePlainKey
+ || node->type == kExprNodeEnvironment) // "ident"
+ + (node->type == kExprNodeRegister) // "name"
+ + (3 // "cmp_type", "ccs_strategy", "invert"
+ * (node->type == kExprNodeComparison))
+ + (node->type == kExprNodeInteger) // "ivalue"
+ + (node->type == kExprNodeFloat) // "fvalue"
+ + (node->type == kExprNodeDoubleQuotedString
+ || node->type == kExprNodeSingleQuotedString) // "svalue"
+ + (node->type == kExprNodeAssignment) // "augmentation"
+ + 0);
+ Dictionary ret_node = {
+ .items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])),
+ .capacity = ret_node_items_size,
+ .size = 0,
+ };
+ *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node);
+ }
+ Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary;
+ if (node->children != NULL) {
+ const size_t num_children = 1 + (node->children->next != NULL);
+ Array children_array = {
+ .items = xmalloc(num_children * sizeof(children_array.items[0])),
+ .capacity = num_children,
+ .size = num_children,
+ };
+ for (size_t i = 0; i < num_children; i++) {
+ children_array.items[i] = NIL;
+ }
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("children"),
+ .value = ARRAY_OBJ(children_array),
+ };
+ kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
+ .node_p = &node->children,
+ .ret_node_p = &children_array.items[0],
+ }));
+ } else if (node->next != NULL) {
+ kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
+ .node_p = &node->next,
+ .ret_node_p = cur_item.ret_node_p + 1,
+ }));
+ } else if (node != NULL) {
+ kv_drop(ast_conv_stack, 1);
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("type"),
+ .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])),
+ };
+ Array start_array = {
+ .items = xmalloc(2 * sizeof(start_array.items[0])),
+ .capacity = 2,
+ .size = 2,
+ };
+ start_array.items[0] = INTEGER_OBJ((Integer)node->start.line);
+ start_array.items[1] = INTEGER_OBJ((Integer)node->start.col);
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("start"),
+ .value = ARRAY_OBJ(start_array),
+ };
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("len"),
+ .value = INTEGER_OBJ((Integer)node->len),
+ };
+ switch (node->type) {
+ case kExprNodeDoubleQuotedString:
+ case kExprNodeSingleQuotedString: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("svalue"),
+ .value = STRING_OBJ(((String) {
+ .data = node->data.str.value,
+ .size = node->data.str.size,
+ })),
+ };
+ break;
+ }
+ case kExprNodeOption: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("scope"),
+ .value = INTEGER_OBJ(node->data.opt.scope),
+ };
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ident"),
+ .value = STRING_OBJ(((String) {
+ .data = xmemdupz(node->data.opt.ident,
+ node->data.opt.ident_len),
+ .size = node->data.opt.ident_len,
+ })),
+ };
+ break;
+ }
+ case kExprNodePlainIdentifier: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("scope"),
+ .value = INTEGER_OBJ(node->data.var.scope),
+ };
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ident"),
+ .value = STRING_OBJ(((String) {
+ .data = xmemdupz(node->data.var.ident,
+ node->data.var.ident_len),
+ .size = node->data.var.ident_len,
+ })),
+ };
+ break;
+ }
+ case kExprNodePlainKey: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ident"),
+ .value = STRING_OBJ(((String) {
+ .data = xmemdupz(node->data.var.ident,
+ node->data.var.ident_len),
+ .size = node->data.var.ident_len,
+ })),
+ };
+ break;
+ }
+ case kExprNodeEnvironment: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ident"),
+ .value = STRING_OBJ(((String) {
+ .data = xmemdupz(node->data.env.ident,
+ node->data.env.ident_len),
+ .size = node->data.env.ident_len,
+ })),
+ };
+ break;
+ }
+ case kExprNodeRegister: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("name"),
+ .value = INTEGER_OBJ(node->data.reg.name),
+ };
+ break;
+ }
+ case kExprNodeComparison: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("cmp_type"),
+ .value = STRING_OBJ(cstr_to_string(
+ eltkn_cmp_type_tab[node->data.cmp.type])),
+ };
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ccs_strategy"),
+ .value = STRING_OBJ(cstr_to_string(
+ ccs_tab[node->data.cmp.ccs])),
+ };
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("invert"),
+ .value = BOOLEAN_OBJ(node->data.cmp.inv),
+ };
+ break;
+ }
+ case kExprNodeFloat: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("fvalue"),
+ .value = FLOAT_OBJ(node->data.flt.value),
+ };
+ break;
+ }
+ case kExprNodeInteger: {
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("ivalue"),
+ .value = INTEGER_OBJ((Integer)(
+ node->data.num.value > API_INTEGER_MAX
+ ? API_INTEGER_MAX
+ : (Integer)node->data.num.value)),
+ };
+ break;
+ }
+ case kExprNodeAssignment: {
+ const ExprAssignmentType asgn_type = node->data.ass.type;
+ ret_node->items[ret_node->size++] = (KeyValuePair) {
+ .key = STATIC_CSTR_TO_STRING("augmentation"),
+ .value = STRING_OBJ(
+ asgn_type == kExprAsgnPlain
+ ? (String)STRING_INIT
+ : cstr_to_string(expr_asgn_type_tab[asgn_type])),
+ };
+ break;
+ }
+ case kExprNodeMissing:
+ case kExprNodeOpMissing:
+ case kExprNodeTernary:
+ case kExprNodeTernaryValue:
+ case kExprNodeSubscript:
+ case kExprNodeListLiteral:
+ case kExprNodeUnaryPlus:
+ case kExprNodeBinaryPlus:
+ case kExprNodeNested:
+ case kExprNodeCall:
+ case kExprNodeComplexIdentifier:
+ case kExprNodeUnknownFigure:
+ case kExprNodeLambda:
+ case kExprNodeDictLiteral:
+ case kExprNodeCurlyBracesIdentifier:
+ case kExprNodeComma:
+ case kExprNodeColon:
+ case kExprNodeArrow:
+ case kExprNodeConcat:
+ case kExprNodeConcatOrSubscript:
+ case kExprNodeOr:
+ case kExprNodeAnd:
+ case kExprNodeUnaryMinus:
+ case kExprNodeBinaryMinus:
+ case kExprNodeNot:
+ case kExprNodeMultiplication:
+ case kExprNodeDivision:
+ case kExprNodeMod: {
+ break;
+ }
+ }
+ assert(cur_item.ret_node_p->data.dictionary.size
+ == cur_item.ret_node_p->data.dictionary.capacity);
+ xfree(*cur_item.node_p);
+ *cur_item.node_p = NULL;
+ }
+ }
+ }
+ kvi_destroy(ast_conv_stack);
+
+ assert(ret.size == ret.capacity);
+ // Should be a no-op actually, leaving it in case non-nodes will need to be
+ // freed later.
+ viml_pexpr_free_ast(east);
+ viml_parser_destroy(&pstate);
+ return ret;
+}
+
/// Writes a message to vim output or error buffer. The string is split
/// and flushed after each newline. Incomplete lines are kept for writing
@@ -944,3 +1470,95 @@ Float nvim__id_float(Float flt)
{
return flt;
}
+
+/// Gets a list of dictionaries representing attached UIs.
+///
+/// @return Array of UI dictionaries
+Array nvim_list_uis(void)
+ FUNC_API_SINCE(4)
+{
+ return ui_array();
+}
+
+/// Gets the immediate children of process `pid`.
+///
+/// @return Array of child process ids, empty if process not found.
+Array nvim_get_proc_children(Integer pid, Error *err)
+ FUNC_API_SINCE(4)
+{
+ Array rvobj = ARRAY_DICT_INIT;
+ int *proc_list = NULL;
+
+ if (pid <= 0 || pid > INT_MAX) {
+ api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
+ goto end;
+ }
+
+ size_t proc_count;
+ int rv = os_proc_children((int)pid, &proc_list, &proc_count);
+ if (rv != 0) {
+ // syscall failed (possibly because of kernel options), try shelling out.
+ DLOG("fallback to vim._os_proc_children()");
+ Array a = ARRAY_DICT_INIT;
+ ADD(a, INTEGER_OBJ(pid));
+ String s = cstr_to_string("return vim._os_proc_children(select(1, ...))");
+ Object o = nvim_execute_lua(s, a, err);
+ api_free_string(s);
+ api_free_array(a);
+ if (o.type == kObjectTypeArray) {
+ rvobj = o.data.array;
+ } else if (!ERROR_SET(err)) {
+ api_set_error(err, kErrorTypeException,
+ "Failed to get process children. pid=%" PRId64 " error=%d",
+ pid, rv);
+ }
+ goto end;
+ }
+
+ for (size_t i = 0; i < proc_count; i++) {
+ ADD(rvobj, INTEGER_OBJ(proc_list[i]));
+ }
+
+end:
+ xfree(proc_list);
+ return rvobj;
+}
+
+/// Gets info describing process `pid`.
+///
+/// @return Map of process properties, or NIL if process not found.
+Object nvim_get_proc(Integer pid, Error *err)
+ FUNC_API_SINCE(4)
+{
+ Object rvobj = OBJECT_INIT;
+ rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT;
+ rvobj.type = kObjectTypeDictionary;
+
+ if (pid <= 0 || pid > INT_MAX) {
+ api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
+ return NIL;
+ }
+#ifdef WIN32
+ rvobj.data.dictionary = os_proc_info((int)pid);
+ if (rvobj.data.dictionary.size == 0) { // Process not found.
+ return NIL;
+ }
+#else
+ // Cross-platform process info APIs are miserable, so use `ps` instead.
+ Array a = ARRAY_DICT_INIT;
+ ADD(a, INTEGER_OBJ(pid));
+ String s = cstr_to_string("return vim._os_proc_info(select(1, ...))");
+ Object o = nvim_execute_lua(s, a, err);
+ api_free_string(s);
+ api_free_array(a);
+ if (o.type == kObjectTypeArray && o.data.array.size == 0) {
+ return NIL; // Process not found.
+ } else if (o.type == kObjectTypeDictionary) {
+ rvobj.data.dictionary = o.data.dictionary;
+ } else if (!ERROR_SET(err)) {
+ api_set_error(err, kErrorTypeException,
+ "Failed to get process info. pid=%" PRId64, pid);
+ }
+#endif
+ return rvobj;
+}
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 1ef51d2a2a..e120e6d492 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -393,491 +393,141 @@ static bool A_is_f(int cur_c)
// Change shape - from ISO-8859-6/Isolated to Form-B Isolated
static int chg_c_a2s(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_HAMZA:
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA:
- tempc = a_s_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_s_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA:
- tempc = a_s_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_s_ALEF_HAMZA_BELOW;
- break;
-
- case a_YEH_HAMZA:
- tempc = a_s_YEH_HAMZA;
- break;
-
- case a_ALEF:
- tempc = a_s_ALEF;
- break;
-
- case a_TEH_MARBUTA:
- tempc = a_s_TEH_MARBUTA;
- break;
-
- case a_DAL:
- tempc = a_s_DAL;
- break;
-
- case a_THAL:
- tempc = a_s_THAL;
- break;
-
- case a_REH:
- tempc = a_s_REH;
- break;
-
- case a_ZAIN:
- tempc = a_s_ZAIN;
- break;
-
- case a_TATWEEL: // exceptions
- tempc = cur_c;
- break;
-
- case a_WAW:
- tempc = a_s_WAW;
- break;
-
- case a_ALEF_MAKSURA:
- tempc = a_s_ALEF_MAKSURA;
- break;
-
- case a_BEH:
- tempc = a_s_BEH;
- break;
-
- case a_TEH:
- tempc = a_s_TEH;
- break;
-
- case a_THEH:
- tempc = a_s_THEH;
- break;
-
- case a_JEEM:
- tempc = a_s_JEEM;
- break;
-
- case a_HAH:
- tempc = a_s_HAH;
- break;
-
- case a_KHAH:
- tempc = a_s_KHAH;
- break;
-
- case a_SEEN:
- tempc = a_s_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_s_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_s_SAD;
- break;
-
- case a_DAD:
- tempc = a_s_DAD;
- break;
-
- case a_TAH:
- tempc = a_s_TAH;
- break;
-
- case a_ZAH:
- tempc = a_s_ZAH;
- break;
-
- case a_AIN:
- tempc = a_s_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_s_GHAIN;
- break;
-
- case a_FEH:
- tempc = a_s_FEH;
- break;
-
- case a_QAF:
- tempc = a_s_QAF;
- break;
-
- case a_KAF:
- tempc = a_s_KAF;
- break;
-
- case a_LAM:
- tempc = a_s_LAM;
- break;
-
- case a_MEEM:
- tempc = a_s_MEEM;
- break;
-
- case a_NOON:
- tempc = a_s_NOON;
- break;
-
- case a_HEH:
- tempc = a_s_HEH;
- break;
-
- case a_YEH:
- tempc = a_s_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_HAMZA: return a_s_HAMZA;
+ case a_ALEF_MADDA: return a_s_ALEF_MADDA;
+ case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE;
+ case a_WAW_HAMZA: return a_s_WAW_HAMZA;
+ case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW;
+ case a_YEH_HAMZA: return a_s_YEH_HAMZA;
+ case a_ALEF: return a_s_ALEF;
+ case a_TEH_MARBUTA: return a_s_TEH_MARBUTA;
+ case a_DAL: return a_s_DAL;
+ case a_THAL: return a_s_THAL;
+ case a_REH: return a_s_REH;
+ case a_ZAIN: return a_s_ZAIN;
+ case a_TATWEEL: return cur_c; // exceptions
+ case a_WAW: return a_s_WAW;
+ case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA;
+ case a_BEH: return a_s_BEH;
+ case a_TEH: return a_s_TEH;
+ case a_THEH: return a_s_THEH;
+ case a_JEEM: return a_s_JEEM;
+ case a_HAH: return a_s_HAH;
+ case a_KHAH: return a_s_KHAH;
+ case a_SEEN: return a_s_SEEN;
+ case a_SHEEN: return a_s_SHEEN;
+ case a_SAD: return a_s_SAD;
+ case a_DAD: return a_s_DAD;
+ case a_TAH: return a_s_TAH;
+ case a_ZAH: return a_s_ZAH;
+ case a_AIN: return a_s_AIN;
+ case a_GHAIN: return a_s_GHAIN;
+ case a_FEH: return a_s_FEH;
+ case a_QAF: return a_s_QAF;
+ case a_KAF: return a_s_KAF;
+ case a_LAM: return a_s_LAM;
+ case a_MEEM: return a_s_MEEM;
+ case a_NOON: return a_s_NOON;
+ case a_HEH: return a_s_HEH;
+ case a_YEH: return a_s_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from ISO-8859-6/Isolated to Initial
static int chg_c_a2i(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_YEH_HAMZA:
- tempc = a_i_YEH_HAMZA;
- break;
-
- case a_HAMZA: // exceptions
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA: // exceptions
- tempc = a_s_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE: // exceptions
- tempc = a_s_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA: // exceptions
- tempc = a_s_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW: // exceptions
- tempc = a_s_ALEF_HAMZA_BELOW;
- break;
-
- case a_ALEF: // exceptions
- tempc = a_s_ALEF;
- break;
-
- case a_TEH_MARBUTA: // exceptions
- tempc = a_s_TEH_MARBUTA;
- break;
-
- case a_DAL: // exceptions
- tempc = a_s_DAL;
- break;
-
- case a_THAL: // exceptions
- tempc = a_s_THAL;
- break;
-
- case a_REH: // exceptions
- tempc = a_s_REH;
- break;
-
- case a_ZAIN: // exceptions
- tempc = a_s_ZAIN;
- break;
-
- case a_TATWEEL: // exceptions
- tempc = cur_c;
- break;
-
- case a_WAW: // exceptions
- tempc = a_s_WAW;
- break;
-
- case a_ALEF_MAKSURA: // exceptions
- tempc = a_s_ALEF_MAKSURA;
- break;
-
- case a_BEH:
- tempc = a_i_BEH;
- break;
-
- case a_TEH:
- tempc = a_i_TEH;
- break;
-
- case a_THEH:
- tempc = a_i_THEH;
- break;
-
- case a_JEEM:
- tempc = a_i_JEEM;
- break;
-
- case a_HAH:
- tempc = a_i_HAH;
- break;
-
- case a_KHAH:
- tempc = a_i_KHAH;
- break;
-
- case a_SEEN:
- tempc = a_i_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_i_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_i_SAD;
- break;
-
- case a_DAD:
- tempc = a_i_DAD;
- break;
-
- case a_TAH:
- tempc = a_i_TAH;
- break;
-
- case a_ZAH:
- tempc = a_i_ZAH;
- break;
-
- case a_AIN:
- tempc = a_i_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_i_GHAIN;
- break;
-
- case a_FEH:
- tempc = a_i_FEH;
- break;
-
- case a_QAF:
- tempc = a_i_QAF;
- break;
-
- case a_KAF:
- tempc = a_i_KAF;
- break;
-
- case a_LAM:
- tempc = a_i_LAM;
- break;
-
- case a_MEEM:
- tempc = a_i_MEEM;
- break;
-
- case a_NOON:
- tempc = a_i_NOON;
- break;
-
- case a_HEH:
- tempc = a_i_HEH;
- break;
-
- case a_YEH:
- tempc = a_i_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_YEH_HAMZA: return a_i_YEH_HAMZA;
+ case a_HAMZA: return a_s_HAMZA; // exceptions
+ case a_ALEF_MADDA: return a_s_ALEF_MADDA; // exceptions
+ case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE; // exceptions
+ case a_WAW_HAMZA: return a_s_WAW_HAMZA; // exceptions
+ case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW; // exceptions
+ case a_ALEF: return a_s_ALEF; // exceptions
+ case a_TEH_MARBUTA: return a_s_TEH_MARBUTA; // exceptions
+ case a_DAL: return a_s_DAL; // exceptions
+ case a_THAL: return a_s_THAL; // exceptions
+ case a_REH: return a_s_REH; // exceptions
+ case a_ZAIN: return a_s_ZAIN; // exceptions
+ case a_TATWEEL: return cur_c; // exceptions
+ case a_WAW: return a_s_WAW; // exceptions
+ case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA; // exceptions
+ case a_BEH: return a_i_BEH;
+ case a_TEH: return a_i_TEH;
+ case a_THEH: return a_i_THEH;
+ case a_JEEM: return a_i_JEEM;
+ case a_HAH: return a_i_HAH;
+ case a_KHAH: return a_i_KHAH;
+ case a_SEEN: return a_i_SEEN;
+ case a_SHEEN: return a_i_SHEEN;
+ case a_SAD: return a_i_SAD;
+ case a_DAD: return a_i_DAD;
+ case a_TAH: return a_i_TAH;
+ case a_ZAH: return a_i_ZAH;
+ case a_AIN: return a_i_AIN;
+ case a_GHAIN: return a_i_GHAIN;
+ case a_FEH: return a_i_FEH;
+ case a_QAF: return a_i_QAF;
+ case a_KAF: return a_i_KAF;
+ case a_LAM: return a_i_LAM;
+ case a_MEEM: return a_i_MEEM;
+ case a_NOON: return a_i_NOON;
+ case a_HEH: return a_i_HEH;
+ case a_YEH: return a_i_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from ISO-8859-6/Isolated to Medial
static int chg_c_a2m(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_HAMZA: // exception
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA: // exception
- tempc = a_f_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE: // exception
- tempc = a_f_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA: // exception
- tempc = a_f_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW: // exception
- tempc = a_f_ALEF_HAMZA_BELOW;
- break;
-
- case a_YEH_HAMZA:
- tempc = a_m_YEH_HAMZA;
- break;
-
- case a_ALEF: // exception
- tempc = a_f_ALEF;
- break;
-
- case a_BEH:
- tempc = a_m_BEH;
- break;
-
- case a_TEH_MARBUTA: // exception
- tempc = a_f_TEH_MARBUTA;
- break;
-
- case a_TEH:
- tempc = a_m_TEH;
- break;
-
- case a_THEH:
- tempc = a_m_THEH;
- break;
-
- case a_JEEM:
- tempc = a_m_JEEM;
- break;
-
- case a_HAH:
- tempc = a_m_HAH;
- break;
-
- case a_KHAH:
- tempc = a_m_KHAH;
- break;
-
- case a_DAL: // exception
- tempc = a_f_DAL;
- break;
-
- case a_THAL: // exception
- tempc = a_f_THAL;
- break;
-
- case a_REH: // exception
- tempc = a_f_REH;
- break;
-
- case a_ZAIN: // exception
- tempc = a_f_ZAIN;
- break;
-
- case a_SEEN:
- tempc = a_m_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_m_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_m_SAD;
- break;
-
- case a_DAD:
- tempc = a_m_DAD;
- break;
-
- case a_TAH:
- tempc = a_m_TAH;
- break;
-
- case a_ZAH:
- tempc = a_m_ZAH;
- break;
-
- case a_AIN:
- tempc = a_m_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_m_GHAIN;
- break;
-
- case a_TATWEEL: // exception
- tempc = cur_c;
- break;
-
- case a_FEH:
- tempc = a_m_FEH;
- break;
-
- case a_QAF:
- tempc = a_m_QAF;
- break;
-
- case a_KAF:
- tempc = a_m_KAF;
- break;
-
- case a_LAM:
- tempc = a_m_LAM;
- break;
-
- case a_MEEM:
- tempc = a_m_MEEM;
- break;
-
- case a_NOON:
- tempc = a_m_NOON;
- break;
-
- case a_HEH:
- tempc = a_m_HEH;
- break;
-
- case a_WAW: // exception
- tempc = a_f_WAW;
- break;
-
- case a_ALEF_MAKSURA: // exception
- tempc = a_f_ALEF_MAKSURA;
- break;
-
- case a_YEH:
- tempc = a_m_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_HAMZA: return a_s_HAMZA; // exception
+ case a_ALEF_MADDA: return a_f_ALEF_MADDA; // exception
+ case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE; // exception
+ case a_WAW_HAMZA: return a_f_WAW_HAMZA; // exception
+ case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW; // exception
+ case a_YEH_HAMZA: return a_m_YEH_HAMZA;
+ case a_ALEF: return a_f_ALEF; // exception
+ case a_BEH: return a_m_BEH;
+ case a_TEH_MARBUTA: return a_f_TEH_MARBUTA; // exception
+ case a_TEH: return a_m_TEH;
+ case a_THEH: return a_m_THEH;
+ case a_JEEM: return a_m_JEEM;
+ case a_HAH: return a_m_HAH;
+ case a_KHAH: return a_m_KHAH;
+ case a_DAL: return a_f_DAL; // exception
+ case a_THAL: return a_f_THAL; // exception
+ case a_REH: return a_f_REH; // exception
+ case a_ZAIN: return a_f_ZAIN; // exception
+ case a_SEEN: return a_m_SEEN;
+ case a_SHEEN: return a_m_SHEEN;
+ case a_SAD: return a_m_SAD;
+ case a_DAD: return a_m_DAD;
+ case a_TAH: return a_m_TAH;
+ case a_ZAH: return a_m_ZAH;
+ case a_AIN: return a_m_AIN;
+ case a_GHAIN: return a_m_GHAIN;
+ case a_TATWEEL: return cur_c; // exception
+ case a_FEH: return a_m_FEH;
+ case a_QAF: return a_m_QAF;
+ case a_KAF: return a_m_KAF;
+ case a_LAM: return a_m_LAM;
+ case a_MEEM: return a_m_MEEM;
+ case a_NOON: return a_m_NOON;
+ case a_HEH: return a_m_HEH;
+ case a_WAW: return a_f_WAW; // exception
+ case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA; // exception
+ case a_YEH: return a_m_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from ISO-8859-6/Isolated to final
static int chg_c_a2f(int cur_c)
{
- int tempc;
-
// NOTE: these encodings need to be accounted for
//
// a_f_ALEF_MADDA;
@@ -888,280 +538,87 @@ static int chg_c_a2f(int cur_c)
// a_f_LAM_ALEF_HAMZA_BELOW;
switch (cur_c) {
- case a_HAMZA: // exception
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA:
- tempc = a_f_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_f_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA:
- tempc = a_f_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_f_ALEF_HAMZA_BELOW;
- break;
-
- case a_YEH_HAMZA:
- tempc = a_f_YEH_HAMZA;
- break;
-
- case a_ALEF:
- tempc = a_f_ALEF;
- break;
-
- case a_BEH:
- tempc = a_f_BEH;
- break;
-
- case a_TEH_MARBUTA:
- tempc = a_f_TEH_MARBUTA;
- break;
-
- case a_TEH:
- tempc = a_f_TEH;
- break;
-
- case a_THEH:
- tempc = a_f_THEH;
- break;
-
- case a_JEEM:
- tempc = a_f_JEEM;
- break;
-
- case a_HAH:
- tempc = a_f_HAH;
- break;
-
- case a_KHAH:
- tempc = a_f_KHAH;
- break;
-
- case a_DAL:
- tempc = a_f_DAL;
- break;
-
- case a_THAL:
- tempc = a_f_THAL;
- break;
-
- case a_REH:
- tempc = a_f_REH;
- break;
-
- case a_ZAIN:
- tempc = a_f_ZAIN;
- break;
-
- case a_SEEN:
- tempc = a_f_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_f_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_f_SAD;
- break;
-
- case a_DAD:
- tempc = a_f_DAD;
- break;
-
- case a_TAH:
- tempc = a_f_TAH;
- break;
-
- case a_ZAH:
- tempc = a_f_ZAH;
- break;
-
- case a_AIN:
- tempc = a_f_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_f_GHAIN;
- break;
-
- case a_TATWEEL: // exception
- tempc = cur_c;
- break;
-
- case a_FEH:
- tempc = a_f_FEH;
- break;
-
- case a_QAF:
- tempc = a_f_QAF;
- break;
-
- case a_KAF:
- tempc = a_f_KAF;
- break;
-
- case a_LAM:
- tempc = a_f_LAM;
- break;
-
- case a_MEEM:
- tempc = a_f_MEEM;
- break;
-
- case a_NOON:
- tempc = a_f_NOON;
- break;
-
- case a_HEH:
- tempc = a_f_HEH;
- break;
-
- case a_WAW:
- tempc = a_f_WAW;
- break;
-
- case a_ALEF_MAKSURA:
- tempc = a_f_ALEF_MAKSURA;
- break;
-
- case a_YEH:
- tempc = a_f_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_HAMZA: return a_s_HAMZA; // exception
+ case a_ALEF_MADDA: return a_f_ALEF_MADDA;
+ case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE;
+ case a_WAW_HAMZA: return a_f_WAW_HAMZA;
+ case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW;
+ case a_YEH_HAMZA: return a_f_YEH_HAMZA;
+ case a_ALEF: return a_f_ALEF;
+ case a_BEH: return a_f_BEH;
+ case a_TEH_MARBUTA: return a_f_TEH_MARBUTA;
+ case a_TEH: return a_f_TEH;
+ case a_THEH: return a_f_THEH;
+ case a_JEEM: return a_f_JEEM;
+ case a_HAH: return a_f_HAH;
+ case a_KHAH: return a_f_KHAH;
+ case a_DAL: return a_f_DAL;
+ case a_THAL: return a_f_THAL;
+ case a_REH: return a_f_REH;
+ case a_ZAIN: return a_f_ZAIN;
+ case a_SEEN: return a_f_SEEN;
+ case a_SHEEN: return a_f_SHEEN;
+ case a_SAD: return a_f_SAD;
+ case a_DAD: return a_f_DAD;
+ case a_TAH: return a_f_TAH;
+ case a_ZAH: return a_f_ZAH;
+ case a_AIN: return a_f_AIN;
+ case a_GHAIN: return a_f_GHAIN;
+ case a_TATWEEL: return cur_c; // exception
+ case a_FEH: return a_f_FEH;
+ case a_QAF: return a_f_QAF;
+ case a_KAF: return a_f_KAF;
+ case a_LAM: return a_f_LAM;
+ case a_MEEM: return a_f_MEEM;
+ case a_NOON: return a_f_NOON;
+ case a_HEH: return a_f_HEH;
+ case a_WAW: return a_f_WAW;
+ case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA;
+ case a_YEH: return a_f_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Initial to Medial
static int chg_c_i2m(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_i_YEH_HAMZA:
- tempc = a_m_YEH_HAMZA;
- break;
-
- case a_i_BEH:
- tempc = a_m_BEH;
- break;
-
- case a_i_TEH:
- tempc = a_m_TEH;
- break;
-
- case a_i_THEH:
- tempc = a_m_THEH;
- break;
-
- case a_i_JEEM:
- tempc = a_m_JEEM;
- break;
-
- case a_i_HAH:
- tempc = a_m_HAH;
- break;
-
- case a_i_KHAH:
- tempc = a_m_KHAH;
- break;
-
- case a_i_SEEN:
- tempc = a_m_SEEN;
- break;
-
- case a_i_SHEEN:
- tempc = a_m_SHEEN;
- break;
-
- case a_i_SAD:
- tempc = a_m_SAD;
- break;
-
- case a_i_DAD:
- tempc = a_m_DAD;
- break;
-
- case a_i_TAH:
- tempc = a_m_TAH;
- break;
-
- case a_i_ZAH:
- tempc = a_m_ZAH;
- break;
-
- case a_i_AIN:
- tempc = a_m_AIN;
- break;
-
- case a_i_GHAIN:
- tempc = a_m_GHAIN;
- break;
-
- case a_i_FEH:
- tempc = a_m_FEH;
- break;
-
- case a_i_QAF:
- tempc = a_m_QAF;
- break;
-
- case a_i_KAF:
- tempc = a_m_KAF;
- break;
-
- case a_i_LAM:
- tempc = a_m_LAM;
- break;
-
- case a_i_MEEM:
- tempc = a_m_MEEM;
- break;
-
- case a_i_NOON:
- tempc = a_m_NOON;
- break;
-
- case a_i_HEH:
- tempc = a_m_HEH;
- break;
-
- case a_i_YEH:
- tempc = a_m_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_i_YEH_HAMZA: return a_m_YEH_HAMZA;
+ case a_i_BEH: return a_m_BEH;
+ case a_i_TEH: return a_m_TEH;
+ case a_i_THEH: return a_m_THEH;
+ case a_i_JEEM: return a_m_JEEM;
+ case a_i_HAH: return a_m_HAH;
+ case a_i_KHAH: return a_m_KHAH;
+ case a_i_SEEN: return a_m_SEEN;
+ case a_i_SHEEN: return a_m_SHEEN;
+ case a_i_SAD: return a_m_SAD;
+ case a_i_DAD: return a_m_DAD;
+ case a_i_TAH: return a_m_TAH;
+ case a_i_ZAH: return a_m_ZAH;
+ case a_i_AIN: return a_m_AIN;
+ case a_i_GHAIN: return a_m_GHAIN;
+ case a_i_FEH: return a_m_FEH;
+ case a_i_QAF: return a_m_QAF;
+ case a_i_KAF: return a_m_KAF;
+ case a_i_LAM: return a_m_LAM;
+ case a_i_MEEM: return a_m_MEEM;
+ case a_i_NOON: return a_m_NOON;
+ case a_i_HEH: return a_m_HEH;
+ case a_i_YEH: return a_m_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Final to Medial
static int chg_c_f2m(int cur_c)
{
- int tempc;
-
switch (cur_c) {
// NOTE: these encodings are multi-positional, no ?
// case a_f_ALEF_MADDA:
// case a_f_ALEF_HAMZA_ABOVE:
// case a_f_ALEF_HAMZA_BELOW:
- case a_f_YEH_HAMZA:
- tempc = a_m_YEH_HAMZA;
- break;
-
+ case a_f_YEH_HAMZA: return a_m_YEH_HAMZA;
case a_f_WAW_HAMZA: // exceptions
case a_f_ALEF:
case a_f_TEH_MARBUTA:
@@ -1171,165 +628,60 @@ static int chg_c_f2m(int cur_c)
case a_f_ZAIN:
case a_f_WAW:
case a_f_ALEF_MAKSURA:
- tempc = cur_c;
- break;
-
- case a_f_BEH:
- tempc = a_m_BEH;
- break;
-
- case a_f_TEH:
- tempc = a_m_TEH;
- break;
-
- case a_f_THEH:
- tempc = a_m_THEH;
- break;
-
- case a_f_JEEM:
- tempc = a_m_JEEM;
- break;
-
- case a_f_HAH:
- tempc = a_m_HAH;
- break;
-
- case a_f_KHAH:
- tempc = a_m_KHAH;
- break;
-
- case a_f_SEEN:
- tempc = a_m_SEEN;
- break;
-
- case a_f_SHEEN:
- tempc = a_m_SHEEN;
- break;
-
- case a_f_SAD:
- tempc = a_m_SAD;
- break;
-
- case a_f_DAD:
- tempc = a_m_DAD;
- break;
-
- case a_f_TAH:
- tempc = a_m_TAH;
- break;
-
- case a_f_ZAH:
- tempc = a_m_ZAH;
- break;
-
- case a_f_AIN:
- tempc = a_m_AIN;
- break;
-
- case a_f_GHAIN:
- tempc = a_m_GHAIN;
- break;
-
- case a_f_FEH:
- tempc = a_m_FEH;
- break;
-
- case a_f_QAF:
- tempc = a_m_QAF;
- break;
-
- case a_f_KAF:
- tempc = a_m_KAF;
- break;
-
- case a_f_LAM:
- tempc = a_m_LAM;
- break;
-
- case a_f_MEEM:
- tempc = a_m_MEEM;
- break;
-
- case a_f_NOON:
- tempc = a_m_NOON;
- break;
-
- case a_f_HEH:
- tempc = a_m_HEH;
- break;
-
- case a_f_YEH:
- tempc = a_m_YEH;
- break;
-
+ return cur_c;
+ case a_f_BEH: return a_m_BEH;
+ case a_f_TEH: return a_m_TEH;
+ case a_f_THEH: return a_m_THEH;
+ case a_f_JEEM: return a_m_JEEM;
+ case a_f_HAH: return a_m_HAH;
+ case a_f_KHAH: return a_m_KHAH;
+ case a_f_SEEN: return a_m_SEEN;
+ case a_f_SHEEN: return a_m_SHEEN;
+ case a_f_SAD: return a_m_SAD;
+ case a_f_DAD: return a_m_DAD;
+ case a_f_TAH: return a_m_TAH;
+ case a_f_ZAH: return a_m_ZAH;
+ case a_f_AIN: return a_m_AIN;
+ case a_f_GHAIN: return a_m_GHAIN;
+ case a_f_FEH: return a_m_FEH;
+ case a_f_QAF: return a_m_QAF;
+ case a_f_KAF: return a_m_KAF;
+ case a_f_LAM: return a_m_LAM;
+ case a_f_MEEM: return a_m_MEEM;
+ case a_f_NOON: return a_m_NOON;
+ case a_f_HEH: return a_m_HEH;
+ case a_f_YEH: return a_m_YEH;
// NOTE: these encodings are multi-positional, no ?
// case a_f_LAM_ALEF_MADDA_ABOVE:
// case a_f_LAM_ALEF_HAMZA_ABOVE:
// case a_f_LAM_ALEF_HAMZA_BELOW:
// case a_f_LAM_ALEF:
- default:
- tempc = 0;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Combination (2 char) to an Isolated.
static int chg_c_laa2i(int hid_c)
{
- int tempc;
-
switch (hid_c) {
- case a_ALEF_MADDA:
- tempc = a_s_LAM_ALEF_MADDA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_s_LAM_ALEF_HAMZA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_s_LAM_ALEF_HAMZA_BELOW;
- break;
-
- case a_ALEF:
- tempc = a_s_LAM_ALEF;
- break;
-
- default:
- tempc = 0;
+ case a_ALEF_MADDA: return a_s_LAM_ALEF_MADDA_ABOVE;
+ case a_ALEF_HAMZA_ABOVE: return a_s_LAM_ALEF_HAMZA_ABOVE;
+ case a_ALEF_HAMZA_BELOW: return a_s_LAM_ALEF_HAMZA_BELOW;
+ case a_ALEF: return a_s_LAM_ALEF;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Combination-Isolated to Final.
static int chg_c_laa2f(int hid_c)
{
- int tempc;
-
switch (hid_c) {
- case a_ALEF_MADDA:
- tempc = a_f_LAM_ALEF_MADDA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_f_LAM_ALEF_HAMZA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_f_LAM_ALEF_HAMZA_BELOW;
- break;
-
- case a_ALEF:
- tempc = a_f_LAM_ALEF;
- break;
-
- default:
- tempc = 0;
+ case a_ALEF_MADDA: return a_f_LAM_ALEF_MADDA_ABOVE;
+ case a_ALEF_HAMZA_ABOVE: return a_f_LAM_ALEF_HAMZA_ABOVE;
+ case a_ALEF_HAMZA_BELOW: return a_f_LAM_ALEF_HAMZA_BELOW;
+ case a_ALEF: return a_f_LAM_ALEF;
}
-
- return tempc;
+ return 0;
}
// Do "half-shaping" on character "c". Return zero if no shaping.
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h
index adde91f9ec..ff6840d690 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
+#include "nvim/macros.h"
#include "nvim/func_attr.h"
#include "nvim/os/os_defs.h"
@@ -98,6 +99,10 @@ static inline bool ascii_isxdigit(int)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
+static inline bool ascii_isident(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
+
static inline bool ascii_isbdigit(int)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
@@ -138,6 +143,14 @@ static inline bool ascii_isxdigit(int c)
|| (c >= 'A' && c <= 'F');
}
+/// Checks if `c` is an “identifier” character
+///
+/// That is, whether it is alphanumeric character or underscore.
+static inline bool ascii_isident(int c)
+{
+ return ASCII_ISALNUM(c) || c == '_';
+}
+
/// Checks if `c` is a binary digit, that is, 0-1.
///
/// @see {ascii_isdigit}
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c
new file mode 100644
index 0000000000..fc421116ea
--- /dev/null
+++ b/src/nvim/aucmd.c
@@ -0,0 +1,41 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/os/os.h"
+#include "nvim/fileio.h"
+#include "nvim/vim.h"
+#include "nvim/main.h"
+#include "nvim/ui.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "aucmd.c.generated.h"
+#endif
+
+static void focusgained_event(void **argv)
+{
+ bool *gainedp = argv[0];
+ do_autocmd_focusgained(*gainedp);
+ xfree(gainedp);
+}
+void aucmd_schedule_focusgained(bool gained)
+{
+ bool *gainedp = xmalloc(sizeof(*gainedp));
+ *gainedp = gained;
+ loop_schedule_deferred(&main_loop,
+ event_create(focusgained_event, 1, gainedp));
+}
+
+static void do_autocmd_focusgained(bool gained)
+ FUNC_ATTR_NONNULL_ALL
+{
+ static bool recursive = false;
+
+ if (recursive) {
+ return; // disallow recursion
+ }
+ recursive = true;
+ apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST),
+ NULL, NULL, false, curbuf);
+ recursive = false;
+}
+
diff --git a/src/nvim/aucmd.h b/src/nvim/aucmd.h
new file mode 100644
index 0000000000..6570ba7a92
--- /dev/null
+++ b/src/nvim/aucmd.h
@@ -0,0 +1,9 @@
+#ifndef NVIM_AUCMD_H
+#define NVIM_AUCMD_H
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "aucmd.h.generated.h"
+#endif
+
+#endif // NVIM_AUCMD_H
+
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 68a47c244f..7dfaf54ff0 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -19,6 +19,8 @@ return {
'BufWriteCmd', -- write buffer using command
'BufWritePost', -- after writing a buffer
'BufWritePre', -- before writing a buffer
+ 'CmdLineEnter', -- after entering cmdline mode
+ 'CmdLineLeave', -- before leaving cmdline mode
'CmdUndefined', -- command undefined
'CmdWinEnter', -- after entering the cmdline window
'CmdWinLeave', -- before leaving the cmdline window
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index b5ca6543c5..0cd6f628b5 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -64,7 +64,6 @@
#include "nvim/spell.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/version.h"
@@ -91,6 +90,57 @@ static char *e_auabort = N_("E855: Autocommands caused command to abort");
// Number of times free_buffer() was called.
static int buf_free_count = 0;
+// Read data from buffer for retrying.
+static int
+read_buffer(
+ int read_stdin, // read file from stdin, otherwise fifo
+ exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
+ int flags) // extra flags for readfile()
+{
+ int retval = OK;
+ linenr_T line_count;
+
+ //
+ // Read from the buffer which the text is already filled in and append at
+ // the end. This makes it possible to retry when 'fileformat' or
+ // 'fileencoding' was guessed wrong.
+ //
+ line_count = curbuf->b_ml.ml_line_count;
+ retval = readfile(
+ read_stdin ? NULL : curbuf->b_ffname,
+ read_stdin ? NULL : curbuf->b_fname,
+ (linenr_T)line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
+ flags | READ_BUFFER);
+ if (retval == OK) {
+ // Delete the binary lines.
+ while (--line_count >= 0) {
+ ml_delete((linenr_T)1, false);
+ }
+ } else {
+ // Delete the converted lines.
+ while (curbuf->b_ml.ml_line_count > line_count) {
+ ml_delete(line_count, false);
+ }
+ }
+ // Put the cursor on the first line.
+ curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+
+ if (read_stdin) {
+ // Set or reset 'modified' before executing autocommands, so that
+ // it can be changed there.
+ if (!readonlymode && !BUFEMPTY()) {
+ changed();
+ } else if (retval != FAIL) {
+ unchanged(curbuf, false);
+ }
+
+ apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
+ curbuf, &retval);
+ }
+ return retval;
+}
+
/*
* Open current buffer, that is: open the memfile and read the file into
* memory.
@@ -106,6 +156,7 @@ open_buffer (
int retval = OK;
bufref_T old_curbuf;
long old_tw = curbuf->b_p_tw;
+ int read_fifo = false;
/*
* The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
@@ -156,13 +207,45 @@ open_buffer (
if (curbuf->b_ffname != NULL) {
int old_msg_silent = msg_silent;
+#ifdef UNIX
+ int save_bin = curbuf->b_p_bin;
+ int perm;
+
+ perm = os_getperm((const char *)curbuf->b_ffname);
+ if (perm >= 0 && (0
+# ifdef S_ISFIFO
+ || S_ISFIFO(perm)
+# endif
+# ifdef S_ISSOCK
+ || S_ISSOCK(perm)
+# endif
+# ifdef OPEN_CHR_FILES
+ || (S_ISCHR(perm)
+ && is_dev_fd_file(curbuf->b_ffname))
+# endif
+ )
+ ) {
+ read_fifo = true;
+ }
+ if (read_fifo) {
+ curbuf->b_p_bin = true;
+ }
+#endif
if (shortmess(SHM_FILEINFO)) {
msg_silent = 1;
}
retval = readfile(curbuf->b_ffname, curbuf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_NEW);
+ flags | READ_NEW | (read_fifo ? READ_FIFO : 0));
+#ifdef UNIX
+ if (read_fifo) {
+ curbuf->b_p_bin = save_bin;
+ if (retval == OK) {
+ retval = read_buffer(false, eap, flags);
+ }
+ }
+#endif
msg_silent = old_msg_silent;
// Help buffer is filtered.
@@ -171,7 +254,6 @@ open_buffer (
}
} else if (read_stdin) {
int save_bin = curbuf->b_p_bin;
- linenr_T line_count;
/*
* First read the text in binary mode into the buffer.
@@ -185,41 +267,13 @@ open_buffer (
flags | (READ_NEW + READ_STDIN));
curbuf->b_p_bin = save_bin;
if (retval == OK) {
- line_count = curbuf->b_ml.ml_line_count;
- retval = readfile(NULL, NULL, (linenr_T)line_count,
- (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_BUFFER);
- if (retval == OK) {
- /* Delete the binary lines. */
- while (--line_count >= 0)
- ml_delete((linenr_T)1, FALSE);
- } else {
- /* Delete the converted lines. */
- while (curbuf->b_ml.ml_line_count > line_count)
- ml_delete(line_count, FALSE);
- }
- /* Put the cursor on the first line. */
- curwin->w_cursor.lnum = 1;
- curwin->w_cursor.col = 0;
-
- // Set or reset 'modified' before executing autocommands, so that
- // it can be changed there.
- if (!readonlymode && !bufempty()) {
- changed();
- } else if (retval == OK) {
- unchanged(curbuf, false);
- }
-
- if (retval == OK) {
- apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
- curbuf, &retval);
- }
+ retval = read_buffer(true, eap, flags);
}
}
/* if first time loading this buffer, init b_chartab[] */
if (curbuf->b_flags & BF_NEVERLOADED) {
- (void)buf_init_chartab(curbuf, FALSE);
+ (void)buf_init_chartab(curbuf, false);
parse_cino(curbuf);
}
@@ -234,7 +288,7 @@ open_buffer (
|| modified_was_set // ":set modified" used in autocmd
|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
changed();
- } else if (retval == OK && !read_stdin) {
+ } else if (retval != FAIL && !read_stdin && !read_fifo) {
unchanged(curbuf, false);
}
save_file_ff(curbuf); // keep this fileformat
@@ -416,8 +470,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
return;
}
- /* When the buffer becomes hidden, but is not unloaded, trigger
- * BufHidden */
+ // When the buffer becomes hidden, but is not unloaded, trigger
+ // BufHidden
if (!unload_buf) {
buf->b_locked++;
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false,
@@ -1148,8 +1202,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;
}
@@ -1333,31 +1387,40 @@ void set_curbuf(buf_T *buf, int action)
/* Don't restart Select mode after switching to another buffer. */
VIsual_reselect = FALSE;
- /* close_windows() or apply_autocmds() may change curbuf */
+ // close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
prevbuf = curbuf;
- bufref_T bufref;
- set_bufref(&bufref, prevbuf);
+ bufref_T newbufref;
+ bufref_T prevbufref;
+ set_bufref(&prevbufref, prevbuf);
+ set_bufref(&newbufref, buf);
+ // Autocommands may delete the curren buffer and/or the buffer we wan to go
+ // to. In those cases don't close the buffer.
if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf)
- || (bufref_valid(&bufref) && !aborting())) {
+ || (bufref_valid(&prevbufref) && bufref_valid(&newbufref)
+ && !aborting())) {
if (prevbuf == curwin->w_buffer) {
reset_synblock(curwin);
}
if (unload) {
close_windows(prevbuf, false);
}
- if (bufref_valid(&bufref) && !aborting()) {
+ if (bufref_valid(&prevbufref) && !aborting()) {
win_T *previouswin = curwin;
- if (prevbuf == curbuf)
- u_sync(FALSE);
- close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf,
- unload ? action : (action == DOBUF_GOTO
- && !P_HID(prevbuf)
- && !bufIsChanged(
- prevbuf)) ? DOBUF_UNLOAD : 0, FALSE);
- if (curwin != previouswin && win_valid(previouswin))
- /* autocommands changed curwin, Grr! */
+ if (prevbuf == curbuf) {
+ u_sync(false);
+ }
+ close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL,
+ prevbuf,
+ unload
+ ? action
+ : (action == DOBUF_GOTO && !buf_hide(prevbuf)
+ && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
+ false);
+ if (curwin != previouswin && win_valid(previouswin)) {
+ // autocommands changed curwin, Grr!
curwin = previouswin;
+ }
}
}
/* An autocommand may have deleted "buf", already entered it (e.g., when
@@ -1409,12 +1472,6 @@ void enter_buffer(buf_T *buf)
/* mark cursor position as being invalid */
curwin->w_valid = 0;
- if (buf->terminal) {
- terminal_resize(buf->terminal,
- (uint16_t)curwin->w_width,
- (uint16_t)curwin->w_height);
- }
-
/* Make sure the buffer is loaded. */
if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */
/* If there is no filetype, allow for detecting one. Esp. useful for
@@ -1568,7 +1625,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
&& curbuf != NULL
&& curbuf->b_ffname == NULL
&& curbuf->b_nwindows <= 1
- && (curbuf->b_ml.ml_mfp == NULL || bufempty())) {
+ && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) {
buf = curbuf;
/* It's like this buffer is deleted. Watch out for autocommands that
* change curbuf! If that happens, allocate a new buffer anyway. */
@@ -1727,6 +1784,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_flp);
clear_string_option(&buf->b_p_isk);
clear_string_option(&buf->b_p_keymap);
+ keymap_ga_clear(&buf->b_kmap_ga);
ga_clear(&buf->b_kmap_ga);
clear_string_option(&buf->b_p_com);
clear_string_option(&buf->b_p_cms);
@@ -1760,6 +1818,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
clear_string_option(&buf->b_p_lw);
clear_string_option(&buf->b_p_bkc);
+ clear_string_option(&buf->b_p_menc);
}
@@ -1822,7 +1881,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
// current buffer isn't empty: open new tab or window
if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
- && !bufempty()) {
+ && !BUFEMPTY()) {
if (swb_flags & SWB_NEWTAB) {
tabpage_new();
} else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
@@ -2624,7 +2683,7 @@ void buflist_altfpos(win_T *win)
}
/// Check that "ffname" is not the same file as current file.
-/// Fname must have a full path (expanded by path_get_absolute_path()).
+/// Fname must have a full path (expanded by path_to_absolute()).
///
/// @param ffname full path name to check
bool otherfile(char_u *ffname)
@@ -2634,7 +2693,7 @@ bool otherfile(char_u *ffname)
}
/// Check that "ffname" is not the same file as the file loaded in "buf".
-/// Fname must have a full path (expanded by path_get_absolute_path()).
+/// Fname must have a full path (expanded by path_to_absolute()).
///
/// @param buf buffer to check
/// @param ffname full path name to check
@@ -3014,8 +3073,8 @@ static bool ti_change(char_u *str, char_u **last)
/// Set current window title
void resettitle(void)
{
- ui_call_set_title(cstr_as_string((char *)lasttitle));
ui_call_set_icon(cstr_as_string((char *)lasticon));
+ ui_call_set_title(cstr_as_string((char *)lasttitle));
ui_flush();
}
@@ -4358,12 +4417,12 @@ do_arg_all (
}
wp->w_arg_idx = i;
- if (i == opened_len && !keep_tabs) { /* close this window */
- if (P_HID(buf) || forceit || buf->b_nwindows > 1
+ if (i == opened_len && !keep_tabs) { // close this window
+ if (buf_hide(buf) || forceit || buf->b_nwindows > 1
|| !bufIsChanged(buf)) {
/* If the buffer was changed, and we would like to hide it,
* try autowriting. */
- if (!P_HID(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
+ if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
bufref_T bufref;
set_bufref(&bufref, buf);
(void)autowrite(buf, false);
@@ -4373,15 +4432,17 @@ do_arg_all (
continue;
}
}
- /* don't close last window */
- if (firstwin == lastwin
- && (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... */
+ // don't close last window
+ if (ONE_WINDOW
+ && (first_tabpage->tp_next == NULL || !had_tab)) {
+ use_firstwin = true;
+ } else {
+ win_close(wp, !buf_hide(buf) && !bufIsChanged(buf));
+ // check if autocommands removed the next window
+ if (!win_valid(wpnext)) {
+ // start all over...
+ wpnext = firstwin;
+ }
}
}
}
@@ -4410,11 +4471,12 @@ do_arg_all (
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
- /* ":drop all" should re-use an empty window to avoid "--remote-tab"
- * leaving an empty tab page when executed locally. */
- if (keep_tabs && bufempty() && curbuf->b_nwindows == 1
- && curbuf->b_ffname == NULL && !curbuf->b_changed)
- use_firstwin = TRUE;
+ // ":drop all" should re-use an empty window to avoid "--remote-tab"
+ // leaving an empty tab page when executed locally.
+ if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1
+ && curbuf->b_ffname == NULL && !curbuf->b_changed) {
+ use_firstwin = true;
+ }
for (i = 0; i < count && i < opened_len && !got_int; ++i) {
if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1)
@@ -4453,14 +4515,15 @@ do_arg_all (
new_curwin = curwin;
new_curtab = curtab;
}
- (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL,
- ECMD_ONE,
- ((P_HID(curwin->w_buffer)
- || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0)
- + ECMD_OLDBUF, curwin);
- if (use_firstwin)
- ++autocmd_no_leave;
- use_firstwin = FALSE;
+ (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
+ ((buf_hide(curwin->w_buffer)
+ || bufIsChanged(curwin->w_buffer))
+ ? ECMD_HIDE : 0) + ECMD_OLDBUF,
+ curwin);
+ if (use_firstwin) {
+ autocmd_no_leave++;
+ }
+ use_firstwin = false;
}
os_breakcheck();
@@ -4538,7 +4601,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);
@@ -4647,14 +4710,14 @@ void ex_buffer_all(exarg_T *eap)
* Close superfluous windows.
*/
for (wp = lastwin; open_wins > count; ) {
- r = (P_HID(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
- || autowrite(wp->w_buffer, FALSE) == OK);
+ r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
+ || autowrite(wp->w_buffer, false) == OK);
if (!win_valid(wp)) {
/* BufWrite Autocommands made the window invalid, start over */
wp = lastwin;
} else if (r) {
- win_close(wp, !P_HID(wp->w_buffer));
- --open_wins;
+ win_close(wp, !buf_hide(wp->w_buffer));
+ open_wins--;
wp = lastwin;
} else {
wp = wp->w_prev;
@@ -5221,6 +5284,44 @@ int bufhl_add_hl(buf_T *buf,
return src_id;
}
+/// Add highlighting to a buffer, bounded by two cursor positions,
+/// with an offset.
+///
+/// @param buf Buffer to add highlights to
+/// @param src_id src_id to use or 0 to use a new src_id group,
+/// or -1 for ungrouped highlight.
+/// @param hl_id Highlight group id
+/// @param pos_start Cursor position to start the hightlighting at
+/// @param pos_end Cursor position to end the highlighting at
+/// @param offset Move the whole highlighting this many columns to the right
+void bufhl_add_hl_pos_offset(buf_T *buf,
+ int src_id,
+ int hl_id,
+ lpos_T pos_start,
+ lpos_T pos_end,
+ colnr_T offset)
+{
+ colnr_T hl_start = 0;
+ colnr_T hl_end = 0;
+
+ for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) {
+ if (pos_start.lnum < lnum && lnum < pos_end.lnum) {
+ hl_start = offset;
+ hl_end = MAXCOL;
+ } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) {
+ hl_start = pos_start.col + offset + 1;
+ hl_end = MAXCOL;
+ } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) {
+ hl_start = offset;
+ hl_end = pos_end.col + offset;
+ } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) {
+ hl_start = pos_start.col + offset + 1;
+ hl_end = pos_end.col + offset;
+ }
+ (void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end);
+ }
+}
+
/// Clear bufhl highlights from a given source group and range of lines.
///
/// @param buf The buffer to remove highlights from
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 559dffb945..8de4286216 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -603,6 +603,7 @@ struct file_buffer {
char_u *b_p_bt; ///< 'buftype'
int b_has_qf_entry; ///< quickfix exists for buffer
int b_p_bl; ///< 'buflisted'
+ long b_p_channel; ///< 'channel'
int b_p_cin; ///< 'cindent'
char_u *b_p_cino; ///< 'cinoptions'
char_u *b_p_cink; ///< 'cinkeys'
@@ -636,6 +637,7 @@ struct file_buffer {
uint32_t b_p_fex_flags; ///< flags for 'formatexpr'
char_u *b_p_kp; ///< 'keywordprg'
int b_p_lisp; ///< 'lisp'
+ char_u *b_p_menc; ///< 'makeencoding'
char_u *b_p_mps; ///< 'matchpairs'
int b_p_ml; ///< 'modeline'
int b_p_ml_nobin; ///< b_p_ml saved for binary mode
@@ -716,6 +718,7 @@ struct file_buffer {
int b_ind_hash_comment;
int b_ind_cpp_namespace;
int b_ind_if_for_while;
+ int b_ind_cpp_extern_c;
linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary
* write should not have an end-of-line */
diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h
index 24bd6b7f29..14b1afa7d9 100644
--- a/src/nvim/bufhl_defs.h
+++ b/src/nvim/bufhl_defs.h
@@ -29,6 +29,6 @@ typedef struct {
} BufhlLineInfo;
#define BUFHL_CMP(a, b) ((int)(((a)->line - (b)->line)))
-KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10)
+KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10) // -V512
typedef kbtree_t(bufhl) BufhlInfo;
#endif // NVIM_BUFHL_DEFS_H
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
new file mode 100644
index 0000000000..2e32af2e9a
--- /dev/null
+++ b/src/nvim/channel.c
@@ -0,0 +1,756 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/api/ui.h"
+#include "nvim/channel.h"
+#include "nvim/eval.h"
+#include "nvim/eval/encode.h"
+#include "nvim/event/socket.h"
+#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/server.h"
+#include "nvim/os/shell.h"
+#include "nvim/path.h"
+#include "nvim/ascii.h"
+
+static bool did_stdio = false;
+PMap(uint64_t) *channels = NULL;
+
+/// next free id for a job or rpc channel
+/// 1 is reserved for stdio channel
+/// 2 is reserved for stderr channel
+static uint64_t next_chan_id = CHAN_STDERR+1;
+
+
+typedef struct {
+ Channel *chan;
+ Callback *callback;
+ const char *type;
+ list_T *received;
+ int status;
+} ChannelEvent;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "channel.c.generated.h"
+#endif
+/// Teardown the module
+void channel_teardown(void)
+{
+ if (!channels) {
+ return;
+ }
+
+ Channel *channel;
+
+ map_foreach_value(channels, channel, {
+ channel_close(channel->id, kChannelPartAll, NULL);
+ });
+}
+
+/// Closes a channel
+///
+/// @param id The channel id
+/// @return true if successful, false otherwise
+bool channel_close(uint64_t id, ChannelPart part, const char **error)
+{
+ Channel *chan;
+ Process *proc;
+
+ const char *dummy;
+ if (!error) {
+ error = &dummy;
+ }
+
+ if (!(chan = find_channel(id))) {
+ if (id < next_chan_id) {
+ // allow double close, even though we can't say what parts was valid.
+ return true;
+ }
+ *error = (const char *)e_invchan;
+ return false;
+ }
+
+ bool close_main = false;
+ if (part == kChannelPartRpc || part == kChannelPartAll) {
+ close_main = true;
+ if (chan->is_rpc) {
+ rpc_close(chan);
+ } else if (part == kChannelPartRpc) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ } else if ((part == kChannelPartStdin || part == kChannelPartStdout)
+ && chan->is_rpc) {
+ *error = (const char *)e_invstreamrpc;
+ return false;
+ }
+
+ switch (chan->streamtype) {
+ case kChannelStreamSocket:
+ if (!close_main) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ stream_may_close(&chan->stream.socket);
+ break;
+
+ case kChannelStreamProc:
+ proc = (Process *)&chan->stream.proc;
+ if (part == kChannelPartStdin || close_main) {
+ stream_may_close(&proc->in);
+ }
+ if (part == kChannelPartStdout || close_main) {
+ stream_may_close(&proc->out);
+ }
+ if (part == kChannelPartStderr || part == kChannelPartAll) {
+ stream_may_close(&proc->err);
+ }
+ if (proc->type == kProcessTypePty && part == kChannelPartAll) {
+ pty_process_close_master(&chan->stream.pty);
+ }
+
+ break;
+
+ case kChannelStreamStdio:
+ if (part == kChannelPartStdin || close_main) {
+ stream_may_close(&chan->stream.stdio.in);
+ }
+ if (part == kChannelPartStdout || close_main) {
+ stream_may_close(&chan->stream.stdio.out);
+ }
+ if (part == kChannelPartStderr) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ break;
+
+ case kChannelStreamStderr:
+ if (part != kChannelPartAll && part != kChannelPartStderr) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ if (!chan->stream.err.closed) {
+ chan->stream.err.closed = true;
+ // Don't close on exit, in case late error messages
+ if (!exiting) {
+ fclose(stderr);
+ }
+ channel_decref(chan);
+ }
+ break;
+
+ case kChannelStreamInternal:
+ if (!close_main) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ break;
+ }
+
+ return true;
+}
+
+/// Initializes the module
+void channel_init(void)
+{
+ channels = pmap_new(uint64_t)();
+ channel_alloc(kChannelStreamStderr);
+ rpc_init();
+ remote_ui_init();
+}
+
+/// Allocates a channel.
+///
+/// Channel is allocated with refcount 1, which should be decreased
+/// when the underlying stream closes.
+static Channel *channel_alloc(ChannelStreamType type)
+{
+ Channel *chan = xcalloc(1, sizeof(*chan));
+ if (type == kChannelStreamStdio) {
+ chan->id = CHAN_STDIO;
+ } else if (type == kChannelStreamStderr) {
+ chan->id = CHAN_STDERR;
+ } else {
+ chan->id = next_chan_id++;
+ }
+ chan->events = multiqueue_new_child(main_loop.events);
+ chan->refcount = 1;
+ chan->streamtype = type;
+ pmap_put(uint64_t)(channels, chan->id, chan);
+ return chan;
+}
+
+/// Not implemented, only logging for now
+void channel_create_event(Channel *chan, const char *ext_source)
+{
+#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
+ const char *stream_desc;
+ const char *mode_desc;
+ const char *source;
+
+ switch (chan->streamtype) {
+ case kChannelStreamProc:
+ if (chan->stream.proc.type == kProcessTypePty) {
+ stream_desc = "pty job";
+ } else {
+ stream_desc = "job";
+ }
+ break;
+
+ case kChannelStreamStdio:
+ stream_desc = "stdio";
+ break;
+
+ case kChannelStreamSocket:
+ stream_desc = "socket";
+ break;
+
+ case kChannelStreamInternal:
+ stream_desc = "socket (internal)";
+ break;
+
+ default:
+ stream_desc = "?";
+ }
+
+ if (chan->is_rpc) {
+ mode_desc = ", rpc";
+ } else if (chan->term) {
+ mode_desc = ", terminal";
+ } else {
+ mode_desc = "";
+ }
+
+ if (ext_source) {
+ // TODO(bfredl): in a future improved traceback solution,
+ // external events should be included.
+ source = ext_source;
+ } else {
+ eval_fmt_source_name_line((char *)IObuff, sizeof(IObuff));
+ source = (const char *)IObuff;
+ }
+
+ ILOG("new channel %" PRIu64 " (%s%s): %s", chan->id, stream_desc,
+ mode_desc, source);
+#else
+ (void)chan;
+ (void)ext_source;
+#endif
+}
+
+void channel_incref(Channel *channel)
+{
+ channel->refcount++;
+}
+
+void channel_decref(Channel *channel)
+{
+ if (!(--channel->refcount)) {
+ multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel);
+ }
+}
+
+void callback_reader_free(CallbackReader *reader)
+{
+ callback_free(&reader->cb);
+ if (reader->buffered) {
+ ga_clear(&reader->buffer);
+ }
+}
+
+void callback_reader_start(CallbackReader *reader)
+{
+ if (reader->buffered) {
+ ga_init(&reader->buffer, sizeof(char *), 32);
+ ga_grow(&reader->buffer, 32);
+ }
+}
+
+static void free_channel_event(void **argv)
+{
+ Channel *channel = argv[0];
+ if (channel->is_rpc) {
+ rpc_free(channel);
+ }
+
+ callback_reader_free(&channel->on_stdout);
+ callback_reader_free(&channel->on_stderr);
+ callback_free(&channel->on_exit);
+
+ pmap_del(uint64_t)(channels, channel->id);
+ multiqueue_free(channel->events);
+ xfree(channel);
+}
+
+static void channel_destroy_early(Channel *chan)
+{
+ if ((chan->id != --next_chan_id)) {
+ abort();
+ }
+
+ if ((--chan->refcount != 0)) {
+ abort();
+ }
+
+ free_channel_event((void **)&chan);
+}
+
+
+static void close_cb(Stream *stream, void *data)
+{
+ channel_decref(data);
+}
+
+Channel *channel_job_start(char **argv, CallbackReader on_stdout,
+ CallbackReader on_stderr, Callback on_exit,
+ bool pty, bool rpc, bool detach, const char *cwd,
+ uint16_t pty_width, uint16_t pty_height,
+ char *term_name, varnumber_T *status_out)
+{
+ Channel *chan = channel_alloc(kChannelStreamProc);
+ chan->on_stdout = on_stdout;
+ chan->on_stderr = on_stderr;
+ chan->on_exit = on_exit;
+ chan->is_rpc = rpc;
+
+ if (pty) {
+ if (detach) {
+ EMSG2(_(e_invarg2), "terminal/pty job cannot be detached");
+ shell_free_argv(argv);
+ xfree(term_name);
+ channel_destroy_early(chan);
+ *status_out = 0;
+ return NULL;
+ }
+ chan->stream.pty = pty_process_init(&main_loop, chan);
+ if (pty_width > 0) {
+ chan->stream.pty.width = pty_width;
+ }
+ if (pty_height > 0) {
+ chan->stream.pty.height = pty_height;
+ }
+ if (term_name) {
+ chan->stream.pty.term_name = term_name;
+ }
+ } else {
+ chan->stream.uv = libuv_process_init(&main_loop, chan);
+ }
+
+ Process *proc = (Process *)&chan->stream.proc;
+ proc->argv = argv;
+ proc->cb = channel_process_exit_cb;
+ proc->events = chan->events;
+ proc->detach = detach;
+ proc->cwd = cwd;
+
+ char *cmd = xstrdup(proc->argv[0]);
+ bool has_out, has_err;
+ if (proc->type == kProcessTypePty) {
+ has_out = true;
+ has_err = false;
+ } else {
+ has_out = chan->is_rpc || callback_reader_set(chan->on_stdout);
+ has_err = callback_reader_set(chan->on_stderr);
+ }
+ int status = process_spawn(proc, true, has_out, has_err);
+ if (status) {
+ EMSG3(_(e_jobspawn), os_strerror(status), cmd);
+ xfree(cmd);
+ if (proc->type == kProcessTypePty) {
+ xfree(chan->stream.pty.term_name);
+ }
+ channel_destroy_early(chan);
+ *status_out = proc->status;
+ return NULL;
+ }
+ xfree(cmd);
+
+ wstream_init(&proc->in, 0);
+ if (has_out) {
+ rstream_init(&proc->out, 0);
+ }
+
+ if (chan->is_rpc) {
+ // the rpc takes over the in and out streams
+ rpc_start(chan);
+ } else {
+ if (has_out) {
+ callback_reader_start(&chan->on_stdout);
+ rstream_start(&proc->out, on_job_stdout, chan);
+ }
+ }
+
+ if (has_err) {
+ callback_reader_start(&chan->on_stderr);
+ rstream_init(&proc->err, 0);
+ rstream_start(&proc->err, on_job_stderr, chan);
+ }
+
+ *status_out = (varnumber_T)chan->id;
+ return chan;
+}
+
+
+uint64_t channel_connect(bool tcp, const char *address,
+ bool rpc, CallbackReader on_output,
+ int timeout, const char **error)
+{
+ Channel *channel;
+
+ if (!tcp && rpc) {
+ char *path = fix_fname(address);
+ bool loopback = server_owns_pipe_address(path);
+ xfree(path);
+ if (loopback) {
+ // Create a loopback channel. This avoids deadlock if nvim connects to
+ // its own named pipe.
+ channel = channel_alloc(kChannelStreamInternal);
+ rpc_start(channel);
+ goto end;
+ }
+ }
+
+ channel = channel_alloc(kChannelStreamSocket);
+ if (!socket_connect(&main_loop, &channel->stream.socket,
+ tcp, address, timeout, error)) {
+ channel_destroy_early(channel);
+ return 0;
+ }
+
+ channel->stream.socket.internal_close_cb = close_cb;
+ channel->stream.socket.internal_data = channel;
+ wstream_init(&channel->stream.socket, 0);
+ rstream_init(&channel->stream.socket, 0);
+
+ if (rpc) {
+ rpc_start(channel);
+ } else {
+ channel->on_stdout = on_output;
+ callback_reader_start(&channel->on_stdout);
+ rstream_start(&channel->stream.socket, on_socket_output, channel);
+ }
+
+end:
+ channel_create_event(channel, address);
+ return channel->id;
+}
+
+/// Creates an RPC channel from a tcp/pipe socket connection
+///
+/// @param watcher The SocketWatcher ready to accept the connection
+void channel_from_connection(SocketWatcher *watcher)
+{
+ Channel *channel = channel_alloc(kChannelStreamSocket);
+ socket_watcher_accept(watcher, &channel->stream.socket);
+ channel->stream.socket.internal_close_cb = close_cb;
+ channel->stream.socket.internal_data = channel;
+ wstream_init(&channel->stream.socket, 0);
+ rstream_init(&channel->stream.socket, 0);
+ rpc_start(channel);
+ channel_create_event(channel, watcher->addr);
+}
+
+/// Creates an API channel from stdin/stdout. This is used when embedding
+/// Neovim
+uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
+ const char **error)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!headless_mode) {
+ *error = _("can only be opened in headless mode");
+ return 0;
+ }
+
+ if (did_stdio) {
+ *error = _("channel was already open");
+ return 0;
+ }
+ did_stdio = true;
+
+ Channel *channel = channel_alloc(kChannelStreamStdio);
+
+ rstream_init_fd(&main_loop, &channel->stream.stdio.in, 0, 0);
+ wstream_init_fd(&main_loop, &channel->stream.stdio.out, 1, 0);
+
+ if (rpc) {
+ rpc_start(channel);
+ } else {
+ channel->on_stdout = on_output;
+ callback_reader_start(&channel->on_stdout);
+ rstream_start(&channel->stream.stdio.in, on_stdio_input, channel);
+ }
+
+ return channel->id;
+}
+
+/// @param data will be consumed
+size_t channel_send(uint64_t id, char *data, size_t len, const char **error)
+{
+ Channel *chan = find_channel(id);
+ if (!chan) {
+ EMSG(_(e_invchan));
+ goto err;
+ }
+
+ if (chan->streamtype == kChannelStreamStderr) {
+ if (chan->stream.err.closed) {
+ *error = _("Can't send data to closed stream");
+ goto err;
+ }
+ // unbuffered write
+ size_t written = fwrite(data, len, 1, stderr);
+ xfree(data);
+ return len * written;
+ }
+
+
+ Stream *in = channel_instream(chan);
+ if (in->closed) {
+ *error = _("Can't send data to closed stream");
+ goto err;
+ }
+
+ if (chan->is_rpc) {
+ *error = _("Can't send raw data to rpc channel");
+ goto err;
+ }
+
+ WBuffer *buf = wstream_new_buffer(data, len, 1, xfree);
+ return wstream_write(in, buf) ? len : 0;
+
+err:
+ xfree(data);
+ return 0;
+}
+
+/// Convert binary byte array to a readfile()-style list
+///
+/// @param[in] buf Array to convert.
+/// @param[in] len Array length.
+///
+/// @return [allocated] Converted list.
+static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
+ // Empty buffer should be represented by [''], encode_list_write() thinks
+ // empty list is fine for the case.
+ tv_list_append_string(l, "", 0);
+ encode_list_write(l, buf, len);
+ return l;
+}
+
+// vimscript job callbacks must be executed on Nvim main loop
+static inline void process_channel_event(Channel *chan, Callback *callback,
+ const char *type, char *buf,
+ size_t count, int status)
+{
+ assert(callback);
+ ChannelEvent *event_data = xmalloc(sizeof(*event_data));
+ event_data->received = NULL;
+ if (buf) {
+ event_data->received = buffer_to_tv_list(buf, count);
+ } else {
+ event_data->status = status;
+ }
+ channel_incref(chan); // Hold on ref to callback
+ event_data->chan = chan;
+ event_data->callback = callback;
+ event_data->type = type;
+
+ multiqueue_put(chan->events, on_channel_event, 1, event_data);
+}
+
+void on_job_stdout(Stream *stream, RBuffer *buf, size_t count,
+ void *data, bool eof)
+{
+ Channel *chan = data;
+ on_channel_output(stream, chan, buf, count, eof, &chan->on_stdout, "stdout");
+}
+
+void on_job_stderr(Stream *stream, RBuffer *buf, size_t count,
+ void *data, bool eof)
+{
+ Channel *chan = data;
+ on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr, "stderr");
+}
+
+static void on_socket_output(Stream *stream, RBuffer *buf, size_t count,
+ void *data, bool eof)
+{
+ Channel *chan = data;
+ on_channel_output(stream, chan, buf, count, eof, &chan->on_stdout, "data");
+}
+
+static void on_stdio_input(Stream *stream, RBuffer *buf, size_t count,
+ void *data, bool eof)
+{
+ Channel *chan = data;
+ on_channel_output(stream, chan, buf, count, eof, &chan->on_stdout, "stdin");
+}
+
+/// @param type must have static lifetime
+static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf,
+ size_t count, bool eof, CallbackReader *reader,
+ const char *type)
+{
+ // stub variable, to keep reading consistent with the order of events, only
+ // consider the count parameter.
+ size_t r;
+ char *ptr = rbuffer_read_ptr(buf, &r);
+
+ if (eof) {
+ if (reader->buffered) {
+ if (reader->cb.type != kCallbackNone) {
+ process_channel_event(chan, &reader->cb, type, reader->buffer.ga_data,
+ (size_t)reader->buffer.ga_len, 0);
+ } else if (reader->self) {
+ if (tv_dict_find(reader->self, type, -1) == NULL) {
+ list_T *data = buffer_to_tv_list(reader->buffer.ga_data,
+ (size_t)reader->buffer.ga_len);
+ tv_dict_add_list(reader->self, type, strlen(type), data);
+ } else {
+ // can't display error message now, defer it.
+ channel_incref(chan);
+ multiqueue_put(chan->events, on_buffered_error, 2, chan, type);
+ }
+ } else {
+ abort();
+ }
+ ga_clear(&reader->buffer);
+ } else if (reader->cb.type != kCallbackNone) {
+ process_channel_event(chan, &reader->cb, type, ptr, 0, 0);
+ }
+ return;
+ }
+
+ // The order here matters, the terminal must receive the data first because
+ // process_channel_event will modify the read buffer(convert NULs into NLs)
+ if (chan->term) {
+ terminal_receive(chan->term, ptr, count);
+ }
+
+ rbuffer_consumed(buf, count);
+ if (reader->buffered) {
+ ga_concat_len(&reader->buffer, ptr, count);
+ } else if (callback_reader_set(*reader)) {
+ process_channel_event(chan, &reader->cb, type, ptr, count, 0);
+ }
+}
+
+static void on_buffered_error(void **args)
+{
+ Channel *chan = (Channel *)args[0];
+ const char *stream = (const char *)args[1];
+ EMSG3(_(e_streamkey), stream, chan->id);
+ channel_decref(chan);
+}
+
+static void channel_process_exit_cb(Process *proc, int status, void *data)
+{
+ Channel *chan = data;
+ if (chan->term) {
+ char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
+ snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status);
+ terminal_close(chan->term, msg);
+ }
+
+ // if status is -1 the process did not really exit,
+ // we just closed the handle onto a detached process
+ if (status >= 0) {
+ process_channel_event(chan, &chan->on_exit, "exit", NULL, 0, status);
+ }
+
+ channel_decref(chan);
+}
+
+static void on_channel_event(void **args)
+{
+ ChannelEvent *ev = (ChannelEvent *)args[0];
+
+ typval_T argv[4];
+
+ argv[0].v_type = VAR_NUMBER;
+ argv[0].v_lock = VAR_UNLOCKED;
+ argv[0].vval.v_number = (varnumber_T)ev->chan->id;
+
+ if (ev->received) {
+ argv[1].v_type = VAR_LIST;
+ argv[1].v_lock = VAR_UNLOCKED;
+ argv[1].vval.v_list = ev->received;
+ tv_list_ref(argv[1].vval.v_list);
+ } else {
+ argv[1].v_type = VAR_NUMBER;
+ argv[1].v_lock = VAR_UNLOCKED;
+ argv[1].vval.v_number = ev->status;
+ }
+
+ argv[2].v_type = VAR_STRING;
+ argv[2].v_lock = VAR_UNLOCKED;
+ argv[2].vval.v_string = (uint8_t *)ev->type;
+
+ typval_T rettv = TV_INITIAL_VALUE;
+ callback_call(ev->callback, 3, argv, &rettv);
+ tv_clear(&rettv);
+ channel_decref(ev->chan);
+ xfree(ev);
+}
+
+
+/// Open terminal for channel
+///
+/// Channel `chan` is assumed to be an open pty channel,
+/// and curbuf is assumed to be a new, unmodified buffer.
+void channel_terminal_open(Channel *chan)
+{
+ TerminalOptions topts;
+ topts.data = chan;
+ topts.width = chan->stream.pty.width;
+ topts.height = chan->stream.pty.height;
+ topts.write_cb = term_write;
+ topts.resize_cb = term_resize;
+ topts.close_cb = term_close;
+ curbuf->b_p_channel = (long)chan->id; // 'channel' option
+ Terminal *term = terminal_open(topts);
+ chan->term = term;
+ channel_incref(chan);
+}
+
+static void term_write(char *buf, size_t size, void *data)
+{
+ Channel *chan = data;
+ if (chan->stream.proc.in.closed) {
+ // If the backing stream was closed abruptly, there may be write events
+ // ahead of the terminal close event. Just ignore the writes.
+ ILOG("write failed: stream is closed");
+ return;
+ }
+ WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree);
+ wstream_write(&chan->stream.proc.in, wbuf);
+}
+
+static void term_resize(uint16_t width, uint16_t height, void *data)
+{
+ Channel *chan = data;
+ pty_process_resize(&chan->stream.pty, width, height);
+}
+
+static inline void term_delayed_free(void **argv)
+{
+ Channel *chan = argv[0];
+ if (chan->stream.proc.in.pending_reqs || chan->stream.proc.out.pending_reqs) {
+ multiqueue_put(chan->events, term_delayed_free, 1, chan);
+ return;
+ }
+
+ terminal_destroy(chan->term);
+ chan->term = NULL;
+ channel_decref(chan);
+}
+
+static void term_close(void *data)
+{
+ Channel *chan = data;
+ process_stop(&chan->stream.proc);
+ multiqueue_put(chan->events, term_delayed_free, 1, data);
+}
+
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
new file mode 100644
index 0000000000..b856d197f1
--- /dev/null
+++ b/src/nvim/channel.h
@@ -0,0 +1,134 @@
+#ifndef NVIM_CHANNEL_H
+#define NVIM_CHANNEL_H
+
+#include "nvim/main.h"
+#include "nvim/event/socket.h"
+#include "nvim/event/process.h"
+#include "nvim/os/pty_process.h"
+#include "nvim/event/libuv_process.h"
+#include "nvim/eval/typval.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+
+#define CHAN_STDIO 1
+#define CHAN_STDERR 2
+
+typedef enum {
+ kChannelStreamProc,
+ kChannelStreamSocket,
+ kChannelStreamStdio,
+ kChannelStreamStderr,
+ kChannelStreamInternal
+} ChannelStreamType;
+
+typedef enum {
+ kChannelPartStdin,
+ kChannelPartStdout,
+ kChannelPartStderr,
+ kChannelPartRpc,
+ kChannelPartAll
+} ChannelPart;
+
+
+typedef struct {
+ Stream in;
+ Stream out;
+} StdioPair;
+
+typedef struct {
+ bool closed;
+} StderrState;
+
+typedef struct {
+ Callback cb;
+ dict_T *self;
+ garray_T buffer;
+ bool buffered;
+} CallbackReader;
+
+#define CALLBACK_READER_INIT ((CallbackReader){ .cb = CALLBACK_NONE, \
+ .self = NULL, \
+ .buffer = GA_EMPTY_INIT_VALUE, \
+ .buffered = false })
+static inline bool callback_reader_set(CallbackReader reader)
+{
+ return reader.cb.type != kCallbackNone || reader.self;
+}
+
+struct Channel {
+ uint64_t id;
+ size_t refcount;
+ MultiQueue *events;
+
+ ChannelStreamType streamtype;
+ union {
+ Process proc;
+ LibuvProcess uv;
+ PtyProcess pty;
+ Stream socket;
+ StdioPair stdio;
+ StderrState err;
+ } stream;
+
+ bool is_rpc;
+ RpcState rpc;
+ Terminal *term;
+
+ CallbackReader on_stdout;
+ CallbackReader on_stderr;
+ Callback on_exit;
+};
+
+EXTERN PMap(uint64_t) *channels;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "channel.h.generated.h"
+#endif
+
+/// @returns Channel with the id or NULL if not found
+static inline Channel *find_channel(uint64_t id)
+{
+ return pmap_get(uint64_t)(channels, id);
+}
+
+static inline Stream *channel_instream(Channel *chan)
+ FUNC_ATTR_NONNULL_ALL
+{
+ switch (chan->streamtype) {
+ case kChannelStreamProc:
+ return &chan->stream.proc.in;
+
+ case kChannelStreamSocket:
+ return &chan->stream.socket;
+
+ case kChannelStreamStdio:
+ return &chan->stream.stdio.out;
+
+ case kChannelStreamInternal:
+ case kChannelStreamStderr:
+ abort();
+ }
+ abort();
+}
+
+static inline Stream *channel_outstream(Channel *chan)
+ FUNC_ATTR_NONNULL_ALL
+{
+ switch (chan->streamtype) {
+ case kChannelStreamProc:
+ return &chan->stream.proc.out;
+
+ case kChannelStreamSocket:
+ return &chan->stream.socket;
+
+ case kChannelStreamStdio:
+ return &chan->stream.stdio.in;
+
+ case kChannelStreamInternal:
+ case kChannelStreamStderr:
+ abort();
+ }
+ abort();
+}
+
+
+#endif // NVIM_CHANNEL_H
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 48db1030a6..980b4ed426 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -762,7 +762,7 @@ bool vim_isIDc(int c)
}
/// Check that "c" is a keyword character:
-/// Letters and characters from 'iskeyword' option for current buffer.
+/// Letters and characters from 'iskeyword' option for the current buffer.
/// For multi-byte characters mb_get_class() is used (builtin rules).
///
/// @param c character to check
@@ -981,10 +981,8 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
mb_ptr_adv(s);
c = *s;
- if (!((c != NUL)
- && (vim_isbreak(c)
- || (!vim_isbreak(c)
- && ((col2 == col) || !vim_isbreak(*ps)))))) {
+ if (!(c != NUL
+ && (vim_isbreak(c) || col2 == col || !vim_isbreak(*ps)))) {
break;
}
@@ -1613,141 +1611,145 @@ bool vim_isblankline(char_u *lbuf)
/// If maxlen > 0, check at a maximum maxlen chars.
///
/// @param start
-/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
-/// '0' = octal, 'b' or 'B' is bin
+/// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is
+/// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using
+/// STR2NR_FORCE is always zero.
/// @param len Returns the detected length of number.
-/// @param what Recognizes what number passed.
+/// @param what Recognizes what number passed, @see ChStr2NrFlags.
/// @param nptr Returns the signed result.
/// @param unptr Returns the unsigned result.
/// @param maxlen Max length of string to check.
void vim_str2nr(const char_u *const start, int *const prep, int *const len,
const int what, varnumber_T *const nptr,
uvarnumber_T *const unptr, const int maxlen)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- const char_u *ptr = start;
+ const char *ptr = (const char *)start;
+#define STRING_ENDED(ptr) \
+ (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
int pre = 0; // default is decimal
- bool negative = false;
+ const bool negative = (ptr[0] == '-');
uvarnumber_T un = 0;
- if (ptr[0] == '-') {
- negative = true;
+ if (negative) {
ptr++;
}
- // Recognize hex, octal and bin.
- if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')
- && (maxlen == 0 || maxlen > 1)) {
+ if (what & STR2NR_FORCE) {
+ // When forcing main consideration is skipping the prefix. Octal and decimal
+ // numbers have no prefixes to skip. pre is not set.
+ switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) {
+ case STR2NR_HEX: {
+ if (!STRING_ENDED(ptr + 2)
+ && ptr[0] == '0'
+ && (ptr[1] == 'x' || ptr[1] == 'X')
+ && ascii_isxdigit(ptr[2])) {
+ ptr += 2;
+ }
+ goto vim_str2nr_hex;
+ }
+ case STR2NR_BIN: {
+ if (!STRING_ENDED(ptr + 2)
+ && ptr[0] == '0'
+ && (ptr[1] == 'b' || ptr[1] == 'B')
+ && ascii_isbdigit(ptr[2])) {
+ ptr += 2;
+ }
+ goto vim_str2nr_bin;
+ }
+ case STR2NR_OCT: {
+ goto vim_str2nr_oct;
+ }
+ case 0: {
+ goto vim_str2nr_dec;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
+ && !STRING_ENDED(ptr + 1)
+ && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
pre = ptr[1];
-
+ // Detect hexadecimal: 0x or 0X follwed by hex digit
if ((what & STR2NR_HEX)
- && ((pre == 'X') || (pre == 'x'))
- && ascii_isxdigit(ptr[2])
- && (maxlen == 0 || maxlen > 2)) {
- // hexadecimal
+ && !STRING_ENDED(ptr + 2)
+ && (pre == 'X' || pre == 'x')
+ && ascii_isxdigit(ptr[2])) {
ptr += 2;
- } else if ((what & STR2NR_BIN)
- && ((pre == 'B') || (pre == 'b'))
- && ascii_isbdigit(ptr[2])
- && (maxlen == 0 || maxlen > 2)) {
- // binary
+ goto vim_str2nr_hex;
+ }
+ // Detect binary: 0b or 0B follwed by 0 or 1
+ if ((what & STR2NR_BIN)
+ && !STRING_ENDED(ptr + 2)
+ && (pre == 'B' || pre == 'b')
+ && ascii_isbdigit(ptr[2])) {
ptr += 2;
- } else {
- // decimal or octal, default is decimal
- pre = 0;
-
- if (what & STR2NR_OCT) {
- // Don't interpret "0", "08" or "0129" as octal.
- for (int n = 1; ascii_isdigit(ptr[n]); ++n) {
- if (ptr[n] > '7') {
- // can't be octal
- pre = 0;
- break;
- }
- if (ptr[n] >= '0') {
- // assume octal
- pre = '0';
- }
- if (n == maxlen) {
- break;
- }
- }
+ goto vim_str2nr_bin;
+ }
+ // Detect octal number: zero followed by octal digits without '8' or '9'
+ pre = 0;
+ if (!(what & STR2NR_OCT)
+ || !('0' <= ptr[1] && ptr[1] <= '7')) {
+ goto vim_str2nr_dec;
+ }
+ for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
+ if (ptr[i] > '7') {
+ goto vim_str2nr_dec;
}
}
+ pre = '0';
+ goto vim_str2nr_oct;
+ } else {
+ goto vim_str2nr_dec;
}
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
- int n = 1;
- if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) {
- // bin
- if (pre != 0) {
- n += 2; // skip over "0b"
+ assert(false); // Should’ve used goto earlier.
+#define PARSE_NUMBER(base, cond, conv) \
+ do { \
+ while (!STRING_ENDED(ptr) && (cond)) { \
+ /* avoid ubsan error for overflow */ \
+ if (un < UVARNUMBER_MAX / base) { \
+ un = base * un + (uvarnumber_T)(conv); \
+ } else { \
+ un = UVARNUMBER_MAX; \
+ } \
+ ptr++; \
+ } \
+ } while (0)
+ switch (pre) {
+ case 'b':
+ case 'B': {
+vim_str2nr_bin:
+ PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
+ break;
}
- while ('0' <= *ptr && *ptr <= '1') {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 2) {
- un = 2 * un + (uvarnumber_T)(*ptr - '0');
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
- }
- } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) {
- // octal
- while ('0' <= *ptr && *ptr <= '7') {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 8) {
- un = 8 * un + (uvarnumber_T)(*ptr - '0');
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
+ case '0': {
+vim_str2nr_oct:
+ PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
+ break;
}
- } else if ((pre == 'X') || (pre == 'x')
- || what == STR2NR_HEX + STR2NR_FORCE) {
- // hex
- if (pre != 0) {
- n += 2; // skip over "0x"
- }
- while (ascii_isxdigit(*ptr)) {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 16) {
- un = 16 * un + (uvarnumber_T)hex2nr(*ptr);
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
+ case 0: {
+vim_str2nr_dec:
+ PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
+ break;
}
- } else {
- // decimal
- while (ascii_isdigit(*ptr)) {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 10) {
- un = 10 * un + (uvarnumber_T)(*ptr - '0');
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
+ case 'x':
+ case 'X': {
+vim_str2nr_hex:
+ PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
+ break;
}
}
+#undef PARSE_NUMBER
if (prep != NULL) {
*prep = pre;
}
if (len != NULL) {
- *len = (int)(ptr - start);
+ *len = (int)(ptr - (const char *)start);
}
if (nptr != NULL) {
@@ -1769,6 +1771,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
if (unptr != NULL) {
*unptr = un;
}
+#undef STRING_ENDED
}
/// Return the value of a single hex character.
diff --git a/src/nvim/charset.h b/src/nvim/charset.h
index c69582c4c6..eb64b6128a 100644
--- a/src/nvim/charset.h
+++ b/src/nvim/charset.h
@@ -4,6 +4,7 @@
#include "nvim/types.h"
#include "nvim/pos.h"
#include "nvim/buffer_defs.h"
+#include "nvim/eval/typval.h"
/// Return the folded-case equivalent of the given character
///
@@ -15,6 +16,21 @@
?((int)(uint8_t)(c)) \
:((int)(c)))
+/// Flags for vim_str2nr()
+typedef enum {
+ STR2NR_DEC = 0,
+ STR2NR_BIN = (1 << 0), ///< Allow binary numbers.
+ STR2NR_OCT = (1 << 1), ///< Allow octal numbers.
+ STR2NR_HEX = (1 << 2), ///< Allow hexadecimal numbers.
+ /// Force one of the above variants.
+ ///
+ /// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero
+ /// as flags, but still present for completeness.
+ STR2NR_FORCE = (1 << 3),
+ /// Recognize all formats vim_str2nr() can recognize.
+ STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX,
+} ChStr2NrFlags;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "charset.h.generated.h"
#endif
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 60002f3cea..0e97e2203f 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -375,17 +375,30 @@ void check_cursor_col_win(win_T *win)
win->w_cursor.col = 0;
}
- /* If virtual editing is on, we can leave the cursor on the old position,
- * only we must set it to virtual. But don't do it when at the end of the
- * line. */
- if (oldcol == MAXCOL)
+ // If virtual editing is on, we can leave the cursor on the old position,
+ // only we must set it to virtual. But don't do it when at the end of the
+ // line.
+ if (oldcol == MAXCOL) {
win->w_cursor.coladd = 0;
- else if (ve_flags == VE_ALL) {
- if (oldcoladd > win->w_cursor.col)
+ } else if (ve_flags == VE_ALL) {
+ if (oldcoladd > win->w_cursor.col) {
win->w_cursor.coladd = oldcoladd - win->w_cursor.col;
- else
- /* avoid weird number when there is a miscalculation or overflow */
+
+ // Make sure that coladd is not more than the char width.
+ // Not for the last character, coladd is then used when the cursor
+ // is actually after the last character.
+ if (win->w_cursor.col + 1 < len && win->w_cursor.coladd > 0) {
+ int cs, ce;
+
+ getvcol(win, &win->w_cursor, &cs, NULL, &ce);
+ if (win->w_cursor.coladd > ce - cs) {
+ win->w_cursor.coladd = ce - cs;
+ }
+ }
+ } else {
+ // avoid weird number when there is a miscalculation or overflow
win->w_cursor.coladd = 0;
+ }
}
}
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 97fc3a3ca3..b45e7002f7 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -102,11 +102,14 @@ char_u *parse_shape_opt(int what)
}
while (*modep != NUL) {
colonp = vim_strchr(modep, ':');
- if (colonp == NULL)
+ commap = vim_strchr(modep, ',');
+
+ if (colonp == NULL || (commap != NULL && commap < colonp)) {
return (char_u *)N_("E545: Missing colon");
- if (colonp == modep)
+ }
+ if (colonp == modep) {
return (char_u *)N_("E546: Illegal mode");
- commap = vim_strchr(modep, ',');
+ }
// Repeat for all modes before the colon.
// For the 'a' mode, we loop to handle all the modes.
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 7da6665cb7..0ee1c3815d 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -135,6 +135,20 @@ void diff_buf_add(buf_T *buf)
EMSGN(_("E96: Cannot diff more than %" PRId64 " buffers"), DB_COUNT);
}
+///
+/// Remove all buffers to make diffs for.
+///
+static void diff_buf_clear(void)
+{
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ curtab->tp_diffbuf[i] = NULL;
+ curtab->tp_diff_invalid = true;
+ diff_redraw(true);
+ }
+ }
+}
+
/// Find buffer "buf" in the list of diff buffers for the current tab page.
///
/// @param buf The buffer to find.
@@ -841,12 +855,13 @@ void ex_diffpatch(exarg_T *eap)
{
char_u *buf = NULL;
win_T *old_curwin = curwin;
- char_u *newname = NULL; // name of patched file buffer
+ char_u *newname = NULL; // name of patched file buffer
+ char_u *esc_name = NULL;
#ifdef UNIX
char_u dirbuf[MAXPATHL];
char_u *fullname = NULL;
-#endif // ifdef UNIX
+#endif
// We need two temp file names.
// Name of original temp file.
char_u *tmp_orig = vim_tempname();
@@ -866,21 +881,21 @@ void ex_diffpatch(exarg_T *eap)
#ifdef UNIX
// Get the absolute path of the patchfile, changing directory below.
- fullname = (char_u *)FullName_save((char *)eap->arg, FALSE);
-#endif // ifdef UNIX
+ fullname = (char_u *)FullName_save((char *)eap->arg, false);
+#endif
+ esc_name = vim_strsave_shellescape(
#ifdef UNIX
- size_t buflen = STRLEN(tmp_orig)
- + (fullname != NULL ? STRLEN(fullname) : STRLEN(eap->arg))
- + STRLEN(tmp_new) + 16;
-#else
- size_t buflen = STRLEN(tmp_orig) + (STRLEN(eap->arg)) + STRLEN(tmp_new) + 16;
-#endif // ifdef UNIX
-
+ fullname != NULL ? fullname :
+#endif
+ eap->arg, true, true);
+ if (esc_name == NULL) {
+ goto theend;
+ }
+ size_t buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16;
buf = xmalloc(buflen);
#ifdef UNIX
-
// Temporarily chdir to /tmp, to avoid patching files in the current
// directory when the patch file contains more than one patch. When we
// have our own temp dir use that instead, it will be cleaned up when we
@@ -897,26 +912,21 @@ void ex_diffpatch(exarg_T *eap)
os_chdir(tempdir);
shorten_fnames(TRUE);
}
-#endif // ifdef UNIX
+#endif
if (*p_pex != NUL) {
// Use 'patchexpr' to generate the new file.
#ifdef UNIX
- eval_patch((char *) tmp_orig,
- (char *) (fullname != NULL ? fullname : eap->arg),
- (char *) tmp_new);
+ eval_patch((char *)tmp_orig,
+ (char *)(fullname != NULL ? fullname : eap->arg),
+ (char *)tmp_new);
#else
- eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new);
-#endif // ifdef UNIX
+ eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new);
+#endif
} else {
// Build the patch command and execute it. Ignore errors.
-#ifdef UNIX
- vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"",
- tmp_new, tmp_orig, fullname != NULL ? fullname : eap->arg);
-#else
- vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"",
- tmp_new, tmp_orig, eap->arg);
-#endif // ifdef UNIX
+ vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s",
+ tmp_new, tmp_orig, esc_name);
block_autocmds(); // Avoid ShellCmdPost stuff
(void)call_shell(buf, kShellOptFilter, NULL);
unblock_autocmds();
@@ -929,10 +939,7 @@ void ex_diffpatch(exarg_T *eap)
}
shorten_fnames(TRUE);
}
-#endif // ifdef UNIX
-
- // patch probably has written over the screen
- redraw_later(CLEAR);
+#endif
// Delete any .orig or .rej file created.
STRCPY(buf, tmp_new);
@@ -998,7 +1005,8 @@ theend:
xfree(buf);
#ifdef UNIX
xfree(fullname);
-#endif // ifdef UNIX
+#endif
+ xfree(esc_name);
}
/// Split the window and edit another file, setting options to show the diffs.
@@ -1033,10 +1041,7 @@ void ex_diffsplit(exarg_T *eap)
if (bufref_valid(&old_curbuf)) {
// Move the cursor position to that of the old window.
curwin->w_cursor.lnum = diff_get_corresponding_line(
- old_curbuf.br_buf,
- old_curwin->w_cursor.lnum,
- curbuf,
- curwin->w_cursor.lnum);
+ old_curbuf.br_buf, old_curwin->w_cursor.lnum);
}
}
// Now that lines are folded scroll to show the cursor at the same
@@ -1053,6 +1058,20 @@ void ex_diffthis(exarg_T *eap)
diff_win_options(curwin, TRUE);
}
+static void set_diff_option(win_T *wp, int value)
+{
+ win_T *old_curwin = curwin;
+
+ curwin = wp;
+ curbuf = curwin->w_buffer;
+ curbuf_lock++;
+ set_option_value("diff", (long)value, NULL, OPT_LOCAL);
+ curbuf_lock--;
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+}
+
+
/// Set options in window "wp" for diff mode.
///
/// @param addbuf Add buffer to diff.
@@ -1110,10 +1129,10 @@ void diff_win_options(win_T *wp, int addbuf)
do_cmdline_cmd("set sbo+=hor");
}
- // Saved the current values, to be restored in ex_diffoff().
- wp->w_p_diff_saved = TRUE;
+ // Save the current values, to be restored in ex_diffoff().
+ wp->w_p_diff_saved = true;
- wp->w_p_diff = true;
+ set_diff_option(wp, true);
if (addbuf) {
diff_buf_add(wp->w_buffer);
@@ -1134,7 +1153,7 @@ void ex_diffoff(exarg_T *eap)
// Set 'diff' off. If option values were saved in
// diff_win_options(), restore the ones whose settings seem to have
// been left over from diff mode.
- wp->w_p_diff = false;
+ set_diff_option(wp, false);
if (wp->w_p_diff_saved) {
if (wp->w_p_scb) {
@@ -1150,7 +1169,9 @@ void ex_diffoff(exarg_T *eap)
}
free_string_option(wp->w_p_fdm);
- wp->w_p_fdm = vim_strsave(wp->w_p_fdm_save);
+ wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save
+ ? wp->w_p_fdm_save
+ : (char_u *)"manual");
if (wp->w_p_fdc == diff_foldcolumn) {
wp->w_p_fdc = wp->w_p_fdc_save;
}
@@ -1178,6 +1199,11 @@ void ex_diffoff(exarg_T *eap)
diffwin |= wp->w_p_diff;
}
+ // Also remove hidden buffers from the list.
+ if (eap->forceit) {
+ diff_buf_clear();
+ }
+
// Remove "hor" from from 'scrollopt' if there are no diff windows left.
if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) {
do_cmdline_cmd("set sbo-=hor");
@@ -2255,7 +2281,7 @@ void ex_diffgetput(exarg_T *eap)
}
}
- buf_empty = bufempty();
+ buf_empty = BUFEMPTY();
added = 0;
for (i = 0; i < count; ++i) {
@@ -2463,25 +2489,17 @@ int diff_move_to(int dir, long count)
return OK;
}
-/// Finds the corresponding line in a diff.
-///
-/// @param buf1
-/// @param lnum1
-/// @param buf2
-/// @param lnum3
-///
-/// @return The corresponding line.
-linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2,
- linenr_T lnum3)
+/// Return the line number in the current window that is closest to "lnum1" in
+/// "buf1" in diff mode.
+static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1)
{
int idx1;
int idx2;
diff_T *dp;
int baseline = 0;
- linenr_T lnum2;
idx1 = diff_buf_idx(buf1);
- idx2 = diff_buf_idx(buf2);
+ idx2 = diff_buf_idx(curbuf);
if ((idx1 == DB_COUNT)
|| (idx2 == DB_COUNT)
@@ -2501,15 +2519,9 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2,
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (dp->df_lnum[idx1] > lnum1) {
- lnum2 = lnum1 - baseline;
-
- // don't end up past the end of the file
- if (lnum2 > buf2->b_ml.ml_line_count) {
- lnum2 = buf2->b_ml.ml_line_count;
- }
-
- return lnum2;
- } else if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) {
+ return lnum1 - baseline;
+ }
+ if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) {
// Inside the diffblock
baseline = lnum1 - dp->df_lnum[idx1];
@@ -2518,30 +2530,42 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2,
}
return dp->df_lnum[idx2] + baseline;
- } else if ((dp->df_lnum[idx1] == lnum1)
- && (dp->df_count[idx1] == 0)
- && (dp->df_lnum[idx2] <= lnum3)
- && ((dp->df_lnum[idx2] + dp->df_count[idx2]) > lnum3)) {
+ }
+ if ((dp->df_lnum[idx1] == lnum1)
+ && (dp->df_count[idx1] == 0)
+ && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum)
+ && ((dp->df_lnum[idx2] + dp->df_count[idx2])
+ > curwin->w_cursor.lnum)) {
// Special case: if the cursor is just after a zero-count
// block (i.e. all filler) and the target cursor is already
// inside the corresponding block, leave the target cursor
// unmoved. This makes repeated CTRL-W W operations work
// as expected.
- return lnum3;
+ return curwin->w_cursor.lnum;
}
baseline = (dp->df_lnum[idx1] + dp->df_count[idx1])
- - (dp->df_lnum[idx2] + dp->df_count[idx2]);
+ - (dp->df_lnum[idx2] + dp->df_count[idx2]);
}
// If we get here then the cursor is after the last diff
- lnum2 = lnum1 - baseline;
+ return lnum1 - baseline;
+}
+
+/// Finds the corresponding line in a diff.
+///
+/// @param buf1
+/// @param lnum1
+///
+/// @return The corresponding line.
+linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1)
+{
+ linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1);
// don't end up past the end of the file
- if (lnum2 > buf2->b_ml.ml_line_count) {
- lnum2 = buf2->b_ml.ml_line_count;
+ if (lnum > curbuf->b_ml.ml_line_count) {
+ return curbuf->b_ml.ml_line_count;
}
-
- return lnum2;
+ return lnum;
}
/// For line "lnum" in the current window find the equivalent lnum in window
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 32e71f61f7..bc4c12e0b7 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -793,6 +793,7 @@ static digr_T digraphdefault[] =
{ '/', '-', 0x2020 },
{ '/', '=', 0x2021 },
{ '.', '.', 0x2025 },
+ { ',', '.', 0x2026 },
{ '%', '0', 0x2030 },
{ '1', '\'', 0x2032 },
{ '2', '\'', 0x2033 },
@@ -1695,7 +1696,7 @@ static void printdigraph(digr_T *dp)
}
}
- p = buf;
+ p = &buf[0];
*p++ = dp->char1;
*p++ = dp->char2;
*p++ = ' ';
@@ -1840,6 +1841,16 @@ void ex_loadkeymap(exarg_T *eap)
status_redraw_curbuf();
}
+/// Frees the buf_T.b_kmap_ga field of a buffer.
+void keymap_ga_clear(garray_T *kmap_ga)
+{
+ kmap_T *kp = (kmap_T *)kmap_ga->ga_data;
+ for (int i = 0; i < kmap_ga->ga_len; i++) {
+ xfree(kp[i].from);
+ xfree(kp[i].to);
+ }
+}
+
/// Stop using 'keymap'.
static void keymap_unload(void)
{
@@ -1857,12 +1868,11 @@ static void keymap_unload(void)
// clear the ":lmap"s
kp = (kmap_T *)curbuf->b_kmap_ga.ga_data;
- for (int i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) {
+ for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s", kp[i].from);
- (void)do_map(1, buf, LANGMAP, FALSE);
- xfree(kp[i].from);
- xfree(kp[i].to);
+ (void)do_map(1, buf, LANGMAP, false);
}
+ keymap_ga_clear(&curbuf->b_kmap_ga);
p_cpo = save_cpo;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 08a2f42f74..b772a944f4 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -462,7 +462,7 @@ static void insert_enter(InsertState *s)
// Always update o_lnum, so that a "CTRL-O ." that adds a line
// still puts the cursor back after the inserted text.
- if (ins_at_eol && gchar_cursor() == NUL) {
+ if (ins_at_eol) {
o_lnum = curwin->w_cursor.lnum;
}
@@ -974,12 +974,8 @@ static int insert_handle_key(InsertState *s)
multiqueue_process_events(main_loop.events);
break;
- case K_FOCUSGAINED: // Neovim has been given focus
- apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
- break;
-
- case K_FOCUSLOST: // Neovim has lost focus
- apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
+ case K_COMMAND: // some command
+ do_cmdline(NULL, getcmdkeycmd, NULL, 0);
break;
case K_HOME: // <Home>
@@ -1935,7 +1931,7 @@ bool vim_is_ctrl_x_key(int c)
case CTRL_X_EVAL:
return (c == Ctrl_P || c == Ctrl_N);
}
- EMSG(_(e_internal));
+ internal_error("vim_is_ctrl_x_key()");
return false;
}
@@ -2406,6 +2402,7 @@ void set_completion(colnr_T startcol, list_T *list)
ins_compl_prep(' ');
}
ins_compl_clear();
+ ins_compl_free();
compl_direction = FORWARD;
if (startcol > curwin->w_cursor.col)
@@ -3166,8 +3163,7 @@ static bool ins_compl_prep(int c)
/* Ignore end of Select mode mapping and mouse scroll buttons. */
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
- || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT
- || c == K_FOCUSGAINED || c == K_FOCUSLOST) {
+ || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT) {
return retval;
}
@@ -3544,19 +3540,19 @@ theend:
/*
* Add completions from a list.
*/
-static void ins_compl_add_list(list_T *list)
+static void ins_compl_add_list(list_T *const list)
{
- listitem_T *li;
int dir = compl_direction;
- /* Go through the List with matches and add each of them. */
- for (li = list->lv_first; li != NULL; li = li->li_next) {
- if (ins_compl_add_tv(&li->li_tv, dir) == OK)
- /* if dir was BACKWARD then honor it just once */
+ // Go through the List with matches and add each of them.
+ TV_LIST_ITER(list, li, {
+ if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir) == OK) {
+ // If dir was BACKWARD then honor it just once.
dir = FORWARD;
- else if (did_emsg)
+ } else if (did_emsg) {
break;
- }
+ }
+ });
}
/*
@@ -3608,6 +3604,8 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true);
cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true);
cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true);
+ cptext[CPT_USER_DATA] = tv_dict_get_string(tv->vval.v_dict,
+ "user_data", true);
icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase");
adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup");
@@ -4051,8 +4049,9 @@ static void ins_compl_insert(int in_compl_func)
// Set completed item.
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc();
- tv_dict_add_str(dict, S_LEN("word"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str));
+ tv_dict_add_str(
+ dict, S_LEN("word"),
+ (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str));
tv_dict_add_str(
dict, S_LEN("abbr"),
(const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR]));
@@ -4065,6 +4064,9 @@ static void ins_compl_insert(int in_compl_func)
tv_dict_add_str(
dict, S_LEN("info"),
(const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO]));
+ tv_dict_add_str(
+ dict, S_LEN("user_data"),
+ (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_USER_DATA]));
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
if (!in_compl_func) {
compl_curr_match = compl_shown_match;
@@ -4689,7 +4691,7 @@ static int ins_complete(int c, bool enable_pum)
line = ml_get(curwin->w_cursor.lnum);
compl_pattern = vim_strnsave(line + compl_col, compl_length);
} else {
- EMSG2(_(e_intern2), "ins_complete()");
+ internal_error("ins_complete()");
return FAIL;
}
@@ -5243,28 +5245,27 @@ insertchar (
buf[0] = c;
i = 1;
- if (textwidth > 0)
+ if (textwidth > 0) {
virtcol = get_nolist_virtcol();
- /*
- * Stop the string when:
- * - no more chars available
- * - finding a special character (command key)
- * - buffer is full
- * - running into the 'textwidth' boundary
- * - need to check for abbreviation: A non-word char after a word-char
- */
- while ( (c = vpeekc()) != NUL
- && !ISSPECIAL(c)
- && (!has_mbyte || MB_BYTE2LEN_CHECK(c) == 1)
- && i < INPUT_BUFLEN
- && (textwidth == 0
- || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth)
- && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) {
+ }
+ // Stop the string when:
+ // - no more chars available
+ // - finding a special character (command key)
+ // - buffer is full
+ // - running into the 'textwidth' boundary
+ // - need to check for abbreviation: A non-word char after a word-char
+ while ((c = vpeekc()) != NUL
+ && !ISSPECIAL(c)
+ && (!has_mbyte || MB_BYTE2LEN_CHECK(c) == 1)
+ && i < INPUT_BUFLEN
+ && !(p_fkmap && KeyTyped) // Farsi mode mapping moves cursor
+ && (textwidth == 0
+ || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth)
+ && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) {
c = vgetc();
- if (p_hkmap && KeyTyped)
- c = hkmap(c); /* Hebrew mode mapping */
- if (p_fkmap && KeyTyped)
- c = fkmap(c); /* Farsi mode mapping */
+ if (p_hkmap && KeyTyped) {
+ c = hkmap(c); // Hebrew mode mapping
+ }
buf[i++] = c;
}
@@ -6074,27 +6075,30 @@ void free_last_insert(void)
#endif
-/*
- * Add character "c" to buffer "s". Escape the special meaning of K_SPECIAL
- * and CSI. Handle multi-byte characters.
- * Returns a pointer to after the added bytes.
- */
+/// Add character "c" to buffer "s"
+///
+/// Escapes the special meaning of K_SPECIAL and CSI, handles multi-byte
+/// characters.
+///
+/// @param[in] c Character to add.
+/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
+///
+/// @return Pointer to after the added bytes.
char_u *add_char2buf(int c, char_u *s)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
char_u temp[MB_MAXBYTES + 1];
- int i;
- int len;
-
- len = (*mb_char2bytes)(c, temp);
- for (i = 0; i < len; ++i) {
+ const int len = utf_char2bytes(c, temp);
+ for (int i = 0; i < len; i++) {
c = temp[i];
- /* Need to escape K_SPECIAL and CSI like in the typeahead buffer. */
+ // Need to escape K_SPECIAL and CSI like in the typeahead buffer.
if (c == K_SPECIAL) {
*s++ = K_SPECIAL;
*s++ = KS_SPECIAL;
*s++ = KE_FILLER;
- } else
+ } else {
*s++ = c;
+ }
}
return s;
}
@@ -6912,7 +6916,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
if (try_match && *look == keytyped) {
return true;
}
- look++;
+ if (*look != NUL) {
+ look++;
+ }
}
/*
@@ -7478,13 +7484,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
int oldState;
int cpc[MAX_MCO]; /* composing characters */
- /*
- * can't delete anything in an empty file
- * can't backup past first character in buffer
- * can't backup past starting point unless 'backspace' > 1
- * can backup to a previous line if 'backspace' == 0
- */
- if (bufempty()
+ // can't delete anything in an empty file
+ // can't backup past first character in buffer
+ // can't backup past starting point unless 'backspace' > 1
+ // can backup to a previous line if 'backspace' == 0
+ if (BUFEMPTY()
|| (!revins_on
&& ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0)
|| (!can_bs(BS_START)
diff --git a/src/nvim/edit.h b/src/nvim/edit.h
index 0d61f26bcc..433a941295 100644
--- a/src/nvim/edit.h
+++ b/src/nvim/edit.h
@@ -6,11 +6,12 @@
/*
* Array indexes used for cptext argument of ins_compl_add().
*/
-#define CPT_ABBR 0 /* "abbr" */
-#define CPT_MENU 1 /* "menu" */
-#define CPT_KIND 2 /* "kind" */
-#define CPT_INFO 3 /* "info" */
-#define CPT_COUNT 4 /* Number of entries */
+#define CPT_ABBR 0 // "abbr"
+#define CPT_MENU 1 // "menu"
+#define CPT_KIND 2 // "kind"
+#define CPT_INFO 3 // "info"
+#define CPT_USER_DATA 4 // "user data"
+#define CPT_COUNT 5 // Number of entries
typedef int (*IndentGetter)(void);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 18aa5bf763..0f7a1eb004 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -24,6 +24,7 @@
#endif
#include "nvim/eval.h"
#include "nvim/buffer.h"
+#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
@@ -47,6 +48,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
#include "nvim/keymap.h"
@@ -108,9 +110,6 @@
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
-#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not
- be freed. */
-
#define AUTOLOAD_CHAR '#' /* Character used as separator in autoload
function/variable names. */
@@ -216,6 +215,15 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL};
static int echo_attr = 0; /* attributes used for ":echo" */
+/// Describe data to return from find_some_match()
+typedef enum {
+ kSomeMatch, ///< Data for match().
+ kSomeMatchEnd, ///< Data for matchend().
+ kSomeMatchList, ///< Data for matchlist().
+ kSomeMatchStr, ///< Data for matchstr().
+ kSomeMatchStrPos, ///< Data for matchstrpos().
+} SomeMatchType;
+
/// trans_function_name() flags
typedef enum {
TFN_INT = 1, ///< May use internal function name
@@ -333,7 +341,7 @@ static struct vimvar {
// VV_SEND_SERVER "servername"
// VV_REG "register"
// VV_OP "operator"
- VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_COUNT, "count", VAR_NUMBER, VV_RO),
VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT),
@@ -364,6 +372,7 @@ static struct vimvar {
VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
+ VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_REG, "register", VAR_STRING, VV_RO),
VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
@@ -393,7 +402,6 @@ static struct vimvar {
VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
- VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0),
VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO),
VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
@@ -436,31 +444,6 @@ static ScopeDictDictItem vimvars_var;
#define vimvarht vimvardict.dv_hashtab
typedef struct {
- union {
- LibuvProcess uv;
- PtyProcess pty;
- } proc;
- Stream in, out, err; // Initialized in common_job_start().
- Terminal *term;
- bool stopped;
- bool exited;
- bool rpc;
- int refcount;
- Callback on_stdout, on_stderr, on_exit;
- varnumber_T *status_ptr;
- uint64_t id;
- MultiQueue *events;
-} TerminalJobData;
-
-typedef struct {
- TerminalJobData *data;
- Callback *callback;
- const char *type;
- list_T *received;
- int status;
-} JobEvent;
-
-typedef struct {
TimeWatcher tw;
int timer_id;
int repeat_count;
@@ -512,7 +495,6 @@ typedef enum {
#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */
#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
valid character */
-static PMap(uint64_t) *jobs = NULL;
static uint64_t last_timer_id = 0;
static PMap(uint64_t) *timers = NULL;
@@ -555,7 +537,6 @@ void eval_init(void)
{
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
- jobs = pmap_new(uint64_t)();
timers = pmap_new(uint64_t)();
struct vimvar *p;
@@ -587,9 +568,9 @@ void eval_init(void)
dict_T *const msgpack_types_dict = tv_dict_alloc();
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
- list_T *const type_list = tv_list_alloc();
- type_list->lv_lock = VAR_FIXED;
- type_list->lv_refcount = 1;
+ list_T *const type_list = tv_list_alloc(0);
+ tv_list_set_lock(type_list, VAR_FIXED);
+ tv_list_ref(type_list);
dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);
di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;
di->di_tv = (typval_T) {
@@ -610,7 +591,8 @@ void eval_init(void)
dict_T *v_event = tv_dict_alloc();
v_event->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_EVENT, v_event);
- set_vim_var_list(VV_ERRORS, tv_list_alloc());
+ set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));
+ set_vim_var_nr(VV_STDERR, CHAN_STDERR);
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
set_vim_var_nr(VV_COUNT1, 1);
@@ -1037,7 +1019,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert)
ga_init(&ga, (int)sizeof(char), 80);
if (tv.vval.v_list != NULL) {
tv_list_join(&ga, tv.vval.v_list, "\n");
- if (tv.vval.v_list->lv_len > 0) {
+ if (tv_list_len(tv.vval.v_list) > 0) {
ga_append(&ga, NL);
}
}
@@ -1125,10 +1107,11 @@ static void restore_vimvar(int idx, typval_T *save_tv)
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
- if (HASHITEM_EMPTY(hi))
- EMSG2(_(e_intern2), "restore_vimvar()");
- else
+ if (HASHITEM_EMPTY(hi)) {
+ internal_error("restore_vimvar()");
+ } else {
hash_remove(&vimvarht, hi);
+ }
}
}
@@ -1166,27 +1149,31 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr)
return list;
}
-/*
- * "list" is supposed to contain two items: a word and a number. Return the
- * word in "pp" and the number as the return value.
- * Return -1 if anything isn't right.
- * Used to get the good word and score from the eval_spell_expr() result.
- */
-int get_spellword(list_T *list, const char **pp)
+/// Get spell word from an entry from spellsuggest=expr:
+///
+/// Entry in question is supposed to be a list (to be checked by the caller)
+/// with two items: a word and a score represented as an unsigned number
+/// (whether it actually is unsigned is not checked).
+///
+/// Used to get the good word and score from the eval_spell_expr() result.
+///
+/// @param[in] list List to get values from.
+/// @param[out] ret_word Suggested word. Not initialized if return value is
+/// -1.
+///
+/// @return -1 in case of error, score otherwise.
+int get_spellword(list_T *const list, const char **ret_word)
{
- listitem_T *li;
-
- li = list->lv_first;
- if (li == NULL) {
+ if (tv_list_len(list) != 2) {
+ EMSG(_("E5700: Expression from 'spellsuggest' must yield lists with "
+ "exactly two values"));
return -1;
}
- *pp = tv_get_string(&li->li_tv);
-
- li = li->li_next;
- if (li == NULL) {
+ *ret_word = tv_list_find_str(list, 0);
+ if (*ret_word == NULL) {
return -1;
}
- return tv_get_number(&li->li_tv);
+ return tv_list_find_nr(list, -1, NULL);
}
@@ -1292,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc,
char *call_func_retstr(const char *const func, const int argc,
const char_u *const *const argv,
const bool safe)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
typval_T rettv;
// All arguments are passed as strings, no conversion to number.
@@ -1527,9 +1514,6 @@ ex_let_vars (
)
{
char_u *arg = arg_start;
- list_T *l;
- int i;
- listitem_T *item;
typval_T ltv;
if (*arg != '[') {
@@ -1541,46 +1525,52 @@ ex_let_vars (
return OK;
}
- /*
- * ":let [v1, v2] = list" or ":for [v1, v2] in listlist"
- */
- if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL) {
+ // ":let [v1, v2] = list" or ":for [v1, v2] in listlist"
+ if (tv->v_type != VAR_LIST) {
EMSG(_(e_listreq));
return FAIL;
}
+ list_T *const l = tv->vval.v_list;
- i = tv_list_len(l);
- if (semicolon == 0 && var_count < i) {
+ const int len = tv_list_len(l);
+ if (semicolon == 0 && var_count < len) {
EMSG(_("E687: Less targets than List items"));
return FAIL;
}
- if (var_count - semicolon > i) {
+ if (var_count - semicolon > len) {
EMSG(_("E688: More targets than List items"));
return FAIL;
}
+ // List l may actually be NULL, but it should fail with E688 or even earlier
+ // if you try to do ":let [] = v:_null_list".
+ assert(l != NULL);
- item = l->lv_first;
+ listitem_T *item = tv_list_first(l);
+ size_t rest_len = tv_list_len(l);
while (*arg != ']') {
arg = skipwhite(arg + 1);
- arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars);
- item = item->li_next;
- if (arg == NULL)
+ arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]",
+ nextchars);
+ if (arg == NULL) {
return FAIL;
+ }
+ rest_len--;
+ item = TV_LIST_ITEM_NEXT(l, item);
arg = skipwhite(arg);
if (*arg == ';') {
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
- l = tv_list_alloc();
+ list_T *const rest_list = tv_list_alloc(rest_len);
while (item != NULL) {
- tv_list_append_tv(l, &item->li_tv);
- item = item->li_next;
+ tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item));
+ item = TV_LIST_ITEM_NEXT(l, item);
}
ltv.v_type = VAR_LIST;
- ltv.v_lock = 0;
- ltv.vval.v_list = l;
- l->lv_refcount = 1;
+ ltv.v_lock = VAR_UNLOCKED;
+ ltv.vval.v_list = rest_list;
+ tv_list_ref(rest_list);
arg = ex_let_one(skipwhite(arg + 1), &ltv, false,
(char_u *)"]", nextchars);
@@ -1590,7 +1580,7 @@ ex_let_vars (
}
break;
} else if (*arg != ',' && *arg != ']') {
- EMSG2(_(e_intern2), "ex_let_vars()");
+ internal_error("ex_let_vars()");
return FAIL;
}
}
@@ -2095,6 +2085,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
* Loop until no more [idx] or .key is following.
*/
lp->ll_tv = &v->di_tv;
+ var1.v_type = VAR_UNKNOWN;
+ var2.v_type = VAR_UNKNOWN;
while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
&& !(lp->ll_tv->v_type == VAR_DICT
@@ -2145,9 +2137,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (!quiet) {
EMSG(_(e_dictrange));
}
- if (!empty1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
return NULL;
}
if (rettv != NULL && (rettv->v_type != VAR_LIST
@@ -2155,9 +2145,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (!quiet) {
emsgf(_("E709: [:] requires a List value"));
}
- if (!empty1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
return NULL;
}
p = skipwhite(p + 1);
@@ -2166,16 +2154,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
} else {
lp->ll_empty2 = false;
if (eval1(&p, &var2, true) == FAIL) { // Recursive!
- if (!empty1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
return NULL;
}
if (!tv_check_str(&var2)) {
// Not a number or string.
- if (!empty1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
tv_clear(&var2);
return NULL;
}
@@ -2188,12 +2172,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (!quiet) {
emsgf(_(e_missbrac));
}
- if (!empty1) {
- tv_clear(&var1);
- }
- if (lp->ll_range && !lp->ll_empty2) {
- tv_clear(&var2);
- }
+ tv_clear(&var1);
+ tv_clear(&var2);
return NULL;
}
@@ -2205,10 +2185,6 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (len == -1) {
// "[key]": get key from "var1"
key = (char_u *)tv_get_string(&var1); // is number or string
- if (key == NULL) {
- tv_clear(&var1);
- return NULL;
- }
}
lp->ll_list = NULL;
lp->ll_dict = lp->ll_tv->vval.v_dict;
@@ -2251,9 +2227,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (!quiet) {
emsgf(_(e_dictkey), key);
}
- if (len == -1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
return NULL;
}
if (len == -1) {
@@ -2261,32 +2235,28 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
} else {
lp->ll_newkey = vim_strnsave(key, len);
}
- if (len == -1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
break;
// existing variable, need to check if it can be changed
} else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags,
(const char *)name,
(size_t)(p - name))) {
- if (len == -1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
return NULL;
}
- if (len == -1) {
- tv_clear(&var1);
- }
+ tv_clear(&var1);
lp->ll_tv = &lp->ll_di->di_tv;
} else {
// Get the number and item for the only or first index of the List.
if (empty1) {
lp->ll_n1 = 0;
} else {
- lp->ll_n1 = (long)tv_get_number(&var1); // Is number or string.
- tv_clear(&var1);
+ // Is number or string.
+ lp->ll_n1 = (long)tv_get_number(&var1);
}
+ tv_clear(&var1);
+
lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list;
lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1);
@@ -2297,9 +2267,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
}
}
if (lp->ll_li == NULL) {
- if (lp->ll_range && !lp->ll_empty2) {
- tv_clear(&var2);
- }
+ tv_clear(&var2);
if (!quiet) {
EMSGN(_(e_listidx), lp->ll_n1);
}
@@ -2337,10 +2305,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
}
}
- lp->ll_tv = &lp->ll_li->li_tv;
+ lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li);
}
}
+ tv_clear(&var1);
return p;
}
@@ -2402,47 +2371,55 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
int ll_n1 = lp->ll_n1;
// Check whether any of the list items is locked
- for (listitem_T *ri = rettv->vval.v_list->lv_first;
+ for (listitem_T *ri = tv_list_first(rettv->vval.v_list);
ri != NULL && ll_li != NULL; ) {
- if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
+ if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock,
+ (const char *)lp->ll_name,
TV_CSTRING)) {
return;
}
- ri = ri->li_next;
+ ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) {
break;
}
- ll_li = ll_li->li_next;
+ ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li);
ll_n1++;
}
/*
* Assign the List values to the list items.
*/
- for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) {
+ for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) {
if (op != NULL && *op != '=') {
- eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op);
+ eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri),
+ (const char *)op);
} else {
- tv_clear(&lp->ll_li->li_tv);
- tv_copy(&ri->li_tv, &lp->ll_li->li_tv);
+ tv_clear(TV_LIST_ITEM_TV(lp->ll_li));
+ tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li));
}
- ri = ri->li_next;
- if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
+ ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);
+ if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) {
break;
- if (lp->ll_li->li_next == NULL) {
+ }
+ assert(lp->ll_li != NULL);
+ if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) {
// Need to add an empty item.
tv_list_append_number(lp->ll_list, 0);
- assert(lp->ll_li->li_next);
+ // ll_li may have become invalid after append, don’t use it.
+ lp->ll_li = tv_list_last(lp->ll_list); // Valid again.
+ } else {
+ lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
}
- lp->ll_li = lp->ll_li->li_next;
- ++lp->ll_n1;
+ lp->ll_n1++;
}
- if (ri != NULL)
+ if (ri != NULL) {
EMSG(_("E710: List value has more items than target"));
- else if (lp->ll_empty2
- ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
- : lp->ll_n1 != lp->ll_n2)
+ } else if (lp->ll_empty2
+ ? (lp->ll_li != NULL
+ && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL)
+ : lp->ll_n1 != lp->ll_n2) {
EMSG(_("E711: List value has not enough items"));
+ }
} else {
typval_T oldtv = TV_INITIAL_VALUE;
dict_T *dict = lp->ll_dict;
@@ -2487,9 +2464,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
notify:
if (watched) {
if (oldtv.v_type == VAR_UNKNOWN) {
+ assert(lp->ll_newkey != NULL);
tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL);
} else {
dictitem_T *di = lp->ll_di;
+ assert(di->di_key != NULL);
tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv);
tv_clear(&oldtv);
}
@@ -2541,7 +2520,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
* list being used in "tv". */
fi->fi_list = l;
tv_list_watch_add(l, &fi->fi_lw);
- fi->fi_lw.lw_item = l->lv_first;
+ fi->fi_lw.lw_item = tv_list_first(l);
}
}
}
@@ -2559,21 +2538,18 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
* Return TRUE when a valid item was found, FALSE when at end of list or
* something wrong.
*/
-int next_for_item(void *fi_void, char_u *arg)
+bool next_for_item(void *fi_void, char_u *arg)
{
- forinfo_T *fi = (forinfo_T *)fi_void;
- int result;
- listitem_T *item;
+ forinfo_T *fi = (forinfo_T *)fi_void;
- item = fi->fi_lw.lw_item;
- if (item == NULL)
- result = FALSE;
- else {
- fi->fi_lw.lw_item = item->li_next;
- result = (ex_let_vars(arg, &item->li_tv, TRUE,
- fi->fi_semicolon, fi->fi_varcount, NULL) == OK);
+ listitem_T *item = fi->fi_lw.lw_item;
+ if (item == NULL) {
+ return false;
+ } else {
+ fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item);
+ return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true,
+ fi->fi_semicolon, fi->fi_varcount, NULL) == OK);
}
- return result;
}
// TODO(ZyX-I): move to eval/ex_cmds
@@ -2897,7 +2873,10 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
}
*name_end = cc;
} else if ((lp->ll_list != NULL
- && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name,
+ // ll_list is not NULL when lvalue is not in a list, NULL lists
+ // yield E689.
+ && tv_check_lock(tv_list_locked(lp->ll_list),
+ (const char *)lp->ll_name,
lp->ll_name_len))
|| (lp->ll_dict != NULL
&& tv_check_lock(lp->ll_dict->dv_lock,
@@ -2905,27 +2884,26 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
lp->ll_name_len))) {
return FAIL;
} else if (lp->ll_range) {
- listitem_T *li;
- listitem_T *ll_li = lp->ll_li;
- int ll_n1 = lp->ll_n1;
-
- while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) {
- li = ll_li->li_next;
- if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
+ assert(lp->ll_list != NULL);
+ // Delete a range of List items.
+ listitem_T *const first_li = lp->ll_li;
+ listitem_T *last_li = first_li;
+ for (;;) {
+ listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
+ if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
+ (const char *)lp->ll_name,
lp->ll_name_len)) {
return false;
}
- ll_li = li;
- ll_n1++;
- }
-
- /* Delete a range of List items. */
- while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
- li = lp->ll_li->li_next;
- tv_list_item_remove(lp->ll_list, lp->ll_li);
lp->ll_li = li;
- ++lp->ll_n1;
+ lp->ll_n1++;
+ if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) {
+ break;
+ } else {
+ last_li = lp->ll_li;
+ }
}
+ tv_list_remove_items(lp->ll_list, first_li, last_li);
} else {
if (lp->ll_list != NULL) {
// unlet a List item.
@@ -2933,6 +2911,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
} else {
// unlet a Dictionary item.
dict_T *d = lp->ll_dict;
+ assert(d != NULL);
dictitem_T *di = lp->ll_di;
bool watched = tv_dict_is_watched(d);
char *key = NULL;
@@ -2987,7 +2966,7 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit)
d = di->di_tv.vval.v_dict;
}
if (d == NULL) {
- EMSG2(_(e_intern2), "do_unlet()");
+ internal_error("do_unlet()");
return FAIL;
}
hashitem_T *hi = hash_find(ht, (const char_u *)varname);
@@ -3070,13 +3049,13 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep,
/* (un)lock a range of List items. */
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
- tv_item_lock(&li->li_tv, deep, lock);
- li = li->li_next;
- ++lp->ll_n1;
+ tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock);
+ li = TV_LIST_ITEM_NEXT(lp->ll_list, li);
+ lp->ll_n1++;
}
} else if (lp->ll_list != NULL) {
// (un)lock a List item.
- tv_item_lock(&lp->ll_li->li_tv, deep, lock);
+ tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock);
} else {
// (un)lock a Dictionary item.
tv_item_lock(&lp->ll_di->di_tv, deep, lock);
@@ -4239,11 +4218,17 @@ static int eval7(
// use its contents.
s = deref_func_name((const char *)s, &len, &partial, !evaluate);
+ // Need to make a copy, in case evaluating the arguments makes
+ // the name invalid.
+ s = xmemdupz(s, len);
+
// Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, partial, NULL);
+ xfree(s);
+
// If evaluate is false rettv->v_type was not set in
// get_func_tv, but it's needed in handle_subscript() to parse
// what follows. So set it here.
@@ -4529,18 +4514,18 @@ eval_index (
if (!empty2 && (n2 < 0 || n2 + 1 < n1)) {
n2 = -1;
}
- l = tv_list_alloc();
+ l = tv_list_alloc(n2 - n1 + 1);
item = tv_list_find(rettv->vval.v_list, n1);
while (n1++ <= n2) {
- tv_list_append_tv(l, &item->li_tv);
- item = item->li_next;
+ tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
+ item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);
}
tv_clear(rettv);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = l;
- l->lv_refcount++;
+ tv_list_ref(l);
} else {
- tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1);
+ tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, n1)), &var1);
tv_clear(rettv);
*rettv = var1;
}
@@ -4885,35 +4870,34 @@ void partial_unref(partial_T *pt)
static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
{
list_T *l = NULL;
- typval_T tv;
- listitem_T *item;
if (evaluate) {
- l = tv_list_alloc();
+ l = tv_list_alloc(kListLenShouldKnow);
}
*arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL) {
- if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
+ typval_T tv;
+ if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
goto failret;
+ }
if (evaluate) {
- item = tv_list_item_alloc();
- item->li_tv = tv;
- item->li_tv.v_lock = 0;
- tv_list_append(l, item);
+ tv.v_lock = VAR_UNLOCKED;
+ tv_list_append_owned_tv(l, tv);
}
- if (**arg == ']')
+ if (**arg == ']') {
break;
+ }
if (**arg != ',') {
- EMSG2(_("E696: Missing comma in List: %s"), *arg);
+ emsgf(_("E696: Missing comma in List: %s"), *arg);
goto failret;
}
*arg = skipwhite(*arg + 1);
}
if (**arg != ']') {
- EMSG2(_("E697: Missing end of List ']': %s"), *arg);
+ emsgf(_("E697: Missing end of List ']': %s"), *arg);
failret:
if (evaluate) {
tv_list_free(l);
@@ -4925,7 +4909,7 @@ failret:
if (evaluate) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = l;
- ++l->lv_refcount;
+ tv_list_ref(l);
}
return OK;
@@ -5136,12 +5120,12 @@ bool garbage_collect(bool testing)
// named functions (matters for closures)
ABORTING(set_ref_in_functions(copyID));
- // Jobs
+ // Channels
{
- TerminalJobData *data;
- map_foreach_value(jobs, data, {
- set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL);
- set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL);
+ Channel *data;
+ map_foreach_value(channels, data, {
+ set_ref_in_callback_reader(&data->on_stdout, copyID, NULL, NULL);
+ set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL);
set_ref_in_callback(&data->on_exit, copyID, NULL, NULL);
})
}
@@ -5193,6 +5177,8 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_list)(sub.additional_elements, copyID);
}
+ ABORTING(set_ref_in_quickfix)(copyID);
+
bool did_free = false;
if (!abort) {
// 2. Free lists and dictionaries that are not referenced.
@@ -5260,8 +5246,8 @@ static int free_unref_items(int copyID)
// But don't free a list that has a watcher (used in a for loop), these
// are not referenced anywhere.
for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) {
- if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
- && ll->lv_watch == NULL) {
+ if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK)
+ && !tv_list_has_watchers(ll)) {
// Free the List and ordinary items it contains, but don't recurse
// into Lists and Dictionaries, they will be in the list of dicts
// or list of lists.
@@ -5281,7 +5267,7 @@ static int free_unref_items(int copyID)
for (ll = gc_first_list; ll != NULL; ll = ll_next) {
ll_next = ll->lv_used_next;
if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
- && ll->lv_watch == NULL) {
+ && !tv_list_has_watchers(ll)) {
// Free the List and ordinary items it contains, but don't recurse
// into Lists and Dictionaries, they will be in the list of dicts
// or list of lists.
@@ -5346,15 +5332,16 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack)
list_T *cur_l = l;
for (;;) {
- if (!abort) {
- // Mark each item in the list. If the item contains a hashtab
- // it is added to ht_stack, if it contains a list it is added to
- // list_stack.
- for (listitem_T *li = cur_l->lv_first; !abort && li != NULL;
- li = li->li_next) {
- abort = set_ref_in_item(&li->li_tv, copyID, ht_stack, &list_stack);
+ // Mark each item in the list. If the item contains a hashtab
+ // it is added to ht_stack, if it contains a list it is added to
+ // list_stack.
+ TV_LIST_ITER(cur_l, li, {
+ if (abort) {
+ break;
}
- }
+ abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack,
+ &list_stack);
+ });
if (list_stack == NULL) {
break;
@@ -5699,10 +5686,6 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
c = *p;
*p = NUL;
arg = vim_strsave(arg);
- if (arg == NULL) {
- *p = c;
- goto err_ret;
- }
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; i++) {
@@ -5826,10 +5809,6 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
fp = (ufunc_T *)xcalloc(1, sizeof(ufunc_T) + STRLEN(name));
pt = (partial_T *)xcalloc(1, sizeof(partial_T));
- if (pt == NULL) {
- xfree(fp);
- goto errret;
- }
ga_init(&newlines, (int)sizeof(char_u *), 1);
ga_grow(&newlines, 1);
@@ -5957,6 +5936,14 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
}
#ifdef INCLUDE_GENERATED_DECLARATIONS
+
+#ifdef _MSC_VER
+// This prevents MSVC from replacing the functions with intrinsics,
+// and causing errors when trying to get their addresses in funcs.generated.h
+#pragma function (ceil)
+#pragma function (floor)
+#endif
+
# include "funcs.generated.h"
#endif
@@ -6215,13 +6202,9 @@ static char_u *fname_trans_sid(const char_u *const name,
fname = fname_buf;
} else {
fname = xmalloc(i + STRLEN(name + llen) + 1);
- if (fname == NULL) {
- *error = ERROR_OTHER;
- } else {
- *tofree = fname;
- memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
- }
+ *tofree = fname;
+ memmove(fname, fname_buf, (size_t)i);
+ STRCPY(fname + i, name + llen);
}
} else {
fname = (char_u *)name;
@@ -6302,9 +6285,6 @@ call_func(
// Make a copy of the name, if it comes from a funcref variable it could
// be changed or deleted in the called function.
name = vim_strnsave(funcname, len);
- if (name == NULL) {
- return ret;
- }
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
@@ -6566,12 +6546,10 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- list_T *l;
-
- rettv->vval.v_number = 1; /* Default: Failed */
+ rettv->vval.v_number = 1; // Default: failed.
if (argvars[0].v_type == VAR_LIST) {
- if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, "add() argument", TV_TRANSLATE)) {
+ list_T *const l = argvars[0].vval.v_list;
+ if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) {
tv_list_append_tv(l, &argvars[1]);
tv_copy(&argvars[0], rettv);
}
@@ -6622,9 +6600,10 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& u_save(lnum, lnum + 1) == OK) {
if (argvars[1].v_type == VAR_LIST) {
l = argvars[1].vval.v_list;
- if (l == NULL)
+ if (l == NULL) {
return;
- li = l->lv_first;
+ }
+ li = tv_list_first(l);
}
for (;; ) {
if (l == NULL) {
@@ -6632,7 +6611,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (li == NULL) {
break; // End of list.
} else {
- tv = &li->li_tv; // Append item from list.
+ tv = TV_LIST_ITEM_TV(li); // Append item from list.
}
const char *const line = tv_get_string_chk(tv);
if (line == NULL) { // Type error.
@@ -6644,7 +6623,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (l == NULL) {
break;
}
- li = li->li_next;
+ li = TV_LIST_ITEM_NEXT(l, li);
}
appended_lines_mark(lnum, added);
@@ -6697,7 +6676,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->v_type = VAR_STRING;
} else {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, ARGCOUNT);
for (idx = 0; idx < ARGCOUNT; idx++) {
tv_list_append_string(rettv->vval.v_list,
(const char *)alist_name(&ARGLIST[idx]), -1);
@@ -6726,6 +6705,39 @@ static void prepare_assert_error(garray_T *gap)
}
}
+// Append "str" to "gap", escaping unprintable characters.
+// Changes NL to \n, CR to \r, etc.
+static void ga_concat_esc(garray_T *gap, char_u *str)
+{
+ char_u *p;
+ char_u buf[NUMBUFLEN];
+
+ if (str == NULL) {
+ ga_concat(gap, (char_u *)"NULL");
+ return;
+ }
+
+ for (p = str; *p != NUL; p++) {
+ switch (*p) {
+ case BS: ga_concat(gap, (char_u *)"\\b"); break;
+ case ESC: ga_concat(gap, (char_u *)"\\e"); break;
+ case FF: ga_concat(gap, (char_u *)"\\f"); break;
+ case NL: ga_concat(gap, (char_u *)"\\n"); break;
+ case TAB: ga_concat(gap, (char_u *)"\\t"); break;
+ case CAR: ga_concat(gap, (char_u *)"\\r"); break;
+ case '\\': ga_concat(gap, (char_u *)"\\\\"); break;
+ default:
+ if (*p < ' ') {
+ vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
+ ga_concat(gap, buf);
+ } else {
+ ga_append(gap, *p);
+ }
+ break;
+ }
+ }
+}
+
// Fill "gap" with information about an assert error.
static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
char_u *exp_str, typval_T *exp_tv,
@@ -6740,28 +6752,30 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
} else {
if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
ga_concat(gap, (char_u *)"Pattern ");
+ } else if (atype == ASSERT_NOTEQUAL) {
+ ga_concat(gap, (char_u *)"Expected not equal to ");
} else {
ga_concat(gap, (char_u *)"Expected ");
}
if (exp_str == NULL) {
- tofree = (char_u *) encode_tv2string(exp_tv, NULL);
- ga_concat(gap, tofree);
+ tofree = (char_u *)encode_tv2string(exp_tv, NULL);
+ ga_concat_esc(gap, tofree);
xfree(tofree);
} else {
- ga_concat(gap, exp_str);
+ ga_concat_esc(gap, exp_str);
}
- tofree = (char_u *)encode_tv2string(got_tv, NULL);
- if (atype == ASSERT_MATCH) {
- ga_concat(gap, (char_u *)" does not match ");
- } else if (atype == ASSERT_NOTMATCH) {
- ga_concat(gap, (char_u *)" does match ");
- } else if (atype == ASSERT_NOTEQUAL) {
- ga_concat(gap, (char_u *)" differs from ");
- } else {
- ga_concat(gap, (char_u *)" but got ");
+ if (atype != ASSERT_NOTEQUAL) {
+ if (atype == ASSERT_MATCH) {
+ ga_concat(gap, (char_u *)" does not match ");
+ } else if (atype == ASSERT_NOTMATCH) {
+ ga_concat(gap, (char_u *)" does match ");
+ } else {
+ ga_concat(gap, (char_u *)" but got ");
+ }
+ tofree = (char_u *)encode_tv2string(got_tv, NULL);
+ ga_concat_esc(gap, tofree);
+ xfree(tofree);
}
- ga_concat(gap, tofree);
- xfree(tofree);
}
}
@@ -6772,7 +6786,7 @@ static void assert_error(garray_T *gap)
if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) {
// Make sure v:errors is a list.
- set_vim_var_list(VV_ERRORS, tv_list_alloc());
+ set_vim_var_list(VV_ERRORS, tv_list_alloc(1));
}
tv_list_append_string(vimvars[VV_ERRORS].vv_list,
(const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
@@ -7142,8 +7156,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& argvars[1].v_type != VAR_UNKNOWN
&& tv_get_number_chk(&argvars[1], &error) != 0
&& !error
- && (name = tv_get_string_chk(&argvars[0])) != NULL
- && !error) {
+ && (name = tv_get_string_chk(&argvars[0])) != NULL) {
buf = buflist_new((char_u *)name, NULL, 1, 0);
}
@@ -7249,30 +7262,26 @@ static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int func_call(char_u *name, typval_T *args, partial_T *partial,
dict_T *selfdict, typval_T *rettv)
{
- listitem_T *item;
typval_T argv[MAX_FUNC_ARGS + 1];
int argc = 0;
int dummy;
int r = 0;
- for (item = args->vval.v_list->lv_first; item != NULL;
- item = item->li_next) {
+ TV_LIST_ITER(args->vval.v_list, item, {
if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) {
EMSG(_("E699: Too many arguments"));
- break;
+ goto func_call_skip_call;
}
- /* Make a copy of each argument. This is needed to be able to set
- * v_lock to VAR_FIXED in the copy without changing the original list.
- */
- tv_copy(&item->li_tv, &argv[argc++]);
- }
+ // Make a copy of each argument. This is needed to be able to set
+ // v_lock to VAR_FIXED in the copy without changing the original list.
+ tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]);
+ });
- if (item == NULL) {
- r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &dummy, true, partial, selfdict);
- }
+ r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &dummy, true, partial, selfdict);
+func_call_skip_call:
// Free the arguments.
while (argc > 0) {
tv_clear(&argv[--argc]);
@@ -7326,6 +7335,76 @@ static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = curbuf->b_u_seq_cur;
}
+// "chanclose(id[, stream])" function
+static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type != VAR_STRING
+ && argvars[1].v_type != VAR_UNKNOWN)) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ ChannelPart part = kChannelPartAll;
+ if (argvars[1].v_type == VAR_STRING) {
+ char *stream = (char *)argvars[1].vval.v_string;
+ if (!strcmp(stream, "stdin")) {
+ part = kChannelPartStdin;
+ } else if (!strcmp(stream, "stdout")) {
+ part = kChannelPartStdout;
+ } else if (!strcmp(stream, "stderr")) {
+ part = kChannelPartStderr;
+ } else if (!strcmp(stream, "rpc")) {
+ part = kChannelPartRpc;
+ } else {
+ EMSG2(_("Invalid channel stream \"%s\""), stream);
+ return;
+ }
+ }
+ const char *error;
+ rettv->vval.v_number = channel_close(argvars[0].vval.v_number, part, &error);
+ if (!rettv->vval.v_number) {
+ EMSG(error);
+ }
+}
+
+// "chansend(id, data)" function
+static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) {
+ // First argument is the channel id and second is the data to write
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ ptrdiff_t input_len = 0;
+ char *input = save_tv_as_string(&argvars[1], &input_len, false);
+ if (!input) {
+ // Either the error has been handled by save_tv_as_string(),
+ // or there is no input to send.
+ return;
+ }
+ uint64_t id = argvars[0].vval.v_number;
+ const char *error = NULL;
+ rettv->vval.v_number = channel_send(id, input, input_len, &error);
+ if (error) {
+ EMSG(error);
+ }
+}
+
/*
* "char2nr(string)" function
*/
@@ -7419,7 +7498,7 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!undo_allowed())
return;
- if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) {
+ if (argvars[1].v_type != VAR_LIST) {
EMSG(_(e_invarg));
return;
}
@@ -7527,7 +7606,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
long idx;
if ((l = argvars[0].vval.v_list) != NULL) {
- li = l->lv_first;
+ li = tv_list_first(l);
if (argvars[2].v_type != VAR_UNKNOWN) {
bool error = false;
@@ -7545,9 +7624,11 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
li = NULL;
}
- for (; li != NULL; li = li->li_next)
- if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
- ++n;
+ for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) {
+ n++;
+ }
+ }
}
} else if (argvars[0].v_type == VAR_DICT) {
int todo;
@@ -7691,7 +7772,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
const char *const name = tv_get_string(&argvars[0]);
- if (name == NULL || *name == NUL) {
+ if (*name == NUL) {
EMSG(_(e_invarg));
return;
}
@@ -7865,34 +7946,51 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool n = true;
switch (argvars[0].v_type) {
- case VAR_STRING:
- case VAR_FUNC:
- n = argvars[0].vval.v_string == NULL
- || *argvars[0].vval.v_string == NUL;
- break;
- case VAR_PARTIAL:
- n = false;
- break;
- case VAR_NUMBER:
- n = argvars[0].vval.v_number == 0;
- break;
- case VAR_FLOAT:
- n = argvars[0].vval.v_float == 0.0;
- break;
- case VAR_LIST:
- n = argvars[0].vval.v_list == NULL
- || argvars[0].vval.v_list->lv_first == NULL;
- break;
- case VAR_DICT:
- n = argvars[0].vval.v_dict == NULL
- || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
- break;
- case VAR_SPECIAL:
- n = argvars[0].vval.v_special != kSpecialVarTrue;
- break;
- case VAR_UNKNOWN:
- EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
- break;
+ case VAR_STRING:
+ case VAR_FUNC: {
+ n = argvars[0].vval.v_string == NULL
+ || *argvars[0].vval.v_string == NUL;
+ break;
+ }
+ case VAR_PARTIAL: {
+ n = false;
+ break;
+ }
+ case VAR_NUMBER: {
+ n = argvars[0].vval.v_number == 0;
+ break;
+ }
+ case VAR_FLOAT: {
+ n = argvars[0].vval.v_float == 0.0;
+ break;
+ }
+ case VAR_LIST: {
+ n = (tv_list_len(argvars[0].vval.v_list) == 0);
+ break;
+ }
+ case VAR_DICT: {
+ n = (tv_dict_len(argvars[0].vval.v_dict) == 0);
+ break;
+ }
+ case VAR_SPECIAL: {
+ // Using switch to get warning if SpecialVarValue receives more values.
+ switch (argvars[0].vval.v_special) {
+ case kSpecialVarTrue: {
+ n = false;
+ break;
+ }
+ case kSpecialVarFalse:
+ case kSpecialVarNull: {
+ n = true;
+ break;
+ }
+ }
+ break;
+ }
+ case VAR_UNKNOWN: {
+ internal_error("f_empty(UNKNOWN)");
+ break;
+ }
}
rettv->vval.v_number = n;
@@ -7956,17 +8054,22 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& os_can_exe((const char_u *)name, NULL, false)));
}
+typedef struct {
+ const list_T *const l;
+ const listitem_T *li;
+} GetListLineCookie;
+
static char_u *get_list_line(int c, void *cookie, int indent)
{
- const listitem_T **const p = (const listitem_T **)cookie;
- const listitem_T *item = *p;
+ GetListLineCookie *const p = (GetListLineCookie *)cookie;
+ const listitem_T *const item = p->li;
if (item == NULL) {
return NULL;
}
char buf[NUMBUFLEN];
- const char *const s = tv_get_string_buf_chk(&item->li_tv, buf);
- *p = item->li_next;
+ const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf);
+ p->li = TV_LIST_ITEM_NEXT(p->l, item);
return (char_u *)(s == NULL ? NULL : xstrdup(s));
}
@@ -8008,11 +8111,14 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
do_cmdline_cmd(tv_get_string(&argvars[0]));
} else if (argvars[0].vval.v_list != NULL) {
list_T *const list = argvars[0].vval.v_list;
- list->lv_refcount++;
- listitem_T *const item = list->lv_first;
- do_cmdline(NULL, get_list_line, (void *)&item,
+ tv_list_ref(list);
+ GetListLineCookie cookie = {
+ .l = list,
+ .li = tv_list_first(list),
+ };
+ do_cmdline(NULL, get_list_line, (void *)&cookie,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED);
- list->lv_refcount--;
+ tv_list_unref(list);
}
msg_silent = save_msg_silent;
emsg_silent = save_emsg_silent;
@@ -8020,8 +8126,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ga_append(capture_ga, NUL);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
- ga_clear(capture_ga);
+ rettv->vval.v_string = capture_ga->ga_data;
capture_ga = save_capture_ga;
}
@@ -8130,7 +8235,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);
emsg_off--;
if (rettv->v_type == VAR_LIST) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);
}
@@ -8153,8 +8258,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options,
WILD_ALL);
} else {
- tv_list_alloc_ret(rettv);
ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list,
(const char *)xpc.xp_files[i], -1);
@@ -8167,6 +8272,19 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+
+/// "menu_get(path [, modes])" function
+static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ int modes = MENU_ALL_MODES;
+ if (argvars[1].v_type == VAR_STRING) {
+ const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]);
+ modes = get_menu_cmd_modes(strmodes, false, NULL, NULL);
+ }
+ menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list);
+}
+
/*
* "extend(list, list [, idx])" function
* "extend(dict, dict [, action])" function
@@ -8181,14 +8299,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *const l1 = argvars[0].vval.v_list;
list_T *const l2 = argvars[1].vval.v_list;
- if (l1 == NULL) {
- const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
- (void)locked;
- assert(locked == true);
- } else if (l2 == NULL) {
- // Do nothing
- tv_copy(&argvars[0], rettv);
- } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) {
+ if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = (long)tv_get_number_chk(&argvars[2], &error);
@@ -8196,7 +8307,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; // Type error; errmsg already given.
}
- if (before == l1->lv_len) {
+ if (before == tv_list_len(l1)) {
item = NULL;
} else {
item = tv_list_find(l1, before);
@@ -8205,8 +8316,9 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
}
- } else
+ } else {
item = NULL;
+ }
tv_list_extend(l1, l2, item);
tv_copy(&argvars[0], rettv);
@@ -8325,7 +8437,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (count < 0) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenUnknown);
}
if (*fname != NUL && !error) {
@@ -8358,7 +8470,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
static void filter_map(typval_T *argvars, typval_T *rettv, int map)
{
typval_T *expr;
- listitem_T *li, *nli;
list_T *l = NULL;
dictitem_T *di;
hashtab_T *ht;
@@ -8376,11 +8487,14 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
int idx = 0;
if (argvars[0].v_type == VAR_LIST) {
+ tv_copy(&argvars[0], rettv);
if ((l = argvars[0].vval.v_list) == NULL
- || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) {
+ || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg,
+ TV_TRANSLATE))) {
return;
}
} else if (argvars[0].v_type == VAR_DICT) {
+ tv_copy(&argvars[0], rettv);
if ((d = argvars[0].vval.v_dict) == NULL
|| (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
@@ -8439,18 +8553,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
} else {
vimvars[VV_KEY].vv_type = VAR_NUMBER;
- for (li = l->lv_first; li != NULL; li = nli) {
+ for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
- && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) {
+ && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
+ TV_TRANSLATE)) {
break;
}
- nli = li->li_next;
vimvars[VV_KEY].vv_nr = idx;
- if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
- || did_emsg)
+ if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL
+ || did_emsg) {
break;
+ }
if (!map && rem) {
- tv_list_item_remove(l, li);
+ li = tv_list_item_remove(l, li);
+ } else {
+ li = TV_LIST_ITEM_NEXT(l, li);
}
idx++;
}
@@ -8461,8 +8578,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
did_emsg |= save_did_emsg;
}
-
- tv_copy(&argvars[0], rettv);
}
static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
@@ -8693,10 +8808,9 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
dashes = get_vim_var_str(VV_FOLDDASHES);
- if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count
- && dashes != NULL) {
- /* Find first non-empty line in the fold. */
- for (lnum = foldstart; lnum < foldend; ++lnum) {
+ if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) {
+ // Find first non-empty line in the fold.
+ for (lnum = foldstart; lnum < foldend; lnum++) {
if (!linewhite(lnum)) {
break;
}
@@ -8819,10 +8933,8 @@ static void common_function(typval_T *argvars, typval_T *rettv,
snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
(int64_t)current_SID);
name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1);
- if (name != NULL) {
- STRCPY(name, sid_buf);
- STRCAT(name, s + off);
- }
+ STRCPY(name, sid_buf);
+ STRCAT(name, s + off);
} else {
name = vim_strsave(s);
}
@@ -8857,7 +8969,7 @@ static void common_function(typval_T *argvars, typval_T *rettv,
goto theend;
}
list = argvars[arg_idx].vval.v_list;
- if (list == NULL || list->lv_len == 0) {
+ if (tv_list_len(list) == 0) {
arg_idx = 0;
}
}
@@ -8868,25 +8980,18 @@ static void common_function(typval_T *argvars, typval_T *rettv,
// result is a VAR_PARTIAL
if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) {
const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc);
- const int lv_len = (list == NULL ? 0 : list->lv_len);
+ const int lv_len = tv_list_len(list);
pt->pt_argc = arg_len + lv_len;
pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc);
- if (pt->pt_argv == NULL) {
- xfree(pt);
- xfree(name);
- goto theend;
- }
int i = 0;
for (; i < arg_len; i++) {
tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
}
if (lv_len > 0) {
- for (listitem_T *li = list->lv_first;
- li != NULL;
- li = li->li_next) {
- tv_copy(&li->li_tv, &pt->pt_argv[i++]);
- }
+ TV_LIST_ITER(list, li, {
+ tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]);
+ });
}
}
@@ -8972,7 +9077,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error));
if (!error && li != NULL) {
- tv = &li->li_tv;
+ tv = TV_LIST_ITEM_TV(li);
}
}
} else if (argvars[0].v_type == VAR_DICT) {
@@ -9013,7 +9118,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (strcmp(what, "args") == 0) {
rettv->v_type = VAR_LIST;
- if (tv_list_alloc_ret(rettv) != NULL) {
+ if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) {
for (int i = 0; i < pt->pt_argc; i++) {
tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
}
@@ -9037,8 +9142,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// Returns information about signs placed in a buffer as list of dicts.
-static void get_buffer_signs(buf_T *buf, list_T *l)
+static list_T *get_buffer_signs(buf_T *buf)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) {
dict_T *const d = tv_dict_alloc();
@@ -9049,6 +9156,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l)
tv_list_append_dict(l, d);
}
+ return l;
}
/// Returns buffer options, variables and other attributes in a dictionary.
@@ -9059,7 +9167,8 @@ static dict_T *get_buffer_info(buf_T *buf)
tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum);
tv_dict_add_str(dict, S_LEN("name"),
buf->b_ffname != NULL ? (const char *)buf->b_ffname : "");
- tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf));
+ tv_dict_add_nr(dict, S_LEN("lnum"),
+ buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf));
tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL);
tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl);
tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf));
@@ -9071,7 +9180,7 @@ static dict_T *get_buffer_info(buf_T *buf)
tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
// List of windows displaying this buffer
- list_T *const windows = tv_list_alloc();
+ list_T *const windows = tv_list_alloc(kListLenMayKnow);
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == buf) {
tv_list_append_number(windows, (varnumber_T)wp->handle);
@@ -9081,9 +9190,7 @@ static dict_T *get_buffer_info(buf_T *buf)
if (buf->b_signlist != NULL) {
// List of signs placed in this buffer
- list_T *const signs = tv_list_alloc();
- get_buffer_signs(buf, signs);
- tv_dict_add_list(dict, S_LEN("signs"), signs);
+ tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
}
return dict;
@@ -9097,7 +9204,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool sel_buflisted = false;
bool sel_bufloaded = false;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
// List of all the buffers or selected buffers
if (argvars[0].v_type == VAR_DICT) {
@@ -9141,9 +9248,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
dict_T *const d = get_buffer_info(buf);
- if (d != NULL) {
- tv_list_append_dict(rettv->vval.v_list, d);
- }
+ tv_list_append_dict(rettv->vval.v_list, d);
if (argbuf != NULL) {
return;
}
@@ -9158,35 +9263,33 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv)
{
- char_u *p;
-
- rettv->v_type = VAR_STRING;
+ rettv->v_type = (retlist ? VAR_LIST : VAR_STRING);
rettv->vval.v_string = NULL;
- if (retlist) {
- tv_list_alloc_ret(rettv);
- }
- if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) {
+ if (retlist) {
+ tv_list_alloc_ret(rettv, 0);
+ }
return;
+ }
- if (!retlist) {
- if (start >= 1 && start <= buf->b_ml.ml_line_count)
- p = ml_get_buf(buf, start, FALSE);
- else
- p = (char_u *)"";
- rettv->vval.v_string = vim_strsave(p);
- } else {
- if (end < start)
- return;
-
- if (start < 1)
+ if (retlist) {
+ if (start < 1) {
start = 1;
- if (end > buf->b_ml.ml_line_count)
+ }
+ if (end > buf->b_ml.ml_line_count) {
end = buf->b_ml.ml_line_count;
+ }
+ tv_list_alloc_ret(rettv, end - start + 1);
while (start <= end) {
tv_list_append_string(rettv->vval.v_list,
(const char *)ml_get_buf(buf, start++, false), -1);
}
+ } else {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
+ ? vim_strsave(ml_get_buf(buf, start, false))
+ : NULL);
}
}
@@ -9511,14 +9614,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
theend:
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
- tv_list_alloc_ret(rettv);
- if (pat != NULL) {
- ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
+ ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
- for (int i = 0; i < xpc.xp_numfiles; i++) {
- tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
- -1);
- }
+ for (int i = 0; i < xpc.xp_numfiles; i++) {
+ tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
+ -1);
}
xfree(pat);
ExpandCleanup(&xpc);
@@ -9808,7 +9909,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const linenr_T lnum = tv_get_lnum(argvars);
if (argvars[1].v_type == VAR_UNKNOWN) {
- end = 0;
+ end = lnum;
retlist = false;
} else {
end = tv_get_lnum(&argvars[1]);
@@ -9822,7 +9923,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
typval_T *rettv)
{
if (what_arg->v_type == VAR_UNKNOWN) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
(void)get_errorlist(wp, -1, rettv->vval.v_list);
}
@@ -9857,7 +9958,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
matchitem_T *cur = curwin->w_match_head;
int i;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
while (cur != NULL) {
dict_T *dict = tv_dict_alloc();
if (cur->match.regprog == NULL) {
@@ -9870,7 +9971,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (llpos->lnum == 0) {
break;
}
- list_T *l = tv_list_alloc();
+ list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0));
tv_list_append_number(l, (varnumber_T)llpos->lnum);
if (llpos->col > 0) {
tv_list_append_number(l, (varnumber_T)llpos->col);
@@ -9919,7 +10020,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
fp = var2fpos(&argvars[0], true, &fnum);
}
- list_T *l = tv_list_alloc_ret(rettv);
+ list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos));
tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
tv_list_append_number(l, ((fp != NULL)
? (varnumber_T)fp->lnum
@@ -9992,12 +10093,12 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (return_list) {
rettv->v_type = VAR_LIST;
- rettv->vval.v_list =
+ rettv->vval.v_list =
get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);
if (rettv->vval.v_list == NULL) {
- rettv->vval.v_list = tv_list_alloc();
+ rettv->vval.v_list = tv_list_alloc(0);
}
- rettv->vval.v_list->lv_refcount++;
+ tv_list_ref(rettv->vval.v_list);
} else {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0);
@@ -10045,7 +10146,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
- list_T *const l = tv_list_alloc();
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
tv_list_append_number(l, (varnumber_T)wp->handle);
}
@@ -10062,7 +10163,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tabpage_T *tparg = NULL;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
+ ? 1
+ : kListLenMayKnow));
if (argvars[0].v_type != VAR_UNKNOWN) {
// Information about one tab page
@@ -10080,9 +10183,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
continue;
}
dict_T *const d = get_tabpage_info(tp, tpnr);
- if (d != NULL) {
- tv_list_append_dict(rettv->vval.v_list, d);
- }
+ tv_list_append_dict(rettv->vval.v_list, d);
if (tparg != NULL) {
return;
}
@@ -10163,7 +10264,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wparg = NULL;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_UNKNOWN) {
wparg = win_id2wp(argvars);
@@ -10184,9 +10285,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
winnr++;
dict_T *const d = get_win_info(wp, tabnr, winnr);
- if (d != NULL) {
- tv_list_append_dict(rettv->vval.v_list, d);
- }
+ tv_list_append_dict(rettv->vval.v_list, d);
if (wparg != NULL) {
// found information about a specific window
return;
@@ -10390,9 +10489,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ExpandOne(
&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL);
} else {
- tv_list_alloc_ret(rettv);
ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
-1);
@@ -10441,7 +10540,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
} else {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
tv_list_append_string(rettv->vval.v_list,
((const char **)(ga.ga_data))[i], -1);
@@ -10580,6 +10679,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"windows",
"winaltkeys",
"writebackup",
+#if defined(HAVE_WSL)
+ "wsl",
+#endif
"nvim",
};
@@ -10616,6 +10718,10 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = has_nvim_version(name + 5);
} else if (STRICMP(name, "vim_starting") == 0) {
n = (starting != 0);
+ } else if (STRICMP(name, "ttyin") == 0) {
+ n = stdin_isatty;
+ } else if (STRICMP(name, "ttyout") == 0) {
+ n = stdout_isatty;
} else if (STRICMP(name, "multi_byte_encoding") == 0) {
n = has_mbyte != 0;
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
@@ -10635,6 +10741,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = true;
}
+ if (STRICMP(name, "ruby") == 0 && n == true) {
+ char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true);
+ if (rubyhost) {
+ if (*rubyhost == NUL) {
+ // Invalid rubyhost executable. Gem is probably not installed.
+ n = false;
+ }
+ xfree(rubyhost);
+ }
+ }
+
rettv->vval.v_number = n;
}
@@ -10954,39 +11071,42 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- list_T *l;
- listitem_T *item;
long idx = 0;
- int ic = FALSE;
+ bool ic = false;
rettv->vval.v_number = -1;
if (argvars[0].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
}
- l = argvars[0].vval.v_list;
+ list_T *const l = argvars[0].vval.v_list;
if (l != NULL) {
- item = l->lv_first;
+ listitem_T *item = tv_list_first(l);
if (argvars[2].v_type != VAR_UNKNOWN) {
bool error = false;
- // Start at specified item. Use the cached index that tv_list_find()
- // sets, so that a negative number also works.
- item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error));
- idx = l->lv_idx;
- if (argvars[3].v_type != VAR_UNKNOWN) {
- ic = tv_get_number_chk(&argvars[3], &error);
- }
- if (error) {
+ // Start at specified item.
+ idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error));
+ if (error || idx == -1) {
item = NULL;
+ } else {
+ item = tv_list_find(l, idx);
+ assert(item != NULL);
+ }
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ ic = !!tv_get_number_chk(&argvars[3], &error);
+ if (error) {
+ item = NULL;
+ }
}
}
- for (; item != NULL; item = item->li_next, ++idx)
- if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) {
+ for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) {
+ if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) {
rettv->vval.v_number = idx;
break;
}
+ }
}
}
@@ -11010,16 +11130,18 @@ void get_user_input(const typval_T *const argvars,
const char *defstr = "";
const char *cancelreturn = NULL;
const char *xp_name = NULL;
+ Callback input_callback = { .type = kCallbackNone };
char prompt_buf[NUMBUFLEN];
char defstr_buf[NUMBUFLEN];
char cancelreturn_buf[NUMBUFLEN];
char xp_name_buf[NUMBUFLEN];
+ char def[1] = { 0 };
if (argvars[0].v_type == VAR_DICT) {
if (argvars[1].v_type != VAR_UNKNOWN) {
emsgf(_("E5050: {opts} must be the only argument"));
return;
}
- const dict_T *const dict = argvars[0].vval.v_dict;
+ dict_T *const dict = argvars[0].vval.v_dict;
prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, "");
if (prompt == NULL) {
return;
@@ -11028,7 +11150,6 @@ void get_user_input(const typval_T *const argvars,
if (defstr == NULL) {
return;
}
- char def[1] = { 0 };
cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),
cancelreturn_buf, def);
if (cancelreturn == NULL) { // error
@@ -11045,6 +11166,9 @@ void get_user_input(const typval_T *const argvars,
if (xp_name == def) { // default to NULL
xp_name = NULL;
}
+ if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) {
+ return;
+ }
} else {
prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf);
if (prompt == NULL) {
@@ -11087,28 +11211,30 @@ void get_user_input(const typval_T *const argvars,
cmd_silent = false; // Want to see the prompt.
// Only the part of the message after the last NL is considered as
- // prompt for the command line.
- const char *p = strrchr(prompt, '\n');
- if (p == NULL) {
- p = prompt;
- } else {
- p++;
- msg_start();
- msg_clr_eos();
- msg_puts_attr_len(prompt, p - prompt, echo_attr);
- msg_didout = false;
- msg_starthere();
+ // prompt for the command line, unlsess cmdline is externalized
+ const char *p = prompt;
+ if (!ui_is_external(kUICmdline)) {
+ const char *lastnl = strrchr(prompt, '\n');
+ if (lastnl != NULL) {
+ p = lastnl+1;
+ msg_start();
+ msg_clr_eos();
+ msg_puts_attr_len(prompt, p - prompt, echo_attr);
+ msg_didout = false;
+ msg_starthere();
+ }
}
cmdline_row = msg_row;
stuffReadbuffSpec(defstr);
- int save_ex_normal_busy = ex_normal_busy;
+ const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
rettv->vval.v_string =
- getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr,
- xp_type, (char_u *)xp_arg);
+ (char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr,
+ xp_type, xp_arg, input_callback);
ex_normal_busy = save_ex_normal_busy;
+ callback_free(&input_callback);
if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
@@ -11144,11 +11270,10 @@ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- listitem_T *li;
int selected;
int mouse_used;
- if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) {
+ if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "inputlist()");
return;
}
@@ -11159,15 +11284,16 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
msg_scroll = TRUE;
msg_clr_eos();
- for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) {
- msg_puts(tv_get_string(&li->li_tv));
+ TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
+ msg_puts(tv_get_string(TV_LIST_ITEM_TV(li)));
msg_putchar('\n');
- }
+ });
- /* Ask for choice. */
+ // Ask for choice.
selected = prompt_for_number(&mouse_used);
- if (mouse_used)
+ if (mouse_used) {
selected -= lines_left;
+ }
rettv->vval.v_number = selected;
}
@@ -11223,9 +11349,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "insert()");
- } else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, N_("insert() argument"),
- TV_TRANSLATE)) {
+ } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("insert() argument"), TV_TRANSLATE)) {
long before = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = tv_get_number_chk(&argvars[2], &error);
@@ -11236,7 +11361,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
listitem_T *item = NULL;
- if (before != l->lv_len) {
+ if (before != tv_list_len(l)) {
item = tv_list_find(l, before);
if (item == NULL) {
EMSGN(_(e_listidx), before);
@@ -11300,7 +11425,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_dictkey), lv.ll_newkey);
} else if (lv.ll_list != NULL) {
// List item.
- rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv);
+ rettv->vval.v_number = tv_islocked(TV_LIST_ITEM_TV(lv.ll_li));
} else {
// Dictionary item.
rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv);
@@ -11329,43 +11454,41 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
return;
}
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
TV_DICT_ITER(tv->vval.v_dict, di, {
- listitem_T *const li = tv_list_item_alloc();
- tv_list_append(rettv->vval.v_list, li);
+ typval_T tv = { .v_lock = VAR_UNLOCKED };
switch (what) {
case kDictListKeys: {
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = VAR_UNLOCKED;
- li->li_tv.vval.v_string = vim_strsave(di->di_key);
+ tv.v_type = VAR_STRING;
+ tv.vval.v_string = vim_strsave(di->di_key);
break;
}
case kDictListValues: {
- tv_copy(&di->di_tv, &li->li_tv);
+ tv_copy(&di->di_tv, &tv);
break;
}
case kDictListItems: {
// items()
- list_T *const sub_l = tv_list_alloc();
- li->li_tv.v_type = VAR_LIST;
- li->li_tv.v_lock = VAR_UNLOCKED;
- li->li_tv.vval.v_list = sub_l;
- sub_l->lv_refcount++;
-
- listitem_T *sub_li = tv_list_item_alloc();
- tv_list_append(sub_l, sub_li);
- sub_li->li_tv.v_type = VAR_STRING;
- sub_li->li_tv.v_lock = VAR_UNLOCKED;
- sub_li->li_tv.vval.v_string = vim_strsave(di->di_key);
-
- sub_li = tv_list_item_alloc();
- tv_list_append(sub_l, sub_li);
- tv_copy(&di->di_tv, &sub_li->li_tv);
+ list_T *const sub_l = tv_list_alloc(2);
+ tv.v_type = VAR_LIST;
+ tv.vval.v_list = sub_l;
+ tv_list_ref(sub_l);
+
+ tv_list_append_owned_tv(sub_l, (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = (char_u *)xstrdup((const char *)di->di_key),
+ });
+
+ tv_list_append_tv(sub_l, &di->di_tv);
+
break;
}
}
+
+ tv_list_append_owned_tv(rettv->vval.v_list, tv);
});
}
@@ -11387,68 +11510,6 @@ static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dict_list(argvars, rettv, 2);
}
-// "jobclose(id[, stream])" function
-static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_restricted() || check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type != VAR_STRING
- && argvars[1].v_type != VAR_UNKNOWN)) {
- EMSG(_(e_invarg));
- return;
- }
-
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
- if (!data) {
- EMSG(_(e_invjob));
- return;
- }
-
- Process *proc = (Process *)&data->proc;
-
- if (argvars[1].v_type == VAR_STRING) {
- char *stream = (char *)argvars[1].vval.v_string;
- if (!strcmp(stream, "stdin")) {
- if (data->rpc) {
- EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')"));
- } else {
- process_close_in(proc);
- }
- } else if (!strcmp(stream, "stdout")) {
- if (data->rpc) {
- EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')"));
- } else {
- process_close_out(proc);
- }
- } else if (!strcmp(stream, "stderr")) {
- process_close_err(proc);
- } else if (!strcmp(stream, "rpc")) {
- if (data->rpc) {
- channel_close(data->id);
- } else {
- EMSG(_("Invalid job stream: Not an rpc job"));
- }
- } else {
- EMSG2(_("Invalid job stream \"%s\""), stream);
- }
- } else {
- if (data->rpc) {
- channel_close(data->id);
- process_close_err(proc);
- } else {
- process_close_streams(proc);
- if (proc->type == kProcessTypePty) {
- pty_process_close_master(&data->proc.pty);
- }
- }
- }
-}
-
// "jobpid(id)" function
static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -11464,61 +11525,15 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
+ Channel *data = find_job(argvars[0].vval.v_number, true);
if (!data) {
- EMSG(_(e_invjob));
return;
}
- Process *proc = (Process *)&data->proc;
+ Process *proc = (Process *)&data->stream.proc;
rettv->vval.v_number = proc->pid;
}
-// "jobsend()" function
-static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_restricted() || check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) {
- // First argument is the job id and second is the string or list to write
- // to the job's stdin
- EMSG(_(e_invarg));
- return;
- }
-
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
- if (!data) {
- EMSG(_(e_invjob));
- return;
- }
-
- if (((Process *)&data->proc)->in->closed) {
- EMSG(_("Can't send data to the job: stdin is closed"));
- return;
- }
-
- if (data->rpc) {
- EMSG(_("Can't send raw data to rpc channel"));
- return;
- }
-
- ptrdiff_t input_len = 0;
- char *input = save_tv_as_string(&argvars[1], &input_len, false);
- if (!input) {
- // Either the error has been handled by save_tv_as_string(), or there is no
- // input to send.
- return;
- }
-
- WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree);
- rettv->vval.v_number = wstream_write(data->proc.uv.process.in, buf);
-}
-
// "jobresize(job, width, height)" function
static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -11537,19 +11552,18 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
+ Channel *data = find_job(argvars[0].vval.v_number, true);
if (!data) {
- EMSG(_(e_invjob));
return;
}
- if (data->proc.uv.process.type != kProcessTypePty) {
- EMSG(_(e_jobnotpty));
+ if (data->stream.proc.type != kProcessTypePty) {
+ EMSG(_(e_channotpty));
return;
}
- pty_process_resize(&data->proc.pty, argvars[1].vval.v_number,
- argvars[2].vval.v_number);
+ pty_process_resize(&data->stream.pty, argvars[1].vval.v_number,
+ argvars[2].vval.v_number);
rettv->vval.v_number = 1;
}
@@ -11569,15 +11583,13 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
}
list_T *argl = cmd_tv->vval.v_list;
- int argc = argl->lv_len;
+ int argc = tv_list_len(argl);
if (!argc) {
EMSG(_(e_invarg)); // List must have at least one item.
return NULL;
}
- assert(argl->lv_first);
-
- const char *exe = tv_get_string_chk(&argl->lv_first->li_tv);
+ const char *exe = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl)));
if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) {
if (exe && executable) {
*executable = false;
@@ -11592,15 +11604,15 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
// Build the argument vector
int i = 0;
char **argv = xcalloc(argc + 1, sizeof(char *));
- for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) {
- const char *a = tv_get_string_chk(&arg->li_tv);
+ TV_LIST_ITER_CONST(argl, arg, {
+ const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg));
if (!a) {
// Did emsg in tv_get_string_chk; just deallocate argv.
shell_free_argv(argv);
return NULL;
}
argv[i++] = xstrdup(a);
- }
+ });
return argv;
}
@@ -11634,8 +11646,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool detach = false;
bool rpc = false;
bool pty = false;
- Callback on_stdout = CALLBACK_NONE;
- Callback on_stderr = CALLBACK_NONE;
+ CallbackReader on_stdout = CALLBACK_READER_INIT,
+ on_stderr = CALLBACK_READER_INIT;
Callback on_exit = CALLBACK_NONE;
char *cwd = NULL;
if (argvars[1].v_type == VAR_DICT) {
@@ -11667,32 +11679,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- pty, rpc, detach, cwd);
- Process *proc = (Process *)&data->proc;
+ uint16_t width = 0, height = 0;
+ char *term_name = NULL;
if (pty) {
- uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width");
- if (width > 0) {
- data->proc.pty.width = width;
- }
- uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height");
- if (height > 0) {
- data->proc.pty.height = height;
- }
- char *term = tv_dict_get_string(job_opts, "TERM", true);
- if (term) {
- data->proc.pty.term_name = term;
- }
+ width = (uint16_t)tv_dict_get_number(job_opts, "width");
+ height = (uint16_t)tv_dict_get_number(job_opts, "height");
+ term_name = tv_dict_get_string(job_opts, "TERM", true);
}
- if (!rpc && on_stdout.type == kCallbackNone) {
- proc->out = NULL;
- }
- if (on_stderr.type == kCallbackNone) {
- proc->err = NULL;
+ Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
+ rpc, detach, cwd, width, height, term_name,
+ &rettv->vval.v_number);
+ if (chan) {
+ channel_create_event(chan, NULL);
}
- common_job_start(data, rettv);
}
// "jobstop()" function
@@ -11712,14 +11713,12 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
- TerminalJobData *data = find_job(argvars[0].vval.v_number);
+ Channel *data = find_job(argvars[0].vval.v_number, true);
if (!data) {
- EMSG(_(e_invjob));
return;
}
- process_stop((Process *)&data->proc);
- data->stopped = true;
+ process_stop((Process *)&data->stream.proc);
rettv->vval.v_number = 1;
}
@@ -11739,30 +11738,34 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+
list_T *args = argvars[0].vval.v_list;
- list_T *rv = tv_list_alloc();
+ Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs));
ui_busy_start();
MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop);
// For each item in the input list append an integer to the output list. -3
// is used to represent an invalid job id, -2 is for a interrupted job and
// -1 for jobs that were skipped or timed out.
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- TerminalJobData *data = NULL;
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = find_job(arg->li_tv.vval.v_number))) {
- tv_list_append_number(rv, -3);
+
+ int i = 0;
+ TV_LIST_ITER_CONST(args, arg, {
+ Channel *chan = NULL;
+ if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER
+ || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) {
+ jobs[i] = NULL;
} else {
- // append the list item and set the status pointer so we'll collect the
- // status code when the job exits
- tv_list_append_number(rv, -1);
- data->status_ptr = &rv->lv_last->li_tv.vval.v_number;
- // Process any pending events for the job because we'll temporarily
- // replace the parent queue
- multiqueue_process_events(data->events);
- multiqueue_replace_parent(data->events, waiting_jobs);
+ jobs[i] = chan;
+ channel_incref(chan);
+ if (chan->stream.proc.status < 0) {
+ // Process any pending events for the job because we'll temporarily
+ // replace the parent queue
+ multiqueue_process_events(chan->events);
+ multiqueue_replace_parent(chan->events, waiting_jobs);
+ }
}
- }
+ i++;
+ });
int remaining = -1;
uint64_t before = 0;
@@ -11771,24 +11774,21 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
before = os_hrtime();
}
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- TerminalJobData *data = NULL;
+ for (i = 0; i < tv_list_len(args); i++) {
if (remaining == 0) {
// timed out
break;
}
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = find_job(arg->li_tv.vval.v_number))) {
+
+ // if the job already exited, but wasn't freed yet
+ if (jobs[i] == NULL || jobs[i]->stream.proc.status >= 0) {
continue;
}
- int status = process_wait((Process *)&data->proc, remaining, waiting_jobs);
+
+ int status = process_wait(&jobs[i]->stream.proc, remaining,
+ waiting_jobs);
if (status < 0) {
// interrupted or timed out, skip remaining jobs.
- if (status == -2) {
- // set the status so the user can distinguish between interrupted and
- // skipped/timeout jobs.
- *data->status_ptr = -2;
- }
break;
}
if (remaining > 0) {
@@ -11801,32 +11801,26 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- TerminalJobData *data = NULL;
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = find_job(arg->li_tv.vval.v_number))) {
- continue;
- }
- // remove the status pointer because the list may be freed before the
- // job exits
- data->status_ptr = NULL;
- }
+ list_T *const rv = tv_list_alloc(tv_list_len(args));
// restore the parent queue for any jobs still alive
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- TerminalJobData *data = NULL;
- if (arg->li_tv.v_type != VAR_NUMBER
- || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) {
+ for (i = 0; i < tv_list_len(args); i++) {
+ if (jobs[i] == NULL) {
+ tv_list_append_number(rv, -3);
continue;
}
// restore the parent queue for the job
- multiqueue_process_events(data->events);
- multiqueue_replace_parent(data->events, main_loop.events);
+ multiqueue_process_events(jobs[i]->events);
+ multiqueue_replace_parent(jobs[i]->events, main_loop.events);
+
+ tv_list_append_number(rv, jobs[i]->stream.proc.status);
+ channel_decref(jobs[i]);
}
multiqueue_free(waiting_jobs);
+ xfree(jobs);
ui_busy_stop();
- rv->lv_refcount++;
+ tv_list_ref(rv);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = rv;
}
@@ -11840,9 +11834,6 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_(e_listreq));
return;
}
- if (argvars[0].vval.v_list == NULL) {
- return;
- }
const char *const sep = (argvars[1].v_type == VAR_UNKNOWN
? " "
: tv_get_string_chk(&argvars[1]));
@@ -12111,15 +12102,17 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
mode = get_map_mode((char_u **)&which, 0);
- keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false,
+ keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
CPO_TO_CPO_FLAGS);
rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local);
xfree(keys_buf);
if (!get_dict) {
- /* Return a string. */
- if (rhs != NULL)
- rettv->vval.v_string = str2special_save(rhs, FALSE);
+ // Return a string.
+ if (rhs != NULL) {
+ rettv->vval.v_string = (char_u *)str2special_save(
+ (const char *)rhs, false, false);
+ }
} else {
tv_dict_alloc_ret(rettv);
@@ -12154,7 +12147,8 @@ void mapblock_fill_dict(dict_T *const dict,
bool compatible)
FUNC_ATTR_NONNULL_ALL
{
- char_u *lhs = str2special_save(mp->m_keys, true);
+ char *const lhs = str2special_save((const char *)mp->m_keys,
+ compatible, !compatible);
char *const mapmode = map_mode_to_chars(mp->m_mode);
varnumber_T noremap_value;
@@ -12168,18 +12162,21 @@ void mapblock_fill_dict(dict_T *const dict,
noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
}
- tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs);
- tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
+ if (compatible) {
+ tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
+ } else {
+ tv_dict_add_allocated_str(dict, S_LEN("rhs"),
+ str2special_save((const char *)mp->m_str, false,
+ true));
+ }
+ tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID);
tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
- tv_dict_add_str(dict, S_LEN("mode"), mapmode);
-
- xfree(lhs);
- xfree(mapmode);
+ tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
}
/*
@@ -12207,7 +12204,8 @@ static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
-static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
+static void find_some_match(typval_T *const argvars, typval_T *const rettv,
+ const SomeMatchType type)
{
char_u *str = NULL;
long len = 0;
@@ -12228,25 +12226,38 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
p_cpo = (char_u *)"";
rettv->vval.v_number = -1;
- if (type == 3 || type == 4) {
- // type 3: return empty list when there are no matches.
- // type 4: return ["", -1, -1, -1]
- tv_list_alloc_ret(rettv);
- if (type == 4) {
+ switch (type) {
+ // matchlist(): return empty list when there are no matches.
+ case kSomeMatchList: {
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ break;
+ }
+ // matchstrpos(): return ["", -1, -1, -1]
+ case kSomeMatchStrPos: {
+ tv_list_alloc_ret(rettv, 4);
tv_list_append_string(rettv->vval.v_list, "", 0);
tv_list_append_number(rettv->vval.v_list, -1);
tv_list_append_number(rettv->vval.v_list, -1);
tv_list_append_number(rettv->vval.v_list, -1);
+ break;
+ }
+ case kSomeMatchStr: {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ break;
+ }
+ case kSomeMatch:
+ case kSomeMatchEnd: {
+ // Do nothing: zero is default.
+ break;
}
- } else if (type == 2) {
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
}
if (argvars[0].v_type == VAR_LIST) {
- if ((l = argvars[0].vval.v_list) == NULL)
+ if ((l = argvars[0].vval.v_list) == NULL) {
goto theend;
- li = l->lv_first;
+ }
+ li = tv_list_first(l);
} else {
expr = str = (char_u *)tv_get_string(&argvars[0]);
len = (long)STRLEN(str);
@@ -12266,11 +12277,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
goto theend;
}
if (l != NULL) {
- li = tv_list_find(l, start);
- if (li == NULL) {
+ idx = tv_list_uidx(l, start);
+ if (idx == -1) {
goto theend;
}
- idx = l->lv_idx; // Use the cached index.
+ li = tv_list_find(l, idx);
} else {
if (start < 0)
start = 0;
@@ -12306,7 +12317,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
break;
}
xfree(tofree);
- tofree = expr = str = (char_u *)encode_tv2echo(&li->li_tv, NULL);
+ tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li),
+ NULL);
if (str == NULL) {
break;
}
@@ -12321,8 +12333,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
/* Advance to just after the match. */
if (l != NULL) {
- li = li->li_next;
- ++idx;
+ li = TV_LIST_ITEM_NEXT(l, li);
+ idx++;
} else {
startcol = (colnr_T)(regmatch.startp[0]
+ (*mb_ptr2len)(regmatch.startp[0]) - str);
@@ -12334,62 +12346,76 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
}
if (match) {
- if (type == 4) {
- listitem_T *li1 = rettv->vval.v_list->lv_first;
- listitem_T *li2 = li1->li_next;
- listitem_T *li3 = li2->li_next;
- listitem_T *li4 = li3->li_next;
- xfree(li1->li_tv.vval.v_string);
-
- int rd = (int)(regmatch.endp[0] - regmatch.startp[0]);
- li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], rd);
- li3->li_tv.vval.v_number = (varnumber_T)(regmatch.startp[0] - expr);
- li4->li_tv.vval.v_number = (varnumber_T)(regmatch.endp[0] - expr);
- if (l != NULL) {
- li2->li_tv.vval.v_number = (varnumber_T)idx;
+ switch (type) {
+ case kSomeMatchStrPos: {
+ list_T *const ret_l = rettv->vval.v_list;
+ listitem_T *li1 = tv_list_first(ret_l);
+ listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1);
+ listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2);
+ listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3);
+ xfree(TV_LIST_ITEM_TV(li1)->vval.v_string);
+
+ const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]);
+ TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz(
+ (const char *)regmatch.startp[0], rd);
+ TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(
+ regmatch.startp[0] - expr);
+ TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(
+ regmatch.endp[0] - expr);
+ if (l != NULL) {
+ TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx;
+ }
+ break;
}
- } else if (type == 3) {
- int i;
-
- /* return list with matched string and submatches */
- for (i = 0; i < NSUBEXP; ++i) {
- if (regmatch.endp[i] == NULL) {
- tv_list_append_string(rettv->vval.v_list, NULL, 0);
- } else {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)regmatch.startp[i],
- (regmatch.endp[i] - regmatch.startp[i]));
+ case kSomeMatchList: {
+ // Return list with matched string and submatches.
+ for (int i = 0; i < NSUBEXP; i++) {
+ if (regmatch.endp[i] == NULL) {
+ tv_list_append_string(rettv->vval.v_list, NULL, 0);
+ } else {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)regmatch.startp[i],
+ (regmatch.endp[i] - regmatch.startp[i]));
+ }
}
+ break;
}
- } else if (type == 2) {
- // Return matched string.
- if (l != NULL) {
- tv_copy(&li->li_tv, rettv);
- } else {
- rettv->vval.v_string = (char_u *)xmemdupz(
- (const char *)regmatch.startp[0],
- (size_t)(regmatch.endp[0] - regmatch.startp[0]));
+ case kSomeMatchStr: {
+ // Return matched string.
+ if (l != NULL) {
+ tv_copy(TV_LIST_ITEM_TV(li), rettv);
+ } else {
+ rettv->vval.v_string = (char_u *)xmemdupz(
+ (const char *)regmatch.startp[0],
+ (size_t)(regmatch.endp[0] - regmatch.startp[0]));
+ }
+ break;
}
- } else if (l != NULL) {
- rettv->vval.v_number = idx;
- } else {
- if (type != 0) {
- rettv->vval.v_number =
- (varnumber_T)(regmatch.startp[0] - str);
- } else {
- rettv->vval.v_number =
- (varnumber_T)(regmatch.endp[0] - str);
+ case kSomeMatch:
+ case kSomeMatchEnd: {
+ if (l != NULL) {
+ rettv->vval.v_number = idx;
+ } else {
+ if (type == kSomeMatch) {
+ rettv->vval.v_number =
+ (varnumber_T)(regmatch.startp[0] - str);
+ } else {
+ rettv->vval.v_number =
+ (varnumber_T)(regmatch.endp[0] - str);
+ }
+ rettv->vval.v_number += (varnumber_T)(str - expr);
+ }
+ break;
}
- rettv->vval.v_number += (varnumber_T)(str - expr);
}
}
vim_regfree(regmatch.regprog);
}
- if (type == 4 && l == NULL) {
+ if (type == kSomeMatchStrPos && l == NULL) {
// matchstrpos() without a list: drop the second item
- tv_list_item_remove(rettv->vval.v_list,
- rettv->vval.v_list->lv_first->li_next);
+ list_T *const ret_l = rettv->vval.v_list;
+ tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l)));
}
theend:
@@ -12402,7 +12428,7 @@ theend:
*/
static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- find_some_match(argvars, rettv, 1);
+ find_some_match(argvars, rettv, kSomeMatch);
}
/*
@@ -12445,7 +12471,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
if (id >= 1 && id <= 3) {
- EMSGN("E798: ID is reserved for \":match\": %" PRId64, id);
+ EMSGN(_("E798: ID is reserved for \":match\": %" PRId64), id);
return;
}
@@ -12455,56 +12481,56 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = -1;
+ rettv->vval.v_number = -1;
- char buf[NUMBUFLEN];
- const char *const group = tv_get_string_buf_chk(&argvars[0], buf);
- if (group == NULL) {
- return;
- }
+ char buf[NUMBUFLEN];
+ const char *const group = tv_get_string_buf_chk(&argvars[0], buf);
+ if (group == NULL) {
+ return;
+ }
- if (argvars[1].v_type != VAR_LIST) {
- EMSG2(_(e_listarg), "matchaddpos()");
- return;
- }
+ if (argvars[1].v_type != VAR_LIST) {
+ EMSG2(_(e_listarg), "matchaddpos()");
+ return;
+ }
- list_T *l;
- l = argvars[1].vval.v_list;
- if (l == NULL) {
- return;
- }
+ list_T *l;
+ l = argvars[1].vval.v_list;
+ if (l == NULL) {
+ return;
+ }
- bool error = false;
- int prio = 10;
- int id = -1;
- const char *conceal_char = NULL;
+ bool error = false;
+ int prio = 10;
+ int id = -1;
+ const char *conceal_char = NULL;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- prio = tv_get_number_chk(&argvars[2], &error);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN) {
- if (argvars[4].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
- return;
- }
- dictitem_T *di;
- if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
- != NULL) {
- conceal_char = tv_get_string(&di->di_tv);
- }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prio = tv_get_number_chk(&argvars[2], &error);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ id = tv_get_number_chk(&argvars[3], &error);
+ if (argvars[4].v_type != VAR_UNKNOWN) {
+ if (argvars[4].v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return;
+ }
+ dictitem_T *di;
+ if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
+ != NULL) {
+ conceal_char = tv_get_string(&di->di_tv);
}
}
}
- if (error == true) {
- return;
- }
+ }
+ if (error == true) {
+ return;
+ }
- // id == 3 is ok because matchaddpos() is supposed to substitute :3match
- if (id == 1 || id == 2) {
- EMSGN("E798: ID is reserved for \"match\": %" PRId64, id);
- return;
- }
+ // id == 3 is ok because matchaddpos() is supposed to substitute :3match
+ if (id == 1 || id == 2) {
+ EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id);
+ return;
+ }
rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l,
conceal_char);
@@ -12515,14 +12541,16 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
+ const int id = tv_get_number(&argvars[0]);
- int id = tv_get_number(&argvars[0]);
+ tv_list_alloc_ret(rettv, (id >= 1 && id <= 3
+ ? 2
+ : 0));
if (id >= 1 && id <= 3) {
- matchitem_T *m;
+ matchitem_T *const m = (matchitem_T *)get_match(curwin, id);
- if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) {
+ if (m != NULL) {
tv_list_append_string(rettv->vval.v_list,
(const char *)syn_id2name(m->hlg_id), -1);
tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1);
@@ -12547,7 +12575,7 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- find_some_match(argvars, rettv, 0);
+ find_some_match(argvars, rettv, kSomeMatchEnd);
}
/*
@@ -12555,7 +12583,7 @@ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- find_some_match(argvars, rettv, 3);
+ find_some_match(argvars, rettv, kSomeMatchList);
}
/*
@@ -12563,13 +12591,13 @@ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- find_some_match(argvars, rettv, 2);
+ find_some_match(argvars, rettv, kSomeMatchStr);
}
/// "matchstrpos()" function
static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- find_some_match(argvars, rettv, 4);
+ find_some_match(argvars, rettv, kSomeMatchStrPos);
}
/// Get maximal/minimal number value in a list or dictionary
@@ -12585,41 +12613,41 @@ static void max_min(const typval_T *const tv, typval_T *const rettv,
const bool domax)
FUNC_ATTR_NONNULL_ALL
{
- varnumber_T n = 0;
bool error = false;
+ rettv->vval.v_number = 0;
+ varnumber_T n = (domax ? VARNUMBER_MIN : VARNUMBER_MAX);
if (tv->v_type == VAR_LIST) {
- const list_T *const l = tv->vval.v_list;
- if (tv_list_len(l) != 0) {
- n = tv_get_number_chk(&l->lv_first->li_tv, &error);
- for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error;
- li = li->li_next) {
- const varnumber_T i = tv_get_number_chk(&li->li_tv, &error);
- if (domax ? i > n : i < n) {
- n = i;
- }
- }
+ if (tv_list_len(tv->vval.v_list) == 0) {
+ return;
}
+ TV_LIST_ITER_CONST(tv->vval.v_list, li, {
+ const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error);
+ if (error) {
+ return;
+ }
+ if (domax ? i > n : i < n) {
+ n = i;
+ }
+ });
} else if (tv->v_type == VAR_DICT) {
- if (tv->vval.v_dict != NULL) {
- bool first = true;
- TV_DICT_ITER(tv->vval.v_dict, di, {
- const varnumber_T i = tv_get_number_chk(&di->di_tv, &error);
- if (error) {
- break;
- }
- if (first) {
- n = i;
- first = true;
- } else if (domax ? i > n : i < n) {
- n = i;
- }
- });
+ if (tv_dict_len(tv->vval.v_dict) == 0) {
+ return;
}
+ TV_DICT_ITER(tv->vval.v_dict, di, {
+ const varnumber_T i = tv_get_number_chk(&di->di_tv, &error);
+ if (error) {
+ return;
+ }
+ if (domax ? i > n : i < n) {
+ n = i;
+ }
+ });
} else {
EMSG2(_(e_listdictarg), domax ? "max()" : "min()");
+ return;
}
- rettv->vval.v_number = error ? 0 : n;
+ rettv->vval.v_number = n;
}
/*
@@ -12704,23 +12732,20 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackdump()");
return;
}
- list_T *ret_list = tv_list_alloc_ret(rettv);
- const list_T *list = argvars[0].vval.v_list;
- if (list == NULL) {
- return;
- }
+ list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
+ list_T *const list = argvars[0].vval.v_list;
msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write);
const char *const msg = _("msgpackdump() argument, index %i");
// Assume that translation will not take more then 4 times more space
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
int idx = 0;
- for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx);
+ TV_LIST_ITER(list, li, {
+ vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx);
idx++;
- if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) {
+ if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {
break;
}
- }
+ });
msgpack_packer_free(lpacker);
}
@@ -12732,12 +12757,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackparse()");
return;
}
- list_T *ret_list = tv_list_alloc_ret(rettv);
- const list_T *list = argvars[0].vval.v_list;
- if (list == NULL || list->lv_first == NULL) {
+ list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
+ const list_T *const list = argvars[0].vval.v_list;
+ if (tv_list_len(list) == 0) {
return;
}
- if (list->lv_first->li_tv.v_type != VAR_STRING) {
+ if (TV_LIST_ITEM_TV(tv_list_first(list))->v_type != VAR_STRING) {
EMSG2(_(e_invarg2), "List item is not a string");
return;
}
@@ -12777,13 +12802,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
goto f_msgpackparse_exit;
}
if (result == MSGPACK_UNPACK_SUCCESS) {
- listitem_T *li = tv_list_item_alloc();
- li->li_tv.v_type = VAR_UNKNOWN;
- tv_list_append(ret_list, li);
- if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) {
+ typval_T tv = { .v_type = VAR_UNKNOWN };
+ if (msgpack_to_vim(unpacked.data, &tv) == FAIL) {
EMSG2(_(e_invarg2), "Failed to convert msgpack string");
goto f_msgpackparse_exit;
}
+ tv_list_append_owned_tv(ret_list, tv);
}
if (result == MSGPACK_UNPACK_CONTINUE) {
if (rlret == OK) {
@@ -12989,7 +13013,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (stride > 0 ? end + 1 < start : end - 1 > start) {
emsgf(_("E727: Start past end"));
} else {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (end - start) / stride);
for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {
tv_list_append_number(rettv->vval.v_list, (varnumber_T)i);
}
@@ -13010,9 +13034,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
long prevlen = 0; /* length of data in prev */
long prevsize = 0; /* size of prev buffer */
long maxline = MAXLNUM;
- long cnt = 0;
- char_u *p; /* position in buf */
- char_u *start; /* start of current line */
if (argvars[1].v_type != VAR_UNKNOWN) {
if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
@@ -13023,7 +13044,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- tv_list_alloc_ret(rettv);
+ list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);
// Always open the file in binary mode, library functions have a mind of
// their own about CR-LF conversion.
@@ -13033,19 +13054,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- while (cnt < maxline || maxline < 0) {
+ while (maxline < 0 || tv_list_len(l) < maxline) {
readlen = (int)fread(buf, 1, io_size, fd);
- /* This for loop processes what was read, but is also entered at end
- * of file so that either:
- * - an incomplete line gets written
- * - a "binary" file gets an empty line at the end if it ends in a
- * newline. */
+ // This for loop processes what was read, but is also entered at end
+ // of file so that either:
+ // - an incomplete line gets written
+ // - a "binary" file gets an empty line at the end if it ends in a
+ // newline.
+ char_u *p; // Position in buf.
+ char_u *start; // Start of current line.
for (p = buf, start = buf;
p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary));
- ++p) {
+ p++) {
if (*p == '\n' || readlen <= 0) {
- listitem_T *li;
char_u *s = NULL;
size_t len = p - start;
@@ -13072,22 +13094,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
prevlen = prevsize = 0;
}
- li = tv_list_item_alloc();
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = s;
- tv_list_append(rettv->vval.v_list, li);
-
- start = p + 1; /* step over newline */
- if ((++cnt >= maxline && maxline >= 0) || readlen <= 0)
+ tv_list_append_owned_tv(l, (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = s,
+ });
+
+ start = p + 1; // Step over newline.
+ if (maxline < 0) {
+ if (tv_list_len(l) > -maxline) {
+ assert(tv_list_len(l) == 1 + (-maxline));
+ tv_list_item_remove(l, tv_list_first(l));
+ }
+ } else if (tv_list_len(l) >= maxline) {
+ assert(tv_list_len(l) == maxline);
break;
- } else if (*p == NUL)
+ }
+ if (readlen <= 0) {
+ break;
+ }
+ } else if (*p == NUL) {
*p = '\n';
- /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this
- * when finding the BF and check the previous two bytes. */
- else if (*p == 0xbf && enc_utf8 && !binary) {
- /* Find the two bytes before the 0xbf. If p is at buf, or buf
- * + 1, these may be in the "prev" string. */
+ // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this
+ // when finding the BF and check the previous two bytes.
+ } else if (*p == 0xbf && !binary) {
+ // Find the two bytes before the 0xbf. If p is at buf, or buf + 1,
+ // these may be in the "prev" string.
char_u back1 = p >= buf + 1 ? p[-1]
: prevlen >= 1 ? prev[prevlen - 1] : NUL;
char_u back2 = p >= buf + 2 ? p[-2]
@@ -13121,8 +13153,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} /* for */
- if ((cnt >= maxline && maxline >= 0) || readlen <= 0)
+ if ((maxline >= 0 && tv_list_len(l) >= maxline) || readlen <= 0) {
break;
+ }
if (start < p) {
/* There's part of a line in buf, store it in "prev". */
if (p - start + prevlen >= prevsize) {
@@ -13146,16 +13179,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} /* while */
- /*
- * For a negative line count use only the lines at the end of the file,
- * free the rest.
- */
- if (maxline < 0)
- while (cnt > -maxline) {
- tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
- cnt--;
- }
-
xfree(prev);
fclose(fd);
}
@@ -13169,9 +13192,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// @return OK In case of success, FAIL in case of error
static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
{
- if (arg->v_type != VAR_LIST
- || arg->vval.v_list == NULL
- || arg->vval.v_list->lv_len != 2) {
+ if (arg->v_type != VAR_LIST || tv_list_len(arg->vval.v_list) != 2) {
return FAIL;
}
@@ -13236,7 +13257,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u),
"type punning will produce incorrect results on this platform");
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 2);
tv_list_append_number(rettv->vval.v_list, u.split.high);
tv_list_append_number(rettv->vval.v_list, u.split.low);
}
@@ -13295,8 +13316,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listdictarg), "remove()");
- } else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) {
+ } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ arg_errmsg, TV_TRANSLATE)) {
bool error = false;
idx = tv_get_number_chk(&argvars[1], &error);
@@ -13307,8 +13328,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else {
if (argvars[2].v_type == VAR_UNKNOWN) {
// Remove one item, return its value.
- tv_list_remove_items(l, item, item);
- *rettv = item->li_tv;
+ tv_list_drop_items(l, item, item);
+ *rettv = *TV_LIST_ITEM_TV(item);
xfree(item);
} else {
// Remove range of items, return list with values.
@@ -13320,21 +13341,17 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else {
int cnt = 0;
- for (li = item; li != NULL; li = li->li_next) {
- ++cnt;
- if (li == item2)
+ for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ cnt++;
+ if (li == item2) {
break;
+ }
}
if (li == NULL) { // Didn't find "item2" after "item".
emsgf(_(e_invrange));
} else {
- tv_list_remove_items(l, item, item2);
- l = tv_list_alloc_ret(rettv);
- l->lv_first = item;
- l->lv_last = item2;
- item->li_prev = NULL;
- item2->li_next = NULL;
- l->lv_len = cnt;
+ tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt),
+ cnt);
}
}
}
@@ -13364,7 +13381,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
varnumber_T n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list));
while (n-- > 0) {
tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);
}
@@ -13479,7 +13496,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
q[-1] = NUL;
q = (char *)path_tail((char_u *)p);
}
- if (q > p && !path_is_absolute_path((const char_u *)buf)) {
+ if (q > p && !path_is_absolute((const char_u *)buf)) {
// Symlink is relative to directory of argument. Replace the
// symlink with the resolved name in the same directory.
const size_t p_len = strlen(p);
@@ -13570,21 +13587,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *l;
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "reverse()");
- } else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, N_("reverse() argument"),
- TV_TRANSLATE)) {
- listitem_T *li = l->lv_last;
- l->lv_first = l->lv_last = NULL;
- l->lv_len = 0;
- while (li != NULL) {
- listitem_T *const ni = li->li_prev;
- tv_list_append(l, li);
- li = ni;
- }
+ } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("reverse() argument"), TV_TRANSLATE)) {
+ tv_list_reverse(l);
rettv->vval.v_list = l;
rettv->v_type = VAR_LIST;
- l->lv_refcount++;
- l->lv_idx = l->lv_len - l->lv_idx - 1;
+ tv_list_ref(l);
}
}
@@ -13767,9 +13775,8 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ADD(args, vim_to_object(tv));
}
- if (!channel_send_event((uint64_t)argvars[0].vval.v_number,
- tv_get_string(&argvars[1]),
- args)) {
+ if (!rpc_send_event((uint64_t)argvars[0].vval.v_number,
+ tv_get_string(&argvars[1]), args)) {
EMSG2(_(e_invarg2), "Channel doesn't exist");
return;
}
@@ -13807,7 +13814,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
scid_T save_current_SID;
uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
linenr_T save_sourcing_lnum;
- int save_autocmd_fname_full, save_autocmd_bufnr;
+ int save_autocmd_bufnr;
void *save_funccalp;
if (l_provider_call_nesting) {
@@ -13818,26 +13825,22 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
save_sourcing_lnum = sourcing_lnum;
save_autocmd_fname = autocmd_fname;
save_autocmd_match = autocmd_match;
- save_autocmd_fname_full = autocmd_fname_full;
save_autocmd_bufnr = autocmd_bufnr;
save_funccalp = save_funccal();
- //
+
current_SID = provider_caller_scope.SID;
sourcing_name = provider_caller_scope.sourcing_name;
sourcing_lnum = provider_caller_scope.sourcing_lnum;
autocmd_fname = provider_caller_scope.autocmd_fname;
autocmd_match = provider_caller_scope.autocmd_match;
- autocmd_fname_full = provider_caller_scope.autocmd_fname_full;
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
restore_funccal(provider_caller_scope.funccalp);
}
Error err = ERROR_INIT;
- Object result = channel_send_call((uint64_t)argvars[0].vval.v_number,
- tv_get_string(&argvars[1]),
- args,
- &err);
+ Object result = rpc_send_call((uint64_t)argvars[0].vval.v_number,
+ tv_get_string(&argvars[1]), args, &err);
if (l_provider_call_nesting) {
current_SID = save_current_SID;
@@ -13845,7 +13848,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
sourcing_lnum = save_sourcing_lnum;
autocmd_fname = save_autocmd_fname;
autocmd_match = save_autocmd_match;
- autocmd_fname_full = save_autocmd_fname_full;
autocmd_bufnr = save_autocmd_bufnr;
restore_funccal(save_funccalp);
}
@@ -13885,14 +13887,17 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int argsl = 0;
if (argvars[1].v_type == VAR_LIST) {
args = argvars[1].vval.v_list;
- argsl = args->lv_len;
+ argsl = tv_list_len(args);
// Assert that all list items are strings
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- if (arg->li_tv.v_type != VAR_STRING) {
- EMSG(_(e_invarg));
+ int i = 0;
+ TV_LIST_ITER_CONST(args, arg, {
+ if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) {
+ emsgf(_("E5010: List item %d of the second argument is not a string"),
+ i);
return;
}
- }
+ i++;
+ });
}
if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) {
@@ -13910,18 +13915,21 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int i = 1;
// Copy arguments to the vector
if (argsl > 0) {
- for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- argv[i++] = xstrdup(tv_get_string(&arg->li_tv));
- }
+ TV_LIST_ITER_CONST(args, arg, {
+ argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg)));
+ });
}
// The last item of argv must be NULL
argv[i] = NULL;
- TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE,
- CALLBACK_NONE, false, true, false,
- NULL);
- common_job_start(data, rettv);
+ Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
+ CALLBACK_READER_INIT, CALLBACK_NONE,
+ false, true, false, NULL, 0, 0, NULL,
+ &rettv->vval.v_number);
+ if (chan) {
+ channel_create_event(chan, NULL);
+ }
}
// "rpcstop()" function
@@ -13941,10 +13949,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// if called with a job, stop it, else closes the channel
- if (pmap_get(uint64_t)(jobs, argvars[0].vval.v_number)) {
+ uint64_t id = argvars[0].vval.v_number;
+ if (find_job(id, false)) {
f_jobstop(argvars, rettv, NULL);
} else {
- rettv->vval.v_number = channel_close(argvars[0].vval.v_number);
+ const char *error;
+ rettv->vval.v_number = channel_close(argvars[0].vval.v_number,
+ kChannelPartRpc, &error);
+ if (!rettv->vval.v_number) {
+ EMSG(error);
+ }
}
}
@@ -14134,7 +14148,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int lnum = 0;
int col = 0;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 2);
if (searchpair_cmn(argvars, &match_pos) > 0) {
lnum = match_pos.lnum;
@@ -14176,6 +14190,8 @@ do_searchpair (
int nest = 1;
int options = SEARCH_KEEP;
proftime_T tm;
+ size_t pat2_len;
+ size_t pat3_len;
/* Make 'cpoptions' empty, the 'l' flag should not be used here. */
save_cpo = p_cpo;
@@ -14184,18 +14200,22 @@ do_searchpair (
/* Set the time limit, if there is one. */
tm = profile_setlimit(time_limit);
- /* Make two search patterns: start/end (pat2, for in nested pairs) and
- * start/middle/end (pat3, for the top pair). */
- pat2 = xmalloc(STRLEN(spat) + STRLEN(epat) + 15);
- pat3 = xmalloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23);
- sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
- if (*mpat == NUL)
+ // Make two search patterns: start/end (pat2, for in nested pairs) and
+ // start/middle/end (pat3, for the top pair).
+ pat2_len = STRLEN(spat) + STRLEN(epat) + 17;
+ pat2 = xmalloc(pat2_len);
+ pat3_len = STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25;
+ pat3 = xmalloc(pat3_len);
+ snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
+ if (*mpat == NUL) {
STRCPY(pat3, pat2);
- else
- sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)",
- spat, epat, mpat);
- if (flags & SP_START)
+ } else {
+ snprintf((char *)pat3, pat3_len,
+ "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat);
+ }
+ if (flags & SP_START) {
options |= SEARCH_START;
+ }
save_cursor = curwin->w_cursor;
pos = curwin->w_cursor;
@@ -14296,18 +14316,14 @@ do_searchpair (
static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T match_pos;
- int lnum = 0;
- int col = 0;
- int n;
int flags = 0;
- tv_list_alloc_ret(rettv);
+ const int n = search_cmn(argvars, &match_pos, &flags);
- n = search_cmn(argvars, &match_pos, &flags);
- if (n > 0) {
- lnum = match_pos.lnum;
- col = match_pos.col;
- }
+ tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT)));
+
+ const int lnum = (n > 0 ? match_pos.lnum : 0);
+ const int col = (n > 0 ? match_pos.col : 0);
tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
tv_list_append_number(rettv->vval.v_list, (varnumber_T)col);
@@ -14323,13 +14339,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char **addrs = server_address_list(&n);
// Copy addrs into a linked list.
- list_T *l = tv_list_alloc_ret(rettv);
+ list_T *const l = tv_list_alloc_ret(rettv, n);
for (size_t i = 0; i < n; i++) {
- listitem_T *li = tv_list_item_alloc();
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = (char_u *)addrs[i];
- tv_list_append(l, li);
+ tv_list_append_allocated_string(l, addrs[i]);
}
xfree(addrs);
}
@@ -14537,25 +14549,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *line = NULL;
if (argvars[1].v_type == VAR_LIST) {
l = argvars[1].vval.v_list;
- li = l->lv_first;
+ li = tv_list_first(l);
} else {
line = tv_get_string_chk(&argvars[1]);
}
- /* default result is zero == OK */
+ // Default result is zero == OK.
for (;; ) {
- if (l != NULL) {
+ if (argvars[1].v_type == VAR_LIST) {
// List argument, get next string.
if (li == NULL) {
break;
}
- line = tv_get_string_chk(&li->li_tv);
- li = li->li_next;
+ line = tv_get_string_chk(TV_LIST_ITEM_TV(li));
+ li = TV_LIST_ITEM_NEXT(l, li);
}
- rettv->vval.v_number = 1; /* FAIL */
- if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
+ rettv->vval.v_number = 1; // FAIL
+ if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) {
break;
+ }
/* When coming here from Insert mode, sync undo, so that this can be
* undone separately from what was previously inserted. */
@@ -14627,7 +14640,8 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
return;
}
const char *const act = tv_get_string_chk(action_arg);
- if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) {
+ if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
+ && act[1] == NUL) {
action = *act;
} else {
EMSG2(_(e_invact), act);
@@ -14656,8 +14670,8 @@ skip_args:
title = (wp ? "setloclist()" : "setqflist()");
}
- list_T *l = list_arg->vval.v_list;
- if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) {
+ list_T *const l = list_arg->vval.v_list;
+ if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) {
rettv->vval.v_number = 0;
}
}
@@ -14682,8 +14696,6 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- list_T *l;
- listitem_T *li;
dict_T *d;
list_T *s = NULL;
@@ -14692,95 +14704,89 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_(e_listreq));
return;
}
- if ((l = argvars[0].vval.v_list) != NULL) {
-
- /* To some extent make sure that we are dealing with a list from
- * "getmatches()". */
- li = l->lv_first;
- while (li != NULL) {
- if (li->li_tv.v_type != VAR_DICT
- || (d = li->li_tv.vval.v_dict) == NULL) {
- EMSG(_(e_invarg));
- return;
- }
- if (!(tv_dict_find(d, S_LEN("group")) != NULL
- && (tv_dict_find(d, S_LEN("pattern")) != NULL
- || tv_dict_find(d, S_LEN("pos1")) != NULL)
- && tv_dict_find(d, S_LEN("priority")) != NULL
- && tv_dict_find(d, S_LEN("id")) != NULL)) {
- EMSG(_(e_invarg));
- return;
- }
- li = li->li_next;
+ list_T *const l = argvars[0].vval.v_list;
+ // To some extent make sure that we are dealing with a list from
+ // "getmatches()".
+ int i = 0;
+ TV_LIST_ITER_CONST(l, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT
+ || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) {
+ emsgf(_("E474: List item %d is either not a dictionary "
+ "or an empty one"), i);
+ return;
}
+ if (!(tv_dict_find(d, S_LEN("group")) != NULL
+ && (tv_dict_find(d, S_LEN("pattern")) != NULL
+ || tv_dict_find(d, S_LEN("pos1")) != NULL)
+ && tv_dict_find(d, S_LEN("priority")) != NULL
+ && tv_dict_find(d, S_LEN("id")) != NULL)) {
+ emsgf(_("E474: List item %d is missing one of the required keys"), i);
+ return;
+ }
+ i++;
+ });
+
+ clear_matches(curwin);
+ bool match_add_failed = false;
+ TV_LIST_ITER_CONST(l, li, {
+ int i = 0;
- clear_matches(curwin);
- li = l->lv_first;
- bool match_add_failed = false;
- while (li != NULL) {
- int i = 0;
+ d = TV_LIST_ITEM_TV(li)->vval.v_dict;
+ dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
+ if (di == NULL) {
+ if (s == NULL) {
+ s = tv_list_alloc(9);
+ }
- d = li->li_tv.vval.v_dict;
- dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
- if (di == NULL) {
- if (s == NULL) {
- s = tv_list_alloc();
- if (s == NULL) {
+ // match from matchaddpos()
+ for (i = 1; i < 9; i++) {
+ char buf[5];
+ snprintf(buf, sizeof(buf), "pos%d", i);
+ dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
+ if (pos_di != NULL) {
+ if (pos_di->di_tv.v_type != VAR_LIST) {
return;
}
- }
-
- // match from matchaddpos()
- for (i = 1; i < 9; i++) {
- char buf[5];
- snprintf(buf, sizeof(buf), "pos%d", i);
- dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
- if (pos_di != NULL) {
- if (pos_di->di_tv.v_type != VAR_LIST) {
- return;
- }
- tv_list_append_tv(s, &pos_di->di_tv);
- s->lv_refcount++;
- } else {
- break;
- }
+ tv_list_append_tv(s, &pos_di->di_tv);
+ tv_list_ref(s);
+ } else {
+ break;
}
}
+ }
- // Note: there are three number buffers involved:
- // - group_buf below.
- // - numbuf in tv_dict_get_string().
- // - mybuf in tv_get_string().
- //
- // If you change this code make sure that buffers will not get
- // accidentally reused.
- char group_buf[NUMBUFLEN];
- const char *const group = tv_dict_get_string_buf(d, "group", group_buf);
- const int priority = (int)tv_dict_get_number(d, "priority");
- const int id = (int)tv_dict_get_number(d, "id");
- dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal"));
- const char *const conceal = (conceal_di != NULL
- ? tv_get_string(&conceal_di->di_tv)
- : NULL);
- if (i == 0) {
- if (match_add(curwin, group,
- tv_dict_get_string(d, "pattern", false),
- priority, id, NULL, conceal) != id) {
- match_add_failed = true;
- }
- } else {
- if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) {
- match_add_failed = true;
- }
- tv_list_unref(s);
- s = NULL;
+ // Note: there are three number buffers involved:
+ // - group_buf below.
+ // - numbuf in tv_dict_get_string().
+ // - mybuf in tv_get_string().
+ //
+ // If you change this code make sure that buffers will not get
+ // accidentally reused.
+ char group_buf[NUMBUFLEN];
+ const char *const group = tv_dict_get_string_buf(d, "group", group_buf);
+ const int priority = (int)tv_dict_get_number(d, "priority");
+ const int id = (int)tv_dict_get_number(d, "id");
+ dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal"));
+ const char *const conceal = (conceal_di != NULL
+ ? tv_get_string(&conceal_di->di_tv)
+ : NULL);
+ if (i == 0) {
+ if (match_add(curwin, group,
+ tv_dict_get_string(d, "pattern", false),
+ priority, id, NULL, conceal) != id) {
+ match_add_failed = true;
}
- li = li->li_next;
- }
- if (!match_add_failed) {
- rettv->vval.v_number = 0;
+ } else {
+ if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) {
+ match_add_failed = true;
+ }
+ tv_list_unref(s);
+ s = NULL;
}
+ });
+ if (!match_add_failed) {
+ rettv->vval.v_number = 0;
}
}
@@ -14897,7 +14903,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type == VAR_LIST) {
list_T *ll = argvars[1].vval.v_list;
// If the list is NULL handle like an empty list.
- int len = ll == NULL ? 0 : ll->lv_len;
+ const int len = tv_list_len(ll);
// First half: use for pointers to result lines; second half: use for
// pointers to allocated copies.
@@ -14906,11 +14912,9 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char **allocval = lstval + len + 2;
char **curallocval = allocval;
- for (listitem_T *li = ll == NULL ? NULL : ll->lv_first;
- li != NULL;
- li = li->li_next) {
+ TV_LIST_ITER_CONST(ll, li, {
char buf[NUMBUFLEN];
- *curval = tv_get_string_buf_chk(&li->li_tv, buf);
+ *curval = tv_get_string_buf_chk(TV_LIST_ITEM_TV(li), buf);
if (*curval == NULL) {
goto free_lstval;
}
@@ -14922,7 +14926,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
curallocval++;
}
curval++;
- }
+ });
*curval++ = NULL;
write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type,
@@ -15118,18 +15122,22 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
bool rpc = false;
+ CallbackReader on_data = CALLBACK_READER_INIT;
if (argvars[2].v_type == VAR_DICT) {
dict_T *opts = argvars[2].vval.v_dict;
rpc = tv_dict_get_number(opts, "rpc") != 0;
- }
- if (!rpc) {
- EMSG2(_(e_invarg2), "rpc option must be true");
- return;
+ if (!tv_dict_get_callback(opts, S_LEN("on_data"), &on_data.cb)) {
+ return;
+ }
+ on_data.buffered = tv_dict_get_number(opts, "data_buffered");
+ if (on_data.buffered && on_data.cb.type == kCallbackNone) {
+ on_data.self = opts;
+ }
}
const char *error = NULL;
- uint64_t id = channel_connect(tcp, address, 50, &error);
+ uint64_t id = channel_connect(tcp, address, rpc, on_data, 50, &error);
if (error) {
EMSG2(_("connection failed: %s"), error);
@@ -15139,12 +15147,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_NUMBER;
}
-/// struct used in the array that's given to qsort()
-typedef struct {
- listitem_T *item;
- int idx;
-} sortItem_T;
-
/// struct storing information about current sort
typedef struct {
int item_compare_ic;
@@ -15165,11 +15167,11 @@ static sortinfo_T *sortinfo = NULL;
*/
static int item_compare(const void *s1, const void *s2, bool keep_zero)
{
- sortItem_T *const si1 = (sortItem_T *)s1;
- sortItem_T *const si2 = (sortItem_T *)s2;
+ ListSortItem *const si1 = (ListSortItem *)s1;
+ ListSortItem *const si2 = (ListSortItem *)s2;
- typval_T *const tv1 = &si1->item->li_tv;
- typval_T *const tv2 = &si2->item->li_tv;
+ typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item);
+ typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item);
int res;
@@ -15241,6 +15243,8 @@ item_compare_end:
// When the result would be zero, compare the item indexes. Makes the
// sort stable.
if (res == 0 && !keep_zero) {
+ // WARNING: When using uniq si1 and si2 are actually listitem_T **, no
+ // indexes are there.
res = si1->idx > si2->idx ? 1 : -1;
}
return res;
@@ -15258,7 +15262,7 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2)
static int item_compare2(const void *s1, const void *s2, bool keep_zero)
{
- sortItem_T *si1, *si2;
+ ListSortItem *si1, *si2;
int res;
typval_T rettv;
typval_T argv[3];
@@ -15271,8 +15275,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
return 0;
}
- si1 = (sortItem_T *)s1;
- si2 = (sortItem_T *)s2;
+ si1 = (ListSortItem *)s1;
+ si2 = (ListSortItem *)s2;
if (partial == NULL) {
func_name = sortinfo->item_compare_func;
@@ -15282,8 +15286,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
// in the copy without changing the original list items.
- tv_copy(&si1->item->li_tv, &argv[0]);
- tv_copy(&si2->item->li_tv, &argv[1]);
+ tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]);
+ tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]);
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
res = call_func((const char_u *)func_name,
@@ -15306,6 +15310,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
// When the result would be zero, compare the pointers themselves. Makes
// the sort stable.
if (res == 0 && !keep_zero) {
+ // WARNING: When using uniq si1 and si2 are actually listitem_T **, no
+ // indexes are there.
res = si1->idx > si2->idx ? 1 : -1;
}
@@ -15327,9 +15333,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
*/
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
{
- list_T *l;
- listitem_T *li;
- sortItem_T *ptrs;
+ ListSortItem *ptrs;
long len;
long i;
@@ -15346,13 +15350,13 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
- l = argvars[0].vval.v_list;
- if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) {
- goto theend;
+ list_T *const l = argvars[0].vval.v_list;
+ if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ goto theend;
}
rettv->vval.v_list = l;
rettv->v_type = VAR_LIST;
- ++l->lv_refcount;
+ tv_list_ref(l);
len = tv_list_len(l);
if (len <= 1) {
@@ -15418,80 +15422,45 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
}
}
- /* Make an array with each entry pointing to an item in the List. */
- ptrs = xmalloc((size_t)(len * sizeof (sortItem_T)));
+ // Make an array with each entry pointing to an item in the List.
+ ptrs = xmalloc((size_t)(len * sizeof(ListSortItem)));
- i = 0;
if (sort) {
- // sort(): ptrs will be the list to sort.
- for (li = l->lv_first; li != NULL; li = li->li_next) {
- ptrs[i].item = li;
- ptrs[i].idx = i;
- i++;
- }
-
info.item_compare_func_err = false;
- // Test the compare function.
- if ((info.item_compare_func != NULL
- || info.item_compare_partial != NULL)
- && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1])
- == ITEM_COMPARE_FAIL) {
+ tv_list_item_sort(l, ptrs,
+ ((info.item_compare_func == NULL
+ && info.item_compare_partial == NULL)
+ ? item_compare_not_keeping_zero
+ : item_compare2_not_keeping_zero),
+ &info.item_compare_func_err);
+ if (info.item_compare_func_err) {
EMSG(_("E702: Sort compare function failed"));
- } else {
- // Sort the array with item pointers.
- qsort(ptrs, (size_t)len, sizeof (sortItem_T),
- (info.item_compare_func == NULL
- && info.item_compare_partial == NULL ?
- item_compare_not_keeping_zero :
- item_compare2_not_keeping_zero));
-
- if (!info.item_compare_func_err) {
- // Clear the list and append the items in the sorted order.
- l->lv_first = NULL;
- l->lv_last = NULL;
- l->lv_idx_item = NULL;
- l->lv_len = 0;
-
- for (i = 0; i < len; i++) {
- tv_list_append(l, ptrs[i].item);
- }
- }
}
} else {
- int (*item_compare_func_ptr)(const void *, const void *);
+ ListSorter item_compare_func_ptr;
// f_uniq(): ptrs will be a stack of items to remove.
info.item_compare_func_err = false;
if (info.item_compare_func != NULL
|| info.item_compare_partial != NULL) {
- item_compare_func_ptr = item_compare2_keeping_zero;
+ item_compare_func_ptr = item_compare2_keeping_zero;
} else {
- item_compare_func_ptr = item_compare_keeping_zero;
+ item_compare_func_ptr = item_compare_keeping_zero;
}
- for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) {
- if (item_compare_func_ptr(&li, &li->li_next) == 0) {
- ptrs[i++].item = li;
- }
- if (info.item_compare_func_err) {
- EMSG(_("E882: Uniq compare function failed"));
- break;
- }
- }
-
- if (!info.item_compare_func_err) {
- while (--i >= 0) {
- assert(ptrs[i].item->li_next);
- li = ptrs[i].item->li_next;
- ptrs[i].item->li_next = li->li_next;
- if (li->li_next != NULL) {
- li->li_next->li_prev = ptrs[i].item;
- } else {
- l->lv_last = ptrs[i].item;
+ int idx = 0;
+ for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
+ ; li != NULL;) {
+ listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
+ if (item_compare_func_ptr(&prev_li, &li) == 0) {
+ if (info.item_compare_func_err) {
+ EMSG(_("E882: Uniq compare function failed"));
+ break;
}
- tv_list_watch_fix(l, li);
- tv_list_item_free(li);
- l->lv_len--;
+ li = tv_list_item_remove(l, li);
+ } else {
+ idx++;
+ li = TV_LIST_ITEM_NEXT(l, li);
}
}
}
@@ -15509,6 +15478,39 @@ static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr)
do_sort_uniq(argvars, rettv, true);
}
+/// "stdioopen()" function
+static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ if (argvars[0].v_type != VAR_DICT) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+
+ bool rpc = false;
+ CallbackReader on_stdin = CALLBACK_READER_INIT;
+ dict_T *opts = argvars[0].vval.v_dict;
+ rpc = tv_dict_get_number(opts, "rpc") != 0;
+
+ if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) {
+ return;
+ }
+ on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered");
+ if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) {
+ on_stdin.self = opts;
+ }
+
+ const char *error;
+ uint64_t id = channel_from_stdio(rpc, on_stdin, &error);
+ if (!id) {
+ EMSG2(e_stdiochan2, error);
+ }
+
+
+ rettv->vval.v_number = (varnumber_T)id;
+ rettv->v_type = VAR_NUMBER;
+}
+
/// "uniq({list})" function
static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -15549,13 +15551,12 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hlf_T attr = HLF_COUNT;
size_t len = 0;
- tv_list_alloc_ret(rettv);
-
if (argvars[0].v_type == VAR_UNKNOWN) {
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, true, true, &attr);
if (len != 0) {
word = (char *)get_cursor_pos_ptr();
+ curwin->w_set_curswant = true;
}
} else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) {
const char *str = tv_get_string_chk(&argvars[0]);
@@ -15575,6 +15576,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
assert(len <= INT_MAX);
+ tv_list_alloc_ret(rettv, 2);
tv_list_append_string(rettv->vval.v_list, word, len);
tv_list_append_string(rettv->vval.v_list,
(attr == HLF_SPB ? "bad"
@@ -15591,41 +15593,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool typeerr = false;
int maxcount;
- garray_T ga;
- listitem_T *li;
+ garray_T ga = GA_EMPTY_INIT_VALUE;
bool need_capital = false;
- tv_list_alloc_ret(rettv);
-
if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
const char *const str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN) {
maxcount = tv_get_number_chk(&argvars[1], &typeerr);
if (maxcount <= 0) {
- return;
+ goto f_spellsuggest_return;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
need_capital = tv_get_number_chk(&argvars[2], &typeerr);
if (typeerr) {
- return;
+ goto f_spellsuggest_return;
}
}
- } else
+ } else {
maxcount = 25;
+ }
spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
+ }
- for (int i = 0; i < ga.ga_len; i++) {
- char *p = ((char **)ga.ga_data)[i];
-
- li = tv_list_item_alloc();
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = (char_u *)p;
- tv_list_append(rettv->vval.v_list, li);
- }
- ga_clear(&ga);
+f_spellsuggest_return:
+ tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len);
+ for (int i = 0; i < ga.ga_len; i++) {
+ char *const p = ((char **)ga.ga_data)[i];
+ tv_list_append_allocated_string(rettv->vval.v_list, p);
}
+ ga_clear(&ga);
}
static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
@@ -15657,10 +15654,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pat = "[\\x01- ]\\+";
}
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
- if (typeerr)
+ if (typeerr) {
return;
+ }
regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
@@ -15677,7 +15675,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else {
end = str + strlen(str);
}
- if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0
+ if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0
&& *str != NUL
&& match
&& end < (const char *)regmatch.endp[0])) {
@@ -16223,7 +16221,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
case 'n': { // name
- p = get_highlight_name(NULL, id - 1);
+ p = get_highlight_name_ext(NULL, id - 1, false);
break;
}
case 'r': { // reverse
@@ -16287,7 +16285,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
memset(str, NUL, sizeof(str));
- tv_list_alloc_ret(rettv);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
(void)syn_get_id(curwin, lnum, col, false, NULL, false);
@@ -16296,18 +16293,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// get the conceal character
if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) {
cchar = syn_get_sub_char();
- if (cchar == NUL && curwin->w_p_cole == 1 && lcs_conceal != NUL) {
- cchar = lcs_conceal;
+ if (cchar == NUL && curwin->w_p_cole == 1) {
+ cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal;
}
if (cchar != NUL) {
- if (has_mbyte)
- (*mb_char2bytes)(cchar, str);
- else
- str[0] = cchar;
+ utf_char2bytes(cchar, str);
}
}
}
+ tv_list_alloc_ret(rettv, 3);
tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);
// -1 to auto-determine strlen
tv_list_append_string(rettv->vval.v_list, (const char *)str, -1);
@@ -16330,7 +16325,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& lnum <= curbuf->b_ml.ml_line_count
&& col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum))) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
(void)syn_get_id(curwin, lnum, col, false, NULL, true);
int id;
@@ -16346,7 +16341,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
if (!keepempty && str[len - 1] == NL) {
len--;
}
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(kListLenMayKnow);
encode_list_write(list, str, len);
return list;
}
@@ -16392,7 +16387,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
if (res == NULL) {
if (retlist) {
// return an empty list when there's no output
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 0);
} else {
rettv->vval.v_string = (char_u *) xstrdup("");
}
@@ -16405,7 +16400,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
keepempty = tv_get_number(&argvars[2]);
}
rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty);
- rettv->vval.v_list->lv_refcount++;
+ tv_list_ref(rettv->vval.v_list);
rettv->v_type = VAR_LIST;
xfree(res);
@@ -16458,7 +16453,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (wp != NULL) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
while (wp != NULL) {
tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum);
wp = wp->w_next;
@@ -16556,7 +16551,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char *fname;
tagname_T tn;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenUnknown);
fname = xmalloc(MAXPATHL);
bool first = true;
@@ -16585,8 +16580,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type != VAR_UNKNOWN) {
fname = tv_get_string(&argvars[1]);
}
- (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern,
- (char_u *)fname);
+ (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown),
+ (char_u *)tag_pattern, (char_u *)fname);
}
/*
@@ -16625,8 +16620,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE,
- on_exit = CALLBACK_NONE;
+ CallbackReader on_stdout = CALLBACK_READER_INIT,
+ on_stderr = CALLBACK_READER_INIT;
+ Callback on_exit = CALLBACK_NONE;
dict_T *job_opts = NULL;
const char *cwd = ".";
if (argvars[1].v_type == VAR_DICT) {
@@ -16649,23 +16645,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- true, false, false, cwd);
- data->proc.pty.width = curwin->w_width;
- data->proc.pty.height = curwin->w_height;
- data->proc.pty.term_name = xstrdup("xterm-256color");
- if (!common_job_start(data, rettv)) {
+ uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin));
+ Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
+ true, false, false, cwd,
+ term_width, curwin->w_height,
+ xstrdup("xterm-256color"),
+ &rettv->vval.v_number);
+ if (rettv->vval.v_number <= 0) {
return;
}
- TerminalOptions topts;
- topts.data = data;
- topts.width = curwin->w_width;
- topts.height = curwin->w_height;
- topts.write_cb = term_write;
- topts.resize_cb = term_resize;
- topts.close_cb = term_close;
- int pid = data->proc.pty.process.pid;
+ int pid = chan->stream.pty.process.pid;
char buf[1024];
// format the title with the pid to conform with the term:// URI
@@ -16676,18 +16666,16 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
(void)setfname(curbuf, (char_u *)buf, NULL, true);
// Save the job id and pid in b:terminal_job_{id,pid}
Error err = ERROR_INIT;
+ // deprecated: use 'channel' buffer option
dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
- INTEGER_OBJ(rettv->vval.v_number), false, false, &err);
+ INTEGER_OBJ(chan->id), false, false, &err);
api_clear_error(&err);
dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
INTEGER_OBJ(pid), false, false, &err);
api_clear_error(&err);
- Terminal *term = terminal_open(topts);
- data->term = term;
- data->refcount++;
-
- return;
+ channel_terminal_open(chan);
+ channel_create_event(chan, NULL);
}
// "test_garbagecollect_now()" function
@@ -16699,6 +16687,18 @@ static void f_test_garbagecollect_now(typval_T *argvars,
garbage_collect(true);
}
+// "test_write_list_log()" function
+static void f_test_write_list_log(typval_T *const argvars,
+ typval_T *const rettv,
+ FunPtr fptr)
+{
+ const char *const fname = tv_get_string_chk(&argvars[0]);
+ if (fname == NULL) {
+ return;
+ }
+ list_write_log(fname);
+}
+
bool callback_from_typval(Callback *const callback, typval_T *const arg)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -16720,30 +16720,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
return true;
}
-/// Unref/free callback
-void callback_free(Callback *const callback)
- FUNC_ATTR_NONNULL_ALL
-{
- switch (callback->type) {
- case kCallbackFuncref: {
- func_unref(callback->data.funcref);
- xfree(callback->data.funcref);
- break;
- }
- case kCallbackPartial: {
- partial_unref(callback->data.partial);
- break;
- }
- case kCallbackNone: {
- break;
- }
- default: {
- abort();
- }
- }
- callback->type = kCallbackNone;
-}
-
bool callback_call(Callback *const callback, const int argcount_in,
typval_T *const argvars_in, typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
@@ -16798,6 +16774,23 @@ static bool set_ref_in_callback(Callback *callback, int copyID,
return false;
}
+static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack)) {
+ return true;
+ }
+
+ if (reader->self) {
+ typval_T tv;
+ tv.v_type = VAR_DICT;
+ tv.vval.v_dict = reader->self;
+ return set_ref_in_item(&tv, copyID, ht_stack, list_stack);
+ }
+ return false;
+}
+
static void add_timer_info(typval_T *rettv, timer_T *timer)
{
list_T *list = rettv->vval.v_list;
@@ -16841,7 +16834,9 @@ static void add_timer_info_all(typval_T *rettv)
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN
+ ? 1
+ : timers->table->n_occupied));
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_NUMBER) {
EMSG(_(e_number_exp));
@@ -17165,7 +17160,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
case VAR_UNKNOWN: {
- EMSG2(_(e_intern2), "f_type(UNKNOWN)");
+ internal_error("f_type(UNKNOWN)");
break;
}
}
@@ -17201,7 +17196,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_alloc_ret(rettv);
dict_T *dict = rettv->vval.v_dict;
- list_T *list;
tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced);
tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last);
@@ -17211,9 +17205,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur);
tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur);
- list = tv_list_alloc();
- u_eval_tree(curbuf->b_u_oldhead, list);
- tv_dict_add_list(dict, S_LEN("entries"), list);
+ tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
}
/*
@@ -17272,7 +17264,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_findbuf()" function
static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
win_findbuf(argvars, rettv->vval.v_list);
}
@@ -17291,8 +17283,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_id2tabwin()" function
static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
- win_id2tabwin(argvars, rettv->vval.v_list);
+ win_id2tabwin(argvars, rettv);
}
/// "win_id2win()" function
@@ -17454,7 +17445,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
}
-/// Writes list of strings to file
+/// Write "list" of strings to file "fd".
///
/// @param fp File to write to.
/// @param[in] list List to write.
@@ -17463,10 +17454,11 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// @return true in case of success, false otherwise.
static bool write_list(FileDescriptor *const fp, const list_T *const list,
const bool binary)
+ FUNC_ATTR_NONNULL_ARG(1)
{
int error = 0;
- for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- const char *const s = tv_get_string_chk(&li->li_tv);
+ TV_LIST_ITER_CONST(list, li, {
+ const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li));
if (s == NULL) {
return false;
}
@@ -17493,14 +17485,14 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list,
}
}
}
- if (!binary || li->li_next != NULL) {
+ if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) {
const ptrdiff_t written = file_write(fp, "\n", 1);
if (written < 0) {
error = (int)written;
goto write_list_error;
}
}
- }
+ });
if ((error = file_flush(fp)) != 0) {
goto write_list_error;
}
@@ -17510,56 +17502,29 @@ write_list_error:
return false;
}
-/// Initializes a static list with 10 items.
-void init_static_list(staticList10_T *sl)
-{
- list_T *l = &sl->sl_list;
-
- memset(sl, 0, sizeof(staticList10_T));
- l->lv_first = &sl->sl_items[0];
- l->lv_last = &sl->sl_items[9];
- l->lv_refcount = DO_NOT_FREE_CNT;
- l->lv_lock = VAR_FIXED;
- sl->sl_list.lv_len = 10;
-
- for (int i = 0; i < 10; i++) {
- listitem_T *li = &sl->sl_items[i];
-
- if (i == 0) {
- li->li_prev = NULL;
- } else {
- li->li_prev = li - 1;
- }
- if (i == 9) {
- li->li_next = NULL;
- } else {
- li->li_next = li + 1;
- }
- }
-}
-
/// Saves a typval_T as a string.
///
-/// For lists, replaces NLs with NUL and separates items with NLs.
+/// For lists or buffers, replaces NLs with NUL and separates items with NLs.
///
-/// @param[in] tv A value to store as a string.
-/// @param[out] len The length of the resulting string or -1 on error.
+/// @param[in] tv Value to store as a string.
+/// @param[out] len Length of the resulting string or -1 on error.
/// @param[in] endnl If true, the output will end in a newline (if a list).
/// @returns an allocated string if `tv` represents a VimL string, list, or
/// number; NULL otherwise.
static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
+ *len = 0;
if (tv->v_type == VAR_UNKNOWN) {
- *len = 0;
return NULL;
}
- // For types other than list, let tv_get_string_buf_chk() get the value or
+ // For other types, let tv_get_string_buf_chk() get the value or
// print an error.
- if (tv->v_type != VAR_LIST) {
+ if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) {
const char *ret = tv_get_string_chk(tv);
- if (ret && (*len = strlen(ret))) {
+ if (ret) {
+ *len = strlen(ret);
return xmemdupz(ret, (size_t)(*len));
} else {
*len = -1;
@@ -17567,12 +17532,44 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
}
}
+ if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id.
+ buf_T *buf = buflist_findnr(tv->vval.v_number);
+ if (buf) {
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ *len += 1;
+ }
+ *len += 1;
+ }
+ } else {
+ EMSGN(_(e_nobufnr), tv->vval.v_number);
+ *len = -1;
+ return NULL;
+ }
+
+ if (*len == 0) {
+ return NULL;
+ }
+
+ char *ret = xmalloc(*len + 1);
+ char *end = ret;
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ *end++ = (*p == '\n') ? NUL : *p;
+ }
+ *end++ = '\n';
+ }
+ *end = NUL;
+ *len = end - ret;
+ return ret;
+ }
+
+ assert(tv->v_type == VAR_LIST);
// Pre-calculate the resulting length.
- *len = 0;
list_T *list = tv->vval.v_list;
- for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- *len += strlen(tv_get_string(&li->li_tv)) + 1;
- }
+ TV_LIST_ITER_CONST(list, li, {
+ *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1;
+ });
if (*len == 0) {
return NULL;
@@ -17580,14 +17577,14 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
char *ret = xmalloc(*len + endnl);
char *end = ret;
- for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) {
+ TV_LIST_ITER_CONST(list, li, {
+ for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) {
*end++ = (*s == '\n') ? NUL : *s;
}
- if (endnl || li->li_next != NULL) {
+ if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) {
*end++ = '\n';
}
- }
+ });
*end = NUL;
*len = end - ret;
return ret;
@@ -17627,9 +17624,6 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "writefile()");
return;
}
- if (argvars[0].vval.v_list == NULL) {
- return;
- }
bool binary = false;
bool append = false;
@@ -17734,9 +17728,9 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
// We accept "$" for the column number: last column.
li = tv_list_find(l, 1L);
- if (li != NULL && li->li_tv.v_type == VAR_STRING
- && li->li_tv.vval.v_string != NULL
- && STRCMP(li->li_tv.vval.v_string, "$") == 0) {
+ if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING
+ && TV_LIST_ITEM_TV(li)->vval.v_string != NULL
+ && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) {
pos.col = len + 1;
}
@@ -17813,17 +17807,18 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
*/
static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
{
- list_T *l = arg->vval.v_list;
+ list_T *l;
long i = 0;
long n;
- /* List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only
- * there when "fnump" isn't NULL; "coladd" and "curswant" are optional. */
+ // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only
+ // there when "fnump" isn't NULL; "coladd" and "curswant" are optional.
if (arg->v_type != VAR_LIST
- || l == NULL
- || l->lv_len < (fnump == NULL ? 2 : 3)
- || l->lv_len > (fnump == NULL ? 4 : 5))
+ || (l = arg->vval.v_list) == NULL
+ || tv_list_len(l) < (fnump == NULL ? 2 : 3)
+ || tv_list_len(l) > (fnump == NULL ? 4 : 5)) {
return FAIL;
+ }
if (fnump != NULL) {
n = tv_list_find_nr(l, i++, NULL); // fnum
@@ -18248,7 +18243,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val)
vimvars[idx].vv_type = VAR_LIST;
vimvars[idx].vv_list = val;
if (val != NULL) {
- val->lv_refcount++;
+ tv_list_ref(val);
}
}
@@ -18548,47 +18543,39 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict)
// Turn "dict.Func" into a partial for "Func" with "dict".
if (fp != NULL && (fp->uf_flags & FC_DICT)) {
partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T));
-
- if (pt != NULL) {
- pt->pt_refcount = 1;
- pt->pt_dict = selfdict;
- (selfdict->dv_refcount)++;
- pt->pt_auto = true;
- if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) {
- // Just a function: Take over the function name and use selfdict.
- pt->pt_name = rettv->vval.v_string;
+ pt->pt_refcount = 1;
+ pt->pt_dict = selfdict;
+ (selfdict->dv_refcount)++;
+ pt->pt_auto = true;
+ if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) {
+ // Just a function: Take over the function name and use selfdict.
+ pt->pt_name = rettv->vval.v_string;
+ } else {
+ partial_T *ret_pt = rettv->vval.v_partial;
+ int i;
+
+ // Partial: copy the function name, use selfdict and copy
+ // args. Can't take over name or args, the partial might
+ // be referenced elsewhere.
+ if (ret_pt->pt_name != NULL) {
+ pt->pt_name = vim_strsave(ret_pt->pt_name);
+ func_ref(pt->pt_name);
} else {
- partial_T *ret_pt = rettv->vval.v_partial;
- int i;
-
- // Partial: copy the function name, use selfdict and copy
- // args. Can't take over name or args, the partial might
- // be referenced elsewhere.
- if (ret_pt->pt_name != NULL) {
- pt->pt_name = vim_strsave(ret_pt->pt_name);
- func_ref(pt->pt_name);
- } else {
- pt->pt_func = ret_pt->pt_func;
- func_ptr_ref(pt->pt_func);
- }
- if (ret_pt->pt_argc > 0) {
- size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
- pt->pt_argv = (typval_T *)xmalloc(arg_size);
- if (pt->pt_argv == NULL) {
- // out of memory: drop the arguments
- pt->pt_argc = 0;
- } else {
- pt->pt_argc = ret_pt->pt_argc;
- for (i = 0; i < pt->pt_argc; i++) {
- tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]);
- }
- }
+ pt->pt_func = ret_pt->pt_func;
+ func_ptr_ref(pt->pt_func);
+ }
+ if (ret_pt->pt_argc > 0) {
+ size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
+ pt->pt_argv = (typval_T *)xmalloc(arg_size);
+ pt->pt_argc = ret_pt->pt_argc;
+ for (i = 0; i < pt->pt_argc; i++) {
+ tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]);
}
- partial_unref(ret_pt);
}
- rettv->v_type = VAR_PARTIAL;
- rettv->vval.v_partial = pt;
+ partial_unref(ret_pt);
}
+ rettv->v_type = VAR_PARTIAL;
+ rettv->vval.v_partial = pt;
}
}
@@ -18652,7 +18639,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht,
case 'l': return (current_funccal == NULL
? NULL : (dictitem_T *)&current_funccal->l_vars_var);
case 'a': return (current_funccal == NULL
- ? NULL : (dictitem_T *)&current_funccal->l_avars_var);
+ ? NULL : (dictitem_T *)&get_funccal()->l_avars_var);
}
return NULL;
}
@@ -19052,7 +19039,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,
}
return;
} else if (v->di_tv.v_type != tv->v_type) {
- EMSG2(_(e_intern2), "set_var()");
+ internal_error("set_var()");
}
}
@@ -19072,6 +19059,9 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,
return;
}
+ // Make sure dict is valid
+ assert(dict != NULL);
+
v = xmalloc(sizeof(dictitem_T) + strlen(varname));
STRCPY(v->di_key, varname);
if (tv_dict_add(dict, v) == FAIL) {
@@ -19289,12 +19279,12 @@ int var_item_copy(const vimconv_T *const conv,
case VAR_LIST:
to->v_type = VAR_LIST;
to->v_lock = 0;
- if (from->vval.v_list == NULL)
+ if (from->vval.v_list == NULL) {
to->vval.v_list = NULL;
- else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) {
- /* use the copy made earlier */
- to->vval.v_list = from->vval.v_list->lv_copylist;
- ++to->vval.v_list->lv_refcount;
+ } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) {
+ // Use the copy made earlier.
+ to->vval.v_list = tv_list_latest_copy(from->vval.v_list);
+ tv_list_ref(to->vval.v_list);
} else {
to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID);
}
@@ -19319,7 +19309,7 @@ int var_item_copy(const vimconv_T *const conv,
}
break;
case VAR_UNKNOWN:
- EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)");
+ internal_error("var_item_copy(UNKNOWN)");
ret = FAIL;
}
--recurse;
@@ -19387,14 +19377,10 @@ void ex_echo(exarg_T *eap)
}
msg_putchar_attr((uint8_t)(*p), echo_attr);
} else {
- if (has_mbyte) {
- int i = (*mb_ptr2len)((const char_u *)p);
+ int i = (*mb_ptr2len)((const char_u *)p);
- (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr);
- p += i - 1;
- } else {
- (void)msg_outtrans_len_attr((char_u *)p, 1, echo_attr);
- }
+ (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr);
+ p += i - 1;
}
}
}
@@ -19519,18 +19505,22 @@ static const char *find_option_end(const char **const arg, int *const opt_flags)
} else if (*p == 'l' && p[1] == ':') {
*opt_flags = OPT_LOCAL;
p += 2;
- } else
+ } else {
*opt_flags = 0;
+ }
- if (!ASCII_ISALPHA(*p))
+ if (!ASCII_ISALPHA(*p)) {
return NULL;
+ }
*arg = p;
- if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL)
- p += 4; /* termcap option */
- else
- while (ASCII_ISALPHA(*p))
- ++p;
+ if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) {
+ p += 4; // t_xx/termcap option
+ } else {
+ while (ASCII_ISALPHA(*p)) {
+ p++;
+ }
+ }
return p;
}
@@ -19564,6 +19554,7 @@ void ex_function(exarg_T *eap)
int todo;
hashitem_T *hi;
int sourcing_lnum_off;
+ bool show_block = false;
/*
* ":function" without argument: list functions.
@@ -19737,6 +19728,11 @@ void ex_function(exarg_T *eap)
goto errret_2;
}
+ if (KeyTyped && ui_is_external(kUICmdline)) {
+ show_block = true;
+ ui_ext_cmdline_block_append(0, (const char *)eap->cmd);
+ }
+
// find extra arguments "range", "dict", "abort" and "closure"
for (;; ) {
p = skipwhite(p);
@@ -19789,7 +19785,9 @@ void ex_function(exarg_T *eap)
if (!eap->skip && did_emsg)
goto erret;
- msg_putchar('\n'); /* don't overwrite the function name */
+ if (!ui_is_external(kUICmdline)) {
+ msg_putchar('\n'); // don't overwrite the function name
+ }
cmdline_row = msg_row;
}
@@ -19823,6 +19821,9 @@ void ex_function(exarg_T *eap)
EMSG(_("E126: Missing :endfunction"));
goto erret;
}
+ if (show_block) {
+ ui_ext_cmdline_block_append(indent, (const char *)theline);
+ }
/* Detect line continuation: sourcing_lnum increased more than one. */
if (sourcing_lnum > sourcing_lnum_off + 1)
@@ -20115,6 +20116,9 @@ ret_free:
xfree(name);
did_emsg |= saved_did_emsg;
need_wait_return |= saved_wait_return;
+ if (show_block) {
+ ui_ext_cmdline_block_leave();
+ }
}
/// Get a function name, translating "<SID>" and "<SNR>".
@@ -20875,7 +20879,9 @@ void ex_delfunction(exarg_T *eap)
if (!eap->skip) {
if (fp == NULL) {
- EMSG2(_(e_nofunc), eap->arg);
+ if (!eap->forceit) {
+ EMSG2(_(e_nofunc), eap->arg);
+ }
return;
}
if (fp->uf_calls > 0) {
@@ -20989,11 +20995,11 @@ void func_unref(char_u *name)
if (fp == NULL && isdigit(*name)) {
#ifdef EXITFREE
if (!entered_free_all_mem) {
- EMSG2(_(e_intern2), "func_unref()");
+ internal_error("func_unref()");
abort();
}
#else
- EMSG2(_(e_intern2), "func_unref()");
+ internal_error("func_unref()");
abort();
#endif
}
@@ -21032,7 +21038,7 @@ void func_ref(char_u *name)
} else if (isdigit(*name)) {
// Only give an error for a numbered function.
// Fail silently, when named or lambda function isn't found.
- EMSG2(_(e_intern2), "func_ref()");
+ internal_error("func_ref()");
}
}
@@ -21044,6 +21050,22 @@ void func_ptr_ref(ufunc_T *fp)
}
}
+/// Check whether funccall is still referenced outside
+///
+/// It is supposed to be referenced if either it is referenced itself or if l:,
+/// a: or a:000 are referenced as all these are statically allocated within
+/// funccall structure.
+static inline bool fc_referenced(const funccall_T *const fc)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_ALL
+{
+ return ((fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated)
+ != DO_NOT_FREE_CNT)
+ || fc->l_vars.dv_refcount != DO_NOT_FREE_CNT
+ || fc->l_avars.dv_refcount != DO_NOT_FREE_CNT
+ || fc->fc_refcount > 0);
+}
+
/// Call a user function
///
/// @param fp Function to call.
@@ -21157,9 +21179,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_list = &fc->l_varlist;
- memset(&fc->l_varlist, 0, sizeof(list_T));
- fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
- fc->l_varlist.lv_lock = VAR_FIXED;
+ tv_list_init_static(&fc->l_varlist);
+ tv_list_set_lock(&fc->l_varlist, VAR_FIXED);
// Set a:firstline to "firstline" and a:lastline to "lastline".
// Set a:name to named arguments.
@@ -21208,8 +21229,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]);
- fc->l_listitems[ai].li_tv = argvars[i];
- fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED;
+ *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i];
+ TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED;
}
}
@@ -21279,15 +21300,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
}
}
+ const bool do_profiling_yes = do_profiling == PROF_YES;
+
bool func_not_yet_profiling_but_should =
- do_profiling == PROF_YES
- && !fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL);
+ do_profiling_yes
+ && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);
if (func_not_yet_profiling_but_should)
func_do_profile(fp);
bool func_or_func_caller_profiling =
- do_profiling == PROF_YES
+ do_profiling_yes
&& (fp->uf_profiling
|| (fc->caller != NULL && fc->caller->func->uf_profiling));
@@ -21297,7 +21320,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
fp->uf_tm_children = profile_zero();
}
- if (do_profiling == PROF_YES) {
+ if (do_profiling_yes) {
script_prof_save(&wait_start);
}
@@ -21373,8 +21396,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
sourcing_name = save_sourcing_name;
sourcing_lnum = save_sourcing_lnum;
current_SID = save_current_SID;
- if (do_profiling == PROF_YES)
+ if (do_profiling_yes) {
script_prof_restore(&wait_start);
+ }
if (p_verbose >= 12 && sourcing_name != NULL) {
++no_wait_return;
@@ -21393,10 +21417,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
// If the a:000 list and the l: and a: dicts are not referenced and there
// is no closure using it, we can free the funccall_T and what's in it.
- if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
- && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
- && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
- && fc->fc_refcount <= 0) {
+ if (!fc_referenced(fc)) {
free_funccal(fc, false);
} else {
// "fc" is still in use. This can happen when returning "a:000",
@@ -21411,10 +21432,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
});
// Make a copy of the a:000 items, since we didn't do that above.
- for (listitem_T *li = fc->l_varlist.lv_first; li != NULL;
- li = li->li_next) {
- tv_copy(&li->li_tv, &li->li_tv);
- }
+ TV_LIST_ITER(&fc->l_varlist, li, {
+ tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li));
+ });
}
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) {
@@ -21441,10 +21461,8 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
return;
}
- if (--fc->fc_refcount <= 0 && (force || (
- fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
- && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
- && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) {
+ fc->fc_refcount--;
+ if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) {
for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {
if (fc == *pfc) {
*pfc = fc->caller;
@@ -21479,8 +21497,6 @@ free_funccal (
int free_val /* a: vars were allocated */
)
{
- listitem_T *li;
-
for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
@@ -21498,14 +21514,14 @@ free_funccal (
// allocated variables.
vars_clear_ext(&fc->l_avars.dv_hashtab, free_val);
- /* free all l: variables */
+ // Free all l: variables.
vars_clear(&fc->l_vars.dv_hashtab);
// Free the a:000 variables if they were allocated.
if (free_val) {
- for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) {
- tv_clear(&li->li_tv);
- }
+ TV_LIST_ITER(&fc->l_varlist, li, {
+ tv_clear(TV_LIST_ITEM_TV(li));
+ });
}
func_ptr_unref(fc->func);
@@ -22352,317 +22368,54 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub,
return ret;
}
-static inline TerminalJobData *common_job_init(char **argv,
- Callback on_stdout,
- Callback on_stderr,
- Callback on_exit,
- bool pty,
- bool rpc,
- bool detach,
- const char *cwd)
-{
- TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
- data->stopped = false;
- data->on_stdout = on_stdout;
- data->on_stderr = on_stderr;
- data->on_exit = on_exit;
- data->events = multiqueue_new_child(main_loop.events);
- data->rpc = rpc;
- if (pty) {
- data->proc.pty = pty_process_init(&main_loop, data);
- } else {
- data->proc.uv = libuv_process_init(&main_loop, data);
- }
- Process *proc = (Process *)&data->proc;
- proc->argv = argv;
- proc->in = &data->in;
- proc->out = &data->out;
- if (!pty) {
- proc->err = &data->err;
- }
- proc->cb = eval_job_process_exit_cb;
- proc->events = data->events;
- proc->detach = detach;
- proc->cwd = cwd;
- return data;
-}
-
/// common code for getting job callbacks for jobstart, termopen and rpcstart
///
/// @return true/false on success/failure.
-static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout,
- Callback *on_stderr, Callback *on_exit)
+static inline bool common_job_callbacks(dict_T *vopts,
+ CallbackReader *on_stdout,
+ CallbackReader *on_stderr,
+ Callback *on_exit)
{
- if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout)
- &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr)
+ if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), &on_stdout->cb)
+ &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), &on_stderr->cb)
&& tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) {
+ on_stdout->buffered = tv_dict_get_number(vopts, "stdout_buffered");
+ on_stderr->buffered = tv_dict_get_number(vopts, "stderr_buffered");
+ if (on_stdout->buffered && on_stdout->cb.type == kCallbackNone) {
+ on_stdout->self = vopts;
+ }
+ if (on_stderr->buffered && on_stderr->cb.type == kCallbackNone) {
+ on_stderr->self = vopts;
+ }
vopts->dv_refcount++;
return true;
}
- callback_free(on_stdout);
- callback_free(on_stderr);
+ callback_reader_free(on_stdout);
+ callback_reader_free(on_stderr);
callback_free(on_exit);
return false;
}
-static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
-{
- Process *proc = (Process *)&data->proc;
- if (proc->type == kProcessTypePty && proc->detach) {
- EMSG2(_(e_invarg2), "terminal/pty job cannot be detached");
- xfree(data->proc.pty.term_name);
- shell_free_argv(proc->argv);
- free_term_job_data_event((void **)&data);
- return false;
- }
-
- data->id = next_chan_id++;
- pmap_put(uint64_t)(jobs, data->id, data);
-
- data->refcount++;
- char *cmd = xstrdup(proc->argv[0]);
- int status = process_spawn(proc);
- if (status) {
- EMSG3(_(e_jobspawn), os_strerror(status), cmd);
- xfree(cmd);
- if (proc->type == kProcessTypePty) {
- xfree(data->proc.pty.term_name);
- }
- rettv->vval.v_number = proc->status;
- term_job_data_decref(data);
- return false;
- }
- xfree(cmd);
-
-
- if (data->rpc) {
- // the rpc channel takes over the in and out streams
- channel_from_process(proc, data->id);
- } else {
- wstream_init(proc->in, 0);
- if (proc->out) {
- rstream_init(proc->out, 0);
- rstream_start(proc->out, on_job_stdout, data);
- }
- }
-
- if (proc->err) {
- rstream_init(proc->err, 0);
- rstream_start(proc->err, on_job_stderr, data);
- }
- rettv->vval.v_number = data->id;
- return true;
-}
-
-static inline void free_term_job_data_event(void **argv)
-{
- TerminalJobData *data = argv[0];
- callback_free(&data->on_stdout);
- callback_free(&data->on_stderr);
- callback_free(&data->on_exit);
- multiqueue_free(data->events);
- pmap_del(uint64_t)(jobs, data->id);
- xfree(data);
-}
-
-static inline void free_term_job_data(TerminalJobData *data)
-{
- // data->queue may still be used after this function returns(process_wait), so
- // only free in the next event loop iteration
- multiqueue_put(main_loop.fast_events, free_term_job_data_event, 1, data);
-}
-
-// vimscript job callbacks must be executed on Nvim main loop
-static inline void process_job_event(TerminalJobData *data, Callback *callback,
- const char *type, char *buf, size_t count,
- int status)
+static Channel *find_job(uint64_t id, bool show_error)
{
- JobEvent event_data;
- event_data.received = NULL;
- if (buf) {
- event_data.received = tv_list_alloc();
- char *ptr = buf;
- size_t remaining = count;
- size_t off = 0;
-
- while (off < remaining) {
- // append the line
- if (ptr[off] == NL) {
- tv_list_append_string(event_data.received, ptr, off);
- size_t skip = off + 1;
- ptr += skip;
- remaining -= skip;
- off = 0;
- continue;
- }
- if (ptr[off] == NUL) {
- // Translate NUL to NL
- ptr[off] = NL;
+ Channel *data = find_channel(id);
+ if (!data || data->streamtype != kChannelStreamProc
+ || process_is_stopped(&data->stream.proc)) {
+ if (show_error) {
+ if (data && data->streamtype != kChannelStreamProc) {
+ EMSG(_(e_invchanjob));
+ } else {
+ EMSG(_(e_invchan));
}
- off++;
}
- tv_list_append_string(event_data.received, ptr, off);
- } else {
- event_data.status = status;
- }
- event_data.data = data;
- event_data.callback = callback;
- event_data.type = type;
- on_job_event(&event_data);
-}
-
-static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count,
- void *job, bool eof)
-{
- TerminalJobData *data = job;
- on_job_output(stream, job, buf, count, eof, &data->on_stdout, "stdout");
-}
-
-static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count,
- void *job, bool eof)
-{
- TerminalJobData *data = job;
- on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr");
-}
-
-static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
- size_t count, bool eof, Callback *callback,
- const char *type)
-{
- if (eof) {
- return;
- }
-
- // stub variable, to keep reading consistent with the order of events, only
- // consider the count parameter.
- size_t r;
- char *ptr = rbuffer_read_ptr(buf, &r);
-
- // The order here matters, the terminal must receive the data first because
- // process_job_event will modify the read buffer(convert NULs into NLs)
- if (data->term) {
- terminal_receive(data->term, ptr, count);
- }
-
- rbuffer_consumed(buf, count);
- if (callback->type != kCallbackNone) {
- process_job_event(data, callback, type, ptr, count, 0);
- }
-}
-
-static void eval_job_process_exit_cb(Process *proc, int status, void *d)
-{
- TerminalJobData *data = d;
- if (data->term && !data->exited) {
- data->exited = true;
- char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
- snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status);
- terminal_close(data->term, msg);
- }
- if (data->rpc) {
- channel_process_exit(data->id, status);
- }
-
- if (data->status_ptr) {
- *data->status_ptr = status;
- }
-
- process_job_event(data, &data->on_exit, "exit", NULL, 0, status);
-
- term_job_data_decref(data);
-}
-
-static void term_write(char *buf, size_t size, void *d)
-{
- TerminalJobData *job = d;
- if (job->in.closed) {
- // If the backing stream was closed abruptly, there may be write events
- // ahead of the terminal close event. Just ignore the writes.
- ILOG("write failed: stream is closed");
- return;
- }
- WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree);
- wstream_write(&job->in, wbuf);
-}
-
-static void term_resize(uint16_t width, uint16_t height, void *d)
-{
- TerminalJobData *data = d;
- pty_process_resize(&data->proc.pty, width, height);
-}
-
-static inline void term_delayed_free(void **argv)
-{
- TerminalJobData *j = argv[0];
- if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) {
- multiqueue_put(j->events, term_delayed_free, 1, j);
- return;
- }
-
- terminal_destroy(j->term);
- term_job_data_decref(j);
-}
-
-static void term_close(void *d)
-{
- TerminalJobData *data = d;
- if (!data->exited) {
- data->exited = true;
- process_stop((Process *)&data->proc);
- }
- multiqueue_put(data->events, term_delayed_free, 1, data);
-}
-
-static void term_job_data_decref(TerminalJobData *data)
-{
- if (!(--data->refcount)) {
- free_term_job_data(data);
- }
-}
-
-static void on_job_event(JobEvent *ev)
-{
- if (!ev->callback) {
- return;
- }
-
- typval_T argv[4];
-
- argv[0].v_type = VAR_NUMBER;
- argv[0].v_lock = 0;
- argv[0].vval.v_number = ev->data->id;
-
- if (ev->received) {
- argv[1].v_type = VAR_LIST;
- argv[1].v_lock = 0;
- argv[1].vval.v_list = ev->received;
- argv[1].vval.v_list->lv_refcount++;
- } else {
- argv[1].v_type = VAR_NUMBER;
- argv[1].v_lock = 0;
- argv[1].vval.v_number = ev->status;
- }
-
- argv[2].v_type = VAR_STRING;
- argv[2].v_lock = 0;
- argv[2].vval.v_string = (uint8_t *)ev->type;
-
- typval_T rettv = TV_INITIAL_VALUE;
- callback_call(ev->callback, 3, argv, &rettv);
- tv_clear(&rettv);
-}
-
-static TerminalJobData *find_job(uint64_t id)
-{
- TerminalJobData *data = pmap_get(uint64_t)(jobs, id);
- if (!data || data->stopped) {
return NULL;
}
return data;
}
+
static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
{
if (check_restricted() || check_secure()) {
@@ -22674,7 +22427,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
return;
}
- list_T *args = tv_list_alloc();
+ list_T *args = tv_list_alloc(1);
tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);
*rettv = eval_call_provider(name, "eval", args);
}
@@ -22692,7 +22445,6 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
.sourcing_lnum = sourcing_lnum,
.autocmd_fname = autocmd_fname,
.autocmd_match = autocmd_match,
- .autocmd_fname_full = autocmd_fname_full,
.autocmd_bufnr = autocmd_bufnr,
.funccalp = save_funccal()
};
@@ -22703,8 +22455,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
{.v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = 0},
{.v_type = VAR_UNKNOWN}
};
- typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0};
- arguments->lv_refcount++;
+ typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
+ tv_list_ref(arguments);
int dummy;
(void)call_func((const char_u *)func,
@@ -22725,13 +22477,14 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
restore_funccal(provider_caller_scope.funccalp);
provider_caller_scope = saved_provider_caller_scope;
provider_call_nesting--;
-
+ assert(provider_call_nesting >= 0);
+
return rettv;
}
bool eval_has_provider(const char *name)
{
-#define check_provider(name) \
+#define CHECK_PROVIDER(name) \
if (has_##name == -1) { \
has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
if (!has_##name) { \
@@ -22747,19 +22500,61 @@ bool eval_has_provider(const char *name)
static int has_python3 = -1;
static int has_ruby = -1;
- if (!strcmp(name, "clipboard")) {
- check_provider(clipboard);
+ if (strequal(name, "clipboard")) {
+ CHECK_PROVIDER(clipboard);
return has_clipboard;
- } else if (!strcmp(name, "python3")) {
- check_provider(python3);
+ } else if (strequal(name, "python3")) {
+ CHECK_PROVIDER(python3);
return has_python3;
- } else if (!strcmp(name, "python")) {
- check_provider(python);
+ } else if (strequal(name, "python")) {
+ CHECK_PROVIDER(python);
return has_python;
- } else if (!strcmp(name, "ruby")) {
- check_provider(ruby);
+ } else if (strequal(name, "ruby")) {
+ CHECK_PROVIDER(ruby);
return has_ruby;
}
return false;
}
+
+/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
+void eval_fmt_source_name_line(char *buf, size_t bufsize)
+{
+ if (sourcing_name) {
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum);
+ } else {
+ snprintf(buf, bufsize, "?");
+ }
+}
+
+/// ":checkhealth [plugins]"
+void ex_checkhealth(exarg_T *eap)
+{
+ bool found = !!find_func((char_u *)"health#check");
+ if (!found
+ && script_autoload("health#check", sizeof("health#check") - 1, false)) {
+ found = !!find_func((char_u *)"health#check");
+ }
+ if (!found) {
+ const char *vimruntime_env = os_getenv("VIMRUNTIME");
+ if (vimruntime_env == NULL) {
+ EMSG(_("E5009: $VIMRUNTIME is empty or unset"));
+ } else {
+ bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env);
+ if (rtp_ok) {
+ EMSG2(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
+ } else {
+ EMSG(_("E5009: Invalid 'runtimepath'"));
+ }
+ }
+ return;
+ }
+
+ size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')");
+ char *buf = xmalloc(bufsize);
+ snprintf(buf, bufsize, "call health#check('%s')", eap->arg);
+
+ do_cmdline_cmd(buf);
+
+ xfree(buf);
+}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 070bc35bd5..b798eae187 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -7,6 +7,9 @@
#include "nvim/eval/typval.h"
#include "nvim/profile.h"
#include "nvim/garray.h"
+#include "nvim/event/rstream.h"
+#include "nvim/event/wstream.h"
+#include "nvim/channel.h"
#define COPYID_INC 2
#define COPYID_MASK (~0x1)
@@ -53,6 +56,7 @@ typedef enum {
VV_DYING,
VV_EXCEPTION,
VV_THROWPOINT,
+ VV_STDERR,
VV_REG,
VV_CMDBANG,
VV_INSERTMODE,
@@ -82,7 +86,6 @@ typedef enum {
VV_OLDFILES,
VV_WINDOWID,
VV_PROGPATH,
- VV_COMMAND_OUTPUT,
VV_COMPLETED_ITEM,
VV_OPTION_NEW,
VV_OPTION_OLD,
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 30766a0734..daa3b637a3 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -2,10 +2,10 @@
--
-- Keys:
--
--- args Number of arguments, list with maximum and minimum number of arguments
--- or list with a minimum number of arguments only. Defaults to zero
+-- args Number of arguments, list with maximum and minimum number of arguments
+-- or list with a minimum number of arguments only. Defaults to zero
-- arguments.
--- func Name of the C function which implements the VimL function. Defaults to
+-- func Name of the C function which implements the VimL function. Defaults to
-- `f_{funcname}`.
local varargs = function(nr)
@@ -29,7 +29,7 @@ return {
assert_exception={args={1, 2}},
assert_fails={args={1, 2}},
assert_false={args={1, 2}},
- assert_inrange={args={2, 3}},
+ assert_inrange={args={3, 4}},
assert_match={args={2, 3}},
assert_notequal={args={2, 3}},
assert_notmatch={args={2, 3}},
@@ -55,6 +55,8 @@ return {
call={args={2, 3}},
ceil={args=1, func="float_op_wrapper", data="&ceil"},
changenr={},
+ chanclose={args={1, 2}},
+ chansend={args=2},
char2nr={args={1, 2}},
cindent={args=1},
clearmatches={},
@@ -173,10 +175,10 @@ return {
islocked={args=1},
id={args=1},
items={args=1},
- jobclose={args={1, 2}},
+ jobclose={args={1, 2}, func="f_chanclose"},
jobpid={args=1},
jobresize={args=3},
- jobsend={args=2},
+ jobsend={args=2, func="f_chansend"},
jobstart={args={1, 2}},
jobstop={args=1},
jobwait={args={1, 2}},
@@ -208,6 +210,7 @@ return {
matchstr={args={2, 4}},
matchstrpos={args={2,4}},
max={args=1},
+ menu_get={args={1, 2}},
min={args=1},
mkdir={args={1, 3}},
mode={args={0, 1}},
@@ -272,6 +275,7 @@ return {
sockconnect={args={2,3}},
sort={args={1, 3}},
soundfold={args=1},
+ stdioopen={args=1},
spellbadword={args={0, 1}},
spellsuggest={args={1, 3}},
split={args={1, 3}},
@@ -309,6 +313,7 @@ return {
tempname={},
termopen={args={1, 2}},
test_garbagecollect_now={},
+ test_write_list_log={args=1},
timer_info={args={0,1}},
timer_pause={args=2},
timer_start={args={2,3}},
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 9c9c2c2dc8..17799b500c 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -60,8 +60,8 @@ static inline void create_special_dict(typval_T *const rettv,
dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE"));
type_di->di_tv.v_type = VAR_LIST;
type_di->di_tv.v_lock = VAR_UNLOCKED;
- type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type];
- type_di->di_tv.vval.v_list->lv_refcount++;
+ type_di->di_tv.vval.v_list = (list_T *)eval_msgpack_type_lists[type];
+ tv_list_ref(type_di->di_tv.vval.v_list);
tv_dict_add(dict, type_di);
dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL"));
val_di->di_tv = val;
@@ -120,16 +120,14 @@ static inline int json_decoder_pop(ValuesStackItem obj,
last_container = kv_last(*container_stack);
}
if (last_container.container.v_type == VAR_LIST) {
- if (last_container.container.vval.v_list->lv_len != 0
+ if (tv_list_len(last_container.container.vval.v_list) != 0
&& !obj.didcomma) {
EMSG2(_("E474: Expected comma before list item: %s"), val_location);
tv_clear(&obj.val);
return FAIL;
}
assert(last_container.special_val == NULL);
- listitem_T *obj_li = tv_list_item_alloc();
- obj_li->li_tv = obj.val;
- tv_list_append(last_container.container.vval.v_list, obj_li);
+ tv_list_append_owned_tv(last_container.container.vval.v_list, obj.val);
} else if (last_container.stack_index == kv_size(*stack) - 2) {
if (!obj.didcolon) {
EMSG2(_("E474: Expected colon before dictionary value: %s"),
@@ -152,14 +150,10 @@ static inline int json_decoder_pop(ValuesStackItem obj,
}
obj_di->di_tv = obj.val;
} else {
- list_T *const kv_pair = tv_list_alloc();
+ list_T *const kv_pair = tv_list_alloc(2);
tv_list_append_list(last_container.special_val, kv_pair);
- listitem_T *const key_li = tv_list_item_alloc();
- key_li->li_tv = key.val;
- tv_list_append(kv_pair, key_li);
- listitem_T *const val_li = tv_list_item_alloc();
- val_li->li_tv = obj.val;
- tv_list_append(kv_pair, val_li);
+ tv_list_append_owned_tv(kv_pair, key.val);
+ tv_list_append_owned_tv(kv_pair, obj.val);
}
} else {
// Object with key only
@@ -227,14 +221,19 @@ static inline int json_decoder_pop(ValuesStackItem obj,
/// Create a new special dictionary that ought to represent a MAP
///
/// @param[out] ret_tv Address where new special dictionary is saved.
+/// @param[in] len Expected number of items to be populated before list
+/// becomes accessible from VimL. It is still valid to
+/// underpopulate a list, value only controls how many elements
+/// will be allocated in advance. @see ListLenSpecials.
///
/// @return [allocated] list which should contain key-value pairs. Return value
/// may be safely ignored.
-list_T *decode_create_map_special_dict(typval_T *const ret_tv)
+list_T *decode_create_map_special_dict(typval_T *const ret_tv,
+ const ptrdiff_t len)
FUNC_ATTR_NONNULL_ALL
{
- list_T *const list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *const list = tv_list_alloc(len);
+ tv_list_ref(list);
create_special_dict(ret_tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@@ -266,11 +265,11 @@ typval_T decode_string(const char *const s, const size_t len,
{
assert(s != NULL || len == 0);
const bool really_hasnul = (hasnul == kNone
- ? memchr(s, NUL, len) != NULL
+ ? ((s != NULL) && (memchr(s, NUL, len) != NULL))
: (bool)hasnul);
if (really_hasnul) {
- list_T *const list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *const list = tv_list_alloc(kListLenMayKnow);
+ tv_list_ref(list);
typval_T tv;
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
.v_type = VAR_LIST,
@@ -291,7 +290,7 @@ typval_T decode_string(const char *const s, const size_t len,
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
.vval = { .v_string = (char_u *)(
- s_allocated ? (char *)s : xmemdupz(s, len)) },
+ (s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
};
}
}
@@ -738,8 +737,9 @@ json_decode_string_cycle_start:
} else if (last_container.special_val == NULL
? (last_container.container.v_type == VAR_DICT
? (DICT_LEN(last_container.container.vval.v_dict) == 0)
- : (last_container.container.vval.v_list->lv_len == 0))
- : (last_container.special_val->lv_len == 0)) {
+ : (tv_list_len(last_container.container.vval.v_list)
+ == 0))
+ : (tv_list_len(last_container.special_val) == 0)) {
emsgf(_("E474: Leading comma: %.*s"), LENP(p, e));
goto json_decode_string_fail;
}
@@ -848,8 +848,8 @@ json_decode_string_cycle_start:
break;
}
case '[': {
- list_T *list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *list = tv_list_alloc(kListLenMayKnow);
+ tv_list_ref(list);
typval_T tv = {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@@ -869,7 +869,7 @@ json_decode_string_cycle_start:
list_T *val_list = NULL;
if (next_map_special) {
next_map_special = false;
- val_list = decode_create_map_special_dict(&tv);
+ val_list = decode_create_map_special_dict(&tv, kListLenMayKnow);
} else {
dict_T *dict = tv_dict_alloc();
dict->dv_refcount++;
@@ -969,8 +969,8 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_number = (varnumber_T) mobj.via.u64 },
};
} else {
- list_T *const list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *const list = tv_list_alloc(4);
+ tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@@ -992,8 +992,8 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_number = (varnumber_T) mobj.via.i64 },
};
} else {
- list_T *const list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *const list = tv_list_alloc(4);
+ tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
@@ -1038,18 +1038,19 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
break;
}
case MSGPACK_OBJECT_ARRAY: {
- list_T *const list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *const list = tv_list_alloc((ptrdiff_t)mobj.via.array.size);
+ tv_list_ref(list);
*rettv = (typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
};
for (size_t i = 0; i < mobj.via.array.size; i++) {
- listitem_T *const li = tv_list_item_alloc();
- li->li_tv.v_type = VAR_UNKNOWN;
- tv_list_append(list, li);
- if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
+ // Not populated yet, need to create list item to push.
+ tv_list_append_owned_tv(list, (typval_T) { .v_type = VAR_UNKNOWN });
+ if (msgpack_to_vim(mobj.via.array.ptr[i],
+ TV_LIST_ITEM_TV(tv_list_last(list)))
+ == FAIL) {
return FAIL;
}
}
@@ -1089,30 +1090,33 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
break;
msgpack_to_vim_generic_map: {}
- list_T *const list = decode_create_map_special_dict(rettv);
+ list_T *const list = decode_create_map_special_dict(
+ rettv, (ptrdiff_t)mobj.via.map.size);
for (size_t i = 0; i < mobj.via.map.size; i++) {
- list_T *const kv_pair = tv_list_alloc();
+ list_T *const kv_pair = tv_list_alloc(2);
tv_list_append_list(list, kv_pair);
- listitem_T *const key_li = tv_list_item_alloc();
- key_li->li_tv.v_type = VAR_UNKNOWN;
- tv_list_append(kv_pair, key_li);
- listitem_T *const val_li = tv_list_item_alloc();
- val_li->li_tv.v_type = VAR_UNKNOWN;
- tv_list_append(kv_pair, val_li);
- if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
+
+ typval_T key_tv = { .v_type = VAR_UNKNOWN };
+ if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_tv) == FAIL) {
+ tv_clear(&key_tv);
return FAIL;
}
- if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
+ tv_list_append_owned_tv(kv_pair, key_tv);
+
+ typval_T val_tv = { .v_type = VAR_UNKNOWN };
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_tv) == FAIL) {
+ tv_clear(&val_tv);
return FAIL;
}
+ tv_list_append_owned_tv(kv_pair, val_tv);
}
break;
}
case MSGPACK_OBJECT_EXT: {
- list_T *const list = tv_list_alloc();
- list->lv_refcount++;
+ list_T *const list = tv_list_alloc(2);
+ tv_list_ref(list);
tv_list_append_number(list, mobj.via.ext.type);
- list_T *const ext_val_list = tv_list_alloc();
+ list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow);
tv_list_append_list(list, ext_val_list);
create_special_dict(rettv, kMPExt, ((typval_T) {
.v_type = VAR_LIST,
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index ef647b3ee4..9bae436e3d 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -28,6 +28,11 @@
#include "nvim/lib/kvec.h"
#include "nvim/eval/typval_encode.h"
+#ifdef __MINGW32__
+# undef fpclassify
+# define fpclassify __fpclassify
+#endif
+
#define ga_concat(a, b) ga_concat(a, (char_u *)b)
#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
@@ -53,17 +58,18 @@ int encode_list_write(void *const data, const char *const buf, const size_t len)
list_T *const list = (list_T *) data;
const char *const end = buf + len;
const char *line_end = buf;
- listitem_T *li = list->lv_last;
+ listitem_T *li = tv_list_last(list);
// Continue the last list element
if (li != NULL) {
line_end = xmemscan(buf, NL, len);
if (line_end != buf) {
const size_t line_length = (size_t)(line_end - buf);
- char *str = (char *)li->li_tv.vval.v_string;
+ char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string;
const size_t li_len = (str == NULL ? 0 : strlen(str));
- li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1);
- str = (char *)li->li_tv.vval.v_string + li_len;
+ TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc(
+ str, li_len + line_length + 1);
+ str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
memcpy(str, buf, line_length);
str[line_length] = 0;
memchrsub(str, NUL, NL, line_length);
@@ -135,21 +141,27 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
}
case kMPConvPairs:
case kMPConvList: {
- int idx = 0;
- const listitem_T *li;
- for (li = v.data.l.list->lv_first;
- li != NULL && li->li_next != v.data.l.li;
- li = li->li_next) {
- idx++;
- }
+ const int idx = (v.data.l.li == tv_list_first(v.data.l.list)
+ ? 0
+ : (v.data.l.li == NULL
+ ? tv_list_len(v.data.l.list) - 1
+ : (int)tv_list_idx_of_item(
+ v.data.l.list,
+ TV_LIST_ITEM_PREV(v.data.l.list,
+ v.data.l.li))));
+ const listitem_T *const li = (v.data.l.li == NULL
+ ? tv_list_last(v.data.l.list)
+ : TV_LIST_ITEM_PREV(v.data.l.list,
+ v.data.l.li));
if (v.type == kMPConvList
|| li == NULL
- || (li->li_tv.v_type != VAR_LIST
- && li->li_tv.vval.v_list->lv_len <= 0)) {
- vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
+ || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
+ && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) {
+ vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx);
ga_concat(&msg_ga, IObuff);
} else {
- typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
+ typval_T key_tv = *TV_LIST_ITEM_TV(
+ tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list));
char *const key = encode_tv2echo(&key_tv, NULL);
vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
xfree(key);
@@ -202,21 +214,17 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len,
FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
size_t len = 0;
- if (list != NULL) {
- for (const listitem_T *li = list->lv_first;
- li != NULL;
- li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING) {
- return false;
- }
- len++;
- if (li->li_tv.vval.v_string != 0) {
- len += STRLEN(li->li_tv.vval.v_string);
- }
+ TV_LIST_ITER_CONST(list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
+ return false;
}
- if (len) {
- len--;
+ len++;
+ if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) {
+ len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string);
}
+ });
+ if (len) {
+ len--;
}
*ret_len = len;
if (len == 0) {
@@ -253,31 +261,34 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
char *const buf_end = buf + nbuf;
char *p = buf;
while (p < buf_end) {
- assert(state->li_length == 0 || state->li->li_tv.vval.v_string != NULL);
+ assert(state->li_length == 0
+ || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
- assert(state->li->li_tv.vval.v_string != NULL);
- const char ch = (char)state->li->li_tv.vval.v_string[state->offset++];
+ assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
+ const char ch = (char)(
+ TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]);
*p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch);
}
if (p < buf_end) {
- state->li = state->li->li_next;
+ state->li = TV_LIST_ITEM_NEXT(state->list, state->li);
if (state->li == NULL) {
*read_bytes = (size_t) (p - buf);
return OK;
}
*p++ = NL;
- if (state->li->li_tv.v_type != VAR_STRING) {
- *read_bytes = (size_t) (p - buf);
+ if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) {
+ *read_bytes = (size_t)(p - buf);
return FAIL;
}
state->offset = 0;
- state->li_length = (state->li->li_tv.vval.v_string == NULL
+ state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL
? 0
- : STRLEN(state->li->li_tv.vval.v_string));
+ : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string));
}
}
*read_bytes = nbuf;
- return (state->offset < state->li_length || state->li->li_next != NULL
+ return ((state->offset < state->li_length
+ || TV_LIST_ITEM_NEXT(state->list, state->li) != NULL)
? NOTDONE
: OK);
}
@@ -340,7 +351,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
do { \
const char *const fun_ = (const char *)(fun); \
if (fun_ == NULL) { \
- EMSG2(_(e_intern2), "string(): NULL function name"); \
+ internal_error("string(): NULL function name"); \
ga_concat(gap, "function(NULL"); \
} else { \
ga_concat(gap, "function("); \
@@ -727,12 +738,11 @@ bool encode_check_json_key(const typval_T *const tv)
if (val_di->di_tv.vval.v_list == NULL) {
return true;
}
- for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first;
- li != NULL; li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING) {
+ TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
return false;
}
- }
+ });
return true;
}
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index 9bc665253b..ccea245ab3 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -33,9 +33,10 @@ int encode_vim_to_echo(garray_T *const packer,
/// Structure defining state for read_from_list()
typedef struct {
+ const list_T *const list; ///< List being currently read.
const listitem_T *li; ///< Item currently read.
- size_t offset; ///< Byte offset inside the read item.
- size_t li_length; ///< Length of the string inside the read item.
+ size_t offset; ///< Byte offset inside the read item.
+ size_t li_length; ///< Length of the string inside the read item.
} ListReaderState;
/// Initialize ListReaderState structure
@@ -43,11 +44,13 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
FUNC_ATTR_NONNULL_ALL
{
return (ListReaderState) {
- .li = list->lv_first,
+ .list = list,
+ .li = tv_list_first(list),
.offset = 0,
- .li_length = (list->lv_first->li_tv.vval.v_string == NULL
+ .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL
? 0
- : STRLEN(list->lv_first->li_tv.vval.v_string)),
+ : STRLEN(TV_LIST_ITEM_TV(
+ tv_list_first(list))->vval.v_string)),
};
}
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 4521085519..c8b550f902 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -3,6 +3,7 @@
#include <stdio.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
@@ -30,6 +31,7 @@
#include "nvim/message.h"
// TODO(ZyX-I): Move line_breakcheck out of misc1
#include "nvim/misc1.h" // For line_breakcheck
+#include "nvim/os/fileio.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
@@ -44,6 +46,70 @@ bool tv_in_free_unref_items = false;
const char *const tv_empty_string = "";
//{{{1 Lists
+//{{{2 List log
+#ifdef LOG_LIST_ACTIONS
+ListLog *list_log_first = NULL;
+ListLog *list_log_last = NULL;
+
+/// Write list log to the given file
+///
+/// @param[in] fname File to write log to. Will be appended to if already
+/// present.
+void list_write_log(const char *const fname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ FileDescriptor fp;
+ const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600);
+ if (fo_ret != 0) {
+ emsgf(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret));
+ return;
+ }
+ for (ListLog *chunk = list_log_first; chunk != NULL;) {
+ for (size_t i = 0; i < chunk->size; i++) {
+ char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2];
+ // act : hex " c:" len "[]" "\n\0"
+ const ListLogEntry entry = chunk->entries[i];
+ const size_t snp_len = (size_t)snprintf(
+ buf, sizeof(buf),
+ "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR
+ "\n",
+ entry.action, entry.l, entry.len, entry.li1, entry.li2);
+ assert(snp_len + 1 == sizeof(buf));
+ const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len);
+ if (fw_ret != (ptrdiff_t)snp_len) {
+ assert(fw_ret < 0);
+ if (i) {
+ memmove(chunk->entries, chunk->entries + i,
+ sizeof(chunk->entries[0]) * (chunk->size - i));
+ chunk->size -= i;
+ }
+ emsgf(_("E5143: Failed to write to file %s: %s"),
+ fname, os_strerror((int)fw_ret));
+ return;
+ }
+ }
+ list_log_first = chunk->next;
+ xfree(chunk);
+ chunk = list_log_first;
+ }
+ const int fc_ret = file_close(&fp, true);
+ if (fc_ret != 0) {
+ emsgf(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret));
+ }
+}
+
+#ifdef EXITFREE
+/// Free list log
+void list_free_log(void)
+{
+ for (ListLog *chunk = list_log_first; chunk != NULL;) {
+ list_log_first = chunk->next;
+ xfree(chunk);
+ chunk = list_log_first;
+ }
+}
+#endif
+#endif
//{{{2 List item
/// Allocate a list item
@@ -52,35 +118,29 @@ const char *const tv_empty_string = "";
/// and specifically set lv_lock.
///
/// @return [allocated] new list item.
-listitem_T *tv_list_item_alloc(void)
+static listitem_T *tv_list_item_alloc(void)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC
{
return xmalloc(sizeof(listitem_T));
}
-/// Free a list item
-///
-/// Also clears the value. Does not touch watchers.
-///
-/// @param[out] item Item to free.
-void tv_list_item_free(listitem_T *const item)
- FUNC_ATTR_NONNULL_ALL
-{
- tv_clear(&item->li_tv);
- xfree(item);
-}
-
/// Remove a list item from a List and free it
///
/// Also clears the value.
///
/// @param[out] l List to remove item from.
/// @param[in,out] item Item to remove.
-void tv_list_item_remove(list_T *const l, listitem_T *const item)
+///
+/// @return Pointer to the list item just after removed one, NULL if removed
+/// item was the last one.
+listitem_T *tv_list_item_remove(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- tv_list_remove_items(l, item, item);
- tv_list_item_free(item);
+ listitem_T *const next_item = TV_LIST_ITEM_NEXT(l, item);
+ tv_list_drop_items(l, item, item);
+ tv_clear(TV_LIST_ITEM_TV(item));
+ xfree(item);
+ return next_item;
}
//{{{2 List watchers
@@ -137,8 +197,14 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
///
/// Caller should take care of the reference count.
///
+/// @param[in] len Expected number of items to be populated before list
+/// becomes accessible from VimL. It is still valid to
+/// underpopulate a list, value only controls how many elements
+/// will be allocated in advance. Currently does nothing.
+/// @see ListLenSpecials.
+///
/// @return [allocated] new list.
-list_T *tv_list_alloc(void)
+list_T *tv_list_alloc(const ptrdiff_t len)
FUNC_ATTR_NONNULL_RET
{
list_T *const list = xcalloc(1, sizeof(list_T));
@@ -150,15 +216,59 @@ list_T *tv_list_alloc(void)
list->lv_used_prev = NULL;
list->lv_used_next = gc_first_list;
gc_first_list = list;
+ list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
return list;
}
+/// Initialize a static list with 10 items
+///
+/// @param[out] sl Static list to initialize.
+void tv_list_init_static10(staticList10_T *const sl)
+ FUNC_ATTR_NONNULL_ALL
+{
+#define SL_SIZE ARRAY_SIZE(sl->sl_items)
+ list_T *const l = &sl->sl_list;
+
+ memset(sl, 0, sizeof(staticList10_T));
+ l->lv_first = &sl->sl_items[0];
+ l->lv_last = &sl->sl_items[SL_SIZE - 1];
+ l->lv_refcount = DO_NOT_FREE_CNT;
+ tv_list_set_lock(l, VAR_FIXED);
+ sl->sl_list.lv_len = 10;
+
+ sl->sl_items[0].li_prev = NULL;
+ sl->sl_items[0].li_next = &sl->sl_items[1];
+ sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2];
+ sl->sl_items[SL_SIZE - 1].li_next = NULL;
+
+ for (size_t i = 1; i < SL_SIZE - 1; i++) {
+ listitem_T *const li = &sl->sl_items[i];
+ li->li_prev = li - 1;
+ li->li_next = li + 1;
+ }
+ list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1],
+ "s10init");
+#undef SL_SIZE
+}
+
+/// Initialize static list with undefined number of elements
+///
+/// @param[out] l List to initialize.
+void tv_list_init_static(list_T *const l)
+ FUNC_ATTR_NONNULL_ALL
+{
+ memset(l, 0, sizeof(*l));
+ l->lv_refcount = DO_NOT_FREE_CNT;
+ list_log(l, NULL, NULL, "sinit");
+}
+
/// Free items contained in a list
///
/// @param[in,out] l List to clear.
void tv_list_free_contents(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, NULL, NULL, "freecont");
for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) {
// Remove the item before deleting it.
l->lv_first = item->li_next;
@@ -188,6 +298,7 @@ void tv_list_free_list(list_T *const l)
if (l->lv_used_next != NULL) {
l->lv_used_next->lv_used_prev = l->lv_used_prev;
}
+ list_log(l, NULL, NULL, "freelist");
xfree(l);
}
@@ -221,17 +332,18 @@ void tv_list_unref(list_T *const l)
//{{{2 Add/remove
-/// Remove items "item" to "item2" from list "l".
+/// Remove items "item" to "item2" from list "l"
///
/// @warning Does not free the listitem or the value!
///
/// @param[out] l List to remove from.
/// @param[in] item First item to remove.
/// @param[in] item2 Last item to remove.
-void tv_list_remove_items(list_T *const l, listitem_T *const item,
- listitem_T *const item2)
+void tv_list_drop_items(list_T *const l, listitem_T *const item,
+ listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, item, item2, "drop");
// Notify watchers.
for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) {
l->lv_len--;
@@ -249,6 +361,51 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item,
item->li_prev->li_next = item2->li_next;
}
l->lv_idx_item = NULL;
+ list_log(l, l->lv_first, l->lv_last, "afterdrop");
+}
+
+/// Like tv_list_drop_items, but also frees all removed items
+void tv_list_remove_items(list_T *const l, listitem_T *const item,
+ listitem_T *const item2)
+ FUNC_ATTR_NONNULL_ALL
+{
+ list_log(l, item, item2, "remove");
+ tv_list_drop_items(l, item, item2);
+ for (listitem_T *li = item;;) {
+ tv_clear(TV_LIST_ITEM_TV(li));
+ listitem_T *const nli = li->li_next;
+ xfree(li);
+ if (li == item2) {
+ break;
+ }
+ li = nli;
+ }
+}
+
+/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l"
+///
+/// @param[out] l List to move from.
+/// @param[in] item First item to move.
+/// @param[in] item2 Last item to move.
+/// @param[out] tgt_l List to move to.
+/// @param[in] cnt Number of items moved.
+void tv_list_move_items(list_T *const l, listitem_T *const item,
+ listitem_T *const item2, list_T *const tgt_l,
+ const int cnt)
+ FUNC_ATTR_NONNULL_ALL
+{
+ list_log(l, item, item2, "move");
+ tv_list_drop_items(l, item, item2);
+ item->li_prev = tgt_l->lv_last;
+ item2->li_next = NULL;
+ if (tgt_l->lv_last == NULL) {
+ tgt_l->lv_first = item;
+ } else {
+ tgt_l->lv_last->li_next = item;
+ }
+ tgt_l->lv_last = item2;
+ tgt_l->lv_len += cnt;
+ list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt");
}
/// Insert list item
@@ -277,6 +434,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni,
}
item->li_prev = ni;
l->lv_len++;
+ list_log(l, ni, item, "insert");
}
}
@@ -303,6 +461,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv,
void tv_list_append(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, item, NULL, "append");
if (l->lv_last == NULL) {
// empty list
l->lv_first = item;
@@ -326,7 +485,19 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv)
FUNC_ATTR_NONNULL_ALL
{
listitem_T *const li = tv_list_item_alloc();
- tv_copy(tv, &li->li_tv);
+ tv_copy(tv, TV_LIST_ITEM_TV(li));
+ tv_list_append(l, li);
+}
+
+/// Like tv_list_append_tv(), but tv is moved to a list
+///
+/// This means that it is no longer valid to use contents of the typval_T after
+/// function exits.
+void tv_list_append_owned_tv(list_T *const l, typval_T tv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ listitem_T *const li = tv_list_item_alloc();
+ *TV_LIST_ITEM_TV(li) = tv;
tv_list_append(l, li);
}
@@ -334,33 +505,29 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv)
///
/// @param[out] l List to append to.
/// @param[in,out] itemlist List to append. Reference count is increased.
-void tv_list_append_list(list_T *const list, list_T *const itemlist)
+void tv_list_append_list(list_T *const l, list_T *const itemlist)
FUNC_ATTR_NONNULL_ARG(1)
{
- listitem_T *const li = tv_list_item_alloc();
-
- li->li_tv.v_type = VAR_LIST;
- li->li_tv.v_lock = VAR_UNLOCKED;
- li->li_tv.vval.v_list = itemlist;
- tv_list_append(list, li);
- if (itemlist != NULL) {
- itemlist->lv_refcount++;
- }
+ tv_list_append_owned_tv(l, (typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_list = itemlist,
+ });
+ tv_list_ref(itemlist);
}
/// Append a dictionary to a list
///
/// @param[out] l List to append to.
/// @param[in,out] dict Dictionary to append. Reference count is increased.
-void tv_list_append_dict(list_T *const list, dict_T *const dict)
+void tv_list_append_dict(list_T *const l, dict_T *const dict)
FUNC_ATTR_NONNULL_ARG(1)
{
- listitem_T *const li = tv_list_item_alloc();
-
- li->li_tv.v_type = VAR_DICT;
- li->li_tv.v_lock = VAR_UNLOCKED;
- li->li_tv.vval.v_dict = dict;
- tv_list_append(list, li);
+ tv_list_append_owned_tv(l, (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_dict = dict,
+ });
if (dict != NULL) {
dict->dv_refcount++;
}
@@ -374,17 +541,18 @@ void tv_list_append_dict(list_T *const list, dict_T *const dict)
/// case string is considered to be usual zero-terminated
/// string or NULL “empty” string.
void tv_list_append_string(list_T *const l, const char *const str,
- const ptrdiff_t len)
+ const ssize_t len)
FUNC_ATTR_NONNULL_ARG(1)
{
- if (str == NULL) {
- assert(len == 0 || len == -1);
- tv_list_append_allocated_string(l, NULL);
- } else {
- tv_list_append_allocated_string(l, (len >= 0
- ? xmemdupz(str, (size_t)len)
- : xstrdup(str)));
- }
+ tv_list_append_owned_tv(l, (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = (str == NULL
+ ? NULL
+ : (len >= 0
+ ? xmemdupz(str, (size_t)len)
+ : xstrdup(str))),
+ });
}
/// Append given string to the list
@@ -396,12 +564,11 @@ void tv_list_append_string(list_T *const l, const char *const str,
void tv_list_append_allocated_string(list_T *const l, char *const str)
FUNC_ATTR_NONNULL_ARG(1)
{
- listitem_T *const li = tv_list_item_alloc();
-
- tv_list_append(l, li);
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = VAR_UNLOCKED;
- li->li_tv.vval.v_string = (char_u *)str;
+ tv_list_append_owned_tv(l, (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = (char_u *)str,
+ });
}
/// Append number to the list
@@ -411,11 +578,11 @@ void tv_list_append_allocated_string(list_T *const l, char *const str)
/// listitem_T.
void tv_list_append_number(list_T *const l, const varnumber_T n)
{
- listitem_T *const li = tv_list_item_alloc();
- li->li_tv.v_type = VAR_NUMBER;
- li->li_tv.v_lock = VAR_UNLOCKED;
- li->li_tv.vval.v_number = n;
- tv_list_append(l, li);
+ tv_list_append_owned_tv(l, (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_number = n,
+ });
}
//{{{2 Operations on the whole list
@@ -438,34 +605,36 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig,
return NULL;
}
- list_T *copy = tv_list_alloc();
+ list_T *copy = tv_list_alloc(tv_list_len(orig));
+ tv_list_ref(copy);
if (copyID != 0) {
// Do this before adding the items, because one of the items may
// refer back to this list.
orig->lv_copyID = copyID;
orig->lv_copylist = copy;
}
- listitem_T *item;
- for (item = orig->lv_first; item != NULL && !got_int;
- item = item->li_next) {
+ TV_LIST_ITER(orig, item, {
+ if (got_int) {
+ break;
+ }
listitem_T *const ni = tv_list_item_alloc();
if (deep) {
- if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) {
+ if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni),
+ deep, copyID) == FAIL) {
xfree(ni);
- break;
+ goto tv_list_copy_error;
}
} else {
- tv_copy(&item->li_tv, &ni->li_tv);
+ tv_copy(TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni));
}
tv_list_append(copy, ni);
- }
- copy->lv_refcount++;
- if (item != NULL) {
- tv_list_unref(copy);
- copy = NULL;
- }
+ });
return copy;
+
+tv_list_copy_error:
+ tv_list_unref(copy);
+ return NULL;
}
/// Extend first list with the second
@@ -475,17 +644,17 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig,
/// @param[in] bef If not NULL, extends before this item.
void tv_list_extend(list_T *const l1, list_T *const l2,
listitem_T *const bef)
- FUNC_ATTR_NONNULL_ARG(1, 2)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- int todo = l2->lv_len;
+ int todo = tv_list_len(l2);
listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev);
listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next);
// We also quit the loop when we have inserted the original item count of
// the list, avoid a hang when we extend a list with itself.
- for (listitem_T *item = l2->lv_first
- ; item != NULL && --todo >= 0
+ for (listitem_T *item = tv_list_first(l2)
+ ; item != NULL && todo--
; item = (item == befbef ? saved_next : item->li_next)) {
- tv_list_insert_tv(l1, &item->li_tv, bef);
+ tv_list_insert_tv(l1, TV_LIST_ITEM_TV(item), bef);
}
}
@@ -540,13 +709,15 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
{
size_t sumlen = 0;
bool first = true;
- listitem_T *item;
// Stringify each item in the list.
- for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) {
+ TV_LIST_ITER(l, item, {
+ if (got_int) {
+ break;
+ }
char *s;
size_t len;
- s = encode_tv2echo(&item->li_tv, &len);
+ s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len);
if (s == NULL) {
return FAIL;
}
@@ -557,7 +728,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
p->tofree = p->s = (char_u *)s;
line_breakcheck();
- }
+ });
// Allocate result buffer with its total size, avoid re-allocation and
// multiple copy operations. Add 2 for a tailing ']' and NUL.
@@ -591,16 +762,16 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
///
/// @return OK in case of success, FAIL otherwise.
int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(1)
{
- if (l->lv_len < 1) {
+ if (!tv_list_len(l)) {
return OK;
}
garray_T join_ga;
int retval;
- ga_init(&join_ga, (int)sizeof(Join), l->lv_len);
+ ga_init(&join_ga, (int)sizeof(Join), tv_list_len(l));
retval = list_join_inner(gap, l, sep, &join_ga);
#define FREE_JOIN_TOFREE(join) xfree((join)->tofree)
@@ -632,11 +803,13 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
return false;
}
- listitem_T *item1 = l1->lv_first;
- listitem_T *item2 = l2->lv_first;
+ listitem_T *item1 = tv_list_first(l1);
+ listitem_T *item2 = tv_list_first(l2);
for (; item1 != NULL && item2 != NULL
- ; item1 = item1->li_next, item2 = item2->li_next) {
- if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) {
+ ; (item1 = TV_LIST_ITEM_NEXT(l1, item1),
+ item2 = TV_LIST_ITEM_NEXT(l2, item2))) {
+ if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic,
+ recursive)) {
return false;
}
}
@@ -644,6 +817,74 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
return true;
}
+/// Reverse list in-place
+///
+/// @param[in,out] l List to reverse.
+void tv_list_reverse(list_T *const l)
+{
+ if (tv_list_len(l) <= 1) {
+ return;
+ }
+ list_log(l, NULL, NULL, "reverse");
+#define SWAP(a, b) \
+ do { \
+ tmp = a; \
+ a = b; \
+ b = tmp; \
+ } while (0)
+ listitem_T *tmp;
+
+ SWAP(l->lv_first, l->lv_last);
+ for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
+ SWAP(li->li_next, li->li_prev);
+ }
+#undef SWAP
+
+ l->lv_idx = l->lv_len - l->lv_idx - 1;
+}
+
+// FIXME Add unit tests for tv_list_item_sort().
+
+/// Sort list using libc qsort
+///
+/// @param[in,out] l List to sort, will be sorted in-place.
+/// @param ptrs Preallocated array of items to sort, must have at least
+/// tv_list_len(l) entries. Should not be initialized.
+/// @param[in] item_compare_func Function used to compare list items.
+/// @param errp Location where information about whether error occurred is
+/// saved by item_compare_func. If boolean there appears to be
+/// true list will not be modified. Must be initialized to false
+/// by the caller.
+void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
+ const ListSorter item_compare_func,
+ bool *errp)
+ FUNC_ATTR_NONNULL_ARG(3, 4)
+{
+ const int len = tv_list_len(l);
+ if (len <= 1) {
+ return;
+ }
+ list_log(l, NULL, NULL, "sort");
+ int i = 0;
+ TV_LIST_ITER(l, li, {
+ ptrs[i].item = li;
+ ptrs[i].idx = i;
+ i++;
+ });
+ // Sort the array with item pointers.
+ qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
+ if (!(*errp)) {
+ // Clear the list and append the items in the sorted order.
+ l->lv_first = NULL;
+ l->lv_last = NULL;
+ l->lv_idx_item = NULL;
+ l->lv_len = 0;
+ for (i = 0; i < len; i++) {
+ tv_list_append(l, ptrs[i].item);
+ }
+ }
+}
+
//{{{2 Indexing/searching
/// Locate item with a given index in a list and return it
@@ -662,13 +903,8 @@ listitem_T *tv_list_find(list_T *const l, int n)
return NULL;
}
- // Negative index is relative to the end.
- if (n < 0) {
- n = l->lv_len + n;
- }
-
- // Check for index out of range.
- if (n < 0 || n >= l->lv_len) {
+ n = tv_list_uidx(l, n);
+ if (n == -1) {
return NULL;
}
@@ -717,6 +953,7 @@ listitem_T *tv_list_find(list_T *const l, int n)
// Cache the used index.
l->lv_idx = idx;
l->lv_idx_item = item;
+ list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find");
return item;
}
@@ -740,7 +977,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error)
}
return -1;
}
- return tv_get_number_chk(&li->li_tv, ret_error);
+ return tv_get_number_chk(TV_LIST_ITEM_TV(li), ret_error);
}
/// Get list item l[n] as a string
@@ -757,7 +994,7 @@ const char *tv_list_find_str(list_T *const l, const int n)
emsgf(_(e_listidx), (int64_t)n);
return NULL;
}
- return tv_get_string(&li->li_tv);
+ return tv_get_string(TV_LIST_ITEM_TV(li));
}
/// Locate item in a list and return its index
@@ -772,15 +1009,14 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item)
if (l == NULL) {
return -1;
}
- long idx = 0;
- const listitem_T *li;
- for (li = l->lv_first; li != NULL && li != item; li = li->li_next) {
+ int idx = 0;
+ TV_LIST_ITER_CONST(l, li, {
+ if (li == item) {
+ return idx;
+ }
idx++;
- }
- if (li == NULL) {
- return -1;
- }
- return idx;
+ });
+ return -1;
}
//{{{1 Dictionaries
@@ -824,7 +1060,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern,
/// @param[in] cb2 Second callback to check.
///
/// @return True if they are equal, false otherwise.
-bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2)
+bool tv_callback_equal(const Callback *cb1, const Callback *cb2)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
if (cb1->type != cb2->type) {
@@ -843,10 +1079,31 @@ bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2)
return true;
}
}
- assert(false);
+ abort();
return false;
}
+/// Unref/free callback
+void callback_free(Callback *callback)
+ FUNC_ATTR_NONNULL_ALL
+{
+ switch (callback->type) {
+ case kCallbackFuncref: {
+ func_unref(callback->data.funcref);
+ xfree(callback->data.funcref);
+ break;
+ }
+ case kCallbackPartial: {
+ partial_unref(callback->data.partial);
+ break;
+ }
+ case kCallbackNone: {
+ break;
+ }
+ }
+ callback->type = kCallbackNone;
+}
+
/// Remove watcher from a dictionary
///
/// @param dict Dictionary to remove watcher from.
@@ -1318,7 +1575,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key,
item->di_tv.v_lock = VAR_UNLOCKED;
item->di_tv.v_type = VAR_LIST;
item->di_tv.vval.v_list = list;
- list->lv_refcount++;
+ tv_list_ref(list);
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
@@ -1374,6 +1631,29 @@ int tv_dict_add_nr(dict_T *const d, const char *const key,
return OK;
}
+/// Add a special entry to dictionary
+///
+/// @param[out] d Dictionary to add entry to.
+/// @param[in] key Key to add.
+/// @param[in] key_len Key length.
+/// @param[in] val SpecialVarValue to add.
+///
+/// @return OK in case of success, FAIL when key already exists.
+int tv_dict_add_special(dict_T *const d, const char *const key,
+ const size_t key_len, SpecialVarValue val)
+{
+ dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
+
+ item->di_tv.v_lock = VAR_UNLOCKED;
+ item->di_tv.v_type = VAR_SPECIAL;
+ item->di_tv.vval.v_special = val;
+ if (tv_dict_add(d, item) == FAIL) {
+ tv_dict_item_free(item);
+ return FAIL;
+ }
+ return OK;
+}
+
/// Add a string entry to dictionary
///
/// @param[out] d Dictionary to add entry to.
@@ -1387,11 +1667,32 @@ int tv_dict_add_str(dict_T *const d,
const char *const val)
FUNC_ATTR_NONNULL_ALL
{
+ return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val));
+}
+
+/// Add a string entry to dictionary
+///
+/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of
+/// creating a new copy.
+///
+/// @warning String will be freed even in case addition fails.
+///
+/// @param[out] d Dictionary to add entry to.
+/// @param[in] key Key to add.
+/// @param[in] key_len Key length.
+/// @param[in] val String to add.
+///
+/// @return OK in case of success, FAIL when key already exists.
+int tv_dict_add_allocated_str(dict_T *const d,
+ const char *const key, const size_t key_len,
+ char *const val)
+ FUNC_ATTR_NONNULL_ALL
+{
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
item->di_tv.v_lock = VAR_UNLOCKED;
item->di_tv.v_type = VAR_STRING;
- item->di_tv.vval.v_string = (char_u *)xstrdup(val);
+ item->di_tv.vval.v_string = (char_u *)val;
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
@@ -1603,16 +1904,20 @@ void tv_dict_set_keys_readonly(dict_T *const dict)
/// Also sets reference count.
///
/// @param[out] ret_tv Structure where list is saved.
+/// @param[in] len Expected number of items to be populated before list
+/// becomes accessible from VimL. It is still valid to
+/// underpopulate a list, value only controls how many elements
+/// will be allocated in advance. @see ListLenSpecials.
///
/// @return [allocated] pointer to the created list.
-list_T *tv_list_alloc_ret(typval_T *const ret_tv)
+list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
FUNC_ATTR_NONNULL_ALL
{
- list_T *const l = tv_list_alloc();
+ list_T *const l = tv_list_alloc(len);
ret_tv->vval.v_list = l;
ret_tv->v_type = VAR_LIST;
ret_tv->v_lock = VAR_UNLOCKED;
- l->lv_refcount++;
+ tv_list_ref(l);
return l;
}
@@ -1729,14 +2034,20 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
tv->v_lock = VAR_UNLOCKED; \
} while (0)
+static inline void _nothing_conv_empty_dict(typval_T *const tv,
+ dict_T **const dictp)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(2)
+{
+ tv_dict_unref(*dictp);
+ *dictp = NULL;
+ if (tv != NULL) {
+ tv->v_lock = VAR_UNLOCKED;
+ }
+}
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
do { \
assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
- tv_dict_unref((dict_T *)dict); \
- *((dict_T **)&dict) = NULL; \
- if (tv != NULL) { \
- ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
- } \
+ _nothing_conv_empty_dict(tv, ((dict_T **)&dict)); \
} while (0)
static inline int _nothing_conv_real_list_after_start(
@@ -1933,7 +2244,7 @@ void tv_free(typval_T *tv)
///
/// @param[in] from Location to copy from.
/// @param[out] to Location to copy to.
-void tv_copy(typval_T *const from, typval_T *const to)
+void tv_copy(const typval_T *const from, typval_T *const to)
{
to->v_type = from->v_type;
to->v_lock = VAR_UNLOCKED;
@@ -1961,9 +2272,7 @@ void tv_copy(typval_T *const from, typval_T *const to)
break;
}
case VAR_LIST: {
- if (from->vval.v_list != NULL) {
- to->vval.v_list->lv_refcount++;
- }
+ tv_list_ref(to->vval.v_list);
break;
}
case VAR_DICT: {
@@ -2019,9 +2328,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
CHANGE_LOCK(lock, l->lv_lock);
if (deep < 0 || deep > 1) {
// Recursive: lock/unlock the items the List contains.
- for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
- tv_item_lock(&li->li_tv, deep - 1, lock);
- }
+ TV_LIST_ITER(l, li, {
+ tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock);
+ });
}
}
break;
@@ -2057,6 +2366,8 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
/// Check whether VimL value is locked itself or refers to a locked container
///
+/// @warning Fixed container is not the same as locked.
+///
/// @param[in] tv Value to check.
///
/// @return True if value is locked, false otherwise.
@@ -2065,8 +2376,7 @@ bool tv_islocked(const typval_T *const tv)
{
return ((tv->v_lock == VAR_LOCKED)
|| (tv->v_type == VAR_LIST
- && tv->vval.v_list != NULL
- && (tv->vval.v_list->lv_lock == VAR_LOCKED))
+ && (tv_list_locked(tv->vval.v_list) == VAR_LOCKED))
|| (tv->v_type == VAR_DICT
&& tv->vval.v_dict != NULL
&& (tv->vval.v_dict->dv_lock == VAR_LOCKED)));
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 3f8ed3b3f9..2272a580d6 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -6,6 +6,8 @@
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
+#include <assert.h>
+#include <limits.h>
#include "nvim/types.h"
#include "nvim/hashtab.h"
@@ -18,6 +20,9 @@
#include "nvim/gettext.h"
#include "nvim/message.h"
#include "nvim/macros.h"
+#ifdef LOG_LIST_ACTIONS
+# include "nvim/memory.h"
+#endif
/// Type used for VimL VAR_NUMBER values
typedef int64_t varnumber_T;
@@ -26,6 +31,28 @@ typedef uint64_t uvarnumber_T;
/// Type used for VimL VAR_FLOAT values
typedef double float_T;
+/// Refcount for dict or list that should not be freed
+enum { DO_NOT_FREE_CNT = (INT_MAX / 2) };
+
+/// Additional values for tv_list_alloc() len argument
+enum {
+ /// List length is not known in advance
+ ///
+ /// To be used when there is neither a way to know how many elements will be
+ /// needed nor are any educated guesses.
+ kListLenUnknown = -1,
+ /// List length *should* be known, but is actually not
+ ///
+ /// All occurrences of this value should be eventually removed. This is for
+ /// the case when the only reason why list length is not known is that it
+ /// would be hard to code without refactoring, but refactoring is needed.
+ kListLenShouldKnow = -2,
+ /// List length may be known in advance, but it requires too much effort
+ ///
+ /// To be used when it looks impractical to determine list length.
+ kListLenMayKnow = -3,
+} ListLenSpecials;
+
/// Maximal possible value of varnumber_T variable
#define VARNUMBER_MAX INT64_MAX
#define UVARNUMBER_MAX UINT64_MAX
@@ -43,7 +70,7 @@ typedef struct partial_S partial_T;
typedef struct ufunc ufunc_T;
typedef enum {
- kCallbackNone,
+ kCallbackNone = 0,
kCallbackFuncref,
kCallbackPartial,
} CallbackType;
@@ -150,12 +177,26 @@ struct listvar_S {
list_T *lv_used_prev; ///< Previous list in used lists list.
};
-// Static list with 10 items. Use init_static_list() to initialize.
+// Static list with 10 items. Use tv_list_init_static10() to initialize.
typedef struct {
list_T sl_list; // must be first
listitem_T sl_items[10];
} staticList10_T;
+#define TV_LIST_STATIC10_INIT { \
+ .sl_list = { \
+ .lv_first = NULL, \
+ .lv_last = NULL, \
+ .lv_refcount = 0, \
+ .lv_len = 0, \
+ .lv_watch = NULL, \
+ .lv_idx_item = NULL, \
+ .lv_lock = VAR_FIXED, \
+ .lv_used_next = NULL, \
+ .lv_used_prev = NULL, \
+ }, \
+ }
+
// Structure to hold an item of a Dictionary.
// Also used for a variable.
// The key is copied into "di_key" to avoid an extra alloc/free for it.
@@ -165,11 +206,11 @@ struct dictitem_S {
char_u di_key[1]; ///< key (actually longer!)
};
-#define TV_DICTITEM_STRUCT(KEY_LEN) \
+#define TV_DICTITEM_STRUCT(...) \
struct { \
typval_T di_tv; /* Structure that holds scope dictionary itself. */ \
uint8_t di_flags; /* Flags. */ \
- char_u di_key[KEY_LEN]; /* Key value. */ \
+ char_u di_key[__VA_ARGS__]; /* Key value. */ \
}
/// Structure to hold a scope dictionary
@@ -277,6 +318,104 @@ typedef struct list_stack_S {
struct list_stack_S *prev;
} list_stack_T;
+/// Structure representing one list item, used for sort array.
+typedef struct {
+ listitem_T *item; ///< Sorted list item.
+ int idx; ///< Sorted list item index.
+} ListSortItem;
+
+typedef int (*ListSorter)(const void *, const void *);
+
+#ifdef LOG_LIST_ACTIONS
+
+/// List actions log entry
+typedef struct {
+ uintptr_t l; ///< List log entry belongs to.
+ uintptr_t li1; ///< First list item log entry belongs to, if applicable.
+ uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
+ int len; ///< List length when log entry was created.
+ const char *action; ///< Logged action.
+} ListLogEntry;
+
+typedef struct list_log ListLog;
+
+/// List actions log
+struct list_log {
+ ListLog *next; ///< Next chunk or NULL.
+ size_t capacity; ///< Number of entries in current chunk.
+ size_t size; ///< Current chunk size.
+ ListLogEntry entries[]; ///< Actual log entries.
+};
+
+extern ListLog *list_log_first; ///< First list log chunk, NULL if missing
+extern ListLog *list_log_last; ///< Last list log chunk
+
+static inline ListLog *list_log_alloc(const size_t size)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Allocate a new log chunk and update globals
+///
+/// @param[in] size Number of entries in a new chunk.
+///
+/// @return [allocated] Newly allocated chunk.
+static inline ListLog *list_log_new(const size_t size)
+{
+ ListLog *ret = xmalloc(offsetof(ListLog, entries)
+ + size * sizeof(ret->entries[0]));
+ ret->size = 0;
+ ret->capacity = size;
+ ret->next = NULL;
+ if (list_log_first == NULL) {
+ list_log_first = ret;
+ } else {
+ list_log_last->next = ret;
+ }
+ list_log_last = ret;
+ return ret;
+}
+
+static inline void list_log(const list_T *const l,
+ const listitem_T *const li1,
+ const listitem_T *const li2,
+ const char *const action)
+ REAL_FATTR_ALWAYS_INLINE;
+
+/// Add new entry to log
+///
+/// If last chunk was filled it uses twice as much memory to allocate the next
+/// chunk.
+///
+/// @param[in] l List to which entry belongs.
+/// @param[in] li1 List item 1.
+/// @param[in] li2 List item 2, often used for integers and not list items.
+/// @param[in] action Logged action.
+static inline void list_log(const list_T *const l,
+ const listitem_T *const li1,
+ const listitem_T *const li2,
+ const char *const action)
+{
+ ListLog *tgt;
+ if (list_log_first == NULL) {
+ tgt = list_log_new(128);
+ } else if (list_log_last->size == list_log_last->capacity) {
+ tgt = list_log_new(list_log_last->capacity * 2);
+ } else {
+ tgt = list_log_last;
+ }
+ tgt->entries[tgt->size++] = (ListLogEntry) {
+ .l = (uintptr_t)l,
+ .li1 = (uintptr_t)li1,
+ .li2 = (uintptr_t)li2,
+ .len = (l == NULL ? 0 : l->lv_len),
+ .action = action,
+ };
+}
+#else
+# define list_log(...)
+# define list_write_log(...)
+# define list_free_log()
+#endif
+
// In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item.
@@ -284,20 +423,181 @@ typedef struct list_stack_S {
#define TV_DICT_HI2DI(hi) \
((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key)))
-static inline long tv_list_len(const list_T *const l)
+/// Increase reference count for a given list
+///
+/// Does nothing for NULL lists.
+///
+/// @param[in] l List to modify.
+static inline void tv_list_ref(list_T *const l)
+{
+ if (l == NULL) {
+ return;
+ }
+ l->lv_refcount++;
+}
+
+static inline VarLockStatus tv_list_locked(const list_T *const l)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Get list lock status
+///
+/// Returns VAR_FIXED for NULL lists.
+///
+/// @param[in] l List to check.
+static inline VarLockStatus tv_list_locked(const list_T *const l)
+{
+ if (l == NULL) {
+ return VAR_FIXED;
+ }
+ return l->lv_lock;
+}
+
+/// Set list lock status
+///
+/// May only “set” VAR_FIXED for NULL lists.
+///
+/// @param[out] l List to modify.
+/// @param[in] lock New lock status.
+static inline void tv_list_set_lock(list_T *const l,
+ const VarLockStatus lock)
+{
+ if (l == NULL) {
+ assert(lock == VAR_FIXED);
+ return;
+ }
+ l->lv_lock = lock;
+}
+
+/// Set list copyID
+///
+/// Does not expect NULL list, be careful.
+///
+/// @param[out] l List to modify.
+/// @param[in] copyid New copyID.
+static inline void tv_list_set_copyid(list_T *const l,
+ const int copyid)
+ FUNC_ATTR_NONNULL_ALL
+{
+ l->lv_copyID = copyid;
+}
+
+static inline int tv_list_len(const list_T *const l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the number of items in a list
///
/// @param[in] l List to check.
-static inline long tv_list_len(const list_T *const l)
+static inline int tv_list_len(const list_T *const l)
{
+ list_log(l, NULL, NULL, "len");
if (l == NULL) {
return 0;
}
return l->lv_len;
}
+static inline int tv_list_copyid(const list_T *const l)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
+
+/// Get list copyID
+///
+/// Does not expect NULL list, be careful.
+///
+/// @param[in] l List to check.
+static inline int tv_list_copyid(const list_T *const l)
+{
+ return l->lv_copyID;
+}
+
+static inline list_T *tv_list_latest_copy(const list_T *const l)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
+
+/// Get latest list copy
+///
+/// Gets lv_copylist field assigned by tv_list_copy() earlier.
+///
+/// Does not expect NULL list, be careful.
+///
+/// @param[in] l List to check.
+static inline list_T *tv_list_latest_copy(const list_T *const l)
+{
+ return l->lv_copylist;
+}
+
+static inline int tv_list_uidx(const list_T *const l, int n)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Normalize index: that is, return either -1 or non-negative index
+///
+/// @param[in] l List to index. Used to get length.
+/// @param[in] n List index, possibly negative.
+///
+/// @return -1 or list index in range [0, tv_list_len(l)).
+static inline int tv_list_uidx(const list_T *const l, int n)
+{
+ // Negative index is relative to the end.
+ if (n < 0) {
+ n += tv_list_len(l);
+ }
+
+ // Check for index out of range.
+ if (n < 0 || n >= tv_list_len(l)) {
+ return -1;
+ }
+ return n;
+}
+
+static inline bool tv_list_has_watchers(const list_T *const l)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Check whether list has watchers
+///
+/// E.g. is referenced by a :for loop.
+///
+/// @param[in] l List to check.
+///
+/// @return true if there are watchers, false otherwise.
+static inline bool tv_list_has_watchers(const list_T *const l)
+{
+ return l && l->lv_watch;
+}
+
+static inline listitem_T *tv_list_first(const list_T *const l)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Get first list item
+///
+/// @param[in] l List to get item from.
+///
+/// @return List item or NULL in case of an empty list.
+static inline listitem_T *tv_list_first(const list_T *const l)
+{
+ if (l == NULL) {
+ list_log(l, NULL, NULL, "first");
+ return NULL;
+ }
+ list_log(l, l->lv_first, NULL, "first");
+ return l->lv_first;
+}
+
+static inline listitem_T *tv_list_last(const list_T *const l)
+ REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Get last list item
+///
+/// @param[in] l List to get item from.
+///
+/// @return List item or NULL in case of an empty list.
+static inline listitem_T *tv_list_last(const list_T *const l)
+{
+ if (l == NULL) {
+ list_log(l, NULL, NULL, "last");
+ return NULL;
+ }
+ list_log(l, l->lv_last, NULL, "last");
+ return l->lv_last;
+}
+
static inline long tv_dict_len(const dict_T *const d)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
@@ -352,6 +652,76 @@ extern const char *const tv_empty_string;
/// Specifies that free_unref_items() function has (not) been entered
extern bool tv_in_free_unref_items;
+/// Iterate over a list
+///
+/// @param modifier Modifier: expected to be const or nothing, volatile should
+/// also work if you have any uses for the volatile list.
+/// @param[in] l List to iterate over.
+/// @param li Name of the variable with current listitem_T entry.
+/// @param code Cycle body.
+#define _TV_LIST_ITER_MOD(modifier, l, li, code) \
+ do { \
+ modifier list_T *const l_ = (l); \
+ list_log(l_, NULL, NULL, "iter" #modifier); \
+ if (l_ != NULL) { \
+ for (modifier listitem_T *li = l_->lv_first; \
+ li != NULL; li = li->li_next) { \
+ code \
+ } \
+ } \
+ } while (0)
+
+/// Iterate over a list
+///
+/// To be used when you need to modify list or values you iterate over, use
+/// #TV_LIST_ITER_CONST if you don’t.
+///
+/// @param[in] l List to iterate over.
+/// @param li Name of the variable with current listitem_T entry.
+/// @param code Cycle body.
+#define TV_LIST_ITER(l, li, code) \
+ _TV_LIST_ITER_MOD(, l, li, code)
+
+/// Iterate over a list
+///
+/// To be used when you don’t need to modify list or values you iterate over,
+/// use #TV_LIST_ITER if you do.
+///
+/// @param[in] l List to iterate over.
+/// @param li Name of the variable with current listitem_T entry.
+/// @param code Cycle body.
+#define TV_LIST_ITER_CONST(l, li, code) \
+ _TV_LIST_ITER_MOD(const, l, li, code)
+
+// Below macros are macros to avoid duplicating code for functionally identical
+// const and non-const function variants.
+
+/// Get typval_T out of list item
+///
+/// @param[in] li List item to get typval_T from, must not be NULL.
+///
+/// @return Pointer to typval_T.
+#define TV_LIST_ITEM_TV(li) (&(li)->li_tv)
+
+/// Get next list item given the current one
+///
+/// @param[in] l List to get item from.
+/// @param[in] li List item to get typval_T from.
+///
+/// @return Pointer to the next item or NULL.
+#define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next)
+
+/// Get previous list item given the current one
+///
+/// @param[in] l List to get item from.
+/// @param[in] li List item to get typval_T from.
+///
+/// @return Pointer to the previous item or NULL.
+#define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev)
+// List argument is not used currently, but it is a must for lists implemented
+// as a pair (size(in list), array) without terminator - basically for lists on
+// top of kvec.
+
/// Iterate over a dictionary
///
/// @param[in] d Dictionary to iterate over.
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index a93ad2dbba..f2d0d7265f 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -355,14 +355,14 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
break;
}
case VAR_LIST: {
- if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
+ if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) {
TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
break;
}
- const int saved_copyID = tv->vval.v_list->lv_copyID;
+ const int saved_copyID = tv_list_copyid(tv->vval.v_list);
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
kMPConvList);
- TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len);
+ TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList,
@@ -371,7 +371,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = {
.l = {
.list = tv->vval.v_list,
- .li = tv->vval.v_list->lv_first,
+ .li = tv_list_first(tv->vval.v_list),
},
},
}));
@@ -440,23 +440,43 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
// bits is not checked), other unsigned and have at most 31
// non-zero bits (number of bits is not checked).
if (val_di->di_tv.v_type != VAR_LIST
- || (val_list = val_di->di_tv.vval.v_list) == NULL
- || val_list->lv_len != 4
- || val_list->lv_first->li_tv.v_type != VAR_NUMBER
- || (sign = val_list->lv_first->li_tv.vval.v_number) == 0
- || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
- || (highest_bits =
- val_list->lv_first->li_next->li_tv.vval.v_number) < 0
- || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
- || (high_bits =
- val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
- || val_list->lv_last->li_tv.v_type != VAR_NUMBER
- || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
+ || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) {
goto _convert_one_value_regular_dict;
}
- uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
- | (uint64_t)(((uint64_t)high_bits) << 31)
- | (uint64_t)low_bits);
+
+ const listitem_T *const sign_li = tv_list_first(val_list);
+ if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER
+ || (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) {
+ goto _convert_one_value_regular_dict;
+ }
+
+ const listitem_T *const highest_bits_li = (
+ TV_LIST_ITEM_NEXT(val_list, sign_li));
+ if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER
+ || ((highest_bits
+ = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number)
+ < 0)) {
+ goto _convert_one_value_regular_dict;
+ }
+
+ const listitem_T *const high_bits_li = (
+ TV_LIST_ITEM_NEXT(val_list, highest_bits_li));
+ if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER
+ || ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number)
+ < 0)) {
+ goto _convert_one_value_regular_dict;
+ }
+
+ const listitem_T *const low_bits_li = tv_list_last(val_list);
+ if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER
+ || ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number)
+ < 0)) {
+ goto _convert_one_value_regular_dict;
+ }
+
+ const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
+ | (uint64_t)(((uint64_t)high_bits) << 31)
+ | (uint64_t)low_bits);
if (sign > 0) {
TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
} else {
@@ -495,12 +515,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict;
}
- const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
+ const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
lv_copyID, copyID,
kMPConvList);
- TYPVAL_ENCODE_CONV_LIST_START(tv,
- val_di->di_tv.vval.v_list->lv_len);
+ TYPVAL_ENCODE_CONV_LIST_START(
+ tv, tv_list_len(val_di->di_tv.vval.v_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
@@ -509,7 +529,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = {
.l = {
.list = val_di->di_tv.vval.v_list,
- .li = val_di->di_tv.vval.v_list->lv_first,
+ .li = tv_list_first(val_di->di_tv.vval.v_list),
},
},
}));
@@ -520,22 +540,21 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
goto _convert_one_value_regular_dict;
}
list_T *const val_list = val_di->di_tv.vval.v_list;
- if (val_list == NULL || val_list->lv_len == 0) {
+ if (val_list == NULL || tv_list_len(val_list) == 0) {
TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
break;
}
- for (const listitem_T *li = val_list->lv_first; li != NULL;
- li = li->li_next) {
- if (li->li_tv.v_type != VAR_LIST
- || li->li_tv.vval.v_list->lv_len != 2) {
+ TV_LIST_ITER_CONST(val_list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
+ || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) {
goto _convert_one_value_regular_dict;
}
- }
- const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
+ });
+ const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
kMPConvPairs);
TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
- val_list->lv_len);
+ tv_list_len(val_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
@@ -544,7 +563,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.data = {
.l = {
.list = val_list,
- .li = val_list->lv_first,
+ .li = tv_list_first(val_list),
},
},
}));
@@ -554,18 +573,23 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
const list_T *val_list;
varnumber_T type;
if (val_di->di_tv.v_type != VAR_LIST
- || (val_list = val_di->di_tv.vval.v_list) == NULL
- || val_list->lv_len != 2
- || (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
- || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
+ || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2
+ || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type
+ != VAR_NUMBER)
+ || ((type
+ = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number)
+ > INT8_MAX)
|| type < INT8_MIN
- || (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
+ || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type
+ != VAR_LIST)) {
goto _convert_one_value_regular_dict;
}
size_t len;
char *buf;
- if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
- &len, &buf)) {
+ if (!(
+ encode_vim_list_to_buf(
+ TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len,
+ &buf))) {
goto _convert_one_value_regular_dict;
}
TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
@@ -600,7 +624,7 @@ _convert_one_value_regular_dict: {}
break;
}
case VAR_UNKNOWN: {
- EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
+ internal_error(STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
return FAIL;
}
}
@@ -671,40 +695,45 @@ typval_encode_stop_converting_one_item:
case kMPConvList: {
if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack);
- cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
+ tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
continue;
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ } else if (cur_mpsv->data.l.li
+ != tv_list_first(cur_mpsv->data.l.list)) {
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
}
- tv = &cur_mpsv->data.l.li->li_tv;
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li);
+ cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
+ cur_mpsv->data.l.li);
break;
}
case kMPConvPairs: {
if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack);
- cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
+ tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
continue;
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ } else if (cur_mpsv->data.l.li
+ != tv_list_first(cur_mpsv->data.l.list)) {
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(
cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
}
- const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
+ const list_T *const kv_pair = (
+ TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list);
TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(
- encode_vim_to__error_ret, kv_pair->lv_first->li_tv);
- if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME,
- &mpstack, cur_mpsv,
- &kv_pair->lv_first->li_tv,
- copyID,
- objname) == FAIL) {
+ encode_vim_to__error_ret, *TV_LIST_ITEM_TV(tv_list_first(kv_pair)));
+ if (
+ _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv,
+ TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname)
+ == FAIL) {
goto encode_vim_to__error_ret;
}
TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
TYPVAL_ENCODE_NODICT_VAR);
- tv = &kv_pair->lv_last->li_tv;
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair));
+ cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
+ cur_mpsv->data.l.li);
break;
}
case kMPConvPartial: {
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index f6a567a520..0c2bf397e5 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -26,19 +26,22 @@ int libuv_process_spawn(LibuvProcess *uvproc)
uvproc->uvopts.file = proc->argv[0];
uvproc->uvopts.args = proc->argv;
uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE;
- if (proc->detach) {
- uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
- }
#ifdef WIN32
// libuv collapses the argv to a CommandLineToArgvW()-style string. cmd.exe
// expects a different syntax (must be prepared by the caller before now).
if (os_shell_is_cmdexe(proc->argv[0])) {
uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
}
+ if (proc->detach) {
+ uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
+ }
+#else
+ // Always setsid() on unix-likes. #8107
+ uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
#endif
uvproc->uvopts.exit_cb = exit_cb;
uvproc->uvopts.cwd = proc->cwd;
- uvproc->uvopts.env = NULL;
+ uvproc->uvopts.env = NULL; // Inherits the parent (nvim) env.
uvproc->uvopts.stdio = uvproc->uvstdio;
uvproc->uvopts.stdio_count = 3;
uvproc->uvstdio[0].flags = UV_IGNORE;
@@ -46,22 +49,22 @@ int libuv_process_spawn(LibuvProcess *uvproc)
uvproc->uvstdio[2].flags = UV_IGNORE;
uvproc->uv.data = proc;
- if (proc->in) {
+ if (!proc->in.closed) {
uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
uvproc->uvstdio[0].data.stream = STRUCT_CAST(uv_stream_t,
- &proc->in->uv.pipe);
+ &proc->in.uv.pipe);
}
- if (proc->out) {
+ if (!proc->out.closed) {
uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
uvproc->uvstdio[1].data.stream = STRUCT_CAST(uv_stream_t,
- &proc->out->uv.pipe);
+ &proc->out.uv.pipe);
}
- if (proc->err) {
+ if (!proc->err.closed) {
uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
uvproc->uvstdio[2].data.stream = STRUCT_CAST(uv_stream_t,
- &proc->err->uv.pipe);
+ &proc->err.uv.pipe);
}
int status;
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 25701a1621..d92464f17b 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -30,19 +30,25 @@ void loop_init(Loop *loop, void *data)
uv_signal_init(&loop->uv, &loop->children_watcher);
uv_timer_init(&loop->uv, &loop->children_kill_timer);
uv_timer_init(&loop->uv, &loop->poll_timer);
+ loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag
}
-void loop_poll_events(Loop *loop, int ms)
+/// Processes one `Loop.uv` event (at most).
+/// Processes all `Loop.fast_events` events.
+///
+/// @returns true if `ms` timeout was reached
+bool loop_poll_events(Loop *loop, int ms)
{
if (loop->recursive++) {
abort(); // Should not re-enter uv_run
}
uv_run_mode mode = UV_RUN_ONCE;
+ bool timeout_expired = false;
if (ms > 0) {
- // Use a repeating timeout of ms milliseconds to make sure
- // we do not block indefinitely for I/O.
+ *((bool *)loop->poll_timer.data) = false; // reset "timeout expired" flag
+ // Dummy timer to ensure UV_RUN_ONCE does not block indefinitely for I/O.
uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
} else if (ms == 0) {
// For ms == 0, do a non-blocking event poll.
@@ -52,14 +58,23 @@ void loop_poll_events(Loop *loop, int ms)
uv_run(&loop->uv, mode);
if (ms > 0) {
+ timeout_expired = *((bool *)loop->poll_timer.data);
uv_timer_stop(&loop->poll_timer);
}
loop->recursive--; // Can re-enter uv_run now
multiqueue_process_events(loop->fast_events);
+ return timeout_expired;
}
-// Schedule an event from another thread
+/// Schedules an event from another thread.
+///
+/// @note Event is queued into `fast_events`, which is processed outside of the
+/// primary `events` queue by loop_poll_events(). For `main_loop`, that
+/// means `fast_events` is NOT processed in an "editor mode"
+/// (VimState.execute), so redraw and other side-effects are likely to be
+/// skipped.
+/// @see loop_schedule_deferred
void loop_schedule(Loop *loop, Event event)
{
uv_mutex_lock(&loop->mutex);
@@ -68,6 +83,24 @@ void loop_schedule(Loop *loop, Event event)
uv_mutex_unlock(&loop->mutex);
}
+/// Schedules an event from another thread. Unlike loop_schedule(), the event
+/// is forwarded to `Loop.events`, instead of being processed immediately.
+///
+/// @see loop_schedule
+void loop_schedule_deferred(Loop *loop, Event event)
+{
+ Event *eventp = xmalloc(sizeof(*eventp));
+ *eventp = event;
+ loop_schedule(loop, event_create(loop_deferred_event, 2, loop, eventp));
+}
+static void loop_deferred_event(void **argv)
+{
+ Loop *loop = argv[0];
+ Event *eventp = argv[1];
+ multiqueue_put_event(loop->events, *eventp);
+ xfree(eventp);
+}
+
void loop_on_put(MultiQueue *queue, void *data)
{
Loop *loop = data;
@@ -86,7 +119,7 @@ bool loop_close(Loop *loop, bool wait)
uv_mutex_destroy(&loop->mutex);
uv_close((uv_handle_t *)&loop->children_watcher, NULL);
uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
- uv_close((uv_handle_t *)&loop->poll_timer, NULL);
+ uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb);
uv_close((uv_handle_t *)&loop->async, NULL);
uint64_t start = wait ? os_hrtime() : 0;
while (true) {
@@ -138,5 +171,11 @@ static void async_cb(uv_async_t *handle)
static void timer_cb(uv_timer_t *handle)
{
+ bool *timeout_expired = handle->data;
+ *timeout_expired = true;
}
+static void timer_close_cb(uv_handle_t *handle)
+{
+ xfree(handle->data);
+}
diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h
index e7d7bdd483..d1a40d5cc9 100644
--- a/src/nvim/event/loop.h
+++ b/src/nvim/event/loop.h
@@ -16,10 +16,27 @@ KLIST_INIT(WatcherPtr, WatcherPtr, _noop)
typedef struct loop {
uv_loop_t uv;
- MultiQueue *events, *fast_events, *thread_events;
+ MultiQueue *events;
+ MultiQueue *thread_events;
+ // Immediate events:
+ // "Processed after exiting uv_run() (to avoid recursion), but before
+ // returning from loop_poll_events()." 502aee690c98
+ // Practical consequence (for main_loop): these events are processed by
+ // state_enter()..os_inchar()
+ // whereas "regular" events (main_loop.events) are processed by
+ // state_enter()..VimState.execute()
+ // But state_enter()..os_inchar() can be "too early" if you want the event
+ // to trigger UI updates and other user-activity-related side-effects.
+ MultiQueue *fast_events;
+
+ // used by process/job-control subsystem
klist_t(WatcherPtr) *children;
uv_signal_t children_watcher;
- uv_timer_t children_kill_timer, poll_timer;
+ uv_timer_t children_kill_timer;
+
+ // generic timer, used by loop_poll_events()
+ uv_timer_t poll_timer;
+
size_t children_stop_requests;
uv_async_t async;
uv_mutex_t mutex;
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index cad49e2007..60650344ce 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -12,6 +12,7 @@
#include "nvim/event/wstream.h"
#include "nvim/event/process.h"
#include "nvim/event/libuv_process.h"
+#include "nvim/os/process.h"
#include "nvim/os/pty_process.h"
#include "nvim/globals.h"
#include "nvim/macros.h"
@@ -21,32 +22,32 @@
# include "event/process.c.generated.h"
#endif
-// Time (ns) for a process to exit cleanly before we send TERM/KILL.
-#define TERM_TIMEOUT 1000000000
-#define KILL_TIMEOUT (TERM_TIMEOUT * 2)
-
-#define CLOSE_PROC_STREAM(proc, stream) \
- do { \
- if (proc->stream && !proc->stream->closed) { \
- stream_close(proc->stream, NULL, NULL); \
- } \
- } while (0)
+// Time for a process to exit cleanly before we send KILL.
+// For pty processes SIGTERM is sent first (in case SIGHUP was not enough).
+#define KILL_TIMEOUT_MS 2000
static bool process_is_tearing_down = false;
/// @returns zero on success, or negative error code
-int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
+int process_spawn(Process *proc, bool in, bool out, bool err)
+ FUNC_ATTR_NONNULL_ALL
{
- if (proc->in) {
- uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0);
+ if (in) {
+ uv_pipe_init(&proc->loop->uv, &proc->in.uv.pipe, 0);
+ } else {
+ proc->in.closed = true;
}
- if (proc->out) {
- uv_pipe_init(&proc->loop->uv, &proc->out->uv.pipe, 0);
+ if (out) {
+ uv_pipe_init(&proc->loop->uv, &proc->out.uv.pipe, 0);
+ } else {
+ proc->out.closed = true;
}
- if (proc->err) {
- uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0);
+ if (err) {
+ uv_pipe_init(&proc->loop->uv, &proc->err.uv.pipe, 0);
+ } else {
+ proc->err.closed = true;
}
int status;
@@ -62,14 +63,14 @@ int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
}
if (status) {
- if (proc->in) {
- uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL);
+ if (in) {
+ uv_close((uv_handle_t *)&proc->in.uv.pipe, NULL);
}
- if (proc->out) {
- uv_close((uv_handle_t *)&proc->out->uv.pipe, NULL);
+ if (out) {
+ uv_close((uv_handle_t *)&proc->out.uv.pipe, NULL);
}
- if (proc->err) {
- uv_close((uv_handle_t *)&proc->err->uv.pipe, NULL);
+ if (err) {
+ uv_close((uv_handle_t *)&proc->err.uv.pipe, NULL);
}
if (proc->type == kProcessTypeUv) {
@@ -82,30 +83,27 @@ int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
return status;
}
- if (proc->in) {
- stream_init(NULL, proc->in, -1,
- STRUCT_CAST(uv_stream_t, &proc->in->uv.pipe));
- proc->in->events = proc->events;
- proc->in->internal_data = proc;
- proc->in->internal_close_cb = on_process_stream_close;
+ if (in) {
+ stream_init(NULL, &proc->in, -1,
+ STRUCT_CAST(uv_stream_t, &proc->in.uv.pipe));
+ proc->in.internal_data = proc;
+ proc->in.internal_close_cb = on_process_stream_close;
proc->refcount++;
}
- if (proc->out) {
- stream_init(NULL, proc->out, -1,
- STRUCT_CAST(uv_stream_t, &proc->out->uv.pipe));
- proc->out->events = proc->events;
- proc->out->internal_data = proc;
- proc->out->internal_close_cb = on_process_stream_close;
+ if (out) {
+ stream_init(NULL, &proc->out, -1,
+ STRUCT_CAST(uv_stream_t, &proc->out.uv.pipe));
+ proc->out.internal_data = proc;
+ proc->out.internal_close_cb = on_process_stream_close;
proc->refcount++;
}
- if (proc->err) {
- stream_init(NULL, proc->err, -1,
- STRUCT_CAST(uv_stream_t, &proc->err->uv.pipe));
- proc->err->events = proc->events;
- proc->err->internal_data = proc;
- proc->err->internal_close_cb = on_process_stream_close;
+ if (err) {
+ stream_init(NULL, &proc->err, -1,
+ STRUCT_CAST(uv_stream_t, &proc->err.uv.pipe));
+ proc->err.internal_data = proc;
+ proc->err.internal_close_cb = on_process_stream_close;
proc->refcount++;
}
@@ -125,8 +123,6 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
// Close handles to process without killing it.
CREATE_EVENT(loop->events, process_close_handles, 1, proc);
} else {
- uv_kill(proc->pid, SIGTERM);
- proc->term_sent = true;
process_stop(proc);
}
}
@@ -138,27 +134,11 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
pty_process_teardown(loop);
}
-// Wrappers around `stream_close` that protect against double-closing.
void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL
{
- process_close_in(proc);
- process_close_out(proc);
- process_close_err(proc);
-}
-
-void process_close_in(Process *proc) FUNC_ATTR_NONNULL_ALL
-{
- CLOSE_PROC_STREAM(proc, in);
-}
-
-void process_close_out(Process *proc) FUNC_ATTR_NONNULL_ALL
-{
- CLOSE_PROC_STREAM(proc, out);
-}
-
-void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL
-{
- CLOSE_PROC_STREAM(proc, err);
+ stream_may_close(&proc->in);
+ stream_may_close(&proc->out);
+ stream_may_close(&proc->err);
}
/// Synchronously wait for a process to finish
@@ -166,16 +146,14 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL
/// @param process Process instance
/// @param ms Time in milliseconds to wait for the process.
/// 0 for no wait. -1 to wait until the process quits.
-/// @return Exit code of the process.
+/// @return Exit code of the process. proc->status will have the same value.
/// -1 if the timeout expired while the process is still running.
/// -2 if the user interruped the wait.
int process_wait(Process *proc, int ms, MultiQueue *events)
FUNC_ATTR_NONNULL_ARG(1)
{
- int status = -1; // default
- bool interrupted = false;
if (!proc->refcount) {
- status = proc->status;
+ int status = proc->status;
LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0);
return status;
}
@@ -195,7 +173,6 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
// we'll assume that a user frantically hitting interrupt doesn't like
// the current job. Signal that it has to be killed.
if (got_int) {
- interrupted = true;
got_int = false;
process_stop(proc);
if (ms == -1) {
@@ -206,12 +183,13 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
} else {
LOOP_PROCESS_EVENTS(proc->loop, events, 0);
}
+
+ proc->status = -2;
}
if (proc->refcount == 1) {
// Job exited, collect status and manually invoke close_cb to free the job
// resources
- status = interrupted ? -2 : proc->status;
decref(proc);
if (events) {
// the decref call created an exit event, process it now
@@ -221,7 +199,7 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
proc->refcount--;
}
- return status;
+ return proc->status;
}
/// Ask a process to terminate and eventually kill if it doesn't respond
@@ -237,7 +215,8 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
// Close the process's stdin. If the process doesn't close its own
// stdout/stderr, they will be closed when it exits(possibly due to being
// terminated after a timeout)
- process_close_in(proc);
+ stream_may_close(&proc->in);
+ os_proc_tree_kill(proc->pid, SIGTERM);
break;
case kProcessTypePty:
// close all streams for pty processes to send SIGHUP to the process
@@ -251,9 +230,10 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
Loop *loop = proc->loop;
if (!loop->children_stop_requests++) {
// When there's at least one stop request pending, start a timer that
- // will periodically check if a signal should be send to a to the job
- DLOG("Starting job kill timer");
- uv_timer_start(&loop->children_kill_timer, children_kill_cb, 100, 100);
+ // will periodically check if a signal should be send to the job.
+ ILOG("starting job kill timer");
+ uv_timer_start(&loop->children_kill_timer, children_kill_cb,
+ KILL_TIMEOUT_MS, KILL_TIMEOUT_MS);
}
}
@@ -269,15 +249,13 @@ static void children_kill_cb(uv_timer_t *handle)
if (!proc->stopped_time) {
continue;
}
- uint64_t elapsed = now - proc->stopped_time;
-
- if (!proc->term_sent && elapsed >= TERM_TIMEOUT) {
- ILOG("Sending SIGTERM to pid %d", proc->pid);
- uv_kill(proc->pid, SIGTERM);
- proc->term_sent = true;
- } else if (elapsed >= KILL_TIMEOUT) {
- ILOG("Sending SIGKILL to pid %d", proc->pid);
- uv_kill(proc->pid, SIGKILL);
+ uint64_t elapsed = (now - proc->stopped_time) / 1000000 + 1;
+
+ if (elapsed >= KILL_TIMEOUT_MS) {
+ int sig = proc->type == kProcessTypePty && elapsed < KILL_TIMEOUT_MS * 2
+ ? SIGTERM
+ : SIGKILL;
+ os_proc_tree_kill(proc->pid, sig);
}
}
}
@@ -324,6 +302,13 @@ static void process_close(Process *proc)
}
assert(!proc->closed);
proc->closed = true;
+
+ if (proc->detach) {
+ if (proc->type == kProcessTypeUv) {
+ uv_unref((uv_handle_t *)&(((LibuvProcess *)proc)->uv));
+ }
+ }
+
switch (proc->type) {
case kProcessTypeUv:
libuv_process_close((LibuvProcess *)proc);
@@ -368,15 +353,15 @@ static void flush_stream(Process *proc, Stream *stream)
// Poll for data and process the generated events.
loop_poll_events(proc->loop, 0);
- if (proc->events) {
- multiqueue_process_events(proc->events);
+ if (stream->events) {
+ multiqueue_process_events(stream->events);
}
// Stream can be closed if it is empty.
if (num_bytes == stream->num_bytes) {
- if (stream->read_cb) {
+ if (stream->read_cb && !stream->did_eof) {
// Stream callback could miss EOF handling if a child keeps the stream
- // open.
+ // open. But only send EOF if we haven't already.
stream->read_cb(stream, stream->buffer, 0, stream->cb_data, true);
}
break;
@@ -388,8 +373,8 @@ static void process_close_handles(void **argv)
{
Process *proc = argv[0];
- flush_stream(proc, proc->out);
- flush_stream(proc, proc->err);
+ flush_stream(proc, &proc->out);
+ flush_stream(proc, &proc->err);
process_close_streams(proc);
process_close(proc);
diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h
index 26d70a5e6d..033ce3604b 100644
--- a/src/nvim/event/process.h
+++ b/src/nvim/event/process.h
@@ -23,13 +23,14 @@ struct process {
uint64_t stopped_time;
const char *cwd;
char **argv;
- Stream *in, *out, *err;
+ Stream in, out, err;
process_exit_cb cb;
internal_process_cb internal_exit_cb, internal_close_cb;
- bool closed, term_sent, detach;
+ bool closed, detach;
MultiQueue *events;
};
+
static inline Process process_init(Loop *loop, ProcessType type, void *data)
{
return (Process) {
@@ -38,23 +39,27 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data)
.loop = loop,
.events = NULL,
.pid = 0,
- .status = 0,
+ .status = -1,
.refcount = 0,
.stopped_time = 0,
.cwd = NULL,
.argv = NULL,
- .in = NULL,
- .out = NULL,
- .err = NULL,
+ .in = { .closed = false },
+ .out = { .closed = false },
+ .err = { .closed = false },
.cb = NULL,
.closed = false,
- .term_sent = false,
.internal_close_cb = NULL,
.internal_exit_cb = NULL,
.detach = false
};
}
+static inline bool process_is_stopped(Process *proc)
+{
+ return proc->stopped_time != 0;
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/process.h.generated.h"
#endif
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 854af474b2..2fbe7f6773 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -95,7 +95,7 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
// `uv_buf_t.len` happens to have different size on Windows.
size_t write_count;
buf->base = rbuffer_write_ptr(stream->buffer, &write_count);
- buf->len = write_count;
+ buf->len = UV_BUF_LEN(write_count);
}
// Callback invoked by libuv after it copies the data into the buffer provided
@@ -105,19 +105,19 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
{
Stream *stream = uvstream->data;
- if (cnt > 0) {
- stream->num_bytes += (size_t)cnt;
- }
-
if (cnt <= 0) {
- if (cnt != UV_ENOBUFS
- // cnt == 0 means libuv asked for a buffer and decided it wasn't needed:
- // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start.
- //
- // We don't need to do anything with the RBuffer because the next call
- // to `alloc_cb` will return the same unused pointer(`rbuffer_produced`
- // won't be called)
- && cnt != 0) {
+ // cnt == 0 means libuv asked for a buffer and decided it wasn't needed:
+ // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start.
+ //
+ // We don't need to do anything with the RBuffer because the next call
+ // to `alloc_cb` will return the same unused pointer(`rbuffer_produced`
+ // won't be called)
+ if (cnt == UV_ENOBUFS || cnt == 0) {
+ return;
+ } else if (cnt == UV_EOF && uvstream->type == UV_TTY) {
+ // The TTY driver might signal TTY without closing the stream
+ invoke_read_cb(stream, 0, true);
+ } else {
DLOG("Closing Stream (%p): %s (%s)", stream,
uv_err_name((int)cnt), os_strerror((int)cnt));
// Read error or EOF, either way stop the stream and invoke the callback
@@ -130,6 +130,7 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
// at this point we're sure that cnt is positive, no error occurred
size_t nread = (size_t)cnt;
+ stream->num_bytes += nread;
// Data was already written, so all we need is to update 'wpos' to reflect
// the space actually used in the buffer.
rbuffer_produced(stream->buffer, nread);
@@ -145,7 +146,7 @@ static void fread_idle_cb(uv_idle_t *handle)
// `uv_buf_t.len` happens to have different size on Windows.
size_t write_count;
stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count);
- stream->uvbuf.len = write_count;
+ stream->uvbuf.len = UV_BUF_LEN(write_count);
// the offset argument to uv_fs_read is int64_t, could someone really try
// to read more than 9 quintillion (9e18) bytes?
@@ -187,6 +188,7 @@ static void read_event(void **argv)
if (stream->read_cb) {
size_t count = (uintptr_t)argv[1];
bool eof = (uintptr_t)argv[2];
+ stream->did_eof = eof;
stream->read_cb(stream, stream->buffer, count, stream->cb_data, eof);
}
stream->pending_reqs--;
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index a796f303ab..6f45b09fce 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -105,9 +105,10 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
// contain 0 in this case, unless uv_tcp_getsockname() is used first.
uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas,
&(int){ sizeof(sas) });
- uint16_t port = (uint16_t)((sas.ss_family == AF_INET)
- ? ((struct sockaddr_in *)&sas)->sin_port
- : ((struct sockaddr_in6 *)&sas)->sin6_port);
+ uint16_t port = (uint16_t)(
+ (sas.ss_family == AF_INET)
+ ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port
+ : (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port);
// v:servername uses the string from watcher->addr
size_t len = strlen(watcher->addr);
snprintf(watcher->addr+len, sizeof(watcher->addr)-len, ":%" PRIu16,
@@ -247,7 +248,7 @@ tcp_retry:
uv_pipe_t *pipe = &stream->uv.pipe;
uv_pipe_init(&loop->uv, pipe, 0);
uv_pipe_connect(&req, pipe, address, connect_cb);
- uv_stream = (uv_stream_t *)pipe;
+ uv_stream = STRUCT_CAST(uv_stream_t, pipe);
}
status = 1;
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1);
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 60ceff9b24..ba25b76ec7 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -7,6 +7,7 @@
#include <uv.h>
+#include "nvim/log.h"
#include "nvim/rbuffer.h"
#include "nvim/macros.h"
#include "nvim/event/stream.h"
@@ -81,6 +82,7 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data)
FUNC_ATTR_NONNULL_ARG(1)
{
assert(!stream->closed);
+ DLOG("closing Stream: %p", stream);
stream->closed = true;
stream->close_cb = on_stream_close;
stream->close_cb_data = data;
@@ -90,6 +92,13 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data)
}
}
+void stream_may_close(Stream *stream)
+{
+ if (!stream->closed) {
+ stream_close(stream, NULL, NULL);
+ }
+}
+
void stream_close_handle(Stream *stream)
FUNC_ATTR_NONNULL_ALL
{
diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h
index d27497e4a4..e713323f5c 100644
--- a/src/nvim/event/stream.h
+++ b/src/nvim/event/stream.h
@@ -14,10 +14,7 @@ typedef struct stream Stream;
///
/// @param stream The Stream instance
/// @param rbuffer The associated RBuffer instance
-/// @param count Number of bytes to read. This must be respected if keeping
-/// the order of events is a requirement. This is because events
-/// may be queued and only processed later when more data is copied
-/// into to the buffer, so one read may starve another.
+/// @param count Number of bytes that was read.
/// @param data User-defined data
/// @param eof If the stream reached EOF.
typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count,
@@ -33,6 +30,8 @@ typedef void (*stream_write_cb)(Stream *stream, void *data, int status);
typedef void (*stream_close_cb)(Stream *stream, void *data);
struct stream {
+ bool closed;
+ bool did_eof;
union {
uv_pipe_t pipe;
uv_tcp_t tcp;
@@ -52,7 +51,6 @@ struct stream {
size_t maxmem;
size_t pending_reqs;
size_t num_bytes;
- bool closed;
MultiQueue *events;
};
diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c
index f453e5898d..d2fb52243c 100644
--- a/src/nvim/event/wstream.c
+++ b/src/nvim/event/wstream.c
@@ -8,6 +8,7 @@
#include <uv.h>
+#include "nvim/log.h"
#include "nvim/event/loop.h"
#include "nvim/event/wstream.h"
#include "nvim/vim.h"
@@ -89,7 +90,7 @@ bool wstream_write(Stream *stream, WBuffer *buffer)
uv_buf_t uvbuf;
uvbuf.base = buffer->data;
- uvbuf.len = buffer->size;
+ uvbuf.len = UV_BUF_LEN(buffer->size);
if (uv_write(&data->uv_req, stream->uvstream, &uvbuf, 1, write_cb)) {
xfree(data);
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 8dcb3ac449..c396b5891a 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -90,14 +90,20 @@ typedef struct {
SubIgnoreType do_ic; ///< ignore case flag
} subflags_T;
-/// Lines matched during :substitute.
+/// Partial result of a substitution during :substitute.
+/// Numbers refer to the buffer _after_ substitution
typedef struct {
- linenr_T lnum;
- long nmatch;
- char_u *line;
- kvec_t(colnr_T) cols; ///< columns of in-line matches
-} MatchedLine;
-typedef kvec_t(MatchedLine) MatchedLineVec;
+ lpos_T start; // start of the match
+ lpos_T end; // end of the match
+ linenr_T pre_match; // where to begin showing lines before the match
+} SubResult;
+
+// Collected results of a substitution for showing them in
+// the preview window
+typedef struct {
+ kvec_t(SubResult) subresults;
+ linenr_T lines_needed; // lines neede in the preview window
+} PreviewLines;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds.c.generated.h"
@@ -1169,7 +1175,7 @@ static void do_filter(
// to read the error messages. Otherwise errors are ignored, so you can see
// the error messages from the command that appear on stdout; use 'u' to fix
// the text.
- // Pass on the kShellDoOut flag when the output is being redirected.
+ // Pass on the kShellOptDoOut flag when the output is being redirected.
if (call_shell(
cmd_buf,
kShellOptFilter | shell_flags,
@@ -1411,7 +1417,7 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
#else
// For shells that don't understand braces around commands, at least allow
// the use of commands in a pipe.
- xstrlcpy(buf, cmd, len);
+ xstrlcpy(buf, (char *)cmd, len);
if (itmp != NULL) {
// If there is a pipe, we have to put the '<' in front of it.
// Don't do this when 'shellquote' is not empty, otherwise the
@@ -2007,9 +2013,10 @@ int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum,
} else
other = (fnum != curbuf->b_fnum);
- if (other)
- ++no_wait_return; /* don't wait for autowrite message */
- if (other && !forceit && curbuf->b_nwindows == 1 && !P_HID(curbuf)
+ if (other) {
+ no_wait_return++; // don't wait for autowrite message
+ }
+ if (other && !forceit && curbuf->b_nwindows == 1 && !buf_hide(curbuf)
&& curbufIsChanged() && autowrite(curbuf, forceit) == FAIL) {
if (p_confirm && p_write)
dialog_changed(curbuf, FALSE);
@@ -2026,17 +2033,19 @@ int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum,
if (setpm)
setpcmark();
if (!other) {
- if (lnum != 0)
+ if (lnum != 0) {
curwin->w_cursor.lnum = lnum;
+ }
check_cursor_lnum();
beginline(BL_SOL | BL_FIX);
- retval = 0; /* it's in the same file */
+ retval = 0; // it's in the same file
} else if (do_ecmd(fnum, ffname, sfname, NULL, lnum,
- (P_HID(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0),
- curwin) == OK)
- retval = -1; /* opened another file */
- else
- retval = 1; /* error encountered */
+ (buf_hide(curbuf) ? ECMD_HIDE : 0)
+ + (forceit ? ECMD_FORCEIT : 0), curwin) == OK) {
+ retval = -1; // opened another file
+ } else {
+ retval = 1; // error encountered
+ }
theend:
xfree(free_me);
@@ -2802,16 +2811,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;
@@ -3166,7 +3177,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
linenr_T old_line_count = curbuf->b_ml.ml_line_count;
char_u *sub_firstline; // allocated copy of first sub line
bool endcolumn = false; // cursor in last column when done
- MatchedLineVec matched_lines = KV_INITIAL_VALUE;
+ PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 };
+ static int pre_src_id = 0; // Source id for the preview highlight
+ static int pre_hl_id = 0;
+ buf_T *orig_buf = curbuf; // save to reset highlighting
pos_T old_cursor = curwin->w_cursor;
int start_nsubs;
int save_ma = 0;
@@ -3330,9 +3344,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
sub = regtilde(sub, p_magic);
// Check for a match on each line.
+ // If preview: limit to max('cmdwinheight', viewport).
linenr_T line2 = eap->line2;
for (linenr_T lnum = eap->line1;
- lnum <= line2 && !(got_quit || aborting());
+ lnum <= line2 && !got_quit && !aborting()
+ && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh
+ || lnum <= curwin->w_botline);
lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL);
@@ -3396,8 +3413,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
sub_firstlnum = lnum;
copycol = 0;
matchcol = 0;
- // the current match
- MatchedLine matched_line = { 0, 0, NULL, KV_INITIAL_VALUE };
/* At first match, remember current cursor position. */
if (!got_match) {
@@ -3414,10 +3429,19 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
* 5. break if there isn't another match in this line
*/
for (;; ) {
- /* Advance "lnum" to the line where the match starts. The
- * match does not start in the first line when there is a line
- * break before \zs. */
+ SubResult current_match = {
+ .start = { 0, 0 },
+ .end = { 0, 0 },
+ .pre_match = 0,
+ };
+ // lnum is where the match start, but maybe not the pattern match,
+ // since we can have \n before \zs in the pattern
+
+ // Advance "lnum" to the line where the match starts. The
+ // match does not start in the first line when there is a line
+ // break before \zs.
if (regmatch.startpos[0].lnum > 0) {
+ current_match.pre_match = lnum;
lnum += regmatch.startpos[0].lnum;
sub_firstlnum += regmatch.startpos[0].lnum;
nmatch -= regmatch.startpos[0].lnum;
@@ -3425,6 +3449,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
sub_firstline = NULL;
}
+ // Now we're at the line where the pattern match starts
+ // Note: If not first match on a line, column can't be known here
+ current_match.start.lnum = sub_firstlnum;
+
if (sub_firstline == NULL) {
sub_firstline = vim_strsave(ml_get(sub_firstlnum));
}
@@ -3434,12 +3462,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
curwin->w_cursor.lnum = lnum;
do_again = FALSE;
- if (preview) {
- // Increment the in-line match count and store the column.
- matched_line.nmatch++;
- kv_push(matched_line.cols, regmatch.startpos[0].col);
- }
-
/*
* 1. Match empty string does not count, except for first
* match. This reproduces the strange vi behaviour.
@@ -3459,6 +3481,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
else
++matchcol;
}
+ // match will be pushed to preview_lines, bring it into a proper state
+ current_match.start.col = matchcol;
+ current_match.end.lnum = sub_firstlnum;
+ current_match.end.col = matchcol;
goto skip;
}
@@ -3497,6 +3523,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
setmouse(); /* disable mouse in xterm */
curwin->w_cursor.col = regmatch.startpos[0].col;
+ if (curwin->w_p_crb) {
+ do_check_cursorbind();
+ }
+
/* When 'cpoptions' contains "u" don't sync undo when
* asking for confirmation. */
if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
@@ -3514,6 +3544,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
curwin->w_cursor.col = regmatch.endpos[0].col - 1;
+ if (curwin->w_cursor.col < 0) {
+ curwin->w_cursor.col = 0;
+ }
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
if (subflags.do_number || curwin->w_p_nu) {
int numw = number_width(curwin) + 1;
@@ -3656,6 +3689,54 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
* use "\=col("."). */
curwin->w_cursor.col = regmatch.startpos[0].col;
+ // When the match included the "$" of the last line it may
+ // go beyond the last line of the buffer.
+ if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) {
+ nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1;
+ current_match.end.lnum = sub_firstlnum + nmatch;
+ skip_match = true;
+ }
+
+#define ADJUST_SUB_FIRSTLNUM() \
+ do { \
+ /* For a multi-line match, make a copy of the last matched */ \
+ /* line and continue in that one. */ \
+ if (nmatch > 1) { \
+ sub_firstlnum += nmatch - 1; \
+ xfree(sub_firstline); \
+ sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \
+ /* When going beyond the last line, stop substituting. */ \
+ if (sub_firstlnum <= line2) { \
+ do_again = true; \
+ } else { \
+ subflags.do_all = false; \
+ } \
+ } \
+ if (skip_match) { \
+ /* Already hit end of the buffer, sub_firstlnum is one */ \
+ /* less than what it ought to be. */ \
+ xfree(sub_firstline); \
+ sub_firstline = vim_strsave((char_u *)""); \
+ copycol = 0; \
+ } \
+ } while (0)
+
+ // Save the line numbers for the preview buffer
+ // NOTE: If the pattern matches a final newline, the next line will
+ // be shown also, but should not be highlighted. Intentional for now.
+ if (preview && !has_second_delim) {
+ current_match.start.col = regmatch.startpos[0].col;
+ if (current_match.end.lnum == 0) {
+ current_match.end.lnum = sub_firstlnum + nmatch - 1;
+ }
+ current_match.end.col = regmatch.endpos[0].col;
+
+ ADJUST_SUB_FIRSTLNUM();
+ lnum += nmatch - 1;
+
+ goto skip;
+ }
+
// 3. Substitute the string. During 'inccommand' preview only do this if
// there is a replace pattern.
if (!preview || has_second_delim) {
@@ -3682,13 +3763,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
goto skip;
}
- // When the match included the "$" of the last line it may
- // go beyond the last line of the buffer.
- if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) {
- nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1;
- skip_match = true;
- }
-
// Need room for:
// - result so far in new_start (not for first sub in line)
// - original text up to match
@@ -3709,6 +3783,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
memmove(new_end, sub_firstline + copycol, (size_t)copy_len);
new_end += copy_len;
+ // Finally, at this point we can know where the match actually will
+ // start in the new text
+ current_match.start.col = new_end - new_start;
+
(void)vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
sub, new_end, true, p_magic, true);
@@ -3719,30 +3797,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
// is beyond the end of the line after the substitution.
curwin->w_cursor.col = 0;
- // For a multi-line match, make a copy of the last matched
- // line and continue in that one.
- if (nmatch > 1) {
- sub_firstlnum += nmatch - 1;
- xfree(sub_firstline);
- sub_firstline = vim_strsave(ml_get(sub_firstlnum));
- // When going beyond the last line, stop substituting.
- if (sub_firstlnum <= line2) {
- do_again = true;
- } else {
- subflags.do_all = false;
- }
- }
-
// Remember next character to be copied.
copycol = regmatch.endpos[0].col;
- if (skip_match) {
- // Already hit end of the buffer, sub_firstlnum is one
- // less than what it ought to be.
- xfree(sub_firstline);
- sub_firstline = vim_strsave((char_u *)"");
- copycol = 0;
- }
+ ADJUST_SUB_FIRSTLNUM();
// Now the trick is to replace CTRL-M chars with a real line
// break. This would make it impossible to insert a CTRL-M in
@@ -3781,6 +3839,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
p1 += (*mb_ptr2len)(p1) - 1;
}
}
+ current_match.end.col = STRLEN(new_start);
+ current_match.end.lnum = lnum;
}
// 4. If subflags.do_all is set, find next match.
@@ -3889,9 +3949,30 @@ skip:
* found the match. */
if (nmatch == -1)
lnum -= regmatch.startpos[0].lnum;
+
+#define PUSH_PREVIEW_LINES() \
+ do { \
+ linenr_T match_lines = current_match.end.lnum \
+ - current_match.start.lnum +1; \
+ if (preview_lines.subresults.size > 0) { \
+ linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
+ if (last == current_match.start.lnum) { \
+ preview_lines.lines_needed += match_lines - 1; \
+ } \
+ } else { \
+ preview_lines.lines_needed += match_lines; \
+ } \
+ kv_push(preview_lines.subresults, current_match); \
+ } while (0)
+
+ // Push the match to preview_lines.
+ PUSH_PREVIEW_LINES();
+
break;
}
}
+ // Push the match to preview_lines.
+ PUSH_PREVIEW_LINES();
line_breakcheck();
}
@@ -3901,12 +3982,6 @@ skip:
xfree(new_start); /* for when substitute was cancelled */
xfree(sub_firstline); /* free the copy of the original line */
sub_firstline = NULL;
-
- if (preview) {
- matched_line.lnum = lnum;
- matched_line.line = vim_strsave(ml_get(lnum));
- kv_push(matched_lines, matched_line);
- }
}
line_breakcheck();
@@ -3981,24 +4056,34 @@ skip:
// Show 'inccommand' preview if there are matched lines.
buf_T *preview_buf = NULL;
+ size_t subsize = preview_lines.subresults.size;
if (preview && !aborting()) {
if (got_quit) { // Substitution is too slow, disable 'inccommand'.
set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE,
SID_NONE);
- } else if (*p_icm != NUL && matched_lines.size != 0 && pat != NULL) {
+ } else if (*p_icm != NUL && pat != NULL) {
+ if (pre_src_id == 0) {
+ // Get a unique new src_id, saved in a static
+ pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0);
+ }
+ if (pre_hl_id == 0) {
+ pre_hl_id = syn_check_group((char_u *)S_LEN("Substitute"));
+ }
curbuf->b_changed = save_b_changed; // preserve 'modified' during preview
- preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines);
+ preview_buf = show_sub(eap, old_cursor, &preview_lines,
+ pre_hl_id, pre_src_id);
+ if (subsize > 0) {
+ bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1,
+ kv_last(preview_lines.subresults).end.lnum);
+ }
}
}
- for (MatchedLine m; kv_size(matched_lines);) {
- m = kv_pop(matched_lines);
- xfree(m.line);
- kv_destroy(m.cols);
- }
- kv_destroy(matched_lines);
+ kv_destroy(preview_lines.subresults);
return preview_buf;
+#undef ADJUST_SUB_FIRSTLNUM
+#undef PUSH_PREVIEW_LINES
} // NOLINT(readability/fn_size)
/*
@@ -4538,18 +4623,20 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la
static char *(mtable[]) = {"*", "g*", "[*", "]*",
"/*", "/\\*", "\"*", "**",
"/\\(\\)", "/\\%(\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
+ "?", ":?", "?<CR>", "g?", "g?g?", "g??",
"/\\?", "/\\z(\\)", "\\=", ":s\\=",
- "[count]", "[quotex]", "[range]",
+ "[count]", "[quotex]",
+ "[range]", ":[range]",
"[pattern]", "\\|", "\\%$",
"s/\\~", "s/\\U", "s/\\L",
"s/\\1", "s/\\2", "s/\\3", "s/\\9"};
static char *(rtable[]) = {"star", "gstar", "[star", "]star",
"/star", "/\\\\star", "quotestar", "starstar",
"/\\\\(\\\\)", "/\\\\%(\\\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
+ "?", ":?", "?<CR>", "g?", "g?g?", "g??",
"/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=",
- "\\[count]", "\\[quotex]", "\\[range]",
+ "\\[count]", "\\[quotex]",
+ "\\[range]", ":\\[range]",
"\\[pattern]", "\\\\bar", "/\\\\%\\$",
"s/\\\\\\~", "s/\\\\U", "s/\\\\L",
"s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"};
@@ -4777,7 +4864,9 @@ void fix_help_buffer(void)
char_u *rt;
// Set filetype to "help".
- set_option_value("ft", 0L, "help", OPT_LOCAL);
+ if (STRCMP(curbuf->b_p_ft, "help") != 0) {
+ set_option_value("ft", 0L, "help", OPT_LOCAL);
+ }
if (!syntax_present(curwin)) {
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
@@ -5022,8 +5111,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
- if (!got_int)
- EMSG2("E151: No match: %s", NameBuff);
+ if (!got_int) {
+ EMSG2(_("E151: No match: %s"), NameBuff);
+ }
return;
}
@@ -5212,7 +5302,6 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
if (!add_pathsep((char *)NameBuff)
|| STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
EMSG(_(e_fnametoolong));
- xfree(dirname);
return;
}
@@ -5222,8 +5311,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
- EMSG2("E151: No match: %s", NameBuff);
- xfree(dirname);
+ EMSG2(_("E151: No match: %s"), NameBuff);
return;
}
@@ -5740,7 +5828,8 @@ static void sign_list_defined(sign_T *sp)
}
if (sp->sn_line_hl > 0) {
msg_puts(" linehl=");
- const char *const p = get_highlight_name(NULL, sp->sn_line_hl - 1);
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_line_hl - 1, false);
if (p == NULL) {
msg_puts("NONE");
} else {
@@ -5749,7 +5838,8 @@ static void sign_list_defined(sign_T *sp)
}
if (sp->sn_text_hl > 0) {
msg_puts(" texthl=");
- const char *const p = get_highlight_name(NULL, sp->sn_text_hl - 1);
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_text_hl - 1, false);
if (p == NULL) {
msg_puts("NONE");
} else {
@@ -5997,8 +6087,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
/// Shows the effects of the :substitute command being typed ('inccommand').
/// If inccommand=split, shows a preview window and later restores the layout.
-static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
- MatchedLineVec *matched_lines)
+static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
+ PreviewLines *preview_lines, int hl_id, int src_id)
FUNC_ATTR_NONNULL_ALL
{
static handle_T bufnr = 0; // special buffer, re-used on each visit
@@ -6006,8 +6096,8 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
win_T *save_curwin = curwin;
cmdmod_T save_cmdmod = cmdmod;
char_u *save_shm_p = vim_strsave(p_shm);
- size_t sub_size = mb_string2cells(sub);
- size_t pat_size = mb_string2cells(pat);
+ PreviewLines lines = *preview_lines;
+ buf_T *orig_buf = curbuf;
// We keep a special-purpose buffer around, but don't assume it exists.
buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0;
@@ -6019,22 +6109,26 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
bool outside_curline = (eap->line1 != old_cusr.lnum
|| eap->line2 != old_cusr.lnum);
- bool split = outside_curline && (*p_icm != 'n') && (sub_size || pat_size);
+ bool split = outside_curline && (*p_icm != 'n');
if (preview_buf == curbuf) { // Preview buffer cannot preview itself!
split = false;
preview_buf = NULL;
}
// Place cursor on nearest matching line, to undo do_sub() cursor placement.
- for (size_t i = 0; i < matched_lines->size; i++) {
- MatchedLine curmatch = matched_lines->items[i];
- if (curmatch.lnum >= old_cusr.lnum) {
- curwin->w_cursor.lnum = curmatch.lnum;
- curwin->w_cursor.col = curmatch.cols.items[0];
+ for (size_t i = 0; i < lines.subresults.size; i++) {
+ SubResult curres = lines.subresults.items[i];
+ if (curres.start.lnum >= old_cusr.lnum) {
+ curwin->w_cursor.lnum = curres.start.lnum;
+ curwin->w_cursor.col = curres.start.col;
break;
} // Else: All matches are above, do_sub() already placed cursor.
}
+ // Width of the "| lnum|..." column which displays the line numbers.
+ linenr_T highest_num_line = 0;
+ int col_width = 0;
+
if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) {
buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]");
buf_clear();
@@ -6049,44 +6143,77 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
curwin->w_p_spell = false;
curwin->w_p_fen = false;
- // Width of the "| lnum|..." column which displays the line numbers.
- linenr_T highest_num_line = kv_last(*matched_lines).lnum;
- int col_width = log10(highest_num_line) + 1 + 3;
-
- char *str = NULL;
- size_t old_line_size = 0;
- size_t line_size;
- int src_id_highlight = 0;
- int hl_id = syn_check_group((char_u *)"Substitute", 13);
-
- // Dump the lines into the preview buffer.
- for (size_t line = 0; line < matched_lines->size; line++) {
- MatchedLine mat = matched_lines->items[line];
- line_size = mb_string2cells(mat.line) + col_width + 1;
-
- // Reallocate if str not long enough
- if (line_size > old_line_size) {
- str = xrealloc(str, line_size * sizeof(char));
- old_line_size = line_size;
+ if (lines.subresults.size > 0) {
+ highest_num_line = kv_last(lines.subresults).end.lnum;
+ col_width = log10(highest_num_line) + 1 + 3;
+ }
+ }
+
+ char *str = NULL; // construct the line to show in here
+ size_t old_line_size = 0;
+ size_t line_size = 0;
+ linenr_T linenr_preview = 0; // last line added to preview buffer
+ linenr_T linenr_origbuf = 0; // last line added to original buffer
+ linenr_T next_linenr = 0; // next line to show for the match
+
+ for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) {
+ SubResult match = lines.subresults.items[matchidx];
+
+ if (split && preview_buf) {
+ lpos_T p_start = { 0, match.start.col }; // match starts here in preview
+ lpos_T p_end = { 0, match.end.col }; // ... and ends here
+
+ if (match.pre_match == 0) {
+ next_linenr = match.start.lnum;
+ } else {
+ next_linenr = match.pre_match;
}
+ // Don't add a line twice
+ if (next_linenr == linenr_origbuf) {
+ next_linenr++;
+ p_start.lnum = linenr_preview; // might be redefined below
+ p_end.lnum = linenr_preview; // might be redefined below
+ }
+
+ for (; next_linenr <= match.end.lnum; next_linenr++) {
+ if (next_linenr == match.start.lnum) {
+ p_start.lnum = linenr_preview + 1;
+ }
+ if (next_linenr == match.end.lnum) {
+ p_end.lnum = linenr_preview + 1;
+ }
+ char *line;
+ if (next_linenr == orig_buf->b_ml.ml_line_count + 1) {
+ line = "";
+ } else {
+ line = (char *)ml_get_buf(orig_buf, next_linenr, false);
+ line_size = strlen(line) + col_width + 1;
- // Put "|lnum| line" into `str` and append it to the preview buffer.
- snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line);
- ml_append(line, (char_u *)str, (colnr_T)line_size, false);
-
- // highlight the replaced part
- if (sub_size > 0) {
- for (size_t i = 0; i < mat.cols.size; i++) {
- colnr_T col_start = mat.cols.items[i] + col_width
- + i * (sub_size - pat_size) + 1;
- colnr_T col_end = col_start - 1 + sub_size;
- src_id_highlight = bufhl_add_hl(curbuf, src_id_highlight, hl_id,
- line + 1, col_start, col_end);
+ // Reallocate if line not long enough
+ if (line_size > old_line_size) {
+ str = xrealloc(str, line_size * sizeof(char));
+ old_line_size = line_size;
+ }
}
+ // Put "|lnum| line" into `str` and append it to the preview buffer.
+ snprintf(str, line_size, "|%*ld| %s", col_width - 3,
+ next_linenr, line);
+ if (linenr_preview == 0) {
+ ml_replace(1, (char_u *)str, true);
+ } else {
+ ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false);
+ }
+ linenr_preview += 1;
}
+ linenr_origbuf = match.end.lnum;
+
+ bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start,
+ p_end, col_width);
}
- xfree(str);
+ bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start,
+ match.end, 0);
}
+ xfree(str);
redraw_later(SOME_VALID);
win_enter(save_curwin, false); // Return to original window
@@ -6136,7 +6263,11 @@ void ex_substitute(exarg_T *eap)
curwin->w_p_cul = false; // Disable 'cursorline'
curwin->w_p_cuc = false; // Disable 'cursorcolumn'
+ // Don't show search highlighting during live substitution
+ bool save_hls = p_hls;
+ p_hls = false;
buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt));
+ p_hls = save_hls;
if (save_changedtick != curbuf->b_changedtick) {
// Undo invisibly. This also moves the cursor!
@@ -6215,7 +6346,6 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
void ex_oldfiles(exarg_T *eap)
{
list_T *l = get_vim_var_list(VV_OLDFILES);
- listitem_T *li;
long nr = 0;
if (l == NULL) {
@@ -6223,19 +6353,22 @@ void ex_oldfiles(exarg_T *eap)
} else {
msg_start();
msg_scroll = true;
- for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) {
+ TV_LIST_ITER(l, li, {
+ if (got_int) {
+ break;
+ }
nr++;
- const char *fname = tv_get_string(&li->li_tv);
+ const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
if (!message_filtered((char_u *)fname)) {
msg_outnum(nr);
MSG_PUTS(": ");
- msg_outtrans((char_u *)tv_get_string(&li->li_tv));
+ msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li)));
msg_clr_eos();
msg_putchar('\n');
ui_flush(); // output one line at a time
os_breakcheck();
}
- }
+ });
// Assume "got_int" was set to truncate the listing.
got_int = false;
@@ -6245,7 +6378,7 @@ void ex_oldfiles(exarg_T *eap)
quit_more = false;
nr = prompt_for_number(false);
msg_starthere();
- if (nr > 0 && nr <= l->lv_len) {
+ if (nr > 0 && nr <= tv_list_len(l)) {
const char *const p = tv_list_find_str(l, nr - 1);
if (p == NULL) {
return;
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 7203fbd97d..ce02808ad3 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -1,4 +1,4 @@
-bit = require 'bit'
+local bit = require 'bit'
-- Description of the values below is contained in ex_cmds_defs.h file.
local RANGE = 0x001
@@ -108,7 +108,7 @@ return {
},
{
command='argedit',
- flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILE1, EDITCMD, ARGOPT, TRLBAR),
+ flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILES, EDITCMD, ARGOPT, TRLBAR),
addr_type=ADDR_ARGUMENTS,
func='ex_argedit',
},
@@ -451,6 +451,12 @@ return {
func='ex_changes',
},
{
+ command='checkhealth',
+ flags=bit.bor(EXTRA, TRLBAR),
+ addr_type=ADDR_LINES,
+ func='ex_checkhealth',
+ },
+ {
command='checkpath',
flags=bit.bor(TRLBAR, BANG, CMDWIN),
addr_type=ADDR_LINES,
@@ -614,7 +620,7 @@ return {
},
{
command='cquit',
- flags=bit.bor(TRLBAR, BANG),
+ flags=bit.bor(RANGE, NOTADR, COUNT, ZEROR, TRLBAR, BANG),
addr_type=ADDR_LINES,
func='ex_cquit',
},
@@ -628,13 +634,13 @@ return {
command='cscope',
flags=bit.bor(EXTRA, NOTRLCOM, XFILE),
addr_type=ADDR_LINES,
- func='do_cscope',
+ func='ex_cscope',
},
{
command='cstag',
flags=bit.bor(BANG, TRLBAR, WORD1),
addr_type=ADDR_LINES,
- func='do_cstag',
+ func='ex_cstag',
},
{
command='cunmap',
@@ -686,7 +692,7 @@ return {
},
{
command='delcommand',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN),
addr_type=ADDR_LINES,
func='ex_delcommand',
},
@@ -1076,7 +1082,7 @@ return {
},
{
command='hide',
- flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, NOTRLCOM),
+ flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, TRLBAR),
addr_type=ADDR_WINDOWS,
func='ex_hide',
},
@@ -1324,7 +1330,7 @@ return {
command='lcscope',
flags=bit.bor(EXTRA, NOTRLCOM, XFILE),
addr_type=ADDR_LINES,
- func='do_cscope',
+ func='ex_cscope',
},
{
command='ldo',
@@ -2334,7 +2340,7 @@ return {
command='scscope',
flags=bit.bor(EXTRA, NOTRLCOM),
addr_type=ADDR_LINES,
- func='do_scscope',
+ func='ex_scscope',
},
{
command='set',
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 1a728647ca..821c050c50 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -189,7 +189,8 @@ void do_debug(char_u *cmd)
}
xfree(cmdline);
- cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL);
+ cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE);
if (typeahead_saved) {
restore_typeahead(&typeaheadbuf);
@@ -1038,9 +1039,14 @@ static void profile_reset(void)
uf->uf_tm_total = profile_zero();
uf->uf_tm_self = profile_zero();
uf->uf_tm_children = profile_zero();
+
+ xfree(uf->uf_tml_count);
+ xfree(uf->uf_tml_total);
+ xfree(uf->uf_tml_self);
uf->uf_tml_count = NULL;
uf->uf_tml_total = NULL;
uf->uf_tml_self = NULL;
+
uf->uf_tml_start = profile_zero();
uf->uf_tml_children = profile_zero();
uf->uf_tml_wait = profile_zero();
@@ -1067,7 +1073,7 @@ static void profile_init(scriptitem_T *si)
si->sn_pr_nest = 0;
}
-/// save time when starting to invoke another script or function.
+/// Save time when starting to invoke another script or function.
void script_prof_save(
proftime_T *tm // place to store wait time
)
@@ -1140,12 +1146,14 @@ static void script_dump_profile(FILE *fd)
if (sfd == NULL) {
fprintf(fd, "Cannot open file!\n");
} else {
- for (int i = 0; i < si->sn_prl_ga.ga_len; i++) {
+ // Keep going till the end of file, so that trailing
+ // continuation lines are listed.
+ for (int i = 0; ; i++) {
if (vim_fgets(IObuff, IOSIZE, sfd)) {
break;
}
- pp = &PRL_ITEM(si, i);
- if (pp->snp_count > 0) {
+ if (i < si->sn_prl_ga.ga_len
+ && (pp = &PRL_ITEM(si, i))->snp_count > 0) {
fprintf(fd, "%5d ", pp->snp_count);
if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) {
fprintf(fd, " ");
@@ -1335,7 +1343,7 @@ void dialog_changed(buf_T *buf, int checkall)
/// hidden, autowriting it or unloading it.
bool can_abandon(buf_T *buf, int forceit)
{
- return P_HID(buf)
+ return buf_hide(buf)
|| !bufIsChanged(buf)
|| buf->b_nwindows > 1
|| autowrite(buf, forceit) == OK
@@ -1550,12 +1558,17 @@ static char_u *do_one_arg(char_u *str)
/// Separate the arguments in "str" and return a list of pointers in the
/// growarray "gap".
-void get_arglist(garray_T *gap, char_u *str)
+static void get_arglist(garray_T *gap, char_u *str, int escaped)
{
ga_init(gap, (int)sizeof(char_u *), 20);
while (*str != NUL) {
GA_APPEND(char_u *, gap, str);
+ // If str is escaped, don't handle backslashes or spaces
+ if (!escaped) {
+ return;
+ }
+
// Isolate one argument, change it in-place, put a NUL after it.
str = do_one_arg(str);
}
@@ -1570,7 +1583,7 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig)
garray_T ga;
int i;
- get_arglist(&ga, str);
+ get_arglist(&ga, str, true);
if (wig) {
i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
@@ -1601,6 +1614,7 @@ static int do_arglist(char_u *str, int what, int after)
char_u **exp_files;
char_u *p;
int match;
+ int arg_escaped = true;
// Set default argument for ":argadd" command.
if (what == AL_ADD && *str == NUL) {
@@ -1608,10 +1622,11 @@ static int do_arglist(char_u *str, int what, int after)
return FAIL;
}
str = curbuf->b_fname;
+ arg_escaped = false;
}
// Collect all file name arguments in "new_ga".
- get_arglist(&new_ga, str);
+ get_arglist(&new_ga, str, arg_escaped);
if (what == AL_DEL) {
regmatch_T regmatch;
@@ -1845,12 +1860,12 @@ void do_argfile(exarg_T *eap, int argn)
// if 'hidden' set, only check for changed file when re-editing
// the same buffer
other = true;
- if (P_HID(curbuf)) {
+ if (buf_hide(curbuf)) {
p = (char_u *)fix_fname((char *)alist_name(&ARGLIST[argn]));
other = otherfile(p);
xfree(p);
}
- if ((!P_HID(curbuf) || !other)
+ if ((!buf_hide(curbuf) || !other)
&& check_changed(curbuf, CCGD_AW
| (other ? 0 : CCGD_MULTWIN)
| (eap->forceit ? CCGD_FORCEIT : 0)
@@ -1870,7 +1885,7 @@ void do_argfile(exarg_T *eap, int argn)
// argument index.
if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
eap, ECMD_LAST,
- (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0)
+ (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
+ (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
curwin->w_arg_idx = old_arg_idx;
} else if (eap->cmdidx != CMD_argdo) {
@@ -1887,7 +1902,7 @@ void ex_next(exarg_T *eap)
// check for changed buffer now, if this fails the argument list is not
// redefined.
- if (P_HID(curbuf)
+ if (buf_hide(curbuf)
|| eap->cmdidx == CMD_snext
|| !check_changed(curbuf, CCGD_AW
| (eap->forceit ? CCGD_FORCEIT : 0)
@@ -1907,31 +1922,21 @@ void ex_next(exarg_T *eap)
/// ":argedit"
void ex_argedit(exarg_T *eap)
{
- int fnum;
- int i;
- char_u *s;
+ int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
- // Add the argument to the buffer list and get the buffer number.
- fnum = buflist_add(eap->arg, BLN_LISTED);
-
- // Check if this argument is already in the argument list.
- for (i = 0; i < ARGCOUNT; i++) {
- if (ARGLIST[i].ae_fnum == fnum) {
- break;
- }
- }
- if (i == ARGCOUNT) {
- // Can't find it, add it to the argument list.
- s = vim_strsave(eap->arg);
- int after = eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1;
- i = alist_add_list(1, &s, after);
- curwin->w_arg_idx = i;
+ if (do_arglist(eap->arg, AL_ADD, i) == FAIL) {
+ return;
}
+ maketitle();
- alist_check_arg_idx();
-
+ if (curwin->w_arg_idx == 0 && (curbuf->b_ml.ml_flags & ML_EMPTY)
+ && curbuf->b_ffname == NULL) {
+ i = 0;
+ }
// Edit the argument.
- do_argfile(eap, i);
+ if (i < ARGCOUNT) {
+ do_argfile(eap, i);
+ }
}
/// ":argadd"
@@ -1951,8 +1956,14 @@ void ex_argdelete(exarg_T *eap)
eap->line2 = ARGCOUNT;
}
linenr_T n = eap->line2 - eap->line1 + 1;
- if (*eap->arg != NUL || n <= 0) {
+ if (*eap->arg != NUL) {
+ // Can't have both a range and an argument.
EMSG(_(e_invarg));
+ } else if (n <= 0) {
+ // Don't give an error for ":%argdel" if the list is empty.
+ if (eap->line1 != 1 || eap->line2 != 0) {
+ EMSG(_(e_invrange));
+ }
} else {
for (linenr_T i = eap->line1; i <= eap->line2; i++) {
xfree(ARGLIST[i - 1].ae_fname);
@@ -1997,7 +2008,7 @@ void ex_listdo(exarg_T *eap)
if (eap->cmdidx == CMD_windo
|| eap->cmdidx == CMD_tabdo
- || P_HID(curbuf)
+ || buf_hide(curbuf)
|| !check_changed(curbuf, CCGD_AW
| (eap->forceit ? CCGD_FORCEIT : 0)
| CCGD_EXCMD)) {
@@ -2318,16 +2329,6 @@ static void source_callback(char_u *fname, void *cookie)
(void)do_source(fname, false, DOSO_NONE);
}
-/// Source the file "name" from all directories in 'runtimepath'.
-/// "name" can contain wildcards.
-/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
-///
-/// return FAIL when no file could be sourced, OK otherwise.
-int source_runtime(char_u *name, int flags)
-{
- return do_in_runtimepath(name, flags, source_callback, NULL);
-}
-
/// Find the file "name" in all directories in "path" and invoke
/// "callback(fname, cookie)".
/// "name" can contain wildcards.
@@ -2433,21 +2434,21 @@ int do_in_path(char_u *path, char_u *name, int flags,
return did_one ? OK : FAIL;
}
-/// Find "name" in 'runtimepath'. When found, invoke the callback function for
+/// Find "name" in "path". When found, invoke the callback function for
/// it: callback(fname, "cookie")
/// When "flags" has DIP_ALL repeat for all matches, otherwise only the first
/// one is used.
/// Returns OK when at least one match found, FAIL otherwise.
-/// If "name" is NULL calls callback for each entry in runtimepath. Cookie is
+/// If "name" is NULL calls callback for each entry in "path". Cookie is
/// passed by reference in this case, setting it to NULL indicates that callback
/// has done its job.
-int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
- void *cookie)
+int do_in_path_and_pp(char_u *path, char_u *name, int flags,
+ DoInRuntimepathCB callback, void *cookie)
{
int done = FAIL;
if ((flags & DIP_NORTP) == 0) {
- done = do_in_path(p_rtp, name, flags, callback, cookie);
+ done = do_in_path(path, name, flags, callback, cookie);
}
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) {
@@ -2475,6 +2476,29 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
return done;
}
+/// Just like do_in_path_and_pp(), using 'runtimepath' for "path".
+int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
+ void *cookie)
+{
+ return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
+}
+
+/// Source the file "name" from all directories in 'runtimepath'.
+/// "name" can contain wildcards.
+/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
+///
+/// return FAIL when no file could be sourced, OK otherwise.
+int source_runtime(char_u *name, int flags)
+{
+ return source_in_path(p_rtp, name, flags);
+}
+
+/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
+int source_in_path(char_u *path, char_u *name, int flags)
+{
+ return do_in_path_and_pp(path, name, flags, source_callback, NULL);
+}
+
// Expand wildcards in "pat" and invoke do_source() for each match.
static void source_all_matches(char_u *pat)
{
@@ -2497,6 +2521,7 @@ static int APP_BOTH;
static void add_pack_plugin(char_u *fname, void *cookie)
{
char_u *p4, *p3, *p2, *p1, *p;
+ char_u *buf = NULL;
char *const ffname = fix_fname((char *)fname);
@@ -2524,26 +2549,30 @@ static void add_pack_plugin(char_u *fname, void *cookie)
// Find "ffname" in "p_rtp", ignoring '/' vs '\' differences
size_t fname_len = strlen(ffname);
const char *insp = (const char *)p_rtp;
- for (;;) {
- if (path_fnamencmp(insp, ffname, fname_len) == 0) {
- break;
+ buf = try_malloc(MAXPATHL);
+ if (buf == NULL) {
+ goto theend;
+ }
+ while (*insp != NUL) {
+ copy_option_part((char_u **)&insp, buf, MAXPATHL, ",");
+ add_pathsep((char *)buf);
+ char *const rtp_ffname = fix_fname((char *)buf);
+ if (rtp_ffname == NULL) {
+ goto theend;
}
- insp = strchr(insp, ',');
- if (insp == NULL) {
+ bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
+ xfree(rtp_ffname);
+ if (match) {
break;
}
- insp++;
}
- if (insp == NULL) {
+ if (*insp == NUL) {
// not found, append at the end
insp = (const char *)p_rtp + STRLEN(p_rtp);
} else {
// append after the matching directory.
- insp += strlen(ffname);
- while (*insp != NUL && *insp != ',') {
- insp++;
- }
+ insp--;
}
*p4 = c;
@@ -2613,26 +2642,35 @@ static void add_pack_plugin(char_u *fname, void *cookie)
}
theend:
+ xfree(buf);
xfree(ffname);
}
-static bool did_source_packages = false;
+/// Add all packages in the "start" directory to 'runtimepath'.
+void add_pack_start_dirs(void)
+{
+ do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
+ add_pack_plugin, &APP_ADD_DIR);
+}
+
+/// Load plugins from all packages in the "start" directory.
+void load_start_packages(void)
+{
+ did_source_packages = true;
+ do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
+ add_pack_plugin, &APP_LOAD);
+}
// ":packloadall"
// Find plugins in the package directories and source them.
-// "eap" is NULL when invoked during startup.
void ex_packloadall(exarg_T *eap)
{
- if (!did_source_packages || (eap != NULL && eap->forceit)) {
- did_source_packages = true;
-
+ if (!did_source_packages || eap->forceit) {
// First do a round to add all directories to 'runtimepath', then load
// the plugins. This allows for plugins to use an autoload directory
// of another plugin.
- do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
- add_pack_plugin, &APP_ADD_DIR);
- do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
- add_pack_plugin, &APP_LOAD);
+ add_pack_start_dirs();
+ load_start_packages();
}
}
@@ -2843,22 +2881,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 0;
- cookie.conv.vc_type = CONV_NONE; // no conversion
-
- // Read the first line so we can check for a UTF-8 BOM.
- firstline = getsourceline(0, (void *)&cookie, 0);
- if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
- && firstline[1] == 0xbb && firstline[2] == 0xbf) {
- // Found BOM; setup conversion, skip over BOM and recode the line.
- convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
- p = string_convert(&cookie.conv, firstline + 3, NULL);
- if (p == NULL) {
- p = vim_strsave(firstline + 3);
- }
- xfree(firstline);
- firstline = p;
- }
-
// start measuring script load time if --startuptime was passed and
// time_fd was successfully opened afterwards.
proftime_T rel_time;
@@ -2931,6 +2953,22 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
}
}
+ cookie.conv.vc_type = CONV_NONE; // no conversion
+
+ // Read the first line so we can check for a UTF-8 BOM.
+ firstline = getsourceline(0, (void *)&cookie, 0);
+ if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
+ && firstline[1] == 0xbb && firstline[2] == 0xbf) {
+ // Found BOM; setup conversion, skip over BOM and recode the line.
+ convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
+ p = string_convert(&cookie.conv, firstline + 3, NULL);
+ if (p == NULL) {
+ p = vim_strsave(firstline + 3);
+ }
+ xfree(firstline);
+ firstline = p;
+ }
+
// Call do_cmdline, which will call getsourceline() to get the lines.
do_cmdline(firstline, getsourceline, (void *)&cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
@@ -3040,6 +3078,8 @@ char_u *get_scriptname(scid_T id)
# if defined(EXITFREE)
void free_scriptnames(void)
{
+ profile_reset();
+
# define FREE_SCRIPTNAME(item) xfree((item)->sn_name)
GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME);
}
@@ -3162,8 +3202,14 @@ static char_u *get_one_sourceline(struct source_cookie *sp)
ga_grow(&ga, 120);
buf = (char_u *)ga.ga_data;
+retry:
+ errno = 0;
if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
sp->fp) == NULL) {
+ if (errno == EINTR) {
+ goto retry;
+ }
+
break;
}
len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
@@ -3253,7 +3299,8 @@ void script_line_start(void)
if (si->sn_prof_on && sourcing_lnum >= 1) {
// Grow the array before starting the timer, so that the time spent
// here isn't counted.
- ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
+ (void)ga_grow(&si->sn_prl_ga,
+ (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
si->sn_prl_idx = sourcing_lnum - 1;
while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
&& si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
@@ -3610,18 +3657,11 @@ void ex_language(exarg_T *eap)
static char_u **locales = NULL; // Array of all available locales
-static bool did_init_locales = false;
-/// Lazy initialization of all available locales.
-static void init_locales(void)
-{
- if (!did_init_locales) {
- did_init_locales = true;
- locales = find_locales();
- }
-}
+#ifndef WIN32
+static bool did_init_locales = false;
-// Return an array of strings for all available locales + NULL for the
+/// Return an array of strings for all available locales + NULL for the
/// last element. Return NULL in case of error.
static char_u **find_locales(void)
{
@@ -3653,6 +3693,18 @@ static char_u **find_locales(void)
((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
return (char_u **)locales_ga.ga_data;
}
+#endif
+
+/// Lazy initialization of all available locales.
+static void init_locales(void)
+{
+#ifndef WIN32
+ if (!did_init_locales) {
+ did_init_locales = true;
+ locales = find_locales();
+ }
+#endif
+}
# if defined(EXITFREE)
void free_locales(void)
@@ -3709,7 +3761,7 @@ static void script_host_execute(char *name, exarg_T *eap)
char *const script = script_get(eap, &len);
if (script != NULL) {
- list_T *const args = tv_list_alloc();
+ list_T *const args = tv_list_alloc(3);
// script
tv_list_append_allocated_string(args, script);
// current range
@@ -3724,7 +3776,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
uint8_t buffer[MAXPATHL];
vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false);
- list_T *args = tv_list_alloc();
+ list_T *args = tv_list_alloc(3);
// filename
tv_list_append_string(args, (const char *)buffer, -1);
// current range
@@ -3735,7 +3787,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
static void script_host_do_range(char *name, exarg_T *eap)
{
- list_T *args = tv_list_alloc();
+ list_T *args = tv_list_alloc(3);
tv_list_append_number(args, (int)eap->line1);
tv_list_append_number(args, (int)eap->line2);
tv_list_append_string(args, (const char *)eap->arg, -1);
@@ -3788,7 +3840,7 @@ void ex_drop(exarg_T *eap)
// to split the current window or data could be lost.
// Skip the check if the 'hidden' option is set, as in this case the
// buffer won't be lost.
- if (!P_HID(curbuf)) {
+ if (!buf_hide(curbuf)) {
emsg_off++;
split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD);
emsg_off--;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index af8845de87..4b37abab9e 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9,6 +9,7 @@
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
#include <inttypes.h>
#include "nvim/vim.h"
@@ -132,7 +133,6 @@ struct dbg_stuff {
char_u *vv_throwpoint;
int did_emsg;
int got_int;
- int did_throw;
int need_rethrow;
int check_cstack;
except_T *current_exception;
@@ -164,12 +164,11 @@ static void save_dbg_stuff(struct dbg_stuff *dsp)
dsp->vv_exception = v_exception(NULL);
dsp->vv_throwpoint = v_throwpoint(NULL);
- /* Necessary for debugging an inactive ":catch", ":finally", ":endtry" */
- dsp->did_emsg = did_emsg; did_emsg = FALSE;
- dsp->got_int = got_int; got_int = FALSE;
- dsp->did_throw = did_throw; did_throw = FALSE;
- dsp->need_rethrow = need_rethrow; need_rethrow = FALSE;
- dsp->check_cstack = check_cstack; check_cstack = FALSE;
+ // Necessary for debugging an inactive ":catch", ":finally", ":endtry".
+ dsp->did_emsg = did_emsg; did_emsg = false;
+ dsp->got_int = got_int; got_int = false;
+ dsp->need_rethrow = need_rethrow; need_rethrow = false;
+ dsp->check_cstack = check_cstack; check_cstack = false;
dsp->current_exception = current_exception; current_exception = NULL;
}
@@ -183,7 +182,6 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp)
(void)v_throwpoint(dsp->vv_throwpoint);
did_emsg = dsp->did_emsg;
got_int = dsp->got_int;
- did_throw = dsp->did_throw;
need_rethrow = dsp->need_rethrow;
check_cstack = dsp->check_cstack;
current_exception = dsp->current_exception;
@@ -340,12 +338,13 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
msg_list = &private_msg_list;
private_msg_list = NULL;
- /* It's possible to create an endless loop with ":execute", catch that
- * here. The value of 200 allows nested function calls, ":source", etc. */
- if (call_depth == 200) {
+ // It's possible to create an endless loop with ":execute", catch that
+ // here. The value of 200 allows nested function calls, ":source", etc.
+ // Allow 200 or 'maxfuncdepth', whatever is larger.
+ if (call_depth >= 200 && call_depth >= p_mfd) {
EMSG(_("E169: Command too recursive"));
- /* When converting to an exception, we do not include the command name
- * since this is not an error of the specific command. */
+ // When converting to an exception, we do not include the command name
+ // since this is not an error of the specific command.
do_errthrow((struct condstack *)NULL, (char_u *)NULL);
msg_list = saved_msg_list;
return FAIL;
@@ -398,16 +397,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
initial_trylevel = trylevel;
- /*
- * "did_throw" will be set to TRUE when an exception is being thrown.
- */
- did_throw = FALSE;
- /*
- * "did_emsg" will be set to TRUE when emsg() is used, in which case we
- * cancel the whole command line, and any if/endif or loop.
- * If force_abort is set, we cancel everything.
- */
- did_emsg = FALSE;
+ current_exception = NULL;
+ // "did_emsg" will be set to TRUE when emsg() is used, in which case we
+ // cancel the whole command line, and any if/endif or loop.
+ // If force_abort is set, we cancel everything.
+ did_emsg = false;
/*
* KeyTyped is only set when calling vgetc(). Reset it here when not
@@ -657,7 +651,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* not to use a cs_line[] from an entry that isn't a ":while"
* or ":for": It would make "current_line" invalid and can
* cause a crash. */
- if (!did_emsg && !got_int && !did_throw
+ if (!did_emsg && !got_int && !current_exception
&& cstack.cs_idx >= 0
&& (cstack.cs_flags[cstack.cs_idx]
& (CSF_WHILE | CSF_FOR))
@@ -705,7 +699,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
}
/*
- * A ":finally" makes did_emsg, got_int, and did_throw pending for
+ * A ":finally" makes did_emsg, got_int and current_exception pending for
* being restored at the ":endtry". Reset them here and set the
* ACTIVE and FINALLY flags, so that the finally clause gets executed.
* This includes the case where a missing ":endif", ":endwhile" or
@@ -713,10 +707,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
*/
if (cstack.cs_lflags & CSL_HAD_FINA) {
cstack.cs_lflags &= ~CSL_HAD_FINA;
- report_make_pending(cstack.cs_pending[cstack.cs_idx]
- & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW),
- did_throw ? (void *)current_exception : NULL);
- did_emsg = got_int = did_throw = FALSE;
+ report_make_pending((cstack.cs_pending[cstack.cs_idx]
+ & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)),
+ current_exception);
+ did_emsg = got_int = false;
+ current_exception = NULL;
cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY;
}
@@ -724,15 +719,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* within this loop. */
trylevel = initial_trylevel + cstack.cs_trylevel;
- /*
- * If the outermost try conditional (across function calls and sourced
- * files) is aborted because of an error, an interrupt, or an uncaught
- * exception, cancel everything. If it is left normally, reset
- * force_abort to get the non-EH compatible abortion behavior for
- * the rest of the script.
- */
- if (trylevel == 0 && !did_emsg && !got_int && !did_throw)
- force_abort = FALSE;
+ // If the outermost try conditional (across function calls and sourced
+ // files) is aborted because of an error, an interrupt, or an uncaught
+ // exception, cancel everything. If it is left normally, reset
+ // force_abort to get the non-EH compatible abortion behavior for
+ // the rest of the script.
+ if (trylevel == 0 && !did_emsg && !got_int && !current_exception) {
+ force_abort = false;
+ }
/* Convert an interrupt to an exception if appropriate. */
(void)do_intthrow(&cstack);
@@ -747,11 +741,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* - there is a command after '|', inside a :if, :while, :for or :try, or
* looping for ":source" command or function call.
*/
- while (!((got_int
- || (did_emsg && force_abort) || did_throw
- )
- && cstack.cs_trylevel == 0
- )
+ while (!((got_int || (did_emsg && force_abort) || current_exception)
+ && cstack.cs_trylevel == 0)
&& !(did_emsg
/* Keep going when inside try/catch, so that the error can be
* deal with, except when it is a syntax error, it may cause
@@ -773,7 +764,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* If a sourced file or executed function ran to its end, report the
* unclosed conditional.
*/
- if (!got_int && !did_throw
+ if (!got_int && !current_exception
&& ((getline_equal(fgetline, cookie, getsourceline)
&& !source_finished(fgetline, cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
@@ -813,17 +804,16 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
? (char_u *)"endfunction" : (char_u *)NULL);
if (trylevel == 0) {
- /*
- * When an exception is being thrown out of the outermost try
- * conditional, discard the uncaught exception, disable the conversion
- * of interrupts or errors to exceptions, and ensure that no more
- * commands are executed.
- */
- if (did_throw) {
- void *p = NULL;
- char_u *saved_sourcing_name;
+ // When an exception is being thrown out of the outermost try
+ // conditional, discard the uncaught exception, disable the conversion
+ // of interrupts or errors to exceptions, and ensure that no more
+ // commands are executed.
+ if (current_exception) {
+ void *p = NULL;
+ char_u *saved_sourcing_name;
int saved_sourcing_lnum;
- struct msglist *messages = NULL, *next;
+ struct msglist *messages = NULL;
+ struct msglist *next;
/*
* If the uncaught exception is a user exception, report it as an
@@ -844,8 +834,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;
@@ -885,22 +873,22 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
suppress_errthrow = TRUE;
}
- /*
- * The current cstack will be freed when do_cmdline() returns. An uncaught
- * exception will have to be rethrown in the previous cstack. If a function
- * has just returned or a script file was just finished and the previous
- * cstack belongs to the same function or, respectively, script file, it
- * will have to be checked for finally clauses to be executed due to the
- * ":return" or ":finish". This is done in do_one_cmd().
- */
- if (did_throw)
- need_rethrow = TRUE;
+ // The current cstack will be freed when do_cmdline() returns. An uncaught
+ // exception will have to be rethrown in the previous cstack. If a function
+ // has just returned or a script file was just finished and the previous
+ // cstack belongs to the same function or, respectively, script file, it
+ // will have to be checked for finally clauses to be executed due to the
+ // ":return" or ":finish". This is done in do_one_cmd().
+ if (current_exception) {
+ need_rethrow = true;
+ }
if ((getline_equal(fgetline, cookie, getsourceline)
&& ex_nesting_level > source_level(real_cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
&& ex_nesting_level > func_level(real_cookie) + 1)) {
- if (!did_throw)
- check_cstack = TRUE;
+ if (!current_exception) {
+ check_cstack = true;
+ }
} else {
/* When leaving a function, reduce nesting level. */
if (getline_equal(fgetline, cookie, get_func_line))
@@ -1480,10 +1468,11 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
char_u *after_modifier = ea.cmd;
- ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0
- && !(cstack->cs_flags[cstack->
- cs_idx]
- & CSF_ACTIVE));
+ ea.skip = (did_emsg
+ || got_int
+ || current_exception
+ || (cstack->cs_idx >= 0
+ && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
/* Count this line for profiling if ea.skip is FALSE. */
if (do_profiling == PROF_YES && !ea.skip) {
@@ -1667,11 +1656,15 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.addr_count++;
if (*ea.cmd == ';') {
- if (!ea.skip)
+ if (!ea.skip) {
curwin->w_cursor.lnum = ea.line2;
- } else if (*ea.cmd != ',')
+ // don't leave the cursor on an illegal line or column
+ check_cursor();
+ }
+ } else if (*ea.cmd != ',') {
break;
- ++ea.cmd;
+ }
+ ea.cmd++;
}
/* One address given: set start and end lines */
@@ -1682,9 +1675,6 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.addr_count = 0;
}
- /* Don't leave the cursor on an illegal line (caused by ';') */
- check_cursor_lnum();
-
/*
* 5. Parse the command.
*/
@@ -1781,13 +1771,14 @@ static char_u * do_one_cmd(char_u **cmdlinep,
));
- /* forced commands */
+ // Forced commands.
if (*p == '!' && ea.cmdidx != CMD_substitute
&& ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic) {
- ++p;
- ea.forceit = TRUE;
- } else
- ea.forceit = FALSE;
+ p++;
+ ea.forceit = true;
+ } else {
+ ea.forceit = false;
+ }
/*
* 6. Parse arguments.
@@ -1813,7 +1804,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (text_locked() && !(ea.argt & CMDWIN)
&& !IS_USER_CMDIDX(ea.cmdidx)) {
// Command not allowed when editing the command line.
- errormsg = get_text_locked_msg();
+ errormsg = (char_u *)_(get_text_locked_msg());
goto doend;
}
/* Disallow editing another buffer when "curbuf_lock" is set.
@@ -3271,6 +3262,12 @@ const char * set_one_cmd_context(
case CMD_echoerr:
case CMD_call:
case CMD_return:
+ case CMD_cexpr:
+ case CMD_caddexpr:
+ case CMD_cgetexpr:
+ case CMD_lexpr:
+ case CMD_laddexpr:
+ case CMD_lgetexpr:
set_context_for_expression(xp, (char_u *)arg, ea.cmdidx);
break;
@@ -3435,11 +3432,20 @@ const char * set_one_cmd_context(
case CMD_profile:
set_context_in_profile_cmd(xp, arg);
break;
+ case CMD_checkhealth:
+ xp->xp_context = EXPAND_CHECKHEALTH;
+ xp->xp_pattern = (char_u *)arg;
+ break;
case CMD_behave:
xp->xp_context = EXPAND_BEHAVE;
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;
@@ -3472,10 +3478,17 @@ char_u *skip_range(
{
unsigned delim;
- while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;", *cmd) != NULL) {
- if (*cmd == '\'') {
- if (*++cmd == NUL && ctx != NULL)
+ while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) {
+ if (*cmd == '\\') {
+ if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') {
+ cmd++;
+ } else {
+ break;
+ }
+ } else if (*cmd == '\'') {
+ if (*++cmd == NUL && ctx != NULL) {
*ctx = EXPAND_NOTHING;
+ }
} else if (*cmd == '/' || *cmd == '?') {
delim = *cmd++;
while (*cmd != NUL && *cmd != delim)
@@ -4670,17 +4683,17 @@ char_u *find_nextcmd(const char_u *p)
return (char_u *)p + 1;
}
-/*
- * Check if *p is a separator between Ex commands.
- * Return NULL if it isn't, (p + 1) if it is.
- */
+/// Check if *p is a separator between Ex commands, skipping over white space.
+/// Return NULL if it isn't, the following character if it is.
char_u *check_nextcmd(char_u *p)
{
- p = skipwhite(p);
- if (*p == '|' || *p == '\n')
- return p + 1;
- else
- return NULL;
+ char_u *s = skipwhite(p);
+
+ if (*s == '|' || *s == '\n') {
+ return (s + 1);
+ } else {
+ return NULL;
+ }
}
/*
@@ -4747,7 +4760,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep,
char_u *rep_buf = NULL;
garray_T *gap;
- replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false,
+ replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true,
CPO_TO_CPO_FLAGS);
if (rep_buf == NULL) {
/* Can't replace termcodes - try using the string as is */
@@ -4852,6 +4865,7 @@ static struct {
{ EXPAND_AUGROUP, "augroup" },
{ EXPAND_BEHAVE, "behave" },
{ EXPAND_BUFFERS, "buffer" },
+ { EXPAND_CHECKHEALTH, "checkhealth" },
{ EXPAND_COLORS, "color" },
{ EXPAND_COMMANDS, "command" },
{ EXPAND_COMPILER, "compiler" },
@@ -4874,6 +4888,7 @@ static struct {
#endif
{ EXPAND_MAPPINGS, "mapping" },
{ EXPAND_MENUS, "menu" },
+ { EXPAND_MESSAGES, "messages" },
{ EXPAND_OWNSYNTAX, "syntax" },
{ EXPAND_SYNTIME, "syntime" },
{ EXPAND_SETTINGS, "option" },
@@ -5905,9 +5920,10 @@ static void ex_colorscheme(exarg_T *eap)
static void ex_highlight(exarg_T *eap)
{
- if (*eap->arg == NUL && eap->cmd[2] == '!')
+ if (*eap->arg == NUL && eap->cmd[2] == '!') {
MSG(_("Greetings, Vim user!"));
- do_highlight(eap->arg, eap->forceit, FALSE);
+ }
+ do_highlight((const char *)eap->arg, eap->forceit, false);
}
@@ -5948,22 +5964,24 @@ static void ex_quit(exarg_T *eap)
wp = curwin;
}
- apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf);
+ // Refuse to quit when locked.
+ if (curbuf_locked()) {
+ return;
+ }
+ apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, wp->w_buffer);
// Refuse to quit when locked or when the buffer in the last window is
// being closed (can only happen in autocommands).
- if (curbuf_locked()
+ if (!win_valid(wp)
|| (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) {
return;
}
-
- /*
- * If there are more files or windows we won't exit.
- */
- if (check_more(FALSE, eap->forceit) == OK && only_one_window())
- exiting = TRUE;
- if ((!P_HID(curbuf)
- && check_changed(curbuf, (p_awa ? CCGD_AW : 0)
+ // If there are more files or windows we won't exit.
+ if (check_more(false, eap->forceit) == OK && only_one_window()) {
+ exiting = true;
+ }
+ if ((!buf_hide(wp->w_buffer)
+ && check_changed(wp->w_buffer, (p_awa ? CCGD_AW : 0)
| (eap->forceit ? CCGD_FORCEIT : 0)
| CCGD_EXCMD))
|| check_more(true, eap->forceit) == FAIL
@@ -5976,11 +5994,11 @@ 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 */
- win_close(wp, !P_HID(wp->w_buffer) || eap->forceit);
+ // close window; may free buffer
+ win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit);
}
}
@@ -5989,7 +6007,7 @@ static void ex_quit(exarg_T *eap)
*/
static void ex_cquit(exarg_T *eap)
{
- getout(1);
+ getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
}
/*
@@ -6079,7 +6097,7 @@ ex_win_close (
buf_T *buf = win->w_buffer;
need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
- if (need_hide && !P_HID(buf) && !forceit) {
+ if (need_hide && !buf_hide(buf) && !forceit) {
if ((p_confirm || cmdmod.confirm) && p_write) {
bufref_T bufref;
set_bufref(&bufref, buf);
@@ -6095,11 +6113,12 @@ ex_win_close (
}
- /* free buffer when not hiding it or when it's a scratch buffer */
- if (tp == NULL)
- win_close(win, !need_hide && !P_HID(buf));
- else
- win_close_othertab(win, !need_hide && !P_HID(buf), tp);
+ // free buffer when not hiding it or when it's a scratch buffer
+ if (tp == NULL) {
+ win_close(win, !need_hide && !buf_hide(buf));
+ } else {
+ win_close_othertab(win, !need_hide && !buf_hide(buf), tp);
+ }
}
/*
@@ -6174,12 +6193,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);
+ }
}
/*
@@ -6207,7 +6228,6 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
if (!valid_tabpage(tp) || tp->tp_firstwin == wp)
break;
}
- apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, FALSE, curbuf);
redraw_tabline = TRUE;
if (h != tabline_height())
@@ -6247,31 +6267,27 @@ void ex_all(exarg_T *eap)
static void ex_hide(exarg_T *eap)
{
- if (*eap->arg != NUL && check_nextcmd(eap->arg) == NULL)
- eap->errmsg = e_invarg;
- else {
- /* ":hide" or ":hide | cmd": hide current window */
- eap->nextcmd = check_nextcmd(eap->arg);
+ // ":hide" or ":hide | cmd": hide current window
if (!eap->skip) {
- if (eap->addr_count == 0)
- win_close(curwin, FALSE); /* don't free buffer */
- else {
- int winnr = 0;
- win_T *win = NULL;
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- winnr++;
- if (winnr == eap->line2) {
- win = wp;
- break;
- }
+ if (eap->addr_count == 0) {
+ win_close(curwin, false); // don't free buffer
+ } else {
+ int winnr = 0;
+ win_T *win = NULL;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ winnr++;
+ if (winnr == eap->line2) {
+ win = wp;
+ break;
+ }
+ }
+ if (win == NULL) {
+ win = lastwin;
+ }
+ win_close(win, false);
}
- if (win == NULL)
- win = lastwin;
- win_close(win, FALSE);
- }
}
- }
}
/*
@@ -6333,7 +6349,7 @@ static void ex_exit(exarg_T *eap)
getout(0);
}
// Quit current window, may free the buffer.
- win_close(curwin, !P_HID(curwin->w_buffer));
+ win_close(curwin, !buf_hide(curwin->w_buffer));
}
}
@@ -6903,24 +6919,23 @@ do_exedit (
empty buffer */
setpcmark();
if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg),
- NULL, eap,
- eap->do_ecmd_lnum,
- (P_HID(curbuf) ? ECMD_HIDE : 0)
- + (eap->forceit ? ECMD_FORCEIT : 0)
- // After a split we can use an existing buffer.
- + (old_curwin != NULL ? ECMD_OLDBUF : 0)
- + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0 )
- , old_curwin == NULL ? curwin : NULL) == FAIL) {
- /* Editing the file failed. If the window was split, close it. */
+ NULL, eap, eap->do_ecmd_lnum,
+ (buf_hide(curbuf) ? ECMD_HIDE : 0)
+ + (eap->forceit ? ECMD_FORCEIT : 0)
+ // After a split we can use an existing buffer.
+ + (old_curwin != NULL ? ECMD_OLDBUF : 0)
+ + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0)
+ , old_curwin == NULL ? curwin : NULL) == FAIL) {
+ // Editing the file failed. If the window was split, close it.
if (old_curwin != NULL) {
need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1);
- if (!need_hide || P_HID(curbuf)) {
+ if (!need_hide || buf_hide(curbuf)) {
cleanup_T cs;
/* Reset the error/interrupt/exception state here so that
* aborting() returns FALSE when closing a window. */
enter_cleanup(&cs);
- win_close(curwin, !need_hide && !P_HID(curbuf));
+ win_close(curwin, !need_hide && !buf_hide(curbuf));
/* Restore the error/interrupt/exception state if not
* discarded by a new aborting error, interrupt, or
@@ -7686,6 +7701,7 @@ static void ex_redraw(exarg_T *eap)
RedrawingDisabled = 0;
p_lz = FALSE;
+ validate_cursor();
update_topline();
update_screen(eap->forceit ? CLEAR :
VIsual_active ? INVERTED :
@@ -8130,6 +8146,10 @@ static void ex_startinsert(exarg_T *eap)
restart_edit = 'i';
curwin->w_curswant = 0; /* avoid MAXCOL */
}
+
+ if (VIsual_active) {
+ showmode();
+ }
}
/*
@@ -8315,7 +8335,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name)
break;
default: /* ":tag" */
if (p_cst && *eap->arg != NUL) {
- do_cstag(eap);
+ ex_cstag(eap);
return;
}
cmd = DT_TAG;
@@ -8520,22 +8540,22 @@ eval_vars (
resultbuf = result; /* remember allocated string */
break;
- case SPEC_AFILE: /* file name for autocommand */
- result = autocmd_fname;
- if (result != NULL && !autocmd_fname_full) {
- /* Still need to turn the fname into a full path. It is
- * postponed to avoid a delay when <afile> is not used. */
- autocmd_fname_full = TRUE;
- result = (char_u *)FullName_save((char *)autocmd_fname, FALSE);
- xfree(autocmd_fname);
- autocmd_fname = result;
+ case SPEC_AFILE: // file name for autocommand
+ if (autocmd_fname != NULL && !path_is_absolute(autocmd_fname)) {
+ // Still need to turn the fname into a full path. It was
+ // postponed to avoid a delay when <afile> is not used.
+ result = (char_u *)FullName_save((char *)autocmd_fname, false);
+ // Copy into `autocmd_fname`, don't reassign it. #8165
+ xstrlcpy((char *)autocmd_fname, (char *)result, MAXPATHL);
+ xfree(result);
}
+ result = autocmd_fname;
if (result == NULL) {
*errormsg = (char_u *)_(
"E495: no autocommand file name to substitute for \"<afile>\"");
return NULL;
}
- result = path_shorten_fname_if_possible(result);
+ result = path_try_shorten_fname(result);
break;
case SPEC_ABUF: /* buffer number for autocommand */
@@ -8805,11 +8825,12 @@ makeopens (
&& buf->b_fname != NULL
&& buf->b_p_bl) {
if (fprintf(fd, "badd +%" PRId64 " ",
- buf->b_wininfo == NULL ?
- (int64_t)1L :
- (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0
- || ses_fname(fd, buf, &ssop_flags) == FAIL)
+ buf->b_wininfo == NULL
+ ? (int64_t)1L
+ : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0
+ || ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
return FAIL;
+ }
}
}
@@ -8880,11 +8901,13 @@ makeopens (
&& !bt_nofile(wp->w_buffer)
) {
if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0
- || ses_fname(fd, wp->w_buffer, &ssop_flags) == FAIL)
+ || ses_fname(fd, wp->w_buffer, &ssop_flags, true) == FAIL) {
return FAIL;
- need_tabnew = FALSE;
- if (!wp->w_arg_idx_invalid)
+ }
+ need_tabnew = false;
+ if (!wp->w_arg_idx_invalid) {
edited_win = wp;
+ }
break;
}
}
@@ -8928,6 +8951,8 @@ makeopens (
// resized when moving between windows.
// Do this before restoring the view, so that the topline and the
// cursor can be set. This is done again below.
+ // winminheight and winminwidth need to be set to avoid an error if the
+ // user has set winheight or winwidth.
if (put_line(fd, "set winminheight=1 winminwidth=1 winheight=1 winwidth=1")
== FAIL) {
return FAIL;
@@ -9216,24 +9241,35 @@ put_view (
if (wp->w_buffer->b_ffname != NULL
&& (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal)
) {
- /*
- * Editing a file in this buffer: use ":edit file".
- * This may have side effects! (e.g., compressed or network file).
- */
- if (fputs("edit ", fd) < 0
- || ses_fname(fd, wp->w_buffer, flagp) == FAIL)
+ // Editing a file in this buffer: use ":edit file".
+ // This may have side effects! (e.g., compressed or network file).
+ //
+ // Note, if a buffer for that file already exists, use :badd to
+ // edit that buffer, to not lose folding information (:edit resets
+ // folds in other buffers)
+ if (fputs("if bufexists('", fd) < 0
+ || ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
+ || fputs("') | buffer ", fd) < 0
+ || ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
+ || fputs(" | else | edit ", fd) < 0
+ || ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
+ || fputs(" | endif", fd) < 0
+ || put_eol(fd) == FAIL) {
return FAIL;
+ }
} else {
- /* No file in this buffer, just make it empty. */
- if (put_line(fd, "enew") == FAIL)
+ // No file in this buffer, just make it empty.
+ if (put_line(fd, "enew") == FAIL) {
return FAIL;
+ }
if (wp->w_buffer->b_ffname != NULL) {
- /* The buffer does have a name, but it's not a file name. */
+ // The buffer does have a name, but it's not a file name.
if (fputs("file ", fd) < 0
- || ses_fname(fd, wp->w_buffer, flagp) == FAIL)
+ || ses_fname(fd, wp->w_buffer, flagp, true) == FAIL) {
return FAIL;
+ }
}
- do_cursor = FALSE;
+ do_cursor = false;
}
}
@@ -9328,15 +9364,18 @@ put_view (
}
}
- /*
- * Local directory.
- */
- if (wp->w_localdir != NULL) {
+ //
+ // Local directory, if the current flag is not view options or the "curdir"
+ // option is included.
+ //
+ if (wp->w_localdir != NULL
+ && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) {
if (fputs("lcd ", fd) < 0
|| ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
- || put_eol(fd) == FAIL)
+ || put_eol(fd) == FAIL) {
return FAIL;
- did_lcd = TRUE;
+ }
+ did_lcd = true;
}
return OK;
@@ -9373,7 +9412,7 @@ ses_arglist (
(void)vim_FullName((char *)s, (char *)buf, MAXPATHL, FALSE);
s = buf;
}
- if (fputs("argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL
+ if (fputs("$argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL
|| put_eol(fd) == FAIL) {
xfree(buf);
return FAIL;
@@ -9384,12 +9423,10 @@ ses_arglist (
return OK;
}
-/*
- * Write a buffer name to the session file.
- * Also ends the line.
- * Returns FAIL if writing fails.
- */
-static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp)
+/// Write a buffer name to the session file.
+/// Also ends the line, if "add_eol" is true.
+/// Returns FAIL if writing fails.
+static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol)
{
char_u *name;
@@ -9406,8 +9443,10 @@ static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp)
name = buf->b_sfname;
else
name = buf->b_ffname;
- if (ses_put_fname(fd, name, flagp) == FAIL || put_eol(fd) == FAIL)
+ if (ses_put_fname(fd, name, flagp) == FAIL
+ || (add_eol && put_eol(fd) == FAIL)) {
return FAIL;
+ }
return OK;
}
@@ -9593,6 +9632,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;
@@ -9670,29 +9719,37 @@ static void ex_filetype(exarg_T *eap)
EMSG2(_(e_invarg2), arg);
}
-/// Do ":filetype plugin indent on" if user did not already do some
-/// permutation thereof.
+/// Set all :filetype options ON if user did not explicitly set any to OFF.
void filetype_maybe_enable(void)
{
- if (filetype_detect == kNone
- && filetype_plugin == kNone
- && filetype_indent == kNone) {
+ if (filetype_detect == kNone) {
source_runtime((char_u *)FILETYPE_FILE, true);
filetype_detect = kTrue;
+ }
+ if (filetype_plugin == kNone) {
source_runtime((char_u *)FTPLUGIN_FILE, true);
filetype_plugin = kTrue;
+ }
+ if (filetype_indent == kNone) {
source_runtime((char_u *)INDENT_FILE, true);
filetype_indent = kTrue;
}
}
-/*
- * ":setfiletype {name}"
- */
+/// ":setfiletype [FALLBACK] {name}"
static void ex_setfiletype(exarg_T *eap)
{
if (!did_filetype) {
- set_option_value("filetype", 0L, (char *)eap->arg, OPT_LOCAL);
+ char_u *arg = eap->arg;
+
+ if (STRNCMP(arg, "FALLBACK ", 9) == 0) {
+ arg += 9;
+ }
+
+ set_option_value("filetype", 0L, (char *)arg, OPT_LOCAL);
+ if (arg != eap->arg) {
+ did_filetype = false;
+ }
}
}
@@ -9820,7 +9877,7 @@ static void ex_terminal(exarg_T *eap)
if (*eap->arg != NUL) { // Run {cmd} in 'shell'.
char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");
snprintf(ex_cmd, sizeof(ex_cmd),
- ":enew%s | call termopen(\"%s\") | startinsert",
+ ":enew%s | call termopen(\"%s\")",
eap->forceit ? "!" : "", name);
xfree(name);
} else { // No {cmd}: run the job with tokenized 'shell'.
@@ -9842,7 +9899,7 @@ static void ex_terminal(exarg_T *eap)
shell_free_argv(argv);
snprintf(ex_cmd, sizeof(ex_cmd),
- ":enew%s | call termopen([%s]) | startinsert",
+ ":enew%s | call termopen([%s])",
eap->forceit ? "!" : "", shell_argv + 1);
}
@@ -9851,7 +9908,7 @@ static void ex_terminal(exarg_T *eap)
/// Checks if `cmd` is "previewable" (i.e. supported by 'inccommand').
///
-/// @param[in] cmd Commandline to check. May start with a range.
+/// @param[in] cmd Commandline to check. May start with a range or modifier.
///
/// @return true if `cmd` is previewable
bool cmd_can_preview(char_u *cmd)
@@ -9860,6 +9917,12 @@ bool cmd_can_preview(char_u *cmd)
return false;
}
+ // Ignore any leading modifiers (:keeppatterns, :verbose, etc.)
+ for (int len = modifier_len(cmd); len != 0; len = modifier_len(cmd)) {
+ cmd += len;
+ cmd = skipwhite(cmd);
+ }
+
exarg_T ea;
// parse the command line
ea.cmd = skip_range(cmd, NULL);
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 5d664b94a8..e23945c842 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -45,22 +45,21 @@
* there or even outside the try conditional. Try conditionals may be nested.
*/
-/*
- * Configuration whether an exception is thrown on error or interrupt. When
- * the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
- * interrupt (got_int) under an active try conditional terminates the script
- * after the non-active finally clauses of all active try conditionals have been
- * executed. Otherwise, errors and/or interrupts are converted into catchable
- * exceptions (did_throw additionally set), which terminate the script only if
- * not caught. For user exceptions, only did_throw is set. (Note: got_int can
- * be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not
- * a reliant test that the exception currently being thrown is an interrupt
- * exception. Similarly, did_emsg can be set afterwards on an error in an
- * (unskipped) conditional command inside an inactive conditional, so did_throw
- * && did_emsg is not a reliant test that the exception currently being thrown
- * is an error exception.) - The macros can be defined as expressions checking
- * for a variable that is allowed to be changed during execution of a script.
- */
+// Configuration whether an exception is thrown on error or interrupt. When
+// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
+// interrupt (got_int) under an active try conditional terminates the script
+// after the non-active finally clauses of all active try conditionals have been
+// executed. Otherwise, errors and/or interrupts are converted into catchable
+// exceptions, which terminate the script only if not caught. For user
+// exceptions, only current_exception is set. (Note: got_int can be set
+// asynchronously afterwards by a SIGINT, so current_exception && got_int is not
+// a reliant test that the exception currently being thrown is an interrupt
+// exception. Similarly, did_emsg can be set afterwards on an error in an
+// (unskipped) conditional command inside an inactive conditional, so
+// current_exception && did_emsg is not a reliant test that the exception
+// currently being thrown is an error exception.) - The macros can be defined
+// as expressions checking for a variable that is allowed to be changed during
+// execution of a script.
// Values used for the Vim release.
#define THROW_ON_ERROR true
@@ -68,6 +67,15 @@
#define THROW_ON_INTERRUPT true
#define THROW_ON_INTERRUPT_TRUE
+// Don't do something after an error, interrupt, or throw, or when
+// there is a surrounding conditional and it was not active.
+#define CHECK_SKIP \
+ (did_emsg \
+ || got_int \
+ || current_exception \
+ || (cstack->cs_idx > 0 \
+ && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))
+
#define discard_pending_return(p) tv_free((typval_T *)(p))
/*
@@ -94,7 +102,7 @@ static int cause_abort = FALSE;
*/
int aborting(void)
{
- return (did_emsg && force_abort) || got_int || did_throw;
+ return (did_emsg && force_abort) || got_int || current_exception;
}
/*
@@ -178,8 +186,9 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore)
* currently throwing an exception, do nothing. The message text will
* then be stored to v:errmsg by emsg() without displaying it.
*/
- if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw)
- return FALSE;
+ if (((trylevel == 0 && !cause_abort) || emsg_silent) && !current_exception) {
+ return false;
+ }
/*
* Ignore an interrupt message when inside a try conditional or when an
@@ -208,12 +217,13 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore)
* exception currently being thrown to prevent it from being caught. Just
* execute finally clauses and terminate.
*/
- if (did_throw) {
- /* When discarding an interrupt exception, reset got_int to prevent the
- * same interrupt being converted to an exception again and discarding
- * the error exception we are about to throw here. */
- if (current_exception->type == ET_INTERRUPT)
- got_int = FALSE;
+ if (current_exception) {
+ // When discarding an interrupt exception, reset got_int to prevent the
+ // same interrupt being converted to an exception again and discarding
+ // the error exception we are about to throw here.
+ if (current_exception->type == ET_INTERRUPT) {
+ got_int = false;
+ }
discard_current_exception();
}
@@ -333,51 +343,49 @@ void do_errthrow(struct condstack *cstack, char_u *cmdname)
*/
int do_intthrow(struct condstack *cstack)
{
- /*
- * If no interrupt occurred or no try conditional is active and no exception
- * is being thrown, do nothing (for compatibility of non-EH scripts).
- */
- if (!got_int || (trylevel == 0 && !did_throw))
- return FALSE;
+ // If no interrupt occurred or no try conditional is active and no exception
+ // is being thrown, do nothing (for compatibility of non-EH scripts).
+ if (!got_int || (trylevel == 0 && !current_exception)) {
+ return false;
+ }
-#ifdef THROW_TEST /* avoid warning for condition always true */
+#ifdef THROW_TEST // avoid warning for condition always true
if (!THROW_ON_INTERRUPT) {
- /*
- * The interrupt aborts everything except for executing finally clauses.
- * Discard any user or error or interrupt exception currently being
- * thrown.
- */
- if (did_throw)
+ // The interrupt aborts everything except for executing finally clauses.
+ // Discard any user or error or interrupt exception currently being
+ // thrown.
+ if (current_exception) {
discard_current_exception();
- } else
+ }
+ } else {
#endif
- {
- /*
- * Throw an interrupt exception, so that everything will be aborted
- * (except for executing finally clauses), until the interrupt exception
- * is caught; if still uncaught at the top level, the script processing
- * will be terminated then. - If an interrupt exception is already
- * being thrown, do nothing.
- *
- */
- if (did_throw) {
- if (current_exception->type == ET_INTERRUPT)
- return FALSE;
+ // Throw an interrupt exception, so that everything will be aborted
+ // (except for executing finally clauses), until the interrupt exception
+ // is caught; if still uncaught at the top level, the script processing
+ // will be terminated then. - If an interrupt exception is already
+ // being thrown, do nothing.
+
+ if (current_exception) {
+ if (current_exception->type == ET_INTERRUPT) {
+ return false;
+ }
- /* An interrupt exception replaces any user or error exception. */
+ // An interrupt exception replaces any user or error exception.
discard_current_exception();
}
- if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL)
+ if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) {
do_throw(cstack);
+ }
+#ifdef THROW_TEST
}
+#endif
- return TRUE;
+ 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 +443,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;
@@ -520,7 +526,7 @@ static void discard_exception(except_T *excp, int was_finished)
char_u *saved_IObuff;
if (excp == NULL) {
- EMSG(_(e_internal));
+ internal_error("discard_exception()");
return;
}
@@ -564,10 +570,11 @@ static void discard_exception(except_T *excp, int was_finished)
*/
void discard_current_exception(void)
{
- discard_exception(current_exception, FALSE);
+ discard_exception(current_exception, false);
+ // Note: all globals manipulated here should be saved/restored in
+ // try_enter/try_leave.
current_exception = NULL;
- did_throw = FALSE;
- need_rethrow = FALSE;
+ need_rethrow = false;
}
/*
@@ -620,8 +627,9 @@ static void catch_exception(except_T *excp)
*/
static void finish_exception(except_T *excp)
{
- if (excp != caught_stack)
- EMSG(_(e_internal));
+ if (excp != caught_stack) {
+ internal_error("finish_exception()");
+ }
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1);
@@ -795,15 +803,7 @@ void ex_if(exarg_T *eap)
++cstack->cs_idx;
cstack->cs_flags[cstack->cs_idx] = 0;
- /*
- * Don't do something after an error, interrupt, or throw, or when there
- * is a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
bool error;
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
@@ -854,15 +854,7 @@ void ex_else(exarg_T *eap)
int result;
struct condstack *cstack = eap->cstack;
- /*
- * Don't do something after an error, interrupt, or throw, or when there is
- * a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
if (cstack->cs_idx < 0
|| (cstack->cs_flags[cstack->cs_idx]
@@ -952,15 +944,7 @@ void ex_while(exarg_T *eap)
cstack->cs_flags[cstack->cs_idx] =
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
- /*
- * Don't do something after an error, interrupt, or throw, or when
- * there is a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
if (eap->cmdidx == CMD_while) {
/*
* ":while bool-expr"
@@ -1233,8 +1217,6 @@ void do_throw(struct condstack *cstack)
cstack->cs_flags[idx] &= ~CSF_ACTIVE;
cstack->cs_exception[idx] = current_exception;
}
-
- did_throw = TRUE;
}
/*
@@ -1253,15 +1235,7 @@ void ex_try(exarg_T *eap)
cstack->cs_flags[cstack->cs_idx] = CSF_TRY;
cstack->cs_pending[cstack->cs_idx] = CSTP_NONE;
- /*
- * Don't do something after an error, interrupt, or throw, or when there
- * is a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
if (!skip) {
/* Set ACTIVE and TRUE. TRUE means that the corresponding ":catch"
@@ -1353,8 +1327,9 @@ void ex_catch(exarg_T *eap)
* corresponding try block never got active (because of an inactive
* surrounding conditional or after an error or interrupt or throw).
*/
- if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE))
- skip = TRUE;
+ if (!current_exception || !(cstack->cs_flags[idx] & CSF_TRUE)) {
+ skip = true;
+ }
/*
* Check for a match only if an exception is thrown but not caught by
@@ -1413,18 +1388,23 @@ void ex_catch(exarg_T *eap)
}
if (caught) {
- /* Make this ":catch" clause active and reset did_emsg, got_int,
- * and did_throw. Put the exception on the caught stack. */
+ /* Make this ":catch" clause active and reset did_emsg and got_int.
+ * Put the exception on the caught stack. */
cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT;
- did_emsg = got_int = did_throw = FALSE;
+ did_emsg = got_int = false;
catch_exception((except_T *)cstack->cs_exception[idx]);
/* It's mandatory that the current exception is stored in the cstack
* so that it can be discarded at the next ":catch", ":finally", or
* ":endtry" or when the catch clause is left by a ":continue",
* ":break", ":return", ":finish", error, interrupt, or another
* exception. */
- if (cstack->cs_exception[cstack->cs_idx] != current_exception)
- EMSG(_(e_internal));
+ if (cstack->cs_exception[cstack->cs_idx] != current_exception) {
+ internal_error("ex_catch()");
+ }
+ // Discarding current_exceptions happens based on what is stored in
+ // cstack->cs_exception, *all* calls to discard_current_exception() are
+ // (and must be) guarded by current_exception check.
+ current_exception = NULL;
} else {
/*
* If there is a preceding catch clause and it caught the exception,
@@ -1483,7 +1463,7 @@ void ex_finally(exarg_T *eap)
* interrupt or throw) or for a ":finally" without ":try" or a multiple
* ":finally". After every other error (did_emsg or the conditional
* errors detected above) or after an interrupt (got_int) or an
- * exception (did_throw), the finally clause must be executed.
+ * exception (current_exception), the finally clause must be executed.
*/
skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
@@ -1510,30 +1490,31 @@ void ex_finally(exarg_T *eap)
cleanup_conditionals(cstack, CSF_TRY, FALSE);
/*
- * Make did_emsg, got_int, did_throw pending. If set, they overrule
- * a pending ":continue", ":break", ":return", or ":finish". Then
- * we have particularly to discard a pending return value (as done
+ * Make did_emsg, got_int, current_exception pending. If set, they
+ * overrule a pending ":continue", ":break", ":return", or ":finish".
+ * Then we have particularly to discard a pending return value (as done
* by the call to cleanup_conditionals() above when did_emsg or
* got_int is set). The pending values are restored by the
* ":endtry", except if there is a new error, interrupt, exception,
* ":continue", ":break", ":return", or ":finish" in the following
* finally clause. A missing ":endwhile", ":endfor" or ":endif"
- * detected here is treated as if did_emsg and did_throw had
+ * detected here is treated as if did_emsg and current_exception had
* already been set, respectively in case that the error is not
- * converted to an exception, did_throw had already been unset.
+ * converted to an exception, current_exception had already been unset.
* We must not set did_emsg here since that would suppress the
* error message.
*/
- if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) {
+ if (pending == CSTP_ERROR || did_emsg || got_int || current_exception) {
if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) {
report_discard_pending(CSTP_RETURN,
cstack->cs_rettv[cstack->cs_idx]);
discard_pending_return(cstack->cs_rettv[cstack->cs_idx]);
}
- if (pending == CSTP_ERROR && !did_emsg)
- pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0;
- else
- pending |= did_throw ? CSTP_THROW : 0;
+ if (pending == CSTP_ERROR && !did_emsg) {
+ pending |= (THROW_ON_ERROR ? CSTP_THROW : 0);
+ } else {
+ pending |= (current_exception ? CSTP_THROW : 0);
+ }
pending |= did_emsg ? CSTP_ERROR : 0;
pending |= got_int ? CSTP_INTERRUPT : 0;
assert(pending >= CHAR_MIN && pending <= CHAR_MAX);
@@ -1546,14 +1527,15 @@ void ex_finally(exarg_T *eap)
* exception. When emsg() is called for a missing ":endif" or
* a missing ":endwhile"/":endfor" detected here, the
* exception will be discarded. */
- if (did_throw && cstack->cs_exception[cstack->cs_idx]
- != current_exception)
- EMSG(_(e_internal));
+ if (current_exception
+ && cstack->cs_exception[cstack->cs_idx] != current_exception) {
+ internal_error("ex_finally()");
+ }
}
/*
* Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
- * got_int, and did_throw and make the finally clause active.
+ * got_int, and current_exception and make the finally clause active.
* This will happen after emsg() has been called for a missing
* ":endif" or a missing ":endwhile"/":endfor" detected here, so
* that the following finally clause will be executed even then.
@@ -1588,7 +1570,7 @@ void ex_endtry(exarg_T *eap)
// made inactive by a ":continue", ":break", ":return", or ":finish" in
// the finally clause. The latter case need not be tested since then
// anything pending has already been discarded.
- skip = (did_emsg || got_int || did_throw
+ skip = (did_emsg || got_int || current_exception
|| !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE));
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
@@ -1605,12 +1587,13 @@ void ex_endtry(exarg_T *eap)
/*
* If an exception is being thrown, discard it to prevent it from
* being rethrown at the end of this function. It would be
- * discarded by the error message, anyway. Resets did_throw.
+ * discarded by the error message, anyway. Resets current_exception.
* This does not affect the script termination due to the error
* since "trylevel" is decremented after emsg() has been called.
*/
- if (did_throw)
+ if (current_exception) {
discard_current_exception();
+ }
} else {
idx = cstack->cs_idx;
@@ -1620,9 +1603,11 @@ void ex_endtry(exarg_T *eap)
* a finally clause, we need to rethrow it after closing the try
* conditional.
*/
- if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE)
- && !(cstack->cs_flags[idx] & CSF_FINALLY))
- rethrow = TRUE;
+ if (current_exception
+ && (cstack->cs_flags[idx] & CSF_TRUE)
+ && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ rethrow = true;
+ }
}
/* If there was no finally clause, show the user when debugging or
@@ -1643,11 +1628,12 @@ void ex_endtry(exarg_T *eap)
if (got_int) {
skip = TRUE;
(void)do_intthrow(cstack);
- /* The do_intthrow() call may have reset did_throw or
- * cstack->cs_pending[idx].*/
- rethrow = FALSE;
- if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY))
- rethrow = TRUE;
+ // The do_intthrow() call may have reset current_exception or
+ // cstack->cs_pending[idx].
+ rethrow = false;
+ if (current_exception && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ rethrow = true;
+ }
}
}
@@ -1709,26 +1695,30 @@ void ex_endtry(exarg_T *eap)
do_finish(eap, FALSE);
break;
- /* When the finally clause was entered due to an error,
- * interrupt or throw (as opposed to a ":continue", ":break",
- * ":return", or ":finish"), restore the pending values of
- * did_emsg, got_int, and did_throw. This is skipped, if there
- * was a new error, interrupt, throw, ":continue", ":break",
- * ":return", or ":finish". in the finally clause. */
+ // When the finally clause was entered due to an error,
+ // interrupt or throw (as opposed to a ":continue", ":break",
+ // ":return", or ":finish"), restore the pending values of
+ // did_emsg, got_int, and current_exception. This is skipped, if there
+ // was a new error, interrupt, throw, ":continue", ":break",
+ // ":return", or ":finish". in the finally clause.
default:
- if (pending & CSTP_ERROR)
- did_emsg = TRUE;
- if (pending & CSTP_INTERRUPT)
- got_int = TRUE;
- if (pending & CSTP_THROW)
- rethrow = TRUE;
+ if (pending & CSTP_ERROR) {
+ did_emsg = true;
+ }
+ if (pending & CSTP_INTERRUPT) {
+ got_int = true;
+ }
+ if (pending & CSTP_THROW) {
+ rethrow = true;
+ }
break;
}
}
- if (rethrow)
- /* Rethrow the current exception (within this cstack). */
+ if (rethrow) {
+ // Rethrow the current exception (within this cstack).
do_throw(cstack);
+ }
}
}
@@ -1758,33 +1748,34 @@ void enter_cleanup(cleanup_T *csp)
int pending = CSTP_NONE;
/*
- * Postpone did_emsg, got_int, did_throw. The pending values will be
+ * Postpone did_emsg, got_int, current_exception. The pending values will be
* restored by leave_cleanup() except if there was an aborting error,
* interrupt, or uncaught exception after this function ends.
*/
- if (did_emsg || got_int || did_throw || need_rethrow) {
- csp->pending = (did_emsg ? CSTP_ERROR : 0)
- | (got_int ? CSTP_INTERRUPT : 0)
- | (did_throw ? CSTP_THROW : 0)
- | (need_rethrow ? CSTP_THROW : 0);
-
- /* If we are currently throwing an exception (did_throw), save it as
- * well. On an error not yet converted to an exception, update
- * "force_abort" and reset "cause_abort" (as do_errthrow() would do).
- * This is needed for the do_cmdline() call that is going to be made
- * for autocommand execution. We need not save *msg_list because
- * there is an extra instance for every call of do_cmdline(), anyway.
+ if (did_emsg || got_int || current_exception || need_rethrow) {
+ csp->pending = (did_emsg ? CSTP_ERROR : 0)
+ | (got_int ? CSTP_INTERRUPT : 0)
+ | (current_exception ? CSTP_THROW : 0)
+ | (need_rethrow ? CSTP_THROW : 0);
+
+ /* If we are currently throwing an exception, save it as well. On an error
+ * not yet converted to an exception, update "force_abort" and reset
+ * "cause_abort" (as do_errthrow() would do). This is needed for the
+ * do_cmdline() call that is going to be made for autocommand execution. We
+ * need not save *msg_list because there is an extra instance for every call
+ * of do_cmdline(), anyway.
*/
- if (did_throw || need_rethrow)
+ if (current_exception || need_rethrow) {
csp->exception = current_exception;
- else {
+ } else {
csp->exception = NULL;
if (did_emsg) {
force_abort |= cause_abort;
cause_abort = FALSE;
}
}
- did_emsg = got_int = did_throw = need_rethrow = FALSE;
+ did_emsg = got_int = need_rethrow = false;
+ current_exception = NULL;
/* Report if required by the 'verbose' option or when debugging. */
report_make_pending(pending, csp->exception);
@@ -1856,19 +1847,20 @@ void leave_cleanup(cleanup_T *csp)
force_abort = FALSE;
}
- /*
- * Restore the pending values of did_emsg, got_int, and did_throw.
- */
- if (pending & CSTP_ERROR)
- did_emsg = TRUE;
- if (pending & CSTP_INTERRUPT)
- got_int = TRUE;
- if (pending & CSTP_THROW)
- need_rethrow = TRUE; /* did_throw will be set by do_one_cmd() */
+ // Restore the pending values of did_emsg, got_int, and current_exception.
+ if (pending & CSTP_ERROR) {
+ did_emsg = true;
+ }
+ if (pending & CSTP_INTERRUPT) {
+ got_int = true;
+ }
+ if (pending & CSTP_THROW) {
+ need_rethrow = true; // current_exception will be set by do_one_cmd()
+ }
- /* Report if required by the 'verbose' option or when debugging. */
- report_resume_pending(pending,
- (pending & CSTP_THROW) ? (void *)current_exception : NULL);
+ // Report if required by the 'verbose' option or when debugging.
+ report_resume_pending(
+ pending, ((pending & CSTP_THROW) ? (void *)current_exception : NULL));
}
}
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index f61e01d25b..d5f8737bf3 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -89,28 +89,29 @@ struct msglist {
struct msglist *next; /* next of several messages in a row */
};
+// The exception types.
+typedef enum
+{
+ ET_USER, // exception caused by ":throw" command
+ ET_ERROR, // error exception
+ ET_INTERRUPT // interrupt exception triggered by Ctrl-C
+} except_type_T;
+
/*
* Structure describing an exception.
* (don't use "struct exception", it's used by the math library).
*/
typedef struct vim_exception except_T;
struct vim_exception {
- int type; /* exception type */
- char_u *value; /* exception value */
- struct msglist *messages; /* message(s) causing error exception */
- char_u *throw_name; /* name of the throw point */
- linenr_T throw_lnum; /* line number of the throw point */
- except_T *caught; /* next exception on the caught stack */
+ except_type_T type; // exception type
+ char_u *value; // exception value
+ struct msglist *messages; // message(s) causing error exception
+ char_u *throw_name; // name of the throw point
+ linenr_T throw_lnum; // line number of the throw point
+ except_T *caught; // next exception on the caught stack
};
/*
- * The exception types.
- */
-#define ET_USER 0 /* exception caused by ":throw" command */
-#define ET_ERROR 1 /* error exception */
-#define ET_INTERRUPT 2 /* interrupt exception triggered by Ctrl-C */
-
-/*
* Structure to save the error/interrupt/exception state between calls to
* enter_cleanup() and leave_cleanup(). Must be allocated as an automatic
* variable by the (common) caller of these functions.
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 7793081957..698419405a 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -12,6 +12,7 @@
#include <inttypes.h>
#include "nvim/assert.h"
+#include "nvim/log.h"
#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/arabic.h"
@@ -62,6 +63,42 @@
#include "nvim/os/os.h"
#include "nvim/event/loop.h"
#include "nvim/os/time.h"
+#include "nvim/lib/kvec.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/viml/parser/parser.h"
+#include "nvim/viml/parser/expressions.h"
+
+/// Command-line colors: one chunk
+///
+/// Defines a region which has the same highlighting.
+typedef struct {
+ int start; ///< Colored chunk start.
+ int end; ///< Colored chunk end (exclusive, > start).
+ int attr; ///< Highlight attr.
+} CmdlineColorChunk;
+
+/// Command-line colors
+///
+/// Holds data about all colors.
+typedef kvec_t(CmdlineColorChunk) CmdlineColors;
+
+/// Command-line coloring
+///
+/// Holds both what are the colors and what have been colored. Latter is used to
+/// suppress unnecessary calls to coloring callbacks.
+typedef struct {
+ unsigned prompt_id; ///< ID of the prompt which was colored last.
+ char *cmdbuff; ///< What exactly was colored last time or NULL.
+ CmdlineColors colors; ///< Last colors.
+} ColoredCmdline;
+
+/// Keeps track how much state must be sent to external ui.
+typedef enum {
+ kCmdRedrawNone,
+ kCmdRedrawPos,
+ kCmdRedrawAll,
+} CmdRedraw;
/*
* Variables shared between getcmdline(), redrawcmdline() and others.
@@ -69,23 +106,33 @@
* structure.
*/
struct cmdline_info {
- char_u *cmdbuff; /* pointer to command line buffer */
- int cmdbufflen; /* length of cmdbuff */
- int cmdlen; /* number of chars in command line */
- int cmdpos; /* current cursor position */
- int cmdspos; /* cursor column on screen */
- int cmdfirstc; /* ':', '/', '?', '=', '>' or NUL */
- int cmdindent; /* number of spaces before cmdline */
- char_u *cmdprompt; /* message in front of cmdline */
- int cmdattr; /* attributes for prompt */
- int overstrike; /* Typing mode on the command line. Shared by
- getcmdline() and put_on_cmdline(). */
- expand_T *xpc; /* struct being used for expansion, xp_pattern
- may point into cmdbuff */
- int xp_context; /* type of expansion */
- char_u *xp_arg; /* user-defined expansion arg */
- int input_fn; /* when TRUE Invoked for input() function */
+ char_u *cmdbuff; // pointer to command line buffer
+ int cmdbufflen; // length of cmdbuff
+ int cmdlen; // number of chars in command line
+ int cmdpos; // current cursor position
+ int cmdspos; // cursor column on screen
+ int cmdfirstc; // ':', '/', '?', '=', '>' or NUL
+ int cmdindent; // number of spaces before cmdline
+ char_u *cmdprompt; // message in front of cmdline
+ int cmdattr; // attributes for prompt
+ int overstrike; // Typing mode on the command line. Shared by
+ // getcmdline() and put_on_cmdline().
+ expand_T *xpc; // struct being used for expansion, xp_pattern
+ // may point into cmdbuff
+ int xp_context; // type of expansion
+ char_u *xp_arg; // user-defined expansion arg
+ int input_fn; // when TRUE Invoked for input() function
+ unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
+ Callback highlight_callback; ///< Callback used for coloring user input.
+ ColoredCmdline last_colors; ///< Last cmdline colors
+ int level; // current cmdline level
+ struct cmdline_info *prev_ccline; ///< pointer to saved cmdline state
+ char special_char; ///< last putcmdline char (used for redraws)
+ bool special_shift; ///< shift of last putcmdline char
+ CmdRedraw redraw_state; ///< needed redraw for external cmdline
};
+/// Last value of prompt_id, incremented when doing new prompt
+static unsigned last_prompt_id = 0;
typedef struct command_line_state {
VimState state;
@@ -135,6 +182,8 @@ typedef struct command_line_state {
struct cmdline_info save_ccline;
} CommandLineState;
+typedef struct cmdline_info CmdlineInfo;
+
/* The current cmdline_info. It is initialized in getcmdline() and after that
* used by other functions. When invoking getcmdline() recursively it needs
* to be saved with save_cmdline() and restored with restore_cmdline().
@@ -145,6 +194,9 @@ static int cmd_showtail; /* Only show path tail in lists ? */
static int new_cmdpos; /* position set by set_cmdline_pos() */
+/// currently displayed block of context
+static Array cmdline_block = ARRAY_DICT_INIT;
+
/*
* Type used by call_user_expand_func
*/
@@ -156,6 +208,12 @@ static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0};
/* identifying (unique) number of newest history entry */
static int hislen = 0; /* actual length of history tables */
+/// Flag for command_line_handle_key to ignore <C-c>
+///
+/// Used if it was received while processing highlight function in order for
+/// user interrupting highlight function to not interrupt command-line.
+static bool getln_interrupted_highlight = false;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
@@ -192,6 +250,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
cmd_hkmap = 0;
}
+ ccline.prompt_id = last_prompt_id++;
+ ccline.level++;
ccline.overstrike = false; // always start in insert mode
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
@@ -211,6 +271,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.cmdlen = ccline.cmdpos = 0;
ccline.cmdbuff[0] = NUL;
+ ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
+ .colors = KV_INITIAL_VALUE };
+
// autoindent for :insert and :append
if (s->firstc <= 0) {
memset(ccline.cmdbuff, ' ', s->indent);
@@ -288,8 +351,57 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
got_int = false;
s->state.check = command_line_check;
s->state.execute = command_line_execute;
+
+ TryState tstate;
+ Error err = ERROR_INIT;
+ bool tl_ret = true;
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+ char firstcbuf[2];
+ firstcbuf[0] = firstc > 0 ? firstc : '-';
+ firstcbuf[1] = 0;
+
+ if (has_event(EVENT_CMDLINEENTER)) {
+ // set v:event to a dictionary with information about the commandline
+ tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
+ tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
+ tv_dict_set_keys_readonly(dict);
+ try_enter(&tstate);
+
+ apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
+ false, curbuf);
+ tv_dict_clear(dict);
+
+
+ tl_ret = try_leave(&tstate, &err);
+ if (!tl_ret && ERROR_SET(&err)) {
+ msg_putchar('\n');
+ msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
+ api_clear_error(&err);
+ redrawcmd();
+ }
+ tl_ret = true;
+ }
+
state_enter(&s->state);
+ if (has_event(EVENT_CMDLINELEAVE)) {
+ tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
+ tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
+ tv_dict_set_keys_readonly(dict);
+ // not readonly:
+ tv_dict_add_special(dict, S_LEN("abort"),
+ s->gotesc ? kSpecialVarTrue : kSpecialVarFalse);
+ try_enter(&tstate);
+ apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf,
+ false, curbuf);
+ // error printed below, to avoid redraw issues
+ tl_ret = try_leave(&tstate, &err);
+ if (tv_dict_get_number(dict, "abort") != 0) {
+ s->gotesc = 1;
+ }
+ tv_dict_clear(dict);
+ }
+
cmdmsg_rl = false;
cmd_fkmap = 0;
@@ -306,7 +418,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
curwin->w_cursor = s->save_cursor;
setpcmark();
}
- curwin->w_cursor = s->search_start;
+ curwin->w_cursor = s->search_start; // -V519
}
curwin->w_curswant = s->old_curswant;
curwin->w_leftcol = s->old_leftcol;
@@ -315,7 +427,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
curwin->w_botline = s->old_botline;
highlight_match = false;
validate_cursor(); // needed for TAB
- redraw_later(SOME_VALID);
+ redraw_all_later(SOME_VALID);
}
if (ccline.cmdbuff != NULL) {
@@ -332,14 +444,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
}
}
- if (s->gotesc) { // abandon command line
- xfree(ccline.cmdbuff);
- ccline.cmdbuff = NULL;
- if (msg_scrolled == 0) {
- compute_cmdrow();
- }
- MSG("");
- redraw_cmdline = true;
+ if (s->gotesc) {
+ abandon_cmdline();
}
}
@@ -350,8 +456,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
msg_scroll = s->save_msg_scroll;
redir_off = false;
+ if (!tl_ret && ERROR_SET(&err)) {
+ msg_putchar('\n');
+ msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
+ api_clear_error(&err);
+ }
+
// When the command line was typed, no need for a wait-return prompt.
- if (s->some_key_typed) {
+ if (s->some_key_typed && tl_ret) {
need_wait_return = false;
}
@@ -361,12 +473,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
setmouse();
ui_cursor_shape(); // may show different cursor shape
xfree(s->save_p_icm);
+ xfree(ccline.last_colors.cmdbuff);
+ kv_destroy(ccline.last_colors.colors);
{
char_u *p = ccline.cmdbuff;
// Make ccline empty, getcmdline() may try to use it.
ccline.cmdbuff = NULL;
+
+ if (ui_is_external(kUICmdline)) {
+ ccline.redraw_state = kCmdRedrawNone;
+ ui_call_cmdline_hide(ccline.level);
+ }
+ ccline.level--;
return p;
}
}
@@ -392,8 +512,12 @@ static int command_line_execute(VimState *state, int key)
CommandLineState *s = (CommandLineState *)state;
s->c = key;
- if (s->c == K_EVENT) {
- multiqueue_process_events(main_loop.events);
+ if (s->c == K_EVENT || s->c == K_COMMAND) {
+ if (s->c == K_EVENT) {
+ multiqueue_process_events(main_loop.events);
+ } else {
+ do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
+ }
redrawcmdline();
return 1;
}
@@ -472,11 +596,15 @@ static int command_line_execute(VimState *state, int key)
}
// free expanded names when finished walking through matches
- if (s->xpc.xp_numfiles != -1
- && !(s->c == p_wc && KeyTyped) && s->c != p_wcm
+ if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm
&& s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
&& s->c != Ctrl_L) {
- (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
+ if (ui_is_external(kUIWildmenu)) {
+ ui_call_wildmenu_hide();
+ }
+ if (s->xpc.xp_numfiles != -1) {
+ (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
+ }
s->did_wild_list = false;
if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) {
s->xpc.xp_context = EXPAND_NOTHING;
@@ -609,7 +737,7 @@ static int command_line_execute(VimState *state, int key)
}
if (vim_ispathsep(ccline.cmdbuff[s->j])
#ifdef BACKSLASH_IN_FILENAME
- && vim_strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1])
+ && vim_strchr((const char_u *)" *?[{`$%#", ccline.cmdbuff[s->j + 1])
== NULL
#endif
) {
@@ -756,7 +884,9 @@ static int command_line_execute(VimState *state, int key)
}
if (!cmd_silent) {
- ui_cursor_goto(msg_row, 0);
+ if (!ui_is_external(kUICmdline)) {
+ ui_cursor_goto(msg_row, 0);
+ }
ui_flush();
}
return 0;
@@ -888,17 +1018,36 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
ui_flush();
pos_T t;
- int search_flags = SEARCH_KEEP + SEARCH_NOOF + SEARCH_PEEK;
+ char_u *pat;
+ int search_flags = SEARCH_NOOF;
+
+
+ if (s->firstc == ccline.cmdbuff[0]) {
+ pat = last_search_pattern();
+ } else {
+ pat = ccline.cmdbuff;
+ }
+
+ save_last_search_pattern();
+
if (next_match) {
t = s->match_end;
+ if (lt(s->match_start, s->match_end)) {
+ // start searching at the end of the match
+ // not at the beginning of the next column
+ (void)decl(&t);
+ }
search_flags += SEARCH_COL;
} else {
t = s->match_start;
}
+ if (!p_hls) {
+ search_flags += SEARCH_KEEP;
+ }
emsg_off++;
s->i = searchit(curwin, curbuf, &t,
next_match ? FORWARD : BACKWARD,
- ccline.cmdbuff, s->count, search_flags,
+ pat, s->count, search_flags,
RE_SEARCH, 0, NULL);
emsg_off--;
ui_busy_stop();
@@ -912,6 +1061,12 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
// put back on the match
s->search_start = t;
(void)decl(&s->search_start);
+ } else if (next_match && s->firstc == '?') {
+ // move just after the current match, so that
+ // when nv_search finishes the cursor will be
+ // put back on the match
+ s->search_start = t;
+ (void)incl(&s->search_start);
}
if (lt(t, s->search_start) && next_match) {
// wrap around
@@ -939,6 +1094,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
} else {
vim_beep(BO_ERROR);
}
+ restore_last_search_pattern();
return;
}
@@ -1086,7 +1242,7 @@ static int command_line_handle_key(CommandLineState *s)
xfree(ccline.cmdbuff); // no commandline to return
ccline.cmdbuff = NULL;
- if (!cmd_silent) {
+ if (!cmd_silent && !ui_is_external(kUICmdline)) {
if (cmdmsg_rl) {
msg_col = Columns;
} else {
@@ -1158,8 +1314,11 @@ static int command_line_handle_key(CommandLineState *s)
case ESC: // get here if p_wc != ESC or when ESC typed twice
case Ctrl_C:
// In exmode it doesn't make sense to return. Except when
- // ":normal" runs out of characters.
- if (exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) {
+ // ":normal" runs out of characters. Also when highlight callback is active
+ // <C-c> should interrupt only it.
+ if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0))
+ || (getln_interrupted_highlight && s->c == Ctrl_C)) {
+ getln_interrupted_highlight = false;
return command_line_not_changed(s);
}
@@ -1222,6 +1381,7 @@ static int command_line_handle_key(CommandLineState *s)
break; // Use ^D as normal char instead
}
+ wild_menu_showing = WM_LIST;
redrawcmd();
return 1; // don't do incremental search now
@@ -1394,7 +1554,7 @@ static int command_line_handle_key(CommandLineState *s)
}
if (s->c != NUL) {
if (s->c == s->firstc
- || vim_strchr((char_u *)(p_magic ? "\\^$.*[" : "\\^$"), s->c)
+ || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), s->c)
!= NULL) {
// put a backslash before special characters
stuffcharReadbuff(s->c);
@@ -1452,7 +1612,7 @@ static int command_line_handle_key(CommandLineState *s)
if (s->hiscnt != s->i) {
// jumped to other entry
char_u *p;
- int len;
+ int len = 0;
int old_firstc;
xfree(ccline.cmdbuff);
@@ -1518,10 +1678,9 @@ static int command_line_handle_key(CommandLineState *s)
case Ctrl_G: // next match
case Ctrl_T: // previous match
if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
- if (char_avail()) {
- return 1;
+ if (ccline.cmdlen != 0) {
+ command_line_next_incsearch(s, s->c == Ctrl_G);
}
- command_line_next_incsearch(s, s->c == Ctrl_G);
return command_line_not_changed(s);
}
break;
@@ -1534,9 +1693,14 @@ static int command_line_handle_key(CommandLineState *s)
s->do_abbr = false; // don't do abbreviation now
// may need to remove ^ when composing char was typed
if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) {
- draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
- msg_putchar(' ');
- cursorcmd();
+ if (ui_is_external(kUICmdline)) {
+ // TODO(bfredl): why not make unputcmdline also work with true?
+ unputcmdline();
+ } else {
+ draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
+ msg_putchar(' ');
+ cursorcmd();
+ }
}
break;
@@ -1568,14 +1732,6 @@ static int command_line_handle_key(CommandLineState *s)
}
return command_line_not_changed(s);
- case K_FOCUSGAINED: // Neovim has been given focus
- apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
- return command_line_not_changed(s);
-
- case K_FOCUSLOST: // Neovim has lost focus
- apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
- return command_line_not_changed(s);
-
default:
// Normal character with no special meaning. Just set mod_mask
// to 0x0 so that typing Shift-Space in the GUI doesn't enter
@@ -1627,6 +1783,20 @@ static int command_line_not_changed(CommandLineState *s)
return command_line_changed(s);
}
+/// Guess that the pattern matches everything. Only finds specific cases, such
+/// as a trailing \|, which can happen while typing a pattern.
+static int empty_pattern(char_u *p)
+{
+ int n = STRLEN(p);
+
+ // remove trailing \v and the like
+ while (n >= 2 && p[n - 2] == '\\'
+ && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) {
+ n -= 2;
+ }
+ return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|');
+}
+
static int command_line_changed(CommandLineState *s)
{
// 'incsearch' highlighting.
@@ -1641,20 +1811,27 @@ static int command_line_changed(CommandLineState *s)
}
s->incsearch_postponed = false;
curwin->w_cursor = s->search_start; // start at old position
+ save_last_search_pattern();
// If there is no command line, don't do anything
if (ccline.cmdlen == 0) {
s->i = 0;
+ SET_NO_HLSEARCH(true); // turn off previous highlight
+ redraw_all_later(SOME_VALID);
} else {
+ int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
ui_busy_start();
ui_flush();
++emsg_off; // So it doesn't beep if bad expr
// Set the time limit to half a second.
tm = profile_setlimit(500L);
+ if (!p_hls) {
+ search_flags += SEARCH_KEEP;
+ }
s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count,
- SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK,
- &tm);
- --emsg_off;
+ search_flags,
+ &tm);
+ emsg_off--;
// if interrupted while searching, behave like it failed
if (got_int) {
(void)vpeekc(); // remove <C-C> from input stream
@@ -1695,6 +1872,12 @@ static int command_line_changed(CommandLineState *s)
end_pos = curwin->w_cursor; // shutup gcc 4
}
+ // Disable 'hlsearch' highlighting if the pattern matches
+ // everything. Avoids a flash when typing "foo\|".
+ if (empty_pattern(ccline.cmdbuff)) {
+ SET_NO_HLSEARCH(true);
+ }
+
validate_cursor();
// May redraw the status line to show the cursor position.
if (p_ru && curwin->w_status_height > 0) {
@@ -1704,6 +1887,7 @@ static int command_line_changed(CommandLineState *s)
save_cmdline(&s->save_ccline);
update_screen(SOME_VALID);
restore_cmdline(&s->save_ccline);
+ restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work.
if (s->i != 0) {
@@ -1750,7 +1934,8 @@ static int command_line_changed(CommandLineState *s)
// right-left typing. Not efficient, but it works.
// Do it only when there are no characters left to read
// to avoid useless intermediate redraws.
- if (vpeekc() == NUL) {
+ // if cmdline is external the ui handles shaping, no redraw needed.
+ if (!ui_is_external(kUICmdline) && vpeekc() == NUL) {
redrawcmd();
}
}
@@ -1758,6 +1943,18 @@ static int command_line_changed(CommandLineState *s)
return 1;
}
+/// Abandon the command line.
+static void abandon_cmdline(void)
+{
+ xfree(ccline.cmdbuff);
+ ccline.cmdbuff = NULL;
+ if (msg_scrolled == 0) {
+ compute_cmdrow();
+ }
+ MSG("");
+ redraw_cmdline = true;
+}
+
/*
* getcmdline() - accept a command line starting with firstc.
*
@@ -1787,41 +1984,54 @@ getcmdline (
return command_line_enter(firstc, count, indent);
}
-/*
- * Get a command line with a prompt.
- * This is prepared to be called recursively from getcmdline() (e.g. by
- * f_input() when evaluating an expression from CTRL-R =).
- * Returns the command line in allocated memory, or NULL.
- */
-char_u *
-getcmdline_prompt (
- int firstc,
- char_u *prompt, /* command line prompt */
- int attr, /* attributes for prompt */
- int xp_context, /* type of expansion */
- char_u *xp_arg /* user-defined expansion argument */
-)
+/// Get a command line with a prompt
+///
+/// This is prepared to be called recursively from getcmdline() (e.g. by
+/// f_input() when evaluating an expression from `<C-r>=`).
+///
+/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug.
+/// @param[in] prompt Prompt string: what is displayed before the user text.
+/// @param[in] attr Prompt highlighting.
+/// @param[in] xp_context Type of expansion.
+/// @param[in] xp_arg User-defined expansion argument.
+/// @param[in] highlight_callback Callback used for highlighting user input.
+///
+/// @return [allocated] Command line or NULL.
+char *getcmdline_prompt(const char firstc, const char *const prompt,
+ const int attr, const int xp_context,
+ const char *const xp_arg,
+ const Callback highlight_callback)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
- char_u *s;
- struct cmdline_info save_ccline;
- int msg_col_save = msg_col;
+ const int msg_col_save = msg_col;
+ struct cmdline_info save_ccline;
save_cmdline(&save_ccline);
- ccline.cmdprompt = prompt;
+
+ ccline.prompt_id = last_prompt_id++;
+ ccline.cmdprompt = (char_u *)prompt;
ccline.cmdattr = attr;
ccline.xp_context = xp_context;
- ccline.xp_arg = xp_arg;
+ ccline.xp_arg = (char_u *)xp_arg;
ccline.input_fn = (firstc == '@');
- s = getcmdline(firstc, 1L, 0);
+ ccline.highlight_callback = highlight_callback;
+
+ int msg_silent_saved = msg_silent;
+ msg_silent = 0;
+
+ char *const ret = (char *)getcmdline(firstc, 1L, 0);
+
restore_cmdline(&save_ccline);
- /* Restore msg_col, the prompt from input() may have changed it.
- * But only if called recursively and the commandline is therefore being
- * restored to an old one; if not, the input() prompt stays on the screen,
- * so we need its modified msg_col left intact. */
- if (ccline.cmdbuff != NULL)
+ msg_silent = msg_silent_saved;
+ // Restore msg_col, the prompt from input() may have changed it.
+ // But only if called recursively and the commandline is therefore being
+ // restored to an old one; if not, the input() prompt stays on the screen,
+ // so we need its modified msg_col left intact.
+ if (ccline.cmdbuff != NULL) {
msg_col = msg_col_save;
+ }
- return s;
+ return ret;
}
/*
@@ -2018,7 +2228,13 @@ getexmodeline (
/* Get one character at a time. Don't use inchar(), it can't handle
* special characters. */
prev_char = c1;
- c1 = vgetc();
+
+ // Check for a ":normal" command and no more characters left.
+ if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
+ c1 = '\n';
+ } else {
+ c1 = vgetc();
+ }
/*
* Handle line editing.
@@ -2282,75 +2498,389 @@ void free_cmdline_buf(void)
# endif
+enum { MAX_CB_ERRORS = 1 };
+
+/// Color expression cmdline using built-in expressions parser
+///
+/// @param[in] colored_ccline Command-line to color.
+/// @param[out] ret_ccline_colors What should be colored.
+///
+/// Always colors the whole cmdline.
+static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
+ ColoredCmdline *const ret_ccline_colors)
+ FUNC_ATTR_NONNULL_ALL
+{
+ ParserLine plines[] = {
+ {
+ .data = (const char *)colored_ccline->cmdbuff,
+ .size = STRLEN(colored_ccline->cmdbuff),
+ .allocated = false,
+ },
+ { NULL, 0, false },
+ };
+ ParserLine *plines_p = plines;
+ ParserHighlight colors;
+ kvi_init(colors);
+ ParserState pstate;
+ viml_parser_init(
+ &pstate, parser_simple_get_line, &plines_p, &colors);
+ ExprAST east = viml_pexpr_parse(&pstate, kExprFlagsDisallowEOC);
+ viml_pexpr_free_ast(east);
+ viml_parser_destroy(&pstate);
+ kv_resize(ret_ccline_colors->colors, kv_size(colors));
+ size_t prev_end = 0;
+ for (size_t i = 0 ; i < kv_size(colors) ; i++) {
+ const ParserHighlightChunk chunk = kv_A(colors, i);
+ if (chunk.start.col != prev_end) {
+ kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = prev_end,
+ .end = chunk.start.col,
+ .attr = 0,
+ }));
+ }
+ const int id = syn_name2id((const char_u *)chunk.group);
+ const int attr = (id == 0 ? 0 : syn_id2attr(id));
+ kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = chunk.start.col,
+ .end = chunk.end_col,
+ .attr = attr,
+ }));
+ prev_end = chunk.end_col;
+ }
+ if (prev_end < (size_t)colored_ccline->cmdlen) {
+ kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = prev_end,
+ .end = (size_t)colored_ccline->cmdlen,
+ .attr = 0,
+ }));
+ }
+ kvi_destroy(colors);
+}
+
+/// Color command-line
+///
+/// Should use built-in command parser or user-specified one. Currently only the
+/// latter is supported.
+///
+/// @param[in,out] colored_ccline Command-line to color. Also holds a cache:
+/// if ->prompt_id and ->cmdbuff values happen
+/// to be equal to those from colored_cmdline it
+/// will just do nothing, assuming that ->colors
+/// already contains needed data.
+///
+/// Always colors the whole cmdline.
+///
+/// @return true if draw_cmdline may proceed, false if it does not need anything
+/// to do.
+static bool color_cmdline(CmdlineInfo *colored_ccline)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ bool printed_errmsg = false;
+
+#define PRINT_ERRMSG(...) \
+ do { \
+ msg_putchar('\n'); \
+ msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, __VA_ARGS__); \
+ printed_errmsg = true; \
+ } while (0)
+ bool ret = true;
+
+ ColoredCmdline *ccline_colors = &colored_ccline->last_colors;
+
+ // Check whether result of the previous call is still valid.
+ if (ccline_colors->prompt_id == colored_ccline->prompt_id
+ && ccline_colors->cmdbuff != NULL
+ && STRCMP(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) {
+ return ret;
+ }
+
+ kv_size(ccline_colors->colors) = 0;
+
+ if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) {
+ // Nothing to do, exiting.
+ xfree(ccline_colors->cmdbuff);
+ ccline_colors->cmdbuff = NULL;
+ return ret;
+ }
+
+ bool arg_allocated = false;
+ typval_T arg = {
+ .v_type = VAR_STRING,
+ .vval.v_string = colored_ccline->cmdbuff,
+ };
+ typval_T tv = { .v_type = VAR_UNKNOWN };
+
+ static unsigned prev_prompt_id = UINT_MAX;
+ static int prev_prompt_errors = 0;
+ Callback color_cb = CALLBACK_NONE;
+ bool can_free_cb = false;
+ TryState tstate;
+ Error err = ERROR_INIT;
+ const char *err_errmsg = (const char *)e_intern2;
+ bool dgc_ret = true;
+ bool tl_ret = true;
+
+ if (colored_ccline->prompt_id != prev_prompt_id) {
+ prev_prompt_errors = 0;
+ prev_prompt_id = colored_ccline->prompt_id;
+ } else if (prev_prompt_errors >= MAX_CB_ERRORS) {
+ goto color_cmdline_end;
+ }
+ if (colored_ccline->highlight_callback.type != kCallbackNone) {
+ // Currently this should only happen while processing input() prompts.
+ assert(colored_ccline->input_fn);
+ color_cb = colored_ccline->highlight_callback;
+ } else if (colored_ccline->cmdfirstc == ':') {
+ try_enter(&tstate);
+ err_errmsg = N_(
+ "E5408: Unable to get g:Nvim_color_cmdline callback: %s");
+ dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
+ &color_cb);
+ tl_ret = try_leave(&tstate, &err);
+ can_free_cb = true;
+ } else if (colored_ccline->cmdfirstc == '=') {
+ color_expr_cmdline(colored_ccline, ccline_colors);
+ }
+ if (!tl_ret || !dgc_ret) {
+ goto color_cmdline_error;
+ }
+
+ if (color_cb.type == kCallbackNone) {
+ goto color_cmdline_end;
+ }
+ if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) {
+ arg_allocated = true;
+ arg.vval.v_string = xmemdupz((const char *)colored_ccline->cmdbuff,
+ (size_t)colored_ccline->cmdlen);
+ }
+ // msg_start() called by e.g. :echo may shift command-line to the first column
+ // even though msg_silent is here. Two ways to workaround this problem without
+ // altering message.c: use full_screen or save and restore msg_col.
+ //
+ // Saving and restoring full_screen does not work well with :redraw!. Saving
+ // and restoring msg_col is neither ideal, but while with full_screen it
+ // appears shifted one character to the right and cursor position is no longer
+ // correct, with msg_col it just misses leading `:`. Since `redraw!` in
+ // callback lags this is least of the user problems.
+ //
+ // Also using try_enter() because error messages may overwrite typed
+ // command-line which is not expected.
+ getln_interrupted_highlight = false;
+ try_enter(&tstate);
+ err_errmsg = N_("E5407: Callback has thrown an exception: %s");
+ const int saved_msg_col = msg_col;
+ msg_silent++;
+ const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
+ msg_silent--;
+ msg_col = saved_msg_col;
+ if (got_int) {
+ getln_interrupted_highlight = true;
+ }
+ if (!try_leave(&tstate, &err) || !cbcall_ret) {
+ goto color_cmdline_error;
+ }
+ if (tv.v_type != VAR_LIST) {
+ PRINT_ERRMSG(_("E5400: Callback should return list"));
+ goto color_cmdline_error;
+ }
+ if (tv.vval.v_list == NULL) {
+ goto color_cmdline_end;
+ }
+ varnumber_T prev_end = 0;
+ int i = 0;
+ TV_LIST_ITER_CONST(tv.vval.v_list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) {
+ PRINT_ERRMSG(_("E5401: List item %i is not a List"), i);
+ goto color_cmdline_error;
+ }
+ const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list;
+ if (tv_list_len(l) != 3) {
+ PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"),
+ i, tv_list_len(l));
+ goto color_cmdline_error;
+ }
+ bool error = false;
+ const varnumber_T start = (
+ tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error));
+ if (error) {
+ goto color_cmdline_error;
+ } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) {
+ PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range "
+ "[%" PRIdVARNUMBER ", %i)"),
+ i, start, prev_end, colored_ccline->cmdlen);
+ goto color_cmdline_error;
+ } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) {
+ PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits "
+ "multibyte character"), i, start);
+ goto color_cmdline_error;
+ }
+ if (start != prev_end) {
+ kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = prev_end,
+ .end = start,
+ .attr = 0,
+ }));
+ }
+ const varnumber_T end = tv_get_number_chk(
+ TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error);
+ if (error) {
+ goto color_cmdline_error;
+ } else if (!(start < end && end <= colored_ccline->cmdlen)) {
+ PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range "
+ "(%" PRIdVARNUMBER ", %i]"),
+ i, end, start, colored_ccline->cmdlen);
+ goto color_cmdline_error;
+ } else if (end < colored_ccline->cmdlen
+ && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]]
+ == 0)) {
+ PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte "
+ "character"), i, end);
+ goto color_cmdline_error;
+ }
+ prev_end = end;
+ const char *const group = tv_get_string_chk(
+ TV_LIST_ITEM_TV(tv_list_last(l)));
+ if (group == NULL) {
+ goto color_cmdline_error;
+ }
+ const int id = syn_name2id((char_u *)group);
+ const int attr = (id == 0 ? 0 : syn_id2attr(id));
+ kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = start,
+ .end = end,
+ .attr = attr,
+ }));
+ i++;
+ });
+ if (prev_end < colored_ccline->cmdlen) {
+ kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = prev_end,
+ .end = colored_ccline->cmdlen,
+ .attr = 0,
+ }));
+ }
+ prev_prompt_errors = 0;
+color_cmdline_end:
+ assert(!ERROR_SET(&err));
+ if (can_free_cb) {
+ callback_free(&color_cb);
+ }
+ xfree(ccline_colors->cmdbuff);
+ // Note: errors “output” is cached just as well as regular results.
+ ccline_colors->prompt_id = colored_ccline->prompt_id;
+ if (arg_allocated) {
+ ccline_colors->cmdbuff = (char *)arg.vval.v_string;
+ } else {
+ ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff,
+ (size_t)colored_ccline->cmdlen);
+ }
+ tv_clear(&tv);
+ return ret;
+color_cmdline_error:
+ if (ERROR_SET(&err)) {
+ PRINT_ERRMSG(_(err_errmsg), err.msg);
+ api_clear_error(&err);
+ }
+ assert(printed_errmsg);
+ (void)printed_errmsg;
+
+ prev_prompt_errors++;
+ kv_size(ccline_colors->colors) = 0;
+ redrawcmdline();
+ ret = false;
+ goto color_cmdline_end;
+#undef PRINT_ERRMSG
+}
+
/*
* Draw part of the cmdline at the current cursor position. But draw stars
* when cmdline_star is TRUE.
*/
static void draw_cmdline(int start, int len)
{
- int i;
+ if (!color_cmdline(&ccline)) {
+ return;
+ }
- if (cmdline_star > 0)
- for (i = 0; i < len; ++i) {
+ if (ui_is_external(kUICmdline)) {
+ ccline.special_char = NUL;
+ ccline.redraw_state = kCmdRedrawAll;
+ return;
+ }
+
+ if (cmdline_star > 0) {
+ for (int i = 0; i < len; i++) {
msg_putchar('*');
- if (has_mbyte)
+ if (has_mbyte) {
i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1;
+ }
}
- else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
- static int buflen = 0;
- char_u *p;
- int j;
- int newlen = 0;
+ } else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
+ bool do_arabicshape = false;
int mb_l;
- int pc, pc1 = 0;
- int prev_c = 0;
- int prev_c1 = 0;
- int u8c;
- int u8cc[MAX_MCO];
- int nc = 0;
+ for (int i = start; i < start + len; i += mb_l) {
+ char_u *p = ccline.cmdbuff + i;
+ int u8cc[MAX_MCO];
+ int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
+ mb_l = utfc_ptr2len_len(p, start + len - i);
+ if (arabic_char(u8c)) {
+ do_arabicshape = true;
+ break;
+ }
+ }
+ if (!do_arabicshape) {
+ goto draw_cmdline_no_arabicshape;
+ }
- /*
- * Do arabic shaping into a temporary buffer. This is very
- * inefficient!
- */
+ static int buflen = 0;
+
+ // Do arabic shaping into a temporary buffer. This is very
+ // inefficient!
if (len * 2 + 2 > buflen) {
- /* Re-allocate the buffer. We keep it around to avoid a lot of
- * alloc()/free() calls. */
+ // Re-allocate the buffer. We keep it around to avoid a lot of
+ // alloc()/free() calls.
xfree(arshape_buf);
buflen = len * 2 + 2;
arshape_buf = xmalloc(buflen);
}
+ int newlen = 0;
if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) {
- /* Prepend a space to draw the leading composing char on. */
+ // Prepend a space to draw the leading composing char on.
arshape_buf[0] = ' ';
newlen = 1;
}
- for (j = start; j < start + len; j += mb_l) {
- p = ccline.cmdbuff + j;
- u8c = utfc_ptr2char_len(p, u8cc, start + len - j);
- mb_l = utfc_ptr2len_len(p, start + len - j);
+ int prev_c = 0;
+ int prev_c1 = 0;
+ for (int i = start; i < start + len; i += mb_l) {
+ char_u *p = ccline.cmdbuff + i;
+ int u8cc[MAX_MCO];
+ int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
+ mb_l = utfc_ptr2len_len(p, start + len - i);
if (arabic_char(u8c)) {
- /* Do Arabic shaping. */
+ int pc;
+ int pc1 = 0;
+ int nc = 0;
+ // Do Arabic shaping.
if (cmdmsg_rl) {
- /* displaying from right to left */
+ // Displaying from right to left.
pc = prev_c;
pc1 = prev_c1;
prev_c1 = u8cc[0];
- if (j + mb_l >= start + len)
+ if (i + mb_l >= start + len) {
nc = NUL;
- else
+ } else {
nc = utf_ptr2char(p + mb_l);
+ }
} else {
- /* displaying from left to right */
- if (j + mb_l >= start + len)
+ // Displaying from left to right.
+ if (i + mb_l >= start + len) {
pc = NUL;
- else {
+ } else {
int pcc[MAX_MCO];
- pc = utfc_ptr2char_len(p + mb_l, pcc,
- start + len - j - mb_l);
+ pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l);
pc1 = pcc[0];
}
nc = prev_c;
@@ -2374,8 +2904,147 @@ static void draw_cmdline(int start, int len)
}
msg_outtrans_len(arshape_buf, newlen);
- } else
- msg_outtrans_len(ccline.cmdbuff + start, len);
+ } else {
+draw_cmdline_no_arabicshape:
+ if (kv_size(ccline.last_colors.colors)) {
+ for (size_t i = 0; i < kv_size(ccline.last_colors.colors); i++) {
+ CmdlineColorChunk chunk = kv_A(ccline.last_colors.colors, i);
+ if (chunk.end <= start) {
+ continue;
+ }
+ const int chunk_start = MAX(chunk.start, start);
+ msg_outtrans_len_attr(ccline.cmdbuff + chunk_start,
+ chunk.end - chunk_start,
+ chunk.attr);
+ }
+ } else {
+ msg_outtrans_len(ccline.cmdbuff + start, len);
+ }
+ }
+}
+
+static void ui_ext_cmdline_show(CmdlineInfo *line)
+{
+ Array content = ARRAY_DICT_INIT;
+ if (cmdline_star) {
+ size_t len = 0;
+ for (char_u *p = ccline.cmdbuff; *p; mb_ptr_adv(p)) {
+ len++;
+ }
+ char *buf = xmallocz(len);
+ memset(buf, '*', len);
+ Array item = ARRAY_DICT_INIT;
+ ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ ADD(item, STRING_OBJ(((String) { .data = buf, .size = len })));
+ ADD(content, ARRAY_OBJ(item));
+ } else if (kv_size(line->last_colors.colors)) {
+ for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
+ CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
+ Array item = ARRAY_DICT_INIT;
+
+ if (chunk.attr) {
+ HlAttrs *aep = syn_cterm_attr2entry(chunk.attr);
+ // TODO(bfredl): this desicion could be delayed by making attr_code a
+ // recognized type
+ Dictionary rgb_attrs = hlattrs2dict(aep, true);
+ ADD(item, DICTIONARY_OBJ(rgb_attrs));
+ } else {
+ ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ }
+ ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start,
+ chunk.end-chunk.start)));
+ ADD(content, ARRAY_OBJ(item));
+ }
+ } else {
+ Array item = ARRAY_DICT_INIT;
+ ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff))));
+ ADD(content, ARRAY_OBJ(item));
+ }
+ ui_call_cmdline_show(content, line->cmdpos,
+ cchar_to_string((char)line->cmdfirstc),
+ cstr_to_string((char *)(line->cmdprompt)),
+ line->cmdindent,
+ line->level);
+ if (line->special_char) {
+ ui_call_cmdline_special_char(cchar_to_string((char)(line->special_char)),
+ line->special_shift,
+ line->level);
+ }
+}
+
+void ui_ext_cmdline_block_append(int indent, const char *line)
+{
+ char *buf = xmallocz(indent + strlen(line));
+ memset(buf, ' ', indent);
+ memcpy(buf + indent, line, strlen(line)); // -V575
+
+ Array item = ARRAY_DICT_INIT;
+ ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ ADD(item, STRING_OBJ(cstr_as_string(buf)));
+ Array content = ARRAY_DICT_INIT;
+ ADD(content, ARRAY_OBJ(item));
+ ADD(cmdline_block, ARRAY_OBJ(content));
+ if (cmdline_block.size > 1) {
+ ui_call_cmdline_block_append(copy_array(content));
+ } else {
+ ui_call_cmdline_block_show(copy_array(cmdline_block));
+ }
+}
+
+void ui_ext_cmdline_block_leave(void)
+{
+ api_free_array(cmdline_block);
+ cmdline_block = (Array)ARRAY_DICT_INIT;
+ ui_call_cmdline_block_hide();
+}
+
+/// Extra redrawing needed for redraw! and on ui_attach
+/// assumes "redrawcmdline()" will already be invoked
+void cmdline_screen_cleared(void)
+{
+ if (!ui_is_external(kUICmdline)) {
+ return;
+ }
+
+ if (cmdline_block.size) {
+ ui_call_cmdline_block_show(copy_array(cmdline_block));
+ }
+
+ int prev_level = ccline.level-1;
+ CmdlineInfo *prev_ccline = ccline.prev_ccline;
+ while (prev_level > 0 && prev_ccline) {
+ if (prev_ccline->level == prev_level) {
+ // don't redraw a cmdline already shown in the cmdline window
+ if (prev_level != cmdwin_level) {
+ prev_ccline->redraw_state = kCmdRedrawAll;
+ }
+ prev_level--;
+ }
+ prev_ccline = prev_ccline->prev_ccline;
+ }
+}
+
+/// called by ui_flush, do what redraws neccessary to keep cmdline updated.
+void cmdline_ui_flush(void)
+{
+ if (!ui_is_external(kUICmdline)) {
+ return;
+ }
+ int level = ccline.level;
+ CmdlineInfo *line = &ccline;
+ while (level > 0 && line) {
+ if (line->level == level) {
+ if (line->redraw_state == kCmdRedrawAll) {
+ ui_ext_cmdline_show(line);
+ } else if (line->redraw_state == kCmdRedrawPos) {
+ ui_call_cmdline_pos(line->cmdpos, line->level);
+ }
+ line->redraw_state = kCmdRedrawNone;
+ level--;
+ }
+ line = line->prev_ccline;
+ }
}
/*
@@ -2385,33 +3054,41 @@ static void draw_cmdline(int start, int len)
*/
void putcmdline(int c, int shift)
{
- if (cmd_silent)
+ if (cmd_silent) {
return;
- msg_no_more = TRUE;
- msg_putchar(c);
- if (shift)
- draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
- msg_no_more = FALSE;
+ }
+ if (!ui_is_external(kUICmdline)) {
+ msg_no_more = true;
+ msg_putchar(c);
+ if (shift) {
+ draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
+ }
+ msg_no_more = false;
+ } else {
+ ccline.special_char = c;
+ ccline.special_shift = shift;
+ if (ccline.redraw_state != kCmdRedrawAll) {
+ ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift,
+ ccline.level);
+ }
+ }
cursorcmd();
ui_cursor_shape();
}
-/*
- * Undo a putcmdline(c, FALSE).
- */
+/// Undo a putcmdline(c, FALSE).
void unputcmdline(void)
{
- if (cmd_silent)
+ if (cmd_silent) {
return;
- msg_no_more = TRUE;
- if (ccline.cmdlen == ccline.cmdpos)
+ }
+ msg_no_more = true;
+ if (ccline.cmdlen == ccline.cmdpos && !ui_is_external(kUICmdline)) {
msg_putchar(' ');
- else if (has_mbyte)
- draw_cmdline(ccline.cmdpos,
- (*mb_ptr2len)(ccline.cmdbuff + ccline.cmdpos));
- else
- draw_cmdline(ccline.cmdpos, 1);
- msg_no_more = FALSE;
+ } else {
+ draw_cmdline(ccline.cmdpos, mb_ptr2len(ccline.cmdbuff + ccline.cmdpos));
+ }
+ msg_no_more = false;
cursorcmd();
ui_cursor_shape();
}
@@ -2545,9 +3222,6 @@ void put_on_cmdline(char_u *str, int len, int redraw)
msg_check();
}
-static struct cmdline_info prev_ccline;
-static int prev_ccline_used = FALSE;
-
/*
* Save ccline, because obtaining the "=" register may execute "normal :cmd"
* and overwrite it. But get_cmdline_str() may need it, thus make it
@@ -2555,15 +3229,12 @@ static int prev_ccline_used = FALSE;
*/
static void save_cmdline(struct cmdline_info *ccp)
{
- if (!prev_ccline_used) {
- memset(&prev_ccline, 0, sizeof(struct cmdline_info));
- prev_ccline_used = TRUE;
- }
- *ccp = prev_ccline;
- prev_ccline = ccline;
+ *ccp = ccline;
+ ccline.prev_ccline = ccp;
ccline.cmdbuff = NULL;
ccline.cmdprompt = NULL;
ccline.xpc = NULL;
+ ccline.special_char = NUL;
}
/*
@@ -2571,8 +3242,7 @@ static void save_cmdline(struct cmdline_info *ccp)
*/
static void restore_cmdline(struct cmdline_info *ccp)
{
- ccline = prev_ccline;
- prev_ccline = *ccp;
+ ccline = *ccp;
}
/*
@@ -2740,17 +3410,25 @@ static void redrawcmdprompt(void)
if (cmd_silent)
return;
- if (ccline.cmdfirstc != NUL)
+ if (ui_is_external(kUICmdline)) {
+ ccline.redraw_state = kCmdRedrawAll;
+ return;
+ }
+ if (ccline.cmdfirstc != NUL) {
msg_putchar(ccline.cmdfirstc);
+ }
if (ccline.cmdprompt != NULL) {
msg_puts_attr((const char *)ccline.cmdprompt, ccline.cmdattr);
ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns;
- /* do the reverse of set_cmdspos() */
- if (ccline.cmdfirstc != NUL)
- --ccline.cmdindent;
- } else
- for (i = ccline.cmdindent; i > 0; --i)
+ // do the reverse of set_cmdspos()
+ if (ccline.cmdfirstc != NUL) {
+ ccline.cmdindent--;
+ }
+ } else {
+ for (i = ccline.cmdindent; i > 0; i--) {
msg_putchar(' ');
+ }
+ }
}
/*
@@ -2761,6 +3439,11 @@ void redrawcmd(void)
if (cmd_silent)
return;
+ if (ui_is_external(kUICmdline)) {
+ draw_cmdline(0, ccline.cmdlen);
+ return;
+ }
+
/* when 'incsearch' is set there may be no command line while redrawing */
if (ccline.cmdbuff == NULL) {
ui_cursor_goto(cmdline_row, 0);
@@ -2804,6 +3487,13 @@ static void cursorcmd(void)
if (cmd_silent)
return;
+ if (ui_is_external(kUICmdline)) {
+ if (ccline.redraw_state < kCmdRedrawPos) {
+ ccline.redraw_state = kCmdRedrawPos;
+ }
+ return;
+ }
+
if (cmdmsg_rl) {
msg_row = cmdline_row + (ccline.cmdspos / (int)(Columns - 1));
msg_col = (int)Columns - (ccline.cmdspos % (int)(Columns - 1)) - 1;
@@ -2821,6 +3511,9 @@ static void cursorcmd(void)
void gotocmdline(int clr)
{
+ if (ui_is_external(kUICmdline)) {
+ return;
+ }
msg_start();
if (cmdmsg_rl)
msg_col = Columns - 1;
@@ -2888,8 +3581,10 @@ nextwild (
return FAIL;
}
- MSG_PUTS("..."); /* show that we are busy */
- ui_flush();
+ if (!ui_is_external(kUIWildmenu)) {
+ MSG_PUTS("..."); // show that we are busy
+ ui_flush();
+ }
i = (int)(xp->xp_pattern - ccline.cmdbuff);
xp->xp_pattern_len = ccline.cmdpos - i;
@@ -3042,11 +3737,17 @@ ExpandOne (
else
findex = -1;
}
- if (p_wmnu)
- win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
- findex, cmd_showtail);
- if (findex == -1)
+ if (p_wmnu) {
+ if (ui_is_external(kUIWildmenu)) {
+ ui_call_wildmenu_select(findex);
+ } else {
+ win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
+ findex, cmd_showtail);
+ }
+ }
+ if (findex == -1) {
return vim_strsave(orig_save);
+ }
return vim_strsave(xp->xp_files[findex]);
} else
return NULL;
@@ -3403,6 +4104,15 @@ static int showmatches(expand_T *xp, int wildmenu)
showtail = cmd_showtail;
}
+ if (ui_is_external(kUIWildmenu)) {
+ Array args = ARRAY_DICT_INIT;
+ for (i = 0; i < num_files; i++) {
+ ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i])));
+ }
+ ui_call_wildmenu_show(args);
+ return EXPAND_OK;
+ }
+
if (!wildmenu) {
msg_didany = FALSE; /* lines_left will be set */
msg_start(); /* prepare for paging */
@@ -3413,12 +4123,12 @@ static int showmatches(expand_T *xp, int wildmenu)
msg_start(); /* prepare for paging */
}
- if (got_int)
- got_int = FALSE; /* only int. the completion, not the cmd line */
- else if (wildmenu)
- win_redr_status_matches(xp, num_files, files_found, 0, showtail);
- else {
- /* find the length of the longest file name */
+ if (got_int) {
+ got_int = false; // only int. the completion, not the cmd line
+ } else if (wildmenu) {
+ win_redr_status_matches(xp, num_files, files_found, -1, showtail);
+ } else {
+ // find the length of the longest file name
maxlen = 0;
for (i = 0; i < num_files; ++i) {
if (!showtail && (xp->xp_context == EXPAND_FILES
@@ -3612,18 +4322,20 @@ addstar (
* use with vim_regcomp(). First work out how long it will be:
*/
- /* For help tags the translation is done in find_help_tags().
- * For a tag pattern starting with "/" no translation is needed. */
+ // For help tags the translation is done in find_help_tags().
+ // For a tag pattern starting with "/" no translation is needed.
if (context == EXPAND_HELP
+ || context == EXPAND_CHECKHEALTH
|| context == EXPAND_COLORS
|| context == EXPAND_COMPILER
|| context == EXPAND_OWNSYNTAX
|| context == EXPAND_FILETYPE
|| context == EXPAND_PACKADD
- || (context == EXPAND_TAGS && fname[0] == '/'))
+ || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS)
+ && fname[0] == '/')) {
retval = vim_strnsave(fname, len);
- else {
- new_len = len + 2; /* +2 for '^' at start, NUL at end */
+ } else {
+ new_len = len + 2; // +2 for '^' at start, NUL at end
for (i = 0; i < len; i++) {
if (fname[i] == '*' || fname[i] == '~')
new_len++; /* '*' needs to be replaced by ".*"
@@ -4020,6 +4732,10 @@ ExpandFromContext (
char *directories[] = { "syntax", "indent", "ftplugin", NULL };
return ExpandRTDir(pat, 0, num_file, file, directories);
}
+ if (xp->xp_context == EXPAND_CHECKHEALTH) {
+ char *directories[] = { "autoload/health", NULL };
+ return ExpandRTDir(pat, 0, num_file, file, directories);
+ }
if (xp->xp_context == EXPAND_USER_LIST) {
return ExpandUserList(xp, num_file, file);
}
@@ -4050,6 +4766,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 },
@@ -4210,13 +4927,14 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file,
flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
bool mustfree = false; // Track memory allocation for *path.
- /* For an absolute name we don't use $PATH. */
- if (path_is_absolute_path(pat))
+ // For an absolute name we don't use $PATH.
+ if (path_is_absolute(pat)) {
path = (char_u *)" ";
- else if ((pat[0] == '.' && (vim_ispathsep(pat[1])
- || (pat[1] == '.' && vim_ispathsep(pat[2])))))
+ } else if (pat[0] == '.' && (vim_ispathsep(pat[1])
+ || (pat[1] == '.'
+ && vim_ispathsep(pat[2])))) {
path = (char_u *)".";
- else {
+ } else {
path = (char_u *)vim_getenv("PATH");
if (path == NULL) {
path = (char_u *)"";
@@ -4381,24 +5099,24 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
*/
static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
{
- list_T *retlist;
- listitem_T *li;
- garray_T ga;
-
- retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp,
- num_file, file);
+ list_T *const retlist = call_user_expand_func(
+ (user_expand_func_T)call_func_retlist, xp, num_file, file);
if (retlist == NULL) {
return FAIL;
}
+ garray_T ga;
ga_init(&ga, (int)sizeof(char *), 3);
- /* Loop over the items in the list. */
- for (li = retlist->lv_first; li != NULL; li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL)
- continue; /* Skip non-string items and empty strings */
+ // Loop over the items in the list.
+ TV_LIST_ITER_CONST(retlist, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
+ || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
+ continue; // Skip non-string items and empty strings.
+ }
- GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string));
- }
+ GA_APPEND(char *, &ga, xstrdup(
+ (const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
+ });
tv_list_unref(retlist);
*file = ga.ga_data;
@@ -4877,13 +5595,15 @@ int get_history_idx(int histype)
*/
static struct cmdline_info *get_ccline_ptr(void)
{
- if ((State & CMDLINE) == 0)
+ if ((State & CMDLINE) == 0) {
return NULL;
- if (ccline.cmdbuff != NULL)
+ } else if (ccline.cmdbuff != NULL) {
return &ccline;
- if (prev_ccline_used && prev_ccline.cmdbuff != NULL)
- return &prev_ccline;
- return NULL;
+ } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) {
+ return ccline.prev_ccline;
+ } else {
+ return NULL;
+ }
}
/*
@@ -5319,6 +6039,7 @@ static int ex_window(void)
return K_IGNORE;
}
cmdwin_type = get_cmdline_type();
+ cmdwin_level = ccline.level;
// Create empty command-line buffer.
buf_open_scratch(0, "[Command Line]");
@@ -5371,6 +6092,10 @@ static int ex_window(void)
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
invalidate_botline();
+ if (ui_is_external(kUICmdline)) {
+ ccline.redraw_state = kCmdRedrawNone;
+ ui_call_cmdline_hide(ccline.level);
+ }
redraw_later(SOME_VALID);
// Save the command line info, can be used recursively.
@@ -5391,6 +6116,7 @@ static int ex_window(void)
i = RedrawingDisabled;
RedrawingDisabled = 0;
+ int save_count = save_batch_count();
/*
* Call the main loop until <CR> or CTRL-C is typed.
@@ -5399,6 +6125,7 @@ static int ex_window(void)
normal_enter(true, false);
RedrawingDisabled = i;
+ restore_batch_count(save_count);
int save_KeyTyped = KeyTyped;
@@ -5411,6 +6138,7 @@ static int ex_window(void)
// Restore the command line info.
restore_cmdline(&save_ccline);
cmdwin_type = 0;
+ cmdwin_level = 0;
exmode_active = save_exmode;
diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c
index 1053cb3ac2..5801a2d8fb 100644
--- a/src/nvim/farsi.c
+++ b/src/nvim/farsi.c
@@ -596,78 +596,83 @@ static void chg_r_to_Xor_X_(void)
int fkmap(int c)
{
int tempc;
- static int revins;
+ int insert_mode = (State & INSERT);
+ static int revins = 0;
if (IS_SPECIAL(c)) {
return c;
}
- if (ascii_isdigit(c)
- || ((c == '.'
- || c == '+'
- || c == '-'
- || c == '^'
- || c == '%'
- || c == '#'
- || c == '=')
- && revins)) {
- if (!revins) {
- if (curwin->w_cursor.col) {
- if (!p_ri) {
- dec_cursor();
- }
+ if (insert_mode) {
+ if (ascii_isdigit(c)
+ || ((c == '.'
+ || c == '+'
+ || c == '-'
+ || c == '^'
+ || c == '%'
+ || c == '#'
+ || c == '=')
+ && revins)) {
+ // Numbers are entered left-to-right.
+ if (!revins) {
+ if (curwin->w_cursor.col) {
+ if (!p_ri) {
+ dec_cursor();
+ }
- chg_c_toX_orX();
- chg_l_toXor_X();
- if (!p_ri) {
- inc_cursor();
+ chg_c_toX_orX();
+ chg_l_toXor_X();
+ if (!p_ri) {
+ inc_cursor();
+ }
}
}
- }
- arrow_used = TRUE;
- (void)stop_arrow();
+ arrow_used = true;
+ (void)stop_arrow();
- if (!curwin->w_p_rl && revins) {
- inc_cursor();
- }
+ if (!curwin->w_p_rl && revins) {
+ inc_cursor();
+ }
- revins++;
- p_ri = 1;
- } else {
- if (revins) {
- arrow_used = TRUE;
- (void)stop_arrow();
+ revins++;
+ p_ri = 1;
+ } else {
+ if (revins) {
+ // Stop entering number.
+ arrow_used = true;
+ (void)stop_arrow();
- revins = 0;
- if (curwin->w_p_rl) {
- while ((F_isdigit(gchar_cursor())
- || (gchar_cursor() == F_PERIOD
- || gchar_cursor() == F_PLUS
- || gchar_cursor() == F_MINUS
- || gchar_cursor() == F_MUL
- || gchar_cursor() == F_DIVIDE
- || gchar_cursor() == F_PERCENT
- || gchar_cursor() == F_EQUALS))
- && gchar_cursor() != NUL) {
- curwin->w_cursor.col++;
- }
- } else {
- if (curwin->w_cursor.col) {
+ revins = 0;
+ if (curwin->w_p_rl) {
while ((F_isdigit(gchar_cursor())
- || (gchar_cursor() == F_PERIOD
- || gchar_cursor() == F_PLUS
- || gchar_cursor() == F_MINUS
- || gchar_cursor() == F_MUL
- || gchar_cursor() == F_DIVIDE
- || gchar_cursor() == F_PERCENT
- || gchar_cursor() == F_EQUALS))
- && --curwin->w_cursor.col) {
+ || (gchar_cursor() == F_PERIOD
+ || gchar_cursor() == F_PLUS
+ || gchar_cursor() == F_MINUS
+ || gchar_cursor() == F_MUL
+ || gchar_cursor() == F_DIVIDE
+ || gchar_cursor() == F_PERCENT
+ || gchar_cursor() == F_EQUALS))
+ && gchar_cursor() != NUL) {
+ curwin->w_cursor.col++;
+ }
+ } else {
+ if (curwin->w_cursor.col) {
+ while ((F_isdigit(gchar_cursor())
+ || (gchar_cursor() == F_PERIOD
+ || gchar_cursor() == F_PLUS
+ || gchar_cursor() == F_MINUS
+ || gchar_cursor() == F_MUL
+ || gchar_cursor() == F_DIVIDE
+ || gchar_cursor() == F_PERCENT
+ || gchar_cursor() == F_EQUALS))
+ && --curwin->w_cursor.col) {
+ }
}
- }
- if (!F_isdigit(gchar_cursor())) {
- ++curwin->w_cursor.col;
+ if (!F_isdigit(gchar_cursor())) {
+ curwin->w_cursor.col++;
+ }
}
}
}
@@ -755,7 +760,7 @@ int fkmap(int c)
case 'Y':
case NL:
case TAB:
- if (p_ri && (c == NL) && curwin->w_cursor.col) {
+ if (p_ri && (c == NL) && curwin->w_cursor.col && insert_mode) {
// If the char before the cursor is _X_ or X_ do not change
// the one under the cursor with X type.
@@ -826,135 +831,137 @@ int fkmap(int c)
}
}
- if (!p_ri) {
- dec_cursor();
- }
+ if (insert_mode) {
+ if (!p_ri) {
+ dec_cursor();
+ }
- switch ((tempc = gchar_cursor())) {
- case _BE:
- case _PE:
- case _TE:
- case _SE:
- case _JIM:
- case _CHE:
- case _HE_J:
- case _XE:
- case _SIN:
- case _SHIN:
- case _SAD:
- case _ZAD:
- case _FE:
- case _GHAF:
- case _KAF:
- case _KAF_H:
- case _GAF:
- case _LAM:
- case _MIM:
- case _NOON:
- case _HE:
- case _HE_:
- case _TA:
- case _ZA:
- put_curr_and_l_to_X(toF_TyA((char_u)tempc));
- break;
+ switch ((tempc = gchar_cursor())) {
+ case _BE:
+ case _PE:
+ case _TE:
+ case _SE:
+ case _JIM:
+ case _CHE:
+ case _HE_J:
+ case _XE:
+ case _SIN:
+ case _SHIN:
+ case _SAD:
+ case _ZAD:
+ case _FE:
+ case _GHAF:
+ case _KAF:
+ case _KAF_H:
+ case _GAF:
+ case _LAM:
+ case _MIM:
+ case _NOON:
+ case _HE:
+ case _HE_:
+ case _TA:
+ case _ZA:
+ put_curr_and_l_to_X(toF_TyA((char_u)tempc));
+ break;
- case _AYN:
- case _AYN_:
- if (!p_ri) {
- if (!curwin->w_cursor.col) {
- put_curr_and_l_to_X(AYN);
- break;
+ case _AYN:
+ case _AYN_:
+ if (!p_ri) {
+ if (!curwin->w_cursor.col) {
+ put_curr_and_l_to_X(AYN);
+ break;
+ }
}
- }
- if (p_ri) {
- inc_cursor();
- } else {
- dec_cursor();
- }
+ if (p_ri) {
+ inc_cursor();
+ } else {
+ dec_cursor();
+ }
- if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
- tempc = AYN_;
- } else {
- tempc = AYN;
- }
+ if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
+ tempc = AYN_;
+ } else {
+ tempc = AYN;
+ }
- if (p_ri) {
- dec_cursor();
- } else {
- inc_cursor();
- }
+ if (p_ri) {
+ dec_cursor();
+ } else {
+ inc_cursor();
+ }
- put_curr_and_l_to_X((char_u)tempc);
- break;
+ put_curr_and_l_to_X((char_u)tempc);
+ break;
- case _GHAYN:
- case _GHAYN_:
+ case _GHAYN:
+ case _GHAYN_:
- if (!p_ri) {
- if (!curwin->w_cursor.col) {
- put_curr_and_l_to_X(GHAYN);
- break;
+ if (!p_ri) {
+ if (!curwin->w_cursor.col) {
+ put_curr_and_l_to_X(GHAYN);
+ break;
+ }
}
- }
- if (p_ri) {
- inc_cursor();
- } else {
- dec_cursor();
- }
+ if (p_ri) {
+ inc_cursor();
+ } else {
+ dec_cursor();
+ }
- if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
- tempc = GHAYN_;
- } else {
- tempc = GHAYN;
- }
+ if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
+ tempc = GHAYN_;
+ } else {
+ tempc = GHAYN;
+ }
- if (p_ri) {
- dec_cursor();
- } else {
- inc_cursor();
- }
+ if (p_ri) {
+ dec_cursor();
+ } else {
+ inc_cursor();
+ }
- put_curr_and_l_to_X((char_u)tempc);
- break;
+ put_curr_and_l_to_X((char_u)tempc);
+ break;
- case _YE:
- case _IE:
- case _YEE:
+ case _YE:
+ case _IE:
+ case _YEE:
- if (!p_ri) {
- if (!curwin->w_cursor.col) {
- put_curr_and_l_to_X(
- (tempc == _YE ? YE : tempc == _IE ? IE : YEE));
- break;
+ if (!p_ri) {
+ if (!curwin->w_cursor.col) {
+ put_curr_and_l_to_X(
+ (tempc == _YE ? YE : tempc == _IE ? IE : YEE));
+ break;
+ }
}
- }
- if (p_ri) {
- inc_cursor();
- } else {
- dec_cursor();
- }
+ if (p_ri) {
+ inc_cursor();
+ } else {
+ dec_cursor();
+ }
- if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
- tempc = (tempc == _YE ? YE_ : tempc == _IE ? IE_ : YEE_);
- } else {
- tempc = (tempc == _YE ? YE : tempc == _IE ? IE : YEE);
- }
+ if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
+ tempc = (tempc == _YE ? YE_ : tempc == _IE ? IE_ : YEE_);
+ } else {
+ tempc = (tempc == _YE ? YE : tempc == _IE ? IE : YEE);
+ }
- if (p_ri) {
- dec_cursor();
- } else {
- inc_cursor();
- }
+ if (p_ri) {
+ dec_cursor();
+ } else {
+ inc_cursor();
+ }
- put_curr_and_l_to_X((char_u)tempc);
- break;
- }
+ put_curr_and_l_to_X((char_u)tempc);
+ break;
+ }
- if (!p_ri) {
- inc_cursor();
+ if (!p_ri) {
+ inc_cursor();
+ }
}
tempc = 0;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 8094a1b266..3272ce81bc 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1560,7 +1560,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope)
tv_dict_set_keys_readonly(dict);
apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
- NULL);
+ curbuf);
tv_dict_clear(dict);
@@ -1586,7 +1586,7 @@ int vim_chdirfile(char_u *fname)
}
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(dir);
+ slash_adjust((char_u *)dir);
#endif
if (!strequal(dir, (char *)NameBuff)) {
do_autocmd_dirchanged(dir, kCdScopeWindow);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index be4188c4df..52686f6651 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -247,6 +247,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
* stdin)
* READ_DUMMY read into a dummy buffer (to check if file contents changed)
* READ_KEEP_UNDO don't clear undo info or read it from a file
+ * READ_FIFO read from fifo/socket instead of a file
*
* return FAIL for failure, NOTDONE for directory (failure), or OK
*/
@@ -267,6 +268,7 @@ readfile (
int filtering = (flags & READ_FILTER);
int read_stdin = (flags & READ_STDIN);
int read_buffer = (flags & READ_BUFFER);
+ int read_fifo = (flags & READ_FIFO);
int set_options = newfile || read_buffer
|| (eap != NULL && eap->read_edit);
linenr_T read_buf_lnum = 1; /* next line to read from curbuf */
@@ -300,12 +302,9 @@ readfile (
linenr_T skip_count = 0;
linenr_T read_count = 0;
int msg_save = msg_scroll;
- linenr_T read_no_eol_lnum = 0; /* non-zero lnum when last line of
- * last read was missing the eol */
- int try_mac = (vim_strchr(p_ffs, 'm') != NULL);
- int try_dos = (vim_strchr(p_ffs, 'd') != NULL);
- int try_unix = (vim_strchr(p_ffs, 'x') != NULL);
- int file_rewind = FALSE;
+ linenr_T read_no_eol_lnum = 0; // non-zero lnum when last line of
+ // last read was missing the eol
+ int file_rewind = false;
int can_retry;
linenr_T conv_error = 0; /* line nr with conversion error */
linenr_T illegal_byte = 0; /* line nr with illegal byte */
@@ -426,7 +425,7 @@ readfile (
}
}
- if (!read_buffer && !read_stdin) {
+ if (!read_buffer && !read_stdin && !read_fifo) {
perm = os_getperm((const char *)fname);
#ifdef UNIX
// On Unix it is possible to read a directory, so we have to
@@ -468,8 +467,8 @@ readfile (
if (check_readonly && !readonlymode)
curbuf->b_p_ro = FALSE;
- if (newfile && !read_stdin && !read_buffer) {
- /* Remember time of file. */
+ if (newfile && !read_stdin && !read_buffer && !read_fifo) {
+ // Remember time of file.
FileInfo file_info;
if (os_fileinfo((char *)fname, &file_info)) {
buf_store_file_info(curbuf, &file_info);
@@ -637,37 +636,46 @@ readfile (
curbuf->b_op_start.lnum = ((from == 0) ? 1 : from);
curbuf->b_op_start.col = 0;
+ int try_mac = (vim_strchr(p_ffs, 'm') != NULL);
+ int try_dos = (vim_strchr(p_ffs, 'd') != NULL);
+ int try_unix = (vim_strchr(p_ffs, 'x') != NULL);
+
if (!read_buffer) {
int m = msg_scroll;
int n = msg_scrolled;
- /*
- * The file must be closed again, the autocommands may want to change
- * the file before reading it.
- */
- if (!read_stdin)
- close(fd); /* ignore errors */
+ // The file must be closed again, the autocommands may want to change
+ // the file before reading it.
+ if (!read_stdin) {
+ close(fd); // ignore errors
+ }
- /*
- * The output from the autocommands should not overwrite anything and
- * should not be overwritten: Set msg_scroll, restore its value if no
- * output was done.
- */
- msg_scroll = TRUE;
- if (filtering)
+ // The output from the autocommands should not overwrite anything and
+ // should not be overwritten: Set msg_scroll, restore its value if no
+ // output was done.
+ msg_scroll = true;
+ if (filtering) {
apply_autocmds_exarg(EVENT_FILTERREADPRE, NULL, sfname,
- FALSE, curbuf, eap);
- else if (read_stdin)
+ false, curbuf, eap);
+ } else if (read_stdin) {
apply_autocmds_exarg(EVENT_STDINREADPRE, NULL, sfname,
- FALSE, curbuf, eap);
- else if (newfile)
+ false, curbuf, eap);
+ } else if (newfile) {
apply_autocmds_exarg(EVENT_BUFREADPRE, NULL, sfname,
- FALSE, curbuf, eap);
- else
+ false, curbuf, eap);
+ } else {
apply_autocmds_exarg(EVENT_FILEREADPRE, sfname, sfname,
- FALSE, NULL, eap);
- if (msg_scrolled == n)
+ false, NULL, eap);
+ }
+
+ // autocommands may have changed it
+ try_mac = (vim_strchr(p_ffs, 'm') != NULL);
+ try_dos = (vim_strchr(p_ffs, 'd') != NULL);
+ try_unix = (vim_strchr(p_ffs, 'x') != NULL);
+
+ if (msg_scrolled == n) {
msg_scroll = m;
+ }
if (aborting()) { /* autocmds may abort script processing */
--no_wait_return;
@@ -895,6 +903,7 @@ retry:
* and we can't do it internally or with iconv().
*/
if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL
+ && !read_fifo
# ifdef USE_ICONV
&& iconv_fd == (iconv_t)-1
# endif
@@ -935,7 +944,7 @@ retry:
/* Set "can_retry" when it's possible to rewind the file and try with
* another "fenc" value. It's FALSE when no other "fenc" to try, reading
* stdin or fixed at a specific encoding. */
- can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc);
+ can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo);
if (!skip_read) {
linerest = 0;
@@ -947,6 +956,7 @@ retry:
&& curbuf->b_ffname != NULL
&& curbuf->b_p_udf
&& !filtering
+ && !read_fifo
&& !read_stdin
&& !read_buffer);
if (read_undo_file)
@@ -1612,7 +1622,8 @@ rewind_retry:
*ptr = NUL; /* end of line */
len = (colnr_T)(ptr - line_start + 1);
if (fileformat == EOL_DOS) {
- if (ptr[-1] == CAR) { /* remove CR */
+ if (ptr > line_start && ptr[-1] == CAR) {
+ // remove CR before NL
ptr[-1] = NUL;
len--;
} else if (ff_error != EOL_DOS) {
@@ -1919,7 +1930,7 @@ failed:
u_read_undo(NULL, hash, fname);
}
- if (!read_stdin && !read_buffer) {
+ if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) {
int m = msg_scroll;
int n = msg_scrolled;
@@ -1937,7 +1948,7 @@ failed:
if (filtering) {
apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname,
false, curbuf, eap);
- } else if (newfile) {
+ } else if (newfile || (read_buffer && sfname != NULL)) {
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
false, curbuf, eap);
if (!au_did_filetype && *curbuf->b_p_ft != NUL) {
@@ -1970,7 +1981,7 @@ failed:
/// Do not accept "/dev/fd/[012]", opening these may hang Vim.
///
/// @param fname file name to check
-static bool is_dev_fd_file(char_u *fname)
+bool is_dev_fd_file(char_u *fname)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
return STRNCMP(fname, "/dev/fd/", 8) == 0
@@ -2566,11 +2577,9 @@ buf_write (
perm = -1;
}
}
-#else /* win32 */
- /*
- * Check for a writable device name.
- */
- c = os_nodetype((char *)fname);
+#else // win32
+ // Check for a writable device name.
+ c = fname == NULL ? NODE_OTHER : os_nodetype((char *)fname);
if (c == NODE_OTHER) {
SET_ERRMSG_NUM("E503", _("is not a file or writable device"));
goto fail;
@@ -2590,9 +2599,8 @@ buf_write (
if (overwriting) {
os_fileinfo((char *)fname, &file_info_old);
}
-
}
-#endif /* !UNIX */
+#endif // !UNIX
if (!device && !newfile) {
/*
@@ -3158,8 +3166,8 @@ nobackup:
#ifdef UNIX
FileInfo file_info;
- /* Don't delete the file when it's a hard or symbolic link. */
- if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1)
+ // Don't delete the file when it's a hard or symbolic link.
+ if ((!newfile && os_fileinfo_hardlinks(&file_info_old) > 1)
|| (os_fileinfo_link((char *)fname, &file_info)
&& !os_fileinfo_id_equal(&file_info, &file_info_old))) {
SET_ERRMSG(_("E166: Can't open linked file for writing"));
@@ -4310,7 +4318,7 @@ void shorten_fnames(int force)
&& !path_with_url((char *)buf->b_fname)
&& (force
|| buf->b_sfname == NULL
- || path_is_absolute_path(buf->b_sfname))) {
+ || path_is_absolute(buf->b_sfname))) {
xfree(buf->b_sfname);
buf->b_sfname = NULL;
p = path_shorten_fname(buf->b_ffname, dirname);
@@ -4435,22 +4443,32 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
/// @return true for end-of-file.
bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
{
- char *eof;
-#define FGETS_SIZE 200
- char tbuf[FGETS_SIZE];
+ char *retval;
+ assert(size > 0);
buf[size - 2] = NUL;
- eof = fgets((char *)buf, size, fp);
+
+ do {
+ errno = 0;
+ retval = fgets((char *)buf, size, fp);
+ } while (retval == NULL && errno == EINTR);
+
if (buf[size - 2] != NUL && buf[size - 2] != '\n') {
- buf[size - 1] = NUL; /* Truncate the line */
+ char tbuf[200];
+
+ buf[size - 1] = NUL; // Truncate the line.
- /* Now throw away the rest of the line: */
+ // Now throw away the rest of the line:
do {
- tbuf[FGETS_SIZE - 2] = NUL;
- ignoredp = fgets((char *)tbuf, FGETS_SIZE, fp);
- } while (tbuf[FGETS_SIZE - 2] != NUL && tbuf[FGETS_SIZE - 2] != '\n');
+ tbuf[sizeof(tbuf) - 2] = NUL;
+ errno = 0;
+ retval = fgets((char *)tbuf, sizeof(tbuf), fp);
+ if (retval == NULL && errno != EINTR) {
+ break;
+ }
+ } while (tbuf[sizeof(tbuf) - 2] != NUL && tbuf[sizeof(tbuf) - 2] != '\n');
}
- return eof == NULL;
+ return retval ? false : feof(fp);
}
/// Read 2 bytes from "fd" and turn them into an int, MSB first.
@@ -4543,6 +4561,7 @@ int put_time(FILE *fd, time_t time_)
///
/// @return -1 for failure, 0 for success
int vim_rename(const char_u *from, const char_u *to)
+ FUNC_ATTR_NONNULL_ALL
{
int fd_in;
int fd_out;
@@ -4818,6 +4837,7 @@ buf_check_timestamp (
buf_T *buf,
int focus /* called for GUI focus event */
)
+ FUNC_ATTR_NONNULL_ALL
{
int retval = 0;
char_u *path;
@@ -5068,14 +5088,12 @@ void buf_reload(buf_T *buf, int orig_mode)
flags |= READ_KEEP_UNDO;
}
- /*
- * To behave like when a new file is edited (matters for
- * BufReadPost autocommands) we first need to delete the current
- * buffer contents. But if reading the file fails we should keep
- * the old contents. Can't use memory only, the file might be
- * too big. Use a hidden buffer to move the buffer contents to.
- */
- if (bufempty() || saved == FAIL) {
+ // To behave like when a new file is edited (matters for
+ // BufReadPost autocommands) we first need to delete the current
+ // buffer contents. But if reading the file fails we should keep
+ // the old contents. Can't use memory only, the file might be
+ // too big. Use a hidden buffer to move the buffer contents to.
+ if (BUFEMPTY() || saved == FAIL) {
savebuf = NULL;
} else {
// Allocate a buffer without putting it in the buffer list.
@@ -5108,7 +5126,7 @@ void buf_reload(buf_T *buf, int orig_mode)
if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) {
// Put the text back from the save buffer. First
// delete any lines that readfile() added.
- while (!bufempty()) {
+ while (!BUFEMPTY()) {
if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) {
break;
}
@@ -6256,13 +6274,13 @@ do_doautocmd (
fname = skipwhite(fname);
- /*
- * Loop over the events.
- */
- while (*arg && !ascii_iswhite(*arg))
- if (apply_autocmds_group(event_name2nr(arg, &arg),
- fname, NULL, TRUE, group, curbuf, NULL))
- nothing_done = FALSE;
+ // Loop over the events.
+ while (*arg && !ends_excmd(*arg) && !ascii_iswhite(*arg)) {
+ if (apply_autocmds_group(event_name2nr(arg, &arg), fname, NULL, true,
+ group, curbuf, NULL)) {
+ nothing_done = false;
+ }
+ }
if (nothing_done && do_msg) {
MSG(_("No matching autocommands"));
@@ -6637,7 +6655,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
char_u *save_sourcing_name;
linenr_T save_sourcing_lnum;
char_u *save_autocmd_fname;
- int save_autocmd_fname_full;
int save_autocmd_bufnr;
char_u *save_autocmd_match;
int save_autocmd_busy;
@@ -6653,12 +6670,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
proftime_T wait_time;
bool did_save_redobuff = false;
- /*
- * Quickly return if there are no autocommands for this event or
- * autocommands are blocked.
- */
- if (first_autopat[(int)event] == NULL || autocmd_blocked > 0)
+ // Quickly return if there are no autocommands for this event or
+ // autocommands are blocked.
+ if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
+ || autocmd_blocked > 0) {
goto BYPASS_AU;
+ }
/*
* When autocommands are busy, new autocommands are only executed when
@@ -6710,7 +6727,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
* Save the autocmd_* variables and info about the current buffer.
*/
save_autocmd_fname = autocmd_fname;
- save_autocmd_fname_full = autocmd_fname_full;
save_autocmd_bufnr = autocmd_bufnr;
save_autocmd_match = autocmd_match;
save_autocmd_busy = autocmd_busy;
@@ -6724,19 +6740,22 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
* invalid.
*/
if (fname_io == NULL) {
- if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET)
+ if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET) {
autocmd_fname = NULL;
- else if (fname != NULL && *fname != NUL)
+ } else if (fname != NULL && !ends_excmd(*fname)) {
autocmd_fname = fname;
- else if (buf != NULL)
+ } else if (buf != NULL) {
autocmd_fname = buf->b_ffname;
- else
+ } else {
autocmd_fname = NULL;
- } else
+ }
+ } else {
autocmd_fname = fname_io;
- if (autocmd_fname != NULL)
- autocmd_fname = vim_strsave(autocmd_fname);
- autocmd_fname_full = FALSE; /* call FullName_save() later */
+ }
+ if (autocmd_fname != NULL) {
+ // Allocate MAXPATHL for when eval_vars() resolves the fullpath.
+ autocmd_fname = vim_strnsave(autocmd_fname, MAXPATHL);
+ }
/*
* Set the buffer number to be used for <abuf>.
@@ -6903,7 +6922,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
sourcing_lnum = save_sourcing_lnum;
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
- autocmd_fname_full = save_autocmd_fname_full;
autocmd_bufnr = save_autocmd_bufnr;
autocmd_match = save_autocmd_match;
current_SID = save_current_SID;
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 426dc0fcb3..8db4b89806 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -4,13 +4,14 @@
#include "nvim/buffer_defs.h"
#include "nvim/os/os.h"
-/* Values for readfile() flags */
-#define READ_NEW 0x01 /* read a file into a new buffer */
-#define READ_FILTER 0x02 /* read filter output */
-#define READ_STDIN 0x04 /* read from stdin */
-#define READ_BUFFER 0x08 /* read from curbuf (converting stdin) */
-#define READ_DUMMY 0x10 /* reading into a dummy buffer */
-#define READ_KEEP_UNDO 0x20 /* keep undo info*/
+// Values for readfile() flags
+#define READ_NEW 0x01 // read a file into a new buffer
+#define READ_FILTER 0x02 // read filter output
+#define READ_STDIN 0x04 // read from stdin
+#define READ_BUFFER 0x08 // read from curbuf (converting stdin)
+#define READ_DUMMY 0x10 // reading into a dummy buffer
+#define READ_KEEP_UNDO 0x20 // keep undo info
+#define READ_FIFO 0x40 // read from fifo or socket
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index acdb25ca67..2666ca6e6f 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -37,7 +37,7 @@ function write_arglist(output, ev, need_copy)
for j = 1, #ev.parameters do
local param = ev.parameters[j]
local kind = string.upper(param[1])
- local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING")
+ local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT")
output:write(' ADD(args, ')
if do_copy then
output:write('copy_object(')
@@ -91,7 +91,7 @@ for i = 1, #events do
recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
argc = argc+2
elseif param[1] == 'Array' then
- send = send..' Array copy_'..param[2]..' = copy_array('..param[2]..');\n'
+ send = send..' Array '..copy..' = copy_array('..param[2]..');\n'
argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
recv = (recv..' Array '..param[2]..
' = (Array){.items = argv['..argc..'],'..
@@ -99,6 +99,15 @@ for i = 1, #events do
recv_argv = recv_argv..', '..param[2]
recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n'
argc = argc+2
+ elseif param[1] == 'Object' then
+ send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
+ send = send..' *'..copy..' = copy_object('..param[2]..');\n'
+ argv = argv..', '..copy
+ recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
+ recv_argv = recv_argv..', '..param[2]
+ recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'..
+ ' xfree(argv['..argc..']);\n')
+ argc = argc+1
elseif param[1] == 'Integer' or param[1] == 'Boolean' then
argv = argv..', INT2PTR('..param[2]..')'
recv_argv = recv_argv..', PTR2INT(argv['..argc..'])'
@@ -119,7 +128,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\n')
end
end
@@ -128,6 +137,7 @@ for i = 1, #events do
call_output:write('\n{\n')
if ev.remote_only then
write_arglist(call_output, ev, false)
+ call_output:write(' UI_LOG('..ev.name..', 0);\n')
call_output:write(' ui_event("'..ev.name..'", args);\n')
else
call_output:write(' UI_CALL')
diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua
index e999e53e4a..c40c37bb3e 100755
--- a/src/nvim/generators/gen_declarations.lua
+++ b/src/nvim/generators/gen_declarations.lua
@@ -164,9 +164,40 @@ local pattern = concat(
)
if fname == '--help' then
- print'Usage:'
- print()
- print' gendeclarations.lua definitions.c static.h non-static.h preprocessor.i'
+ print([[
+Usage:
+
+ gendeclarations.lua definitions.c static.h non-static.h definitions.i
+
+Generates declarations for a C file definitions.c, putting declarations for
+static functions into static.h and declarations for non-static functions into
+non-static.h. File `definitions.i' should contain an already preprocessed
+version of definitions.c and it is the only one which is actually parsed,
+definitions.c is needed only to determine functions from which file out of all
+functions found in definitions.i are needed.
+
+Additionally uses the following environment variables:
+
+ NVIM_GEN_DECLARATIONS_LINE_NUMBERS:
+ If set to 1 then all generated declarations receive a comment with file
+ name and line number after the declaration. This may be useful for
+ debugging gen_declarations script, but not much beyond that with
+ configured development environment (i.e. with ctags/cscope/finding
+ definitions with clang/etc).
+
+ WARNING: setting this to 1 will cause extensive rebuilds: declarations
+ generator script will not regenerate non-static.h file if its
+ contents did not change, but including line numbers will make
+ contents actually change.
+
+ With contents changed timestamp of the file is regenerated even
+ when no real changes were made (e.g. a few lines were added to
+ a function which is not at the bottom of the file).
+
+ With changed timestamp build system will assume that header
+ changed, triggering rebuilds of all C files which depend on the
+ "changed" header.
+]])
os.exit()
end
@@ -193,23 +224,15 @@ local static = header
local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"'
local curfile
-local init = 0
+local init = 1
local curfile = nil
local neededfile = fname:match('[^/]+$')
local declline = 0
local declendpos = 0
local curdir = nil
local is_needed_file = false
+local init_is_nl = true
while init ~= nil do
- init = text:find('[\n;}]', init)
- if init == nil then
- break
- end
- local init_is_nl = text:sub(init, init) == '\n'
- init = init + 1
- if init_is_nl and is_needed_file then
- declline = declline + 1
- end
if init_is_nl and text:sub(init, init) == '#' then
local line, dir, file = text:match(filepattern, init)
if file ~= nil then
@@ -249,8 +272,10 @@ while init ~= nil do
declaration = declaration:gsub(' $', '')
declaration = declaration:gsub('^ ', '')
declaration = declaration .. ';'
- declaration = declaration .. (' // %s/%s:%u'):format(
- curdir, curfile, declline)
+ if os.getenv('NVIM_GEN_DECLARATIONS_LINE_NUMBERS') == '1' then
+ declaration = declaration .. (' // %s/%s:%u'):format(
+ curdir, curfile, declline)
+ end
declaration = declaration .. '\n'
if declaration:sub(1, 6) == 'static' then
static = static .. declaration
@@ -260,6 +285,15 @@ while init ~= nil do
declendpos = e
end
end
+ init = text:find('[\n;}]', init)
+ if init == nil then
+ break
+ end
+ init_is_nl = text:sub(init, init) == '\n'
+ init = init + 1
+ if init_is_nl and is_needed_file then
+ declline = declline + 1
+ end
end
non_static = non_static .. footer
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index ca0134043c..fdc00d5dc0 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -36,6 +36,7 @@ local redraw_flags={
all_windows='P_RALL',
everything='P_RCLR',
curswant='P_CURSWANT',
+ ui_option='P_UI_OPTION',
}
local list_flags={
@@ -74,6 +75,7 @@ local get_flags = function(o)
{'gettext'},
{'noglob'},
{'normal_fname_chars', 'P_NFNAME'},
+ {'normal_dname_chars', 'P_NDNAME'},
{'pri_mkrc'},
{'deny_in_modelines', 'P_NO_ML'},
{'deny_duplicates', 'P_NODUP'},
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 4e42042959..5d4e61d56a 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -92,17 +92,15 @@ static int typeahead_char = 0; /* typeahead char that's not flushed */
*/
static int block_redo = FALSE;
-/*
- * Make a hash value for a mapping.
- * "mode" is the lower 4 bits of the State for the mapping.
- * "c1" is the first character of the "lhs".
- * Returns a value between 0 and 255, index in maphash.
- * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
- */
+// Make a hash value for a mapping.
+// "mode" is the lower 4 bits of the State for the mapping.
+// "c1" is the first character of the "lhs".
+// Returns a value between 0 and 255, index in maphash.
+// Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
#define MAP_HASH(mode, \
c1) (((mode) & \
(NORMAL + VISUAL + SELECTMODE + \
- OP_PENDING)) ? (c1) : ((c1) ^ 0x80))
+ OP_PENDING + TERM_FOCUS)) ? (c1) : ((c1) ^ 0x80))
// Each mapping is put in one of the MAX_MAPHASH hash lists,
// to speed up finding it.
@@ -256,16 +254,17 @@ static void add_buff(buffheader_T *const buf, const char *const s,
return;
}
- if (buf->bh_first.b_next == NULL) { /* first add to list */
+ if (buf->bh_first.b_next == NULL) { // first add to list
buf->bh_space = 0;
buf->bh_curr = &(buf->bh_first);
- } else if (buf->bh_curr == NULL) { /* buffer has already been read */
- EMSG(_("E222: Add to read buffer"));
+ } else if (buf->bh_curr == NULL) { // buffer has already been read
+ IEMSG(_("E222: Add to read buffer"));
return;
- } else if (buf->bh_index != 0)
+ } else if (buf->bh_index != 0) {
memmove(buf->bh_first.b_next->b_str,
- buf->bh_first.b_next->b_str + buf->bh_index,
- STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
+ buf->bh_first.b_next->b_str + buf->bh_index,
+ STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
+ }
buf->bh_index = 0;
size_t len;
@@ -870,20 +869,15 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
addlen = (int)STRLEN(str);
- /*
- * Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off]
- */
if (offset == 0 && addlen <= typebuf.tb_off) {
+ // Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off]
typebuf.tb_off -= addlen;
memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen);
- }
- /*
- * Need to allocate a new buffer.
- * In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4
- * characters. We add some extra room to avoid having to allocate too
- * often.
- */
- else {
+ } else {
+ // Need to allocate a new buffer.
+ // In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4
+ // characters. We add some extra room to avoid having to allocate too
+ // often.
newoff = MAXMAPLEN + 4;
newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4);
if (newlen < 0) { /* string is getting too long */
@@ -1159,14 +1153,16 @@ void alloc_typebuf(void)
*/
void free_typebuf(void)
{
- if (typebuf.tb_buf == typebuf_init)
- EMSG2(_(e_intern2), "Free typebuf 1");
- else
+ if (typebuf.tb_buf == typebuf_init) {
+ internal_error("Free typebuf 1");
+ } else {
xfree(typebuf.tb_buf);
- if (typebuf.tb_noremap == noremapbuf_init)
- EMSG2(_(e_intern2), "Free typebuf 2");
- else
+ }
+ if (typebuf.tb_noremap == noremapbuf_init) {
+ internal_error("Free typebuf 2");
+ } else {
xfree(typebuf.tb_noremap);
+ }
}
/*
@@ -1665,10 +1661,10 @@ static int vgetorpeek(int advance)
}
if (c != NUL && !got_int) {
if (advance) {
- /* KeyTyped = FALSE; When the command that stuffed something
- * was typed, behave like the stuffed command was typed.
- * needed for CTRL-W CTRl-] to open a fold, for example. */
- KeyStuffed = TRUE;
+ // KeyTyped = FALSE; When the command that stuffed something
+ // was typed, behave like the stuffed command was typed.
+ // needed for CTRL-W CTRL-] to open a fold, for example.
+ KeyStuffed = true;
}
if (typebuf.tb_no_abbr_cnt == 0)
typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */
@@ -1806,7 +1802,7 @@ static int vgetorpeek(int advance)
* <M-a> and then changing 'encoding'. Beware
* that 0x80 is escaped. */
char_u *p1 = mp->m_keys;
- char_u *p2 = mb_unescape(&p1);
+ char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2))
mlen = 0;
@@ -2537,7 +2533,6 @@ do_map (
bool unique = false;
bool nowait = false;
bool silent = false;
- bool special = false;
bool expr = false;
int noremap;
char_u *orig_rhs;
@@ -2583,12 +2578,9 @@ do_map (
continue;
}
- /*
- * Check for "<special>": accept special keys in <>
- */
+ // Ignore obsolete "<special>" modifier.
if (STRNCMP(keys, "<special>", 9) == 0) {
keys = skipwhite(keys + 9);
- special = true;
continue;
}
@@ -2657,7 +2649,7 @@ do_map (
// needs to be freed later (*keys_buf and *arg_buf).
// replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
if (haskey) {
- keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special,
+ keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
CPO_TO_CPO_FLAGS);
}
orig_rhs = rhs;
@@ -2665,7 +2657,7 @@ do_map (
if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing
rhs = (char_u *)"";
} else {
- rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special,
+ rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, true,
CPO_TO_CPO_FLAGS);
}
}
@@ -3245,7 +3237,7 @@ bool map_to_exists(const char *const str, const char *const modechars,
char_u *buf;
char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf,
- false, true, false,
+ false, true, true,
CPO_TO_CPO_FLAGS);
#define MAPMODE(mode, modechars, chr, modeflags) \
@@ -3374,6 +3366,10 @@ set_context_in_map_cmd (
arg = skipwhite(arg + 8);
continue;
}
+ if (STRNCMP(arg, "<special>", 9) == 0) {
+ arg = skipwhite(arg + 9);
+ continue;
+ }
if (STRNCMP(arg, "<script>", 8) == 0) {
arg = skipwhite(arg + 8);
continue;
@@ -3416,21 +3412,24 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
for (round = 1; round <= 2; ++round) {
count = 0;
- for (i = 0; i < 6; ++i) {
- if (i == 0)
+ for (i = 0; i < 7; i++) {
+ if (i == 0) {
p = (char_u *)"<silent>";
- else if (i == 1)
+ } else if (i == 1) {
p = (char_u *)"<unique>";
- else if (i == 2)
+ } else if (i == 2) {
p = (char_u *)"<script>";
- else if (i == 3)
+ } else if (i == 3) {
p = (char_u *)"<expr>";
- else if (i == 4 && !expand_buffer)
+ } else if (i == 4 && !expand_buffer) {
p = (char_u *)"<buffer>";
- else if (i == 5)
+ } else if (i == 5) {
p = (char_u *)"<nowait>";
- else
+ } else if (i == 6) {
+ p = (char_u *)"<special>";
+ } else {
continue;
+ }
if (vim_regexec(regmatch, p, (colnr_T)0)) {
if (round == 1)
@@ -3916,7 +3915,7 @@ makemap (
c1 = 't';
break;
default:
- EMSG(_("E228: makemap: Illegal mode"));
+ IEMSG(_("E228: makemap: Illegal mode"));
return FAIL;
}
do { /* do this twice if c2 is set, 3 times with c3 */
@@ -3999,12 +3998,10 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
return OK;
}
- for (; *str != NUL; ++str) {
- char_u *p;
-
- /* Check for a multi-byte character, which may contain escaped
- * K_SPECIAL and CSI bytes */
- p = mb_unescape(&str);
+ for (; *str != NUL; str++) {
+ // Check for a multi-byte character, which may contain escaped
+ // K_SPECIAL and CSI bytes.
+ const char *p = mb_unescape((const char **)&str);
if (p != NULL) {
while (*p != NUL)
if (fputc(*p++, fd) < 0)
@@ -4160,8 +4157,7 @@ void add_map(char_u *map, int mode)
}
// Translate an internal mapping/abbreviation representation into the
-// corresponding external one recognized by :map/:abbrev commands;
-// respects the current B/k/< settings of 'cpoption'.
+// corresponding external one recognized by :map/:abbrev commands.
//
// This function is called when expanding mappings/abbreviations on the
// command-line, and for building the "Ambiguous mapping..." error message.
@@ -4181,7 +4177,6 @@ static char_u * translate_mapping (
ga_init(&ga, 1, 40);
bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH);
- bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI);
for (; *str; ++str) {
int c = *str;
@@ -4194,7 +4189,7 @@ static char_u * translate_mapping (
}
if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- if (expmap && cpo_special) {
+ if (expmap) {
ga_clear(&ga);
return NULL;
}
@@ -4205,8 +4200,8 @@ static char_u * translate_mapping (
}
str += 2;
}
- if (IS_SPECIAL(c) || modifiers) { /* special key */
- if (expmap && cpo_special) {
+ if (IS_SPECIAL(c) || modifiers) { // special key
+ if (expmap) {
ga_clear(&ga);
return NULL;
}
@@ -4216,7 +4211,7 @@ static char_u * translate_mapping (
}
if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
- || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash)) {
+ || (c == '\\' && !cpo_bslash)) {
ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
}
@@ -4252,3 +4247,70 @@ mapblock_T *get_maphash(int index, buf_T *buf)
return (buf == NULL) ? maphash[index] : buf->b_maphash[index];
}
+
+/// Get command argument for <Cmd> key
+char_u * getcmdkeycmd(int promptc, void *cookie, int indent)
+{
+ garray_T line_ga;
+ int c1 = -1, c2;
+ int cmod = 0;
+ bool aborted = false;
+
+ ga_init(&line_ga, 1, 32);
+
+ no_mapping++;
+
+ got_int = false;
+ while (c1 != NUL && !aborted) {
+ ga_grow(&line_ga, 32);
+
+ if (vgetorpeek(false) == NUL) {
+ // incomplete <Cmd> is an error, because there is not much the user
+ // could do in this state.
+ EMSG(e_cmdmap_err);
+ aborted = true;
+ break;
+ }
+
+ // Get one character at a time.
+ c1 = vgetorpeek(true);
+ // Get two extra bytes for special keys
+ if (c1 == K_SPECIAL) {
+ c1 = vgetorpeek(true); // no mapping for these chars
+ c2 = vgetorpeek(true);
+ if (c1 == KS_MODIFIER) {
+ cmod = c2;
+ continue;
+ }
+ c1 = TO_SPECIAL(c1, c2);
+ }
+
+
+ if (got_int) {
+ aborted = true;
+ } else if (c1 == '\r' || c1 == '\n') {
+ c1 = NUL; // end the line
+ } else if (c1 == ESC) {
+ aborted = true;
+ } else if (c1 == K_COMMAND) {
+ // special case to give nicer error message
+ EMSG(e_cmdmap_repeated);
+ aborted = true;
+ } else if (IS_SPECIAL(c1)) {
+ EMSG2(e_cmdmap_key, get_special_key_name(c1, cmod));
+ aborted = true;
+ } else {
+ ga_append(&line_ga, (char)c1);
+ }
+
+ cmod = 0;
+ }
+
+ no_mapping--;
+
+ if (aborted) {
+ ga_clear(&line_ga);
+ }
+
+ return (char_u *)line_ga.ga_data;
+}
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 28584e0534..e634273e0d 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -5,12 +5,15 @@
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
-/* Values for "noremap" argument of ins_typebuf(). Also used for
- * map->m_noremap and menu->noremap[]. */
-#define REMAP_YES 0 /* allow remapping */
-#define REMAP_NONE -1 /* no remapping */
-#define REMAP_SCRIPT -2 /* remap script-local mappings only */
-#define REMAP_SKIP -3 /* no remapping for first char */
+/// Values for "noremap" argument of ins_typebuf(). Also used for
+/// map->m_noremap and menu->noremap[].
+/// @addtogroup REMAP_VALUES
+/// @{
+#define REMAP_YES 0 ///< allow remapping
+#define REMAP_NONE -1 ///< no remapping
+#define REMAP_SCRIPT -2 ///< remap script-local mappings only
+#define REMAP_SKIP -3 ///< no remapping for first char
+/// @}
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h
index aa0e97233e..60317b8484 100644
--- a/src/nvim/gettext.h
+++ b/src/nvim/gettext.h
@@ -13,6 +13,7 @@
#else
# define _(x) ((char *)(x))
# define N_(x) x
+# define ngettext(x, xs, n) ((n) == 1 ? (x) : (xs))
# define bindtextdomain(x, y) // empty
# define bind_textdomain_codeset(x, y) // empty
# define textdomain(x) // empty
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 6d1bd1de12..31bde4aa1e 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -317,17 +317,11 @@ EXTERN int do_profiling INIT(= PROF_NONE); /* PROF_ values */
/*
* The exception currently being thrown. Used to pass an exception to
* a different cstack. Also used for discarding an exception before it is
- * caught or made pending. Only valid when did_throw is TRUE.
+ * caught or made pending.
*/
EXTERN except_T *current_exception;
/*
- * did_throw: An exception is being thrown. Reset when the exception is caught
- * or as long as it is pending in a finally clause.
- */
-EXTERN int did_throw INIT(= FALSE);
-
-/*
* need_rethrow: set to TRUE when a throw that cannot be handled in do_cmdline()
* must be propagated to the cstack of the previously called do_cmdline().
*/
@@ -407,13 +401,16 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);
/* ID of script being sourced or was sourced to define the current function. */
EXTERN scid_T current_SID INIT(= 0);
+
+EXTERN bool did_source_packages INIT(= false);
+
// Scope information for the code that indirectly triggered the current
// provider function call
EXTERN struct caller_scope {
scid_T SID;
uint8_t *sourcing_name, *autocmd_fname, *autocmd_match;
linenr_T sourcing_lnum;
- int autocmd_fname_full, autocmd_bufnr;
+ int autocmd_bufnr;
void *funccalp;
} provider_caller_scope;
EXTERN int provider_call_nesting INIT(= 0);
@@ -494,6 +491,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.
@@ -560,21 +558,22 @@ EXTERN int ru_col; /* column for ruler */
EXTERN int ru_wid; /* 'rulerfmt' width of ruler when non-zero */
EXTERN int sc_col; /* column for shown command */
-/*
- * When starting or exiting some things are done differently (e.g. screen
- * updating).
- */
+//
+// When starting or exiting some things are done differently (e.g. screen
+// updating).
+//
+
+// First NO_SCREEN, then NO_BUFFERS, then 0 when startup finished.
EXTERN int starting INIT(= NO_SCREEN);
-/* first NO_SCREEN, then NO_BUFFERS and then
- * set to 0 when starting up finished */
-EXTERN int exiting INIT(= FALSE);
-/* TRUE when planning to exit Vim. Might
- * still keep on running if there is a changed
- * buffer. */
-// volatile because it is used in signal handler deathtrap().
+// true when planning to exit. Might keep running if there is a changed buffer.
+EXTERN int exiting INIT(= false);
+// is stdin a terminal?
+EXTERN int stdin_isatty INIT(= true);
+// is stdout a terminal?
+EXTERN int stdout_isatty INIT(= true);
+// true when doing full-screen output, otherwise only writing some messages.
+// volatile because it is used in a signal handler.
EXTERN volatile int full_screen INIT(= false);
-// TRUE when doing full-screen output
-// otherwise only writing some messages
EXTERN int restricted INIT(= FALSE);
// TRUE when started in restricted mode (-Z)
@@ -724,29 +723,6 @@ EXTERN int vr_lines_changed INIT(= 0); /* #Lines changed by "gR" so far */
/// Encoding used when 'fencs' is set to "default"
EXTERN char_u *fenc_default INIT(= NULL);
-// To speed up BYTELEN(); keep a lookup table to quickly get the length in
-// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes
-// which are illegal when used as the first byte have a 1. The NUL byte has
-// length 1.
-EXTERN char utf8len_tab[256] INIT(= {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1,
-});
-
# if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
/* Pointers to functions and variables to be loaded at runtime */
EXTERN size_t (*iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft,
@@ -890,7 +866,6 @@ EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
-EXTERN int autocmd_fname_full; // autocmd_fname is full path
EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd
@@ -930,8 +905,11 @@ EXTERN char_u langmap_mapchar[256]; /* mapping for language keys */
EXTERN int save_p_ls INIT(= -1); /* Save 'laststatus' setting */
EXTERN int save_p_wmh INIT(= -1); /* Save 'winminheight' setting */
EXTERN int wild_menu_showing INIT(= 0);
-# define WM_SHOWN 1 /* wildmenu showing */
-# define WM_SCROLLED 2 /* wildmenu showing with scroll */
+enum {
+ WM_SHOWN = 1, ///< wildmenu showing
+ WM_SCROLLED = 2, ///< wildmenu showing with scroll
+ WM_LIST = 3, ///< cmdline CTRL-D
+};
EXTERN char breakat_flags[256]; /* which characters are in 'breakat' */
@@ -952,7 +930,7 @@ extern char_u *compiled_sys;
* directory is not a local directory, globaldir is NULL. */
EXTERN char_u *globaldir INIT(= NULL);
-/* Characters from 'listchars' option */
+// 'listchars' characters. Defaults are overridden in set_chars_option().
EXTERN int lcs_eol INIT(= '$');
EXTERN int lcs_ext INIT(= NUL);
EXTERN int lcs_prec INIT(= NUL);
@@ -963,20 +941,21 @@ EXTERN int lcs_tab2 INIT(= NUL);
EXTERN int lcs_trail INIT(= NUL);
EXTERN int lcs_conceal INIT(= ' ');
-/* Characters from 'fillchars' option */
+// 'fillchars' characters. Defaults are overridden in set_chars_option().
EXTERN int fill_stl INIT(= ' ');
EXTERN int fill_stlnc INIT(= ' ');
-EXTERN int fill_vert INIT(= ' ');
-EXTERN int fill_fold INIT(= '-');
+EXTERN int fill_vert INIT(= 9474); // │
+EXTERN int fill_fold INIT(= 183); // ·
EXTERN int fill_diff INIT(= '-');
/* Whether 'keymodel' contains "stopsel" and "startsel". */
EXTERN int km_stopsel INIT(= FALSE);
EXTERN int km_startsel INIT(= FALSE);
-EXTERN int cedit_key INIT(= -1); /* key value of 'cedit' option */
-EXTERN int cmdwin_type INIT(= 0); /* type of cmdline window or 0 */
-EXTERN int cmdwin_result INIT(= 0); /* result of cmdline window or 0 */
+EXTERN int cedit_key INIT(= -1); ///< key value of 'cedit' option
+EXTERN int cmdwin_type INIT(= 0); ///< type of cmdline window or 0
+EXTERN int cmdwin_result INIT(= 0); ///< result of cmdline window or 0
+EXTERN int cmdwin_level INIT(= 0); ///< cmdline recursion level
EXTERN char_u no_lines_msg[] INIT(= N_("--No lines in buffer--"));
@@ -1057,6 +1036,7 @@ EXTERN char_u e_for[] INIT(= N_("E588: :endfor without :for"));
EXTERN char_u e_exists[] INIT(= N_("E13: File exists (add ! to override)"));
EXTERN char_u e_failed[] INIT(= N_("E472: Command failed"));
EXTERN char_u e_internal[] INIT(= N_("E473: Internal error"));
+EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s"));
EXTERN char_u e_interr[] INIT(= N_("Interrupted"));
EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address"));
EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument"));
@@ -1065,11 +1045,20 @@ EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s"));
EXTERN char_u e_invrange[] INIT(= N_("E16: Invalid range"));
EXTERN char_u e_invcmd[] INIT(= N_("E476: Invalid command"));
EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory"));
-EXTERN char_u e_invjob[] INIT(= N_("E900: Invalid job id"));
+EXTERN char_u e_invchan[] INIT(= N_("E900: Invalid channel id"));
+EXTERN char_u e_invchanjob[] INIT(= N_("E900: Invalid channel id: not a job"));
EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full"));
EXTERN char_u e_jobspawn[] INIT(= N_(
- "E903: Process failed to start: %s: \"%s\""));
-EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty"));
+ "E903: Process failed to start: %s: \"%s\""));
+EXTERN char_u e_channotpty[] INIT(= N_("E904: channel is not a pty"));
+EXTERN char_u e_stdiochan2[] INIT(= N_(
+ "E905: Couldn't open stdio channel: %s"));
+EXTERN char_u e_invstream[] INIT(= N_("E906: invalid stream for channel"));
+EXTERN char_u e_invstreamrpc[] INIT(= N_(
+ "E906: invalid stream for rpc channel, use 'rpc'"));
+EXTERN char_u e_streamkey[] INIT(= N_(
+ "E5210: dict key '%s' already set for buffered stream in channel %"
+ PRIu64));
EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\""));
EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s"));
EXTERN char_u e_markinval[] INIT(= N_("E19: Mark has invalid line number"));
@@ -1139,9 +1128,9 @@ EXTERN char_u e_winheight[] INIT(= N_(
EXTERN char_u e_winwidth[] INIT(= N_(
"E592: 'winwidth' cannot be smaller than 'winminwidth'"));
EXTERN char_u e_write[] INIT(= N_("E80: Error while writing"));
-EXTERN char_u e_zerocount[] INIT(= N_("Zero count"));
-EXTERN char_u e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context"));
-EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s"));
+EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
+EXTERN char_u e_usingsid[] INIT(= N_(
+ "E81: Using <SID> not in a script context"));
EXTERN char_u e_maxmempat[] INIT(= N_(
"E363: pattern uses more memory than 'maxmempattern'"));
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));
@@ -1157,6 +1146,14 @@ EXTERN char_u e_dirnotf[] INIT(= N_(
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));
+EXTERN char_u e_autocmd_err[] INIT(=N_(
+ "E5500: autocmd has thrown an exception: %s"));
+EXTERN char_u e_cmdmap_err[] INIT(=N_(
+ "E5520: <Cmd> mapping must end with <CR>"));
+EXTERN char_u e_cmdmap_repeated[] INIT(=N_(
+ "E5521: <Cmd> mapping must end with <CR> before second <Cmd>"));
+EXTERN char_u e_cmdmap_key[] INIT(=N_(
+ "E5522: <Cmd> mapping must not include %s key"));
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
@@ -1177,9 +1174,9 @@ EXTERN char *ignoredp;
// If a msgpack-rpc channel should be started over stdin/stdout
EXTERN bool embedded_mode INIT(= false);
-
-/// next free id for a job or rpc channel
-EXTERN uint64_t next_chan_id INIT(= 1);
+// Dont try to start an user interface
+// or read/write to stdio (unless embedding)
+EXTERN bool headless_mode INIT(= false);
/// Used to track the status of external functions.
/// Currently only used for iconv().
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index 3397788b00..526bc284a4 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -208,7 +208,7 @@ int hash_add(hashtab_T *ht, char_u *key)
hash_T hash = hash_hash(key);
hashitem_T *hi = hash_lookup(ht, (const char *)key, STRLEN(key), hash);
if (!HASHITEM_EMPTY(hi)) {
- EMSG2(_(e_intern2), "hash_add()");
+ internal_error("hash_add()");
return FAIL;
}
hash_add_item(ht, hi, key, hash);
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 927fc94bbe..08157935f5 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -7,6 +7,34 @@
typedef int32_t RgbValue;
+/// Highlighting attribute bits.
+typedef enum {
+ HL_INVERSE = 0x01,
+ HL_BOLD = 0x02,
+ HL_ITALIC = 0x04,
+ HL_UNDERLINE = 0x08,
+ HL_UNDERCURL = 0x10,
+ HL_STANDOUT = 0x20,
+} HlAttrFlags;
+
+/// Stores a complete highlighting entry, including colors and attributes
+/// for both TUI and GUI.
+typedef struct attr_entry {
+ int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc.
+ RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color;
+ int cterm_fg_color, cterm_bg_color;
+} HlAttrs;
+
+#define HLATTRS_INIT (HlAttrs) { \
+ .rgb_ae_attr = 0, \
+ .cterm_ae_attr = 0, \
+ .rgb_fg_color = -1, \
+ .rgb_bg_color = -1, \
+ .rgb_sp_color = -1, \
+ .cterm_fg_color = 0, \
+ .cterm_bg_color = 0, \
+}
+
/// Values for index in highlight_attr[].
/// When making changes, also update hlf_names below!
typedef enum {
@@ -117,7 +145,6 @@ EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.
EXTERN int highlight_user[9]; // User[1-9] attributes
EXTERN int highlight_stlnc[9]; // On top of user
EXTERN int cterm_normal_fg_color INIT(= 0);
-EXTERN int cterm_normal_fg_bold INIT(= 0);
EXTERN int cterm_normal_bg_color INIT(= 0);
EXTERN RgbValue normal_fg INIT(= -1);
EXTERN RgbValue normal_bg INIT(= -1);
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index f7706f5a0d..b78b56562c 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -172,14 +172,10 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
}
-/*
- * PRIVATE: do_cscope_general
- *
- * Find the command, print help if invalid, and then call the corresponding
- * command function.
- */
-static void
-do_cscope_general (
+/// Find the command, print help if invalid, and then call the corresponding
+/// command function.
+static void
+do_cscope_general(
exarg_T *eap,
int make_split /* whether to split window */
)
@@ -208,29 +204,20 @@ do_cscope_general (
postponed_split_tab = 0;
}
-/*
- * PUBLIC: do_cscope
- */
-void do_cscope(exarg_T *eap)
+/// Implementation of ":cscope" and ":lcscope"
+void ex_cscope(exarg_T *eap)
{
do_cscope_general(eap, FALSE);
}
-/*
- * PUBLIC: do_scscope
- *
- * same as do_cscope, but splits window, too.
- */
-void do_scscope(exarg_T *eap)
+/// Implementation of ":scscope". Same as ex_cscope(), but splits window, too.
+void ex_scscope(exarg_T *eap)
{
do_cscope_general(eap, TRUE);
}
-/*
- * PUBLIC: do_cstag
- *
- */
-void do_cstag(exarg_T *eap)
+/// Implementation of ":cstag"
+void ex_cstag(exarg_T *eap)
{
int ret = FALSE;
@@ -285,18 +272,13 @@ void do_cstag(exarg_T *eap)
(void)EMSG(_("E257: cstag: tag not found"));
g_do_tagpreview = 0;
}
-
-} /* do_cscope */
+}
-/*
- * PUBLIC: cs_find
- *
- * this simulates a vim_fgets(), but for cscope, returns the next line
- * from the cscope output. should only be called from find_tags()
- *
- * returns TRUE if eof, FALSE otherwise
- */
+/// This simulates a vim_fgets(), but for cscope, returns the next line
+/// from the cscope output. should only be called from find_tags()
+///
+/// @return TRUE if eof, FALSE otherwise
int cs_fgets(char_u *buf, int size)
{
char *p;
@@ -309,21 +291,13 @@ int cs_fgets(char_u *buf, int size)
} /* cs_fgets */
-/*
- * PUBLIC: cs_free_tags
- *
- * called only from do_tag(), when popping the tag stack
- */
+/// Called only from do_tag(), when popping the tag stack.
void cs_free_tags(void)
{
cs_manage_matches(NULL, NULL, 0, Free);
}
-/*
- * PUBLIC: cs_print_tags
- *
- * called from do_tag()
- */
+/// Called from do_tag().
void cs_print_tags(void)
{
cs_manage_matches(NULL, NULL, 0, Print);
@@ -404,14 +378,8 @@ int cs_connection(int num, char_u *dbpath, char_u *ppath)
* PRIVATE functions
****************************************************************************/
-/*
- * PRIVATE: cs_add
- *
- * add cscope database or a directory name (to look for cscope.out)
- * to the cscope connection list
- *
- * MAXPATHL 256
- */
+/// Add cscope database or a directory name (to look for cscope.out)
+/// to the cscope connection list.
static int cs_add(exarg_T *eap)
{
char *fname, *ppath, *flags = NULL;
@@ -437,17 +405,13 @@ static void cs_stat_emsg(char *fname)
}
-/*
- * PRIVATE: cs_add_common
- *
- * the common routine to add a new cscope connection. called by
- * cs_add() and cs_reset(). i really don't like to do this, but this
- * routine uses a number of goto statements.
- */
-static int
-cs_add_common (
- char *arg1, /* filename - may contain environment variables */
- char *arg2, /* prepend path - may contain environment variables */
+/// The common routine to add a new cscope connection. Called by
+/// cs_add() and cs_reset(). I really don't like to do this, but this
+/// routine uses a number of goto statements.
+static int
+cs_add_common(
+ char *arg1, // filename - may contain environment variables
+ char *arg2, // prepend path - may contain environment variables
char *flags
)
{
@@ -561,11 +525,7 @@ static int cs_check_for_tags(void)
return p_tags[0] != NUL && curbuf->b_p_tags != NULL;
} /* cs_check_for_tags */
-/*
- * PRIVATE: cs_cnt_connections
- *
- * count the number of cscope connections
- */
+/// Count the number of cscope connections.
static size_t cs_cnt_connections(void)
{
size_t cnt = 0;
@@ -585,21 +545,23 @@ static void cs_reading_emsg(
}
#define CSREAD_BUFSIZE 2048
-/*
- * PRIVATE: cs_cnt_matches
- *
- * count the number of matches for a given cscope connection.
- */
+/// Count the number of matches for a given cscope connection.
static int cs_cnt_matches(size_t idx)
{
char *stok;
- int nlines;
+ int nlines = 0;
char *buf = xmalloc(CSREAD_BUFSIZE);
for (;; ) {
+ errno = 0;
if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) {
- if (feof(csinfo[idx].fr_fp))
+ if (errno == EINTR) {
+ continue;
+ }
+
+ if (feof(csinfo[idx].fr_fp)) {
errno = EIO;
+ }
cs_reading_emsg(idx);
@@ -607,16 +569,20 @@ static int cs_cnt_matches(size_t idx)
return CSCOPE_FAILURE;
}
- /*
- * If the database is out of date, or there's some other problem,
- * cscope will output error messages before the number-of-lines output.
- * Display/discard any output that doesn't match what we want.
- * Accept "\S*cscope: X lines", also matches "mlcscope".
- */
- if ((stok = strtok(buf, (const char *)" ")) == NULL)
+ // If the database is out of date, or there's some other problem,
+ // cscope will output error messages before the number-of-lines output.
+ // Display/discard any output that doesn't match what we want.
+ // Accept "\S*cscope: X lines", also matches "mlcscope".
+ // Bail out for the "Unable to search" error.
+ if (strstr((const char *)buf, "Unable to search database") != NULL) {
+ break;
+ }
+ if ((stok = strtok(buf, (const char *)" ")) == NULL) {
continue;
- if (strstr((const char *)stok, "cscope:") == NULL)
+ }
+ if (strstr((const char *)stok, "cscope:") == NULL) {
continue;
+ }
if ((stok = strtok(NULL, (const char *)" ")) == NULL)
continue;
@@ -639,11 +605,7 @@ static int cs_cnt_matches(size_t idx)
} /* cs_cnt_matches */
-/*
- * PRIVATE: cs_create_cmd
- *
- * Creates the actual cscope command query from what the user entered.
- */
+/// Creates the actual cscope command query from what the user entered.
static char *cs_create_cmd(char *csoption, char *pattern)
{
char *cmd;
@@ -699,12 +661,8 @@ static char *cs_create_cmd(char *csoption, char *pattern)
} /* cs_create_cmd */
-/*
- * PRIVATE: cs_create_connection
- *
- * This piece of code was taken/adapted from nvi. do we need to add
- * the BSD license notice?
- */
+/// This piece of code was taken/adapted from nvi. do we need to add
+/// the BSD license notice?
static int cs_create_connection(size_t i)
{
#ifdef UNIX
@@ -830,7 +788,6 @@ err_closing:
if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1)
PERROR(_("cs_create_connection exec failed"));
- stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
exit(127);
/* NOTREACHED */
default: /* parent. */
@@ -893,14 +850,10 @@ err_closing:
} /* cs_create_connection */
-/*
- * PRIVATE: cs_find
- *
- * query cscope using command line interface. parse the output and use tselect
- * to allow choices. like Nvi, creates a pipe to send to/from query/cscope.
- *
- * returns TRUE if we jump to a tag or abort, FALSE if not.
- */
+/// Query cscope using command line interface. Parse the output and use tselect
+/// to allow choices. Like Nvi, creates a pipe to send to/from query/cscope.
+///
+/// @return TRUE if we jump to a tag or abort, FALSE if not.
static int cs_find(exarg_T *eap)
{
char *opt, *pat;
@@ -934,11 +887,7 @@ static int cs_find(exarg_T *eap)
} /* cs_find */
-/*
- * PRIVATE: cs_find_common
- *
- * common code for cscope find, shared by cs_find() and do_cstag()
- */
+/// Common code for cscope find, shared by cs_find() and ex_cstag().
static int cs_find_common(char *opt, char *pat, int forceit, int verbose,
int use_ll, char_u *cmdline)
{
@@ -1067,9 +1016,9 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose,
fclose(f);
if (use_ll) /* Use location list */
wp = curwin;
- /* '-' starts a new error list */
+ // '-' starts a new error list
if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m",
- *qfpos == '-', cmdline) > 0) {
+ *qfpos == '-', cmdline, NULL) > 0) {
if (postponed_split != 0) {
(void)win_split(postponed_split > 0 ? postponed_split : 0,
postponed_split_flags);
@@ -1111,11 +1060,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose,
} /* cs_find_common */
-/*
- * PRIVATE: cs_help
- *
- * print help
- */
+/// Print help.
static int cs_help(exarg_T *eap)
{
cscmd_T *cmdp = cs_cmds;
@@ -1163,11 +1108,7 @@ static void clear_csinfo(size_t i)
csinfo[i].to_fp = NULL;
}
-/*
- * PRIVATE: cs_insert_filelist
- *
- * insert a new cscope database filename into the filelist
- */
+/// Insert a new cscope database filename into the filelist.
static int cs_insert_filelist(char *fname, char *ppath, char *flags,
FileInfo *file_info)
{
@@ -1227,11 +1168,7 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags,
} /* cs_insert_filelist */
-/*
- * PRIVATE: cs_lookup_cmd
- *
- * find cscope command in command table
- */
+/// Find cscope command in command table.
static cscmd_T * cs_lookup_cmd(exarg_T *eap)
{
cscmd_T *cmdp;
@@ -1256,11 +1193,7 @@ static cscmd_T * cs_lookup_cmd(exarg_T *eap)
} /* cs_lookup_cmd */
-/*
- * PRIVATE: cs_kill
- *
- * nuke em
- */
+/// Nuke em.
static int cs_kill(exarg_T *eap)
{
char *stok;
@@ -1317,11 +1250,7 @@ static int cs_kill(exarg_T *eap)
} /* cs_kill */
-/*
- * PRIVATE: cs_kill_execute
- *
- * Actually kills a specific cscope connection.
- */
+/// Actually kills a specific cscope connection.
static void cs_kill_execute(
size_t i, /* cscope table index */
char *cname /* cscope database name */
@@ -1336,26 +1265,22 @@ static void cs_kill_execute(
}
-/*
- * PRIVATE: cs_make_vim_style_matches
- *
- * convert the cscope output into a ctags style entry (as might be found
- * in a ctags tags file). there's one catch though: cscope doesn't tell you
- * the type of the tag you are looking for. for example, in Darren Hiebert's
- * ctags (the one that comes with vim), #define's use a line number to find the
- * tag in a file while function definitions use a regexp search pattern.
- *
- * i'm going to always use the line number because cscope does something
- * quirky (and probably other things i don't know about):
- *
- * if you have "# define" in your source file, which is
- * perfectly legal, cscope thinks you have "#define". this
- * will result in a failed regexp search. :(
- *
- * besides, even if this particular case didn't happen, the search pattern
- * would still have to be modified to escape all the special regular expression
- * characters to comply with ctags formatting.
- */
+/// Convert the cscope output into a ctags style entry (as might be found
+/// in a ctags tags file). there's one catch though: cscope doesn't tell you
+/// the type of the tag you are looking for. for example, in Darren Hiebert's
+/// ctags (the one that comes with vim), #define's use a line number to find the
+/// tag in a file while function definitions use a regexp search pattern.
+///
+/// I'm going to always use the line number because cscope does something
+/// quirky (and probably other things i don't know about):
+///
+/// if you have "# define" in your source file, which is
+/// perfectly legal, cscope thinks you have "#define". this
+/// will result in a failed regexp search. :(
+///
+/// Besides, even if this particular case didn't happen, the search pattern
+/// would still have to be modified to escape all the special regular expression
+/// characters to comply with ctags formatting.
static char *cs_make_vim_style_matches(char *fname, char *slno, char *search,
char *tagstr)
{
@@ -1389,24 +1314,20 @@ static char *cs_make_vim_style_matches(char *fname, char *slno, char *search,
} /* cs_make_vim_style_matches */
-/*
- * PRIVATE: cs_manage_matches
- *
- * this is kind of hokey, but i don't see an easy way round this..
- *
- * Store: keep a ptr to the (malloc'd) memory of matches originally
- * generated from cs_find(). the matches are originally lines directly
- * from cscope output, but transformed to look like something out of a
- * ctags. see cs_make_vim_style_matches for more details.
- *
- * Get: used only from cs_fgets(), this simulates a vim_fgets() to return
- * the next line from the cscope output. it basically keeps track of which
- * lines have been "used" and returns the next one.
- *
- * Free: frees up everything and resets
- *
- * Print: prints the tags
- */
+/// This is kind of hokey, but i don't see an easy way round this.
+///
+/// Store: keep a ptr to the (malloc'd) memory of matches originally
+/// generated from cs_find(). the matches are originally lines directly
+/// from cscope output, but transformed to look like something out of a
+/// ctags. see cs_make_vim_style_matches for more details.
+///
+/// Get: used only from cs_fgets(), this simulates a vim_fgets() to return
+/// the next line from the cscope output. it basically keeps track of which
+/// lines have been "used" and returns the next one.
+///
+/// Free: frees up everything and resets
+///
+/// Print: prints the tags
static char *cs_manage_matches(char **matches, char **contexts,
size_t totmatches, mcmd_e cmd)
{
@@ -1452,8 +1373,8 @@ static char *cs_manage_matches(char **matches, char **contexts,
case Print:
cs_print_tags_priv(mp, cp, cnt);
break;
- default: /* should not reach here */
- (void)EMSG(_("E570: fatal error in cs_manage_matches"));
+ default: // should not reach here
+ IEMSG(_("E570: fatal error in cs_manage_matches"));
return NULL;
}
@@ -1461,11 +1382,7 @@ static char *cs_manage_matches(char **matches, char **contexts,
} /* cs_manage_matches */
-/*
- * PRIVATE: cs_parse_results
- *
- * parse cscope output
- */
+/// Parse cscope output.
static char *cs_parse_results(size_t cnumber, char *buf, int bufsize,
char **context, char **linenumber, char **search)
{
@@ -1473,9 +1390,16 @@ static char *cs_parse_results(size_t cnumber, char *buf, int bufsize,
char *p;
char *name;
+retry:
+ errno = 0;
if (fgets(buf, bufsize, csinfo[cnumber].fr_fp) == NULL) {
- if (feof(csinfo[cnumber].fr_fp))
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ if (feof(csinfo[cnumber].fr_fp)) {
errno = EIO;
+ }
cs_reading_emsg(cnumber);
@@ -1515,11 +1439,7 @@ static char *cs_parse_results(size_t cnumber, char *buf, int bufsize,
return name;
}
-/*
- * PRIVATE: cs_file_results
- *
- * write cscope find results to file
- */
+/// Write cscope find results to file.
static void cs_file_results(FILE *f, int *nummatches_a)
{
char *search, *slno;
@@ -1560,13 +1480,9 @@ static void cs_file_results(FILE *f, int *nummatches_a)
xfree(buf);
}
-/*
- * PRIVATE: cs_fill_results
- *
- * get parsed cscope output and calls cs_make_vim_style_matches to convert
- * into ctags format
- * When there are no matches sets "*matches_p" to NULL.
- */
+/// Get parsed cscope output and calls cs_make_vim_style_matches to convert
+/// into ctags format.
+/// When there are no matches sets "*matches_p" to NULL.
static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a,
char ***matches_p, char ***cntxts_p,
size_t *matched)
@@ -1758,11 +1674,7 @@ static void cs_print_tags_priv(char **matches, char **cntxts,
xfree(buf);
}
-/*
- * PRIVATE: cs_read_prompt
- *
- * read a cscope prompt (basically, skip over the ">> ")
- */
+/// Read a cscope prompt (basically, skip over the ">> ").
static int cs_read_prompt(size_t i)
{
int ch;
@@ -1777,8 +1689,15 @@ static int cs_read_prompt(size_t i)
assert(IOSIZE >= cs_emsg_len);
size_t maxlen = IOSIZE - cs_emsg_len;
- for (;; ) {
- while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0]) {
+ while (1) {
+ while (1) {
+ do {
+ errno = 0;
+ ch = fgetc(csinfo[i].fr_fp);
+ } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp));
+ if (ch == EOF || ch == CSCOPE_PROMPT[0]) {
+ break;
+ }
// if there is room and char is printable
if (bufpos < maxlen - 1 && vim_isprintc(ch)) {
// lazy buffer allocation
@@ -1807,9 +1726,13 @@ static int cs_read_prompt(size_t i)
}
}
- for (size_t n = 0; n < strlen(CSCOPE_PROMPT); ++n) {
- if (n > 0)
- ch = (char)getc(csinfo[i].fr_fp);
+ for (size_t n = 0; n < strlen(CSCOPE_PROMPT); n++) {
+ if (n > 0) {
+ do {
+ errno = 0;
+ ch = fgetc(csinfo[i].fr_fp);
+ } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp));
+ }
if (ch == EOF) {
PERROR("cs_read_prompt EOF");
if (buf != NULL && buf[0] != NUL)
@@ -1847,12 +1770,8 @@ static void sig_handler(int s) {
#endif
-/*
- * PRIVATE: cs_release_csp
- *
- * Does the actual free'ing for the cs ptr with an optional flag of whether
- * or not to free the filename. Called by cs_kill and cs_reset.
- */
+/// Does the actual free'ing for the cs ptr with an optional flag of whether
+/// or not to free the filename. Called by cs_kill and cs_reset.
static void cs_release_csp(size_t i, int freefnpp)
{
// Trying to exit normally (not sure whether it is fit to Unix cscope)
@@ -1964,11 +1883,7 @@ static void cs_release_csp(size_t i, int freefnpp)
} /* cs_release_csp */
-/*
- * PRIVATE: cs_reset
- *
- * calls cs_kill on all cscope connections then reinits
- */
+/// Calls cs_kill on all cscope connections then reinits.
static int cs_reset(exarg_T *eap)
{
char **dblist = NULL, **pplist = NULL, **fllist = NULL;
@@ -2018,17 +1933,13 @@ static int cs_reset(exarg_T *eap)
} /* cs_reset */
-/*
- * PRIVATE: cs_resolve_file
- *
- * Construct the full pathname to a file found in the cscope database.
- * (Prepends ppath, if there is one and if it's not already prepended,
- * otherwise just uses the name found.)
- *
- * We need to prepend the prefix because on some cscope's (e.g., the one that
- * ships with Solaris 2.6), the output never has the prefix prepended.
- * Contrast this with my development system (Digital Unix), which does.
- */
+/// Construct the full pathname to a file found in the cscope database.
+/// (Prepends ppath, if there is one and if it's not already prepended,
+/// otherwise just uses the name found.)
+///
+/// We need to prepend the prefix because on some cscope's (e.g., the one that
+/// ships with Solaris 2.6), the output never has the prefix prepended.
+/// Contrast this with my development system (Digital Unix), which does.
static char *cs_resolve_file(size_t i, char *name)
{
char *fullname;
@@ -2074,11 +1985,7 @@ static char *cs_resolve_file(size_t i, char *name)
}
-/*
- * PRIVATE: cs_show
- *
- * show all cscope connections
- */
+/// Show all cscope connections.
static int cs_show(exarg_T *eap)
{
if (cs_cnt_connections() == 0)
@@ -2106,11 +2013,7 @@ static int cs_show(exarg_T *eap)
} /* cs_show */
-/*
- * PUBLIC: cs_end
- *
- * Only called when VIM exits to quit any cscope sessions.
- */
+/// Only called when VIM exits to quit any cscope sessions.
void cs_end(void)
{
for (size_t i = 0; i < csinfo_size; i++)
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index fd194a4080..2a215f854f 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -515,34 +515,41 @@ int cin_isscopedecl(char_u *s)
/* Maximum number of lines to search back for a "namespace" line. */
#define FIND_NAMESPACE_LIM 20
-/*
- * Recognize a "namespace" scope declaration.
- */
-static int cin_is_cpp_namespace(char_u *s)
+// Recognize a "namespace" scope declaration.
+static bool cin_is_cpp_namespace(char_u *s)
{
- char_u *p;
- int has_name = FALSE;
+ char_u *p;
+ bool has_name = false;
+ bool has_name_start = false;
s = cin_skipcomment(s);
if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) {
p = cin_skipcomment(skipwhite(s + 9));
while (*p != NUL) {
if (ascii_iswhite(*p)) {
- has_name = TRUE; /* found end of a name */
+ has_name = true; // found end of a name
p = cin_skipcomment(skipwhite(p));
} else if (*p == '{') {
break;
} else if (vim_iswordc(*p)) {
- if (has_name)
- return FALSE; /* word character after skipping past name */
- ++p;
+ has_name_start = true;
+ if (has_name) {
+ return false; // word character after skipping past name
+ }
+ p++;
+ } else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) {
+ if (!has_name_start || has_name) {
+ return false;
+ }
+ // C++ 17 nested namespace
+ p += 3;
} else {
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -727,16 +734,20 @@ static int cin_ispreproc(char_u *s)
return FALSE;
}
-/*
- * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a
- * continuation line of a preprocessor statement. Decrease "*lnump" to the
- * start and return the line in "*pp".
- */
-static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump)
+/// Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a
+/// continuation line of a preprocessor statement. Decrease "*lnump" to the
+/// start and return the line in "*pp".
+/// Put the amount of indent in "*amount".
+static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount)
{
char_u *line = *pp;
linenr_T lnum = *lnump;
- int retval = FALSE;
+ int retval = false;
+ int candidate_amount = *amount;
+
+ if (*line != NUL && line[STRLEN(line) - 1] == '\\') {
+ candidate_amount = get_indent_lnum(lnum);
+ }
for (;; ) {
if (cin_ispreproc(line)) {
@@ -751,8 +762,12 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump)
break;
}
- if (lnum != *lnump)
+ if (lnum != *lnump) {
*pp = ml_get(*lnump);
+ }
+ if (retval) {
+ *amount = candidate_amount;
+ }
return retval;
}
@@ -1299,6 +1314,43 @@ static int cin_starts_with(char_u *s, char *word)
return STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l]);
}
+/// Recognize a `extern "C"` or `extern "C++"` linkage specifications.
+static int cin_is_cpp_extern_c(char_u *s)
+{
+ char_u *p;
+ int has_string_literal = false;
+
+ s = cin_skipcomment(s);
+ if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) {
+ p = cin_skipcomment(skipwhite(s + 6));
+ while (*p != NUL) {
+ if (ascii_iswhite(*p)) {
+ p = cin_skipcomment(skipwhite(p));
+ } else if (*p == '{') {
+ break;
+ } else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') {
+ if (has_string_literal) {
+ return false;
+ }
+ has_string_literal = true;
+ p += 3;
+ } else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+'
+ && p[4] == '"') {
+ if (has_string_literal) {
+ return false;
+ }
+ has_string_literal = true;
+ p += 5;
+ } else {
+ return false;
+ }
+ }
+ return has_string_literal ? true : false;
+ }
+ return false;
+}
+
+
/*
* Skip strings, chars and comments until at or past "trypos".
* Return the column found.
@@ -1307,14 +1359,19 @@ static int cin_skip2pos(pos_T *trypos)
{
char_u *line;
char_u *p;
+ char_u *new_p;
p = line = ml_get(trypos->lnum);
while (*p && (colnr_T)(p - line) < trypos->col) {
- if (cin_iscomment(p))
+ if (cin_iscomment(p)) {
p = cin_skipcomment(p);
- else {
- p = skip_string(p);
- ++p;
+ } else {
+ new_p = skip_string(p);
+ if (new_p == p) {
+ p++;
+ } else {
+ p = new_p;
+ }
}
}
return (int)(p - line);
@@ -1604,6 +1661,12 @@ void parse_cino(buf_T *buf)
* while(). */
buf->b_ind_if_for_while = 0;
+ // indentation for # comments
+ buf->b_ind_hash_comment = 0;
+
+ // Handle C++ extern "C" or "C++"
+ buf->b_ind_cpp_extern_c = 0;
+
for (p = buf->b_p_cino; *p; ) {
l = p++;
if (*p == '-')
@@ -1672,6 +1735,7 @@ void parse_cino(buf_T *buf)
case '#': buf->b_ind_hash_comment = n; break;
case 'N': buf->b_ind_cpp_namespace = n; break;
case 'k': buf->b_ind_if_for_while = n; break;
+ case 'E': buf->b_ind_cpp_extern_c = n; break;
}
if (*p == ',')
++p;
@@ -1987,10 +2051,12 @@ int get_c_indent(void)
amount = -1;
for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) {
l = skipwhite(ml_get(lnum));
- if (cin_nocode(l)) /* skip comment lines */
+ if (cin_nocode(l)) { // skip comment lines
continue;
- if (cin_ispreproc_cont(&l, &lnum))
- continue; /* ignore #define, #if, etc. */
+ }
+ if (cin_ispreproc_cont(&l, &lnum, &amount)) {
+ continue; // ignore #define, #if, etc.
+ }
curwin->w_cursor.lnum = lnum;
/* Skip a comment or raw string. XXX */
@@ -2300,8 +2366,11 @@ int get_c_indent(void)
amount += curbuf->b_ind_open_imag;
l = skipwhite(get_cursor_line_ptr());
- if (cin_is_cpp_namespace(l))
+ if (cin_is_cpp_namespace(l)) {
amount += curbuf->b_ind_cpp_namespace;
+ } else if (cin_is_cpp_extern_c(l)) {
+ amount += curbuf->b_ind_cpp_extern_c;
+ }
} else {
/* Compensate for adding b_ind_open_extra later. */
amount -= curbuf->b_ind_open_extra;
@@ -2346,15 +2415,14 @@ int get_c_indent(void)
* up with it.
*/
if (curwin->w_cursor.lnum <= ourscope) {
- /* we reached end of scope:
- * if looking for an enum or structure initialization
- * go further back:
- * if it is an initializer (enum xxx or xxx =), then
- * don't add ind_continuation, otherwise it is a variable
- * declaration:
- * int x,
- * here; <-- add ind_continuation
- */
+ // We reached end of scope:
+ // If looking for a enum or structure initialization
+ // go further back:
+ // If it is an initializer (enum xxx or xxx =), then
+ // don't add ind_continuation, otherwise it is a variable
+ // declaration:
+ // int x,
+ // here; <-- add ind_continuation
if (lookfor == LOOKFOR_ENUM_OR_INIT) {
if (curwin->w_cursor.lnum == 0
|| curwin->w_cursor.lnum
@@ -2382,11 +2450,12 @@ int get_c_indent(void)
continue;
}
- /*
- * Skip preprocessor directives and blank lines.
- */
- if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum))
+ //
+ // Skip preprocessor directives and blank lines.
+ //
+ if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) {
continue;
+ }
if (cin_nocode(l))
continue;
@@ -2490,15 +2559,19 @@ int get_c_indent(void)
continue;
}
- /* Skip preprocessor directives and blank lines. */
- if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum))
+ // Skip preprocessor directives and blank lines.
+ if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) {
continue;
+ }
/* Finally the actual check for "namespace". */
if (cin_is_cpp_namespace(l)) {
amount += curbuf->b_ind_cpp_namespace
- added_to_amount;
break;
+ } else if (cin_is_cpp_extern_c(l)) {
+ amount += curbuf->b_ind_cpp_extern_c - added_to_amount;
+ break;
}
if (cin_nocode(l))
@@ -2655,9 +2728,10 @@ int get_c_indent(void)
* unlocked it)
*/
l = get_cursor_line_ptr();
- if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)
- || cin_nocode(l))
+ if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)
+ || cin_nocode(l)) {
continue;
+ }
/*
* Are we at the start of a cpp base class declaration or
@@ -3302,11 +3376,12 @@ term_again:
break;
}
- /*
- * Skip preprocessor directives and blank lines.
- */
- if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum))
+ //
+ // Skip preprocessor directives and blank lines.
+ //
+ if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) {
continue;
+ }
if (cin_nocode(l))
continue;
@@ -3398,9 +3473,10 @@ term_again:
while (curwin->w_cursor.lnum > 1) {
look = ml_get(--curwin->w_cursor.lnum);
- if (!(cin_nocode(look) || cin_ispreproc_cont(
- &look, &curwin->w_cursor.lnum)))
+ if (!(cin_nocode(look)
+ || cin_ispreproc_cont(&look, &curwin->w_cursor.lnum, &amount))) {
break;
+ }
}
if (curwin->w_cursor.lnum > 0
&& cin_ends_in(look, (char_u *)"}", NULL))
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index ee67b4c19d..628bfef221 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -24,23 +24,23 @@
* Some useful tables.
*/
-static struct modmasktable {
- short mod_mask; /* Bit-mask for particular key modifier */
- short mod_flag; /* Bit(s) for particular key modifier */
- char_u name; /* Single letter name of modifier */
-} mod_mask_table[] =
-{
- {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M'},
- {MOD_MASK_META, MOD_MASK_META, (char_u)'T'},
- {MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C'},
- {MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S'},
- {MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'},
- {MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'},
- {MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'},
- {MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'},
+static const struct modmasktable {
+ uint16_t mod_mask; ///< Bit-mask for particular key modifier.
+ uint16_t mod_flag; ///< Bit(s) for particular key modifier.
+ char_u name; ///< Single letter name of modifier.
+} mod_mask_table[] = {
+ { MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M' },
+ { MOD_MASK_META, MOD_MASK_META, (char_u)'T' },
+ { MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C' },
+ { MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S' },
+ { MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2' },
+ { MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3' },
+ { MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4' },
+ { MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D' },
// 'A' must be the last one
- {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'},
- {0, 0, NUL}
+ { MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A' },
+ { 0, 0, NUL }
+ // NOTE: when adding an entry, update MAX_KEY_NAME_LEN!
};
/*
@@ -139,156 +139,155 @@ static char_u modifier_keys_table[] =
NUL
};
-static struct key_name_entry {
- int key; /* Special key code or ascii value */
- char_u *name; /* Name of key */
-} key_names_table[] =
-{
- {' ', (char_u *)"Space"},
- {TAB, (char_u *)"Tab"},
- {K_TAB, (char_u *)"Tab"},
- {NL, (char_u *)"NL"},
- {NL, (char_u *)"NewLine"}, /* Alternative name */
- {NL, (char_u *)"LineFeed"}, /* Alternative name */
- {NL, (char_u *)"LF"}, /* Alternative name */
- {CAR, (char_u *)"CR"},
- {CAR, (char_u *)"Return"}, /* Alternative name */
- {CAR, (char_u *)"Enter"}, /* Alternative name */
- {K_BS, (char_u *)"BS"},
- {K_BS, (char_u *)"BackSpace"}, /* Alternative name */
- {ESC, (char_u *)"Esc"},
- {CSI, (char_u *)"CSI"},
- {K_CSI, (char_u *)"xCSI"},
- {'|', (char_u *)"Bar"},
- {'\\', (char_u *)"Bslash"},
- {K_DEL, (char_u *)"Del"},
- {K_DEL, (char_u *)"Delete"}, /* Alternative name */
- {K_KDEL, (char_u *)"kDel"},
- {K_UP, (char_u *)"Up"},
- {K_DOWN, (char_u *)"Down"},
- {K_LEFT, (char_u *)"Left"},
- {K_RIGHT, (char_u *)"Right"},
- {K_XUP, (char_u *)"xUp"},
- {K_XDOWN, (char_u *)"xDown"},
- {K_XLEFT, (char_u *)"xLeft"},
- {K_XRIGHT, (char_u *)"xRight"},
-
- {K_F1, (char_u *)"F1"},
- {K_F2, (char_u *)"F2"},
- {K_F3, (char_u *)"F3"},
- {K_F4, (char_u *)"F4"},
- {K_F5, (char_u *)"F5"},
- {K_F6, (char_u *)"F6"},
- {K_F7, (char_u *)"F7"},
- {K_F8, (char_u *)"F8"},
- {K_F9, (char_u *)"F9"},
- {K_F10, (char_u *)"F10"},
-
- {K_F11, (char_u *)"F11"},
- {K_F12, (char_u *)"F12"},
- {K_F13, (char_u *)"F13"},
- {K_F14, (char_u *)"F14"},
- {K_F15, (char_u *)"F15"},
- {K_F16, (char_u *)"F16"},
- {K_F17, (char_u *)"F17"},
- {K_F18, (char_u *)"F18"},
- {K_F19, (char_u *)"F19"},
- {K_F20, (char_u *)"F20"},
-
- {K_F21, (char_u *)"F21"},
- {K_F22, (char_u *)"F22"},
- {K_F23, (char_u *)"F23"},
- {K_F24, (char_u *)"F24"},
- {K_F25, (char_u *)"F25"},
- {K_F26, (char_u *)"F26"},
- {K_F27, (char_u *)"F27"},
- {K_F28, (char_u *)"F28"},
- {K_F29, (char_u *)"F29"},
- {K_F30, (char_u *)"F30"},
-
- {K_F31, (char_u *)"F31"},
- {K_F32, (char_u *)"F32"},
- {K_F33, (char_u *)"F33"},
- {K_F34, (char_u *)"F34"},
- {K_F35, (char_u *)"F35"},
- {K_F36, (char_u *)"F36"},
- {K_F37, (char_u *)"F37"},
-
- {K_XF1, (char_u *)"xF1"},
- {K_XF2, (char_u *)"xF2"},
- {K_XF3, (char_u *)"xF3"},
- {K_XF4, (char_u *)"xF4"},
-
- {K_HELP, (char_u *)"Help"},
- {K_UNDO, (char_u *)"Undo"},
- {K_INS, (char_u *)"Insert"},
- {K_INS, (char_u *)"Ins"}, /* Alternative name */
- {K_KINS, (char_u *)"kInsert"},
- {K_HOME, (char_u *)"Home"},
- {K_KHOME, (char_u *)"kHome"},
- {K_XHOME, (char_u *)"xHome"},
- {K_ZHOME, (char_u *)"zHome"},
- {K_END, (char_u *)"End"},
- {K_KEND, (char_u *)"kEnd"},
- {K_XEND, (char_u *)"xEnd"},
- {K_ZEND, (char_u *)"zEnd"},
- {K_PAGEUP, (char_u *)"PageUp"},
- {K_PAGEDOWN, (char_u *)"PageDown"},
- {K_KPAGEUP, (char_u *)"kPageUp"},
- {K_KPAGEDOWN, (char_u *)"kPageDown"},
-
- {K_KPLUS, (char_u *)"kPlus"},
- {K_KMINUS, (char_u *)"kMinus"},
- {K_KDIVIDE, (char_u *)"kDivide"},
- {K_KMULTIPLY, (char_u *)"kMultiply"},
- {K_KENTER, (char_u *)"kEnter"},
- {K_KPOINT, (char_u *)"kPoint"},
-
- {K_K0, (char_u *)"k0"},
- {K_K1, (char_u *)"k1"},
- {K_K2, (char_u *)"k2"},
- {K_K3, (char_u *)"k3"},
- {K_K4, (char_u *)"k4"},
- {K_K5, (char_u *)"k5"},
- {K_K6, (char_u *)"k6"},
- {K_K7, (char_u *)"k7"},
- {K_K8, (char_u *)"k8"},
- {K_K9, (char_u *)"k9"},
-
- {'<', (char_u *)"lt"},
-
- {K_MOUSE, (char_u *)"Mouse"},
- {K_LEFTMOUSE, (char_u *)"LeftMouse"},
- {K_LEFTMOUSE_NM, (char_u *)"LeftMouseNM"},
- {K_LEFTDRAG, (char_u *)"LeftDrag"},
- {K_LEFTRELEASE, (char_u *)"LeftRelease"},
- {K_LEFTRELEASE_NM, (char_u *)"LeftReleaseNM"},
- {K_MIDDLEMOUSE, (char_u *)"MiddleMouse"},
- {K_MIDDLEDRAG, (char_u *)"MiddleDrag"},
- {K_MIDDLERELEASE, (char_u *)"MiddleRelease"},
- {K_RIGHTMOUSE, (char_u *)"RightMouse"},
- {K_RIGHTDRAG, (char_u *)"RightDrag"},
- {K_RIGHTRELEASE, (char_u *)"RightRelease"},
- {K_MOUSEDOWN, (char_u *)"ScrollWheelUp"},
- {K_MOUSEUP, (char_u *)"ScrollWheelDown"},
- {K_MOUSELEFT, (char_u *)"ScrollWheelRight"},
- {K_MOUSERIGHT, (char_u *)"ScrollWheelLeft"},
- {K_MOUSEDOWN, (char_u *)"MouseDown"}, /* OBSOLETE: Use */
- {K_MOUSEUP, (char_u *)"MouseUp"}, /* ScrollWheelXXX instead */
- {K_X1MOUSE, (char_u *)"X1Mouse"},
- {K_X1DRAG, (char_u *)"X1Drag"},
- {K_X1RELEASE, (char_u *)"X1Release"},
- {K_X2MOUSE, (char_u *)"X2Mouse"},
- {K_X2DRAG, (char_u *)"X2Drag"},
- {K_X2RELEASE, (char_u *)"X2Release"},
- {K_DROP, (char_u *)"Drop"},
- {K_ZERO, (char_u *)"Nul"},
- {K_SNR, (char_u *)"SNR"},
- {K_PLUG, (char_u *)"Plug"},
- {K_PASTE, (char_u *)"Paste"},
- {K_FOCUSGAINED, (char_u *)"FocusGained"},
- {K_FOCUSLOST, (char_u *)"FocusLost"},
- {0, NULL}
+static const struct key_name_entry {
+ int key; // Special key code or ascii value
+ const char *name; // Name of key
+} key_names_table[] = {
+ { ' ', "Space" },
+ { TAB, "Tab" },
+ { K_TAB, "Tab" },
+ { NL, "NL" },
+ { NL, "NewLine" }, // Alternative name
+ { NL, "LineFeed" }, // Alternative name
+ { NL, "LF" }, // Alternative name
+ { CAR, "CR" },
+ { CAR, "Return" }, // Alternative name
+ { CAR, "Enter" }, // Alternative name
+ { K_BS, "BS" },
+ { K_BS, "BackSpace" }, // Alternative name
+ { ESC, "Esc" },
+ { CSI, "CSI" },
+ { K_CSI, "xCSI" },
+ { '|', "Bar" },
+ { '\\', "Bslash" },
+ { K_DEL, "Del" },
+ { K_DEL, "Delete" }, // Alternative name
+ { K_KDEL, "kDel" },
+ { K_UP, "Up" },
+ { K_DOWN, "Down" },
+ { K_LEFT, "Left" },
+ { K_RIGHT, "Right" },
+ { K_XUP, "xUp" },
+ { K_XDOWN, "xDown" },
+ { K_XLEFT, "xLeft" },
+ { K_XRIGHT, "xRight" },
+
+ { K_F1, "F1" },
+ { K_F2, "F2" },
+ { K_F3, "F3" },
+ { K_F4, "F4" },
+ { K_F5, "F5" },
+ { K_F6, "F6" },
+ { K_F7, "F7" },
+ { K_F8, "F8" },
+ { K_F9, "F9" },
+ { K_F10, "F10" },
+
+ { K_F11, "F11" },
+ { K_F12, "F12" },
+ { K_F13, "F13" },
+ { K_F14, "F14" },
+ { K_F15, "F15" },
+ { K_F16, "F16" },
+ { K_F17, "F17" },
+ { K_F18, "F18" },
+ { K_F19, "F19" },
+ { K_F20, "F20" },
+
+ { K_F21, "F21" },
+ { K_F22, "F22" },
+ { K_F23, "F23" },
+ { K_F24, "F24" },
+ { K_F25, "F25" },
+ { K_F26, "F26" },
+ { K_F27, "F27" },
+ { K_F28, "F28" },
+ { K_F29, "F29" },
+ { K_F30, "F30" },
+
+ { K_F31, "F31" },
+ { K_F32, "F32" },
+ { K_F33, "F33" },
+ { K_F34, "F34" },
+ { K_F35, "F35" },
+ { K_F36, "F36" },
+ { K_F37, "F37" },
+
+ { K_XF1, "xF1" },
+ { K_XF2, "xF2" },
+ { K_XF3, "xF3" },
+ { K_XF4, "xF4" },
+
+ { K_HELP, "Help" },
+ { K_UNDO, "Undo" },
+ { K_INS, "Insert" },
+ { K_INS, "Ins" }, // Alternative name
+ { K_KINS, "kInsert" },
+ { K_HOME, "Home" },
+ { K_KHOME, "kHome" },
+ { K_XHOME, "xHome" },
+ { K_ZHOME, "zHome" },
+ { K_END, "End" },
+ { K_KEND, "kEnd" },
+ { K_XEND, "xEnd" },
+ { K_ZEND, "zEnd" },
+ { K_PAGEUP, "PageUp" },
+ { K_PAGEDOWN, "PageDown" },
+ { K_KPAGEUP, "kPageUp" },
+ { K_KPAGEDOWN, "kPageDown" },
+
+ { K_KPLUS, "kPlus" },
+ { K_KMINUS, "kMinus" },
+ { K_KDIVIDE, "kDivide" },
+ { K_KMULTIPLY, "kMultiply" },
+ { K_KENTER, "kEnter" },
+ { K_KPOINT, "kPoint" },
+
+ { K_K0, "k0" },
+ { K_K1, "k1" },
+ { K_K2, "k2" },
+ { K_K3, "k3" },
+ { K_K4, "k4" },
+ { K_K5, "k5" },
+ { K_K6, "k6" },
+ { K_K7, "k7" },
+ { K_K8, "k8" },
+ { K_K9, "k9" },
+
+ { '<', "lt" },
+
+ { K_MOUSE, "Mouse" },
+ { K_LEFTMOUSE, "LeftMouse" },
+ { K_LEFTMOUSE_NM, "LeftMouseNM" },
+ { K_LEFTDRAG, "LeftDrag" },
+ { K_LEFTRELEASE, "LeftRelease" },
+ { K_LEFTRELEASE_NM, "LeftReleaseNM" },
+ { K_MIDDLEMOUSE, "MiddleMouse" },
+ { K_MIDDLEDRAG, "MiddleDrag" },
+ { K_MIDDLERELEASE, "MiddleRelease" },
+ { K_RIGHTMOUSE, "RightMouse" },
+ { K_RIGHTDRAG, "RightDrag" },
+ { K_RIGHTRELEASE, "RightRelease" },
+ { K_MOUSEDOWN, "ScrollWheelUp" },
+ { K_MOUSEUP, "ScrollWheelDown" },
+ { K_MOUSELEFT, "ScrollWheelRight" },
+ { K_MOUSERIGHT, "ScrollWheelLeft" },
+ { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use
+ { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead
+ { K_X1MOUSE, "X1Mouse" },
+ { K_X1DRAG, "X1Drag" },
+ { K_X1RELEASE, "X1Release" },
+ { K_X2MOUSE, "X2Mouse" },
+ { K_X2DRAG, "X2Drag" },
+ { K_X2RELEASE, "X2Release" },
+ { K_DROP, "Drop" },
+ { K_ZERO, "Nul" },
+ { K_SNR, "SNR" },
+ { K_PLUG, "Plug" },
+ { K_PASTE, "Paste" },
+ { K_COMMAND, "Cmd" },
+ { 0, NULL }
+ // NOTE: When adding a long name update MAX_KEY_NAME_LEN.
};
static struct mousetable {
@@ -320,73 +319,73 @@ static struct mousetable {
{0, 0, 0, 0},
};
-/*
- * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given
- * modifier name ('S' for Shift, 'C' for Ctrl etc).
- */
+/// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name
+///
+/// E.g. 'S' for shift, 'C' for ctrl.
int name_to_mod_mask(int c)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
- int i;
-
c = TOUPPER_ASC(c);
- for (i = 0; mod_mask_table[i].mod_mask != 0; i++)
- if (c == mod_mask_table[i].name)
+ for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) {
+ if (c == mod_mask_table[i].name) {
return mod_mask_table[i].mod_flag;
+ }
+ }
return 0;
}
-/*
- * Check if if there is a special key code for "key" that includes the
- * modifiers specified.
- */
-int simplify_key(int key, int *modifiers)
+/// Check if there is a special key code for "key" with specified modifiers
+///
+/// @param[in] key Initial key code.
+/// @param[in,out] modifiers Initial modifiers, is adjusted to have simplified
+/// modifiers.
+///
+/// @return Simplified key code.
+int simplify_key(const int key, int *modifiers)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- int i;
- int key0;
- int key1;
-
if (*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) {
- /* TAB is a special case */
+ // TAB is a special case.
if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) {
*modifiers &= ~MOD_MASK_SHIFT;
return K_S_TAB;
}
- key0 = KEY2TERMCAP0(key);
- key1 = KEY2TERMCAP1(key);
- for (i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE)
+ const int key0 = KEY2TERMCAP0(key);
+ const int key1 = KEY2TERMCAP1(key);
+ for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) {
if (key0 == modifier_keys_table[i + 3]
&& key1 == modifier_keys_table[i + 4]
&& (*modifiers & modifier_keys_table[i])) {
*modifiers &= ~modifier_keys_table[i];
return TERMCAP2KEY(modifier_keys_table[i + 1],
- modifier_keys_table[i + 2]);
+ modifier_keys_table[i + 2]);
}
+ }
}
return key;
}
-/*
- * Change <xHome> to <Home>, <xUp> to <Up>, etc.
- */
-int handle_x_keys(int key)
+/// Change <xKey> to <Key>
+int handle_x_keys(const int key)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
switch (key) {
- case K_XUP: return K_UP;
- case K_XDOWN: return K_DOWN;
- case K_XLEFT: return K_LEFT;
- case K_XRIGHT: return K_RIGHT;
- case K_XHOME: return K_HOME;
- case K_ZHOME: return K_HOME;
- case K_XEND: return K_END;
- case K_ZEND: return K_END;
- case K_XF1: return K_F1;
- case K_XF2: return K_F2;
- case K_XF3: return K_F3;
- case K_XF4: return K_F4;
- case K_S_XF1: return K_S_F1;
- case K_S_XF2: return K_S_F2;
- case K_S_XF3: return K_S_F3;
- case K_S_XF4: return K_S_F4;
+ case K_XUP: return K_UP;
+ case K_XDOWN: return K_DOWN;
+ case K_XLEFT: return K_LEFT;
+ case K_XRIGHT: return K_RIGHT;
+ case K_XHOME: return K_HOME;
+ case K_ZHOME: return K_HOME;
+ case K_XEND: return K_END;
+ case K_ZEND: return K_END;
+ case K_XF1: return K_F1;
+ case K_XF2: return K_F2;
+ case K_XF3: return K_F3;
+ case K_XF4: return K_F4;
+ case K_S_XF1: return K_S_F1;
+ case K_S_XF2: return K_S_F2;
+ case K_S_XF3: return K_S_F3;
+ case K_S_XF4: return K_S_F4;
}
return key;
}
@@ -476,16 +475,20 @@ char_u *get_special_key_name(int c, int modifiers)
string[idx++] = *s++;
}
}
- } else { /* use name of special key */
- STRCPY(string + idx, key_names_table[table_idx].name);
- idx = (int)STRLEN(string);
+ } else { // use name of special key
+ size_t len = STRLEN(key_names_table[table_idx].name);
+
+ if ((int)len + idx + 2 <= MAX_KEY_NAME_LEN) {
+ STRCPY(string + idx, key_names_table[table_idx].name);
+ idx += (int)len;
+ }
}
string[idx++] = '>';
string[idx] = NUL;
return string;
}
-/// Try translating a <> name
+/// Try translating a <> name ("keycode").
///
/// @param[in,out] srcp Source from which <> are translated. Is advanced to
/// after the <> name if there is a match.
@@ -510,7 +513,7 @@ unsigned int trans_special(const char_u **srcp, const size_t src_len,
return 0;
}
- /* Put the appropriate modifier in a string */
+ // Put the appropriate modifier in a string.
if (modifiers != 0) {
dst[dlen++] = K_SPECIAL;
dst[dlen++] = KS_MODIFIER;
@@ -571,15 +574,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Find end of modifier list
last_dash = src;
- for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) {
+ for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) {
if (*bp == '-') {
last_dash = bp;
if (bp + 1 <= end) {
- if (has_mbyte) {
- l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1);
- } else {
- l = 1;
- }
+ l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1);
// Anything accepted, like <C-?>.
// <C-"> or <M-"> are not special in strings as " is
// the string delimiter. With a backslash it works: <M-\">
@@ -627,9 +626,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Modifier with single letter, or special key name.
if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') {
- off = 2;
+ // Special case for a double-quoted string
+ off = l = 2;
+ } else {
+ l = mb_ptr2len(last_dash + 1);
}
- l = mb_ptr2len(last_dash + 1);
if (modifiers != 0 && last_dash[l + 1] == '>') {
key = PTR2CHAR(last_dash + off);
} else {
@@ -704,33 +705,39 @@ int find_special_key_in_table(int c)
{
int i;
- for (i = 0; key_names_table[i].name != NULL; i++)
- if (c == key_names_table[i].key)
+ for (i = 0; key_names_table[i].name != NULL; i++) {
+ if (c == key_names_table[i].key) {
break;
- if (key_names_table[i].name == NULL)
+ }
+ }
+ if (key_names_table[i].name == NULL) {
i = -1;
+ }
return i;
}
-/*
- * Find the special key with the given name (the given string does not have to
- * end with NUL, the name is assumed to end before the first non-idchar).
- * If the name starts with "t_" the next two characters are interpreted as a
- * termcap name.
- * Return the key code, or 0 if not found.
- */
+/// Find the special key with the given name
+///
+/// @param[in] name Name of the special. Does not have to end with NUL, it is
+/// assumed to end before the first non-idchar. If name starts
+/// with "t_" the next two characters are interpreted as
+/// a termcap name.
+///
+/// @return Key code or 0 if not found.
int get_special_key_code(const char_u *name)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *table_name;
- int i, j;
-
- for (i = 0; key_names_table[i].name != NULL; i++) {
- table_name = key_names_table[i].name;
- for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++)
- if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j]))
+ for (int i = 0; key_names_table[i].name != NULL; i++) {
+ const char *const table_name = key_names_table[i].name;
+ int j;
+ for (j = 0; ascii_isident(name[j]) && table_name[j] != NUL; j++) {
+ if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) {
break;
- if (!vim_isIDc(name[j]) && table_name[j] == NUL)
+ }
+ }
+ if (!ascii_isident(name[j]) && table_name[j] == NUL) {
return key_names_table[i].key;
+ }
}
return 0;
@@ -756,9 +763,9 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
/// Replace any terminal code strings with the equivalent internal
/// representation
///
-/// This is used for the "from" and "to" part of a mapping, and the "to" part of
+/// Used for the "from" and "to" part of a mapping, and the "to" part of
/// a menu command. Any strings like "<C-UP>" are also replaced, unless
-/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL
+/// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL
/// KS_SPECIAL KE_FILLER.
///
/// @param[in] from What characters to replace.
@@ -771,7 +778,7 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash
/// can be used in place of <C-v>. All other <C-v>
/// characters are removed.
-/// @param[in] special If true, always accept <key> notation.
+/// @param[in] special Replace keycodes, e.g. <CR> becomes a "\n" char.
/// @param[in] cpo_flags Relevant flags derived from p_cpo, see
/// #CPO_TO_CPO_FLAGS.
///
@@ -790,11 +797,9 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
const char_u *src;
const char_u *const end = from + from_len - 1;
int do_backslash; // backslash is a special character
- int do_special; // recognize <> key codes
char_u *result; // buffer for resulting string
do_backslash = !(cpo_flags&FLAG_CPO_BSLASH);
- do_special = !(cpo_flags&FLAG_CPO_SPECI) || special;
// Allocate space for the translation. Worst case a single character is
// replaced by 6 bytes (shifted special key), plus a NUL at the end.
@@ -817,10 +822,9 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
// Copy each byte from *from to result[dlen]
while (src <= end) {
- // If 'cpoptions' does not contain '<', check for special key codes,
- // like "<C-S-LeftMouse>"
- if (do_special && (do_lt || ((end - src) >= 3
- && STRNCMP(src, "<lt>", 4) != 0))) {
+ // Check for special <> keycodes, like "<C-S-LeftMouse>"
+ if (special && (do_lt || ((end - src) >= 3
+ && STRNCMP(src, "<lt>", 4) != 0))) {
// Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
@@ -839,14 +843,14 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
}
slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true,
- true);
+ false);
if (slen) {
dlen += slen;
continue;
}
}
- if (do_special) {
+ if (special) {
char_u *p, *s, len;
// Replace <Leader> by the value of "mapleader".
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index bb8ba84a6a..00e9cf6ed3 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -243,6 +243,7 @@ enum key_extra {
, KE_EVENT // event
, KE_PASTE // special key to toggle the 'paste' option.
// sent only by UIs
+ , KE_COMMAND // special key to execute command in any mode
};
/*
@@ -428,11 +429,10 @@ enum key_extra {
#define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN)
#define K_DROP TERMCAP2KEY(KS_EXTRA, KE_DROP)
-#define K_FOCUSGAINED TERMCAP2KEY(KS_EXTRA, KE_FOCUSGAINED)
-#define K_FOCUSLOST TERMCAP2KEY(KS_EXTRA, KE_FOCUSLOST)
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE)
+#define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND)
/* Bits for modifier mask */
/* 0x01 cannot be used, because the modifier must be 0x02 or higher */
@@ -450,9 +450,10 @@ enum key_extra {
/*
* The length of the longest special key name, including modifiers.
- * Current longest is <M-C-S-T-4-MiddleRelease> (length includes '<' and '>').
+ * Current longest is <M-C-S-T-D-A-4-ScrollWheelRight> (length includes '<' and
+ * '>').
*/
-#define MAX_KEY_NAME_LEN 25
+#define MAX_KEY_NAME_LEN 32
// Maximum length of a special key event as tokens. This includes modifiers.
// The longest event is something like <M-C-S-T-4-LeftDrag> which would be the
@@ -464,13 +465,9 @@ enum key_extra {
#define MAX_KEY_CODE_LEN 6
#define FLAG_CPO_BSLASH 0x01
-#define FLAG_CPO_SPECI 0x02
-#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \
- ? 0 \
- : FLAG_CPO_BSLASH)| \
- (vim_strchr(p_cpo, CPO_SPECI) == NULL \
- ? 0 \
- : FLAG_CPO_SPECI))
+#define CPO_TO_CPO_FLAGS ((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \
+ ? 0 \
+ : FLAG_CPO_BSLASH)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "keymap.h.generated.h"
diff --git a/src/nvim/lib/kbtree.h b/src/nvim/lib/kbtree.h
index a3943054e6..e2688064a8 100644
--- a/src/nvim/lib/kbtree.h
+++ b/src/nvim/lib/kbtree.h
@@ -390,34 +390,14 @@
#define KBTREE_INIT(name, key_t, __cmp, T) \
KBTREE_INIT_IMPL(name, key_t, kbnode_##name##_t, __cmp, T, (sizeof(kbnode_##name##_t)+(2*T)*sizeof(void *)))
-#if (!defined(__clang__) && !defined(__INTEL_COMPILER)) && (__GNUC__ > 4 )
-
-// The index trickery shouldn't be UB anymore,
-// still it is to much for gcc:s -Werror=array-bounds
-# define __KB_PRAGMA_START \
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Warray-bounds\"")
-
-# define __KB_PRAGMA_END \
- _Pragma("GCC diagnostic pop") \
-
-#else
-
-# define __KB_PRAGMA_START
-# define __KB_PRAGMA_END
-
-#endif
-
#define KBTREE_INIT_IMPL(name, key_t, kbnode_t, __cmp, T, ILEN) \
- __KB_PRAGMA_START \
__KB_TREE_T(name, key_t, T) \
__KB_GET_AUX1(name, key_t, kbnode_t, __cmp) \
__KB_GET(name, key_t, kbnode_t) \
__KB_INTERVAL(name, key_t, kbnode_t) \
__KB_PUT(name, key_t, kbnode_t, __cmp, T, ILEN) \
__KB_DEL(name, key_t, kbnode_t, T) \
- __KB_ITR(name, key_t, kbnode_t) \
- __KB_PRAGMA_END
+ __KB_ITR(name, key_t, kbnode_t)
#define KB_DEFAULT_SIZE 512
diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h
index 584282d773..ad56c9237b 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/nvim/lib/kvec.h
@@ -41,6 +41,7 @@
#include <string.h>
#include "nvim/memory.h"
+#include "nvim/os/os_defs.h"
#define kv_roundup32(x) \
((--(x)), \
@@ -62,7 +63,16 @@
#define kv_pop(v) ((v).items[--(v).size])
#define kv_size(v) ((v).size)
#define kv_max(v) ((v).capacity)
-#define kv_last(v) kv_A(v, kv_size(v) - 1)
+#define kv_Z(v, i) kv_A(v, kv_size(v) - (i) - 1)
+#define kv_last(v) kv_Z(v, 0)
+
+/// Drop last n items from kvec without resizing
+///
+/// Previously spelled as `(void)kv_pop(v)`, repeated n times.
+///
+/// @param[out] v Kvec to drop items from.
+/// @param[in] n Number of elements to drop.
+#define kv_drop(v, n) ((v).size -= (n))
#define kv_resize(v, s) \
((v).capacity = (s), \
diff --git a/src/nvim/lib/ringbuf.h b/src/nvim/lib/ringbuf.h
index 12b75ec65a..e63eae70b0 100644
--- a/src/nvim/lib/ringbuf.h
+++ b/src/nvim/lib/ringbuf.h
@@ -15,6 +15,7 @@
#ifndef NVIM_LIB_RINGBUF_H
#define NVIM_LIB_RINGBUF_H
+#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
@@ -73,6 +74,32 @@ typedef struct { \
RBType *buf_end; \
} TypeName##RingBuffer;
+/// Dummy item free macros, for use in RINGBUF_INIT
+///
+/// This macros actually does nothing.
+///
+/// @param[in] item Item to be freed.
+#define RINGBUF_DUMMY_FREE(item)
+
+/// Static ring buffer
+///
+/// @warning Ring buffers created with this macros must neither be freed nor
+/// deallocated.
+///
+/// @param scope Ring buffer scope.
+/// @param TypeName Ring buffer type name.
+/// @param RBType Type of the single ring buffer element.
+/// @param varname Variable name.
+/// @param rbsize Ring buffer size.
+#define RINGBUF_STATIC(scope, TypeName, RBType, varname, rbsize) \
+static RBType _##varname##_buf[rbsize]; \
+scope TypeName##RingBuffer varname = { \
+ .buf = _##varname##_buf, \
+ .next = _##varname##_buf, \
+ .first = NULL, \
+ .buf_end = _##varname##_buf + rbsize - 1, \
+};
+
/// Initialize a new ring buffer
///
/// @param TypeName Ring buffer type name. Actual type name will be
diff --git a/src/nvim/log.c b/src/nvim/log.c
index f1dbe61dda..7bfe5c4089 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -25,6 +25,10 @@ static uv_mutex_t mutex;
# include "log.c.generated.h"
#endif
+#ifdef HAVE_EXECINFO_BACKTRACE
+# include <execinfo.h>
+#endif
+
static bool log_try_create(char *fname)
{
if (fname == NULL || fname[0] == '\0') {
@@ -91,8 +95,12 @@ void log_unlock(void)
uv_mutex_unlock(&mutex);
}
-bool do_log(int log_level, const char *func_name, int line_num, bool eol,
- const char* fmt, ...) FUNC_ATTR_UNUSED
+/// @param context description of a shared context or subsystem
+/// @param func_name function name, or NULL
+/// @param line_num source line number, or -1
+bool do_log(int log_level, const char *context, const char *func_name,
+ int line_num, bool eol, const char *fmt, ...)
+ FUNC_ATTR_UNUSED
{
if (log_level < MIN_LOG_LEVEL) {
return false;
@@ -108,8 +116,8 @@ bool do_log(int log_level, const char *func_name, int line_num, bool eol,
va_list args;
va_start(args, fmt);
- ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol,
- fmt, args);
+ ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num,
+ eol, fmt, args);
va_end(args);
if (log_file != stderr && log_file != stdout) {
@@ -147,7 +155,7 @@ FILE *open_log_file(void)
static bool opening_log_file = false;
// check if it's a recursive call
if (opening_log_file) {
- do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true,
+ do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Cannot LOG() recursively.");
return stderr;
}
@@ -167,33 +175,87 @@ FILE *open_log_file(void)
// - LOG() is called before early_init()
// - Directory does not exist
// - File is not writable
- do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true,
+ do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Logging to stderr, failed to open $" LOG_FILE_ENV ": %s",
log_file_path);
return stderr;
}
-static bool do_log_to_file(FILE *log_file, int log_level,
+#ifdef HAVE_EXECINFO_BACKTRACE
+void log_callstack_to_file(FILE *log_file, 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.
+
+ 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 ...
+
+ do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true,
+ "trace:");
+ 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);
+ }
+}
+
+void log_callstack(const char *const func_name, const int line_num)
+{
+ log_lock();
+ FILE *log_file = open_log_file();
+ if (log_file == NULL) {
+ goto end;
+ }
+
+ log_callstack_to_file(log_file, func_name, line_num);
+
+end:
+ log_unlock();
+}
+#endif
+
+static bool do_log_to_file(FILE *log_file, int log_level, const char *context,
const char *func_name, int line_num, bool eol,
const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
- bool ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol,
- fmt, args);
+ bool ret = v_do_log_to_file(log_file, log_level, context, func_name,
+ line_num, eol, fmt, args);
va_end(args);
return ret;
}
static bool v_do_log_to_file(FILE *log_file, int log_level,
- const char *func_name, int line_num, bool eol,
- const char* fmt, va_list args)
+ const char *context, const char *func_name,
+ int line_num, bool eol, const char *fmt,
+ va_list args)
{
static const char *log_levels[] = {
[DEBUG_LOG_LEVEL] = "DEBUG",
[INFO_LOG_LEVEL] = "INFO ",
- [WARNING_LOG_LEVEL] = "WARN ",
+ [WARN_LOG_LEVEL] = "WARN ",
[ERROR_LOG_LEVEL] = "ERROR",
};
assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL);
@@ -211,8 +273,15 @@ static bool v_do_log_to_file(FILE *log_file, int log_level,
// print the log message prefixed by the current timestamp and pid
int64_t pid = os_get_pid();
- if (fprintf(log_file, "%s %s %" PRId64 "/%s:%d: ", date_time,
- log_levels[log_level], pid, func_name, line_num) < 0) {
+ int rv = (line_num == -1 || func_name == NULL)
+ ? fprintf(log_file, "%s %s %" PRId64 " %s", date_time,
+ log_levels[log_level], pid,
+ (context == NULL ? "?:" : context))
+ : fprintf(log_file, "%s %s %" PRId64 " %s%s:%d: ", date_time,
+ log_levels[log_level], pid,
+ (context == NULL ? "" : context),
+ func_name, line_num);
+ if (rv < 0) {
return false;
}
if (vfprintf(log_file, fmt, args) < 0) {
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 221f0bbaf6..f378b92039 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -6,7 +6,7 @@
#define DEBUG_LOG_LEVEL 0
#define INFO_LOG_LEVEL 1
-#define WARNING_LOG_LEVEL 2
+#define WARN_LOG_LEVEL 2
#define ERROR_LOG_LEVEL 3
#define DLOG(...)
@@ -22,45 +22,50 @@
# define MIN_LOG_LEVEL INFO_LOG_LEVEL
#endif
-#define LOG(level, ...) do_log((level), __func__, __LINE__, true, \
+#define LOG(level, ...) do_log((level), NULL, __func__, __LINE__, true, \
__VA_ARGS__)
#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
# undef DLOG
# undef DLOGN
-# define DLOG(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, true, \
+# define DLOG(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__)
-# define DLOGN(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, false, \
+# define DLOGN(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__)
#endif
#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
# undef ILOG
# undef ILOGN
-# define ILOG(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, true, \
+# define ILOG(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__)
-# define ILOGN(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, false, \
+# define ILOGN(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= WARNING_LOG_LEVEL
+#if MIN_LOG_LEVEL <= WARN_LOG_LEVEL
# undef WLOG
# undef WLOGN
-# define WLOG(...) do_log(WARNING_LOG_LEVEL, __func__, __LINE__, true, \
+# define WLOG(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__)
-# define WLOGN(...) do_log(WARNING_LOG_LEVEL, __func__, __LINE__, false, \
+# define WLOGN(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__)
#endif
#if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL
# undef ELOG
# undef ELOGN
-# define ELOG(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, true, \
+# define ELOG(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__)
-# define ELOGN(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, false, \
+# define ELOGN(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__)
#endif
+#ifdef HAVE_EXECINFO_BACKTRACE
+# define LOG_CALLSTACK() log_callstack(__func__, __LINE__)
+# define LOG_CALLSTACK_TO_FILE(fp) log_callstack_to_file(fp, __func__, __LINE__)
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "log.h.generated.h"
#endif
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index cacba3ce87..5da6d2c0a0 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -211,20 +211,28 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
size_t len;
const char *s = lua_tolstring(lstate, -2, &len);
if (cur.special) {
- list_T *const kv_pair = tv_list_alloc();
- tv_list_append_list(cur.tv->vval.v_list, kv_pair);
- listitem_T *const key = tv_list_item_alloc();
- key->li_tv = decode_string(s, len, kTrue, false, false);
- tv_list_append(kv_pair, key);
- if (key->li_tv.v_type == VAR_UNKNOWN) {
+ list_T *const kv_pair = tv_list_alloc(2);
+
+ typval_T s_tv = decode_string(s, len, kTrue, false, false);
+ if (s_tv.v_type == VAR_UNKNOWN) {
ret = false;
tv_list_unref(kv_pair);
continue;
}
- listitem_T *const val = tv_list_item_alloc();
- tv_list_append(kv_pair, val);
+ tv_list_append_owned_tv(kv_pair, s_tv);
+
+ // Value: not populated yet, need to create list item to push.
+ tv_list_append_owned_tv(kv_pair, (typval_T) {
+ .v_type = VAR_UNKNOWN,
+ });
kv_push(stack, cur);
- cur = (TVPopStackItem) { &val->li_tv, false, false, 0 };
+ tv_list_append_list(cur.tv->vval.v_list, kv_pair);
+ cur = (TVPopStackItem) {
+ .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)),
+ .container = false,
+ .special = false,
+ .idx = 0,
+ };
} else {
dictitem_T *const di = tv_dict_item_alloc_len(s, len);
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
@@ -239,15 +247,23 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
}
} else {
assert(cur.tv->v_type == VAR_LIST);
- lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1);
+ lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1);
if (lua_isnil(lstate, -1)) {
lua_pop(lstate, 2);
continue;
}
- listitem_T *const li = tv_list_item_alloc();
- tv_list_append(cur.tv->vval.v_list, li);
+ // Not populated yet, need to create list item to push.
+ tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) {
+ .v_type = VAR_UNKNOWN,
+ });
kv_push(stack, cur);
- cur = (TVPopStackItem) { &li->li_tv, false, false, 0 };
+ // TODO(ZyX-I): Use indexes, here list item *will* be reallocated.
+ cur = (TVPopStackItem) {
+ .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)),
+ .container = false,
+ .special = false,
+ .idx = 0,
+ };
}
}
assert(!cur.container);
@@ -305,8 +321,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
switch (table_props.type) {
case kObjectTypeArray: {
cur.tv->v_type = VAR_LIST;
- cur.tv->vval.v_list = tv_list_alloc();
- cur.tv->vval.v_list->lv_refcount++;
+ cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx);
+ tv_list_ref(cur.tv->vval.v_list);
if (table_props.maxidx != 0) {
cur.container = true;
cur.idx = lua_gettop(lstate);
@@ -322,7 +338,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
} else {
cur.special = table_props.has_string_with_nul;
if (table_props.has_string_with_nul) {
- decode_create_map_special_dict(cur.tv);
+ decode_create_map_special_dict(
+ cur.tv, (ptrdiff_t)table_props.string_keys_num);
assert(cur.tv->v_type == VAR_DICT);
dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict,
S_LEN("_VAL"));
@@ -406,7 +423,7 @@ nlua_pop_typval_table_processing_end:
#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
- TYPVAL_ENCODE_CONV_NIL()
+ TYPVAL_ENCODE_CONV_NIL(tv)
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 9ec5bfb8ad..02eabb9c89 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -40,66 +40,6 @@ typedef struct {
/// Name of the run code for use in messages
#define NLUA_EVAL_NAME "<VimL compiled string>"
-/// Call C function which does not expect any arguments
-///
-/// @param function Called function
-/// @param numret Number of returned arguments
-#define NLUA_CALL_C_FUNCTION_0(lstate, function, numret) \
- do { \
- lua_pushcfunction(lstate, &function); \
- lua_call(lstate, 0, numret); \
- } while (0)
-/// Call C function which expects one argument
-///
-/// @param function Called function
-/// @param numret Number of returned arguments
-/// @param a… Supplied argument (should be a void* pointer)
-#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \
- do { \
- lua_pushcfunction(lstate, &function); \
- lua_pushlightuserdata(lstate, a1); \
- lua_call(lstate, 1, numret); \
- } while (0)
-/// Call C function which expects two arguments
-///
-/// @param function Called function
-/// @param numret Number of returned arguments
-/// @param a… Supplied argument (should be a void* pointer)
-#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \
- do { \
- lua_pushcfunction(lstate, &function); \
- lua_pushlightuserdata(lstate, a1); \
- lua_pushlightuserdata(lstate, a2); \
- lua_call(lstate, 2, numret); \
- } while (0)
-/// Call C function which expects three arguments
-///
-/// @param function Called function
-/// @param numret Number of returned arguments
-/// @param a… Supplied argument (should be a void* pointer)
-#define NLUA_CALL_C_FUNCTION_3(lstate, function, numret, a1, a2, a3) \
- do { \
- lua_pushcfunction(lstate, &function); \
- lua_pushlightuserdata(lstate, a1); \
- lua_pushlightuserdata(lstate, a2); \
- lua_pushlightuserdata(lstate, a3); \
- lua_call(lstate, 3, numret); \
- } while (0)
-/// Call C function which expects five arguments
-///
-/// @param function Called function
-/// @param numret Number of returned arguments
-/// @param a… Supplied argument (should be a void* pointer)
-#define NLUA_CALL_C_FUNCTION_4(lstate, function, numret, a1, a2, a3, a4) \
- do { \
- lua_pushcfunction(lstate, &function); \
- lua_pushlightuserdata(lstate, a1); \
- lua_pushlightuserdata(lstate, a2); \
- lua_pushlightuserdata(lstate, a3); \
- lua_pushlightuserdata(lstate, a4); \
- lua_call(lstate, 4, numret); \
- } while (0)
-
/// Convert lua error into a Vim error message
///
/// @param lstate Lua interpreter state.
@@ -124,140 +64,50 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
/// omitted.
static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
- const char *s1 = luaL_checklstring(lstate, 1, NULL);
- const char *s2 = luaL_checklstring(lstate, 2, NULL);
- const int ret = STRICMP(s1, s2);
+ size_t s1_len;
+ size_t s2_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ const char *s2 = luaL_checklstring(lstate, 2, &s2_len);
+ char *nul1;
+ char *nul2;
+ int ret = 0;
+ assert(s1[s1_len] == NUL);
+ assert(s2[s2_len] == NUL);
+ do {
+ nul1 = memchr(s1, NUL, s1_len);
+ nul2 = memchr(s2, NUL, s2_len);
+ ret = STRICMP(s1, s2);
+ if (ret == 0) {
+ // Compare "a\0" greater then "a".
+ if ((nul1 == NULL) != (nul2 == NULL)) {
+ ret = ((nul1 != NULL) - (nul2 != NULL));
+ break;
+ }
+ if (nul1 != NULL) {
+ assert(nul2 != NULL);
+ // Can't shift both strings by the same amount of bytes: lowercase
+ // letter may have different byte-length than uppercase.
+ s1_len -= (size_t)(nul1 - s1) + 1;
+ s2_len -= (size_t)(nul2 - s2) + 1;
+ s1 = nul1 + 1;
+ s2 = nul2 + 1;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (true);
lua_pop(lstate, 2);
lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0)));
return 1;
}
-/// Evaluate lua string
-///
-/// Expects two values on the stack: string to evaluate, pointer to the
-/// location where result is saved. Always returns nothing (from the lua point
-/// of view).
-static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
-{
- const String *const str = (const String *)lua_touserdata(lstate, 1);
- typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2);
- lua_pop(lstate, 2);
-
- if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) {
- nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s"));
- return 0;
- }
- if (lua_pcall(lstate, 0, 1, 0)) {
- nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s"));
- return 0;
- }
- if (!nlua_pop_typval(lstate, ret_tv)) {
- return 0;
- }
- return 0;
-}
-
-/// Evaluate lua string for each line in range
-///
-/// Expects two values on the stack: string to evaluate and pointer to integer
-/// array with line range. Always returns nothing (from the lua point of view).
-static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
-{
- const String *const str = (const String *)lua_touserdata(lstate, 1);
- const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2);
- lua_pop(lstate, 2);
-
-#define DOSTART "return function(line, linenr) "
-#define DOEND " end"
- const size_t lcmd_len = (str->size
- + (sizeof(DOSTART) - 1)
- + (sizeof(DOEND) - 1));
- char *lcmd;
- if (lcmd_len < IOSIZE) {
- lcmd = (char *)IObuff;
- } else {
- lcmd = xmalloc(lcmd_len + 1);
- }
- memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1);
- memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size);
- memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, DOEND, sizeof(DOEND) - 1);
-#undef DOSTART
-#undef DOEND
-
- if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
- nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s"));
- if (lcmd_len >= IOSIZE) {
- xfree(lcmd);
- }
- return 0;
- }
- if (lcmd_len >= IOSIZE) {
- xfree(lcmd);
- }
- if (lua_pcall(lstate, 0, 1, 0)) {
- nlua_error(lstate, _("E5110: Error while creating lua function: %.*s"));
- return 0;
- }
- for (linenr_T l = range[0]; l <= range[1]; l++) {
- if (l > curbuf->b_ml.ml_line_count) {
- break;
- }
- lua_pushvalue(lstate, -1);
- lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false));
- lua_pushnumber(lstate, (lua_Number)l);
- if (lua_pcall(lstate, 2, 1, 0)) {
- nlua_error(lstate, _("E5111: Error while calling lua function: %.*s"));
- break;
- }
- if (lua_isstring(lstate, -1)) {
- size_t new_line_len;
- const char *const new_line = lua_tolstring(lstate, -1, &new_line_len);
- char *const new_line_transformed = xmemdupz(new_line, new_line_len);
- for (size_t i = 0; i < new_line_len; i++) {
- if (new_line_transformed[i] == NUL) {
- new_line_transformed[i] = '\n';
- }
- }
- ml_replace(l, (char_u *)new_line_transformed, false);
- changed_bytes(l, 0);
- }
- lua_pop(lstate, 1);
- }
- lua_pop(lstate, 1);
- check_cursor();
- update_screen(NOT_VALID);
- return 0;
-}
-
-/// Evaluate lua file
-///
-/// Expects one value on the stack: file to evaluate. Always returns nothing
-/// (from the lua point of view).
-static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
-{
- const char *const filename = (const char *)lua_touserdata(lstate, 1);
- lua_pop(lstate, 1);
-
- if (luaL_loadfile(lstate, filename)) {
- nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s"));
- return 0;
- }
- if (lua_pcall(lstate, 0, 0, 0)) {
- nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s"));
- return 0;
- }
- return 0;
-}
-
/// Initialize lua interpreter state
///
/// Called by lua interpreter itself to initialize state.
static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
- // stricmp
- lua_pushcfunction(lstate, &nlua_stricmp);
- lua_setglobal(lstate, "stricmp");
-
// print
lua_pushcfunction(lstate, &nlua_print);
lua_setglobal(lstate, "print");
@@ -277,13 +127,17 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
nlua_add_api_functions(lstate);
// vim.types, vim.type_idx, vim.val_idx
nlua_init_types(lstate);
+ // stricmp
+ lua_pushcfunction(lstate, &nlua_stricmp);
+ lua_setfield(lstate, -2, "stricmp");
+
lua_setglobal(lstate, "vim");
return 0;
}
/// Initialize lua interpreter
///
-/// Crashes NeoVim if initialization fails. Should be called once per lua
+/// Crashes Nvim if initialization fails. Should be called once per lua
/// interpreter instance.
///
/// @return New lua interpreter instance.
@@ -296,7 +150,7 @@ static lua_State *nlua_init(void)
preserve_exit();
}
luaL_openlibs(lstate);
- NLUA_CALL_C_FUNCTION_0(lstate, nlua_state_init, 0);
+ nlua_state_init(lstate);
return lstate;
}
@@ -305,8 +159,8 @@ static lua_State *nlua_init(void)
/// Calls nlua_init() if needed. Is responsible for pre-lua call initalization
/// like updating `package.[c]path` with directories derived from &runtimepath.
///
-/// @return Interprter instance to use. Will either be initialized now or taken
-/// from previous initalization.
+/// @return Interpreter instance to use. Will either be initialized now or
+/// taken from previous initialization.
static lua_State *nlua_enter(void)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -347,108 +201,18 @@ static lua_State *nlua_enter(void)
void executor_exec_lua(const String str, typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
- NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_lua_string, 0,
- (void *)&str, ret_tv);
-}
-
-/// Evaluate lua string
-///
-/// Used for luaeval(). Expects three values on the stack:
-///
-/// 1. String to evaluate.
-/// 2. _A value.
-/// 3. Pointer to location where result is saved.
-///
-/// @param[in,out] lstate Lua interpreter state.
-static int nlua_eval_lua_string(lua_State *const lstate)
- FUNC_ATTR_NONNULL_ALL
-{
- const String *const str = (const String *)lua_touserdata(lstate, 1);
- typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2);
- typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3);
- lua_pop(lstate, 3);
+ lua_State *const lstate = nlua_enter();
- garray_T str_ga;
- ga_init(&str_ga, 1, 80);
-#define EVALHEADER "local _A=select(1,...) return ("
- const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1;
- char *lcmd;
- if (lcmd_len < IOSIZE) {
- lcmd = (char *)IObuff;
- } else {
- lcmd = xmalloc(lcmd_len);
- }
- memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1);
- memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size);
- lcmd[lcmd_len - 1] = ')';
-#undef EVALHEADER
- if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
- nlua_error(lstate,
- _("E5107: Error while creating lua chunk for luaeval(): %.*s"));
- if (lcmd != (char *)IObuff) {
- xfree(lcmd);
- }
- return 0;
- }
- if (lcmd != (char *)IObuff) {
- xfree(lcmd);
- }
-
- if (arg == NULL || arg->v_type == VAR_UNKNOWN) {
- lua_pushnil(lstate);
- } else {
- nlua_push_typval(lstate, arg);
- }
- if (lua_pcall(lstate, 1, 1, 0)) {
- nlua_error(lstate,
- _("E5108: Error while calling lua chunk for luaeval(): %.*s"));
- return 0;
- }
- if (!nlua_pop_typval(lstate, ret_tv)) {
- return 0;
- }
-
- return 0;
-}
-
-/// Evaluate lua string
-///
-/// Expects four values on the stack: string to evaluate, pointer to args array,
-/// and locations where result and error are saved, respectively. Always
-/// returns nothing (from the lua point of view).
-static int nlua_exec_lua_string_api(lua_State *const lstate)
- FUNC_ATTR_NONNULL_ALL
-{
- const String *str = (const String *)lua_touserdata(lstate, 1);
- const Array *args = (const Array *)lua_touserdata(lstate, 2);
- Object *retval = (Object *)lua_touserdata(lstate, 3);
- Error *err = (Error *)lua_touserdata(lstate, 4);
-
- lua_pop(lstate, 4);
-
- if (luaL_loadbuffer(lstate, str->data, str->size, "<nvim>")) {
- size_t len;
- const char *str = lua_tolstring(lstate, -1, &len);
- api_set_error(err, kErrorTypeValidation,
- "Error loading lua: %.*s", (int)len, str);
- return 0;
- }
-
- for (size_t i = 0; i < args->size; i++) {
- nlua_push_Object(lstate, args->items[i]);
+ if (luaL_loadbuffer(lstate, str.data, str.size, NLUA_EVAL_NAME)) {
+ nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s"));
+ return;
}
-
- if (lua_pcall(lstate, (int)args->size, 1, 0)) {
- size_t len;
- const char *str = lua_tolstring(lstate, -1, &len);
- api_set_error(err, kErrorTypeException,
- "Error executing lua: %.*s", (int)len, str);
- return 0;
+ if (lua_pcall(lstate, 0, 1, 0)) {
+ nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s"));
+ return;
}
- *retval = nlua_pop_Object(lstate, err);
-
- return 0;
+ nlua_pop_typval(lstate, ret_tv);
}
/// Print as a Vim message
@@ -519,7 +283,7 @@ static int nlua_print(lua_State *const lstate)
}
msg((char_u *)str + start);
}
- if (str[len - 1] == NUL) { // Last was newline
+ if (len && str[len - 1] == NUL) { // Last was newline
msg((char_u *)"");
}
}
@@ -586,8 +350,46 @@ void executor_eval_lua(const String str, typval_T *const arg,
typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
- NLUA_CALL_C_FUNCTION_3(nlua_enter(), nlua_eval_lua_string, 0,
- (void *)&str, arg, ret_tv);
+ lua_State *const lstate = nlua_enter();
+
+ garray_T str_ga;
+ ga_init(&str_ga, 1, 80);
+#define EVALHEADER "local _A=select(1,...) return ("
+ const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str.size + 1;
+ char *lcmd;
+ if (lcmd_len < IOSIZE) {
+ lcmd = (char *)IObuff;
+ } else {
+ lcmd = xmalloc(lcmd_len);
+ }
+ memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1);
+ memcpy(lcmd + sizeof(EVALHEADER) - 1, str.data, str.size);
+ lcmd[lcmd_len - 1] = ')';
+#undef EVALHEADER
+ if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
+ nlua_error(lstate,
+ _("E5107: Error while creating lua chunk for luaeval(): %.*s"));
+ if (lcmd != (char *)IObuff) {
+ xfree(lcmd);
+ }
+ return;
+ }
+ if (lcmd != (char *)IObuff) {
+ xfree(lcmd);
+ }
+
+ if (arg->v_type == VAR_UNKNOWN) {
+ lua_pushnil(lstate);
+ } else {
+ nlua_push_typval(lstate, arg);
+ }
+ if (lua_pcall(lstate, 1, 1, 0)) {
+ nlua_error(lstate,
+ _("E5108: Error while calling lua chunk for luaeval(): %.*s"));
+ return;
+ }
+
+ nlua_pop_typval(lstate, ret_tv);
}
/// Execute lua string
@@ -601,10 +403,29 @@ void executor_eval_lua(const String str, typval_T *const arg,
/// @return Return value of the execution.
Object executor_exec_lua_api(const String str, const Array args, Error *err)
{
- Object retval = NIL;
- NLUA_CALL_C_FUNCTION_4(nlua_enter(), nlua_exec_lua_string_api, 0,
- (void *)&str, (void *)&args, &retval, err);
- return retval;
+ lua_State *const lstate = nlua_enter();
+
+ if (luaL_loadbuffer(lstate, str.data, str.size, "<nvim>")) {
+ size_t len;
+ const char *errstr = lua_tolstring(lstate, -1, &len);
+ api_set_error(err, kErrorTypeValidation,
+ "Error loading lua: %.*s", (int)len, errstr);
+ return NIL;
+ }
+
+ for (size_t i = 0; i < args.size; i++) {
+ nlua_push_Object(lstate, args.items[i]);
+ }
+
+ if (lua_pcall(lstate, (int)args.size, 1, 0)) {
+ size_t len;
+ const char *errstr = lua_tolstring(lstate, -1, &len);
+ api_set_error(err, kErrorTypeException,
+ "Error executing lua: %.*s", (int)len, errstr);
+ return NIL;
+ }
+
+ return nlua_pop_Object(lstate, err);
}
@@ -640,13 +461,70 @@ void ex_luado(exarg_T *const eap)
EMSG(_("cannot save undo information"));
return;
}
- const String cmd = {
- .size = STRLEN(eap->arg),
- .data = (char *)eap->arg,
- };
- const linenr_T range[] = { eap->line1, eap->line2 };
- NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_luado_string, 0,
- (void *)&cmd, (void *)range);
+ const char *const cmd = (const char *)eap->arg;
+ const size_t cmd_len = strlen(cmd);
+
+ lua_State *const lstate = nlua_enter();
+
+#define DOSTART "return function(line, linenr) "
+#define DOEND " end"
+ const size_t lcmd_len = (cmd_len
+ + (sizeof(DOSTART) - 1)
+ + (sizeof(DOEND) - 1));
+ char *lcmd;
+ if (lcmd_len < IOSIZE) {
+ lcmd = (char *)IObuff;
+ } else {
+ lcmd = xmalloc(lcmd_len + 1);
+ }
+ memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1);
+ memcpy(lcmd + sizeof(DOSTART) - 1, cmd, cmd_len);
+ memcpy(lcmd + sizeof(DOSTART) - 1 + cmd_len, DOEND, sizeof(DOEND) - 1);
+#undef DOSTART
+#undef DOEND
+
+ if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
+ nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s"));
+ if (lcmd_len >= IOSIZE) {
+ xfree(lcmd);
+ }
+ return;
+ }
+ if (lcmd_len >= IOSIZE) {
+ xfree(lcmd);
+ }
+ if (lua_pcall(lstate, 0, 1, 0)) {
+ nlua_error(lstate, _("E5110: Error while creating lua function: %.*s"));
+ return;
+ }
+ for (linenr_T l = eap->line1; l <= eap->line2; l++) {
+ if (l > curbuf->b_ml.ml_line_count) {
+ break;
+ }
+ lua_pushvalue(lstate, -1);
+ lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false));
+ lua_pushnumber(lstate, (lua_Number)l);
+ if (lua_pcall(lstate, 2, 1, 0)) {
+ nlua_error(lstate, _("E5111: Error while calling lua function: %.*s"));
+ break;
+ }
+ if (lua_isstring(lstate, -1)) {
+ size_t new_line_len;
+ const char *const new_line = lua_tolstring(lstate, -1, &new_line_len);
+ char *const new_line_transformed = xmemdupz(new_line, new_line_len);
+ for (size_t i = 0; i < new_line_len; i++) {
+ if (new_line_transformed[i] == NUL) {
+ new_line_transformed[i] = '\n';
+ }
+ }
+ ml_replace(l, (char_u *)new_line_transformed, false);
+ changed_bytes(l, 0);
+ }
+ lua_pop(lstate, 1);
+ }
+ lua_pop(lstate, 1);
+ check_cursor();
+ update_screen(NOT_VALID);
}
/// Run lua file
@@ -657,6 +535,15 @@ void ex_luado(exarg_T *const eap)
void ex_luafile(exarg_T *const eap)
FUNC_ATTR_NONNULL_ALL
{
- NLUA_CALL_C_FUNCTION_1(nlua_enter(), nlua_exec_lua_file, 0,
- (void *)eap->arg);
+ lua_State *const lstate = nlua_enter();
+
+ if (luaL_loadfile(lstate, (const char *)eap->arg)) {
+ nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s"));
+ return;
+ }
+
+ if (lua_pcall(lstate, 0, 0, 0)) {
+ nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s"));
+ return;
+ }
}
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index c7952520b0..e1bbb03d3f 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -1,3 +1,62 @@
+-- Internal-only until comments in #8107 are addressed.
+-- Returns:
+-- {errcode}, {output}
+local function _system(cmd)
+ local out = vim.api.nvim_call_function('system', { cmd })
+ local err = vim.api.nvim_get_vvar('shell_error')
+ return err, out
+end
+
+-- Gets process info from the `ps` command.
+-- Used by nvim_get_proc() as a fallback.
+local function _os_proc_info(pid)
+ if pid == nil or pid <= 0 or type(pid) ~= 'number' then
+ error('invalid pid')
+ end
+ local cmd = { 'ps', '-p', pid, '-o', 'ucomm=', }
+ local err, name = _system(cmd)
+ if 1 == err and string.gsub(name, '%s*', '') == '' then
+ return {} -- Process not found.
+ elseif 0 ~= err then
+ local args_str = vim.api.nvim_call_function('string', { cmd })
+ error('command failed: '..args_str)
+ end
+ local _, ppid = _system({ 'ps', '-p', pid, '-o', 'ppid=', })
+ -- Remove trailing whitespace.
+ name = string.gsub(name, '%s+$', '')
+ ppid = string.gsub(ppid, '%s+$', '')
+ ppid = tonumber(ppid) == nil and -1 or tonumber(ppid)
+ return {
+ name = name,
+ pid = pid,
+ ppid = ppid,
+ }
+end
+
+-- Gets process children from the `pgrep` command.
+-- Used by nvim_get_proc_children() as a fallback.
+local function _os_proc_children(ppid)
+ if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then
+ error('invalid ppid')
+ end
+ local cmd = { 'pgrep', '-P', ppid, }
+ local err, rv = _system(cmd)
+ if 1 == err and string.gsub(rv, '%s*', '') == '' then
+ return {} -- Process not found.
+ elseif 0 ~= err then
+ local args_str = vim.api.nvim_call_function('string', { cmd })
+ error('command failed: '..args_str)
+ end
+ local children = {}
+ for s in string.gmatch(rv, '%S+') do
+ local i = tonumber(s)
+ if i ~= nil then
+ table.insert(children, i)
+ end
+ end
+ return children
+end
+
-- TODO(ZyX-I): Create compatibility layer.
--{{{1 package.path updater function
-- Last inserted paths. Used to clear out items from package.[c]path when they
@@ -58,7 +117,12 @@ local function _update_package_paths()
end
last_nvim_paths = cur_nvim_paths
end
---{{{1 Module definition
-return {
+
+local module = {
_update_package_paths = _update_package_paths,
+ _os_proc_children = _os_proc_children,
+ _os_proc_info = _os_proc_info,
+ _system = _system,
}
+
+return module
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 26d4f74b6a..4e01265498 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -28,15 +28,11 @@
/// @return `s, sizeof(s) - 1`
#define S_LEN(s) (s), (sizeof(s) - 1)
-/*
- * lineempty() - return TRUE if the line is empty
- */
-#define lineempty(p) (*ml_get(p) == NUL)
+/// LINEEMPTY() - return TRUE if the line is empty
+#define LINEEMPTY(p) (*ml_get(p) == NUL)
-/*
- * bufempty() - return TRUE if the current buffer is empty
- */
-#define bufempty() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \
+/// BUFEMPTY() - return TRUE if the current buffer is empty
+#define BUFEMPTY() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \
NUL)
/*
@@ -75,7 +71,7 @@
do { \
if (*p_langmap \
&& (condition) \
- && (p_lrm || KeyTyped) \
+ && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \
&& !KeyStuffed \
&& (c) >= 0) \
{ \
@@ -148,7 +144,8 @@
/// zero in those cases (-Wdiv-by-zero in GCC).
#define ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0])))))
-#define RGB(r, g, b) ((r << 16) | (g << 8) | b)
+// Duplicated in os/win_defs.h to avoid include-order sensitivity.
+#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
#define STR_(x) #x
#define STR(x) STR_(x)
@@ -183,4 +180,19 @@
/// @return ((Type *)obj).
#define STRUCT_CAST(Type, obj) ((Type *)(obj))
+// Type of uv_buf_t.len is platform-dependent.
+// Related: https://github.com/libuv/libuv/pull/1236
+#if defined(WIN32)
+# define UV_BUF_LEN(x) (ULONG)(x)
+#else
+# define UV_BUF_LEN(x) (x)
+#endif
+
+// Type of read()/write() `count` param is platform-dependent.
+#if defined(WIN32)
+# define IO_COUNT(x) (unsigned)(x)
+#else
+# define IO_COUNT(x) (x)
+#endif
+
#endif // NVIM_MACROS_H
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 19a661d7db..4288d7f9d7 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -57,6 +57,7 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+#include "nvim/os/fileio.h"
#include "nvim/event/loop.h"
#include "nvim/os/signal.h"
#include "nvim/event/process.h"
@@ -67,6 +68,9 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/handle.h"
#include "nvim/api/private/dispatch.h"
+#ifndef WIN32
+# include "nvim/os/pty_process_unix.h"
+#endif
/* Maximum number of commands from + or -c arguments. */
#define MAX_ARG_CMDS 10
@@ -97,10 +101,7 @@ typedef struct {
bool input_isatty; // stdin is a terminal
bool output_isatty; // stdout is a terminal
bool err_isatty; // stderr is a terminal
- bool headless; // Dont try to start an user interface
- // or read/write to stdio(unless
- // embedding)
- int no_swap_file; /* "-n" argument used */
+ int no_swap_file; // "-n" argument used
int use_debug_break_level;
int window_count; /* number of windows to use */
int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */
@@ -216,10 +217,22 @@ void early_init(void)
#ifdef MAKE_LIB
int nvim_main(int argc, char **argv)
+#elif defined(WIN32)
+int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
#else
int main(int argc, char **argv)
#endif
{
+#if defined(WIN32) && !defined(MAKE_LIB)
+ char **argv = xmalloc((size_t)argc * sizeof(char *));
+ for (int i = 0; i < argc; i++) {
+ char *buf = NULL;
+ utf16_to_utf8(argv_w[i], &buf);
+ assert(buf);
+ argv[i] = buf;
+ }
+#endif
+
argv0 = argv[0];
char_u *fname = NULL; // file name from command line
@@ -282,8 +295,8 @@ int main(int argc, char **argv)
assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
cmdline_row = (int)(Rows - p_ch);
msg_row = cmdline_row;
- screenalloc(false); /* allocate screen buffers */
- set_init_2(params.headless);
+ screenalloc(false); // allocate screen buffers
+ set_init_2(headless_mode);
TIME_MSG("inits 2");
msg_scroll = TRUE;
@@ -295,8 +308,9 @@ int main(int argc, char **argv)
/* Set the break level after the terminal is initialized. */
debug_break_level = params.use_debug_break_level;
- bool reading_input = !params.headless && (params.input_isatty
- || params.output_isatty || params.err_isatty);
+ bool reading_input = !headless_mode
+ && (params.input_isatty || params.output_isatty
+ || params.err_isatty);
if (reading_input) {
// One of the startup commands (arguments, sourced scripts or plugins) may
@@ -393,7 +407,7 @@ int main(int argc, char **argv)
}
// It's better to make v:oldfiles an empty list than NULL.
if (get_vim_var_list(VV_OLDFILES) == NULL) {
- set_vim_var_list(VV_OLDFILES, tv_list_alloc());
+ set_vim_var_list(VV_OLDFILES, tv_list_alloc(0));
}
/*
@@ -432,7 +446,7 @@ int main(int argc, char **argv)
wait_return(TRUE);
}
- if (!params.headless) {
+ if (!headless_mode) {
// Stop reading from input stream, the UI layer will take over now.
input_stop();
ui_builtin_start();
@@ -552,11 +566,15 @@ int main(int argc, char **argv)
*/
normal_enter(false, false);
+#if defined(WIN32) && !defined(MAKE_LIB)
+ xfree(argv);
+#endif
return 0;
}
-/* Exit properly */
+/// Exit properly
void getout(int exitval)
+ FUNC_ATTR_NORETURN
{
tabpage_T *tp, *next_tp;
@@ -633,6 +651,11 @@ void getout(int exitval)
/* Position the cursor again, the autocommands may have moved it */
ui_cursor_goto((int)Rows - 1, 0);
+ // Apply 'titleold'.
+ if (p_title && *p_titleold != NUL) {
+ ui_call_set_title(cstr_as_string((char *)p_titleold));
+ }
+
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
iconv_end();
#endif
@@ -766,23 +789,36 @@ static void command_line_scan(mparm_T *parmp)
version();
mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
- msgpack_sbuffer* b = msgpack_sbuffer_new();
- msgpack_packer* p = msgpack_packer_new(b, msgpack_sbuffer_write);
- Object md = DICTIONARY_OBJ(api_metadata());
- msgpack_rpc_from_object(md, p);
+ FileDescriptor fp;
+ const int fof_ret = file_open_fd(&fp, STDOUT_FILENO, true);
+ msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write);
+
+ if (fof_ret != 0) {
+ emsgf(_("E5421: Failed to open stdin: %s"), os_strerror(fof_ret));
+ }
- for (size_t i = 0; i < b->size; i++) {
- putchar(b->data[i]);
+ if (p == NULL) {
+ emsgf(_(e_outofmem));
}
+ Object md = DICTIONARY_OBJ(api_metadata());
+ msgpack_rpc_from_object(md, p);
+
msgpack_packer_free(p);
+ const int ff_ret = file_flush(&fp);
+ if (ff_ret < 0) {
+ msgpack_file_write_error(ff_ret);
+ }
mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "headless") == 0) {
- parmp->headless = true;
+ headless_mode = true;
} else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
embedded_mode = true;
- parmp->headless = true;
- channel_from_stdio();
+ headless_mode = true;
+ const char *err;
+ if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
+ abort();
+ }
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
#if !defined(UNIX)
parmp->literal = TRUE;
@@ -921,10 +957,11 @@ static void command_line_scan(mparm_T *parmp)
break;
case 's':
- if (exmode_active) /* "-s" silent (batch) mode */
- silent_mode = TRUE;
- else /* "-s {scriptin}" read from script file */
- want_argument = TRUE;
+ if (exmode_active) { // "-es" silent (batch) mode
+ silent_mode = true;
+ } else { // "-s {scriptin}" read from script file
+ want_argument = true;
+ }
break;
case 't': /* "-t {tag}" or "-t{tag}" jump to tag */
@@ -1184,7 +1221,6 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
memset(paramp, 0, sizeof(*paramp));
paramp->argc = argc;
paramp->argv = argv;
- paramp->headless = false;
paramp->want_full_screen = true;
paramp->use_debug_break_level = -1;
paramp->window_count = -1;
@@ -1208,9 +1244,19 @@ static void init_startuptime(mparm_T *paramp)
static void check_and_set_isatty(mparm_T *paramp)
{
- paramp->input_isatty = os_isatty(fileno(stdin));
- paramp->output_isatty = os_isatty(fileno(stdout));
+ stdin_isatty
+ = paramp->input_isatty = os_isatty(fileno(stdin));
+ stdout_isatty
+ = paramp->output_isatty = os_isatty(fileno(stdout));
paramp->err_isatty = os_isatty(fileno(stderr));
+#ifndef WIN32
+ int tty_fd = paramp->input_isatty
+ ? STDIN_FILENO
+ : (paramp->output_isatty
+ ? STDOUT_FILENO
+ : (paramp->err_isatty ? STDERR_FILENO : -1));
+ pty_process_save_termios(tty_fd);
+#endif
TIME_MSG("window checked");
}
@@ -1281,10 +1327,29 @@ static void set_window_layout(mparm_T *paramp)
static void load_plugins(void)
{
if (p_lpl) {
- source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); // NOLINT
+ char_u *rtp_copy = NULL;
+
+ // First add all package directories to 'runtimepath', so that their
+ // autoload directories can be found. Only if not done already with a
+ // :packloadall command.
+ // Make a copy of 'runtimepath', so that source_runtime does not use the
+ // pack directories.
+ if (!did_source_packages) {
+ rtp_copy = vim_strsave(p_rtp);
+ add_pack_start_dirs();
+ }
+
+ source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy,
+ (char_u *)"plugin/**/*.vim", // NOLINT
+ DIP_ALL | DIP_NOAFTER);
TIME_MSG("loading plugins");
+ xfree(rtp_copy);
- ex_packloadall(NULL);
+ // Only source "start" packages if not done already with a :packloadall
+ // command.
+ if (!did_source_packages) {
+ load_start_packages();
+ }
TIME_MSG("loading packages");
source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER);
@@ -1303,7 +1368,7 @@ static void handle_quickfix(mparm_T *paramp)
set_string_option_direct((char_u *)"ef", -1,
paramp->use_ef, OPT_FREE, SID_CARG);
vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef);
- if (qf_init(NULL, p_ef, p_efm, true, IObuff) < 0) {
+ if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
ui_linefeed();
mch_exit(3);
}
@@ -1334,7 +1399,7 @@ static void handle_tag(char_u *tagname)
// When starting in Ex mode and commands come from a file, set Silent mode.
static void check_tty(mparm_T *parmp)
{
- if (parmp->headless) {
+ if (headless_mode) {
return;
}
@@ -1815,6 +1880,7 @@ static int process_env(char *env, bool is_viminit)
/// os_fileinfo_link() respectively for extra security.
static bool file_owned(const char *fname)
{
+ assert(fname != NULL);
uid_t uid = getuid();
FileInfo file_info;
bool file_owned = os_fileinfo(fname, &file_info)
@@ -1867,54 +1933,47 @@ static void usage(void)
signal_stop(); // kill us with CTRL-C here, if you like
mch_msg(_("Usage:\n"));
- mch_msg(_(" nvim [arguments] [file ...] Edit specified file(s)\n"));
- mch_msg(_(" nvim [arguments] - Read text from stdin\n"));
- mch_msg(_(" nvim [arguments] -t <tag> Edit file where tag is defined\n"));
- mch_msg(_(" nvim [arguments] -q [errorfile] Edit file with first error\n"));
- mch_msg(_("\nArguments:\n"));
+ mch_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
+ mch_msg(_(" nvim [options] - Read text from stdin\n"));
+ mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
+ mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
+ mch_msg(_("\nOptions:\n"));
mch_msg(_(" -- Only file names after this\n"));
-#if !defined(UNIX)
- mch_msg(_(" --literal Don't expand wildcards\n"));
-#endif
- mch_msg(_(" -e Ex mode\n"));
- mch_msg(_(" -E Improved Ex mode\n"));
- mch_msg(_(" -s Silent (batch) mode (only for ex mode)\n"));
+ mch_msg(_(" + Start at end of file\n"));
+ mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n"));
+ mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"));
+ mch_msg("\n");
+ mch_msg(_(" -b Binary mode\n"));
mch_msg(_(" -d Diff mode\n"));
- mch_msg(_(" -R Read-only mode\n"));
- mch_msg(_(" -Z Restricted mode\n"));
+ mch_msg(_(" -e, -E Ex mode, Improved Ex mode\n"));
+ mch_msg(_(" -es Silent (batch) mode\n"));
+ mch_msg(_(" -h, --help Print this help message\n"));
+ mch_msg(_(" -i <shada> Use this shada file\n"));
mch_msg(_(" -m Modifications (writing files) not allowed\n"));
mch_msg(_(" -M Modifications in text not allowed\n"));
- mch_msg(_(" -b Binary mode\n"));
- mch_msg(_(" -l Lisp mode\n"));
- mch_msg(_(" -A Arabic mode\n"));
- mch_msg(_(" -F Farsi mode\n"));
- mch_msg(_(" -H Hebrew mode\n"));
- mch_msg(_(" -V[N][file] Be verbose [level N][log messages to file]\n"));
- mch_msg(_(" -D Debugging mode\n"));
mch_msg(_(" -n No swap file, use memory only\n"));
- mch_msg(_(" -r, -L List swap files and exit\n"));
- mch_msg(_(" -r <file> Recover crashed session\n"));
- mch_msg(_(" -u <vimrc> Use <vimrc> instead of the default\n"));
- mch_msg(_(" -i <shada> Use <shada> instead of the default\n"));
- mch_msg(_(" --noplugin Don't load plugin scripts\n"));
- mch_msg(_(" -o[N] Open N windows (default: one for each file)\n"));
- mch_msg(_(" -O[N] Like -o but split vertically\n"));
- mch_msg(_(" -p[N] Open N tab pages (default: one for each file)\n"));
- mch_msg(_(" + Start at end of file\n"));
- mch_msg(_(" +<linenum> Start at line <linenum>\n"));
- mch_msg(_(" +/<pattern> Start at first occurrence of <pattern>\n"));
- mch_msg(_(" --cmd <command> Execute <command> before loading any vimrc\n"));
- mch_msg(_(" -c <command> Execute <command> after loading the first file\n"));
+ mch_msg(_(" -o[N] Open N windows (default: one per file)\n"));
+ mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n"));
+ mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n"));
+ mch_msg(_(" -r, -L List swap files\n"));
+ mch_msg(_(" -r <file> Recover edit state for this file\n"));
+ mch_msg(_(" -R Read-only mode\n"));
mch_msg(_(" -S <session> Source <session> after loading the first file\n"));
mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
- mch_msg(_(" -w <scriptout> Append all typed characters to <scriptout>\n"));
- mch_msg(_(" -W <scriptout> Write all typed characters to <scriptout>\n"));
- mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
- mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n"));
+ mch_msg(_(" -u <config> Use this config file\n"));
+ mch_msg(_(" -v, --version Print version information\n"));
+ mch_msg(_(" -V[N][file] Verbose [level][file]\n"));
+ mch_msg(_(" -Z Restricted mode\n"));
+ mch_msg("\n");
+ mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
- mch_msg(_(" -v, --version Print version information and exit\n"));
- mch_msg(_(" -h, --help Print this help message and exit\n"));
+#if !defined(UNIX)
+ mch_msg(_(" --literal Don't expand wildcards\n"));
+#endif
+ mch_msg(_(" --noplugin Don't load plugins\n"));
+ mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
+ mch_msg(_("\nSee \":help startup-options\" for all options.\n"));
}
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 7889fabd45..3cd26a5bf7 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -171,6 +171,10 @@ void setpcmark(void)
curwin->w_prev_pcmark = curwin->w_pcmark;
curwin->w_pcmark = curwin->w_cursor;
+ if (curwin->w_pcmark.lnum == 0) {
+ curwin->w_pcmark.lnum = 1;
+ }
+
/* If jumplist is full: remove oldest entry */
if (++curwin->w_jumplistlen > JUMPLISTSIZE) {
curwin->w_jumplistlen = JUMPLISTSIZE;
@@ -951,11 +955,17 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
- /* quickfix marks */
- qf_mark_adjust(NULL, line1, line2, amount, amount_after);
- /* location lists */
+ // quickfix marks
+ if (!qf_mark_adjust(NULL, line1, line2, amount, amount_after)) {
+ curbuf->b_has_qf_entry &= ~BUF_HAS_QF_ENTRY;
+ }
+ // location lists
+ bool found_one = false;
FOR_ALL_TAB_WINDOWS(tab, win) {
- qf_mark_adjust(win, line1, line2, amount, amount_after);
+ found_one |= qf_mark_adjust(win, line1, line2, amount, amount_after);
+ }
+ if (!found_one) {
+ curbuf->b_has_qf_entry &= ~BUF_HAS_LL_ENTRY;
}
sign_mark_adjust(line1, line2, amount, amount_after);
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index d5907da2ed..008bce6df6 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -72,19 +72,49 @@ struct interval {
# include "unicode_tables.generated.h"
#endif
-/*
- * Like utf8len_tab above, but using a zero for illegal lead bytes.
- */
-static uint8_t utf8len_tab_zero[256] =
-{
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0,
+// To speed up BYTELEN(); keep a lookup table to quickly get the length in
+// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes
+// which are illegal when used as the first byte have a 1. The NUL byte has
+// length 1.
+const uint8_t utf8len_tab[] = {
+ // ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B?
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C?
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D?
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E?
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, // F?
+};
+
+// Like utf8len_tab above, but using a zero for illegal lead bytes.
+const uint8_t utf8len_tab_zero[] = {
+ // ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7?
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8?
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9?
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A?
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B?
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C?
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D?
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E?
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // F?
};
/*
@@ -528,45 +558,52 @@ int utf_off2cells(unsigned off, unsigned max_off)
return (off + 1 < max_off && ScreenLines[off + 1] == 0) ? 2 : 1;
}
-/*
- * Convert a UTF-8 byte sequence to a wide character.
- * If the sequence is illegal or truncated by a NUL the first byte is
- * returned.
- * Does not include composing characters, of course.
- */
-int utf_ptr2char(const char_u *p)
+/// Convert a UTF-8 byte sequence to a wide character
+///
+/// If the sequence is illegal or truncated by a NUL then the first byte is
+/// returned. Does not include composing characters for obvious reasons.
+///
+/// @param[in] p String to convert.
+///
+/// @return Unicode codepoint or byte value.
+int utf_ptr2char(const char_u *const p)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- uint8_t len;
-
- if (p[0] < 0x80) /* be quick for ASCII */
+ if (p[0] < 0x80) { // Be quick for ASCII.
return p[0];
+ }
- len = utf8len_tab_zero[p[0]];
+ const uint8_t len = utf8len_tab_zero[p[0]];
if (len > 1 && (p[1] & 0xc0) == 0x80) {
- if (len == 2)
+ if (len == 2) {
return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
+ }
if ((p[2] & 0xc0) == 0x80) {
- if (len == 3)
- return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
- + (p[2] & 0x3f);
+ if (len == 3) {
+ return (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
+ + (p[2] & 0x3f));
+ }
if ((p[3] & 0xc0) == 0x80) {
- if (len == 4)
- return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
- + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
+ if (len == 4) {
+ return (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
+ + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f));
+ }
if ((p[4] & 0xc0) == 0x80) {
- if (len == 5)
- return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
- + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
- + (p[4] & 0x3f);
- if ((p[5] & 0xc0) == 0x80 && len == 6)
- return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
- + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
- + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
+ if (len == 5) {
+ return (((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
+ + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
+ + (p[4] & 0x3f));
+ }
+ if ((p[5] & 0xc0) == 0x80 && len == 6) {
+ return (((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
+ + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
+ + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f));
+ }
}
}
}
}
- /* Illegal value, just return the first byte */
+ // Illegal value: just return the first byte.
return p[0];
}
@@ -767,23 +804,24 @@ int utfc_char2bytes(int off, char_u *buf)
return len;
}
-/*
- * Get the length of a UTF-8 byte sequence, not including any following
- * composing characters.
- * Returns 0 for "".
- * Returns 1 for an illegal byte sequence.
- */
-int utf_ptr2len(const char_u *p)
+/// Get the length of a UTF-8 byte sequence representing a single codepoint
+///
+/// @param[in] p UTF-8 string.
+///
+/// @return Sequence length, 0 for empty string and 1 for non-UTF-8 byte
+/// sequence.
+int utf_ptr2len(const char_u *const p)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- int len;
- int i;
-
- if (*p == NUL)
+ if (*p == NUL) {
return 0;
- len = utf8len_tab[*p];
- for (i = 1; i < len; ++i)
- if ((p[i] & 0xc0) != 0x80)
+ }
+ const int len = utf8len_tab[*p];
+ for (int i = 1; i < len; i++) {
+ if ((p[i] & 0xc0) != 0x80) {
return 1;
+ }
+ }
return len;
}
@@ -824,38 +862,38 @@ int utf_ptr2len_len(const char_u *p, int size)
return len;
}
-/*
- * Return the number of bytes the UTF-8 encoding of the character at "p" takes.
- * This includes following composing characters.
- */
-int utfc_ptr2len(const char_u *p)
+/// Return the number of bytes occupied by a UTF-8 character in a string
+///
+/// This includes following composing characters.
+int utfc_ptr2len(const char_u *const p)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- int len;
- int b0 = *p;
- int prevlen;
+ uint8_t b0 = (uint8_t)(*p);
- if (b0 == NUL)
+ if (b0 == NUL) {
return 0;
- if (b0 < 0x80 && p[1] < 0x80) /* be quick for ASCII */
+ }
+ if (b0 < 0x80 && p[1] < 0x80) { // be quick for ASCII
return 1;
+ }
- /* Skip over first UTF-8 char, stopping at a NUL byte. */
- len = utf_ptr2len(p);
+ // Skip over first UTF-8 char, stopping at a NUL byte.
+ int len = utf_ptr2len(p);
- /* Check for illegal byte. */
- if (len == 1 && b0 >= 0x80)
+ // Check for illegal byte.
+ if (len == 1 && b0 >= 0x80) {
return 1;
+ }
- /*
- * Check for composing characters. We can handle only the first six, but
- * skip all of them (otherwise the cursor would get stuck).
- */
- prevlen = 0;
- for (;; ) {
- if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len))
+ // Check for composing characters. We can handle only the first six, but
+ // skip all of them (otherwise the cursor would get stuck).
+ int prevlen = 0;
+ for (;;) {
+ if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len)) {
return len;
+ }
- /* Skip over composing char */
+ // Skip over composing char.
prevlen = len;
len += utf_ptr2len(p + len);
}
@@ -913,70 +951,65 @@ int utfc_ptr2len_len(const char_u *p, int size)
return len;
}
-/*
- * Return the number of bytes the UTF-8 encoding of character "c" takes.
- * This does not include composing characters.
- */
-int utf_char2len(int c)
+/// Determine how many bytes certain unicode codepoint will occupy
+int utf_char2len(const int c)
{
- if (c < 0x80)
+ if (c < 0x80) {
return 1;
- if (c < 0x800)
+ } else if (c < 0x800) {
return 2;
- if (c < 0x10000)
+ } else if (c < 0x10000) {
return 3;
- if (c < 0x200000)
+ } else if (c < 0x200000) {
return 4;
- if (c < 0x4000000)
+ } else if (c < 0x4000000) {
return 5;
- return 6;
+ } else {
+ return 6;
+ }
}
-/*
- * Convert Unicode character "c" to UTF-8 string in "buf[]".
- * Returns the number of bytes.
- * This does not include composing characters.
- */
-int utf_char2bytes(int c, char_u *buf)
+/// Convert Unicode character to UTF-8 string
+///
+/// @param c character to convert to \p buf
+/// @param[out] buf UTF-8 string generated from \p c, does not add \0
+/// @return Number of bytes (1-6). Does not include composing characters.
+int utf_char2bytes(const int c, char_u *const buf)
{
- if (c < 0x80) { /* 7 bits */
+ if (c < 0x80) { // 7 bits
buf[0] = c;
return 1;
- }
- if (c < 0x800) { /* 11 bits */
+ } else if (c < 0x800) { // 11 bits
buf[0] = 0xc0 + ((unsigned)c >> 6);
buf[1] = 0x80 + (c & 0x3f);
return 2;
- }
- if (c < 0x10000) { /* 16 bits */
+ } else if (c < 0x10000) { // 16 bits
buf[0] = 0xe0 + ((unsigned)c >> 12);
buf[1] = 0x80 + (((unsigned)c >> 6) & 0x3f);
buf[2] = 0x80 + (c & 0x3f);
return 3;
- }
- if (c < 0x200000) { /* 21 bits */
+ } else if (c < 0x200000) { // 21 bits
buf[0] = 0xf0 + ((unsigned)c >> 18);
buf[1] = 0x80 + (((unsigned)c >> 12) & 0x3f);
buf[2] = 0x80 + (((unsigned)c >> 6) & 0x3f);
buf[3] = 0x80 + (c & 0x3f);
return 4;
- }
- if (c < 0x4000000) { /* 26 bits */
+ } else if (c < 0x4000000) { // 26 bits
buf[0] = 0xf8 + ((unsigned)c >> 24);
buf[1] = 0x80 + (((unsigned)c >> 18) & 0x3f);
buf[2] = 0x80 + (((unsigned)c >> 12) & 0x3f);
buf[3] = 0x80 + (((unsigned)c >> 6) & 0x3f);
buf[4] = 0x80 + (c & 0x3f);
return 5;
+ } else { // 31 bits
+ buf[0] = 0xfc + ((unsigned)c >> 30);
+ buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f);
+ buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f);
+ buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f);
+ buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f);
+ buf[5] = 0x80 + (c & 0x3f);
+ return 6;
}
- /* 31 bits */
- buf[0] = 0xfc + ((unsigned)c >> 30);
- buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f);
- buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f);
- buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f);
- buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f);
- buf[5] = 0x80 + (c & 0x3f);
- return 6;
}
/*
@@ -1513,14 +1546,15 @@ int utf_head_off(const char_u *base, const char_u *p)
return (int)(p - q);
}
-/*
- * Copy a character from "*fp" to "*tp" and advance the pointers.
- */
-void mb_copy_char(const char_u **fp, char_u **tp)
+/// Copy a character, advancing the pointers
+///
+/// @param[in,out] fp Source of the character to copy.
+/// @param[in,out] tp Destination to copy to.
+void mb_copy_char(const char_u **const fp, char_u **const tp)
{
- int l = (*mb_ptr2len)(*fp);
+ const size_t l = (size_t)utfc_ptr2len(*fp);
- memmove(*tp, *fp, (size_t)l);
+ memmove(*tp, *fp, l);
*tp += l;
*fp += l;
}
@@ -1739,52 +1773,55 @@ int mb_charlen_len(char_u *str, int len)
return count;
}
-/*
- * Try to un-escape a multi-byte character.
- * Used for the "to" and "from" part of a mapping.
- * Return the un-escaped string if it is a multi-byte character, and advance
- * "pp" to just after the bytes that formed it.
- * Return NULL if no multi-byte char was found.
- */
-char_u * mb_unescape(char_u **pp)
-{
- static char_u buf[6];
- int n;
- int m = 0;
- char_u *str = *pp;
-
- /* Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI
- * KS_EXTRA KE_CSI to CSI.
- * Maximum length of a utf-8 character is 4 bytes. */
- for (n = 0; str[n] != NUL && m < 4; ++n) {
- if (str[n] == K_SPECIAL
- && str[n + 1] == KS_SPECIAL
- && str[n + 2] == KE_FILLER) {
- buf[m++] = K_SPECIAL;
- n += 2;
- } else if ((str[n] == K_SPECIAL
- )
- && str[n + 1] == KS_EXTRA
- && str[n + 2] == (int)KE_CSI) {
- buf[m++] = CSI;
- n += 2;
- } else if (str[n] == K_SPECIAL
- )
- break; /* a special key can't be a multibyte char */
- else
- buf[m++] = str[n];
- buf[m] = NUL;
+/// Try to unescape a multibyte character
+///
+/// Used for the rhs and lhs of the mappings.
+///
+/// @param[in,out] pp String to unescape. Is advanced to just after the bytes
+/// that form a multibyte character.
+///
+/// @return Unescaped string if it is a multibyte character, NULL if no
+/// multibyte character was found. Returns a static buffer, always one
+/// and the same.
+const char *mb_unescape(const char **const pp)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ static char buf[6];
+ size_t buf_idx = 0;
+ uint8_t *str = (uint8_t *)(*pp);
+
+ // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI
+ // KS_EXTRA KE_CSI to CSI.
+ // Maximum length of a utf-8 character is 4 bytes.
+ for (size_t str_idx = 0; str[str_idx] != NUL && buf_idx < 4; str_idx++) {
+ if (str[str_idx] == K_SPECIAL
+ && str[str_idx + 1] == KS_SPECIAL
+ && str[str_idx + 2] == KE_FILLER) {
+ buf[buf_idx++] = (char)K_SPECIAL;
+ str_idx += 2;
+ } else if ((str[str_idx] == K_SPECIAL)
+ && str[str_idx + 1] == KS_EXTRA
+ && str[str_idx + 2] == KE_CSI) {
+ buf[buf_idx++] = (char)CSI;
+ str_idx += 2;
+ } else if (str[str_idx] == K_SPECIAL) {
+ break; // A special key can't be a multibyte char.
+ } else {
+ buf[buf_idx++] = (char)str[str_idx];
+ }
+ buf[buf_idx] = NUL;
- /* Return a multi-byte character if it's found. An illegal sequence
- * will result in a 1 here. */
- if ((*mb_ptr2len)(buf) > 1) {
- *pp = str + n + 1;
+ // Return a multi-byte character if it's found. An illegal sequence
+ // will result in a 1 here.
+ if (utf_ptr2len((const char_u *)buf) > 1) {
+ *pp = (const char *)str + str_idx + 1;
return buf;
}
- /* Bail out quickly for ASCII. */
- if (buf[0] < 128)
+ // Bail out quickly for ASCII.
+ if ((uint8_t)buf[0] < 128) {
break;
+ }
}
return NULL;
}
@@ -2259,9 +2296,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8,
if (vcp->vc_type == CONV_ICONV && vcp->vc_fd != (iconv_t)-1)
iconv_close(vcp->vc_fd);
# endif
- vcp->vc_type = CONV_NONE;
- vcp->vc_factor = 1;
- vcp->vc_fail = false;
+ *vcp = (vimconv_T)MBYTE_NONE_CONV;
/* No conversion when one of the names is empty or they are equal. */
if (from == NULL || *from == NUL || to == NULL || *to == NUL
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index ad9e38004c..a5ce1b0a15 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -1,6 +1,7 @@
#ifndef NVIM_MBYTE_H
#define NVIM_MBYTE_H
+#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -18,6 +19,9 @@
#define MB_BYTE2LEN(b) utf8len_tab[b]
#define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b])
+// max length of an unicode char
+#define MB_MAXCHAR 6
+
/* properties used in enc_canon_table[] (first three mutually exclusive) */
#define ENC_8BIT 0x01
#define ENC_DBCS 0x02
@@ -56,6 +60,12 @@ typedef enum {
CONV_ICONV = 5,
} ConvFlags;
+#define MBYTE_NONE_CONV { \
+ .vc_type = CONV_NONE, \
+ .vc_factor = 1, \
+ .vc_fail = false, \
+}
+
/// Structure used for string conversions
typedef struct {
int vc_type; ///< Zero or more ConvFlags.
@@ -67,6 +77,10 @@ typedef struct {
///< otherwise use '?'.
} vimconv_T;
+extern const uint8_t utf8len_tab_zero[256];
+
+extern const uint8_t utf8len_tab[256];
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mbyte.h.generated.h"
#endif
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 9429703620..4eeba12b87 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -376,8 +376,9 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile)
{
unsigned flags = hp->bh_flags;
- if ((flags & BH_LOCKED) == 0)
- EMSG(_("E293: block was not locked"));
+ if ((flags & BH_LOCKED) == 0) {
+ IEMSG(_("E293: block was not locked"));
+ }
flags &= ~BH_LOCKED;
if (dirty) {
flags |= BH_DIRTY;
@@ -895,6 +896,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
{
// fname cannot be NameBuff, because it must have been allocated.
mf_set_fnames(mfp, fname);
+ assert(mfp->mf_fname != NULL);
/// Extra security check: When creating a swap file it really shouldn't
/// exist yet. If there is a symbolic link, this is most likely an attack.
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 55e7e01825..c11ca74f5c 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -293,7 +293,7 @@ int ml_open(buf_T *buf)
*/
hp = mf_new(mfp, false, 1);
if (hp->bh_bnum != 0) {
- EMSG(_("E298: Didn't get block nr 0?"));
+ IEMSG(_("E298: Didn't get block nr 0?"));
goto error;
}
b0p = hp->bh_data;
@@ -335,7 +335,7 @@ int ml_open(buf_T *buf)
if ((hp = ml_new_ptr(mfp)) == NULL)
goto error;
if (hp->bh_bnum != 1) {
- EMSG(_("E298: Didn't get block nr 1?"));
+ IEMSG(_("E298: Didn't get block nr 1?"));
goto error;
}
pp = hp->bh_data;
@@ -351,7 +351,7 @@ int ml_open(buf_T *buf)
*/
hp = ml_new_data(mfp, FALSE, 1);
if (hp->bh_bnum != 2) {
- EMSG(_("E298: Didn't get block nr 2?"));
+ IEMSG(_("E298: Didn't get block nr 2?"));
goto error;
}
@@ -635,13 +635,14 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what)
if (mfp == NULL || (hp = mf_get(mfp, 0, 1)) == NULL)
return;
b0p = hp->bh_data;
- if (ml_check_b0_id(b0p) == FAIL)
- EMSG(_("E304: ml_upd_block0(): Didn't get block 0??"));
- else {
- if (what == UB_FNAME)
+ if (ml_check_b0_id(b0p) == FAIL) {
+ IEMSG(_("E304: ml_upd_block0(): Didn't get block 0??"));
+ } else {
+ if (what == UB_FNAME) {
set_b0_fname(b0p, buf);
- else /* what == UB_SAME_DIR */
+ } else { // what == UB_SAME_DIR
set_b0_dir_flag(b0p, buf);
+ }
}
mf_put(mfp, hp, true, false);
}
@@ -1329,10 +1330,14 @@ recover_names (
names[2] = (char_u *)concat_fnames((char *)dir_name, ".sw?", TRUE);
num_names = 3;
} else {
- p = dir_name + STRLEN(dir_name);
- if (after_pathsep((char *)dir_name, (char *)p) && p[-1] == p[-2]) {
- /* Ends with '//', Use Full path for swap name */
- tail = (char_u *)make_percent_swname((char *)dir_name, (char *)fname_res);
+ int len = (int)STRLEN(dir_name);
+ p = dir_name + len;
+ if (after_pathsep((char *)dir_name, (char *)p)
+ && len > 1
+ && p[-1] == p[-2]) {
+ // Ends with '//', Use Full path for swap name
+ tail = (char_u *)make_percent_swname((char *)dir_name,
+ (char *)fname_res);
} else {
tail = path_tail(fname_res);
tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, TRUE);
@@ -1460,6 +1465,7 @@ static int process_still_running;
*/
static time_t swapfile_info(char_u *fname)
{
+ assert(fname != NULL);
int fd;
struct block0 b0;
time_t x = (time_t)0;
@@ -1741,11 +1747,11 @@ ml_get_buf (
if (lnum > buf->b_ml.ml_line_count) { /* invalid line number */
if (recursive == 0) {
- /* Avoid giving this message for a recursive call, may happen when
- * the GUI redraws part of the text. */
- ++recursive;
- EMSGN(_("E315: ml_get: invalid lnum: %" PRId64), lnum);
- --recursive;
+ // Avoid giving this message for a recursive call, may happen when
+ // the GUI redraws part of the text.
+ recursive++;
+ IEMSGN(_("E315: ml_get: invalid lnum: %" PRId64), lnum);
+ recursive--;
}
errorret:
STRCPY(IObuff, "???");
@@ -1773,11 +1779,11 @@ errorret:
*/
if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) {
if (recursive == 0) {
- /* Avoid giving this message for a recursive call, may happen
- * when the GUI redraws part of the text. */
- ++recursive;
- EMSGN(_("E316: ml_get: cannot find line %" PRId64), lnum);
- --recursive;
+ // Avoid giving this message for a recursive call, may happen
+ // when the GUI redraws part of the text.
+ recursive++;
+ IEMSGN(_("E316: ml_get: cannot find line %" PRId64), lnum);
+ recursive--;
}
goto errorret;
}
@@ -2161,7 +2167,7 @@ ml_append_int (
return FAIL;
pp = hp->bh_data; /* must be pointer block */
if (pp->pb_id != PTR_ID) {
- EMSG(_("E317: pointer block id wrong 3"));
+ IEMSG(_("E317: pointer block id wrong 3"));
mf_put(mfp, hp, false, false);
return FAIL;
}
@@ -2294,8 +2300,8 @@ ml_append_int (
* Safety check: fallen out of for loop?
*/
if (stack_idx < 0) {
- EMSG(_("E318: Updated too many blocks?"));
- buf->b_ml.ml_stack_top = 0; /* invalidate stack */
+ IEMSG(_("E318: Updated too many blocks?"));
+ buf->b_ml.ml_stack_top = 0; // invalidate stack
}
}
@@ -2434,7 +2440,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, int message)
return FAIL;
pp = hp->bh_data; /* must be pointer block */
if (pp->pb_id != PTR_ID) {
- EMSG(_("E317: pointer block id wrong 4"));
+ IEMSG(_("E317: pointer block id wrong 4"));
mf_put(mfp, hp, false, false);
return FAIL;
}
@@ -2629,9 +2635,9 @@ static void ml_flush_line(buf_T *buf)
new_line = buf->b_ml.ml_line_ptr;
hp = ml_find_line(buf, lnum, ML_FIND);
- if (hp == NULL)
- EMSGN(_("E320: Cannot find line %" PRId64), lnum);
- else {
+ if (hp == NULL) {
+ IEMSGN(_("E320: Cannot find line %" PRId64), lnum);
+ } else {
dp = hp->bh_data;
idx = lnum - buf->b_ml.ml_locked_low;
start = ((dp->db_index[idx]) & DB_INDEX_MASK);
@@ -2840,7 +2846,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
pp = (PTR_BL *)(dp); /* must be pointer block */
if (pp->pb_id != PTR_ID) {
- EMSG(_("E317: pointer block id wrong"));
+ IEMSG(_("E317: pointer block id wrong"));
goto error_block;
}
@@ -2877,13 +2883,14 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
break;
}
}
- if (idx >= (int)pp->pb_count) { /* past the end: something wrong! */
- if (lnum > buf->b_ml.ml_line_count)
- EMSGN(_("E322: line number out of range: %" PRId64 " past the end"),
- lnum - buf->b_ml.ml_line_count);
+ if (idx >= (int)pp->pb_count) { // past the end: something wrong!
+ if (lnum > buf->b_ml.ml_line_count) {
+ IEMSGN(_("E322: line number out of range: %" PRId64 " past the end"),
+ lnum - buf->b_ml.ml_line_count);
- else
- EMSGN(_("E323: line count wrong in block %" PRId64), bnum);
+ } else {
+ IEMSGN(_("E323: line count wrong in block %" PRId64), bnum);
+ }
goto error_block;
}
if (action == ML_DELETE) {
@@ -2959,7 +2966,7 @@ static void ml_lineadd(buf_T *buf, int count)
pp = hp->bh_data; /* must be pointer block */
if (pp->pb_id != PTR_ID) {
mf_put(mfp, hp, false, false);
- EMSG(_("E317: pointer block id wrong 2"));
+ IEMSG(_("E317: pointer block id wrong 2"));
break;
}
pp->pb_pointer[ip->ip_index].pe_line_count += count;
@@ -3013,20 +3020,17 @@ int resolve_symlink(const char_u *fname, char_u *buf)
}
buf[ret] = NUL;
- /*
- * Check whether the symlink is relative or absolute.
- * If it's relative, build a new path based on the directory
- * portion of the filename (if any) and the path the symlink
- * points to.
- */
- if (path_is_absolute_path(buf))
+ // Check whether the symlink is relative or absolute.
+ // If it's relative, build a new path based on the directory
+ // portion of the filename (if any) and the path the symlink
+ // points to.
+ if (path_is_absolute(buf)) {
STRCPY(tmp, buf);
- else {
- char_u *tail;
-
- tail = path_tail(tmp);
- if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
+ } else {
+ char_u *tail = path_tail(tmp);
+ if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) {
return FAIL;
+ }
STRCPY(tail, buf);
}
}
@@ -3051,9 +3055,12 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name
#ifdef HAVE_READLINK
char_u fname_buf[MAXPATHL];
#endif
+ int len = (int)STRLEN(dir_name);
- s = dir_name + STRLEN(dir_name);
- if (after_pathsep((char *)dir_name, (char *)s) && s[-1] == s[-2]) { /* Ends with '//', Use Full path */
+ s = dir_name + len;
+ if (after_pathsep((char *)dir_name, (char *)s)
+ && len > 1
+ && s[-1] == s[-2]) { // Ends with '//', Use Full path
r = NULL;
if ((s = (char_u *)make_percent_swname((char *)dir_name, (char *)fname)) != NULL) {
r = (char_u *)modname((char *)s, ".swp", FALSE);
@@ -3135,6 +3142,7 @@ attention_message (
char_u *fname /* swap file name */
)
{
+ assert(buf->b_fname != NULL);
time_t x, sx;
char *p;
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 74c58fb203..a66ab6a3cc 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -559,6 +559,7 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
#include "nvim/tag.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/eval/typval.h"
/*
* Free everything that we allocated.
@@ -585,7 +586,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. */
@@ -692,6 +693,7 @@ void free_all_mem(void)
free_screenlines();
clear_hl_tables();
+ list_free_log();
}
#endif
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 7e9e9e9e5c..42417f75d5 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -26,7 +26,7 @@
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
-
+#include "nvim/eval/typval.h"
#define MENUDEPTH 10 /* maximum depth of menus */
@@ -38,8 +38,8 @@
-/* The character for each menu mode */
-static char_u menu_mode_chars[] = {'n', 'v', 's', 'o', 'i', 'c', 't'};
+/// The character for each menu mode
+static char_u menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' };
static char_u e_notsubmenu[] = N_(
"E327: Part of menu-item path is not sub-menu");
@@ -47,20 +47,16 @@ static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
-/*
- * Do the :menu command and relatives.
- */
-void
-ex_menu (
- exarg_T *eap /* Ex command arguments */
-)
+/// Do the :menu command and relatives.
+/// @param eap Ex command arguments
+void
+ex_menu(exarg_T *eap)
{
char_u *menu_path;
int modes;
- char_u *map_to;
+ char_u *map_to; // command mapped to the menu entry
int noremap;
bool silent = false;
- bool special = false;
int unmenu;
char_u *map_buf;
char_u *arg;
@@ -86,7 +82,7 @@ ex_menu (
continue;
}
if (STRNCMP(arg, "<special>", 9) == 0) {
- special = true;
+ // Ignore obsolete "<special>" modifier.
arg = skipwhite(arg + 9);
continue;
}
@@ -94,7 +90,8 @@ ex_menu (
}
- /* Locate an optional "icon=filename" argument. */
+ // Locate an optional "icon=filename" argument
+ // TODO(nvim): Currently this is only parsed. Should expose it to UIs.
if (STRNCMP(arg, "icon=", 5) == 0) {
arg += 5;
while (*arg != NUL && *arg != ' ') {
@@ -108,12 +105,12 @@ ex_menu (
}
}
- /*
- * Fill in the priority table.
- */
- for (p = arg; *p; ++p)
- if (!ascii_isdigit(*p) && *p != '.')
+ // Fill in the priority table.
+ for (p = arg; *p; p++) {
+ if (!ascii_isdigit(*p) && *p != '.') {
break;
+ }
+ }
if (ascii_iswhite(*p)) {
for (i = 0; i < MENUDEPTH && !ascii_iswhite(*arg); ++i) {
pri_tab[i] = getdigits_long(&arg);
@@ -222,13 +219,12 @@ ex_menu (
map_buf = NULL; // Menu tips are plain text.
} else {
map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true,
- special, CPO_TO_CPO_FLAGS);
+ true, CPO_TO_CPO_FLAGS);
}
menuarg.modes = modes;
menuarg.noremap[0] = noremap;
menuarg.silent[0] = silent;
- add_menu_path(menu_path, &menuarg, pri_tab, map_to
- );
+ add_menu_path(menu_path, &menuarg, pri_tab, map_to);
/*
* For the PopUp menu, add a menu for each mode separately.
@@ -253,16 +249,18 @@ theend:
;
}
-/*
- * Add the menu with the given name to the menu hierarchy
- */
-static int
-add_menu_path (
- char_u *menu_path,
- vimmenu_T *menuarg, /* passes modes, iconfile, iconidx,
- icon_builtin, silent[0], noremap[0] */
- long *pri_tab,
- char_u *call_data
+
+/// Add the menu with the given name to the menu hierarchy
+///
+/// @param[out] menuarg menu entry
+/// @param[] pri_tab priority table
+/// @param[in] call_data Right hand side command
+static int
+add_menu_path(
+ const char_u *const menu_path,
+ vimmenu_T *menuarg,
+ const long *const pri_tab,
+ const char_u *const call_data
)
{
char_u *path_name;
@@ -297,8 +295,9 @@ add_menu_path (
if (map_to != NULL) {
en_name = name;
name = map_to;
- } else
+ } else {
en_name = NULL;
+ }
dname = menu_text(name, NULL, NULL);
if (*dname == NUL) {
/* Only a mnemonic or accelerator is not valid. */
@@ -312,14 +311,15 @@ add_menu_path (
while (menu != NULL) {
if (menu_name_equal(name, menu) || menu_name_equal(dname, menu)) {
if (*next_name == NUL && menu->children != NULL) {
- if (!sys_menu)
+ if (!sys_menu) {
EMSG(_("E330: Menu path must not lead to a sub-menu"));
+ }
goto erret;
}
- if (*next_name != NUL && menu->children == NULL
- ) {
- if (!sys_menu)
+ if (*next_name != NUL && menu->children == NULL) {
+ if (!sys_menu) {
EMSG(_(e_notsubmenu));
+ }
goto erret;
}
break;
@@ -353,7 +353,7 @@ add_menu_path (
menu->modes = modes;
menu->enabled = MENU_ALL_MODES;
menu->name = vim_strsave(name);
- /* separate mnemonic and accelerator text from actual menu name */
+ // separate mnemonic and accelerator text from actual menu name
menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
if (en_name != NULL) {
menu->en_name = vim_strsave(en_name);
@@ -365,9 +365,7 @@ add_menu_path (
menu->priority = pri_tab[pri_idx];
menu->parent = parent;
- /*
- * Add after menu that has lower priority.
- */
+ // Add after menu that has lower priority.
menu->next = *lower_pri;
*lower_pri = menu;
@@ -393,8 +391,9 @@ add_menu_path (
name = next_name;
xfree(dname);
dname = NULL;
- if (pri_tab[pri_idx + 1] != -1)
- ++pri_idx;
+ if (pri_tab[pri_idx + 1] != -1) {
+ pri_idx++;
+ }
}
xfree(path_name);
@@ -420,8 +419,7 @@ add_menu_path (
// Don't do this for "<Nop>".
c = 0;
d = 0;
- if (amenu && call_data != NULL && *call_data != NUL
- ) {
+ if (amenu && call_data != NULL && *call_data != NUL) {
switch (1 << i) {
case MENU_VISUAL_MODE:
case MENU_SELECT_MODE:
@@ -439,9 +437,9 @@ add_menu_path (
if (c != 0) {
menu->strings[i] = xmalloc(STRLEN(call_data) + 5 );
menu->strings[i][0] = c;
- if (d == 0)
+ if (d == 0) {
STRCPY(menu->strings[i] + 1, call_data);
- else {
+ } else {
menu->strings[i][1] = d;
STRCPY(menu->strings[i] + 2, call_data);
}
@@ -453,8 +451,9 @@ add_menu_path (
menu->strings[i][len + 1] = Ctrl_G;
menu->strings[i][len + 2] = NUL;
}
- } else
+ } else {
menu->strings[i] = p;
+ }
menu->noremap[i] = menuarg->noremap[0];
menu->silent[i] = menuarg->silent[0];
}
@@ -658,20 +657,109 @@ static void free_menu_string(vimmenu_T *menu, int idx)
menu->strings[idx] = NULL;
}
-/*
- * Show the mapping associated with a menu item or hierarchy in a sub-menu.
- */
-static int show_menus(char_u *path_name, int modes)
+/// Export menus
+///
+/// @param[in] menu if null, starts from root_menu
+/// @param modes, a choice of \ref MENU_MODES
+/// @return a dict with name/commands
+/// @see menu_get
+static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
+{
+ dict_T *dict;
+
+ if (!menu || (menu->modes & modes) == 0x0) {
+ return NULL;
+ }
+
+ dict = tv_dict_alloc();
+ tv_dict_add_str(dict, S_LEN("name"), (char *)menu->dname);
+ tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority);
+ tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname));
+
+ if (menu->mnemonic) {
+ char buf[MB_MAXCHAR + 1] = { 0 }; // > max value of utf8_char2bytes
+ utf_char2bytes(menu->mnemonic, (char_u *)buf);
+ tv_dict_add_str(dict, S_LEN("shortcut"), buf);
+ }
+
+ if (menu->actext) {
+ tv_dict_add_str(dict, S_LEN("actext"), (char *)menu->actext);
+ }
+
+ if (menu->modes & MENU_TIP_MODE && menu->strings[MENU_INDEX_TIP]) {
+ tv_dict_add_str(dict, S_LEN("tooltip"),
+ (char *)menu->strings[MENU_INDEX_TIP]);
+ }
+
+ if (!menu->children) {
+ // leaf menu
+ dict_T *commands = tv_dict_alloc();
+ tv_dict_add_dict(dict, S_LEN("mappings"), commands);
+
+ for (int bit = 0; bit < MENU_MODES; bit++) {
+ if ((menu->modes & modes & (1 << bit)) != 0) {
+ dict_T *impl = tv_dict_alloc();
+ tv_dict_add_allocated_str(impl, S_LEN("rhs"),
+ str2special_save((char *)menu->strings[bit],
+ false, false));
+ tv_dict_add_nr(impl, S_LEN("silent"), menu->silent[bit]);
+ tv_dict_add_nr(impl, S_LEN("enabled"),
+ (menu->enabled & (1 << bit)) ? 1 : 0);
+ tv_dict_add_nr(impl, S_LEN("noremap"),
+ (menu->noremap[bit] & REMAP_NONE) ? 1 : 0);
+ tv_dict_add_nr(impl, S_LEN("sid"),
+ (menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0);
+ tv_dict_add_dict(commands, (char *)&menu_mode_chars[bit], 1, impl);
+ }
+ }
+ } else {
+ // visit recursively all children
+ list_T *const children_list = tv_list_alloc(kListLenMayKnow);
+ for (menu = menu->children; menu != NULL; menu = menu->next) {
+ dict_T *dic = menu_get_recursive(menu, modes);
+ if (tv_dict_len(dict) > 0) {
+ tv_list_append_dict(children_list, dic);
+ }
+ }
+ tv_dict_add_list(dict, S_LEN("submenus"), children_list);
+ }
+ return dict;
+}
+
+
+/// Export menus matching path \p path_name
+///
+/// @param path_name
+/// @param modes supported modes, see \ref MENU_MODES
+/// @param[in,out] list must be allocated
+/// @return false if could not find path_name
+bool menu_get(char_u *const path_name, int modes, list_T *list)
{
- char_u *p;
- char_u *name;
vimmenu_T *menu;
- vimmenu_T *parent = NULL;
+ menu = find_menu(root_menu, path_name, modes);
+ if (!menu) {
+ return false;
+ }
+ for (; menu != NULL; menu = menu->next) {
+ dict_T *dict = menu_get_recursive(menu, modes);
+ if (dict && tv_dict_len(dict) > 0) {
+ tv_list_append_dict(list, dict);
+ }
+ }
+ return true;
+}
- menu = root_menu;
- name = path_name = vim_strsave(path_name);
- /* First, find the (sub)menu with the given name */
+/// Find menu matching required name and modes
+///
+/// @param menu top menu to start looking from
+/// @param name path towards the menu
+/// @return menu if \p name is null, found menu or NULL
+vimmenu_T *
+find_menu(vimmenu_T *menu, char_u * name, int modes)
+{
+ char_u *p;
+
while (*name) {
p = menu_name_skip(name);
while (menu != NULL) {
@@ -679,39 +767,46 @@ static int show_menus(char_u *path_name, int modes)
/* Found menu */
if (*p != NUL && menu->children == NULL) {
EMSG(_(e_notsubmenu));
- xfree(path_name);
- return FAIL;
+ return NULL;
} else if ((menu->modes & modes) == 0x0) {
EMSG(_(e_othermode));
- xfree(path_name);
- return FAIL;
+ return NULL;
}
break;
}
menu = menu->next;
}
+
if (menu == NULL) {
EMSG2(_(e_nomenu), name);
- xfree(path_name);
- return FAIL;
+ return NULL;
}
name = p;
- parent = menu;
menu = menu->children;
}
- xfree(path_name);
+ return menu;
+}
+
+/// Show the mapping associated with a menu item or hierarchy in a sub-menu.
+static int show_menus(char_u *const path_name, int modes)
+{
+ vimmenu_T *menu;
+
+ // First, find the (sub)menu with the given name
+ menu = find_menu(root_menu, path_name, modes);
+ if (!menu) {
+ return FAIL;
+ }
/* Now we have found the matching menu, and we list the mappings */
/* Highlight title */
MSG_PUTS_TITLE(_("\n--- Menus ---"));
- show_menus_recursive(parent, modes, 0);
+ show_menus_recursive(menu->parent, modes, 0);
return OK;
}
-/*
- * Recursively show the mappings associated with the menus under the given one
- */
+/// Recursively show the mappings associated with the menus under the given one
static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
{
int i;
@@ -994,12 +1089,13 @@ char_u *get_menu_names(expand_T *xp, int idx)
return str;
}
-/*
- * Skip over this element of the menu path and return the start of the next
- * element. Any \ and ^Vs are removed from the current element.
- * "name" may be modified.
- */
-char_u *menu_name_skip(char_u *name)
+
+/// Skip over this element of the menu path and return the start of the next
+/// element. Any \ and ^Vs are removed from the current element.
+///
+/// @param name may be modified.
+/// @return start of the next element
+char_u *menu_name_skip(char_u *const name)
{
char_u *p;
@@ -1019,16 +1115,16 @@ char_u *menu_name_skip(char_u *name)
* Return TRUE when "name" matches with menu "menu". The name is compared in
* two ways: raw menu name and menu name without '&'. ignore part after a TAB.
*/
-static int menu_name_equal(char_u *name, vimmenu_T *menu)
+static bool menu_name_equal(const char_u *const name, vimmenu_T *const menu)
{
if (menu->en_name != NULL
&& (menu_namecmp(name, menu->en_name)
|| menu_namecmp(name, menu->en_dname)))
- return TRUE;
+ return true;
return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname);
}
-static int menu_namecmp(char_u *name, char_u *mname)
+static bool menu_namecmp(const char_u *const name, const char_u *const mname)
{
int i;
@@ -1039,18 +1135,20 @@ static int menu_namecmp(char_u *name, char_u *mname)
&& (mname[i] == NUL || mname[i] == TAB);
}
-/*
- * Return the modes specified by the given menu command (eg :menu! returns
- * MENU_CMDLINE_MODE | MENU_INSERT_MODE).
- * If "noremap" is not NULL, then the flag it points to is set according to
- * whether the command is a "nore" command.
- * If "unmenu" is not NULL, then the flag it points to is set according to
- * whether the command is an "unmenu" command.
- */
-static int
-get_menu_cmd_modes (
- char_u *cmd,
- int forceit, /* Was there a "!" after the command? */
+
+/// Returns the \ref MENU_MODES specified by menu command `cmd`.
+/// (eg :menu! returns MENU_CMDLINE_MODE | MENU_INSERT_MODE)
+///
+/// @param[in] cmd string like "nmenu", "vmenu", etc.
+/// @param[in] forceit bang (!) was given after the command
+/// @param[out] noremap If not NULL, the flag it points to is set according
+/// to whether the command is a "nore" command.
+/// @param[out] unmenu If not NULL, the flag it points to is set according
+/// to whether the command is an "unmenu" command.
+int
+get_menu_cmd_modes(
+ const char_u * cmd,
+ bool forceit,
int *noremap,
int *unmenu
)
@@ -1091,12 +1189,15 @@ get_menu_cmd_modes (
}
/* FALLTHROUGH */
default:
- --cmd;
- if (forceit) /* menu!! */
+ cmd--;
+ if (forceit) {
+ // menu!!
modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
- else /* menu */
+ } else {
+ // menu
modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE
| MENU_OP_PENDING_MODE;
+ }
}
if (noremap != NULL)
@@ -1202,12 +1303,14 @@ int menu_is_separator(char_u *name)
return name[0] == '-' && name[STRLEN(name) - 1] == '-';
}
-/*
- * Return TRUE if the menu is hidden: Starts with ']'
- */
+
+/// True if a popup menu or starts with \ref MNU_HIDDEN_CHAR
+///
+/// @return true if the menu is hidden
static int menu_is_hidden(char_u *name)
{
- return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
+ return (name[0] == MNU_HIDDEN_CHAR)
+ || (menu_is_popup(name) && name[5] != NUL);
}
/*
@@ -1314,17 +1417,20 @@ void ex_emenu(exarg_T *eap)
idx = MENU_INDEX_NORMAL;
}
- if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL) {
- /* When executing a script or function execute the commands right now.
- * Otherwise put them in the typeahead buffer. */
- if (current_SID != 0)
+ assert(idx != MENU_INDEX_INVALID);
+ if (menu->strings[idx] != NULL) {
+ // When executing a script or function execute the commands right now.
+ // Otherwise put them in the typeahead buffer.
+ if (current_SID != 0) {
exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
- menu->silent[idx]);
- else
- ins_typebuf(menu->strings[idx], menu->noremap[idx], 0,
- TRUE, menu->silent[idx]);
- } else
+ menu->silent[idx]);
+ } else {
+ ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, true,
+ menu->silent[idx]);
+ }
+ } else {
EMSG2(_("E335: Menu not defined for %s mode"), mode);
+ }
}
/*
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index a84b7d812e..5ff979f2bf 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -6,7 +6,9 @@
#include "nvim/types.h" // for char_u and expand_T
#include "nvim/ex_cmds_defs.h" // for exarg_T
-/* Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode */
+/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
+/// \addtogroup MENU_INDEX
+/// @{
#define MENU_INDEX_INVALID -1
#define MENU_INDEX_NORMAL 0
#define MENU_INDEX_VISUAL 1
@@ -16,8 +18,12 @@
#define MENU_INDEX_CMDLINE 5
#define MENU_INDEX_TIP 6
#define MENU_MODES 7
+/// @}
+/// note MENU_INDEX_TIP is not a 'real' mode
-/* Menu modes */
+/// Menu modes
+/// \addtogroup MENU_MODES
+/// @{
#define MENU_NORMAL_MODE (1 << MENU_INDEX_NORMAL)
#define MENU_VISUAL_MODE (1 << MENU_INDEX_VISUAL)
#define MENU_SELECT_MODE (1 << MENU_INDEX_SELECT)
@@ -26,31 +32,30 @@
#define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE)
#define MENU_TIP_MODE (1 << MENU_INDEX_TIP)
#define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1)
-/*note MENU_INDEX_TIP is not a 'real' mode*/
+/// @}
-/* Start a menu name with this to not include it on the main menu bar */
+/// Start a menu name with this to not include it on the main menu bar
#define MNU_HIDDEN_CHAR ']'
typedef struct VimMenu vimmenu_T;
struct VimMenu {
- int modes; /* Which modes is this menu visible for? */
- int enabled; /* for which modes the menu is enabled */
- char_u *name; /* Name of menu, possibly translated */
- char_u *dname; /* Displayed Name ("name" without '&') */
- char_u *en_name; /* "name" untranslated, NULL when "name"
- * was not translated */
- char_u *en_dname; /* "dname" untranslated, NULL when "dname"
- * was not translated */
- int mnemonic; /* mnemonic key (after '&') */
- char_u *actext; /* accelerator text (after TAB) */
- long priority; /* Menu order priority */
- char_u *strings[MENU_MODES]; /* Mapped string for each mode */
- int noremap[MENU_MODES]; /* A REMAP_ flag for each mode */
- bool silent[MENU_MODES]; /* A silent flag for each mode */
- vimmenu_T *children; /* Children of sub-menu */
- vimmenu_T *parent; /* Parent of menu */
- vimmenu_T *next; /* Next item in menu */
+ int modes; ///< Which modes is this menu visible for
+ int enabled; ///< for which modes the menu is enabled
+ char_u *name; ///< Name of menu, possibly translated
+ char_u *dname; ///< Displayed Name ("name" without '&')
+ char_u *en_name; ///< "name" untranslated, NULL when
+ ///< was not translated
+ char_u *en_dname; ///< NULL when "dname" untranslated
+ int mnemonic; ///< mnemonic key (after '&')
+ char_u *actext; ///< accelerator text (after TAB)
+ long priority; ///< Menu order priority
+ char_u *strings[MENU_MODES]; ///< Mapped string for each mode
+ int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode
+ bool silent[MENU_MODES]; ///< A silent flag for each mode
+ vimmenu_T *children; ///< Children of sub-menu
+ vimmenu_T *parent; ///< Parent of menu
+ vimmenu_T *next; ///< Next item in menu
};
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 057ce75f79..12e5b844be 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -487,9 +487,6 @@ int emsg(const char_u *s_)
}
called_emsg = true;
- if (emsg_silent == 0) {
- ex_exitval = 1;
- }
// If "emsg_severe" is TRUE: When an error exception is to be thrown,
// prefer this message over previous messages for the same command.
@@ -540,6 +537,8 @@ int emsg(const char_u *s_)
return true;
}
+ ex_exitval = 1;
+
// Reset msg_silent, an error causes messages to be switched back on.
msg_silent = 0;
cmd_silent = FALSE;
@@ -583,19 +582,60 @@ void emsg_invreg(int name)
/// Print an error message with unknown number of arguments
bool emsgf(const char *const fmt, ...)
{
+ bool ret;
+
+ va_list ap;
+ va_start(ap, fmt);
+ ret = emsgfv(fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/// Print an error message with unknown number of arguments
+static bool emsgfv(const char *fmt, va_list ap)
+{
static char errbuf[IOSIZE];
if (emsg_not_now()) {
return true;
}
- va_list ap;
- va_start(ap, fmt);
vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
- va_end(ap);
return emsg((const char_u *)errbuf);
}
+/// Same as emsg(...), but abort on error when ABORT_ON_INTERNAL_ERROR is
+/// defined. It is used for internal errors only, so that they can be
+/// detected when fuzzing vim.
+void iemsg(const char *s)
+{
+ msg((char_u *)s);
+#ifdef ABORT_ON_INTERNAL_ERROR
+ abort();
+#endif
+}
+
+/// Same as emsgf(...) but abort on error when ABORT_ON_INTERNAL_ERROR is
+/// defined. It is used for internal errors only, so that they can be
+/// detected when fuzzing vim.
+void iemsgf(const char *s, ...)
+{
+ va_list ap;
+ va_start(ap, s);
+ (void)emsgfv(s, ap);
+ va_end(ap);
+#ifdef ABORT_ON_INTERNAL_ERROR
+ abort();
+#endif
+}
+
+/// Give an "Internal error" message.
+void internal_error(char *where)
+{
+ IEMSG2(_(e_intern2), where);
+}
+
static void msg_emsgf_event(void **argv)
{
char *s = argv[0];
@@ -1196,7 +1236,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr)
len -= mb_l - 1;
str += mb_l;
} else {
- s = transchar_byte(*str);
+ s = transchar_byte((uint8_t)(*str));
if (s[1] != NUL) {
// Unprintable char: print the printable chars so far and the
// translation of the unprintable char.
@@ -1237,31 +1277,30 @@ void msg_make(char_u *arg)
}
}
-/*
- * Output the string 'str' upto a NUL character.
- * Return the number of characters it takes on the screen.
- *
- * If K_SPECIAL is encountered, then it is taken in conjunction with the
- * following character and shown as <F1>, <S-Up> etc. Any other character
- * which is not printable shown in <> form.
- * If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
- * If a character is displayed in one of these special ways, is also
- * highlighted (its highlight name is '8' in the p_hl variable).
- * Otherwise characters are not highlighted.
- * This function is used to show mappings, where we want to see how to type
- * the character/string -- webb
- */
-int
-msg_outtrans_special (
- char_u *strstart,
- int from /* TRUE for lhs of a mapping */
+/// Output the string 'str' upto a NUL character.
+/// Return the number of characters it takes on the screen.
+///
+/// If K_SPECIAL is encountered, then it is taken in conjunction with the
+/// following character and shown as <F1>, <S-Up> etc. Any other character
+/// which is not printable shown in <> form.
+/// If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
+/// If a character is displayed in one of these special ways, is also
+/// highlighted (its highlight name is '8' in the p_hl variable).
+/// Otherwise characters are not highlighted.
+/// This function is used to show mappings, where we want to see how to type
+/// the character/string -- webb
+int msg_outtrans_special(
+ const char_u *strstart,
+ int from ///< true for LHS of a mapping
)
{
- char_u *str = strstart;
+ if (strstart == NULL) {
+ return 0; // Do nothing.
+ }
+ const char_u *str = strstart;
int retval = 0;
- int attr;
+ int attr = hl_attr(HLF_8);
- attr = hl_attr(HLF_8);
while (*str != NUL) {
const char *string;
// Leading and trailing spaces need to be displayed in <> form.
@@ -1269,7 +1308,7 @@ msg_outtrans_special (
string = "<Space>";
str++;
} else {
- string = (const char *)str2special((char_u **)&str, from);
+ string = str2special((const char **)&str, from, false);
}
const int len = vim_strsize((char_u *)string);
// Highlight special keys
@@ -1281,108 +1320,125 @@ msg_outtrans_special (
return retval;
}
-/*
- * Return the lhs or rhs of a mapping, with the key codes turned into printable
- * strings, in an allocated string.
- */
-char_u *
-str2special_save (
- char_u *str,
- int is_lhs /* TRUE for lhs, FALSE for rhs */
-)
+/// Convert string, replacing key codes with printables
+///
+/// Used for lhs or rhs of mappings.
+///
+/// @param[in] str String to convert.
+/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used fo
+/// lhs, but not rhs.
+/// @param[in] replace_lt Convert `<` into `<lt>`.
+///
+/// @return [allocated] Converted string.
+char *str2special_save(const char *const str, const bool replace_spaces,
+ const bool replace_lt)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
+ FUNC_ATTR_NONNULL_RET
{
garray_T ga;
- char_u *p = str;
-
ga_init(&ga, 1, 40);
- while (*p != NUL)
- ga_concat(&ga, str2special(&p, is_lhs));
+
+ const char *p = str;
+ while (*p != NUL) {
+ ga_concat(&ga, (const char_u *)str2special(&p, replace_spaces, replace_lt));
+ }
ga_append(&ga, NUL);
- return (char_u *)ga.ga_data;
+ return (char *)ga.ga_data;
}
-/*
- * Return the printable string for the key codes at "*sp".
- * Used for translating the lhs or rhs of a mapping to printable chars.
- * Advances "sp" to the next code.
- */
-char_u *
-str2special (
- char_u **sp,
- int from /* TRUE for lhs of mapping */
-)
-{
- int c;
- static char_u buf[7];
- char_u *str = *sp;
- int modifiers = 0;
- int special = FALSE;
-
- if (has_mbyte) {
- char_u *p;
-
- /* Try to un-escape a multi-byte character. Return the un-escaped
- * string if it is a multi-byte character. */
- p = mb_unescape(sp);
- if (p != NULL)
- return p;
+/// Convert character, replacing key with printable representation.
+///
+/// @param[in,out] sp String to convert. Is advanced to the next key code.
+/// @param[in] replace_spaces Convert spaces into <Space>, normally used for
+/// lhs, but not rhs.
+/// @param[in] replace_lt Convert `<` into `<lt>`.
+///
+/// @return Converted key code, in a static buffer. Buffer is always one and the
+/// same, so save converted string somewhere before running str2special
+/// for the second time.
+const char *str2special(const char **const sp, const bool replace_spaces,
+ const bool replace_lt)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
+{
+ static char buf[7];
+
+ // Try to un-escape a multi-byte character. Return the un-escaped
+ // string if it is a multi-byte character.
+ const char *const p = mb_unescape(sp);
+ if (p != NULL) {
+ return p;
}
- c = *str;
+ const char *str = *sp;
+ int c = (uint8_t)(*str);
+ int modifiers = 0;
+ bool special = false;
if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- if (str[1] == KS_MODIFIER) {
- modifiers = str[2];
+ if ((uint8_t)str[1] == KS_MODIFIER) {
+ modifiers = (uint8_t)str[2];
str += 3;
- c = *str;
+ c = (uint8_t)(*str);
}
if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- c = TO_SPECIAL(str[1], str[2]);
+ c = TO_SPECIAL((uint8_t)str[1], (uint8_t)str[2]);
str += 2;
- if (c == KS_ZERO) /* display <Nul> as ^@ or <Nul> */
+ if (c == KS_ZERO) { // display <Nul> as ^@ or <Nul>
c = NUL;
+ }
+ }
+ if (IS_SPECIAL(c) || modifiers) { // Special key.
+ special = true;
}
- if (IS_SPECIAL(c) || modifiers) /* special key */
- special = TRUE;
}
- if (has_mbyte && !IS_SPECIAL(c)) {
- int len = (*mb_ptr2len)(str);
+ if (!IS_SPECIAL(c)) {
+ const int len = utf_ptr2len((const char_u *)str);
- /* For multi-byte characters check for an illegal byte. */
- if (has_mbyte && MB_BYTE2LEN(*str) > len) {
- transchar_nonprint(buf, c);
+ // Check for an illegal byte.
+ if (MB_BYTE2LEN((uint8_t)(*str)) > len) {
+ transchar_nonprint((char_u *)buf, c);
*sp = str + 1;
return buf;
}
- /* Since 'special' is TRUE the multi-byte character 'c' will be
- * processed by get_special_key_name() */
- c = (*mb_ptr2char)(str);
+ // Since 'special' is TRUE the multi-byte character 'c' will be
+ // processed by get_special_key_name().
+ c = utf_ptr2char((const char_u *)str);
*sp = str + len;
- } else
+ } else {
*sp = str + 1;
+ }
- /* Make unprintable characters in <> form, also <M-Space> and <Tab>.
- * Use <Space> only for lhs of a mapping. */
- if (special || char2cells(c) > 1 || (from && c == ' '))
- return get_special_key_name(c, modifiers);
+ // Make unprintable characters in <> form, also <M-Space> and <Tab>.
+ if (special
+ || char2cells(c) > 1
+ || (replace_spaces && c == ' ')
+ || (replace_lt && c == '<')) {
+ return (const char *)get_special_key_name(c, modifiers);
+ }
buf[0] = c;
buf[1] = NUL;
return buf;
}
-/*
- * Translate a key sequence into special key names.
- */
-void str2specialbuf(char_u *sp, char_u *buf, int len)
+/// Convert string, replacing key codes with printables
+///
+/// @param[in] str String to convert.
+/// @param[out] buf Buffer to save results to.
+/// @param[in] len Buffer length.
+void str2specialbuf(const char *sp, char *buf, size_t len)
+ FUNC_ATTR_NONNULL_ALL
{
- char_u *s;
-
- *buf = NUL;
while (*sp) {
- s = str2special(&sp, FALSE);
- if ((int)(STRLEN(s) + STRLEN(buf)) < len)
- STRCAT(buf, s);
+ const char *s = str2special(&sp, false, false);
+ const size_t s_len = strlen(s);
+ if (len <= s_len) {
+ break;
+ }
+ memcpy(buf, s, s_len);
+ buf += s_len;
+ len -= s_len;
}
+ *buf = NUL;
}
/*
@@ -1611,6 +1667,27 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
}
}
+/// Print a formatted message
+///
+/// Message printed is limited by #IOSIZE. Must not be used from inside
+/// msg_puts_attr().
+///
+/// @param[in] attr Highlight attributes.
+/// @param[in] fmt Format string.
+void msg_printf_attr(const int attr, const char *const fmt, ...)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ static char msgbuf[IOSIZE];
+
+ va_list ap;
+ va_start(ap, fmt);
+ const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap, NULL);
+ va_end(ap);
+
+ msg_scroll = true;
+ msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr);
+}
+
/*
* The display part of msg_puts_attr_len().
* May be called recursively to display scroll-back text.
@@ -1747,17 +1824,13 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
} while (msg_col & 7);
} else if (*s == BELL) { // beep (from ":sh")
vim_beep(BO_SH);
- } else {
- if (has_mbyte) {
- cw = (*mb_ptr2cells)(s);
- if (enc_utf8 && maxlen >= 0)
- /* avoid including composing chars after the end */
- l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
- else
- l = (*mb_ptr2len)(s);
+ } else if (*s >= 0x20) { // printable char
+ cw = mb_ptr2cells(s);
+ if (maxlen >= 0) {
+ // avoid including composing chars after the end
+ l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
} else {
- cw = 1;
- l = 1;
+ l = utfc_ptr2len(s);
}
// When drawing from right to left or when a double-wide character
// doesn't fit, draw a single character here. Otherwise collect
@@ -2705,9 +2778,11 @@ do_dialog (
int c;
int i;
- /* Don't output anything in silent mode ("ex -s") */
- if (silent_mode)
- return dfltbutton; /* return default option */
+ if (silent_mode // No dialogs in silent mode ("ex -s")
+ || !ui_active() // Without a UI Nvim waits for input forever.
+ ) {
+ return dfltbutton; // return default option
+ }
oldState = State;
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 904a9d3ca1..82935a36a9 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -49,6 +49,15 @@
/// Like #EMSG, but for messages with one "%" PRIu64 inside
#define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n))
+/// Like #EMSG, but for internal messages
+#define IEMSG(s) iemsg((const char *)(s))
+
+/// Like #EMSG2, but for internal messages
+#define IEMSG2(s, p) iemsgf((const char *)(s), (p))
+
+/// Like #EMSGN, but for internal messages
+#define IEMSGN(s, n) iemsgf((const char *)(s), (int64_t)(n))
+
/// Display message at the recorded position
#define MSG_PUTS(s) msg_puts((const char *)(s))
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 835b9c7b20..b0232b6516 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -749,8 +749,9 @@ open_line (
// Postpone calling changed_lines(), because it would mess up folding
// with markers.
// Skip mark_adjust when adding a line after the last one, there can't
- // be marks there.
- if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count) {
+ // be marks there. But still needed in diff mode.
+ if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count
+ || curwin->w_p_diff) {
mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false);
}
did_append = true;
@@ -1273,8 +1274,8 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
* Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
*/
width = wp->w_width - win_col_off(wp);
- if (width <= 0) {
- return 32000; // bigger than the number of lines of the screen
+ if (width <= 0 || col > 32000) {
+ return 32000; // bigger than the number of screen columns
}
if (col <= (unsigned int)width) {
return 1;
@@ -1468,7 +1469,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
}
- char_u *newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen));
+ char_u *newp = xmalloc((size_t)(linelen + newlen - oldlen));
// Copy bytes before the cursor.
if (col > 0) {
@@ -1477,7 +1478,10 @@ void ins_char_bytes(char_u *buf, size_t charlen)
// Copy bytes after the changed character(s).
char_u *p = newp + col;
- memmove(p + newlen, oldp + col + oldlen, (size_t)(linelen - col - oldlen));
+ if (linelen > col + oldlen) {
+ memmove(p + newlen, oldp + col + oldlen,
+ (size_t)(linelen - col - oldlen));
+ }
// Insert or overwrite the new character.
memmove(p, buf, charlen);
@@ -1864,8 +1868,8 @@ void appended_lines(linenr_T lnum, long count)
void appended_lines_mark(linenr_T lnum, long count)
{
// Skip mark_adjust when adding a line after the last one, there can't
- // be marks there.
- if (lnum + count < curbuf->b_ml.ml_line_count) {
+ // be marks there. But it's still needed in diff mode.
+ if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false);
}
changed_lines(lnum + 1, 0, lnum + 1, count);
@@ -2203,7 +2207,7 @@ change_warning (
set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
msg_clr_eos();
(void)msg_end();
- if (msg_silent == 0 && !silent_mode) {
+ if (msg_silent == 0 && !silent_mode && ui_active()) {
ui_flush();
os_delay(1000L, true); /* give the user time to think about it */
}
@@ -2609,20 +2613,22 @@ int match_user(char_u *name)
return result;
}
-/*
- * Preserve files and exit.
- * When called IObuff must contain a message.
- * NOTE: This may be called from deathtrap() in a signal handler, avoid unsafe
- * functions, such as allocating memory.
- */
+/// Preserve files and exit.
+/// @note IObuff must contain a message.
+/// @note This may be called from deadly_signal() in a signal handler, avoid
+/// unsafe functions, such as allocating memory.
void preserve_exit(void)
+ FUNC_ATTR_NORETURN
{
// 'true' when we are sure to exit, e.g., after a deadly signal
static bool really_exiting = false;
// Prevent repeated calls into this method.
if (really_exiting) {
- stream_set_blocking(input_global_fd(), true); //normalize stream (#2598)
+ if (input_global_fd() >= 0) {
+ // normalize stream (#2598)
+ stream_set_blocking(input_global_fd(), true);
+ }
exit(2);
}
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index d908a022f1..6f636f643a 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -608,8 +608,7 @@ bool mouse_scroll_horiz(int dir)
return leftcol_changed();
}
-// Adjust the clicked column position if there are concealed characters
-// before the current column. But only when it's absolutely necessary.
+/// Adjusts the clicked column position when 'conceallevel' > 0
static int mouse_adjust_click(win_T *wp, int row, int col)
{
if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0
@@ -617,64 +616,102 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
return col;
}
- int end = (colnr_T)STRLEN(ml_get(wp->w_cursor.lnum));
- int vend = getviscol2(end, 0);
+ // `col` is the position within the current line that is highlighted by the
+ // cursor without consideration for concealed characters. The current line is
+ // scanned *up to* `col`, nudging it left or right when concealed characters
+ // are encountered.
+ //
+ // chartabsize() is used to keep track of the virtual column position relative
+ // to the line's bytes. For example: if col == 9 and the line starts with a
+ // tab that's 8 columns wide, we would want the cursor to be highlighting the
+ // second byte, not the ninth.
+
+ linenr_T lnum = wp->w_cursor.lnum;
+ char_u *line = ml_get(lnum);
+ char_u *ptr = line;
+ char_u *ptr_end = line;
+ char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end`
- if (col >= vend) {
- return col;
+ // Find the offset where scanning should begin.
+ int offset = wp->w_leftcol;
+ if (row > 0) {
+ offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) -
+ wp->w_leftcol + wp->w_skipcol);
}
- int i = wp->w_leftcol;
+ int vcol;
+
+ if (offset) {
+ // Skip everything up to an offset since nvim takes care of displaying the
+ // correct portion of the line when horizontally scrolling.
+ // When 'wrap' is enabled, only the row (of the wrapped line) needs to be
+ // checked for concealed characters.
+ vcol = 0;
+ while (vcol < offset && *ptr != NUL) {
+ vcol += chartabsize(ptr, vcol);
+ ptr += utfc_ptr2len(ptr);
+ }
- if (row > 0) {
- i += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp)
- - wp->w_leftcol) + wp->w_skipcol;
+ ptr_row_offset = ptr;
}
- int start_col = i;
- int matchid;
- int last_matchid;
- int bcol = end - (vend - col);
+ // Align `ptr_end` with `col`
+ vcol = offset;
+ ptr_end = ptr_row_offset;
+ while (vcol < col && *ptr_end != NUL) {
+ vcol += chartabsize(ptr_end, vcol);
+ ptr_end += utfc_ptr2len(ptr_end);
+ }
- while (i < bcol) {
- matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i);
+ int matchid;
+ int prev_matchid;
+ int nudge = 0;
+ int cwidth = 0;
+
+ vcol = offset;
+
+#define incr() nudge++; ptr_end += utfc_ptr2len(ptr_end)
+#define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end)
+
+ while (ptr < ptr_end && *ptr != NUL) {
+ cwidth = chartabsize(ptr, vcol);
+ vcol += cwidth;
+ if (cwidth > 1 && *ptr == '\t' && nudge > 0) {
+ // A tab will "absorb" any previous adjustments.
+ cwidth = MIN(cwidth, nudge);
+ while (cwidth > 0) {
+ decr();
+ cwidth--;
+ }
+ }
+ matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
if (matchid != 0) {
if (wp->w_p_cole == 3) {
- bcol++;
+ incr();
} else {
- if (row > 0 && i == start_col) {
- // Check if the current concealed character is actually part of
- // the previous wrapped row's conceal group.
- last_matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum,
- i - 1);
- if (last_matchid == matchid) {
- bcol++;
- }
- } else if (wp->w_p_cole == 1
- || (wp->w_p_cole == 2
- && (lcs_conceal != NUL
- || syn_get_sub_char() != NUL))) {
+ if (!(row > 0 && ptr == ptr_row_offset)
+ && (wp->w_p_cole == 1 || (wp->w_p_cole == 2
+ && (lcs_conceal != NUL
+ || syn_get_sub_char() != NUL)))) {
// At least one placeholder character will be displayed.
- bcol--;
+ decr();
}
- last_matchid = matchid;
+ prev_matchid = matchid;
- // Adjust for concealed text that spans more than one character.
- do {
- i++;
- bcol++;
- matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i);
- } while (last_matchid == matchid);
+ while (prev_matchid == matchid && *ptr != NUL) {
+ incr();
+ ptr += utfc_ptr2len(ptr);
+ matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
+ }
continue;
}
}
- i++;
+ ptr += utfc_ptr2len(ptr);
}
- return getviscol2(bcol, 0);
+ return col + nudge;
}
-
diff --git a/src/nvim/move.c b/src/nvim/move.c
index d5be4cb8c3..cb13a9e207 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -155,12 +155,11 @@ void update_topline(void)
old_topline = curwin->w_topline;
old_topfill = curwin->w_topfill;
- /*
- * If the buffer is empty, always set topline to 1.
- */
- if (bufempty()) { /* special case - file is empty */
- if (curwin->w_topline != 1)
+ // If the buffer is empty, always set topline to 1.
+ if (BUFEMPTY()) { // special case - file is empty
+ if (curwin->w_topline != 1) {
redraw_later(NOT_VALID);
+ }
curwin->w_topline = 1;
curwin->w_botline = 2;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
@@ -673,7 +672,7 @@ int win_col_off(win_T *wp)
return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
+ (cmdwin_type == 0 || wp != curwin ? 0 : 1)
+ (int)wp->w_p_fdc
- + (signcolumn_on(wp) ? 2 : 0);
+ + (signcolumn_on(wp) ? win_signcol_width(wp) : 0);
}
int curwin_col_off(void)
@@ -1763,7 +1762,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 +1796,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;
@@ -1989,9 +1988,8 @@ void halfpage(bool flag, linenr_T Prenum)
while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count) {
if (curwin->w_topfill > 0) {
i = 1;
- if (--n < 0 && scrolled > 0)
- break;
- --curwin->w_topfill;
+ n--;
+ curwin->w_topfill--;
} else {
i = plines_nofill(curwin->w_topline);
n -= i;
@@ -2067,9 +2065,8 @@ void halfpage(bool flag, linenr_T Prenum)
while (n > 0 && curwin->w_topline > 1) {
if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline)) {
i = 1;
- if (--n < 0 && scrolled > 0)
- break;
- ++curwin->w_topfill;
+ n--;
+ curwin->w_topfill++;
} else {
i = plines_nofill(curwin->w_topline - 1);
n -= i;
@@ -2147,14 +2144,12 @@ void do_check_cursorbind(void)
curbuf = curwin->w_buffer;
/* skip original window and windows with 'noscrollbind' */
if (curwin != old_curwin && curwin->w_p_crb) {
- if (curwin->w_p_diff)
- curwin->w_cursor.lnum
- = diff_get_corresponding_line(old_curbuf,
- line,
- curbuf,
- curwin->w_cursor.lnum);
- else
+ if (curwin->w_p_diff) {
+ curwin->w_cursor.lnum =
+ diff_get_corresponding_line(old_curbuf, line);
+ } else {
curwin->w_cursor.lnum = line;
+ }
curwin->w_cursor.col = col;
curwin->w_cursor.coladd = coladd;
curwin->w_curswant = curswant;
@@ -2166,16 +2161,21 @@ void do_check_cursorbind(void)
int restart_edit_save = restart_edit;
restart_edit = true;
check_cursor();
+ if (curwin->w_p_cul || curwin->w_p_cuc) {
+ validate_cursor();
+ }
restart_edit = restart_edit_save;
}
- /* Correct cursor for multi-byte character. */
- if (has_mbyte)
+ // Correct cursor for multi-byte character.
+ if (has_mbyte) {
mb_adjust_cursor();
+ }
redraw_later(VALID);
- /* Only scroll when 'scrollbind' hasn't done this. */
- if (!curwin->w_p_scb)
+ // Only scroll when 'scrollbind' hasn't done this.
+ if (!curwin->w_p_scb) {
update_topline();
+ }
curwin->w_redr_status = true;
}
}
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 68ac35bc4e..32781cf4d9 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -11,8 +11,8 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/api/ui.h"
+#include "nvim/channel.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/server.h"
#include "nvim/event/loop.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/rstream.h"
@@ -29,60 +29,14 @@
#include "nvim/map.h"
#include "nvim/log.h"
#include "nvim/misc1.h"
-#include "nvim/path.h"
#include "nvim/lib/kvec.h"
#include "nvim/os/input.h"
-#define CHANNEL_BUFFER_SIZE 0xffff
-
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
#define log_client_msg(...)
#define log_server_msg(...)
#endif
-typedef enum {
- kChannelTypeSocket,
- kChannelTypeProc,
- kChannelTypeStdio,
- kChannelTypeInternal
-} ChannelType;
-
-typedef struct {
- uint64_t request_id;
- bool returned, errored;
- Object result;
-} ChannelCallFrame;
-
-typedef struct {
- uint64_t id;
- size_t refcount;
- size_t pending_requests;
- PMap(cstr_t) *subscribed_events;
- bool closed;
- ChannelType type;
- msgpack_unpacker *unpacker;
- union {
- Stream stream;
- Process *proc;
- struct {
- Stream in;
- Stream out;
- } std;
- } data;
- uint64_t next_request_id;
- kvec_t(ChannelCallFrame *) call_stack;
- kvec_t(WBuffer *) delayed_notifications;
- MultiQueue *events;
-} Channel;
-
-typedef struct {
- Channel *channel;
- MsgpackRpcRequestHandler handler;
- Array args;
- uint64_t request_id;
-} RequestEvent;
-
-static PMap(uint64_t) *channels = NULL;
static PMap(cstr_t) *event_strings = NULL;
static msgpack_sbuffer out_buffer;
@@ -90,121 +44,64 @@ static msgpack_sbuffer out_buffer;
# include "msgpack_rpc/channel.c.generated.h"
#endif
-/// Initializes the module
-void channel_init(void)
+void rpc_init(void)
{
ch_before_blocking_events = multiqueue_new_child(main_loop.events);
- channels = pmap_new(uint64_t)();
event_strings = pmap_new(cstr_t)();
msgpack_sbuffer_init(&out_buffer);
- remote_ui_init();
}
-/// Teardown the module
-void channel_teardown(void)
-{
- if (!channels) {
- return;
- }
-
- Channel *channel;
- map_foreach_value(channels, channel, {
- close_channel(channel);
- });
-}
-
-/// 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)
+void rpc_start(Channel *channel)
{
- Channel *channel = register_channel(kChannelTypeProc, id, proc->events);
- incref(channel); // process channels are only closed by the exit_cb
- channel->data.proc = proc;
+ channel_incref(channel);
+ channel->is_rpc = true;
+ RpcState *rpc = &channel->rpc;
+ rpc->closed = false;
+ rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
+ rpc->subscribed_events = pmap_new(cstr_t)();
+ rpc->next_request_id = 1;
+ kv_init(rpc->call_stack);
- wstream_init(proc->in, 0);
- rstream_init(proc->out, 0);
- rstream_start(proc->out, receive_msgpack, channel);
+ if (channel->streamtype != kChannelStreamInternal) {
+ Stream *out = channel_outstream(channel);
+#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+ Stream *in = channel_instream(channel);
+ DLOG("rpc ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id, in, out);
+#endif
- return channel->id;
+ rstream_start(out, receive_msgpack, channel);
+ }
}
-/// Creates an API channel from a tcp/pipe socket connection
-///
-/// @param watcher The SocketWatcher ready to accept the connection
-void channel_from_connection(SocketWatcher *watcher)
-{
- Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
- 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;
- channel->data.stream.internal_data = channel;
- wstream_init(&channel->data.stream, 0);
- rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE);
- rstream_start(&channel->data.stream, receive_msgpack, channel);
-}
-uint64_t channel_connect(bool tcp, const char *address,
- int timeout, const char **error)
+static Channel *find_rpc_channel(uint64_t id)
{
- if (!tcp) {
- char *path = fix_fname(address);
- if (server_owns_pipe_address(path)) {
- // avoid deadlock
- xfree(path);
- return channel_create_internal();
- }
- xfree(path);
- }
-
- Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
- if (!socket_connect(&main_loop, &channel->data.stream,
- tcp, address, timeout, error)) {
- decref(channel);
- return 0;
+ Channel *chan = find_channel(id);
+ if (!chan || !chan->is_rpc || chan->rpc.closed) {
+ return NULL;
}
-
- incref(channel); // close channel only after the stream is closed
- channel->data.stream.internal_close_cb = close_cb;
- channel->data.stream.internal_data = channel;
- wstream_init(&channel->data.stream, 0);
- rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE);
- rstream_start(&channel->data.stream, receive_msgpack, channel);
- return channel->id;
+ return chan;
}
-/// Sends event/arguments to channel
+/// Publishes an event to a channel.
///
-/// @param id The channel id. If 0, the event will be sent to all
-/// channels that have subscribed to the event type
-/// @param name The event name, an arbitrary string
-/// @param args Array with event arguments
+/// @param id Channel id. 0 means "broadcast to all subscribed channels"
+/// @param name Event name (application-defined)
+/// @param args Array of event arguments
/// @return True if the event was sent successfully, false otherwise.
-bool channel_send_event(uint64_t id, const char *name, Array args)
+bool rpc_send_event(uint64_t id, const char *name, Array args)
{
Channel *channel = NULL;
- if (id && (!(channel = pmap_get(uint64_t)(channels, id))
- || channel->closed)) {
+ if (id && (!(channel = find_rpc_channel(id)))) {
api_free_array(args);
return false;
}
if (channel) {
- if (channel->pending_requests) {
- // Pending request, queue the notification for later sending.
- const String method = cstr_as_string((char *)name);
- WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1);
- kv_push(channel->delayed_notifications, buffer);
- } else {
- send_event(channel, name, args);
- }
+ send_event(channel, name, args);
} else {
- // TODO(tarruda): Implement event broadcasting in vimscript
broadcast_event(name, args);
}
@@ -218,31 +115,30 @@ bool channel_send_event(uint64_t id, const char *name, Array args)
/// @param args Array with method arguments
/// @param[out] error True if the return value is an error
/// @return Whatever the remote method returned
-Object channel_send_call(uint64_t id,
- const char *method_name,
- Array args,
- Error *err)
+Object rpc_send_call(uint64_t id,
+ const char *method_name,
+ Array args,
+ Error *err)
{
Channel *channel = NULL;
- if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) {
+ if (!(channel = find_rpc_channel(id))) {
api_set_error(err, kErrorTypeException, "Invalid channel: %" PRIu64, id);
api_free_array(args);
return NIL;
}
- incref(channel);
- uint64_t request_id = channel->next_request_id++;
+ channel_incref(channel);
+ RpcState *rpc = &channel->rpc;
+ uint64_t request_id = rpc->next_request_id++;
// Send the msgpack-rpc request
send_request(channel, request_id, method_name, args);
// Push the frame
ChannelCallFrame frame = { request_id, false, false, NIL };
- kv_push(channel->call_stack, &frame);
- channel->pending_requests++;
+ kv_push(rpc->call_stack, &frame);
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned);
- (void)kv_pop(channel->call_stack);
- channel->pending_requests--;
+ (void)kv_pop(rpc->call_stack);
if (frame.errored) {
if (frame.result.type == kObjectTypeString) {
@@ -267,11 +163,7 @@ Object channel_send_call(uint64_t id,
api_free_object(frame.result);
}
- if (!channel->pending_requests) {
- send_delayed_notifications(channel);
- }
-
- decref(channel);
+ channel_decref(channel);
return frame.errored ? NIL : frame.result;
}
@@ -280,11 +172,11 @@ Object channel_send_call(uint64_t id,
///
/// @param id The channel id
/// @param event The event type string
-void channel_subscribe(uint64_t id, char *event)
+void rpc_subscribe(uint64_t id, char *event)
{
Channel *channel;
- if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) {
+ if (!(channel = find_rpc_channel(id))) {
abort();
}
@@ -295,97 +187,52 @@ void channel_subscribe(uint64_t id, char *event)
pmap_put(cstr_t)(event_strings, event_string, event_string);
}
- pmap_put(cstr_t)(channel->subscribed_events, event_string, event_string);
+ pmap_put(cstr_t)(channel->rpc.subscribed_events, event_string, event_string);
}
/// Unsubscribes to event broadcasts
///
/// @param id The channel id
/// @param event The event type string
-void channel_unsubscribe(uint64_t id, char *event)
+void rpc_unsubscribe(uint64_t id, char *event)
{
Channel *channel;
- if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) {
+ if (!(channel = find_rpc_channel(id))) {
abort();
}
unsubscribe(channel, event);
}
-/// Closes a channel
-///
-/// @param id The channel id
-/// @return true if successful, false otherwise
-bool channel_close(uint64_t id)
-{
- Channel *channel;
-
- if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) {
- return false;
- }
-
- close_channel(channel);
- return true;
-}
-
-/// Creates an API channel from stdin/stdout. This is used when embedding
-/// Neovim
-void channel_from_stdio(void)
-{
- Channel *channel = register_channel(kChannelTypeStdio, 0, 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);
- rstream_start(&channel->data.std.in, receive_msgpack, channel);
- // write stream
- wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0);
-}
-
-/// Creates a loopback channel. This is used to avoid deadlock
-/// when an instance connects to its own named pipe.
-uint64_t channel_create_internal(void)
-{
- Channel *channel = register_channel(kChannelTypeInternal, 0, NULL);
- incref(channel); // internal channel lives until process exit
- return channel->id;
-}
-
-void channel_process_exit(uint64_t id, int status)
-{
- Channel *channel = pmap_get(uint64_t)(channels, id);
-
- channel->closed = true;
- decref(channel);
-}
-
static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c,
void *data, bool eof)
{
Channel *channel = data;
- incref(channel);
+ channel_incref(channel);
if (eof) {
- close_channel(channel);
+ channel_close(channel->id, kChannelPartRpc, NULL);
char buf[256];
snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client",
channel->id);
- call_set_error(channel, buf, WARNING_LOG_LEVEL);
+ call_set_error(channel, buf, WARN_LOG_LEVEL);
goto end;
}
size_t count = rbuffer_size(rbuf);
- DLOG("parsing %u bytes of msgpack data from Stream(%p)", count, stream);
+ DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p",
+ channel->id, count, stream);
// Feed the unpacker with data
- msgpack_unpacker_reserve_buffer(channel->unpacker, count);
- rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->unpacker), count);
- msgpack_unpacker_buffer_consumed(channel->unpacker, count);
+ msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, count);
+ rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->rpc.unpacker), count);
+ msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, count);
parse_msgpack(channel);
end:
- decref(channel);
+ channel_decref(channel);
}
static void parse_msgpack(Channel *channel)
@@ -395,8 +242,8 @@ static void parse_msgpack(Channel *channel)
msgpack_unpack_return result;
// Deserialize everything we can.
- while ((result = msgpack_unpacker_next(channel->unpacker, &unpacked)) ==
- MSGPACK_UNPACK_SUCCESS) {
+ while ((result = msgpack_unpacker_next(channel->rpc.unpacker, &unpacked)) ==
+ MSGPACK_UNPACK_SUCCESS) {
bool is_response = is_rpc_response(&unpacked.data);
log_client_msg(channel->id, !is_response, unpacked.data);
@@ -422,7 +269,7 @@ static void parse_msgpack(Channel *channel)
if (result == MSGPACK_UNPACK_NOMEM_ERROR) {
mch_errmsg(e_outofmem);
mch_errmsg("\n");
- decref(channel);
+ channel_decref(channel);
preserve_exit();
}
@@ -431,8 +278,8 @@ static void parse_msgpack(Channel *channel)
// causes for this error(search for 'goto _failed')
//
// A not so uncommon cause for this might be deserializing objects with
- // a high nesting level: msgpack will break when it's internal parse stack
- // size exceeds MSGPACK_EMBED_STACK_SIZE(defined as 32 by default)
+ // a high nesting level: msgpack will break when its internal parse stack
+ // size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default)
send_error(channel, 0, "Invalid msgpack payload. "
"This error can also happen when deserializing "
"an object with high level of nesting");
@@ -487,7 +334,7 @@ static void handle_request(Channel *channel, msgpack_object *request)
evdata->handler = handler;
evdata->args = args;
evdata->request_id = request_id;
- incref(channel);
+ channel_incref(channel);
if (handler.async) {
bool is_get_mode = handler.fn == handle_nvim_get_mode;
@@ -525,44 +372,37 @@ static void on_request_event(void **argv)
api_free_object(result);
}
api_free_array(args);
- decref(channel);
+ channel_decref(channel);
xfree(e);
api_clear_error(&error);
}
static bool channel_write(Channel *channel, WBuffer *buffer)
{
- bool success = false;
+ bool success;
- if (channel->closed) {
+ if (channel->rpc.closed) {
wstream_release_wbuffer(buffer);
return false;
}
- switch (channel->type) {
- case kChannelTypeSocket:
- success = wstream_write(&channel->data.stream, buffer);
- break;
- case kChannelTypeProc:
- success = wstream_write(channel->data.proc->in, buffer);
- break;
- case kChannelTypeStdio:
- success = wstream_write(&channel->data.std.out, buffer);
- break;
- case kChannelTypeInternal:
- incref(channel);
- CREATE_EVENT(channel->events, internal_read_event, 2, channel, buffer);
- success = true;
- break;
+ if (channel->streamtype == kChannelStreamInternal) {
+ channel_incref(channel);
+ CREATE_EVENT(channel->events, internal_read_event, 2, channel, buffer);
+ success = true;
+ } else {
+ Stream *in = channel_instream(channel);
+ success = wstream_write(in, buffer);
}
+
if (!success) {
// If the write failed for any reason, close the channel
char buf[256];
snprintf(buf,
sizeof(buf),
- "Before returning from a RPC call, ch %" PRIu64 " was "
- "closed due to a failed write",
+ "ch %" PRIu64 ": stream write failed. "
+ "RPC canceled; closing channel",
channel->id);
call_set_error(channel, buf, ERROR_LOG_LEVEL);
}
@@ -575,14 +415,14 @@ static void internal_read_event(void **argv)
Channel *channel = argv[0];
WBuffer *buffer = argv[1];
- msgpack_unpacker_reserve_buffer(channel->unpacker, buffer->size);
- memcpy(msgpack_unpacker_buffer(channel->unpacker),
+ msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, buffer->size);
+ memcpy(msgpack_unpacker_buffer(channel->rpc.unpacker),
buffer->data, buffer->size);
- msgpack_unpacker_buffer_consumed(channel->unpacker, buffer->size);
+ msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, buffer->size);
parse_msgpack(channel);
- decref(channel);
+ channel_decref(channel);
wstream_release_wbuffer(buffer);
}
@@ -631,7 +471,8 @@ static void broadcast_event(const char *name, Array args)
Channel *channel;
map_foreach_value(channels, channel, {
- if (pmap_has(cstr_t)(channel->subscribed_events, name)) {
+ if (channel->is_rpc
+ && pmap_has(cstr_t)(channel->rpc.subscribed_events, name)) {
kv_push(subscribed, channel);
}
});
@@ -651,11 +492,7 @@ static void broadcast_event(const char *name, Array args)
for (size_t i = 0; i < kv_size(subscribed); i++) {
Channel *channel = kv_A(subscribed, i);
- if (channel->pending_requests) {
- kv_push(channel->delayed_notifications, buffer);
- } else {
- channel_write(channel, buffer);
- }
+ channel_write(channel, buffer);
}
end:
@@ -665,10 +502,11 @@ end:
static void unsubscribe(Channel *channel, char *event)
{
char *event_string = pmap_get(cstr_t)(event_strings, event);
- pmap_del(cstr_t)(channel->subscribed_events, event_string);
+ pmap_del(cstr_t)(channel->rpc.subscribed_events, event_string);
map_foreach_value(channels, channel, {
- if (pmap_has(cstr_t)(channel->subscribed_events, event_string)) {
+ if (channel->is_rpc
+ && pmap_has(cstr_t)(channel->rpc.subscribed_events, event_string)) {
return;
}
});
@@ -678,90 +516,43 @@ static void unsubscribe(Channel *channel, char *event)
xfree(event_string);
}
-/// Close the channel streams/process and free the channel resources.
-static void close_channel(Channel *channel)
+
+/// Mark rpc state as closed, and release its reference to the channel.
+/// Don't call this directly, call channel_close(id, kChannelPartRpc, &error)
+void rpc_close(Channel *channel)
{
- if (channel->closed) {
+ if (channel->rpc.closed) {
return;
}
- channel->closed = true;
+ channel->rpc.closed = true;
+ channel_decref(channel);
- switch (channel->type) {
- case kChannelTypeSocket:
- stream_close(&channel->data.stream, NULL, NULL);
- break;
- case kChannelTypeProc:
- // Only close the rpc channel part,
- // there could be an error message on the stderr stream
- process_close_in(channel->data.proc);
- process_close_out(channel->data.proc);
- break;
- case kChannelTypeStdio:
- stream_close(&channel->data.std.in, NULL, NULL);
- stream_close(&channel->data.std.out, NULL, NULL);
- multiqueue_put(main_loop.fast_events, exit_event, 1, channel);
- return;
- case kChannelTypeInternal:
- // nothing to free.
- break;
+ if (channel->streamtype == kChannelStreamStdio) {
+ multiqueue_put(main_loop.fast_events, exit_event, 0);
}
-
- decref(channel);
}
static void exit_event(void **argv)
{
- decref(argv[0]);
-
if (!exiting) {
mch_exit(0);
}
}
-static void free_channel(Channel *channel)
+void rpc_free(Channel *channel)
{
remote_ui_disconnect(channel->id);
- pmap_del(uint64_t)(channels, channel->id);
- msgpack_unpacker_free(channel->unpacker);
+ msgpack_unpacker_free(channel->rpc.unpacker);
// Unsubscribe from all events
char *event_string;
- map_foreach_value(channel->subscribed_events, event_string, {
+ map_foreach_value(channel->rpc.subscribed_events, event_string, {
unsubscribe(channel, event_string);
});
- pmap_free(cstr_t)(channel->subscribed_events);
- kv_destroy(channel->call_stack);
- kv_destroy(channel->delayed_notifications);
- if (channel->type != kChannelTypeProc) {
- multiqueue_free(channel->events);
- }
- xfree(channel);
-}
-
-static void close_cb(Stream *stream, void *data)
-{
- decref(data);
-}
-
-static Channel *register_channel(ChannelType type, uint64_t id,
- MultiQueue *events)
-{
- Channel *rv = xmalloc(sizeof(Channel));
- rv->events = events ? events : multiqueue_new_child(main_loop.events);
- rv->type = type;
- rv->refcount = 1;
- rv->closed = false;
- rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
- rv->id = id > 0 ? id : next_chan_id++;
- rv->pending_requests = 0;
- rv->subscribed_events = pmap_new(cstr_t)();
- rv->next_request_id = 1;
- kv_init(rv->call_stack);
- kv_init(rv->delayed_notifications);
- pmap_put(uint64_t)(channels, rv->id, rv);
- return rv;
+ pmap_free(cstr_t)(channel->rpc.subscribed_events);
+ kv_destroy(channel->rpc.call_stack);
}
static bool is_rpc_response(msgpack_object *obj)
@@ -776,15 +567,18 @@ static bool is_rpc_response(msgpack_object *obj)
static bool is_valid_rpc_response(msgpack_object *obj, Channel *channel)
{
uint64_t response_id = obj->via.array.ptr[1].via.u64;
+ if (kv_size(channel->rpc.call_stack) == 0) {
+ return false;
+ }
+
// Must be equal to the frame at the stack's bottom
- return kv_size(channel->call_stack) && response_id
- == kv_A(channel->call_stack, kv_size(channel->call_stack) - 1)->request_id;
+ ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
+ return response_id == frame->request_id;
}
static void complete_call(msgpack_object *obj, Channel *channel)
{
- ChannelCallFrame *frame = kv_A(channel->call_stack,
- kv_size(channel->call_stack) - 1);
+ ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
frame->returned = true;
frame->errored = obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL;
@@ -798,14 +592,15 @@ static void complete_call(msgpack_object *obj, Channel *channel)
static void call_set_error(Channel *channel, char *msg, int loglevel)
{
LOG(loglevel, "RPC: %s", msg);
- for (size_t i = 0; i < kv_size(channel->call_stack); i++) {
- ChannelCallFrame *frame = kv_A(channel->call_stack, i);
+ for (size_t i = 0; i < kv_size(channel->rpc.call_stack); i++) {
+ ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i);
frame->returned = true;
frame->errored = true;
+ api_free_object(frame->result);
frame->result = STRING_OBJ(cstr_to_string(msg));
}
- close_channel(channel);
+ channel_close(channel->id, kChannelPartRpc, NULL);
}
static WBuffer *serialize_request(uint64_t channel_id,
@@ -847,28 +642,6 @@ static WBuffer *serialize_response(uint64_t channel_id,
return rv;
}
-static void send_delayed_notifications(Channel* channel)
-{
- for (size_t i = 0; i < kv_size(channel->delayed_notifications); i++) {
- WBuffer *buffer = kv_A(channel->delayed_notifications, i);
- channel_write(channel, buffer);
- }
-
- kv_size(channel->delayed_notifications) = 0;
-}
-
-static void incref(Channel *channel)
-{
- channel->refcount++;
-}
-
-static void decref(Channel *channel)
-{
- if (!(--channel->refcount)) {
- free_channel(channel);
- }
-}
-
#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
#define REQ "[request] "
#define RES "[response] "
diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h
index f8fe6f129b..9ff5abdc5f 100644
--- a/src/nvim/msgpack_rpc/channel.h
+++ b/src/nvim/msgpack_rpc/channel.h
@@ -8,6 +8,7 @@
#include "nvim/event/socket.h"
#include "nvim/event/process.h"
#include "nvim/vim.h"
+#include "nvim/channel.h"
#define METHOD_MAXLEN 512
@@ -16,6 +17,7 @@
/// of os_inchar(), so they are processed "just-in-time".
MultiQueue *ch_before_blocking_events;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "msgpack_rpc/channel.h.generated.h"
#endif
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
new file mode 100644
index 0000000000..6d8362e8b7
--- /dev/null
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -0,0 +1,36 @@
+#ifndef NVIM_MSGPACK_RPC_CHANNEL_DEFS_H
+#define NVIM_MSGPACK_RPC_CHANNEL_DEFS_H
+
+#include <stdbool.h>
+#include <uv.h>
+#include <msgpack.h>
+
+#include "nvim/api/private/defs.h"
+#include "nvim/event/socket.h"
+#include "nvim/event/process.h"
+#include "nvim/vim.h"
+
+typedef struct Channel Channel;
+
+typedef struct {
+ uint64_t request_id;
+ bool returned, errored;
+ Object result;
+} ChannelCallFrame;
+
+typedef struct {
+ Channel *channel;
+ MsgpackRpcRequestHandler handler;
+ Array args;
+ uint64_t request_id;
+} RequestEvent;
+
+typedef struct {
+ PMap(cstr_t) *subscribed_events;
+ bool closed;
+ msgpack_unpacker *unpacker;
+ uint64_t next_request_id;
+ kvec_t(ChannelCallFrame *) call_stack;
+} RpcState;
+
+#endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 444c6cc256..fecae11d45 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -88,7 +88,12 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
{
bool ret = true;
kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE;
- kv_push(stack, ((MPToAPIObjectStackItem) { obj, arg, false, 0 }));
+ kv_push(stack, ((MPToAPIObjectStackItem) {
+ .mobj = obj,
+ .aobj = arg,
+ .container = false,
+ .idx = 0,
+ }));
while (ret && kv_size(stack)) {
MPToAPIObjectStackItem cur = kv_last(stack);
if (!cur.container) {
@@ -361,7 +366,7 @@ typedef struct {
size_t idx;
} APIToMPObjectStackItem;
-/// Convert type used by Neovim API to msgpack
+/// Convert type used by Nvim API to msgpack type.
///
/// @param[in] result Object to convert.
/// @param[out] res Structure that defines where conversion results are saved.
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 1e0cc27886..9bf122f4db 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -180,6 +180,7 @@ int server_start(const char *endpoint)
void server_stop(char *endpoint)
{
SocketWatcher *watcher;
+ bool watcher_found = false;
char addr[ADDRESS_MAX_SIZE];
// Trim to `ADDRESS_MAX_SIZE`
@@ -189,11 +190,12 @@ void server_stop(char *endpoint)
for (; i < watchers.ga_len; i++) {
watcher = ((SocketWatcher **)watchers.ga_data)[i];
if (strcmp(addr, watcher->addr) == 0) {
+ watcher_found = true;
break;
}
}
- if (i >= watchers.ga_len) {
+ if (!watcher_found) {
ELOG("Not listening on %s", addr);
return;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 39cd2c6631..e4310de5d8 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -13,6 +13,7 @@
#include <stdbool.h>
#include <stdlib.h>
+#include "nvim/log.h"
#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/normal.h"
@@ -344,8 +345,7 @@ static const struct nv_cmd {
{ K_F8, farsi_f8, 0, 0 },
{ K_F9, farsi_f9, 0, 0 },
{ K_EVENT, nv_event, NV_KEEPREG, 0 },
- { K_FOCUSGAINED, nv_focusgained, NV_KEEPREG, 0 },
- { K_FOCUSLOST, nv_focuslost, NV_KEEPREG, 0 },
+ { K_COMMAND, nv_colon, 0, 0 },
};
/* Number of commands in nv_cmds[]. */
@@ -1451,9 +1451,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
/* Never redo "zf" (define fold). */
if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK)
&& ((!VIsual_active || oap->motion_force)
- /* Also redo Operator-pending Visual mode mappings */
- || (VIsual_active && cap->cmdchar == ':'
- && oap->op_type != OP_COLON))
+ // Also redo Operator-pending Visual mode mappings.
+ || (cap->cmdchar == ':' && oap->op_type != OP_COLON))
&& cap->cmdchar != 'D'
&& oap->op_type != OP_FOLD
&& oap->op_type != OP_FOLDOPEN
@@ -1475,13 +1474,13 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
AppendToRedobuffLit(cap->searchbuf, -1);
}
AppendToRedobuff(NL_STR);
- } else if (cap->cmdchar == ':') {
- /* do_cmdline() has stored the first typed line in
- * "repeat_cmdline". When several lines are typed repeating
- * won't be possible. */
- if (repeat_cmdline == NULL)
+ } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
+ // do_cmdline() has stored the first typed line in
+ // "repeat_cmdline". When several lines are typed repeating
+ // won't be possible.
+ if (repeat_cmdline == NULL) {
ResetRedobuff();
- else {
+ } else {
AppendToRedobuffLit(repeat_cmdline, -1);
AppendToRedobuff(NL_STR);
xfree(repeat_cmdline);
@@ -1550,8 +1549,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
}
oap->start = VIsual;
- if (VIsual_mode == 'V')
+ if (VIsual_mode == 'V') {
oap->start.col = 0;
+ oap->start.coladd = 0;
+ }
}
/*
@@ -1637,16 +1638,22 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
/* Prepare for redoing. Only use the nchar field for "r",
* otherwise it might be the second char of the operator. */
if (cap->cmdchar == 'g' && (cap->nchar == 'n'
- || cap->nchar == 'N'))
+ || cap->nchar == 'N')) {
prep_redo(oap->regname, cap->count0,
- get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
- oap->motion_force, cap->cmdchar, cap->nchar);
- else if (cap->cmdchar != ':')
- prep_redo(oap->regname, 0L, NUL, 'v',
- get_op_char(oap->op_type),
- get_extra_op_char(oap->op_type),
- oap->op_type == OP_REPLACE
- ? cap->nchar : NUL);
+ get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
+ oap->motion_force, cap->cmdchar, cap->nchar);
+ } else if (cap->cmdchar != ':') {
+ int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
+
+ // reverse what nv_replace() did
+ if (nchar == REPLACE_CR_NCHAR) {
+ nchar = CAR;
+ } else if (nchar == REPLACE_NL_NCHAR) {
+ nchar = NL;
+ }
+ prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
+ get_extra_op_char(oap->op_type), nchar);
+ }
if (!redo_VIsual_busy) {
redo_VIsual_mode = resel_VIsual_mode;
redo_VIsual_vcol = resel_VIsual_vcol;
@@ -1944,8 +1951,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
* the lines. */
auto_format(false, true);
- if (restart_edit == 0)
+ if (restart_edit == 0) {
restart_edit = restart_edit_save;
+ } else {
+ cap->retval |= CA_COMMAND_BUSY;
+ }
}
break;
@@ -3658,6 +3668,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
@@ -3684,6 +3727,7 @@ find_decl (
bool retval = true;
bool incll;
int searchflags = flags_arg;
+ bool valid;
pat = xmalloc(len + 7);
@@ -3718,6 +3762,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)
@@ -3748,20 +3793,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;
}
@@ -4465,23 +4525,22 @@ static void nv_exmode(cmdarg_T *cap)
}
}
-/*
- * Handle a ":" command.
- */
+/// Handle a ":" command and <Cmd>.
static void nv_colon(cmdarg_T *cap)
{
int old_p_im;
bool cmd_result;
+ bool is_cmdkey = cap->cmdchar == K_COMMAND;
- if (VIsual_active)
+ if (VIsual_active && !is_cmdkey) {
nv_operator(cap);
- else {
+ } else {
if (cap->oap->op_type != OP_NOP) {
// Using ":" as a movement is characterwise exclusive.
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
- } else if (cap->count0) {
- /* translate "count:" into ":.,.+(count - 1)" */
+ } else if (cap->count0 && !is_cmdkey) {
+ // translate "count:" into ":.,.+(count - 1)"
stuffcharReadbuff('.');
if (cap->count0 > 1) {
stuffReadbuff(",.+");
@@ -4495,9 +4554,9 @@ static void nv_colon(cmdarg_T *cap)
old_p_im = p_im;
- /* get a command line and execute it */
- cmd_result = do_cmdline(NULL, getexline, NULL,
- cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
+ // get a command line and execute it
+ cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
+ cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
/* If 'insertmode' changed, enter or exit Insert mode */
if (p_im != old_p_im) {
@@ -4978,26 +5037,21 @@ static void nv_right(cmdarg_T *cap)
if ((!PAST_LINE && oneright() == false)
|| (PAST_LINE && *get_cursor_pos_ptr() == NUL)
) {
- /*
- * <Space> wraps to next line if 'whichwrap' has 's'.
- * 'l' wraps to next line if 'whichwrap' has 'l'.
- * CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
- */
- if ( ((cap->cmdchar == ' '
- && vim_strchr(p_ww, 's') != NULL)
- || (cap->cmdchar == 'l'
- && vim_strchr(p_ww, 'l') != NULL)
- || (cap->cmdchar == K_RIGHT
- && vim_strchr(p_ww, '>') != NULL))
- && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- /* When deleting we also count the NL as a character.
- * Set cap->oap->inclusive when last char in the line is
- * included, move to next line after that */
- if ( cap->oap->op_type != OP_NOP
- && !cap->oap->inclusive
- && !lineempty(curwin->w_cursor.lnum))
+ // <Space> wraps to next line if 'whichwrap' has 's'.
+ // 'l' wraps to next line if 'whichwrap' has 'l'.
+ // CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
+ if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL)
+ || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL)
+ || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL))
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ // When deleting we also count the NL as a character.
+ // Set cap->oap->inclusive when last char in the line is
+ // included, move to next line after that
+ if (cap->oap->op_type != OP_NOP
+ && !cap->oap->inclusive
+ && !LINEEMPTY(curwin->w_cursor.lnum)) {
cap->oap->inclusive = true;
- else {
+ } else {
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
@@ -5007,12 +5061,14 @@ static void nv_right(cmdarg_T *cap)
continue;
}
if (cap->oap->op_type == OP_NOP) {
- /* Only beep and flush if not moved at all */
- if (n == cap->count1)
+ // Only beep and flush if not moved at all
+ if (n == cap->count1) {
beep_flush();
+ }
} else {
- if (!lineempty(curwin->w_cursor.lnum))
+ if (!LINEEMPTY(curwin->w_cursor.lnum)) {
cap->oap->inclusive = true;
+ }
}
break;
} else if (PAST_LINE) {
@@ -5070,13 +5126,12 @@ static void nv_left(cmdarg_T *cap)
coladvance((colnr_T)MAXCOL);
curwin->w_set_curswant = true;
- /* When the NL before the first char has to be deleted we
- * put the cursor on the NUL after the previous line.
- * This is a very special case, be careful!
- * Don't adjust op_end now, otherwise it won't work. */
- if ( (cap->oap->op_type == OP_DELETE
- || cap->oap->op_type == OP_CHANGE)
- && !lineempty(curwin->w_cursor.lnum)) {
+ // When the NL before the first char has to be deleted we
+ // put the cursor on the NUL after the previous line.
+ // This is a very special case, be careful!
+ // Don't adjust op_end now, otherwise it won't work.
+ if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE)
+ && !LINEEMPTY(curwin->w_cursor.lnum)) {
char_u *cp = get_cursor_pos_ptr();
if (*cp != NUL) {
@@ -5175,12 +5230,12 @@ static void nv_gotofile(cmdarg_T *cap)
if (ptr != NULL) {
// do autowrite if necessary
- if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) {
+ if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf)) {
(void)autowrite(curbuf, false);
}
setpcmark();
(void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
- P_HID(curbuf) ? ECMD_HIDE : 0, curwin);
+ buf_hide(curbuf) ? ECMD_HIDE : 0, curwin);
if (cap->nchar == 'F' && lnum >= 0) {
curwin->w_cursor.lnum = lnum;
check_cursor_lnum();
@@ -5634,6 +5689,8 @@ static void nv_brackets(cmdarg_T *cap)
cap->nchar == 's', false, NULL) == 0) {
clearopbeep(cap->oap);
break;
+ } else {
+ curwin->w_set_curswant = true;
}
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
foldOpenCursor();
@@ -5803,10 +5860,13 @@ static void nv_replace(cmdarg_T *cap)
if (got_int)
reset_VIsual();
if (had_ctrl_v) {
- if (cap->nchar == '\r')
- cap->nchar = -1;
- else if (cap->nchar == '\n')
- cap->nchar = -2;
+ // Use a special (negative) number to make a difference between a
+ // literal CR or NL and a line break.
+ if (cap->nchar == CAR) {
+ cap->nchar = REPLACE_CR_NCHAR;
+ } else if (cap->nchar == NL) {
+ cap->nchar = REPLACE_NL_NCHAR;
+ }
}
nv_operator(cap);
return;
@@ -6043,10 +6103,11 @@ static void n_swapchar(cmdarg_T *cap)
pos_T startpos;
int did_change = 0;
- if (checkclearopq(cap->oap))
+ if (checkclearopq(cap->oap)) {
return;
+ }
- if (lineempty(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
+ if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
clearopbeep(cap->oap);
return;
}
@@ -6209,15 +6270,18 @@ static void nv_gomark(cmdarg_T *cap)
} else
nv_cursormark(cap, cap->arg, pos);
- /* May need to clear the coladd that a mark includes. */
- if (!virtual_active())
+ // May need to clear the coladd that a mark includes.
+ if (!virtual_active()) {
curwin->w_cursor.coladd = 0;
+ }
+ check_cursor_col();
if (cap->oap->op_type == OP_NOP
&& pos != NULL
&& (pos == (pos_T *)-1 || !equalpos(old_cursor, *pos))
&& (fdo_flags & FDO_MARK)
- && old_KeyTyped)
+ && old_KeyTyped) {
foldOpenCursor();
+ }
}
/*
@@ -7908,18 +7972,7 @@ static void nv_event(cmdarg_T *cap)
may_garbage_collect = false;
multiqueue_process_events(main_loop.events);
cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
-}
-
-/// Trigger FocusGained event.
-static void nv_focusgained(cmdarg_T *cap)
-{
- apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
-}
-
-/// Trigger FocusLost event.
-static void nv_focuslost(cmdarg_T *cap)
-{
- apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
+ finish_op = false;
}
/*
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 5c6f4d0d07..b421d81b7e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -55,12 +55,11 @@ static yankreg_T y_regs[NUM_REGISTERS];
static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */
-static bool clipboard_didwarn_unnamed = false;
-
// for behavior between start_batch_changes() and end_batch_changes())
-static bool clipboard_delay_update = false; // delay clipboard update
static int batch_change_count = 0; // inside a script
+static bool clipboard_delay_update = false; // delay clipboard update
static bool clipboard_needs_update = false; // clipboard was updated
+static bool clipboard_didwarn = false;
/*
* structure used by block_prep, op_delete and op_yank for blockwise operators
@@ -1630,13 +1629,18 @@ int op_replace(oparg_T *oap, int c)
colnr_T oldlen;
struct block_def bd;
char_u *after_p = NULL;
- int had_ctrl_v_cr = (c == -1 || c == -2);
+ int had_ctrl_v_cr = false;
if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty)
return OK; /* nothing to do */
- if (had_ctrl_v_cr)
- c = (c == -1 ? '\r' : '\n');
+ if (c == REPLACE_CR_NCHAR) {
+ had_ctrl_v_cr = true;
+ c = CAR;
+ } else if (c == REPLACE_NL_NCHAR) {
+ had_ctrl_v_cr = true;
+ c = NL;
+ }
if (has_mbyte)
mb_adjust_opend(oap);
@@ -1714,7 +1718,7 @@ int op_replace(oparg_T *oap, int c)
// insert pre-spaces
memset(newp + bd.textcol, ' ', (size_t)bd.startspaces);
// insert replacement chars CHECK FOR ALLOCATED SPACE
- // -1/-2 is used for entering CR literally.
+ // REPLACE_CR_NCHAR/REPLACE_NL_NCHAR is used for entering CR literally.
size_t after_p_len = 0;
if (had_ctrl_v_cr || (c != '\r' && c != '\n')) {
// strlen(newp) at this point
@@ -2053,15 +2057,16 @@ void op_insert(oparg_T *oap, long count1)
curwin->w_cursor = oap->end;
check_cursor_col();
- /* Works just like an 'i'nsert on the next character. */
- if (!lineempty(curwin->w_cursor.lnum)
- && oap->start_vcol != oap->end_vcol)
+ // Works just like an 'i'nsert on the next character.
+ if (!LINEEMPTY(curwin->w_cursor.lnum)
+ && oap->start_vcol != oap->end_vcol) {
inc_cursor();
+ }
}
}
t1 = oap->start;
- edit(NUL, false, (linenr_T)count1);
+ (void)edit(NUL, false, (linenr_T)count1);
// When a tab was inserted, and the characters in front of the tab
// have been converted to a tab as well, the column of the cursor
@@ -2181,9 +2186,10 @@ int op_change(oparg_T *oap)
} else if (op_delete(oap) == FAIL)
return FALSE;
- if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum)
- && !virtual_op)
+ if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum)
+ && !virtual_op) {
inc_cursor();
+ }
// check for still on same line (<CR> in inserted text meaningless)
// skip blank lines too
@@ -2565,11 +2571,11 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
dict_T *dict = get_vim_var_dict(VV_EVENT);
// the yanked text
- list_T *list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
- list->lv_lock = VAR_FIXED;
+ tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
// the register type
@@ -2736,7 +2742,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// Autocommands may be executed when saving lines for undo, which may make
// y_array invalid. Start undo now to avoid that.
if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) {
- ELOG(_("Failed to save undo information"));
+ ELOG("Failed to save undo information");
return;
}
}
@@ -2856,25 +2862,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
} else if (y_type == kMTLineWise) {
lnum = curwin->w_cursor.lnum;
- /* Correct line number for closed fold. Don't move the cursor yet,
- * u_save() uses it. */
- if (dir == BACKWARD)
+ // Correct line number for closed fold. Don't move the cursor yet,
+ // u_save() uses it.
+ if (dir == BACKWARD) {
(void)hasFolding(lnum, &lnum, NULL);
- else
+ } else {
(void)hasFolding(lnum, NULL, &lnum);
- if (dir == FORWARD)
- ++lnum;
- /* In an empty buffer the empty line is going to be replaced, include
- * it in the saved lines. */
- if ((bufempty() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL)
+ }
+ if (dir == FORWARD) {
+ lnum++;
+ }
+ // In an empty buffer the empty line is going to be replaced, include
+ // it in the saved lines.
+ if ((BUFEMPTY() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) {
goto end;
- if (dir == FORWARD)
+ }
+ if (dir == FORWARD) {
curwin->w_cursor.lnum = lnum - 1;
- else
+ } else {
curwin->w_cursor.lnum = lnum;
- curbuf->b_op_start = curwin->w_cursor; /* for mark_adjust() */
- } else if (u_save_cursor() == FAIL)
+ }
+ curbuf->b_op_start = curwin->w_cursor; // for mark_adjust()
+ } else if (u_save_cursor() == FAIL) {
goto end;
+ }
yanklen = (int)STRLEN(y_array[0]);
@@ -3067,15 +3078,26 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
--lnum;
new_cursor = curwin->w_cursor;
- /*
- * simple case: insert into current line
- */
+ // simple case: insert into current line
if (y_type == kMTCharWise && y_size == 1) {
+ linenr_T end_lnum = 0; // init for gcc
+
+ if (VIsual_active) {
+ end_lnum = curbuf->b_visual.vi_end.lnum;
+ if (end_lnum < curbuf->b_visual.vi_start.lnum) {
+ end_lnum = curbuf->b_visual.vi_start.lnum;
+ }
+ }
+
do {
totlen = (size_t)(count * yanklen);
if (totlen > 0) {
oldp = ml_get(lnum);
- newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
+ if (VIsual_active && col > (int)STRLEN(oldp)) {
+ lnum++;
+ continue;
+ }
+ newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
memmove(newp, oldp, (size_t)col);
ptr = newp + col;
for (i = 0; i < (size_t)count; i++) {
@@ -3091,11 +3113,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.col += (colnr_T)(totlen - 1);
}
}
- if (VIsual_active)
+ if (VIsual_active) {
lnum++;
- } while (VIsual_active
- && (lnum <= curbuf->b_visual.vi_end.lnum
- || lnum <= curbuf->b_visual.vi_start.lnum));
+ }
+ } while (VIsual_active && lnum <= end_lnum);
if (VIsual_active) { /* reset lnum to the last visual line */
lnum--;
@@ -3178,9 +3199,9 @@ error:
curbuf->b_op_start.lnum++;
}
// Skip mark_adjust when adding lines after the last one, there
- // can't be marks there.
+ // can't be marks there. But still needed in diff mode.
if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
- < curbuf->b_ml.ml_line_count) {
+ < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
(linenr_T)MAXLNUM, nr_lines, 0L, false);
}
@@ -3998,7 +4019,7 @@ format_lines (
&& (do_second_indent || do_number_indent)
&& prev_is_end_par
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (do_second_indent && !lineempty(curwin->w_cursor.lnum + 1)) {
+ if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
if (leader_len == 0 && next_leader_len == 0) {
/* no comment found */
second_indent =
@@ -4855,9 +4876,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags)
if (!(flags & kGRegList)) {
return s;
}
- list_T *list = tv_list_alloc();
- tv_list_append_string(list, NULL, 0);
- list->lv_first->li_tv.vval.v_string = s;
+ list_T *const list = tv_list_alloc(1);
+ tv_list_append_allocated_string(list, (char *)s);
return list;
}
@@ -4906,7 +4926,7 @@ void *get_reg_contents(int regname, int flags)
return NULL;
if (flags & kGRegList) {
- list_T *list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
@@ -5524,7 +5544,7 @@ int get_default_register_name(void)
}
/// Determine if register `*name` should be used as a clipboard.
-/// In an unnammed operation, `*name` is `NUL` and will be adjusted to `'*'/'+'` if
+/// In an unnamed operation, `*name` is `NUL` and will be adjusted to */+ if
/// `clipboard=unnamed[plus]` is set.
///
/// @param name The name of register, or `NUL` if unnamed.
@@ -5535,33 +5555,41 @@ int get_default_register_name(void)
/// if the register isn't a clipboard or provider isn't available.
static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
{
- if (*name == '*' || *name == '+') {
- if(!eval_has_provider("clipboard")) {
- if (!quiet) {
- EMSG("clipboard: No provider. Try \":CheckHealth\" or "
- "\":h clipboard\".");
- }
- return NULL;
- }
- return &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
- } else if ((*name == NUL) && (cb_flags & CB_UNNAMEDMASK)) {
- if(!eval_has_provider("clipboard")) {
- if (!quiet && !clipboard_didwarn_unnamed) {
- msg((char_u *)"clipboard: No provider. Try \":CheckHealth\" or "
- "\":h clipboard\".");
- clipboard_didwarn_unnamed = true;
- }
- return NULL;
+#define MSG_NO_CLIP "clipboard: No provider. " \
+ "Try \":checkhealth\" or \":h clipboard\"."
+
+ yankreg_T *target = NULL;
+ bool explicit_cb_reg = (*name == '*' || *name == '+');
+ bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK);
+ if (!explicit_cb_reg && !implicit_cb_reg) {
+ goto end;
+ }
+
+ if (!eval_has_provider("clipboard")) {
+ if (batch_change_count == 1 && !quiet
+ && (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) {
+ clipboard_didwarn = true;
+ // Do NOT error (emsg()) here--if it interrupts :redir we get into
+ // a weird state, stuck in "redirect mode".
+ msg((char_u *)MSG_NO_CLIP);
}
+ // ... else, be silent (don't flood during :while, :redir, etc.).
+ goto end;
+ }
+
+ if (explicit_cb_reg) {
+ target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
+ goto end;
+ } else { // unnamed register: "implicit" clipboard
if (writing && clipboard_delay_update) {
+ // For "set" (copy), defer the clipboard call.
clipboard_needs_update = true;
- return NULL;
+ goto end;
} else if (!writing && clipboard_needs_update) {
- // use the internal value
- return NULL;
+ // For "get" (paste), use the internal value.
+ goto end;
}
- yankreg_T *target;
if (cb_flags & CB_UNNAMEDPLUS) {
*name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';
target = &y_regs[PLUS_REGISTER];
@@ -5569,10 +5597,11 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
*name = '*';
target = &y_regs[STAR_REGISTER];
}
- return target; // unnamed register
+ goto end;
}
- // don't do anything for other register names
- return NULL;
+
+end:
+ return target;
}
static bool get_clipboard(int name, yankreg_T **target, bool quiet)
@@ -5586,7 +5615,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
}
free_register(reg);
- list_T *const args = tv_list_alloc();
+ list_T *const args = tv_list_alloc(1);
const char regname = (char)name;
tv_list_append_string(args, &regname, 1);
@@ -5602,13 +5631,14 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
list_T *res = result.vval.v_list;
list_T *lines = NULL;
- if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) {
- lines = res->lv_first->li_tv.vval.v_list;
- if (res->lv_last->li_tv.v_type != VAR_STRING) {
+ if (tv_list_len(res) == 2
+ && TV_LIST_ITEM_TV(tv_list_first(res))->v_type == VAR_LIST) {
+ lines = TV_LIST_ITEM_TV(tv_list_first(res))->vval.v_list;
+ if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) {
goto err;
}
- char_u *regtype = res->lv_last->li_tv.vval.v_string;
- if (regtype == NULL || strlen((char*)regtype) > 1) {
+ char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
+ if (regtype == NULL || strlen((char *)regtype) > 1) {
goto err;
}
switch (regtype[0]) {
@@ -5633,20 +5663,21 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
reg->y_type = kMTUnknown;
}
- reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *));
- reg->y_size = (size_t)lines->lv_len;
+ reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *));
+ reg->y_size = (size_t)tv_list_len(lines);
reg->additional_data = NULL;
reg->timestamp = 0;
// Timestamp is not saved for clipboard registers because clipboard registers
// are not saved in the ShaDa file.
int i = 0;
- for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING) {
+ TV_LIST_ITER_CONST(lines, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
goto err;
}
- reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string);
- }
+ reg->y_array[i++] = (char_u *)xstrdupnul(
+ (const char *)TV_LIST_ITEM_TV(li)->vval.v_string);
+ });
if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) {
// a known-to-be charwise yank might have a final linebreak
@@ -5703,15 +5734,13 @@ static void set_clipboard(int name, yankreg_T *reg)
return;
}
- list_T *lines = tv_list_alloc();
+ list_T *const lines = tv_list_alloc(
+ (ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise));
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(lines, (const char *)reg->y_array[i], -1);
}
- list_T *args = tv_list_alloc();
- tv_list_append_list(args, lines);
-
char regtype;
switch (reg->y_type) {
case kMTLineWise: {
@@ -5732,25 +5761,25 @@ static void set_clipboard(int name, yankreg_T *reg)
assert(false);
}
}
- tv_list_append_string(args, &regtype, 1);
- const char regname = (char)name;
- tv_list_append_string(args, &regname, 1);
+ list_T *args = tv_list_alloc(3);
+ tv_list_append_list(args, lines);
+ tv_list_append_string(args, &regtype, 1);
+ tv_list_append_string(args, ((char[]) { (char)name }), 1);
(void)eval_call_provider("clipboard", "set", args);
}
-/// Avoid clipboard (slow) during batch operations (i.e., a script).
+/// Avoid slow things (clipboard) during batch operations (while/for-loops).
void start_batch_changes(void)
{
if (++batch_change_count > 1) {
return;
}
clipboard_delay_update = true;
- clipboard_needs_update = false;
}
-/// Update the clipboard after batch changes finished.
+/// Counterpart to start_batch_changes().
void end_batch_changes(void)
{
if (--batch_change_count > 0) {
@@ -5759,11 +5788,37 @@ void end_batch_changes(void)
}
clipboard_delay_update = false;
if (clipboard_needs_update) {
+ // must be before, as set_clipboard will invoke
+ // start/end_batch_changes recursively
+ clipboard_needs_update = false;
+ // unnamed ("implicit" clipboard)
set_clipboard(NUL, y_previous);
+ }
+}
+
+int save_batch_count(void)
+{
+ int save_count = batch_change_count;
+ batch_change_count = 0;
+ clipboard_delay_update = false;
+ if (clipboard_needs_update) {
clipboard_needs_update = false;
+ // unnamed ("implicit" clipboard)
+ set_clipboard(NUL, y_previous);
+ }
+ return save_count;
+}
+
+void restore_batch_count(int save_count)
+{
+ assert(batch_change_count == 0);
+ batch_change_count = save_count;
+ if (batch_change_count > 0) {
+ clipboard_delay_update = true;
}
}
+
/// Check whether register is empty
static inline bool reg_empty(const yankreg_T *const reg)
FUNC_ATTR_PURE
diff --git a/src/nvim/option.c b/src/nvim/option.c
index a4be2abf9a..41cfdf9856 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -75,7 +75,9 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/os/input.h"
+#include "nvim/os/lang.h"
/*
* The options that are local to a window or buffer have "indir" set to one of
@@ -106,6 +108,9 @@ typedef enum {
*/
#define VAR_WIN ((char_u *)-1)
+static char *p_term = NULL;
+static char *p_ttytype = NULL;
+
/*
* These are the global values for options which are also local to a buffer.
* Only to be used in option.c!
@@ -116,6 +121,7 @@ static int p_bomb;
static char_u *p_bh;
static char_u *p_bt;
static int p_bl;
+static long p_channel;
static int p_ci;
static int p_cin;
static char_u *p_cink;
@@ -243,6 +249,8 @@ typedef struct vimoption {
#define P_NO_DEF_EXP 0x8000000U ///< Do not expand default value.
#define P_RWINONLY 0x10000000U ///< only redraw current window
+#define P_NDNAME 0x20000000U ///< only normal dir name chars allowed
+#define P_UI_OPTION 0x40000000U ///< send option to remote ui
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \
@@ -780,6 +788,8 @@ void set_init_1(void)
didset_options2();
+ lang_init();
+
// enc_locale() will try to find the encoding of the current locale.
// This will be used when 'default' is used as encoding specifier
// in 'fileencodings'
@@ -822,24 +832,26 @@ set_option_default (
if (flags & P_STRING) {
/* Use set_string_option_direct() for local options to handle
* freeing and allocating the value. */
- if (options[opt_idx].indir != PV_NONE)
+ if (options[opt_idx].indir != PV_NONE) {
set_string_option_direct(NULL, opt_idx,
- options[opt_idx].def_val[dvi], opt_flags, 0);
- else {
- if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED))
+ options[opt_idx].def_val[dvi], opt_flags, 0);
+ } else {
+ if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
free_string_option(*(char_u **)(varp));
+ }
*(char_u **)varp = options[opt_idx].def_val[dvi];
options[opt_idx].flags &= ~P_ALLOCED;
}
} else if (flags & P_NUM) {
- if (options[opt_idx].indir == PV_SCROLL)
+ if (options[opt_idx].indir == PV_SCROLL) {
win_comp_scroll(curwin);
- else {
- *(long *)varp = (long)options[opt_idx].def_val[dvi];
- /* May also set global value for local option. */
- if (both)
+ } else {
+ *(long *)varp = (long)(intptr_t)options[opt_idx].def_val[dvi];
+ // May also set global value for local option.
+ if (both) {
*(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
*(long *)varp;
+ }
}
} else { /* P_BOOL */
*(int *)varp = (int)(intptr_t)options[opt_idx].def_val[dvi];
@@ -917,7 +929,7 @@ void set_number_default(char *name, long val)
opt_idx = findoption(name);
if (opt_idx >= 0) {
- options[opt_idx].def_val[VI_DEFAULT] = (char_u *)val;
+ options[opt_idx].def_val[VI_DEFAULT] = (char_u *)(intptr_t)val;
}
}
@@ -970,10 +982,13 @@ void set_init_2(bool headless)
p_window = Rows - 1;
}
set_number_default("window", Rows - 1);
+#if 0
+ // This bodges around problems that should be fixed in the TUI layer.
if (!headless && !os_term_is_nice()) {
set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"",
OPT_GLOBAL, SID_NONE);
}
+#endif
parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor'
(void)parse_printoptions(); // parse 'printoptions' default value
}
@@ -1045,7 +1060,7 @@ void set_init_3(void)
xfree(p);
}
- if (bufempty()) {
+ if (BUFEMPTY()) {
int idx_ffs = findoption_len(S_LEN("ffs"));
// Apply the first entry of 'fileformats' to the initial buffer.
@@ -1178,15 +1193,12 @@ do_set (
set_options_default(OPT_FREE | opt_flags);
didset_options();
didset_options2();
+ ui_refresh_options();
redraw_all_later(CLEAR);
} else {
showoptions(1, opt_flags);
did_show = TRUE;
}
- } else if (STRNCMP(arg, "termcap",
- 7) == 0 && !(opt_flags & OPT_MODELINE)) {
- did_show = TRUE;
- arg += 7;
} else {
prefix = 1;
if (STRNCMP(arg, "no", 2) == 0) {
@@ -1431,20 +1443,19 @@ do_set (
* [-]0-9 set number
* other error
*/
- ++arg;
- if (nextchar == '&')
- value = (long)options[opt_idx].def_val[
- ((flags & P_VI_DEF) || cp_val)
- ? VI_DEFAULT : VIM_DEFAULT];
- else if (nextchar == '<') {
- /* For 'undolevels' NO_LOCAL_UNDOLEVEL means to
- * use the global value. */
- if ((long *)varp == &curbuf->b_p_ul
- && opt_flags == OPT_LOCAL)
+ arg++;
+ if (nextchar == '&') {
+ value = (long)(intptr_t)options[opt_idx].def_val[
+ ((flags & P_VI_DEF) || cp_val) ? VI_DEFAULT : VIM_DEFAULT];
+ } else if (nextchar == '<') {
+ // For 'undolevels' NO_LOCAL_UNDOLEVEL means to
+ // use the global value.
+ if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
value = NO_LOCAL_UNDOLEVEL;
- else
+ } else {
value = *(long *)get_varp_scope(
&(options[opt_idx]), OPT_GLOBAL);
+ }
} else if (((long *)varp == &p_wc
|| (long *)varp == &p_wcm)
&& (*arg == '<'
@@ -1487,6 +1498,7 @@ do_set (
char_u *newval;
char_u *origval = NULL;
char *saved_origval = NULL;
+ char *saved_newval = NULL;
unsigned newlen;
int comma;
int bs;
@@ -1503,7 +1515,17 @@ do_set (
/* The old value is kept until we are sure that the
* new value is valid. */
oldval = *(char_u **)varp;
- if (nextchar == '&') { /* set to default val */
+
+ // When setting the local value of a global
+ // option, the old value may be the global value.
+ if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags
+ & OPT_LOCAL)) {
+ origval = *(char_u **)get_varp(&options[opt_idx]);
+ } else {
+ origval = oldval;
+ }
+
+ if (nextchar == '&') { // set to default val
newval = options[opt_idx].def_val[
((flags & P_VI_DEF) || cp_val)
? VI_DEFAULT : VIM_DEFAULT];
@@ -1562,6 +1584,9 @@ do_set (
break;
}
xfree(oldval);
+ if (origval == oldval) {
+ origval = *(char_u **)varp;
+ }
oldval = *(char_u **)varp;
}
/*
@@ -1598,15 +1623,6 @@ do_set (
++arg;
}
- /* When setting the local value of a global
- * option, the old value may be the global value. */
- if (((int)options[opt_idx].indir & PV_BOTH)
- && (opt_flags & OPT_LOCAL))
- origval = *(char_u **)get_varp(
- &options[opt_idx]);
- else
- origval = oldval;
-
/*
* Copy the new string into allocated memory.
* Can't use set_string_option_direct(), because
@@ -1750,7 +1766,7 @@ do_set (
if (flags & P_FLAGLIST) {
// Remove flags that appear twice.
- for (s = newval; *s; s++) {
+ for (s = newval; *s;) {
// if options have P_FLAGLIST and P_ONECOMMA such as
// 'whichwrap'
if (flags & P_ONECOMMA) {
@@ -1758,15 +1774,16 @@ do_set (
&& vim_strchr(s + 2, *s) != NULL) {
// Remove the duplicated value and the next comma.
STRMOVE(s, s + 2);
- s -= 2;
+ continue;
}
} else {
if ((!(flags & P_COMMA) || *s != ',')
&& vim_strchr(s + 1, *s) != NULL) {
STRMOVE(s, s + 1);
- s--;
+ continue;
}
}
+ s++;
}
}
@@ -1775,39 +1792,37 @@ do_set (
new_value_alloced = TRUE;
}
- /* Set the new value. */
+ // Set the new value.
*(char_u **)(varp) = newval;
- if (!starting && origval != NULL) {
+ if (!starting && origval != NULL && newval != NULL) {
// origval may be freed by
// did_set_string_option(), make a copy.
- saved_origval = xstrdup((char *) origval);
+ saved_origval = xstrdup((char *)origval);
+ // newval (and varp) may become invalid if the
+ // buffer is closed by autocommands.
+ saved_newval = xstrdup((char *)newval);
}
- /* Handle side effects, and set the global value for
- * ":set" on local options. */
+ // Handle side effects, and set the global value for
+ // ":set" on local options. Note: when setting 'syntax'
+ // or 'filetype' autocommands may be triggered that can
+ // cause havoc.
errmsg = did_set_string_option(opt_idx, (char_u **)varp,
new_value_alloced, oldval, errbuf, opt_flags);
+ if (errmsg == NULL) {
+ trigger_optionsset_string(opt_idx, opt_flags, saved_origval,
+ saved_newval);
+ }
+ xfree(saved_origval);
+ xfree(saved_newval);
+
// If error detected, print the error message.
if (errmsg != NULL) {
- xfree(saved_origval);
goto skip;
}
- if (saved_origval != NULL) {
- char buf_type[7];
- vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1);
- set_vim_var_string(VV_OPTION_OLD, saved_origval, -1);
- set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- apply_autocmds(EVENT_OPTIONSET,
- (char_u *)options[opt_idx].fullname,
- NULL, false, NULL);
- reset_v_option_vars();
- xfree(saved_origval);
- }
} else {
// key code option(FIXME(tarruda): Show a warning or something
// similar)
@@ -2202,6 +2217,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_tsr);
check_string_option(&buf->b_p_lw);
check_string_option(&buf->b_p_bkc);
+ check_string_option(&buf->b_p_menc);
}
/*
@@ -2243,7 +2259,7 @@ int was_set_insecurely(char_u *opt, int opt_flags)
uint32_t *flagp = insecure_flag(idx, opt_flags);
return (*flagp & P_INSECURE) != 0;
}
- EMSG2(_(e_intern2), "was_set_insecurely()");
+ internal_error("was_set_insecurely()");
return -1;
}
@@ -2302,8 +2318,8 @@ set_string_option_direct (
if (idx == -1) { // Use name.
idx = findoption((const char *)name);
if (idx < 0) { // Not found (should not happen).
- EMSG2(_(e_intern2), "set_string_option_direct()");
- EMSG2(_("For option %s"), name);
+ internal_error("set_string_option_direct()");
+ IEMSG2(_("For option %s"), name);
return;
}
}
@@ -2389,6 +2405,7 @@ static char *set_string_option(const int opt_idx, const char *const value,
*varp = s;
char *const saved_oldval = (starting ? NULL : xstrdup(oldval));
+ char *const saved_newval = (starting ? NULL : xstrdup(s));
char *const r = (char *)did_set_string_option(
opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags);
@@ -2397,19 +2414,12 @@ static char *set_string_option(const int opt_idx, const char *const value,
}
// call autocommand after handling side effects
- if (saved_oldval != NULL) {
- char buf_type[7];
- vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, (char *)(*varp), -1);
- set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1);
- set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- apply_autocmds(EVENT_OPTIONSET,
- (char_u *)options[opt_idx].fullname,
- NULL, false, NULL);
- reset_v_option_vars();
- xfree(saved_oldval);
+ if (r == NULL) {
+ trigger_optionsset_string(opt_idx, opt_flags,
+ saved_oldval, saved_newval);
}
+ xfree(saved_oldval);
+ xfree(saved_newval);
return r;
}
@@ -2426,6 +2436,11 @@ static bool valid_filetype(char_u *val)
return true;
}
+#ifdef _MSC_VER
+// MSVC optimizations are disabled for this function because it
+// incorrectly generates an empty string for SHM_ALL.
+#pragma optimize("", off)
+#endif
/*
* Handle string options that need some action to perform when changed.
* Returns NULL for success, or an error message for an error.
@@ -2445,6 +2460,7 @@ did_set_string_option (
int did_chartab = FALSE;
char_u **gvarp;
bool free_oldval = (options[opt_idx].flags & P_ALLOCED);
+ int ft_changed = false;
/* Get the global option to compare with, otherwise we would have to check
* two values for all local options. */
@@ -2454,12 +2470,14 @@ did_set_string_option (
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
errmsg = e_secure;
- }
- /* Check for a "normal" file name in some options. Disallow a path
- * separator (slash and/or backslash), wildcards and characters that are
- * often illegal in a file name. */
- else if ((options[opt_idx].flags & P_NFNAME)
- && vim_strpbrk(*varp, (char_u *)"/\\*?[|<>") != NULL) {
+ } else if (((options[opt_idx].flags & P_NFNAME)
+ && vim_strpbrk(*varp, (char_u *)(secure ? "/\\*?[|;&<>\r\n"
+ : "/\\*?[<>\r\n")) != NULL)
+ || ((options[opt_idx].flags & P_NDNAME)
+ && vim_strpbrk(*varp, (char_u *)"*?[|;&<>\r\n") != NULL)) {
+ // Check for a "normal" directory or file name in some options. Disallow a
+ // path separator (slash and/or backslash), wildcards and characters that
+ // are often illegal in a file name. Be more permissive if "secure" is off.
errmsg = e_invarg;
}
/* 'backupcopy' */
@@ -2614,8 +2632,8 @@ did_set_string_option (
else if (varp == &p_ei) {
if (check_ei() == FAIL)
errmsg = e_invarg;
- /* 'encoding' and 'fileencoding' */
- } else if (varp == &p_enc || gvarp == &p_fenc) {
+ // 'encoding', 'fileencoding' and 'makeencoding'
+ } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
if (gvarp == &p_fenc) {
if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) {
errmsg = e_modifiable;
@@ -2998,9 +3016,10 @@ did_set_string_option (
if (s[-1] == 'k' || s[-1] == 's') {
/* skip optional filename after 'k' and 's' */
while (*s && *s != ',' && *s != ' ') {
- if (*s == '\\')
- ++s;
- ++s;
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
+ }
+ s++;
}
} else {
if (errbuf != NULL) {
@@ -3031,7 +3050,7 @@ did_set_string_option (
/* 'pastetoggle': translate key codes like in a mapping */
else if (varp == &p_pt) {
if (*p_pt) {
- (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false,
+ (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true,
CPO_TO_CPO_FLAGS);
if (p != NULL) {
if (new_value_alloced)
@@ -3161,6 +3180,8 @@ did_set_string_option (
} else if (gvarp == &p_ft) {
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
+ } else {
+ ft_changed = STRCMP(oldval, *varp) != 0;
}
} else if (gvarp == &p_syn) {
if (!valid_filetype(*varp)) {
@@ -3173,17 +3194,18 @@ did_set_string_option (
} else {
// Options that are a list of flags.
p = NULL;
- if (varp == &p_ww)
+ if (varp == &p_ww) { // 'whichwrap'
p = (char_u *)WW_ALL;
- if (varp == &p_shm)
+ }
+ if (varp == &p_shm) { // 'shortmess'
p = (char_u *)SHM_ALL;
- else if (varp == &(p_cpo))
+ } else if (varp == &(p_cpo)) { // 'cpoptions'
p = (char_u *)CPO_VI;
- else if (varp == &(curbuf->b_p_fo))
+ } else if (varp == &(curbuf->b_p_fo)) { // 'formatoptions'
p = (char_u *)FO_ALL;
- else if (varp == &curwin->w_p_cocu)
+ } else if (varp == &curwin->w_p_cocu) { // 'concealcursor'
p = (char_u *)COCU_ALL;
- else if (varp == &p_mouse) {
+ } else if (varp == &p_mouse) { // 'mouse'
p = (char_u *)MOUSE_ALL;
}
if (p != NULL) {
@@ -3242,10 +3264,12 @@ did_set_string_option (
apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
curbuf->b_fname, TRUE, curbuf);
} else if (varp == &(curbuf->b_p_ft)) {
- /* 'filetype' is set, trigger the FileType autocommand */
- did_filetype = TRUE;
- apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft,
- curbuf->b_fname, TRUE, curbuf);
+ // 'filetype' is set, trigger the FileType autocommand
+ if (!(opt_flags & OPT_MODELINE) || ft_changed) {
+ did_filetype = true;
+ apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft,
+ curbuf->b_fname, true, curbuf);
+ }
}
if (varp == &(curwin->w_s->b_p_spl)) {
char_u fname[200];
@@ -3286,6 +3310,9 @@ did_set_string_option (
return errmsg;
}
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif
/*
* Simple int comparison function for use with qsort()
@@ -3362,37 +3389,38 @@ skip:
return NULL; /* no error */
}
-/*
- * Handle setting 'listchars' or 'fillchars'.
- * Returns error message, NULL if it's OK.
- */
+
+/// Handle setting 'listchars' or 'fillchars'.
+/// Assume monocell characters
+///
+/// @param varp either &p_lcs ('listchars') or &p_fcs ('fillchar')
+/// @return error message, NULL if it's OK.
static char_u *set_chars_option(char_u **varp)
{
int round, i, len, entries;
char_u *p, *s;
int c1, c2 = 0;
struct charstab {
- int *cp;
- char *name;
+ int *cp; ///< char value
+ char *name; ///< char id
+ int def; ///< default value
};
- static struct charstab filltab[] =
- {
- {&fill_stl, "stl"},
- {&fill_stlnc, "stlnc"},
- {&fill_vert, "vert"},
- {&fill_fold, "fold"},
- {&fill_diff, "diff"},
+ static struct charstab filltab[] = {
+ { &fill_stl, "stl" , ' ' },
+ { &fill_stlnc, "stlnc", ' ' },
+ { &fill_vert, "vert" , 9474 }, // │
+ { &fill_fold, "fold" , 183 }, // ·
+ { &fill_diff, "diff" , '-' },
};
- static struct charstab lcstab[] =
- {
- {&lcs_eol, "eol"},
- {&lcs_ext, "extends"},
- {&lcs_nbsp, "nbsp"},
- {&lcs_prec, "precedes"},
- {&lcs_space, "space"},
- {&lcs_tab2, "tab"},
- {&lcs_trail, "trail"},
- {&lcs_conceal, "conceal"},
+ static struct charstab lcstab[] = {
+ { &lcs_eol, "eol", NUL },
+ { &lcs_ext, "extends", NUL },
+ { &lcs_nbsp, "nbsp", NUL },
+ { &lcs_prec, "precedes", NUL },
+ { &lcs_space, "space", NUL },
+ { &lcs_tab2, "tab", NUL },
+ { &lcs_trail, "trail", NUL },
+ { &lcs_conceal, "conceal", NUL },
};
struct charstab *tab;
@@ -3402,20 +3430,29 @@ static char_u *set_chars_option(char_u **varp)
} else {
tab = filltab;
entries = ARRAY_SIZE(filltab);
+ if (*p_ambw == 'd') {
+ // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is
+ // forbidden (TUI limitation?). Set old defaults.
+ filltab[2].def = '|';
+ filltab[3].def = '-';
+ } else {
+ filltab[2].def = 9474; // │
+ filltab[3].def = 183; // ·
+ }
}
- /* first round: check for valid value, second round: assign values */
- for (round = 0; round <= 1; ++round) {
+ // first round: check for valid value, second round: assign values
+ for (round = 0; round <= 1; round++) {
if (round > 0) {
- /* After checking that the value is valid: set defaults: space for
- * 'fillchars', NUL for 'listchars' */
- for (i = 0; i < entries; ++i)
- if (tab[i].cp != NULL)
- *(tab[i].cp) = (varp == &p_lcs ? NUL : ' ');
- if (varp == &p_lcs)
+ // After checking that the value is valid: set defaults
+ for (i = 0; i < entries; i++) {
+ if (tab[i].cp != NULL) {
+ *(tab[i].cp) = tab[i].def;
+ }
+ }
+ if (varp == &p_lcs) {
lcs_tab1 = NUL;
- else
- fill_diff = '-';
+ }
}
p = *varp;
while (*p) {
@@ -4012,6 +4049,10 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
(char_u *) options[opt_idx].fullname,
NULL, false, NULL);
reset_v_option_vars();
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ BOOLEAN_OBJ(value));
+ }
}
comp_col(); /* in case 'ruler' or 'showcmd' changed */
@@ -4118,6 +4159,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
if (value < 0) {
errmsg = e_positive;
}
+ } else if (pp == &p_titlelen) {
+ if (value < 0) {
+ errmsg = e_positive;
+ }
} else if (pp == &p_so) {
if (value < 0 && full_screen) {
errmsg = e_scroll;
@@ -4172,6 +4217,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
if (value < -1 || value > B_IMODE_LAST) {
errmsg = e_invarg;
}
+ } else if (pp == &curbuf->b_p_channel || pp == &p_channel) {
+ errmsg = e_invarg;
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
if (value < -1 || value > SB_MAX
|| (value != -1 && opt_flags == OPT_LOCAL && !curbuf->terminal)) {
@@ -4215,17 +4262,17 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
// Number options that need some action when changed
if (pp == &p_wh) {
- if (lastwin != firstwin && curwin->w_height < p_wh) {
+ if (!ONE_WINDOW && curwin->w_height < p_wh) {
win_setheight((int)p_wh);
}
} else if (pp == &p_hh) {
- if (lastwin != firstwin && curbuf->b_help && curwin->w_height < p_hh) {
+ if (!ONE_WINDOW && curbuf->b_help && curwin->w_height < p_hh) {
win_setheight((int)p_hh);
}
} else if (pp == &p_wmh) {
win_setminheight();
} else if (pp == &p_wiw) {
- if (lastwin != firstwin && curwin->w_width < p_wiw) {
+ if (!ONE_WINDOW && curwin->w_width < p_wiw) {
win_setwidth((int)p_wiw);
}
} else if (pp == &p_ls) {
@@ -4388,6 +4435,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
(char_u *) options[opt_idx].fullname,
NULL, false, NULL);
reset_v_option_vars();
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ INTEGER_OBJ(value));
+ }
}
comp_col(); /* in case 'columns' or 'ls' changed */
@@ -4399,6 +4450,27 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
return (char *)errmsg;
}
+static void trigger_optionsset_string(int opt_idx, int opt_flags,
+ char *oldval, char *newval)
+{
+ if (oldval != NULL && newval != NULL) {
+ char buf_type[7];
+
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
+ (opt_flags & OPT_LOCAL) ? "local" : "global");
+ set_vim_var_string(VV_OPTION_OLD, oldval, -1);
+ set_vim_var_string(VV_OPTION_NEW, newval, -1);
+ set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
+ apply_autocmds(EVENT_OPTIONSET,
+ (char_u *)options[opt_idx].fullname, NULL, false, NULL);
+ reset_v_option_vars();
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ STRING_OBJ(cstr_as_string(newval)));
+ }
+ }
+}
+
/*
* Called after an option changed: check if something needs to be redrawn.
*/
@@ -4495,13 +4567,17 @@ int findoption_len(const char *const arg, const size_t len)
bool is_tty_option(const char *name)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0;
+ return (name[0] == 't' && name[1] == '_')
+ || strequal(name, "term")
+ || strequal(name, "ttytype");
}
#define TCO_BUFFER_SIZE 8
+/// @param name TUI-related option
+/// @param[out,allocated] value option string value
bool get_tty_option(char *name, char **value)
{
- if (!strcmp(name, "t_Co")) {
+ if (strequal(name, "t_Co")) {
if (value) {
if (t_colors <= 1) {
*value = xstrdup("");
@@ -4513,9 +4589,16 @@ bool get_tty_option(char *name, char **value)
return true;
}
- if (!strcmp(name, "term") || !strcmp(name, "ttytype")) {
+ if (strequal(name, "term")) {
+ if (value) {
+ *value = p_term ? xstrdup(p_term) : xstrdup("nvim");
+ }
+ return true;
+ }
+
+ if (strequal(name, "ttytype")) {
if (value) {
- *value = xstrdup("nvim");
+ *value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim");
}
return true;
}
@@ -4531,25 +4614,25 @@ bool get_tty_option(char *name, char **value)
return false;
}
-bool set_tty_option(const char *name, const char *value)
+bool set_tty_option(const char *name, char *value)
{
- if (!strcmp(name, "t_Co")) {
- int colors = atoi(value);
-
- // Only reinitialize colors if t_Co value has really changed to
- // avoid expensive reload of colorscheme if t_Co is set to the
- // same value multiple times
- if (colors != t_colors) {
- t_colors = colors;
- // We now have a different color setup, initialize it again.
- init_highlight(true, false);
+ if (strequal(name, "term")) {
+ if (p_term) {
+ xfree(p_term);
}
+ p_term = value;
+ return true;
+ }
+ if (strequal(name, "ttytype")) {
+ if (p_ttytype) {
+ xfree(p_ttytype);
+ }
+ p_ttytype = value;
return true;
}
- return (is_tty_option(name) || !strcmp(name, "term")
- || !strcmp(name, "ttytype"));
+ return false;
}
/// Find index for an option
@@ -4562,21 +4645,18 @@ static int findoption(const char *const arg)
return findoption_len(arg, strlen(arg));
}
-/*
- * Get the value for an option.
- *
- * Returns:
- * Number or Toggle option: 1, *numval gets value.
- * String option: 0, *stringval gets allocated string.
- * Hidden Number or Toggle option: -1.
- * hidden String option: -2.
- * unknown option: -3.
- */
-int
-get_option_value (
+/// Gets the value for an option.
+///
+/// @returns:
+/// Number or Toggle option: 1, *numval gets value.
+/// String option: 0, *stringval gets allocated string.
+/// Hidden Number or Toggle option: -1.
+/// hidden String option: -2.
+/// unknown option: -3.
+int get_option_value(
char_u *name,
long *numval,
- char_u **stringval, /* NULL when only checking existence */
+ char_u **stringval, ///< NULL when only checking existence
int opt_flags
)
{
@@ -4584,32 +4664,31 @@ get_option_value (
return 0;
}
- int opt_idx;
- char_u *varp;
-
- opt_idx = findoption((const char *)name);
+ int opt_idx = findoption((const char *)name);
if (opt_idx < 0) { // Unknown option.
return -3;
}
- varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
if (options[opt_idx].flags & P_STRING) {
- if (varp == NULL) /* hidden option */
+ if (varp == NULL) { // hidden option
return -2;
+ }
if (stringval != NULL) {
*stringval = vim_strsave(*(char_u **)(varp));
}
return 0;
}
- if (varp == NULL) /* hidden option */
+ if (varp == NULL) { // hidden option
return -1;
- if (options[opt_idx].flags & P_NUM)
+ }
+ if (options[opt_idx].flags & P_NUM) {
*numval = *(long *)varp;
- else {
- /* Special case: 'modified' is b_changed, but we also want to consider
- * it set when 'ff' or 'fenc' changed. */
+ } else {
+ // Special case: 'modified' is b_changed, but we also want to consider
+ // it set when 'ff' or 'fenc' changed.
if ((int *)varp == &curbuf->b_changed) {
*numval = curbufIsChanged();
} else {
@@ -4756,8 +4835,8 @@ char *set_option_value(const char *const name, const long number,
const char *const string, const int opt_flags)
FUNC_ATTR_NONNULL_ARG(1)
{
- if (set_tty_option(name, string)) {
- return NULL;
+ if (is_tty_option(name)) {
+ return NULL; // Fail silently; many old vimrcs set t_xx options.
}
int opt_idx;
@@ -4861,15 +4940,14 @@ showoptions (
vimoption_T **items = xmalloc(sizeof(vimoption_T *) * PARAM_COUNT);
- /* Highlight title */
- if (all == 2)
- MSG_PUTS_TITLE(_("\n--- Terminal codes ---"));
- else if (opt_flags & OPT_GLOBAL)
+ // Highlight title
+ if (opt_flags & OPT_GLOBAL) {
MSG_PUTS_TITLE(_("\n--- Global option values ---"));
- else if (opt_flags & OPT_LOCAL)
+ } else if (opt_flags & OPT_LOCAL) {
MSG_PUTS_TITLE(_("\n--- Local option values ---"));
- else
+ } else {
MSG_PUTS_TITLE(_("\n--- Options ---"));
+ }
/*
* do the loop two times:
@@ -4944,14 +5022,39 @@ static int optval_default(vimoption_T *p, char_u *varp)
if (varp == NULL)
return TRUE; /* hidden option is always at default */
dvi = ((p->flags & P_VI_DEF) || p_cp) ? VI_DEFAULT : VIM_DEFAULT;
- if (p->flags & P_NUM)
- return *(long *)varp == (long)p->def_val[dvi];
- if (p->flags & P_BOOL)
+ if (p->flags & P_NUM) {
+ return *(long *)varp == (long)(intptr_t)p->def_val[dvi];
+ }
+ if (p->flags & P_BOOL) {
return *(int *)varp == (int)(intptr_t)p->def_val[dvi];
- /* P_STRING */
+ }
+ // P_STRING
return STRCMP(*(char_u **)varp, p->def_val[dvi]) == 0;
}
+/// Send update to UIs with values of UI relevant options
+void ui_refresh_options(void)
+{
+ for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
+ uint32_t flags = options[opt_idx].flags;
+ if (!(flags & P_UI_OPTION)) {
+ continue;
+ }
+ String name = cstr_as_string(options[opt_idx].fullname);
+ void *varp = options[opt_idx].var;
+ Object value = OBJECT_INIT;
+ if (flags & P_BOOL) {
+ value = BOOLEAN_OBJ(*(int *)varp);
+ } else if (flags & P_NUM) {
+ value = INTEGER_OBJ(*(long *)varp);
+ } else if (flags & P_STRING) {
+ // cstr_as_string handles NULL string
+ value = STRING_OBJ(cstr_as_string(*(char **)varp));
+ }
+ ui_call_option_set(name, value);
+ }
+}
+
/*
* showoneopt: show the value of one option
* must not be called with a hidden option!
@@ -5149,9 +5252,13 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
* CTRL-V or backslash */
if (valuep == &p_pt) {
s = *valuep;
- while (*s != NUL)
- if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL)
+ while (*s != NUL) {
+ if (put_escstr(fd, (char_u *)str2special((const char **)&s, false,
+ false), 2)
+ == FAIL) {
return FAIL;
+ }
+ }
} else if (expand) {
buf = xmalloc(MAXPATHL);
home_replace(NULL, *valuep, buf, MAXPATHL, FALSE);
@@ -5206,7 +5313,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;
@@ -5307,6 +5414,9 @@ void unset_global_local_option(char *name, void *from)
case PV_LW:
clear_string_option(&buf->b_p_lw);
break;
+ case PV_MENC:
+ clear_string_option(&buf->b_p_menc);
+ break;
}
}
@@ -5340,6 +5450,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
case PV_UL: return (char_u *)&(curbuf->b_p_ul);
case PV_LW: return (char_u *)&(curbuf->b_p_lw);
case PV_BKC: return (char_u *)&(curbuf->b_p_bkc);
+ case PV_MENC: return (char_u *)&(curbuf->b_p_menc);
}
return NULL; /* "cannot happen" */
}
@@ -5395,6 +5506,8 @@ static char_u *get_varp(vimoption_T *p)
? (char_u *)&(curbuf->b_p_ul) : p->var;
case PV_LW: return *curbuf->b_p_lw != NUL
? (char_u *)&(curbuf->b_p_lw) : p->var;
+ case PV_MENC: return *curbuf->b_p_menc != NUL
+ ? (char_u *)&(curbuf->b_p_menc) : p->var;
case PV_ARAB: return (char_u *)&(curwin->w_p_arab);
case PV_LIST: return (char_u *)&(curwin->w_p_list);
@@ -5437,6 +5550,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_BH: return (char_u *)&(curbuf->b_p_bh);
case PV_BT: return (char_u *)&(curbuf->b_p_bt);
case PV_BL: return (char_u *)&(curbuf->b_p_bl);
+ case PV_CHANNEL:return (char_u *)&(curbuf->b_p_channel);
case PV_CI: return (char_u *)&(curbuf->b_p_ci);
case PV_CIN: return (char_u *)&(curbuf->b_p_cin);
case PV_CINK: return (char_u *)&(curbuf->b_p_cink);
@@ -5490,7 +5604,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
- default: EMSG(_("E356: get_varp ERROR"));
+ default: IEMSG(_("E356: get_varp ERROR"));
}
/* always return a valid pointer to avoid a crash! */
return (char_u *)&(curbuf->b_p_wm);
@@ -5685,7 +5799,22 @@ void buf_copy_options(buf_T *buf, int flags)
free_buf_options(buf, TRUE);
buf->b_p_ro = FALSE; /* don't copy readonly */
buf->b_p_fenc = vim_strsave(p_fenc);
- buf->b_p_ff = vim_strsave(p_ff);
+ switch (*p_ffs) {
+ case 'm':
+ buf->b_p_ff = vim_strsave((char_u *)FF_MAC);
+ break;
+ case 'd':
+ buf->b_p_ff = vim_strsave((char_u *)FF_DOS);
+ break;
+ case 'u':
+ buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ break;
+ default:
+ buf->b_p_ff = vim_strsave(p_ff);
+ }
+ if (buf->b_p_ff != NULL) {
+ buf->b_start_ffc = *buf->b_p_ff;
+ }
buf->b_p_bh = empty_option;
buf->b_p_bt = empty_option;
} else
@@ -5723,6 +5852,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_nf = vim_strsave(p_nf);
buf->b_p_mps = vim_strsave(p_mps);
buf->b_p_si = p_si;
+ buf->b_p_channel = 0;
buf->b_p_ci = p_ci;
buf->b_p_cin = p_cin;
buf->b_p_cink = vim_strsave(p_cink);
@@ -5775,6 +5905,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_qe = vim_strsave(p_qe);
buf->b_p_udf = p_udf;
buf->b_p_lw = empty_option;
+ buf->b_p_menc = empty_option;
/*
* Don't copy the options set by ex_help(), use the saved values,
@@ -6023,8 +6154,8 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
int count = 0;
char_u *str;
int loop;
- static char *(names[]) = {"all", "termcap"};
- int ic = regmatch->rm_ic; /* remember the ignore-case flag */
+ static char *(names[]) = { "all" };
+ int ic = regmatch->rm_ic; // remember the ignore-case flag
/* do this loop twice:
* loop == 0: count the number of matching options
@@ -6147,15 +6278,16 @@ option_value2string (
}
} else { // P_STRING
varp = *(char_u **)(varp);
- if (varp == NULL) /* just in case */
+ if (varp == NULL) { // Just in case.
NameBuff[0] = NUL;
- else if (opp->flags & P_EXPAND)
- home_replace(NULL, varp, NameBuff, MAXPATHL, FALSE);
- /* Translate 'pastetoggle' into special key names */
- else if ((char_u **)opp->var == &p_pt)
- str2specialbuf(p_pt, NameBuff, MAXPATHL);
- else
+ } else if (opp->flags & P_EXPAND) {
+ home_replace(NULL, varp, NameBuff, MAXPATHL, false);
+ // Translate 'pastetoggle' into special key names.
+ } else if ((char_u **)opp->var == &p_pt) {
+ str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL);
+ } else {
STRLCPY(NameBuff, varp, MAXPATHL);
+ }
}
}
@@ -7005,8 +7137,11 @@ dict_T *get_winbuf_options(const int bufopt)
if (opt->flags & P_STRING) {
tv_dict_add_str(d, opt->fullname, strlen(opt->fullname),
*(const char **)varp);
+ } else if (opt->flags & P_NUM) {
+ tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname),
+ *(long *)varp);
} else {
- tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp);
+ tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *(int *)varp);
}
}
}
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index e68dba734e..e2e98f251e 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -81,58 +81,56 @@
#define DFLT_FO_VIM "tcqj"
#define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */
-/* characters for the p_cpo option: */
-#define CPO_ALTREAD 'a' /* ":read" sets alternate file name */
-#define CPO_ALTWRITE 'A' /* ":write" sets alternate file name */
-#define CPO_BAR 'b' /* "\|" ends a mapping */
-#define CPO_BSLASH 'B' /* backslash in mapping is not special */
+// characters for the p_cpo option:
+#define CPO_ALTREAD 'a' // ":read" sets alternate file name
+#define CPO_ALTWRITE 'A' // ":write" sets alternate file name
+#define CPO_BAR 'b' // "\|" ends a mapping
+#define CPO_BSLASH 'B' // backslash in mapping is not special
#define CPO_SEARCH 'c'
-#define CPO_CONCAT 'C' /* Don't concatenate sourced lines */
-#define CPO_DOTTAG 'd' /* "./tags" in 'tags' is in current dir */
-#define CPO_DIGRAPH 'D' /* No digraph after "r", "f", etc. */
+#define CPO_CONCAT 'C' // Don't concatenate sourced lines
+#define CPO_DOTTAG 'd' // "./tags" in 'tags' is in current dir
+#define CPO_DIGRAPH 'D' // No digraph after "r", "f", etc.
#define CPO_EXECBUF 'e'
-#define CPO_EMPTYREGION 'E' /* operating on empty region is an error */
-#define CPO_FNAMER 'f' /* set file name for ":r file" */
-#define CPO_FNAMEW 'F' /* set file name for ":w file" */
-#define CPO_INTMOD 'i' /* interrupt a read makes buffer modified */
-#define CPO_INDENT 'I' /* remove auto-indent more often */
-#define CPO_ENDOFSENT 'J' /* need two spaces to detect end of sentence */
-#define CPO_KEYCODE 'k' /* don't recognize raw key code in mappings */
-#define CPO_KOFFSET 'K' /* don't wait for key code in mappings */
-#define CPO_LITERAL 'l' /* take char after backslash in [] literal */
-#define CPO_LISTWM 'L' /* 'list' changes wrapmargin */
+#define CPO_EMPTYREGION 'E' // operating on empty region is an error
+#define CPO_FNAMER 'f' // set file name for ":r file"
+#define CPO_FNAMEW 'F' // set file name for ":w file"
+#define CPO_INTMOD 'i' // interrupt a read makes buffer modified
+#define CPO_INDENT 'I' // remove auto-indent more often
+#define CPO_ENDOFSENT 'J' // need two spaces to detect end of sentence
+#define CPO_KOFFSET 'K' // don't wait for key code in mappings
+#define CPO_LITERAL 'l' // take char after backslash in [] literal
+#define CPO_LISTWM 'L' // 'list' changes wrapmargin
#define CPO_SHOWMATCH 'm'
-#define CPO_MATCHBSL 'M' /* "%" ignores use of backslashes */
-#define CPO_NUMCOL 'n' /* 'number' column also used for text */
+#define CPO_MATCHBSL 'M' // "%" ignores use of backslashes
+#define CPO_NUMCOL 'n' // 'number' column also used for text
#define CPO_LINEOFF 'o'
-#define CPO_OVERNEW 'O' /* silently overwrite new file */
-#define CPO_LISP 'p' /* 'lisp' indenting */
-#define CPO_FNAMEAPP 'P' /* set file name for ":w >>file" */
-#define CPO_JOINCOL 'q' /* with "3J" use column after first join */
+#define CPO_OVERNEW 'O' // silently overwrite new file
+#define CPO_LISP 'p' // 'lisp' indenting
+#define CPO_FNAMEAPP 'P' // set file name for ":w >>file"
+#define CPO_JOINCOL 'q' // with "3J" use column after first join
#define CPO_REDO 'r'
-#define CPO_REMMARK 'R' /* remove marks when filtering */
+#define CPO_REMMARK 'R' // remove marks when filtering
#define CPO_BUFOPT 's'
#define CPO_BUFOPTGLOB 'S'
#define CPO_TAGPAT 't'
-#define CPO_UNDO 'u' /* "u" undoes itself */
-#define CPO_BACKSPACE 'v' /* "v" keep deleted text */
-#define CPO_FWRITE 'W' /* "w!" doesn't overwrite readonly files */
+#define CPO_UNDO 'u' // "u" undoes itself
+#define CPO_BACKSPACE 'v' // "v" keep deleted text
+#define CPO_FWRITE 'W' // "w!" doesn't overwrite readonly files
#define CPO_ESC 'x'
-#define CPO_REPLCNT 'X' /* "R" with a count only deletes chars once */
+#define CPO_REPLCNT 'X' // "R" with a count only deletes chars once
#define CPO_YANK 'y'
-#define CPO_KEEPRO 'Z' /* don't reset 'readonly' on ":w!" */
+#define CPO_KEEPRO 'Z' // don't reset 'readonly' on ":w!"
#define CPO_DOLLAR '$'
#define CPO_FILTER '!'
#define CPO_MATCH '%'
-#define CPO_PLUS '+' /* ":write file" resets 'modified' */
-#define CPO_SPECI '<' /* don't recognize <> in mappings */
-#define CPO_REGAPPEND '>' /* insert NL when appending to a register */
-#define CPO_SCOLON ';' /* using "," and ";" will skip over char if
- * cursor would not move */
+#define CPO_PLUS '+' // ":write file" resets 'modified'
+#define CPO_REGAPPEND '>' // insert NL when appending to a register
+#define CPO_SCOLON ';' // using "," and ";" will skip over char if
+ // cursor would not move
#define CPO_CHANGEW '_' // "cw" special-case
// default values for Vim and Vi
#define CPO_VIM "aABceFs_"
-#define CPO_VI "aAbBcCdDeEfFiIJkKlLmMnoOpPqrRsStuvWxXyZ$!%+<>;_"
+#define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_"
/* characters for p_ww option: */
#define WW_ALL "bshl<>[],~"
@@ -449,13 +447,13 @@ EXTERN char_u *p_popt; // 'printoptions'
EXTERN char_u *p_header; // 'printheader'
EXTERN int p_prompt; // 'prompt'
EXTERN char_u *p_guicursor; // 'guicursor'
+EXTERN char_u *p_guifont; // 'guifont'
+EXTERN char_u *p_guifontset; // 'guifontset'
+EXTERN char_u *p_guifontwide; // 'guifontwide'
EXTERN char_u *p_hf; // 'helpfile'
EXTERN long p_hh; // 'helpheight'
EXTERN char_u *p_hlg; // 'helplang'
EXTERN int p_hid; // 'hidden'
-// Use P_HID to check if a buffer is to be hidden when it is no longer
-// visible in a window.
-# define P_HID(buf) (buf_hide(buf))
EXTERN char_u *p_hl; // 'highlight'
EXTERN int p_hls; // 'hlsearch'
EXTERN long p_hi; // 'history'
@@ -480,6 +478,7 @@ EXTERN char_u *p_langmap; // 'langmap'
EXTERN int p_lnr; // 'langnoremap'
EXTERN int p_lrm; // 'langremap'
EXTERN char_u *p_lm; // 'langmenu'
+EXTERN long *p_linespace; // 'linespace'
EXTERN char_u *p_lispwords; // 'lispwords'
EXTERN long p_ls; // 'laststatus'
EXTERN long p_stal; // 'showtabline'
@@ -488,6 +487,7 @@ EXTERN char_u *p_lcs; // 'listchars'
EXTERN int p_lz; // 'lazyredraw'
EXTERN int p_lpl; // 'loadplugins'
EXTERN int p_magic; // 'magic'
+EXTERN char_u *p_menc; // 'makeencoding'
EXTERN char_u *p_mef; // 'makeef'
EXTERN char_u *p_mp; // 'makeprg'
EXTERN char_u *p_cc; // 'colorcolumn'
@@ -541,7 +541,7 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
"localoptions", "options", "help", "blank",
"globals", "slash", "unix",
"sesdir", "curdir", "folds", "cursor",
- "tabpages", NULL};
+ "tabpages", NULL };
# endif
# define SSOP_BUFFERS 0x001
# define SSOP_WINPOS 0x002
@@ -559,16 +559,17 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
# define SSOP_FOLDS 0x2000
# define SSOP_CURSOR 0x4000
# define SSOP_TABPAGES 0x8000
-EXTERN char_u *p_sh; /* 'shell' */
-EXTERN char_u *p_shcf; /* 'shellcmdflag' */
-EXTERN char_u *p_sp; /* 'shellpipe' */
-EXTERN char_u *p_shq; /* 'shellquote' */
-EXTERN char_u *p_sxq; /* 'shellxquote' */
-EXTERN char_u *p_sxe; /* 'shellxescape' */
-EXTERN char_u *p_srr; /* 'shellredir' */
-EXTERN int p_stmp; /* 'shelltemp' */
+
+EXTERN char_u *p_sh; // 'shell'
+EXTERN char_u *p_shcf; // 'shellcmdflag'
+EXTERN char_u *p_sp; // 'shellpipe'
+EXTERN char_u *p_shq; // 'shellquote'
+EXTERN char_u *p_sxq; // 'shellxquote'
+EXTERN char_u *p_sxe; // 'shellxescape'
+EXTERN char_u *p_srr; // 'shellredir'
+EXTERN int p_stmp; // 'shelltemp'
#ifdef BACKSLASH_IN_FILENAME
-EXTERN int p_ssl; /* 'shellslash' */
+EXTERN int p_ssl; // 'shellslash'
#endif
EXTERN char_u *p_stl; // 'statusline'
EXTERN int p_sr; // 'shiftround'
@@ -696,6 +697,7 @@ enum {
, BV_BIN
, BV_BL
, BV_BOMB
+ , BV_CHANNEL
, BV_CI
, BV_CIN
, BV_CINK
@@ -733,6 +735,7 @@ enum {
, BV_KP
, BV_LISP
, BV_LW
+ , BV_MENC
, BV_MA
, BV_ML
, BV_MOD
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index c2778a6329..d653147943 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -7,7 +7,7 @@
-- enable_if=nil,
-- defaults={condition=nil, if_true={vi=224, vim=0}, if_false=nil},
-- secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil,
--- pri_mkrc=nil, deny_in_modelines=nil,
+-- pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil,
-- expand=nil, nodefault=nil, no_mkrc=nil, vi_def=true, vim=true,
-- alloced=nil,
-- save_pv_indir=nil,
@@ -68,7 +68,8 @@ return {
type='bool', scope={'global'},
vi_def=true,
vim=true,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
+
varname='p_arshape',
defaults={if_true={vi=true}}
},
@@ -91,7 +92,7 @@ return {
full_name='ambiwidth', abbreviation='ambw',
type='string', scope={'global'},
vi_def=true,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
varname='p_ambw',
defaults={if_true={vi="single"}}
},
@@ -295,6 +296,14 @@ return {
defaults={if_true={vi="", vim=macros('CTRL_F_STR')}}
},
{
+ full_name='channel',
+ type='number', scope={'buffer'},
+ no_mkrc=true,
+ nodefault=true,
+ varname='p_channel',
+ defaults={if_true={vi=0}}
+ },
+ {
full_name='charconvert', abbreviation='ccv',
type='string', scope={'global'},
secure=true,
@@ -524,7 +533,7 @@ return {
vi_def=true,
vim=true,
varname='p_csverbose',
- defaults={if_true={vi=0}}
+ defaults={if_true={vi=1}}
},
{
full_name='cursorbind', abbreviation='crb',
@@ -575,6 +584,7 @@ return {
full_name='dictionary', abbreviation='dict',
type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
+ normal_dname_chars=true,
vi_def=true,
expand=true,
varname='p_dict',
@@ -652,7 +662,7 @@ return {
full_name='emoji', abbreviation='emo',
type='bool', scope={'global'},
vi_def=true,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
varname='p_emoji',
defaults={if_true={vi=true}}
},
@@ -802,7 +812,7 @@ return {
vi_def=true,
redraw={'all_windows'},
varname='p_fcs',
- defaults={if_true={vi="vert:|,fold:-"}}
+ defaults={if_true={vi=''}}
},
{
full_name='fixendofline', abbreviation='fixeol',
@@ -992,11 +1002,11 @@ return {
expand=true,
varname='p_gp',
defaults={
- condition='UNIX',
+ condition='WIN32',
-- Add an extra file name so that grep will always
-- insert a file name in the match line. */
- if_true={vi="grep -n $* /dev/null"},
- if_false={vi="grep -n "},
+ if_true={vi="findstr /n $* nul"},
+ if_false={vi="grep -n $* /dev/null"}
}
},
{
@@ -1012,23 +1022,26 @@ return {
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ varname='p_guifont',
+ redraw={'everything', 'ui_option'},
+ defaults={if_true={vi=""}}
},
{
full_name='guifontset', abbreviation='gfs',
type='string', list='onecomma', scope={'global'},
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ varname='p_guifontset',
+ redraw={'everything', 'ui_option'},
+ defaults={if_true={vi=""}}
},
{
full_name='guifontwide', abbreviation='gfw',
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ redraw={'everything', 'ui_option'},
+ varname='p_guifontwide',
+ defaults={if_true={vi=""}}
},
{
full_name='guioptions', abbreviation='go',
@@ -1386,8 +1399,9 @@ return {
full_name='linespace', abbreviation='lsp',
type='number', scope={'global'},
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ redraw={'everything', 'ui_option'},
+ varname='p_linespace',
+ defaults={if_true={vi=0}}
},
{
full_name='lisp',
@@ -1444,6 +1458,13 @@ return {
defaults={if_true={vi=""}}
},
{
+ full_name='makeencoding', abbreviation='menc',
+ type='string', scope={'global', 'buffer'},
+ vi_def=true,
+ varname='p_menc',
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='makeprg', abbreviation='mp',
type='string', scope={'global', 'buffer'},
secure=true,
@@ -1750,6 +1771,7 @@ return {
{
full_name='printexpr', abbreviation='pexpr',
type='string', scope={'global'},
+ secure=true,
vi_def=true,
varname='p_pexpr',
defaults={if_true={vi=""}}
@@ -1922,7 +1944,7 @@ return {
vi_def=true,
varname='p_scbk',
redraw={'current_buffer'},
- defaults={if_true={vi=1000}}
+ defaults={if_true={vi=10000}}
},
{
full_name='scrollbind', abbreviation='scb',
@@ -2026,7 +2048,7 @@ return {
varname='p_shcf',
defaults={
condition='WIN32',
- if_true={vi="/c"},
+ if_true={vi="/s /c"},
if_false={vi="-c"}
}
},
@@ -2082,7 +2104,11 @@ return {
secure=true,
vi_def=true,
varname='p_sxq',
- defaults={if_true={vi=""}}
+ defaults={
+ condition='WIN32',
+ if_true={vi="\""},
+ if_false={vi=""},
+ }
},
{
full_name='shellxescape', abbreviation='sxe',
@@ -2154,7 +2180,7 @@ return {
full_name='showtabline', abbreviation='stal',
type='number', scope={'global'},
vi_def=true,
- redraw={'all_windows'},
+ redraw={'all_windows', 'ui_option'},
varname='p_stal',
defaults={if_true={vi=1}}
},
@@ -2163,7 +2189,7 @@ return {
type='number', scope={'global'},
vi_def=true,
varname='p_ss',
- defaults={if_true={vi=0}}
+ defaults={if_true={vi=1}}
},
{
full_name='sidescrolloff', abbreviation='siso',
@@ -2425,7 +2451,7 @@ return {
full_name='termguicolors', abbreviation='tgc',
type='bool', scope={'global'},
vi_def=false,
- redraw={'everything'},
+ redraw={'ui_option'},
varname='p_tgc',
defaults={if_true={vi=false}}
},
@@ -2449,6 +2475,7 @@ return {
full_name='thesaurus', abbreviation='tsr',
type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
+ normal_dname_chars=true,
vi_def=true,
expand=true,
varname='p_tsr',
@@ -2494,11 +2521,10 @@ return {
full_name='titleold',
type='string', scope={'global'},
secure=true,
- gettext=true,
no_mkrc=true,
vi_def=true,
varname='p_titleold',
- defaults={if_true={vi=N_("Thanks for flying Vim")}}
+ defaults={if_true={vi=""}}
},
{
full_name='titlestring',
@@ -2513,14 +2539,14 @@ return {
vi_def=true,
vim=true,
varname='p_ttimeout',
- defaults={if_true={vi=false}}
+ defaults={if_true={vi=true}}
},
{
full_name='ttimeoutlen', abbreviation='ttm',
type='number', scope={'global'},
vi_def=true,
varname='p_ttm',
- defaults={if_true={vi=-1}}
+ defaults={if_true={vi=50}}
},
{
full_name='ttyfast', abbreviation='tf',
@@ -2607,7 +2633,7 @@ return {
deny_duplicates=true,
vi_def=true,
varname='p_vop',
- defaults={if_true={vi="folds,options,cursor"}}
+ defaults={if_true={vi="folds,options,cursor,curdir"}}
},
{
full_name='viminfo', abbreviation='vi',
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index de0cd10d9c..3fcb9415c7 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -886,8 +886,8 @@ bool os_setenv_append_path(const char *fname)
// No prescribed maximum on unix.
# define MAX_ENVPATHLEN INT_MAX
#endif
- if (!path_is_absolute_path((char_u *)fname)) {
- EMSG2(_(e_intern2), "os_setenv_append_path()");
+ if (!path_is_absolute((char_u *)fname)) {
+ internal_error("os_setenv_append_path()");
return false;
}
const char *tail = (char *)path_tail_with_sep((char_u *)fname);
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 4309ac723c..a95adc86b6 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -4,7 +4,7 @@
/// @file fileio.c
///
/// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with
-/// Neovim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite
+/// Nvim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite
/// replacement.
#include <assert.h>
@@ -26,6 +26,7 @@
#include "nvim/globals.h"
#include "nvim/rbuffer.h"
#include "nvim/macros.h"
+#include "nvim/message.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fileio.c.generated.h"
@@ -38,17 +39,16 @@
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
/// writing to the file at once is not supported, so either
-/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
+/// kFileWriteOnly or kFileReadOnly is required.
/// @param[in] mode Permissions for the newly created file (ignored if flags
-/// does not have FILE_CREATE\*).
+/// does not have kFileCreate\*).
///
-/// @return Error code (@see os_strerror()) or 0.
+/// @return Error code, or 0 on success. @see os_strerror()
int file_open(FileDescriptor *const ret_fp, const char *const fname,
const int flags, const int mode)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
int os_open_flags = 0;
- int fd;
TriState wr = kNone;
// -V:FLAG:501
#define FLAG(flags, flag, fcntl_flags, wrval, cond) \
@@ -73,14 +73,35 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
#endif
#undef FLAG
+ // wr is used for kFileReadOnly flag, but on
+ // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with
+ // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]`
+ (void)wr;
- fd = os_open(fname, os_open_flags, mode);
+ const int fd = os_open(fname, os_open_flags, mode);
if (fd < 0) {
return fd;
}
+ return file_open_fd(ret_fp, fd, (wr == kTrue));
+}
- ret_fp->wr = (wr == kTrue);
+/// Wrap file descriptor with FileDescriptor structure
+///
+/// @warning File descriptor wrapped like this must not be accessed by other
+/// means.
+///
+/// @param[out] ret_fp Address where information needed for reading from or
+/// writing to a file is saved
+/// @param[in] fd File descriptor to wrap.
+/// @param[in] wr True if fd is opened for writing only, false if it is read
+/// only.
+///
+/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
+int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ ret_fp->wr = wr;
ret_fp->fd = fd;
ret_fp->eof = false;
ret_fp->rv = rbuffer_new(kRWBufferSize);
@@ -94,12 +115,11 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
/// Like file_open(), but allocate and return ret_fp
///
-/// @param[out] error Error code, @see os_strerror(). Is set to zero on
-/// success.
+/// @param[out] error Error code, or 0 on success. @see os_strerror()
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags.
/// @param[in] mode Permissions for the newly created file (ignored if flags
-/// does not have FILE_CREATE\*).
+/// does not have kFileCreate\*).
///
/// @return [allocated] Opened file or NULL in case of error.
FileDescriptor *file_open_new(int *const error, const char *const fname,
@@ -114,6 +134,25 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
return fp;
}
+/// Like file_open_fd(), but allocate and return ret_fp
+///
+/// @param[out] error Error code, or 0 on success. @see os_strerror()
+/// @param[in] fd File descriptor to wrap.
+/// @param[in] wr True if fd is opened for writing only, false if it is read
+/// only.
+///
+/// @return [allocated] Opened file or NULL in case of error.
+FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ FileDescriptor *const fp = xmalloc(sizeof(*fp));
+ if ((*error = file_open_fd(fp, fd, wr)) != 0) {
+ xfree(fp);
+ return NULL;
+ }
+ return fp;
+}
+
/// Close file and free its buffer
///
/// @param[in,out] fp File to close.
@@ -345,3 +384,32 @@ ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size)
return (ptrdiff_t)read_bytes;
}
+
+/// Msgpack callback for writing to a file
+///
+/// @param data File to write to.
+/// @param[in] buf Data to write.
+/// @param[in] len Length of the data to write.
+///
+/// @return 0 in case of success, -1 in case of error.
+int msgpack_file_write(void *data, const char *buf, size_t len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ assert(len < PTRDIFF_MAX);
+ const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len);
+ if (written_bytes < 0) {
+ return msgpack_file_write_error((int)written_bytes);
+ }
+ return 0;
+}
+
+/// Print error which occurs when failing to write msgpack data
+///
+/// @param[in] error Error code of the error to print.
+///
+/// @return -1 (error return for msgpack_packer callbacks).
+int msgpack_file_write_error(const int error)
+{
+ emsgf(_("E5420: Failed to write to file: %s"), os_strerror(error));
+ return -1;
+}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 14dacd97c4..b7c2714296 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -131,23 +131,14 @@ bool os_isdir(const char_u *name)
/// NODE_WRITABLE: writable device, socket, fifo, etc.
/// NODE_OTHER: non-writable things
int os_nodetype(const char *name)
+ FUNC_ATTR_NONNULL_ALL
{
-#ifdef WIN32
- // Edge case from Vim os_win32.c:
- // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read
- // from it later will cause Vim to hang. Thus return NODE_WRITABLE here.
- if (STRNCMP(name, "\\\\.\\", 4) == 0) {
- return NODE_WRITABLE;
- }
-#endif
-
+#ifndef WIN32 // Unix
uv_stat_t statbuf;
if (0 != os_stat(name, &statbuf)) {
return NODE_NORMAL; // File doesn't exist.
}
-
-#ifndef WIN32
- // libuv does not handle BLK and DIR in uv_handle_type.
+ // uv_handle_type does not distinguish BLK and DIR.
// Related: https://github.com/joyent/libuv/pull/1421
if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) {
return NODE_NORMAL;
@@ -155,48 +146,51 @@ int os_nodetype(const char *name)
if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable
return NODE_OTHER;
}
-#endif
+ // Everything else is writable?
+ // buf_write() expects NODE_WRITABLE for char device /dev/stderr.
+ return NODE_WRITABLE;
+#else // Windows
+ // Edge case from Vim os_win32.c:
+ // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read
+ // from it later will cause Vim to hang. Thus return NODE_WRITABLE here.
+ if (STRNCMP(name, "\\\\.\\", 4) == 0) {
+ return NODE_WRITABLE;
+ }
- // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015):
- // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) {
- // wn = enc_to_utf16(name, NULL);
- // hFile = CreatFile(wn, ...)
- // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we
- // give it. uv_fs_open calls fs__capture_path which does a similar dance and
- // saves us the hassle.
+ // Vim os_win32.c:mch_nodetype does (since 7.4.015):
+ // wn = enc_to_utf16(name, NULL);
+ // hFile = CreatFile(wn, ...)
+ // to get a HANDLE. Whereas libuv just calls _get_osfhandle() on the fd we
+ // give it. But uv_fs_open later calls fs__capture_path which does a similar
+ // utf8-to-utf16 dance and saves us the hassle.
- int nodetype = NODE_WRITABLE;
+ // macOS: os_open(/dev/stderr) would return UV_EACCES.
int fd = os_open(name, O_RDONLY
-#ifdef O_NONBLOCK
+# ifdef O_NONBLOCK
| O_NONBLOCK
-#endif
+# endif
, 0);
- if (fd == -1) {
- return NODE_OTHER; // open() failed.
+ if (fd < 0) { // open() failed.
+ return NODE_NORMAL;
+ }
+ int guess = uv_guess_handle(fd);
+ if (close(fd) == -1) {
+ ELOG("close(%d) failed. name='%s'", fd, name);
}
- switch (uv_guess_handle(fd)) {
- case UV_TTY: // FILE_TYPE_CHAR
- nodetype = NODE_WRITABLE;
- break;
- case UV_FILE: // FILE_TYPE_DISK
- nodetype = NODE_NORMAL;
- break;
- case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c
- case UV_UDP: // unix only
- case UV_TCP: // unix only
+ switch (guess) {
+ case UV_TTY: // FILE_TYPE_CHAR
+ return NODE_WRITABLE;
+ case UV_FILE: // FILE_TYPE_DISK
+ return NODE_NORMAL;
+ case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c
+ case UV_UDP: // unix only
+ case UV_TCP: // unix only
case UV_UNKNOWN_HANDLE:
default:
-#ifdef WIN32
- nodetype = NODE_NORMAL;
-#else
- nodetype = NODE_WRITABLE; // Everything else is writable?
-#endif
- break;
+ return NODE_OTHER; // Vim os_win32.c default
}
-
- close(fd);
- return nodetype;
+#endif
}
/// Gets the absolute path of the currently running executable.
@@ -228,7 +222,7 @@ int os_exepath(char *buffer, size_t *size)
bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
FUNC_ATTR_NONNULL_ARG(1)
{
- bool no_path = !use_path || path_is_absolute_path(name);
+ bool no_path = !use_path || path_is_absolute(name);
#ifndef WIN32
// If the filename is "qualified" (relative or absolute) do not check $PATH.
no_path |= (name[0] == '.'
@@ -250,7 +244,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
#endif
if (ok) {
if (abspath != NULL) {
- *abspath = save_absolute_path(name);
+ *abspath = save_abs_path(name);
}
return true;
}
@@ -363,7 +357,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
#endif
if (ok) {
if (abspath != NULL) { // Caller asked for a copy of the path.
- *abspath = save_absolute_path((char_u *)buf);
+ *abspath = save_abs_path((char_u *)buf);
}
rv = true;
@@ -393,9 +387,11 @@ end:
/// @param mode Permissions for the newly-created file (IGNORED if 'flags' is
/// not `O_CREAT` or `O_TMPFILE`), subject to the current umask
/// @return file descriptor, or libuv error code on failure
-int os_open(const char* path, int flags, int mode)
- FUNC_ATTR_NONNULL_ALL
+int os_open(const char *path, int flags, int mode)
{
+ if (path == NULL) { // uv_fs_open asserts on NULL. #7561
+ return UV_EINVAL;
+ }
int r;
RUN_UV_FS_FUNC(r, uv_fs_open, path, flags, mode, NULL);
return r;
@@ -465,7 +461,7 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
while (read_bytes != size) {
assert(size >= read_bytes);
const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes,
- size - read_bytes);
+ IO_COUNT(size - read_bytes));
if (cur_read_bytes > 0) {
read_bytes += (size_t)cur_read_bytes;
}
@@ -568,7 +564,7 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
while (written_bytes != size) {
assert(size >= written_bytes);
const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes,
- size - written_bytes);
+ IO_COUNT(size - written_bytes));
if (cur_written_bytes > 0) {
written_bytes += (size_t)cur_written_bytes;
}
@@ -602,10 +598,13 @@ int os_fsync(int fd)
/// Get stat information for a file.
///
-/// @return libuv return code.
+/// @return libuv return code, or -errno
static int os_stat(const char *name, uv_stat_t *statbuf)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(2)
{
+ if (!name) {
+ return UV_EINVAL;
+ }
uv_fs_t request;
int result = uv_fs_stat(&fs_loop, &request, name, NULL);
*statbuf = request.statbuf;
@@ -617,7 +616,6 @@ static int os_stat(const char *name, uv_stat_t *statbuf)
///
/// @return libuv error code on error.
int32_t os_getperm(const char *name)
- FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
int stat_result = os_stat(name, &statbuf);
@@ -656,7 +654,6 @@ int os_fchown(int fd, uv_uid_t owner, uv_gid_t group)
///
/// @return `true` if `path` exists
bool os_path_exists(const char_u *path)
- FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
return os_stat((char *)path, &statbuf) == kLibuvSuccess;
@@ -846,7 +843,7 @@ int os_remove(const char *path)
/// @param[out] file_info Pointer to a FileInfo to put the information in.
/// @return `true` on success, `false` for failure.
bool os_fileinfo(const char *path, FileInfo *file_info)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(2)
{
return os_stat(path, &(file_info->stat)) == kLibuvSuccess;
}
@@ -857,8 +854,11 @@ bool os_fileinfo(const char *path, FileInfo *file_info)
/// @param[out] file_info Pointer to a FileInfo to put the information in.
/// @return `true` on success, `false` for failure.
bool os_fileinfo_link(const char *path, FileInfo *file_info)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(2)
{
+ if (path == NULL) {
+ return false;
+ }
uv_fs_t request;
int result = uv_fs_lstat(&fs_loop, &request, path, NULL);
file_info->stat = request.statbuf;
@@ -990,7 +990,7 @@ bool os_fileid_equal_fileinfo(const FileID *file_id,
/// to and return that name in allocated memory.
/// Otherwise NULL is returned.
char *os_resolve_shortcut(const char *fname)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
HRESULT hr;
IPersistFile *ppf = NULL;
@@ -1073,7 +1073,8 @@ shortcut_end:
#endif
-int os_translate_sys_error(int sys_errno) {
+int os_translate_sys_error(int sys_errno)
+{
#ifdef HAVE_UV_TRANSLATE_SYS_ERROR
return uv_translate_sys_error(sys_errno);
#elif defined(WIN32)
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 31e06ce404..405500767d 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -37,7 +37,7 @@ typedef enum {
static Stream read_stream = {.closed = true};
static RBuffer *input_buffer = NULL;
static bool input_eof = false;
-static int global_fd = 0;
+static int global_fd = -1;
static int events_enabled = 0;
static bool blocking = false;
@@ -188,7 +188,7 @@ size_t input_enqueue(String keys)
uint8_t buf[6] = { 0 };
unsigned int new_size
= trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true,
- true);
+ false);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
new file mode 100644
index 0000000000..47c278ee97
--- /dev/null
+++ b/src/nvim/os/lang.c
@@ -0,0 +1,43 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#ifdef __APPLE__
+# define Boolean CFBoolean // Avoid conflict with API's Boolean
+# include <CoreFoundation/CFLocale.h>
+# include <CoreFoundation/CFString.h>
+# undef Boolean
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#include "nvim/os/os.h"
+
+void lang_init(void)
+{
+#ifdef __APPLE__
+ if (os_getenv("LANG") == NULL) {
+ CFLocaleRef cf_locale = CFLocaleCopyCurrent();
+ CFTypeRef cf_lang_region = CFLocaleGetValue(cf_locale,
+ kCFLocaleIdentifier);
+ CFRetain(cf_lang_region);
+ CFRelease(cf_locale);
+
+ const char *lang_region = CFStringGetCStringPtr(cf_lang_region,
+ kCFStringEncodingUTF8);
+ if (lang_region) {
+ os_setenv("LANG", lang_region, true);
+ } else {
+ char buf[20] = { 0 };
+ if (CFStringGetCString(cf_lang_region, buf, 20,
+ kCFStringEncodingUTF8)) {
+ os_setenv("LANG", lang_region, true);
+ }
+ }
+ CFRelease(cf_lang_region);
+# ifdef HAVE_LOCALE_H
+ setlocale(LC_ALL, "");
+# endif
+ }
+#endif
+}
diff --git a/src/nvim/os/lang.h b/src/nvim/os/lang.h
new file mode 100644
index 0000000000..f60e064f57
--- /dev/null
+++ b/src/nvim/os/lang.h
@@ -0,0 +1,7 @@
+#ifndef NVIM_OS_LANG_H
+#define NVIM_OS_LANG_H
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/lang.h.generated.h"
+#endif
+#endif // NVIM_OS_LANG_H
diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
new file mode 100644
index 0000000000..e23ba8a4ee
--- /dev/null
+++ b/src/nvim/os/process.c
@@ -0,0 +1,253 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+/// OS process functions
+///
+/// psutil is a good reference for cross-platform syscall voodoo:
+/// https://github.com/giampaolo/psutil/tree/master/psutil/arch
+
+#include <uv.h> // for HANDLE (win32)
+
+#ifdef WIN32
+# include <tlhelp32.h> // for CreateToolhelp32Snapshot
+#endif
+
+#if defined(__FreeBSD__) // XXX: OpenBSD, NetBSD ?
+# include <string.h>
+# include <sys/types.h>
+# include <sys/user.h>
+#endif
+
+#if defined(__APPLE__) || defined(BSD)
+# include <sys/sysctl.h>
+# include <pwd.h>
+#endif
+
+#include "nvim/globals.h"
+#include "nvim/log.h"
+#include "nvim/os/process.h"
+#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/api/private/helpers.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/process.c.generated.h"
+#endif
+
+#ifdef WIN32
+static bool os_proc_tree_kill_rec(HANDLE process, int sig)
+{
+ if (process == NULL) {
+ return false;
+ }
+ PROCESSENTRY32 pe;
+ DWORD pid = GetProcessId(process);
+
+ if (pid != 0) {
+ HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (h != INVALID_HANDLE_VALUE) {
+ pe.dwSize = sizeof(PROCESSENTRY32);
+ if (!Process32First(h, &pe)) {
+ goto theend;
+ }
+ do {
+ if (pe.th32ParentProcessID == pid) {
+ HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, pe.th32ProcessID);
+ if (ph != NULL) {
+ os_proc_tree_kill_rec(ph, sig);
+ CloseHandle(ph);
+ }
+ }
+ } while (Process32Next(h, &pe));
+ CloseHandle(h);
+ }
+ }
+
+theend:
+ return (bool)TerminateProcess(process, (unsigned int)sig);
+}
+/// Kills process `pid` and its descendants recursively.
+bool os_proc_tree_kill(int pid, int sig)
+{
+ assert(sig >= 0);
+ assert(sig == SIGTERM || sig == SIGKILL);
+ if (pid > 0) {
+ ILOG("terminating process tree: %d", pid);
+ HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid);
+ return os_proc_tree_kill_rec(h, sig);
+ } else {
+ ELOG("invalid pid: %d", pid);
+ }
+ return false;
+}
+#else
+/// Kills process group where `pid` is the process group leader.
+bool os_proc_tree_kill(int pid, int sig)
+{
+ assert(sig == SIGTERM || sig == SIGKILL);
+ int pgid = getpgid(pid);
+ if (pgid > 0) { // Ignore error. Never kill self (pid=0).
+ if (pgid == pid) {
+ ILOG("sending %s to process group: -%d",
+ sig == SIGTERM ? "SIGTERM" : "SIGKILL", pgid);
+ int rv = uv_kill(-pgid, sig);
+ return rv == 0;
+ } else {
+ // Should never happen, because process_spawn() did setsid() in the child.
+ ELOG("pgid %d != pid %d", pgid, pid);
+ }
+ } else {
+ ELOG("getpgid(%d) returned %d", pid, pgid);
+ }
+ return false;
+}
+#endif
+
+/// Gets the process ids of the immediate children of process `ppid`.
+///
+/// @param ppid Process to inspect.
+/// @param[out,allocated] proc_list Child process ids.
+/// @param[out] proc_count Number of child processes.
+/// @return 0 on success, 1 if process not found, 2 on other error.
+int os_proc_children(int ppid, int **proc_list, size_t *proc_count)
+{
+ if (ppid < 0) {
+ return 2;
+ }
+
+ int *temp = NULL;
+ *proc_list = NULL;
+ *proc_count = 0;
+
+#ifdef WIN32
+ PROCESSENTRY32 pe;
+
+ // Snapshot of all processes.
+ HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (h == INVALID_HANDLE_VALUE) {
+ return 2;
+ }
+
+ pe.dwSize = sizeof(PROCESSENTRY32);
+ // Get root process.
+ if (!Process32First(h, &pe)) {
+ CloseHandle(h);
+ return 2;
+ }
+ // Collect processes whose parent matches `ppid`.
+ do {
+ if (pe.th32ParentProcessID == (DWORD)ppid) {
+ temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp));
+ temp[*proc_count] = (int)pe.th32ProcessID;
+ (*proc_count)++;
+ }
+ } while (Process32Next(h, &pe));
+ CloseHandle(h);
+
+#elif defined(__APPLE__) || defined(BSD)
+# if defined(__APPLE__)
+# define KP_PID(o) o.kp_proc.p_pid
+# define KP_PPID(o) o.kp_eproc.e_ppid
+# elif defined(__FreeBSD__)
+# define KP_PID(o) o.ki_pid
+# define KP_PPID(o) o.ki_ppid
+# else
+# define KP_PID(o) o.p_pid
+# define KP_PPID(o) o.p_ppid
+# endif
+ static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
+
+ // Get total process count.
+ size_t len = 0;
+ int rv = sysctl(name, ARRAY_SIZE(name) - 1, NULL, &len, NULL, 0);
+ if (rv) {
+ return 2;
+ }
+
+ // Get ALL processes.
+ struct kinfo_proc *p_list = xmalloc(len);
+ rv = sysctl(name, ARRAY_SIZE(name) - 1, p_list, &len, NULL, 0);
+ if (rv) {
+ xfree(p_list);
+ return 2;
+ }
+
+ // Collect processes whose parent matches `ppid`.
+ bool exists = false;
+ size_t p_count = len / sizeof(*p_list);
+ for (size_t i = 0; i < p_count; i++) {
+ exists = exists || KP_PID(p_list[i]) == ppid;
+ if (KP_PPID(p_list[i]) == ppid) {
+ temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp));
+ temp[*proc_count] = KP_PID(p_list[i]);
+ (*proc_count)++;
+ }
+ }
+ xfree(p_list);
+ if (!exists) {
+ return 1; // Process not found.
+ }
+
+#elif defined(__linux__)
+ char proc_p[256] = { 0 };
+ // Collect processes whose parent matches `ppid`.
+ // Rationale: children are defined in thread with same ID of process.
+ snprintf(proc_p, sizeof(proc_p), "/proc/%d/task/%d/children", ppid, ppid);
+ FILE *fp = fopen(proc_p, "r");
+ if (fp == NULL) {
+ return 2; // Process not found, or /proc/…/children not supported.
+ }
+ int match_pid;
+ while (fscanf(fp, "%d", &match_pid) > 0) {
+ temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp));
+ temp[*proc_count] = match_pid;
+ (*proc_count)++;
+ }
+ fclose(fp);
+#endif
+
+ *proc_list = temp;
+ return 0;
+}
+
+#ifdef WIN32
+/// Gets various properties of the process identified by `pid`.
+///
+/// @param pid Process to inspect.
+/// @return Map of process properties, empty on error.
+Dictionary os_proc_info(int pid)
+{
+ Dictionary pinfo = ARRAY_DICT_INIT;
+ PROCESSENTRY32 pe;
+
+ // Snapshot of all processes. This is used instead of:
+ // OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, …)
+ // to avoid ERROR_PARTIAL_COPY. https://stackoverflow.com/a/29942376
+ HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (h == INVALID_HANDLE_VALUE) {
+ return pinfo; // Return empty.
+ }
+
+ pe.dwSize = sizeof(PROCESSENTRY32);
+ // Get root process.
+ if (!Process32First(h, &pe)) {
+ CloseHandle(h);
+ return pinfo; // Return empty.
+ }
+ // Find the process.
+ do {
+ if (pe.th32ProcessID == (DWORD)pid) {
+ break;
+ }
+ } while (Process32Next(h, &pe));
+ CloseHandle(h);
+
+ if (pe.th32ProcessID == (DWORD)pid) {
+ PUT(pinfo, "pid", INTEGER_OBJ(pid));
+ PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID));
+ PUT(pinfo, "name", STRING_OBJ(cstr_to_string(pe.szExeFile)));
+ }
+
+ return pinfo;
+}
+#endif
diff --git a/src/nvim/os/process.h b/src/nvim/os/process.h
new file mode 100644
index 0000000000..1722d56bd3
--- /dev/null
+++ b/src/nvim/os/process.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_OS_PROCESS_H
+#define NVIM_OS_PROCESS_H
+
+#include <stddef.h>
+#include "nvim/api/private/defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/process.h.generated.h"
+#endif
+
+#endif // NVIM_OS_PROCESS_H
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index ee3ab96a83..dfe2cfbb8d 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -36,31 +36,43 @@
# include "os/pty_process_unix.c.generated.h"
#endif
+/// termios saved at startup (for TUI) or initialized by pty_process_spawn().
+static struct termios termios_default;
+
+/// Saves the termios properties associated with `tty_fd`.
+///
+/// @param tty_fd TTY file descriptor, or -1 if not in a terminal.
+void pty_process_save_termios(int tty_fd)
+{
+ if (tty_fd == -1 || tcgetattr(tty_fd, &termios_default) != 0) {
+ return;
+ }
+}
+
/// @returns zero on success, or negative error code
int pty_process_spawn(PtyProcess *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
- static struct termios termios;
- if (!termios.c_cflag) {
- init_termios(&termios);
+ if (!termios_default.c_cflag) {
+ // TODO(jkeyes): We could pass NULL to forkpty() instead ...
+ init_termios(&termios_default);
}
int status = 0; // zero or negative error code (libuv convention)
Process *proc = (Process *)ptyproc;
- assert(!proc->err);
+ assert(proc->err.closed);
uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD);
ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 };
uv_disable_stdio_inheritance();
int master;
- int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize);
+ int pid = forkpty(&master, NULL, &termios_default, &ptyproc->winsize);
if (pid < 0) {
status = -errno;
ELOG("forkpty failed: %s", strerror(errno));
return status;
} else if (pid == 0) {
- init_child(ptyproc);
- abort();
+ init_child(ptyproc); // never returns
}
// make sure the master file descriptor is non blocking
@@ -83,12 +95,12 @@ int pty_process_spawn(PtyProcess *ptyproc)
goto error;
}
- if (proc->in
- && (status = set_duplicating_descriptor(master, &proc->in->uv.pipe))) {
+ if (!proc->in.closed
+ && (status = set_duplicating_descriptor(master, &proc->in.uv.pipe))) {
goto error;
}
- if (proc->out
- && (status = set_duplicating_descriptor(master, &proc->out->uv.pipe))) {
+ if (!proc->out.closed
+ && (status = set_duplicating_descriptor(master, &proc->out.uv.pipe))) {
goto error;
}
@@ -133,8 +145,12 @@ void pty_process_teardown(Loop *loop)
uv_signal_stop(&loop->children_watcher);
}
-static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
+static void init_child(PtyProcess *ptyproc)
+ FUNC_ATTR_NONNULL_ALL
{
+ // New session/process-group. #6530
+ setsid();
+
unsetenv("COLUMNS");
unsetenv("LINES");
unsetenv("TERMCAP");
@@ -150,14 +166,15 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
Process *proc = (Process *)ptyproc;
if (proc->cwd && os_chdir(proc->cwd) != 0) {
- fprintf(stderr, "chdir failed: %s\n", strerror(errno));
+ ELOG("chdir failed: %s", strerror(errno));
return;
}
char *prog = ptyproc->process.argv[0];
setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
execvp(prog, ptyproc->process.argv);
- fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog);
+ ELOG("execvp failed: %s: %s", strerror(errno), prog);
+ _exit(122); // 122 is EXEC_FAILED in the Vim source.
}
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c
new file mode 100644
index 0000000000..a6774a6275
--- /dev/null
+++ b/src/nvim/os/pty_process_win.c
@@ -0,0 +1,413 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <winpty_constants.h>
+
+#include "nvim/os/os.h"
+#include "nvim/ascii.h"
+#include "nvim/memory.h"
+#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
+#include "nvim/os/pty_process_win.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/pty_process_win.c.generated.h"
+#endif
+
+static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused)
+ FUNC_ATTR_NONNULL_ALL
+{
+ PtyProcess *ptyproc = (PtyProcess *)context;
+ Process *proc = (Process *)ptyproc;
+
+ uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer);
+ ptyproc->wait_eof_timer.data = (void *)ptyproc;
+ uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200);
+}
+
+/// @returns zero on success, or negative error code.
+int pty_process_spawn(PtyProcess *ptyproc)
+ FUNC_ATTR_NONNULL_ALL
+{
+ Process *proc = (Process *)ptyproc;
+ int status = 0;
+ winpty_error_ptr_t err = NULL;
+ winpty_config_t *cfg = NULL;
+ winpty_spawn_config_t *spawncfg = NULL;
+ winpty_t *winpty_object = NULL;
+ char *in_name = NULL;
+ char *out_name = NULL;
+ HANDLE process_handle = NULL;
+ uv_connect_t *in_req = NULL;
+ uv_connect_t *out_req = NULL;
+ wchar_t *cmd_line = NULL;
+ wchar_t *cwd = NULL;
+ const char *emsg = NULL;
+
+ assert(proc->err.closed);
+
+ cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err);
+ if (cfg == NULL) {
+ emsg = "Failed, winpty_config_new.";
+ goto cleanup;
+ }
+
+ winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height);
+ winpty_object = winpty_open(cfg, &err);
+ if (winpty_object == NULL) {
+ emsg = "Failed, winpty_open.";
+ goto cleanup;
+ }
+
+ status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name);
+ if (status != 0) {
+ emsg = "Failed to convert in_name from utf16 to utf8.";
+ goto cleanup;
+ }
+
+ status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name);
+ if (status != 0) {
+ emsg = "Failed to convert out_name from utf16 to utf8.";
+ goto cleanup;
+ }
+
+ if (!proc->in.closed) {
+ in_req = xmalloc(sizeof(uv_connect_t));
+ uv_pipe_connect(
+ in_req,
+ &proc->in.uv.pipe,
+ in_name,
+ pty_process_connect_cb);
+ }
+
+ if (!proc->out.closed) {
+ out_req = xmalloc(sizeof(uv_connect_t));
+ uv_pipe_connect(
+ out_req,
+ &proc->out.uv.pipe,
+ out_name,
+ pty_process_connect_cb);
+ }
+
+ if (proc->cwd != NULL) {
+ status = utf8_to_utf16(proc->cwd, &cwd);
+ if (status != 0) {
+ emsg = "Failed to convert pwd form utf8 to utf16.";
+ goto cleanup;
+ }
+ }
+
+ status = build_cmd_line(proc->argv, &cmd_line,
+ os_shell_is_cmdexe(proc->argv[0]));
+ if (status != 0) {
+ emsg = "Failed to convert cmd line form utf8 to utf16.";
+ goto cleanup;
+ }
+
+ spawncfg = winpty_spawn_config_new(
+ WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN,
+ NULL, // Optional application name
+ cmd_line,
+ cwd,
+ NULL, // Optional environment variables
+ &err);
+ if (spawncfg == NULL) {
+ emsg = "Failed winpty_spawn_config_new.";
+ goto cleanup;
+ }
+
+ DWORD win_err = 0;
+ if (!winpty_spawn(winpty_object,
+ spawncfg,
+ &process_handle,
+ NULL, // Optional thread handle
+ &win_err,
+ &err)) {
+ if (win_err) {
+ status = (int)win_err;
+ emsg = "Failed spawn process.";
+ } else {
+ emsg = "Failed winpty_spawn.";
+ }
+ goto cleanup;
+ }
+ proc->pid = (int)GetProcessId(process_handle);
+
+ if (!RegisterWaitForSingleObject(
+ &ptyproc->finish_wait,
+ process_handle,
+ pty_process_finish1,
+ ptyproc,
+ INFINITE,
+ WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) {
+ abort();
+ }
+
+ // Wait until pty_process_connect_cb is called.
+ while ((in_req != NULL && in_req->handle != NULL)
+ || (out_req != NULL && out_req->handle != NULL)) {
+ uv_run(&proc->loop->uv, UV_RUN_ONCE);
+ }
+
+ ptyproc->winpty_object = winpty_object;
+ ptyproc->process_handle = process_handle;
+ winpty_object = NULL;
+ process_handle = NULL;
+
+cleanup:
+ if (status) {
+ // In the case of an error of MultiByteToWideChar or CreateProcessW.
+ ELOG("%s error code: %d", emsg, status);
+ status = os_translate_sys_error(status);
+ } else if (err != NULL) {
+ status = (int)winpty_error_code(err);
+ ELOG("%s error code: %d", emsg, status);
+ status = translate_winpty_error(status);
+ }
+ winpty_error_free(err);
+ winpty_config_free(cfg);
+ winpty_spawn_config_free(spawncfg);
+ winpty_free(winpty_object);
+ xfree(in_name);
+ xfree(out_name);
+ if (process_handle != NULL) {
+ CloseHandle(process_handle);
+ }
+ xfree(in_req);
+ xfree(out_req);
+ xfree(cmd_line);
+ xfree(cwd);
+ return status;
+}
+
+void pty_process_resize(PtyProcess *ptyproc, uint16_t width,
+ uint16_t height)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (ptyproc->winpty_object != NULL) {
+ winpty_set_size(ptyproc->winpty_object, width, height, NULL);
+ }
+}
+
+void pty_process_close(PtyProcess *ptyproc)
+ FUNC_ATTR_NONNULL_ALL
+{
+ Process *proc = (Process *)ptyproc;
+
+ pty_process_close_master(ptyproc);
+
+ if (proc->internal_close_cb) {
+ proc->internal_close_cb(proc);
+ }
+}
+
+void pty_process_close_master(PtyProcess *ptyproc)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (ptyproc->winpty_object != NULL) {
+ winpty_free(ptyproc->winpty_object);
+ ptyproc->winpty_object = NULL;
+ }
+}
+
+void pty_process_teardown(Loop *loop)
+ FUNC_ATTR_NONNULL_ALL
+{
+}
+
+static void pty_process_connect_cb(uv_connect_t *req, int status)
+ FUNC_ATTR_NONNULL_ALL
+{
+ assert(status == 0);
+ req->handle = NULL;
+}
+
+static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer)
+ FUNC_ATTR_NONNULL_ALL
+{
+ PtyProcess *ptyproc = wait_eof_timer->data;
+ Process *proc = (Process *)ptyproc;
+
+ if (proc->out.closed || !uv_is_readable(proc->out.uvstream)) {
+ uv_timer_stop(&ptyproc->wait_eof_timer);
+ pty_process_finish2(ptyproc);
+ }
+}
+
+static void pty_process_finish2(PtyProcess *ptyproc)
+ FUNC_ATTR_NONNULL_ALL
+{
+ Process *proc = (Process *)ptyproc;
+
+ UnregisterWaitEx(ptyproc->finish_wait, NULL);
+ uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL);
+
+ DWORD exit_code = 0;
+ GetExitCodeProcess(ptyproc->process_handle, &exit_code);
+ proc->status = (int)exit_code;
+
+ CloseHandle(ptyproc->process_handle);
+ ptyproc->process_handle = NULL;
+
+ proc->internal_exit_cb(proc);
+}
+
+/// Build the command line to pass to CreateProcessW.
+///
+/// @param[in] argv Array with string arguments.
+/// @param[out] cmd_line Location where saved builded cmd line.
+///
+/// @returns zero on success, or error code of MultiByteToWideChar function.
+///
+static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t utf8_cmd_line_len = 0;
+ size_t argc = 0;
+ QUEUE args_q;
+
+ QUEUE_INIT(&args_q);
+ while (*argv) {
+ size_t buf_len = is_cmdexe ? (strlen(*argv) + 1) : (strlen(*argv) * 2 + 3);
+ ArgNode *arg_node = xmalloc(sizeof(*arg_node));
+ arg_node->arg = xmalloc(buf_len);
+ if (is_cmdexe) {
+ xstrlcpy(arg_node->arg, *argv, buf_len);
+ } else {
+ quote_cmd_arg(arg_node->arg, buf_len, *argv);
+ }
+ utf8_cmd_line_len += strlen(arg_node->arg);
+ QUEUE_INIT(&arg_node->node);
+ QUEUE_INSERT_TAIL(&args_q, &arg_node->node);
+ argc++;
+ argv++;
+ }
+
+ utf8_cmd_line_len += argc;
+ char *utf8_cmd_line = xmalloc(utf8_cmd_line_len);
+ *utf8_cmd_line = NUL;
+ while (1) {
+ QUEUE *head = QUEUE_HEAD(&args_q);
+ QUEUE_REMOVE(head);
+ ArgNode *arg_node = QUEUE_DATA(head, ArgNode, node);
+ xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len);
+ xfree(arg_node->arg);
+ xfree(arg_node);
+ if (QUEUE_EMPTY(&args_q)) {
+ break;
+ } else {
+ xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len);
+ }
+ }
+
+ int result = utf8_to_utf16(utf8_cmd_line, cmd_line);
+ xfree(utf8_cmd_line);
+ return result;
+}
+
+/// Emulate quote_cmd_arg of libuv and quotes command line argument.
+/// Most of the code came from libuv.
+///
+/// @param[out] dest Location where saved quotes argument.
+/// @param dest_remaining Destination buffer size.
+/// @param[in] src Pointer to argument.
+///
+static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t src_len = strlen(src);
+ bool quote_hit = true;
+ char *start = dest;
+
+ if (src_len == 0) {
+ // Need double quotation for empty argument.
+ snprintf(dest, dest_remaining, "\"\"");
+ return;
+ }
+
+ if (NULL == strpbrk(src, " \t\"")) {
+ // No quotation needed.
+ xstrlcpy(dest, src, dest_remaining);
+ return;
+ }
+
+ if (NULL == strpbrk(src, "\"\\")) {
+ // No embedded double quotes or backlashes, so I can just wrap quote marks.
+ // around the whole thing.
+ snprintf(dest, dest_remaining, "\"%s\"", src);
+ return;
+ }
+
+ // Expected input/output:
+ // input : 'hello"world'
+ // output: '"hello\"world"'
+ // input : 'hello""world'
+ // output: '"hello\"\"world"'
+ // input : 'hello\world'
+ // output: 'hello\world'
+ // input : 'hello\\world'
+ // output: 'hello\\world'
+ // input : 'hello\"world'
+ // output: '"hello\\\"world"'
+ // input : 'hello\\"world'
+ // output: '"hello\\\\\"world"'
+ // input : 'hello world\'
+ // output: '"hello world\\"'
+
+ assert(dest_remaining--);
+ *(dest++) = NUL;
+ assert(dest_remaining--);
+ *(dest++) = '"';
+ for (size_t i = src_len; i > 0; i--) {
+ assert(dest_remaining--);
+ *(dest++) = src[i - 1];
+ if (quote_hit && src[i - 1] == '\\') {
+ assert(dest_remaining--);
+ *(dest++) = '\\';
+ } else if (src[i - 1] == '"') {
+ quote_hit = true;
+ assert(dest_remaining--);
+ *(dest++) = '\\';
+ } else {
+ quote_hit = false;
+ }
+ }
+ assert(dest_remaining);
+ *dest = '"';
+
+ while (start < dest) {
+ char tmp = *start;
+ *start = *dest;
+ *dest = tmp;
+ start++;
+ dest--;
+ }
+}
+
+/// Translate winpty error code to libuv error.
+///
+/// @param[in] winpty_errno Winpty error code returned by winpty_error_code
+/// function.
+///
+/// @returns Error code of libuv error.
+int translate_winpty_error(int winpty_errno)
+{
+ if (winpty_errno <= 0) {
+ return winpty_errno; // If < 0 then it's already a libuv error.
+ }
+
+ switch (winpty_errno) {
+ case WINPTY_ERROR_OUT_OF_MEMORY: return UV_ENOMEM;
+ case WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED: return UV_EAI_FAIL;
+ case WINPTY_ERROR_LOST_CONNECTION: return UV_ENOTCONN;
+ case WINPTY_ERROR_AGENT_EXE_MISSING: return UV_ENOENT;
+ case WINPTY_ERROR_UNSPECIFIED: return UV_UNKNOWN;
+ case WINPTY_ERROR_AGENT_DIED: return UV_ESRCH;
+ case WINPTY_ERROR_AGENT_TIMEOUT: return UV_ETIMEDOUT;
+ case WINPTY_ERROR_AGENT_CREATION_FAILED: return UV_EAI_FAIL;
+ default: return UV_UNKNOWN;
+ }
+}
diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h
index 8e2b37a1c1..1a4019e654 100644
--- a/src/nvim/os/pty_process_win.h
+++ b/src/nvim/os/pty_process_win.h
@@ -1,20 +1,27 @@
#ifndef NVIM_OS_PTY_PROCESS_WIN_H
#define NVIM_OS_PTY_PROCESS_WIN_H
-#include "nvim/event/libuv_process.h"
+#include <uv.h>
+#include <winpty.h>
+
+#include "nvim/event/process.h"
+#include "nvim/lib/queue.h"
typedef struct pty_process {
Process process;
char *term_name;
uint16_t width, height;
+ winpty_t *winpty_object;
+ HANDLE finish_wait;
+ HANDLE process_handle;
+ uv_timer_t wait_eof_timer;
} PtyProcess;
-#define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job)
-#define pty_process_close(job) libuv_process_close((LibuvProcess *)job)
-#define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job)
-#define pty_process_resize(job, width, height) ( \
- (void)job, (void)width, (void)height, 0)
-#define pty_process_teardown(loop) ((void)loop, 0)
+// Structure used by build_cmd_line()
+typedef struct arg_node {
+ char *arg; // pointer to argument.
+ QUEUE node; // QUEUE structure.
+} ArgNode;
static inline PtyProcess pty_process_init(Loop *loop, void *data)
{
@@ -23,7 +30,14 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data)
rv.term_name = NULL;
rv.width = 80;
rv.height = 24;
+ rv.winpty_object = NULL;
+ rv.finish_wait = NULL;
+ rv.process_handle = NULL;
return rv;
}
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/pty_process_win.h.generated.h"
+#endif
+
#endif // NVIM_OS_PTY_PROCESS_WIN_H
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 9d80a43718..f650a51fe7 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -123,6 +123,9 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
if (opts & kShellOptRead) {
output_ptr = &output;
forward_output = false;
+ } else if (opts & kShellOptDoOut) {
+ // Caller has already redirected output
+ forward_output = false;
}
}
@@ -189,6 +192,7 @@ static int do_os_system(char **argv,
{
out_data_decide_throttle(0); // Initialize throttle decider.
out_data_ring(NULL, 0); // Initialize output ring-buffer.
+ bool has_input = (input != NULL && input[0] != '\0');
// the output buffer
DynamicBuffer buf = DYNAMIC_BUFFER_INIT;
@@ -207,16 +211,12 @@ static int do_os_system(char **argv,
char prog[MAXPATHL];
xstrlcpy(prog, argv[0], MAXPATHL);
- Stream in, out, err;
LibuvProcess uvproc = libuv_process_init(&main_loop, &buf);
Process *proc = &uvproc.process;
MultiQueue *events = multiqueue_new_child(main_loop.events);
proc->events = events;
proc->argv = argv;
- proc->in = input != NULL ? &in : NULL;
- proc->out = &out;
- proc->err = &err;
- int status = process_spawn(proc);
+ int status = process_spawn(proc, has_input, true, true);
if (status) {
loop_poll_events(&main_loop, 0);
// Failed, probably 'shell' is not executable.
@@ -231,43 +231,54 @@ static int do_os_system(char **argv,
return -1;
}
- // We want to deal with stream events as fast a possible while queueing
- // process events, so reset everything to NULL. It prevents closing the
+ // Note: unlike process events, stream events are not queued, as we want to
+ // deal with stream events as fast a possible. It prevents closing the
// streams while there's still data in the OS buffer (due to the process
// exiting before all data is read).
- if (input != NULL) {
- proc->in->events = NULL;
- wstream_init(proc->in, 0);
+ if (has_input) {
+ wstream_init(&proc->in, 0);
}
- proc->out->events = NULL;
- rstream_init(proc->out, 0);
- rstream_start(proc->out, data_cb, &buf);
- proc->err->events = NULL;
- rstream_init(proc->err, 0);
- rstream_start(proc->err, data_cb, &buf);
+ rstream_init(&proc->out, 0);
+ rstream_start(&proc->out, data_cb, &buf);
+ rstream_init(&proc->err, 0);
+ rstream_start(&proc->err, data_cb, &buf);
// write the input, if any
- if (input) {
- WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL);
+ if (has_input) {
+ WBuffer *input_buffer = wstream_new_buffer((char *)input, len, 1, NULL);
- if (!wstream_write(&in, input_buffer)) {
+ if (!wstream_write(&proc->in, input_buffer)) {
// couldn't write, stop the process and tell the user about it
process_stop(proc);
return -1;
}
// close the input stream after everything is written
- wstream_set_write_cb(&in, shell_write_cb, NULL);
+ wstream_set_write_cb(&proc->in, shell_write_cb, NULL);
}
// Invoke busy_start here so LOOP_PROCESS_EVENTS_UNTIL will not change the
// busy state.
ui_busy_start();
ui_flush();
+ if (forward_output) {
+ msg_sb_eol();
+ msg_start();
+ msg_no_more = true;
+ lines_left = -1;
+ }
int exitcode = process_wait(proc, -1, NULL);
if (!got_int && out_data_decide_throttle(0)) {
// Last chunk of output was skipped; display it now.
out_data_ring(NULL, SIZE_MAX);
}
+ if (forward_output) {
+ // caller should decide if wait_return is invoked
+ no_wait_return++;
+ msg_end();
+ no_wait_return--;
+ msg_no_more = false;
+ }
+
ui_busy_stop();
// prepare the out parameters if requested
@@ -411,7 +422,7 @@ static void out_data_ring(char *output, size_t size)
}
if (output == NULL && size == SIZE_MAX) { // Print mode
- out_data_append_to_screen(last_skipped, last_skipped_len, true);
+ out_data_append_to_screen(last_skipped, &last_skipped_len, true);
return;
}
@@ -439,78 +450,58 @@ static void out_data_ring(char *output, size_t size)
/// @param output Data to append to screen lines.
/// @param remaining Size of data.
/// @param new_line If true, next data output will be on a new line.
-static void out_data_append_to_screen(char *output, size_t remaining,
- bool new_line)
+static void out_data_append_to_screen(char *output, size_t *count,
+ bool eof)
{
- static colnr_T last_col = 0; // Column of last row to append to.
-
- size_t off = 0;
- int last_row = (int)Rows - 1;
-
- while (off < remaining) {
- // Found end of line?
- if (output[off] == NL) {
- // Can we start a new line or do we need to continue the last one?
- if (last_col == 0) {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
+ char *p = output, *end = output + *count;
+ while (p < end) {
+ if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) {
+ msg_putchar_attr((uint8_t)(*p), 0);
+ p++;
+ } else {
+ // Note: this is not 100% precise:
+ // 1. we don't check if received continuation bytes are already invalid
+ // and we thus do some buffering that could be avoided
+ // 2. we don't compose chars over buffer boundaries, even if we see an
+ // incomplete UTF-8 sequence that could be composing with the last
+ // complete sequence.
+ // This will be corrected when we switch to vterm based implementation
+ int i = *p ? mb_ptr2len_len((char_u *)p, (int)(end-p)) : 1;
+ if (!eof && i == 1 && utf8len_tab_zero[*(uint8_t *)p] > (end-p)) {
+ *count = (size_t)(p - output);
+ goto end;
}
- screen_puts_len((char_u *)output, (int)off, last_row, last_col, 0);
- last_col = 0;
-
- size_t skip = off + 1;
- output += skip;
- remaining -= skip;
- off = 0;
- continue;
- }
- // TODO(bfredl): using msg_puts would be better until
- // terminal emulation is implemented.
- if (output[off] < 0x20) {
- output[off] = ' ';
+ (void)msg_outtrans_len_attr((char_u *)p, i, 0);
+ p += i;
}
-
- off++;
- }
-
- if (remaining) {
- if (last_col == 0) {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
- }
- screen_puts_len((char_u *)output, (int)remaining, last_row, last_col, 0);
- last_col += (colnr_T)remaining;
- }
-
- if (new_line) {
- last_col = 0;
}
+end:
ui_flush();
}
static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
bool eof)
{
- // We always output the whole buffer, so the buffer can never
- // wrap around.
size_t cnt;
char *ptr = rbuffer_read_ptr(buf, &cnt);
- if (ptr == NULL || cnt == 0) {
- // Nothing to read;
- return;
- }
-
- if (out_data_decide_throttle(cnt)) { // Skip output above a threshold.
+ if (ptr != NULL && cnt > 0
+ && out_data_decide_throttle(cnt)) { // Skip output above a threshold.
// Save the skipped output. If it is the final chunk, we display it later.
out_data_ring(ptr, cnt);
} else {
- out_data_append_to_screen(ptr, cnt, eof);
+ out_data_append_to_screen(ptr, &cnt, eof);
}
if (cnt) {
rbuffer_consumed(buf, cnt);
}
+
+ // Move remaining data to start of buffer, so the buffer can never
+ // wrap around.
+ rbuffer_reset(buf);
}
/// Parses a command string into a sequence of words, taking quotes into
@@ -599,14 +590,10 @@ static void read_input(DynamicBuffer *buf)
if (len == l) {
// Finished a line, add a NL, unless this line should not have one.
- // FIXME need to make this more readable
if (lnum != curbuf->b_op_end.lnum
- || (!curbuf->b_p_bin
- && curbuf->b_p_fixeol)
+ || (!curbuf->b_p_bin && curbuf->b_p_fixeol)
|| (lnum != curbuf->b_no_eol_lnum
- && (lnum !=
- curbuf->b_ml.ml_line_count
- || curbuf->b_p_eol))) {
+ && (lnum != curbuf->b_ml.ml_line_count || curbuf->b_p_eol))) {
dynamic_buffer_ensure(buf, buf->len + 1);
buf->data[buf->len++] = NL;
}
@@ -688,10 +675,6 @@ static void shell_write_cb(Stream *stream, void *data, int status)
msg_schedule_emsgf(_("E5677: Error writing input to shell-command: %s"),
uv_err_name(status));
}
- if (stream->closed) { // Process may have exited before this write.
- WLOG("stream was already closed");
- return;
- }
stream_close(stream, NULL, NULL);
}
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index fd6d3b32e4..732be072e1 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -10,6 +10,7 @@
#endif
#include "nvim/ascii.h"
+#include "nvim/log.h"
#include "nvim/vim.h"
#include "nvim/globals.h"
#include "nvim/memline.h"
@@ -162,7 +163,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
}
break;
default:
- fprintf(stderr, "Invalid signal %d", signum);
+ ELOG("invalid signal: %d", signum);
break;
}
}
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index c471352c02..290d421acc 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -38,33 +38,33 @@ uint64_t os_hrtime(void)
return uv_hrtime();
}
-/// Sleeps for a certain amount of milliseconds.
+/// Sleeps for `ms` milliseconds.
///
-/// @param milliseconds Number of milliseconds to sleep
+/// @param ms Number of milliseconds to sleep
/// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt.
-void os_delay(uint64_t milliseconds, bool ignoreinput)
+void os_delay(uint64_t ms, bool ignoreinput)
{
if (ignoreinput) {
- if (milliseconds > INT_MAX) {
- milliseconds = INT_MAX;
+ if (ms > INT_MAX) {
+ ms = INT_MAX;
}
- LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int);
+ LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms, got_int);
} else {
- os_microdelay(milliseconds * 1000u, ignoreinput);
+ os_microdelay(ms * 1000u, ignoreinput);
}
}
-/// Sleeps for a certain amount of microseconds.
+/// Sleeps for `us` microseconds.
///
-/// @param ms Number of microseconds to sleep.
+/// @param us Number of microseconds to sleep.
/// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C).
/// If false, waiting is aborted on any input.
-void os_microdelay(uint64_t ms, bool ignoreinput)
+void os_microdelay(uint64_t us, bool ignoreinput)
{
uint64_t elapsed = 0u;
uint64_t base = uv_hrtime();
// Convert microseconds to nanoseconds, or UINT64_MAX on overflow.
- const uint64_t ns = (ms < UINT64_MAX / 1000u) ? ms * 1000u : UINT64_MAX;
+ const uint64_t ns = (us < UINT64_MAX / 1000u) ? us * 1000u : UINT64_MAX;
uv_mutex_lock(&delay_mutex);
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index 5c9daca476..60a2dfa882 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -1,9 +1,8 @@
#ifndef NVIM_OS_UNIX_DEFS_H
#define NVIM_OS_UNIX_DEFS_H
-// Windows doesn't have unistd.h, so we include it here to avoid numerous
-// instances of `#ifdef WIN32'.
#include <unistd.h>
+#include <sys/param.h>
// POSIX.1-2008 says that NAME_MAX should be in here
#include <limits.h>
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 7c980c3768..db93f016bf 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -30,8 +30,10 @@
#define USE_CRNL
-// We have our own RGB macro in macros.h.
-#undef RGB
+// Windows defines a RGB macro that produces 0x00bbggrr color values for use
+// with GDI. Our macro is different, and we don't use GDI.
+// Duplicated from macros.h to avoid include-order sensitivity.
+#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
#ifdef _MSC_VER
# ifndef inline
@@ -40,6 +42,9 @@
# ifndef restrict
# define restrict __restrict
# endif
+# ifndef STDIN_FILENO
+# define STDIN_FILENO _fileno(stdin)
+# endif
# ifndef STDOUT_FILENO
# define STDOUT_FILENO _fileno(stdout)
# endif
@@ -55,6 +60,7 @@
#ifdef _MSC_VER
typedef SSIZE_T ssize_t;
+typedef int mode_t;
#endif
#ifndef SSIZE_MAX
@@ -91,4 +97,14 @@ typedef SSIZE_T ssize_t;
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#endif
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+# define STDERR_FILENO 2
+#endif
+
#endif // NVIM_OS_WIN_DEFS_H
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 692bcc97f4..a27fee4e90 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -133,7 +133,8 @@ void mch_free_acl(vim_acl_T aclent)
}
#endif
-void mch_exit(int r) FUNC_ATTR_NORETURN
+void mch_exit(int r)
+ FUNC_ATTR_NORETURN
{
exiting = true;
@@ -144,7 +145,9 @@ void mch_exit(int r) FUNC_ATTR_NORETURN
if (!event_teardown() && r == 0) {
r = 1; // Exit with error if main_loop did not teardown gracefully.
}
- stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
+ if (input_global_fd() >= 0) {
+ stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
+ }
#ifdef EXITFREE
free_all_mem();
diff --git a/src/nvim/path.c b/src/nvim/path.c
index f2339c8046..168d835a66 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -332,7 +332,7 @@ int path_fnamencmp(const char *const fname1, const char *const fname2,
&& (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) {
break;
}
- len -= MB_PTR2LEN((const char_u *)p1);
+ len -= (size_t)MB_PTR2LEN((const char_u *)p1);
p1 += MB_PTR2LEN((const char_u *)p1);
p2 += MB_PTR2LEN((const char_u *)p2);
}
@@ -452,10 +452,10 @@ char *FullName_save(const char *fname, bool force)
/// Saves the absolute path.
/// @param name An absolute or relative path.
/// @return The absolute path of `name`.
-char_u *save_absolute_path(const char_u *name)
+char_u *save_abs_path(const char_u *name)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- if (!path_is_absolute_path(name)) {
+ if (!path_is_absolute(name)) {
return (char_u *)FullName_save((char *)name, true);
}
return vim_strsave((char_u *) name);
@@ -814,7 +814,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
STRCPY(buf, curdir); // relative to current directory
} else if (path_with_url((char *)buf)) {
continue; // URL can't be used here
- } else if (!path_is_absolute_path(buf)) {
+ } else if (!path_is_absolute(buf)) {
// Expand relative path to their full path equivalent
size_t len = STRLEN(curdir);
if (len + STRLEN(buf) + 3 > MAXPATHL) {
@@ -949,19 +949,17 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
}
}
- if (path_is_absolute_path(path)) {
- /*
- * Last resort: shorten relative to curdir if possible.
- * 'possible' means:
- * 1. It is under the current directory.
- * 2. The result is actually shorter than the original.
- *
- * Before curdir After
- * /foo/bar/file.txt /foo/bar ./file.txt
- * c:\foo\bar\file.txt c:\foo\bar .\file.txt
- * /file.txt / /file.txt
- * c:\file.txt c:\ .\file.txt
- */
+ if (path_is_absolute(path)) {
+ // Last resort: shorten relative to curdir if possible.
+ // 'possible' means:
+ // 1. It is under the current directory.
+ // 2. The result is actually shorter than the original.
+ //
+ // Before curdir After
+ // /foo/bar/file.txt /foo/bar ./file.txt
+ // c:\foo\bar\file.txt c:\foo\bar .\file.txt
+ // /file.txt / /file.txt
+ // c:\file.txt c:\ .\file.txt
short_name = path_shorten_fname(path, curdir);
if (short_name != NULL && short_name > path + 1
) {
@@ -1221,7 +1219,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
*/
if (path_has_exp_wildcard(p)) {
if ((flags & EW_PATH)
- && !path_is_absolute_path(p)
+ && !path_is_absolute(p)
&& !(p[0] == '.'
&& (vim_ispathsep(p[1])
|| (p[1] == '.' && vim_ispathsep(p[2]))))
@@ -1667,7 +1665,7 @@ int path_with_url(const char *fname)
*/
bool vim_isAbsName(char_u *name)
{
- return path_with_url((char *)name) != 0 || path_is_absolute_path(name);
+ return path_with_url((char *)name) != 0 || path_is_absolute(name);
}
/// Save absolute file name to "buf[len]".
@@ -1690,6 +1688,9 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (strlen(fname) > (len - 1)) {
xstrlcpy(buf, fname, len); // truncate
+#ifdef WIN32
+ slash_adjust((char_u *)buf);
+#endif
return FAIL;
}
@@ -1698,10 +1699,13 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
return OK;
}
- int rv = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
+ int rv = path_to_absolute((char_u *)fname, (char_u *)buf, len, force);
if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename
}
+#ifdef WIN32
+ slash_adjust((char_u *)buf);
+#endif
return rv;
}
@@ -1735,7 +1739,7 @@ char *fix_fname(const char *fname)
path_fix_case((char_u *)fname); // set correct case for file name
# endif
- return fname;
+ return (char *)fname;
#endif
}
@@ -1904,7 +1908,7 @@ int pathcmp(const char *p, const char *q, int maxlen)
/// - Pointer into `full_path` if shortened.
/// - `full_path` unchanged if no shorter name is possible.
/// - NULL if `full_path` is NULL.
-char_u *path_shorten_fname_if_possible(char_u *full_path)
+char_u *path_try_shorten_fname(char_u *full_path)
{
char_u *dirname = xmalloc(MAXPATHL);
char_u *p = full_path;
@@ -2185,8 +2189,8 @@ int append_path(char *path, const char *to_append, size_t max_len)
/// @param force also expand when "fname" is already absolute.
///
/// @return FAIL for failure, OK for success.
-static int path_get_absolute_path(const char_u *fname, char_u *buf,
- size_t len, int force)
+static int path_to_absolute(const char_u *fname, char_u *buf, size_t len,
+ int force)
{
char_u *p;
*buf = NUL;
@@ -2195,12 +2199,18 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf,
char *end_of_path = (char *) fname;
// expand it if forced or not an absolute path
- if (force || !path_is_absolute_path(fname)) {
- if ((p = vim_strrchr(fname, '/')) != NULL) {
+ if (force || !path_is_absolute(fname)) {
+ p = vim_strrchr(fname, '/');
+#ifdef WIN32
+ if (p == NULL) {
+ p = vim_strrchr(fname, '\\');
+ }
+#endif
+ if (p != NULL) {
// relative to root
if (p == fname) {
// only one path component
- relative_directory[0] = '/';
+ relative_directory[0] = PATHSEP;
relative_directory[1] = NUL;
} else {
assert(p >= fname);
@@ -2222,10 +2232,10 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf,
return append_path((char *)buf, end_of_path, len);
}
-/// Check if the given file is absolute.
+/// Check if file `fname` is a full (absolute) path.
///
/// @return `TRUE` if "fname" is absolute.
-int path_is_absolute_path(const char_u *fname)
+int path_is_absolute(const char_u *fname)
{
#ifdef WIN32
// A name like "d:/foo" and "//server/share" is absolute
@@ -2250,7 +2260,7 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
{
char *path = getenv("PATH");
- if (path == NULL || path_is_absolute_path((char_u *)argv0)) {
+ if (path == NULL || path_is_absolute((char_u *)argv0)) {
xstrlcpy(buf, argv0, bufsize);
} else if (argv0[0] == '.' || strchr(argv0, PATHSEP)) {
// Relative to CWD.
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index eb6be42688..f42ad04409 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -26,7 +26,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim 6.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
+"POT-Creation-Date: 2017-11-07 20:04+0100\n"
"PO-Revision-Date: Wed Oct 31 13:41 SAST 2001\n"
"Last-Translator: Danie Roux <droux@tuks.co.za>\n"
"Language-Team: Danie Roux <droux@tuks.co.za>\n"
@@ -35,101 +35,74 @@ msgstr ""
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
-#: ../api/private/helpers.c:201
-#, fuzzy
-msgid "Unable to get option value"
-msgstr "E258: Kan nie na klint stuur nie"
-
-#: ../api/private/helpers.c:204
-msgid "internal error: unknown option type"
-msgstr ""
-
-#: ../buffer.c:92
-msgid "[Location List]"
-msgstr ""
+#~ msgid "[Location List]"
+#~ msgstr ""
-#: ../buffer.c:93
-msgid "[Quickfix List]"
-msgstr ""
+#~ msgid "[Quickfix List]"
+#~ msgstr ""
-#: ../buffer.c:94
-msgid "E855: Autocommands caused command to abort"
-msgstr ""
+#~ msgid "E855: Autocommands caused command to abort"
+#~ msgstr ""
-#: ../buffer.c:135
msgid "E82: Cannot allocate any buffer, exiting..."
msgstr "E82: Kan nie buffer toeken nie, program sluit..."
-#: ../buffer.c:138
msgid "E83: Cannot allocate buffer, using other one..."
msgstr "E83: Kan nie buffer toeken nie, gaan ander een gebruik..."
-#: ../buffer.c:763
+#~ msgid "E937: Attempt to delete a buffer that is in use"
+#~ msgstr ""
+
msgid "E515: No buffers were unloaded"
msgstr "E515: Geen buffers is uitgelaai nie"
-#: ../buffer.c:765
msgid "E516: No buffers were deleted"
msgstr "E516: Geen buffers is geskrap nie"
-#: ../buffer.c:767
msgid "E517: No buffers were wiped out"
msgstr "E517: Geen buffers is geskrap nie"
-#: ../buffer.c:772
msgid "1 buffer unloaded"
msgstr "1 buffer uitgelaai"
-#: ../buffer.c:774
#, c-format
msgid "%d buffers unloaded"
msgstr "%d buffers uitgelaai"
-#: ../buffer.c:777
msgid "1 buffer deleted"
msgstr "1 buffer geskrap"
-#: ../buffer.c:779
#, c-format
msgid "%d buffers deleted"
msgstr "%d buffers geskrap"
-#: ../buffer.c:782
msgid "1 buffer wiped out"
msgstr "1 buffer geskrap"
-#: ../buffer.c:784
#, c-format
msgid "%d buffers wiped out"
msgstr "%d buffers geskrap"
-#: ../buffer.c:806
msgid "E90: Cannot unload last buffer"
msgstr "E90: Kan nie laaste buffer uitlaai nie"
-#: ../buffer.c:874
msgid "E84: No modified buffer found"
msgstr "E84: Geen veranderde buffer gevind nie"
#. back where we started, didn't find anything.
-#: ../buffer.c:903
msgid "E85: There is no listed buffer"
msgstr "E85: Daar is geen gelyste buffer nie"
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: Buffer %<PRId64> bestaan nie"
-
-#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: Kan nie verby laaste buffer gaan nie"
-#: ../buffer.c:917
msgid "E88: Cannot go before first buffer"
msgstr "E88: Kan nie vr eerste buffer gaan nie"
-#: ../buffer.c:945
+#, fuzzy, c-format
+#~ msgid "E89: %s will be killed(add ! to override)"
+#~ msgstr "E189: \"%s\" bestaan (gebruik ! om te dwing)"
+
#, c-format
msgid ""
"E89: No write since last change for buffer %<PRId64> (add ! to override)"
@@ -138,117 +111,85 @@ msgstr ""
"dwing)"
#. wrap around (may cause duplicates)
-#: ../buffer.c:1423
msgid "W14: Warning: List of file names overflow"
msgstr "W14: Waarskuwing: Lerlys loop oor"
-#: ../buffer.c:1555 ../quickfix.c:3361
#, c-format
msgid "E92: Buffer %<PRId64> not found"
msgstr "E92: buffer %<PRId64> kon nie gevind word nie"
-#: ../buffer.c:1798
#, c-format
msgid "E93: More than one match for %s"
msgstr "E93: Meer as een treffer vir %s"
-#: ../buffer.c:1800
#, c-format
msgid "E94: No matching buffer for %s"
msgstr "E94: Geen buffer wat by %s pas nie"
-#: ../buffer.c:2161
#, c-format
msgid "line %<PRId64>"
msgstr "rel %<PRId64>"
-#: ../buffer.c:2233
msgid "E95: Buffer with this name already exists"
msgstr "E95: Buffer met hierdie naam bestaan alreeds"
-#: ../buffer.c:2498
msgid " [Modified]"
msgstr " [Gewysig]"
-#: ../buffer.c:2501
msgid "[Not edited]"
msgstr "[Ongewysig]"
-#: ../buffer.c:2504
msgid "[New file]"
msgstr "[Nuwe ler]"
-#: ../buffer.c:2505
msgid "[Read errors]"
msgstr "[Leesfoute]"
-#: ../buffer.c:2506 ../buffer.c:3217 ../fileio.c:1807 ../screen.c:4895
msgid "[RO]"
msgstr "[RO]"
-#: ../buffer.c:2507 ../fileio.c:1807
msgid "[readonly]"
msgstr "[lees alleen]"
-#: ../buffer.c:2524
#, c-format
msgid "1 line --%d%%--"
msgstr "1 rel --%d%%--"
-#: ../buffer.c:2526
#, c-format
msgid "%<PRId64> lines --%d%%--"
msgstr "%<PRId64> rels --%d%%--"
-#: ../buffer.c:2530
#, c-format
msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
msgstr "rel %<PRId64> van %<PRId64> --%d%%-- kolom "
-#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
#, fuzzy
-msgid "[No Name]"
-msgstr "[Geen ler]"
+#~ msgid "[No Name]"
+#~ msgstr "[Geen ler]"
#. must be a help buffer
-#: ../buffer.c:2667
msgid "help"
msgstr "help"
-#: ../buffer.c:3225 ../screen.c:4883
#, fuzzy
-msgid "[Help]"
-msgstr "[help]"
+#~ msgid "[Help]"
+#~ msgstr "[help]"
-#: ../buffer.c:3254 ../screen.c:4887
msgid "[Preview]"
msgstr "[Voorskou]"
-#: ../buffer.c:3528
msgid "All"
msgstr "Alles"
-#: ../buffer.c:3528
msgid "Bot"
msgstr "Ond"
-#: ../buffer.c:3531
msgid "Top"
msgstr "Bo"
-#: ../buffer.c:4244
-msgid ""
-"\n"
-"# Buffer list:\n"
-msgstr ""
-"\n"
-"# Buffer lys:\n"
-
-#: ../buffer.c:4289
-msgid "[Scratch]"
-msgstr ""
+#~ msgid "[Scratch]"
+#~ msgstr ""
-#: ../buffer.c:4529
msgid ""
"\n"
"--- Signs ---"
@@ -256,208 +197,161 @@ msgstr ""
"\n"
"--- Tekens ---"
-#: ../buffer.c:4538
#, c-format
msgid "Signs for %s:"
msgstr "Tekens vir %s:"
-#: ../buffer.c:4543
#, c-format
msgid " line=%<PRId64> id=%d name=%s"
msgstr " rel=%<PRId64> id=%d naam=%s"
-#: ../cursor_shape.c:68
msgid "E545: Missing colon"
msgstr "E545: Ontbrekende dubbelpunt"
-#: ../cursor_shape.c:70 ../cursor_shape.c:94
msgid "E546: Illegal mode"
msgstr "E546: Ongeldige modus"
-#: ../cursor_shape.c:134
msgid "E548: digit expected"
msgstr "E548: syfer verwag"
-#: ../cursor_shape.c:138
msgid "E549: Illegal percentage"
msgstr "E549: Ongeldige persentasie"
-#: ../diff.c:146
-#, c-format
-msgid "E96: Can not diff more than %<PRId64> buffers"
-msgstr "E96: Kan nie meer as %<PRId64> buffers 'diff' nie"
+#, fuzzy, c-format
+#~ msgid "E96: Cannot diff more than %<PRId64> buffers"
+#~ msgstr "E96: Kan nie meer as %<PRId64> buffers 'diff' nie"
-#: ../diff.c:753
#, fuzzy
-msgid "E810: Cannot read or write temp files"
-msgstr "E557: Kan nie 'termcap'-ler oopmaak nie"
+#~ msgid "E810: Cannot read or write temp files"
+#~ msgstr "E557: Kan nie 'termcap'-ler oopmaak nie"
-#: ../diff.c:755
msgid "E97: Cannot create diffs"
msgstr "E97: Kan nie 'diffs' skep nie "
-#: ../diff.c:966
#, fuzzy
-msgid "E816: Cannot read patch output"
-msgstr "E98: Kan nie 'diff' afvoer lees nie"
+#~ msgid "E816: Cannot read patch output"
+#~ msgstr "E98: Kan nie 'diff' afvoer lees nie"
-#: ../diff.c:1220
msgid "E98: Cannot read diff output"
msgstr "E98: Kan nie 'diff' afvoer lees nie"
-#: ../diff.c:2081
msgid "E99: Current buffer is not in diff mode"
msgstr "E99: Huidige buffer is nie in 'diff' modus nie"
-#: ../diff.c:2100
#, fuzzy
-msgid "E793: No other buffer in diff mode is modifiable"
-msgstr "E100: Geen ander buffer in 'diff' modus nie"
+#~ msgid "E793: No other buffer in diff mode is modifiable"
+#~ msgstr "E100: Geen ander buffer in 'diff' modus nie"
-#: ../diff.c:2102
msgid "E100: No other buffer in diff mode"
msgstr "E100: Geen ander buffer in 'diff' modus nie"
-#: ../diff.c:2112
msgid "E101: More than two buffers in diff mode, don't know which one to use"
msgstr ""
"E101: Meer as twee buffers in 'diff' modus, weet nie watter een om te "
"gebruik nie"
-#: ../diff.c:2141
#, c-format
msgid "E102: Can't find buffer \"%s\""
msgstr "E102: Kan buffer %s nie vind nie"
-#: ../diff.c:2152
#, c-format
msgid "E103: Buffer \"%s\" is not in diff mode"
msgstr "E103: Buffer \"%s\" is nie in 'diff' modus nie"
-#: ../diff.c:2193
-msgid "E787: Buffer changed unexpectedly"
-msgstr ""
+#~ msgid "E787: Buffer changed unexpectedly"
+#~ msgstr ""
-#: ../digraph.c:1598
msgid "E104: Escape not allowed in digraph"
msgstr "E104: 'Escape' nie toegelaat in digraaf nie"
-#: ../digraph.c:1760
msgid "E544: Keymap file not found"
msgstr "E544: Sleutelbindingler nie gevind nie"
-#: ../digraph.c:1785
msgid "E105: Using :loadkeymap not in a sourced file"
msgstr "E105: :loadkeymap word buite 'n uitvoerler gebruik"
-#: ../digraph.c:1821
-msgid "E791: Empty keymap entry"
-msgstr ""
+#~ msgid "E791: Empty keymap entry"
+#~ msgstr ""
-#: ../edit.c:82
msgid " Keyword completion (^N^P)"
msgstr " Sleutelwoord voltooiing (^N^P)"
#. ctrl_x_mode == 0, ^P/^N compl.
-#: ../edit.c:83
#, fuzzy
-msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
-msgstr " ^X modus (^E^Y^L^]^F^I^K^D^V^N^P)"
+#~ msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+#~ msgstr " ^X modus (^E^Y^L^]^F^I^K^D^V^N^P)"
-#: ../edit.c:85
msgid " Whole line completion (^L^N^P)"
msgstr " Hele-rel voltooiing (^L^N^P)"
-#: ../edit.c:86
msgid " File name completion (^F^N^P)"
msgstr " Lernaam voltooiing (^F^N^P)"
-#: ../edit.c:87
msgid " Tag completion (^]^N^P)"
msgstr " Etiketvoltooiing (^]^N^P)"
-#: ../edit.c:88
msgid " Path pattern completion (^N^P)"
msgstr " Gidspatroon voltooiing (^N^P)"
-#: ../edit.c:89
msgid " Definition completion (^D^N^P)"
msgstr " Definisievoltooiing (^D^N^P)"
-#: ../edit.c:91
msgid " Dictionary completion (^K^N^P)"
msgstr " Woordeboekvoltooiing (^K^N^P)"
-#: ../edit.c:92
msgid " Thesaurus completion (^T^N^P)"
msgstr " Tesourusvoltooiing (^T^N^P)"
-#: ../edit.c:93
msgid " Command-line completion (^V^N^P)"
msgstr " Bevelrelvoltooiing (^V^N^P)"
-#: ../edit.c:94
#, fuzzy
-msgid " User defined completion (^U^N^P)"
-msgstr " Hele-rel voltooiing (^L^N^P)"
+#~ msgid " User defined completion (^U^N^P)"
+#~ msgstr " Hele-rel voltooiing (^L^N^P)"
-#: ../edit.c:95
#, fuzzy
-msgid " Omni completion (^O^N^P)"
-msgstr " Etiketvoltooiing (^]^N^P)"
+#~ msgid " Omni completion (^O^N^P)"
+#~ msgstr " Etiketvoltooiing (^]^N^P)"
-#: ../edit.c:96
#, fuzzy
-msgid " Spelling suggestion (s^N^P)"
-msgstr " Hele-rel voltooiing (^L^N^P)"
+#~ msgid " Spelling suggestion (s^N^P)"
+#~ msgstr " Hele-rel voltooiing (^L^N^P)"
-#: ../edit.c:97
msgid " Keyword Local completion (^N^P)"
msgstr " Sleutelwoord Lokale voltooiing (^N^P)"
-#: ../edit.c:100
msgid "Hit end of paragraph"
msgstr "Het einde van paragraaf getref"
-#: ../edit.c:101
-msgid "E839: Completion function changed window"
-msgstr ""
+#~ msgid "E839: Completion function changed window"
+#~ msgstr ""
-#: ../edit.c:102
-msgid "E840: Completion function deleted text"
-msgstr ""
+#~ msgid "E840: Completion function deleted text"
+#~ msgstr ""
-#: ../edit.c:1847
msgid "'dictionary' option is empty"
msgstr "'dictionary' opsie is leeg"
-#: ../edit.c:1848
msgid "'thesaurus' option is empty"
msgstr "'thesaurus' opsie is leeg"
-#: ../edit.c:2655
#, c-format
msgid "Scanning dictionary: %s"
msgstr "Deursoek woordeboek: %s"
-#: ../edit.c:3079
msgid " (insert) Scroll (^E/^Y)"
msgstr " (invoeg) Rol (^E/^Y)"
-#: ../edit.c:3081
msgid " (replace) Scroll (^E/^Y)"
msgstr " (vervang) Rol (^E/^Y)"
-#: ../edit.c:3587
#, c-format
msgid "Scanning: %s"
msgstr "Soek vir: %s"
-#: ../edit.c:3614
msgid "Scanning tags."
msgstr "Deursoek etikette."
-#: ../edit.c:4519
msgid " Adding"
msgstr " Word bygevoeg"
@@ -465,605 +359,550 @@ msgstr " Word bygevoeg"
#. * be called before line = ml_get(), or when this address is no
#. * longer needed. -- Acevedo.
#.
-#: ../edit.c:4562
msgid "-- Searching..."
msgstr "-- Soekend..."
-#: ../edit.c:4618
msgid "Back at original"
msgstr "Terug by oorspronklike"
-#: ../edit.c:4621
msgid "Word from other line"
msgstr "Woord van ander rel"
-#: ../edit.c:4624
msgid "The only match"
msgstr "Die enigste treffer"
-#: ../edit.c:4680
#, c-format
msgid "match %d of %d"
msgstr "treffer %d van %d"
-#: ../edit.c:4684
#, c-format
msgid "match %d"
msgstr "treffer %d"
-#: ../eval.c:137
#, fuzzy
-msgid "E18: Unexpected characters in :let"
-msgstr "E18: Onverwagte karakters voor '='"
+#~ msgid "E18: Unexpected characters in :let"
+#~ msgstr "E18: Onverwagte karakters voor '='"
-#: ../eval.c:138
-#, fuzzy, c-format
-msgid "E684: list index out of range: %<PRId64>"
-msgstr "E322: relnommer buite perke: %<PRId64> verby die einde"
-
-#: ../eval.c:139
-#, c-format
-msgid "E121: Undefined variable: %s"
-msgstr "E121: Ongedefinieerde veranderlike: %s"
-
-#: ../eval.c:140
msgid "E111: Missing ']'"
msgstr "E111: Ontbrekende ']'"
-#: ../eval.c:141
#, fuzzy, c-format
-msgid "E686: Argument of %s must be a List"
-msgstr "E487: Parameter moet positief wees"
+#~ msgid "E686: Argument of %s must be a List"
+#~ msgstr "E487: Parameter moet positief wees"
-#: ../eval.c:143
#, fuzzy, c-format
-msgid "E712: Argument of %s must be a List or Dictionary"
-msgstr "E487: Parameter moet positief wees"
+#~ msgid "E712: Argument of %s must be a List or Dictionary"
+#~ msgstr "E487: Parameter moet positief wees"
-#: ../eval.c:144
#, fuzzy
-msgid "E713: Cannot use empty key for Dictionary"
-msgstr "E214: Kan nie tydelike ler vind vir skryf nie"
+#~ msgid "E714: List required"
+#~ msgstr "E471: Parameter benodig"
-#: ../eval.c:145
#, fuzzy
-msgid "E714: List required"
-msgstr "E471: Parameter benodig"
+#~ msgid "E715: Dictionary required"
+#~ msgstr "E129: Funksienaam vereis"
-#: ../eval.c:146
#, fuzzy
-msgid "E715: Dictionary required"
-msgstr "E129: Funksienaam vereis"
+#~ msgid "E928: String required"
+#~ msgstr "E397: Lernaam benodig"
-#: ../eval.c:147
#, c-format
msgid "E118: Too many arguments for function: %s"
msgstr "E118: Te veel parameters vir funksie: %s"
-#: ../eval.c:148
#, c-format
-msgid "E716: Key not present in Dictionary: %s"
-msgstr ""
+#~ msgid "E716: Key not present in Dictionary: %s"
+#~ msgstr ""
-#: ../eval.c:150
#, c-format
msgid "E122: Function %s already exists, add ! to replace it"
msgstr "E122: Funksie %s bestaan alreeds, gebruik ! om te vervang"
-#: ../eval.c:151
#, fuzzy
-msgid "E717: Dictionary entry already exists"
-msgstr "E95: Buffer met hierdie naam bestaan alreeds"
+#~ msgid "E717: Dictionary entry already exists"
+#~ msgstr "E95: Buffer met hierdie naam bestaan alreeds"
-#: ../eval.c:152
#, fuzzy
-msgid "E718: Funcref required"
-msgstr "E129: Funksienaam vereis"
+#~ msgid "E718: Funcref required"
+#~ msgstr "E129: Funksienaam vereis"
-#: ../eval.c:153
#, fuzzy
-msgid "E719: Cannot use [:] with a Dictionary"
-msgstr "E360: Kan nie dop met -f opsie uitvoer nie"
+#~ msgid "E719: Cannot use [:] with a Dictionary"
+#~ msgstr "E360: Kan nie dop met -f opsie uitvoer nie"
-#: ../eval.c:154
-#, c-format
-msgid "E734: Wrong variable type for %s="
-msgstr ""
-
-#: ../eval.c:155
#, fuzzy, c-format
-msgid "E130: Unknown function: %s"
-msgstr "E117: Onbekende funksie: %s"
+#~ msgid "E130: Unknown function: %s"
+#~ msgstr "E117: Onbekende funksie: %s"
-#: ../eval.c:156
#, c-format
msgid "E461: Illegal variable name: %s"
msgstr "E461: Ongeldige veranderlikenaam: %s"
-#: ../eval.c:157
-msgid "E806: using Float as a String"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E46: Cannot change read-only variable \"%.*s\""
+#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
-#: ../eval.c:1830
-msgid "E687: Less targets than List items"
-msgstr ""
+#. TODO(ZyX-I): move to eval/executor
+#, c-format
+#~ msgid "E734: Wrong variable type for %s="
+#~ msgstr ""
-#: ../eval.c:1834
-msgid "E688: More targets than List items"
-msgstr ""
+#~ msgid "E687: Less targets than List items"
+#~ msgstr ""
-#: ../eval.c:1906
-msgid "Double ; in list of variables"
-msgstr ""
+#~ msgid "E688: More targets than List items"
+#~ msgstr ""
+
+#~ msgid "Double ; in list of variables"
+#~ msgstr ""
-#: ../eval.c:2078
#, fuzzy, c-format
-msgid "E738: Can't list variables for %s"
-msgstr "E138: Kan nie viminfo ler %s stoor nie!"
+#~ msgid "E738: Can't list variables for %s"
+#~ msgstr "E138: Kan nie viminfo ler %s stoor nie!"
-#: ../eval.c:2391
-msgid "E689: Can only index a List or Dictionary"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E121: Undefined variable: %.*s"
+#~ msgstr "E121: Ongedefinieerde veranderlike: %s"
-#: ../eval.c:2396
-msgid "E708: [:] must come last"
-msgstr ""
+#~ msgid "E689: Can only index a List or Dictionary"
+#~ msgstr ""
-#: ../eval.c:2439
-msgid "E709: [:] requires a List value"
-msgstr ""
+#~ msgid "E708: [:] must come last"
+#~ msgstr ""
-#: ../eval.c:2674
-msgid "E710: List value has more items than target"
-msgstr ""
+#, fuzzy
+#~ msgid "E713: Cannot use empty key after ."
+#~ msgstr "E214: Kan nie tydelike ler vind vir skryf nie"
-#: ../eval.c:2678
-msgid "E711: List value has not enough items"
-msgstr ""
+#~ msgid "E709: [:] requires a List value"
+#~ msgstr ""
+
+#~ msgid "E710: List value has more items than target"
+#~ msgstr ""
+
+#~ msgid "E711: List value has not enough items"
+#~ msgstr ""
-#: ../eval.c:2867
#, fuzzy
-msgid "E690: Missing \"in\" after :for"
-msgstr "E69: Ontbrekende ] na %s%%["
+#~ msgid "E690: Missing \"in\" after :for"
+#~ msgstr "E69: Ontbrekende ] na %s%%["
-#: ../eval.c:3063
#, c-format
msgid "E107: Missing parentheses: %s"
msgstr "E107: Ontbrekende hakies: %s"
-#: ../eval.c:3263
#, c-format
msgid "E108: No such variable: \"%s\""
msgstr "E108: Geen veranderlike: \"%s\""
-#: ../eval.c:3333
-msgid "E743: variable nested too deep for (un)lock"
-msgstr ""
+#. For historical reasons this error is not given for Lists and
+#. Dictionaries. E.g. b: dictionary may be locked/unlocked.
+#, fuzzy, c-format
+#~ msgid "E940: Cannot lock or unlock variable %s"
+#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
-#: ../eval.c:3630
msgid "E109: Missing ':' after '?'"
msgstr "E109: Ontbrekende ':' na '?'"
-#: ../eval.c:3893
-msgid "E691: Can only compare List with List"
-msgstr ""
+#~ msgid "E691: Can only compare List with List"
+#~ msgstr ""
-#: ../eval.c:3895
#, fuzzy
-msgid "E692: Invalid operation for Lists"
-msgstr "E449: Ongeldige uitdrukking ontvang"
+#~ msgid "E692: Invalid operation for List"
+#~ msgstr "E449: Ongeldige uitdrukking ontvang"
-#: ../eval.c:3915
-msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr ""
+#~ msgid "E735: Can only compare Dictionary with Dictionary"
+#~ msgstr ""
-#: ../eval.c:3917
#, fuzzy
-msgid "E736: Invalid operation for Dictionary"
-msgstr "E116: Ongeldige parameters vir funksie %s"
-
-#: ../eval.c:3932
-msgid "E693: Can only compare Funcref with Funcref"
-msgstr ""
+#~ msgid "E736: Invalid operation for Dictionary"
+#~ msgstr "E116: Ongeldige parameters vir funksie %s"
-#: ../eval.c:3934
#, fuzzy
-msgid "E694: Invalid operation for Funcrefs"
-msgstr "E116: Ongeldige parameters vir funksie %s"
+#~ msgid "E694: Invalid operation for Funcrefs"
+#~ msgstr "E116: Ongeldige parameters vir funksie %s"
-#: ../eval.c:4277
#, fuzzy
-msgid "E804: Cannot use '%' with Float"
-msgstr "E360: Kan nie dop met -f opsie uitvoer nie"
+#~ msgid "E804: Cannot use '%' with Float"
+#~ msgstr "E360: Kan nie dop met -f opsie uitvoer nie"
-#: ../eval.c:4478
msgid "E110: Missing ')'"
msgstr "E110: Ontbrekende ')'"
-#: ../eval.c:4609
#, fuzzy
-msgid "E695: Cannot index a Funcref"
-msgstr "E90: Kan nie laaste buffer uitlaai nie"
+#~ msgid "E695: Cannot index a Funcref"
+#~ msgstr "E90: Kan nie laaste buffer uitlaai nie"
+
+#, fuzzy
+#~ msgid "E909: Cannot index a special variable"
+#~ msgstr "E90: Kan nie laaste buffer uitlaai nie"
-#: ../eval.c:4839
#, c-format
msgid "E112: Option name missing: %s"
msgstr "E112: Opsienaam ontbreek: %s"
-#: ../eval.c:4855
#, c-format
msgid "E113: Unknown option: %s"
msgstr "E113: Onbekende opsie: %s"
-#: ../eval.c:4904
#, c-format
msgid "E114: Missing quote: %s"
msgstr "E114: Ontbrekende aanhalingsteken: %s"
-#: ../eval.c:5020
#, c-format
msgid "E115: Missing quote: %s"
msgstr "E115: Ontbrekende aanhalingsteken: %s"
-#: ../eval.c:5084
#, fuzzy, c-format
-msgid "E696: Missing comma in List: %s"
-msgstr "E405: Ontbrekende gelykaanteken: %s"
+#~ msgid "E696: Missing comma in List: %s"
+#~ msgstr "E405: Ontbrekende gelykaanteken: %s"
-#: ../eval.c:5091
#, fuzzy, c-format
-msgid "E697: Missing end of List ']': %s"
-msgstr "E398: Ontbrekende '=': %s"
+#~ msgid "E697: Missing end of List ']': %s"
+#~ msgstr "E398: Ontbrekende '=': %s"
+
+#~ msgid "Not enough memory to set references, garbage collection aborted!"
+#~ msgstr ""
-#: ../eval.c:6475
#, fuzzy, c-format
-msgid "E720: Missing colon in Dictionary: %s"
-msgstr "E242: Ontbrekende kleur: %s"
+#~ msgid "E720: Missing colon in Dictionary: %s"
+#~ msgstr "E242: Ontbrekende kleur: %s"
-#: ../eval.c:6499
#, c-format
-msgid "E721: Duplicate key in Dictionary: \"%s\""
-msgstr ""
+#~ msgid "E721: Duplicate key in Dictionary: \"%s\""
+#~ msgstr ""
-#: ../eval.c:6517
#, fuzzy, c-format
-msgid "E722: Missing comma in Dictionary: %s"
-msgstr "E242: Ontbrekende kleur: %s"
+#~ msgid "E722: Missing comma in Dictionary: %s"
+#~ msgstr "E242: Ontbrekende kleur: %s"
-#: ../eval.c:6524
#, fuzzy, c-format
-msgid "E723: Missing end of Dictionary '}': %s"
-msgstr "E126: Ontbrekende ':endfunction'"
+#~ msgid "E723: Missing end of Dictionary '}': %s"
+#~ msgstr "E126: Ontbrekende ':endfunction'"
-#: ../eval.c:6555
-#, fuzzy
-msgid "E724: variable nested too deep for displaying"
-msgstr "E22: Skripte te diep ge-nes"
+#, c-format
+msgid "E125: Illegal argument: %s"
+msgstr "E125: Ongeldige parameter: %s"
-#: ../eval.c:7188
#, fuzzy, c-format
-msgid "E740: Too many arguments for function %s"
-msgstr "E118: Te veel parameters vir funksie: %s"
+#~ msgid "E853: Duplicate argument name: %s"
+#~ msgstr "E125: Ongeldige parameter: %s"
+
+#, fuzzy, c-format
+#~ msgid "E740: Too many arguments for function %s"
+#~ msgstr "E118: Te veel parameters vir funksie: %s"
-#: ../eval.c:7190
#, c-format
msgid "E116: Invalid arguments for function %s"
msgstr "E116: Ongeldige parameters vir funksie %s"
-#: ../eval.c:7377
#, c-format
msgid "E117: Unknown function: %s"
msgstr "E117: Onbekende funksie: %s"
-#: ../eval.c:7383
+#, fuzzy, c-format
+#~ msgid "E933: Function was deleted: %s"
+#~ msgstr "E129: Funksienaam vereis"
+
#, c-format
msgid "E119: Not enough arguments for function: %s"
msgstr "E119: Te min parameters vir funksie: %s"
-#: ../eval.c:7387
#, c-format
msgid "E120: Using <SID> not in a script context: %s"
msgstr "E120: <SID> word buite skripkonteks gebruik: %s"
-#: ../eval.c:7391
#, c-format
-msgid "E725: Calling dict function without Dictionary: %s"
-msgstr ""
+#~ msgid "E725: Calling dict function without Dictionary: %s"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "Error converting the call result: %s"
+#~ msgstr ""
-#: ../eval.c:7453
#, fuzzy
-msgid "E808: Number or Float required"
-msgstr "E521: Nommer vereis na ="
+#~ msgid "E699: Too many arguments"
+#~ msgstr "Te veel redigeer-parameters"
-#: ../eval.c:7503
#, fuzzy
-msgid "add() argument"
-msgstr "Ongeldige parameter vir"
+#~ msgid "E785: complete() can only be used in Insert mode"
+#~ msgstr "E328: Kieslys bestaan slegs in 'n ander modus"
+
+msgid "&Ok"
+msgstr "&Ok"
-#: ../eval.c:7907
#, fuzzy
-msgid "E699: Too many arguments"
-msgstr "Te veel redigeer-parameters"
+#~ msgid "dictwatcheradd() argument"
+#~ msgstr "Ongeldige parameter vir"
+
+#~ msgid "extend() argument"
+#~ msgstr ""
-#: ../eval.c:8073
#, fuzzy
-msgid "E785: complete() can only be used in Insert mode"
-msgstr "E328: Kieslys bestaan slegs in 'n ander modus"
+#~ msgid "map() argument"
+#~ msgstr " vim [parameters] "
-#: ../eval.c:8156
-msgid "&Ok"
-msgstr "&Ok"
+#~ msgid "filter() argument"
+#~ msgstr ""
-#: ../eval.c:8676
#, fuzzy, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E227: binding bestaan alreeds vir %s"
+#~ msgid "+-%s%3ld line: "
+#~ msgid_plural "+-%s%3ld lines: "
+#~ msgstr[0] "+-%s%3ld rels: "
+#~ msgstr[1] "+-%s%3ld rels: "
-#: ../eval.c:8692
-msgid "extend() argument"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E700: Unknown function: %s"
+#~ msgstr "E117: Onbekende funksie: %s"
+
+#~ msgid "E922: expected a dict"
+#~ msgstr ""
-#: ../eval.c:8915
#, fuzzy
-msgid "map() argument"
-msgstr " vim [parameters] "
+#~ msgid "E923: Second argument of function() must be a list or a dict"
+#~ msgstr "E487: Parameter moet positief wees"
-#: ../eval.c:8916
-msgid "filter() argument"
-msgstr ""
+#, fuzzy
+#~ msgid "E5000: Cannot find tab number."
+#~ msgstr "E90: Kan nie laaste buffer uitlaai nie"
-#: ../eval.c:9229
-#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld rels: "
+#~ msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0."
+#~ msgstr ""
-#: ../eval.c:9291
-#, fuzzy, c-format
-msgid "E700: Unknown function: %s"
-msgstr "E117: Onbekende funksie: %s"
+#, fuzzy
+#~ msgid "E5002: Cannot find window number."
+#~ msgstr "E671: Kan nie venster titel vind nie \"%s\""
+
+#~ msgid "E5050: {opts} must be the only argument"
+#~ msgstr ""
-#: ../eval.c:10729
msgid "called inputrestore() more often than inputsave()"
msgstr "inputrestore() is meer gereeld as inputsave() geroep"
-#: ../eval.c:10771
#, fuzzy
-msgid "insert() argument"
-msgstr "Te veel redigeer-parameters"
+#~ msgid "insert() argument"
+#~ msgstr "Te veel redigeer-parameters"
-#: ../eval.c:10841
#, fuzzy
-msgid "E786: Range not allowed"
-msgstr "E481: Geen omvang toegelaat nie"
+#~ msgid "E786: Range not allowed"
+#~ msgstr "E481: Geen omvang toegelaat nie"
+
+#~ msgid "Invalid stream on rpc job, use jobclose(id, 'rpc')"
+#~ msgstr ""
+
+#~ msgid "Invalid job stream: Not an rpc job"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "Invalid job stream \"%s\""
+#~ msgstr ""
+
+#~ msgid "Can't send data to the job: stdin is closed"
+#~ msgstr ""
+
+#~ msgid "Can't send raw data to rpc channel"
+#~ msgstr ""
+
+#~ msgid "E474: Failed to convert list to string"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E474: Failed to parse %.*s"
+#~ msgstr "E241: Kan nie na %s stuur nie"
-#: ../eval.c:11140
#, fuzzy
-msgid "E701: Invalid type for len()"
-msgstr "E596: Ongeldige font(e)"
+#~ msgid "E701: Invalid type for len()"
+#~ msgstr "E596: Ongeldige font(e)"
-#: ../eval.c:11980
-msgid "E726: Stride is zero"
-msgstr ""
+#, c-format
+#~ msgid "E798: ID is reserved for \":match\": %<PRId64>"
+#~ msgstr ""
-#: ../eval.c:11982
-msgid "E727: Start past end"
-msgstr ""
+#, c-format
+#~ msgid "E798: ID is reserved for \"match\": %<PRId64>"
+#~ msgstr ""
-#: ../eval.c:12024 ../eval.c:15297
-msgid "<empty>"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "msgpackdump() argument, index %i"
+#~ msgstr " vim [parameters] "
-#: ../eval.c:12282
-msgid "remove() argument"
-msgstr ""
+#~ msgid "E5070: Character number must not be less than zero"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E5071: Character number must not be greater than INT_MAX (%i)"
+#~ msgstr ""
+
+#~ msgid "E726: Stride is zero"
+#~ msgstr ""
+
+#~ msgid "E727: Start past end"
+#~ msgstr ""
+
+#~ msgid "<empty>"
+#~ msgstr ""
+
+#~ msgid "remove() argument"
+#~ msgstr ""
-#: ../eval.c:12466
msgid "E655: Too many symbolic links (cycle?)"
msgstr "E655: Te veel simboliese skakels (siklus?)"
-#: ../eval.c:12593
-msgid "reverse() argument"
-msgstr ""
+#~ msgid "reverse() argument"
+#~ msgstr ""
-#: ../eval.c:13721
-msgid "sort() argument"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E927: Invalid action: '%s'"
+#~ msgstr "E383: Ongeldige soekstring: %s"
+
+#, fuzzy, c-format
+#~ msgid "connection failed: %s"
+#~ msgstr "XSMP 'SmcOpenConnection' het gefaal: %s"
+
+#~ msgid "sort() argument"
+#~ msgstr ""
-#: ../eval.c:13721
#, fuzzy
-msgid "uniq() argument"
-msgstr "Ongeldige parameter vir"
+#~ msgid "uniq() argument"
+#~ msgstr "Ongeldige parameter vir"
-#: ../eval.c:13776
#, fuzzy
-msgid "E702: Sort compare function failed"
-msgstr "E237: Drukker-seleksie het gefaal"
+#~ msgid "E702: Sort compare function failed"
+#~ msgstr "E237: Drukker-seleksie het gefaal"
-#: ../eval.c:13806
-msgid "E882: Uniq compare function failed"
-msgstr ""
+#~ msgid "E882: Uniq compare function failed"
+#~ msgstr ""
-#: ../eval.c:14085
msgid "(Invalid)"
msgstr "(Ongeldig)"
-#: ../eval.c:14590
-#, fuzzy
-msgid "E677: Error writing temp file"
-msgstr "E208: Kan nie skryf na \"%s\""
-
-#: ../eval.c:16159
-msgid "E805: Using a Float as a Number"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E935: invalid submatch number: %d"
+#~ msgstr "E354: Ongeldige registernaam: '%s'"
-#: ../eval.c:16162
-msgid "E703: Using a Funcref as a Number"
-msgstr ""
+#~ msgid "Can only call this function in an unmodified buffer"
+#~ msgstr ""
-#: ../eval.c:16170
-msgid "E745: Using a List as a Number"
-msgstr ""
+#, fuzzy
+#~ msgid "E921: Invalid callback argument"
+#~ msgstr "E474: Ongeldige parameter"
-#: ../eval.c:16173
-msgid "E728: Using a Dictionary as a Number"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E80: Error while writing: %s"
+#~ msgstr "E80: Fout tydens skryfoperasie"
-#: ../eval.c:16259
-msgid "E729: using Funcref as a String"
-msgstr ""
+#. Using %s, p and not %c, *p to preserve multibyte characters
+#, fuzzy, c-format
+#~ msgid "E5060: Unknown flag: %s"
+#~ msgstr "E235: Onbekende font: %s"
-#: ../eval.c:16262
#, fuzzy
-msgid "E730: using List as a String"
-msgstr "E374: Ontbrekende ] in formaatstring"
+#~ msgid "E482: Can't open file with an empty name"
+#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie"
-#: ../eval.c:16265
-msgid "E731: using Dictionary as a String"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E482: Can't open file %s for writing: %s"
+#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie"
-#: ../eval.c:16619
#, fuzzy, c-format
-msgid "E706: Variable type mismatch for: %s"
-msgstr "E93: Meer as een treffer vir %s"
+#~ msgid "E80: Error when closing file %s: %s"
+#~ msgstr "E209: Kan \"%s\" nie sluit nie"
-#: ../eval.c:16705
#, fuzzy, c-format
-msgid "E795: Cannot delete variable %s"
-msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
+#~ msgid "E794: Cannot set variable in the sandbox: \"%.*s\""
+#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
-#: ../eval.c:16724
#, fuzzy, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
+#~ msgid "E795: Cannot delete variable %.*s"
+#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
-#: ../eval.c:16732
-#, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "E704: Funcref variable name must start with a capital: %s"
+#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
-#: ../eval.c:16763
#, c-format
-msgid "E741: Value is locked: %s"
-msgstr ""
-
-#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
-msgid "Unknown"
-msgstr "Onbekend"
-
-#: ../eval.c:16768
-#, fuzzy, c-format
-msgid "E742: Cannot change value of %s"
-msgstr "E284: Kan nie IC waardes stel nie"
+#~ msgid "E705: Variable name conflicts with existing function: %s"
+#~ msgstr ""
-#: ../eval.c:16838
-msgid "E698: variable nested too deep for making a copy"
-msgstr ""
+#~ msgid "E698: variable nested too deep for making a copy"
+#~ msgstr ""
-#: ../eval.c:17249
#, c-format
msgid "E123: Undefined function: %s"
msgstr "E123: Ongedefinieerde funksie: %s"
-#: ../eval.c:17260
#, c-format
msgid "E124: Missing '(': %s"
msgstr "E124: Ontbrekende '(': %s"
-#: ../eval.c:17293
#, fuzzy
-msgid "E862: Cannot use g: here"
-msgstr "E284: Kan nie IC waardes stel nie"
+#~ msgid "E862: Cannot use g: here"
+#~ msgstr "E284: Kan nie IC waardes stel nie"
-#: ../eval.c:17312
#, c-format
-msgid "E125: Illegal argument: %s"
-msgstr "E125: Ongeldige parameter: %s"
-
-#: ../eval.c:17323
-#, fuzzy, c-format
-msgid "E853: Duplicate argument name: %s"
-msgstr "E125: Ongeldige parameter: %s"
+#~ msgid "E932: Closure function should not be at top level: %s"
+#~ msgstr ""
-#: ../eval.c:17416
msgid "E126: Missing :endfunction"
msgstr "E126: Ontbrekende ':endfunction'"
-#: ../eval.c:17537
#, fuzzy, c-format
-msgid "E707: Function name conflicts with variable: %s"
-msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
+#~ msgid "E707: Function name conflicts with variable: %s"
+#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
-#: ../eval.c:17549
#, c-format
msgid "E127: Cannot redefine function %s: It is in use"
msgstr "E127: Kan funksie %s nie herdefinieer nie: Dit is in gebruik"
-#: ../eval.c:17604
#, fuzzy, c-format
-msgid "E746: Function name does not match script file name: %s"
-msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
+#~ msgid "E746: Function name does not match script file name: %s"
+#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
-#: ../eval.c:17716
msgid "E129: Function name required"
msgstr "E129: Funksienaam vereis"
-#: ../eval.c:17824
#, fuzzy, c-format
-msgid "E128: Function name must start with a capital or \"s:\": %s"
-msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
+#~ msgid "E128: Function name must start with a capital or \"s:\": %s"
+#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
-#: ../eval.c:17833
#, fuzzy, c-format
-msgid "E884: Function name cannot contain a colon: %s"
-msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
+#~ msgid "E884: Function name cannot contain a colon: %s"
+#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s"
-#: ../eval.c:18336
#, c-format
msgid "E131: Cannot delete function %s: It is in use"
msgstr "E131: Kan funksie %s nie verwyder nie: Dit is in gebruik"
-#: ../eval.c:18441
+#, fuzzy, c-format
+#~ msgid "Cannot delete function %s: It is being used internally"
+#~ msgstr "E131: Kan funksie %s nie verwyder nie: Dit is in gebruik"
+
msgid "E132: Function call depth is higher than 'maxfuncdepth'"
msgstr "E132: Funksieroepdiepte is groter as 'maxfuncdepth'"
-#: ../eval.c:18568
#, c-format
msgid "calling %s"
msgstr "roep %s"
-#: ../eval.c:18651
#, c-format
msgid "%s aborted"
msgstr "%s gekanselleer"
-#: ../eval.c:18653
#, c-format
msgid "%s returning #%<PRId64>"
msgstr "%s lewer #%<PRId64> op"
-#: ../eval.c:18670
#, fuzzy, c-format
-msgid "%s returning %s"
-msgstr "%s lewer \"%s\" op"
+#~ msgid "%s returning %s"
+#~ msgstr "%s lewer \"%s\" op"
-#: ../eval.c:18691 ../ex_cmds2.c:2695
#, c-format
msgid "continuing in %s"
msgstr "vervolg in %s"
-#: ../eval.c:18795
msgid "E133: :return not inside a function"
msgstr "E133: ':return' buite funksie"
-#: ../eval.c:19159
-msgid ""
-"\n"
-"# global variables:\n"
-msgstr ""
-"\n"
-"# globale veranderlikes:\n"
-
-#: ../eval.c:19254
msgid ""
"\n"
"\tLast set from "
@@ -1071,154 +910,418 @@ msgstr ""
"\n"
"\tLaas gestel vanaf "
-#: ../eval.c:19272
-#, fuzzy
-msgid "No old files"
-msgstr "Geen ingeslote lers nie"
+#~ msgid "E5009: $VIMRUNTIME is empty or unset"
+#~ msgstr ""
-#: ../ex_cmds.c:122
#, c-format
-msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
-msgstr "<%s>%s%s %d, Hex %02x, Oktaal %03o"
+#~ msgid "E5009: Invalid $VIMRUNTIME: %s"
+#~ msgstr ""
-#: ../ex_cmds.c:145
#, c-format
-msgid "> %d, Hex %04x, Octal %o"
-msgstr "> %d, Hex %04x, Oktaal %o"
+#~ msgid "E474: Expected comma before list item: %s"
+#~ msgstr ""
-#: ../ex_cmds.c:146
#, c-format
-msgid "> %d, Hex %08x, Octal %o"
-msgstr "> %d, Hex %08x, Oktaal %o"
+#~ msgid "E474: Expected colon before dictionary value: %s"
+#~ msgstr ""
-#: ../ex_cmds.c:684
-msgid "E134: Move lines into themselves"
-msgstr "E134: Skuif rels in hulself in"
+#, fuzzy, c-format
+#~ msgid "E474: Expected string key: %s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
-#: ../ex_cmds.c:747
-msgid "1 line moved"
-msgstr "1 rel geskuif"
+#, fuzzy, c-format
+#~ msgid "E474: Expected comma before dictionary key: %s"
+#~ msgstr "E242: Ontbrekende kleur: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Unfinished escape sequence: %.*s"
+#~ msgstr "E540: Onvoltooide uitdrukkingreeks"
-#: ../ex_cmds.c:749
#, c-format
-msgid "%<PRId64> lines moved"
-msgstr "%<PRId64> rels geskuif"
+#~ msgid "E474: Unfinished unicode escape sequence: %.*s"
+#~ msgstr ""
-#: ../ex_cmds.c:1175
#, c-format
-msgid "%<PRId64> lines filtered"
-msgstr "%<PRId64> rels filtreer"
+#~ msgid "E474: Expected four hex digits after \\u: %.*s"
+#~ msgstr ""
-#: ../ex_cmds.c:1194
-msgid "E135: *Filter* Autocommands must not change current buffer"
-msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie"
+#, fuzzy, c-format
+#~ msgid "E474: Unknown escape sequence: %.*s"
+#~ msgstr "E409: Onbekende groepnaam: %s"
-#: ../ex_cmds.c:1244
-msgid "[No write since last change]\n"
-msgstr "[Ongestoor sedert vorige verandering]\n"
+#, c-format
+#~ msgid "E474: ASCII control characters cannot be present inside string: %.*s"
+#~ msgstr ""
-#: ../ex_cmds.c:1424
#, c-format
-msgid "%sviminfo: %s in line: "
-msgstr "%sviminfo: %s in rel: "
+#~ msgid "E474: Only UTF-8 strings allowed: %.*s"
+#~ msgstr ""
-#: ../ex_cmds.c:1431
-msgid "E136: viminfo: Too many errors, skipping rest of file"
-msgstr "E136: viminfo: Te veel foute, slaan die res van die ler oor"
+#, c-format
+#~ msgid ""
+#~ "E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: "
+#~ "%.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E474: Expected string end: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Leading zeroes are not allowed: %.*s"
+#~ msgstr "E481: Geen omvang toegelaat nie"
+
+#, fuzzy, c-format
+#~ msgid "E474: Missing number after minus sign: %.*s"
+#~ msgstr "E526: Ontbrekende nommer na <%s>"
+
+#, fuzzy, c-format
+#~ msgid "E474: Missing number after decimal dot: %.*s"
+#~ msgstr "E526: Ontbrekende nommer na <%s>"
+
+#, fuzzy, c-format
+#~ msgid "E474: Missing exponent: %.*s"
+#~ msgstr "E114: Ontbrekende aanhalingsteken: %s"
-#: ../ex_cmds.c:1458
#, c-format
-msgid "Reading viminfo file \"%s\"%s%s%s"
-msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees"
+#~ msgid ""
+#~ "E685: internal error: while converting number \"%.*s\" to float string2float "
+#~ "consumed %zu bytes in place of %zu"
+#~ msgstr ""
-#: ../ex_cmds.c:1460
-msgid " info"
-msgstr " inligting"
+#, c-format
+#~ msgid ""
+#~ "E685: internal error: while converting number \"%.*s\" to integer vim_str2nr "
+#~ "consumed %i bytes in place of %zu"
+#~ msgstr ""
-#: ../ex_cmds.c:1461
-msgid " marks"
-msgstr " merkers"
+#~ msgid "E474: Attempt to decode a blank string"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E474: No container to close: %.*s"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E474: Closing list with curly bracket: %.*s"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E474: Closing dictionary with square bracket: %.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E474: Trailing comma: %.*s"
+#~ msgstr "E488: Oorbodige karakters"
+
+#, c-format
+#~ msgid "E474: Expected value after colon: %.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E474: Expected value: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Comma not inside container: %.*s"
+#~ msgstr "E242: Kleurnaam is onbekend: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Duplicate comma: %.*s"
+#~ msgstr "E125: Ongeldige parameter: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Comma after colon: %.*s"
+#~ msgstr "E254: Kan nie kleur %s toeken nie"
+
+#, fuzzy, c-format
+#~ msgid "E474: Using comma in place of colon: %.*s"
+#~ msgstr "E242: Ontbrekende kleur: %s"
+
+#, c-format
+#~ msgid "E474: Leading comma: %.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E474: Colon not inside container: %.*s"
+#~ msgstr "E242: Kleurnaam is onbekend: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Using colon not in dictionary: %.*s"
+#~ msgstr "E242: Ontbrekende kleur: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Unexpected colon: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, c-format
+#~ msgid "E474: Colon after comma: %.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E474: Duplicate colon: %.*s"
+#~ msgstr "gelaaide fontnaam: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Expected null: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Expected true: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Expected false: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Unidentified byte: %.*s"
+#~ msgstr "E121: Ongedefinieerde veranderlike: %s"
+
+#, fuzzy, c-format
+#~ msgid "E474: Trailing characters: %.*s"
+#~ msgstr "E488: Oorbodige karakters"
+
+#, fuzzy, c-format
+#~ msgid "E474: Unexpected end of input: %.*s"
+#~ msgstr "E415: onverwagte gelykaanteken: %s"
+
+#, c-format
+#~ msgid "key %s"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "key %s at index %i from special map"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "index %i"
+#~ msgstr ""
+
+#~ msgid "partial"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "argument %i"
+#~ msgstr " vim [parameters] "
+
+#~ msgid "partial self dictionary"
+#~ msgstr ""
+
+#~ msgid "itself"
+#~ msgstr ""
+
+#. Only give this message once for a recursive call to avoid
+#. flooding the user with errors.
+#~ msgid "E724: unable to correctly dump variable with self-referencing container"
+#~ msgstr ""
+
+#~ msgid "E474: Unable to represent NaN value in JSON"
+#~ msgstr ""
+
+#~ msgid "E474: Unable to represent infinity in JSON"
+#~ msgstr ""
+
+#, c-format
+#~ msgid ""
+#~ "E474: String \"%.*s\" contains byte that does not start any UTF-8 character"
+#~ msgstr ""
+
+#, c-format
+#~ msgid ""
+#~ "E474: UTF-8 string contains code point which belongs to a surrogate pair: "
+#~ "%.*s"
+#~ msgstr ""
-#: ../ex_cmds.c:1462
#, fuzzy
-msgid " oldfiles"
-msgstr "Geen ingeslote lers nie"
+#~ msgid "E474: Unable to convert EXT string to JSON"
+#~ msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie"
-#: ../ex_cmds.c:1463
-msgid " FAILED"
-msgstr " GEFAAL"
+#, c-format
+#~ msgid "E474: Error while dumping %s, %s: attempt to dump function reference"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E474: Invalid key in special dictionary"
+#~ msgstr "E116: Ongeldige parameters vir funksie %s"
+
+#, fuzzy
+#~ msgid "encode_tv2string() argument"
+#~ msgstr "Te veel redigeer-parameters"
+
+#, fuzzy
+#~ msgid ":echo argument"
+#~ msgstr " vim [parameters] "
+
+#, fuzzy
+#~ msgid "encode_tv2json() argument"
+#~ msgstr "Te veel redigeer-parameters"
-#. avoid a wait_return for this message, it's annoying
-#: ../ex_cmds.c:1541
#, c-format
-msgid "E137: Viminfo file is not writable: %s"
-msgstr "E137: Viminfo ler is nie skryfbaar nie: %s"
+#~ msgid "E5004: Error while dumping %s, %s: attempt to dump function reference"
+#~ msgstr ""
-#: ../ex_cmds.c:1626
#, c-format
-msgid "E138: Can't write viminfo file %s!"
-msgstr "E138: Kan nie viminfo ler %s stoor nie!"
+#~ msgid "E5005: Unable to dump %s: container references itself in %s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E684: list index out of range: %<PRId64>"
+#~ msgstr "E322: relnommer buite perke: %<PRId64> verby die einde"
+
+#~ msgid "E6000: Argument is not a function or function name"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E737: Key already exists: %s"
+#~ msgstr "E227: binding bestaan alreeds vir %s"
+
+#~ msgid "E743: variable nested too deep for (un)lock"
+#~ msgstr ""
-#: ../ex_cmds.c:1635
#, c-format
-msgid "Writing viminfo file \"%s\""
-msgstr "Besig om viminfo ler \"%s\" te stoor"
+#~ msgid "E741: Value is locked: %.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E742: Cannot change value of %.*s"
+#~ msgstr "E284: Kan nie IC waardes stel nie"
+
+msgid "Unknown"
+msgstr "Onbekend"
+
+#~ msgid "E805: Expected a Number or a String, Float found"
+#~ msgstr ""
+
+#~ msgid "E703: Expected a Number or a String, Funcref found"
+#~ msgstr ""
+
+#~ msgid "E745: Expected a Number or a String, List found"
+#~ msgstr ""
+
+#~ msgid "E728: Expected a Number or a String, Dictionary found"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E5300: Expected a Number or a String"
+#~ msgstr "E373: Onverwagte %%%c in formaatstring"
+
+#~ msgid "E745: Using a List as a Number"
+#~ msgstr ""
+
+#~ msgid "E728: Using a Dictionary as a Number"
+#~ msgstr ""
+
+#~ msgid "E805: Using a Float as a Number"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E685: using an invalid value as a Number"
+#~ msgstr "E19: Merker het ongeldige relnommer"
+
+#, fuzzy
+#~ msgid "E730: using List as a String"
+#~ msgstr "E374: Ontbrekende ] in formaatstring"
+
+#~ msgid "E731: using Dictionary as a String"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E908: using an invalid value as a String"
+#~ msgstr "E374: Ontbrekende ] in formaatstring"
+
+#~ msgid "E891: Using a Funcref as a Float"
+#~ msgstr ""
+
+#~ msgid "E892: Using a String as a Float"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E893: Using a List as a Float"
+#~ msgstr "E374: Ontbrekende ] in formaatstring"
+
+#, fuzzy
+#~ msgid "E894: Using a Dictionary as a Float"
+#~ msgstr "E242: Ontbrekende kleur: %s"
+
+#~ msgid "E907: Using a special value as a Float"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E808: Number or Float required"
+#~ msgstr "E521: Nommer vereis na ="
+
+#~ msgid "tcp address must be host:port"
+#~ msgstr ""
+
+#~ msgid "failed to lookup host or port"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "connection refused"
+#~ msgstr "'cscope' verbinding gesluit"
-#. Write the info:
-#: ../ex_cmds.c:1720
#, c-format
-msgid "# This viminfo file was generated by Vim %s.\n"
-msgstr "# Hierdie viminfo ler is gegenereer deur Vim %s.\n"
+msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
+msgstr "<%s>%s%s %d, Hex %02x, Oktaal %03o"
-#: ../ex_cmds.c:1722
-msgid ""
-"# You may edit it if you're careful!\n"
-"\n"
-msgstr ""
-"# Jy mag dit wysig as jy versigtig is!\n"
-"\n"
+#, c-format
+msgid "> %d, Hex %04x, Octal %o"
+msgstr "> %d, Hex %04x, Oktaal %o"
+
+#, c-format
+msgid "> %d, Hex %08x, Octal %o"
+msgstr "> %d, Hex %08x, Oktaal %o"
-#: ../ex_cmds.c:1723
-msgid "# Value of 'encoding' when this file was written\n"
-msgstr "# Waarde van 'encoding' toe hierdie ler gestoor is\n"
+msgid "E134: Move lines into themselves"
+msgstr "E134: Skuif rels in hulself in"
-#: ../ex_cmds.c:1800
-msgid "Illegal starting char"
-msgstr "Ongeldige beginkarakter"
+msgid "1 line moved"
+msgstr "1 rel geskuif"
+
+#, c-format
+msgid "%<PRId64> lines moved"
+msgstr "%<PRId64> rels geskuif"
+
+#, c-format
+msgid "E482: Can't create file %s"
+msgstr "E482: Kan nie ler %s skep nie"
+
+#, c-format
+msgid "%<PRId64> lines filtered"
+msgstr "%<PRId64> rels filtreer"
+
+msgid "E135: *Filter* Autocommands must not change current buffer"
+msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie"
+
+msgid "[No write since last change]\n"
+msgstr "[Ongestoor sedert vorige verandering]\n"
-#: ../ex_cmds.c:2162
msgid "Write partial file?"
msgstr "Skryf gedeeltelike ler?"
-#: ../ex_cmds.c:2166
msgid "E140: Use ! to write partial buffer"
msgstr "E140: Gebruik ! om gedeeltelike buffer te skryf"
-#: ../ex_cmds.c:2281
#, fuzzy, c-format
-msgid "Overwrite existing file \"%s\"?"
-msgstr "Oorskryf bestaande ler \"%.*s\"?"
+#~ msgid "Overwrite existing file \"%s\"?"
+#~ msgstr "Oorskryf bestaande ler \"%.*s\"?"
-#: ../ex_cmds.c:2317
#, c-format
-msgid "Swap file \"%s\" exists, overwrite anyway?"
-msgstr ""
+#~ msgid "Swap file \"%s\" exists, overwrite anyway?"
+#~ msgstr ""
-#: ../ex_cmds.c:2326
#, fuzzy, c-format
-msgid "E768: Swap file exists: %s (:silent! overrides)"
-msgstr "E13: Ler bestaan (gebruik ! om te dwing)"
+#~ msgid "E768: Swap file exists: %s (:silent! overrides)"
+#~ msgstr "E13: Ler bestaan (gebruik ! om te dwing)"
-#: ../ex_cmds.c:2381
#, c-format
msgid "E141: No file name for buffer %<PRId64>"
msgstr "E141: Geen lernaam vir buffer %<PRId64> nie"
-#: ../ex_cmds.c:2412
msgid "E142: File not written: Writing is disabled by 'write' option"
msgstr "E142: Ler nie gestoor nie: Stoor is afgeskakel deur die 'write' opsie"
-#: ../ex_cmds.c:2434
#, fuzzy, c-format
msgid ""
"'readonly' option is set for \"%s\".\n"
@@ -1227,823 +1330,667 @@ msgstr ""
"'readonly' opsie is aan vir \"%.*s\".\n"
"Wil jy dit forseer?"
-#: ../ex_cmds.c:2439
#, c-format
-msgid ""
-"File permissions of \"%s\" are read-only.\n"
-"It may still be possible to write it.\n"
-"Do you wish to try?"
-msgstr ""
+#~ msgid ""
+#~ "File permissions of \"%s\" are read-only.\n"
+#~ "It may still be possible to write it.\n"
+#~ "Do you wish to try?"
+#~ msgstr ""
-#: ../ex_cmds.c:2451
#, fuzzy, c-format
-msgid "E505: \"%s\" is read-only (add ! to override)"
-msgstr "is lees-alleen (gebruik ! om te dwing)"
+#~ msgid "E505: \"%s\" is read-only (add ! to override)"
+#~ msgstr "is lees-alleen (gebruik ! om te dwing)"
-#: ../ex_cmds.c:3120
#, c-format
msgid "E143: Autocommands unexpectedly deleted new buffer %s"
msgstr "E143: Outobevele het nuwe buffer %s onverwags geskrap"
-#: ../ex_cmds.c:3313
msgid "E144: non-numeric argument to :z"
msgstr "E144: nie-numeriese parameter vir :z"
-#: ../ex_cmds.c:3404
-msgid "E145: Shell commands not allowed in rvim"
-msgstr "E145: Dop bevele nie toegelaat in rvim"
+#, fuzzy
+#~ msgid "E145: Shell commands not allowed in restricted mode"
+#~ msgstr "E145: Dop bevele nie toegelaat in rvim"
-#: ../ex_cmds.c:3498
msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Patrone kan nie deur letters afgebaken word nie"
-#: ../ex_cmds.c:3964
#, c-format
msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
msgstr "vervang met %s (y/n/a/q/l/^E/^Y)?"
-#: ../ex_cmds.c:4379
msgid "(Interrupted) "
msgstr "(Onderbreek) "
-#: ../ex_cmds.c:4384
#, fuzzy
-msgid "1 match"
-msgstr "; treffer "
+#~ msgid "1 match"
+#~ msgstr "; treffer "
-#: ../ex_cmds.c:4384
msgid "1 substitution"
msgstr "1 vervanging"
-#: ../ex_cmds.c:4387
#, fuzzy, c-format
-msgid "%<PRId64> matches"
-msgstr "%<PRId64> veranderinge"
+#~ msgid "%<PRId64> matches"
+#~ msgstr "%<PRId64> veranderinge"
-#: ../ex_cmds.c:4388
#, c-format
msgid "%<PRId64> substitutions"
msgstr "%<PRId64> vervangings"
-#: ../ex_cmds.c:4392
msgid " on 1 line"
msgstr " op 1 rel"
-#: ../ex_cmds.c:4395
#, c-format
msgid " on %<PRId64> lines"
msgstr " op %<PRId64> rels"
-#: ../ex_cmds.c:4438
msgid "E147: Cannot do :global recursive"
msgstr "E147: Kan nie :global rekursief doen nie "
-#: ../ex_cmds.c:4467
msgid "E148: Regular expression missing from global"
msgstr "E148: Patroon ontbreek uit globaal"
-#: ../ex_cmds.c:4508
#, c-format
msgid "Pattern found in every line: %s"
msgstr "Patroon gevind in elke rel: %s"
-#: ../ex_cmds.c:4510
#, fuzzy, c-format
-msgid "Pattern not found: %s"
-msgstr "Patroon nie gevind nie"
+#~ msgid "Pattern not found: %s"
+#~ msgstr "Patroon nie gevind nie"
-#: ../ex_cmds.c:4587
-msgid ""
-"\n"
-"# Last Substitute String:\n"
-"$"
-msgstr ""
-"\n"
-"# Vorige Vervangstring:\n"
-"$"
-
-#: ../ex_cmds.c:4679
msgid "E478: Don't panic!"
msgstr "E478: Bly kalm!"
-#: ../ex_cmds.c:4717
#, c-format
msgid "E661: Sorry, no '%s' help for %s"
msgstr "E661: Jammer, geen '%s' hulp vir %s nie"
-#: ../ex_cmds.c:4719
#, c-format
msgid "E149: Sorry, no help for %s"
msgstr "E149: Jammer, geen hulp vir %s nie"
-#: ../ex_cmds.c:4751
#, c-format
msgid "Sorry, help file \"%s\" not found"
msgstr "Jammer, hulpler \"%s\" kan nie gevind word nie"
-#: ../ex_cmds.c:5323
-#, c-format
-msgid "E150: Not a directory: %s"
-msgstr "E150: Nie 'n gids nie: %s"
+#, fuzzy, c-format
+#~ msgid "E151: No match: %s"
+#~ msgstr "E480: Geen treffer: %s"
-#: ../ex_cmds.c:5446
#, c-format
msgid "E152: Cannot open %s for writing"
msgstr "E152: Kan nie %s oopmaak om te skryf nie"
-#: ../ex_cmds.c:5471
#, c-format
msgid "E153: Unable to open %s for reading"
msgstr "E153: Kan nie %s oop maak om te lees nie"
-#: ../ex_cmds.c:5500
#, c-format
msgid "E670: Mix of help file encodings within a language: %s"
msgstr "E670: 'n Mengsel van hulpler enkoderings in 'n taal: %s"
-#: ../ex_cmds.c:5565
#, c-format
msgid "E154: Duplicate tag \"%s\" in file %s/%s"
msgstr "E154: Duplikaat etiket \"%s\" in ler %s/%s"
-#: ../ex_cmds.c:5687
+#, c-format
+msgid "E150: Not a directory: %s"
+msgstr "E150: Nie 'n gids nie: %s"
+
#, c-format
msgid "E160: Unknown sign command: %s"
msgstr "E160: Onbekende funksie: %s"
-#: ../ex_cmds.c:5704
msgid "E156: Missing sign name"
msgstr "E156: Ontbrekende tekennaam"
-#: ../ex_cmds.c:5746
msgid "E612: Too many signs defined"
msgstr "E612: Te veel tekens gedefinieer"
-#: ../ex_cmds.c:5813
#, c-format
msgid "E239: Invalid sign text: %s"
msgstr "E239: Ongeldige tekenteks: %s"
-#: ../ex_cmds.c:5844 ../ex_cmds.c:6035
#, c-format
msgid "E155: Unknown sign: %s"
msgstr "E155: Onbekende opsie: %s"
-#: ../ex_cmds.c:5877
msgid "E159: Missing sign number"
msgstr "E159: Ontbrekende tekennommer"
-#: ../ex_cmds.c:5971
#, c-format
msgid "E158: Invalid buffer name: %s"
msgstr "E158: Ongeldige buffernaam: %s"
-#: ../ex_cmds.c:6008
+#~ msgid "E934: Cannot jump to a buffer that does not have a name"
+#~ msgstr ""
+
#, c-format
msgid "E157: Invalid sign ID: %<PRId64>"
msgstr "E157: Ongeldige teken ID: %<PRId64>"
-#: ../ex_cmds.c:6066
+#, c-format
+#~ msgid "E885: Not possible to change sign %s"
+#~ msgstr ""
+
msgid " (not supported)"
msgstr " (word nie ondersteun nie)"
-#: ../ex_cmds.c:6169
msgid "[Deleted]"
msgstr "[Geskrap]"
-#: ../ex_cmds2.c:139
+#, fuzzy
+#~ msgid "No old files"
+#~ msgstr "Geen ingeslote lers nie"
+
msgid "Entering Debug mode. Type \"cont\" to continue."
msgstr "Ontfoutmodus begin nou. Tik \"cont\" om te verlaat."
-#: ../ex_cmds2.c:143 ../ex_docmd.c:759
#, c-format
msgid "line %<PRId64>: %s"
msgstr "rel %<PRId64>: %s"
-#: ../ex_cmds2.c:145
#, c-format
msgid "cmd: %s"
msgstr "cmd: %s"
-#: ../ex_cmds2.c:322
+#~ msgid "frame is zero"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "frame at highest level: %d"
+#~ msgstr ""
+
#, c-format
msgid "Breakpoint in \"%s%s\" line %<PRId64>"
msgstr "Inspeksiepunt in \"%s%s\" rel %<PRId64>"
-#: ../ex_cmds2.c:581
#, c-format
msgid "E161: Breakpoint not found: %s"
msgstr "E161: Inspeksiepunt kon nie gevind word nie: %s"
-#: ../ex_cmds2.c:611
msgid "No breakpoints defined"
msgstr "Geen inspeksiepunte gedefinieer nie"
-#: ../ex_cmds2.c:617
#, c-format
msgid "%3d %s %s line %<PRId64>"
msgstr "%3d %s %s rel %<PRId64>"
-#: ../ex_cmds2.c:942
-msgid "E750: First use \":profile start {fname}\""
-msgstr ""
+#~ msgid "E750: First use \":profile start {fname}\""
+#~ msgstr ""
-#: ../ex_cmds2.c:1269
#, fuzzy, c-format
-msgid "Save changes to \"%s\"?"
-msgstr "Stoor veranderinge na \"%.*s\"?"
+#~ msgid "Save changes to \"%s\"?"
+#~ msgstr "Stoor veranderinge na \"%.*s\"?"
-#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851
msgid "Untitled"
msgstr "Ongetiteld"
-#: ../ex_cmds2.c:1421
#, c-format
msgid "E162: No write since last change for buffer \"%s\""
msgstr "E162: Buffer \"%s\" is nie geskryf sedert vorige wysiging nie"
-#: ../ex_cmds2.c:1480
msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr "Waarskuwing: Ander buffer onverwags betree (kyk na outobevele)"
-#: ../ex_cmds2.c:1826
msgid "E163: There is only one file to edit"
msgstr "E163: Daar is net een ler om te bewerk"
-#: ../ex_cmds2.c:1828
msgid "E164: Cannot go before first file"
msgstr "E164: Kan nie vr die eerste ler gaan nie"
-#: ../ex_cmds2.c:1830
msgid "E165: Cannot go beyond last file"
msgstr "E165: Kan nie verby die laaste ler gaan nie"
-#: ../ex_cmds2.c:2175
#, c-format
msgid "E666: compiler not supported: %s"
msgstr "E666: vertaler word nie ondersteun nie: %s"
-#: ../ex_cmds2.c:2257
#, c-format
msgid "Searching for \"%s\" in \"%s\""
msgstr "Besig om te soek vir \"%s\" in \"%s\""
-#: ../ex_cmds2.c:2284
#, c-format
msgid "Searching for \"%s\""
msgstr "Besig om te soek vir \"%s\""
-#: ../ex_cmds2.c:2307
-#, c-format
-msgid "not found in 'runtimepath': \"%s\""
-msgstr "kon nie in 'runtimepath' gevind word nie: \"%s\""
+#, fuzzy, c-format
+#~ msgid "not found in '%s': \"%s\""
+#~ msgstr "kon nie in 'runtimepath' gevind word nie: \"%s\""
-#: ../ex_cmds2.c:2472
#, c-format
msgid "Cannot source a directory: \"%s\""
msgstr "Kan nie gids uitvoer nie: \"%s\""
-#: ../ex_cmds2.c:2518
#, c-format
msgid "could not source \"%s\""
msgstr "kon nie \"%s\" uitvoer nie"
-#: ../ex_cmds2.c:2520
#, c-format
msgid "line %<PRId64>: could not source \"%s\""
msgstr "rel %<PRId64>: kon nie \"%s\" uitvoer nie"
-#: ../ex_cmds2.c:2535
#, c-format
msgid "sourcing \"%s\""
msgstr "besig om \"%s\" uit te voer"
-#: ../ex_cmds2.c:2537
#, c-format
msgid "line %<PRId64>: sourcing \"%s\""
msgstr "rel %<PRId64>: voer nou \"%s\" uit"
-#: ../ex_cmds2.c:2693
#, c-format
msgid "finished sourcing %s"
msgstr "%s klaar uitgevoer"
-#: ../ex_cmds2.c:2765
#, fuzzy
-msgid "modeline"
-msgstr "1 rel meer"
+#~ msgid "modeline"
+#~ msgstr "1 rel meer"
-#: ../ex_cmds2.c:2767
#, fuzzy
-msgid "--cmd argument"
-msgstr " vim [parameters] "
+#~ msgid "--cmd argument"
+#~ msgstr " vim [parameters] "
-#: ../ex_cmds2.c:2769
#, fuzzy
-msgid "-c argument"
-msgstr " vim [parameters] "
+#~ msgid "-c argument"
+#~ msgstr " vim [parameters] "
-#: ../ex_cmds2.c:2771
#, fuzzy
-msgid "environment variable"
-msgstr "Stel die 'LANG' omgewingsveranderlike na jou lokaal toe"
+#~ msgid "environment variable"
+#~ msgstr "Stel die 'LANG' omgewingsveranderlike na jou lokaal toe"
-#: ../ex_cmds2.c:2773
#, fuzzy
-msgid "error handler"
-msgstr "Fout en onderbreking"
+#~ msgid "error handler"
+#~ msgstr "Fout en onderbreking"
-#: ../ex_cmds2.c:3020
msgid "W15: Warning: Wrong line separator, ^M may be missing"
msgstr "W15: Waarskuwing: Verkeerde relskeiding, ^M ontbreek dalk"
-#: ../ex_cmds2.c:3139
msgid "E167: :scriptencoding used outside of a sourced file"
msgstr "E167: ':scriptencoding' buite 'n uitvoerler gebruik"
-#: ../ex_cmds2.c:3166
msgid "E168: :finish used outside of a sourced file"
msgstr "E168: ':finish' buite 'n uitvoerler gebruik"
-#: ../ex_cmds2.c:3389
#, c-format
msgid "Current %slanguage: \"%s\""
msgstr "Huidige %staal: \"%s\""
-#: ../ex_cmds2.c:3404
#, c-format
msgid "E197: Cannot set language to \"%s\""
msgstr "E197: Kan nie taal na \"%s\" verander nie"
#. don't redisplay the window
#. don't wait for return
-#: ../ex_docmd.c:387
msgid "Entering Ex mode. Type \"visual\" to go to Normal mode."
msgstr "Betree Ex modus. Tik \"visual\" om na Normale modus terug te keer."
-#: ../ex_docmd.c:428
msgid "E501: At end-of-file"
msgstr "E501: By lereinde"
-#: ../ex_docmd.c:513
msgid "E169: Command too recursive"
msgstr "E169: Bevel te rekursief"
-#: ../ex_docmd.c:1006
+#, fuzzy
+#~ msgid "line %"
+#~ msgstr "rel %4ld:"
+
#, c-format
msgid "E605: Exception not caught: %s"
msgstr "E605: Uitsondering nie gevang nie: %s"
-#: ../ex_docmd.c:1085
msgid "End of sourced file"
msgstr "Einde van uitvoerler"
-#: ../ex_docmd.c:1086
msgid "End of function"
msgstr "Einde van funksie "
-#: ../ex_docmd.c:1628
msgid "E464: Ambiguous use of user-defined command"
msgstr "E464: Dubbelsinnige gebruik van gebruiker-gedefinieerde bevel"
-#: ../ex_docmd.c:1638
msgid "E492: Not an editor command"
msgstr "E492: Nie 'n verwerkerbevel nie"
-#: ../ex_docmd.c:1729
msgid "E493: Backwards range given"
msgstr "E493: Terugwaardse omvang gegee"
-#: ../ex_docmd.c:1733
msgid "Backwards range given, OK to swap"
msgstr "Terugwaardse omvang gegee, OK om te ruil"
#. append
#. typed wrong
-#: ../ex_docmd.c:1787
msgid "E494: Use w or w>>"
msgstr "E494: Gebruik w of w>>"
-#: ../ex_docmd.c:3454
msgid "E319: The command is not available in this version"
msgstr "E319: Jammer, die bevel is nie gemplementeer nie"
-#: ../ex_docmd.c:3752
-msgid "E172: Only one file name allowed"
-msgstr "E172: Slegs een lernaam toegelaat"
-
-#: ../ex_docmd.c:4238
msgid "1 more file to edit. Quit anyway?"
msgstr "Nog 1 ler om te bewerk. Stop in elk geval?"
-#: ../ex_docmd.c:4242
#, c-format
msgid "%d more files to edit. Quit anyway?"
msgstr "Nog %d lers om te bewerk. Stop in elk geval?"
-#: ../ex_docmd.c:4248
msgid "E173: 1 more file to edit"
msgstr "E173: Nog 1 ler om te bewerk"
-#: ../ex_docmd.c:4250
#, c-format
msgid "E173: %<PRId64> more files to edit"
msgstr "E173: Nog %<PRId64> lers om te bewerk"
-#: ../ex_docmd.c:4320
msgid "E174: Command already exists: add ! to replace it"
msgstr "E174: Bevel bestaan alreeds: gebruik ! om te herdefinieer"
-#: ../ex_docmd.c:4432
+#, fuzzy
msgid ""
"\n"
-" Name Args Range Complete Definition"
+" Name Args Address Complete Definition"
msgstr ""
"\n"
" Naam Args Reeks Klaar Definisie"
-#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
msgstr "Geen gebruiker-gedefinieerde bevele gevind nie"
-#: ../ex_docmd.c:4538
msgid "E175: No attribute specified"
msgstr "E175: Geen eienskappe gespesifiseer nie"
-#: ../ex_docmd.c:4583
msgid "E176: Invalid number of arguments"
msgstr "E176: Ongeldige aantal parameters"
-#: ../ex_docmd.c:4594
msgid "E177: Count cannot be specified twice"
msgstr "E177: Telling kan nie twee keer gespesifiseer word nie"
-#: ../ex_docmd.c:4603
msgid "E178: Invalid default value for count"
msgstr "E178: Ongeldige verstekwaarde vir telling"
-#: ../ex_docmd.c:4625
#, fuzzy
-msgid "E179: argument required for -complete"
-msgstr "E179: parameter nodig vir voltooiing"
+#~ msgid "E179: argument required for -complete"
+#~ msgstr "E179: parameter nodig vir voltooiing"
+
+#, fuzzy
+#~ msgid "E179: argument required for -addr"
+#~ msgstr "E179: parameter nodig vir voltooiing"
-#: ../ex_docmd.c:4635
#, c-format
msgid "E181: Invalid attribute: %s"
msgstr "E181: Ongeldige eienskap: %s"
-#: ../ex_docmd.c:4678
msgid "E182: Invalid command name"
msgstr "E182: Ongeldige bevelnaam"
-#: ../ex_docmd.c:4691
msgid "E183: User defined commands must start with an uppercase letter"
msgstr "E183: Gebruiker-gedefinieerde bevele moet met 'n hoofletter begin"
-#: ../ex_docmd.c:4696
#, fuzzy
-msgid "E841: Reserved name, cannot be used for user defined command"
-msgstr "E464: Dubbelsinnige gebruik van gebruiker-gedefinieerde bevel"
+#~ msgid "E841: Reserved name, cannot be used for user defined command"
+#~ msgstr "E464: Dubbelsinnige gebruik van gebruiker-gedefinieerde bevel"
-#: ../ex_docmd.c:4751
#, c-format
msgid "E184: No such user-defined command: %s"
msgstr "E184: Geen gebruiker-gedefinieerde bevel nie: %s"
-#: ../ex_docmd.c:5219
+#, fuzzy, c-format
+#~ msgid "E180: Invalid address type value: %s"
+#~ msgstr "E180: Ongeldige voltooiingswaarde: %s"
+
#, c-format
msgid "E180: Invalid complete value: %s"
msgstr "E180: Ongeldige voltooiingswaarde: %s"
-#: ../ex_docmd.c:5225
msgid "E468: Completion argument only allowed for custom completion"
msgstr "E468: Voltooiingsargument words slegs toegelaat vir eie voltooiing"
-#: ../ex_docmd.c:5231
msgid "E467: Custom completion requires a function argument"
msgstr "E467: Eie voltooiing benodig 'n funksie parameter"
-#: ../ex_docmd.c:5257
#, fuzzy, c-format
-msgid "E185: Cannot find color scheme '%s'"
-msgstr "E185: Kan nie kleurskema %s vind nie"
+#~ msgid "E185: Cannot find color scheme '%s'"
+#~ msgstr "E185: Kan nie kleurskema %s vind nie"
-#: ../ex_docmd.c:5263
msgid "Greetings, Vim user!"
msgstr "Goeiedag, Vim gebruiker!"
-#: ../ex_docmd.c:5431
#, fuzzy
-msgid "E784: Cannot close last tab page"
-msgstr "E444: Kan nie laaste venster toemaak nie"
+#~ msgid "E784: Cannot close last tab page"
+#~ msgstr "E444: Kan nie laaste venster toemaak nie"
-#: ../ex_docmd.c:5462
#, fuzzy
-msgid "Already only one tab page"
-msgstr "Daar is alreeds slegs een venster"
+#~ msgid "Already only one tab page"
+#~ msgstr "Daar is alreeds slegs een venster"
-#: ../ex_docmd.c:6004
#, fuzzy, c-format
-msgid "Tab page %d"
-msgstr "Bladsy %d"
+#~ msgid "Tab page %d"
+#~ msgstr "Bladsy %d"
-#: ../ex_docmd.c:6295
msgid "No swap file"
msgstr "Geen ruiller"
-#: ../ex_docmd.c:6478
-#, fuzzy
-msgid "E747: Cannot change directory, buffer is modified (add ! to override)"
-msgstr "E509: Kan rugsteunler nie skep nie (gebruik ! om te dwing)"
-
-#: ../ex_docmd.c:6485
msgid "E186: No previous directory"
msgstr "E186: Geen vorige gids nie"
-#: ../ex_docmd.c:6530
msgid "E187: Unknown"
msgstr "E187: Onbekend"
-#: ../ex_docmd.c:6610
msgid "E465: :winsize requires two number arguments"
msgstr "E465: ':winsize' benodig twee nommer parameters"
-#: ../ex_docmd.c:6655
-msgid "E188: Obtaining window position not implemented for this platform"
-msgstr ""
-"E188: Verkryging van vensterposisie is nie vir hierdie platform "
-"gemplementeer nie"
-
-#: ../ex_docmd.c:6662
-msgid "E466: :winpos requires two number arguments"
-msgstr "E466: :winpos benodig twee parameters"
-
-#: ../ex_docmd.c:7241
-#, fuzzy, c-format
-msgid "E739: Cannot create directory: %s"
-msgstr "Kan nie gids uitvoer nie: \"%s\""
-
-#: ../ex_docmd.c:7268
#, c-format
msgid "E189: \"%s\" exists (add ! to override)"
msgstr "E189: \"%s\" bestaan (gebruik ! om te dwing)"
-#: ../ex_docmd.c:7273
#, c-format
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: Kan \"%s\" nie oopmaak vir skryf nie"
#. set mark
-#: ../ex_docmd.c:7294
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr ""
"E191: Parameter moet 'n letter of 'n terug/vorentoe aanhalingsteken wees"
-#: ../ex_docmd.c:7333
msgid "E192: Recursive use of :normal too deep"
msgstr "E192: Rekursiewe gebruik van ':normal' te diep"
-#: ../ex_docmd.c:7807
msgid "E194: No alternate file name to substitute for '#'"
msgstr "E194: Geen alternatiewe lernaam vir '#' nie"
-#: ../ex_docmd.c:7841
msgid "E495: no autocommand file name to substitute for \"<afile>\""
msgstr "E495: geen outobevel-lernaam om \"<afile>\" mee te vervang nie"
-#: ../ex_docmd.c:7850
msgid "E496: no autocommand buffer number to substitute for \"<abuf>\""
msgstr "E496: geen outobevel buffernommer om \"<abuf>\" mee te vervang nie"
-#: ../ex_docmd.c:7861
msgid "E497: no autocommand match name to substitute for \"<amatch>\""
msgstr "E497: geen outobevel treffernaam om \"<amatch>\" mee te vervang nie"
-#: ../ex_docmd.c:7870
msgid "E498: no :source file name to substitute for \"<sfile>\""
msgstr "E498: geen ':source' lernaam om \"<sfile>\" mee te vervang nie"
-#: ../ex_docmd.c:7876
#, fuzzy
-msgid "E842: no line number to use for \"<slnum>\""
-msgstr "E498: geen ':source' lernaam om \"<sfile>\" mee te vervang nie"
+#~ msgid "E842: no line number to use for \"<slnum>\""
+#~ msgstr "E498: geen ':source' lernaam om \"<sfile>\" mee te vervang nie"
-#: ../ex_docmd.c:7903
#, c-format
msgid "E499: Empty file name for '%' or '#', only works with \":p:h\""
msgstr "E499: Le lernaam vir '%' of '#', werk slegs met \":p:h\""
-#: ../ex_docmd.c:7905
msgid "E500: Evaluates to an empty string"
msgstr "E500: Evalueer na 'n le string"
-#: ../ex_docmd.c:8838
-msgid "E195: Cannot open viminfo file for reading"
-msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie"
-
-#: ../ex_eval.c:464
msgid "E608: Cannot :throw exceptions with 'Vim' prefix"
msgstr "E608: Kan nie uitsonderings ':throw' met 'Vim' voorvoegsel nie"
#. always scroll up, don't overwrite
-#: ../ex_eval.c:496
#, c-format
msgid "Exception thrown: %s"
msgstr "Uitsondering gegooi: %s"
-#: ../ex_eval.c:545
+#. always scroll up, don't overwrite
#, c-format
msgid "Exception finished: %s"
msgstr "Uitsondering het klaar gemaak: %s"
-#: ../ex_eval.c:546
#, c-format
msgid "Exception discarded: %s"
msgstr "Uitsondering weg gegooi: %s"
-#: ../ex_eval.c:588 ../ex_eval.c:634
#, c-format
msgid "%s, line %<PRId64>"
msgstr "%s, rel %<PRId64>"
#. always scroll up, don't overwrite
-#: ../ex_eval.c:608
#, c-format
msgid "Exception caught: %s"
msgstr "Uitsondering gevang: %s"
-#: ../ex_eval.c:676
#, c-format
msgid "%s made pending"
msgstr "%s is afwagtend gemaak"
-#: ../ex_eval.c:679
#, c-format
msgid "%s resumed"
msgstr "%s teruggekeer"
-#: ../ex_eval.c:683
#, c-format
msgid "%s discarded"
msgstr "%s weg gegooi"
-#: ../ex_eval.c:708
msgid "Exception"
msgstr "Uitsondering"
-#: ../ex_eval.c:713
msgid "Error and interrupt"
msgstr "Fout en onderbreking"
-#: ../ex_eval.c:715
msgid "Error"
msgstr "Fout"
#. if (pending & CSTP_INTERRUPT)
-#: ../ex_eval.c:717
msgid "Interrupt"
msgstr "Onderbreek"
-#: ../ex_eval.c:795
msgid "E579: :if nesting too deep"
msgstr "E579: geneste ':if' te diep"
-#: ../ex_eval.c:830
msgid "E580: :endif without :if"
msgstr "E580: ':endif' sonder ':if'"
-#: ../ex_eval.c:873
msgid "E581: :else without :if"
msgstr "E581: ':else' sonder ':if'"
-#: ../ex_eval.c:876
msgid "E582: :elseif without :if"
msgstr "E582: ':elseif' sonder ':if'"
-#: ../ex_eval.c:880
msgid "E583: multiple :else"
msgstr "E583: meer as een ':else'"
-#: ../ex_eval.c:883
msgid "E584: :elseif after :else"
msgstr "E584: ':elseif' na ':else'"
-#: ../ex_eval.c:941
#, fuzzy
-msgid "E585: :while/:for nesting too deep"
-msgstr "E585: ':while' te diep genes"
+#~ msgid "E585: :while/:for nesting too deep"
+#~ msgstr "E585: ':while' te diep genes"
-#: ../ex_eval.c:1028
#, fuzzy
-msgid "E586: :continue without :while or :for"
-msgstr "E586: ':continue' sonder ':while'"
+#~ msgid "E586: :continue without :while or :for"
+#~ msgstr "E586: ':continue' sonder ':while'"
-#: ../ex_eval.c:1061
#, fuzzy
-msgid "E587: :break without :while or :for"
-msgstr "E587: ':break' sonder ':while'"
+#~ msgid "E587: :break without :while or :for"
+#~ msgstr "E587: ':break' sonder ':while'"
-#: ../ex_eval.c:1102
#, fuzzy
-msgid "E732: Using :endfor with :while"
-msgstr "E170: Ontbrekende ':endwhile'"
+#~ msgid "E732: Using :endfor with :while"
+#~ msgstr "E170: Ontbrekende ':endwhile'"
-#: ../ex_eval.c:1104
#, fuzzy
-msgid "E733: Using :endwhile with :for"
-msgstr "E170: Ontbrekende ':endwhile'"
+#~ msgid "E733: Using :endwhile with :for"
+#~ msgstr "E170: Ontbrekende ':endwhile'"
-#: ../ex_eval.c:1247
msgid "E601: :try nesting too deep"
msgstr "E601: geneste ':try' te diep"
-#: ../ex_eval.c:1317
msgid "E603: :catch without :try"
msgstr "E603: ':catch' sonder ':try'"
#. Give up for a ":catch" after ":finally" and ignore it.
#. * Just parse.
-#: ../ex_eval.c:1332
msgid "E604: :catch after :finally"
msgstr "E604: ':catch' na ':finally'"
-#: ../ex_eval.c:1451
msgid "E606: :finally without :try"
msgstr "E606: ':finally' sonder ':try'"
#. Give up for a multiple ":finally" and ignore it.
-#: ../ex_eval.c:1467
msgid "E607: multiple :finally"
msgstr "E607: meer as een ':finally'"
-#: ../ex_eval.c:1571
msgid "E602: :endtry without :try"
msgstr "E602: ':endtry' sonder ':try'"
-#: ../ex_eval.c:2026
msgid "E193: :endfunction not inside a function"
msgstr "E193: ':endfunction' nie in 'n funksie nie"
-#: ../ex_getln.c:1643
#, fuzzy
-msgid "E788: Not allowed to edit another buffer now"
-msgstr "E48: Nie toegelaat in sandput nie"
+#~ msgid "E788: Not allowed to edit another buffer now"
+#~ msgstr "E48: Nie toegelaat in sandput nie"
-#: ../ex_getln.c:1656
#, fuzzy
-msgid "E811: Not allowed to change buffer information now"
-msgstr "E94: Geen buffer wat by %s pas nie"
+#~ msgid "E811: Not allowed to change buffer information now"
+#~ msgstr "E94: Geen buffer wat by %s pas nie"
-#: ../ex_getln.c:3178
-msgid "tagname"
-msgstr "etiketnaam"
+#, c-format
+#~ msgid "E5408: Unable to get g:Nvim_color_cmdline callback: %s"
+#~ msgstr ""
-#: ../ex_getln.c:3181
-msgid " kind file\n"
-msgstr " tipe ler\n"
+#, c-format
+#~ msgid "E5409: Unable to get g:Nvim_color_expr callback: %s"
+#~ msgstr ""
-#: ../ex_getln.c:4799
-msgid "'history' option is zero"
-msgstr "'history' opsie is nul"
+#, c-format
+#~ msgid "E5407: Callback has thrown an exception: %s"
+#~ msgstr ""
+
+#~ msgid "E5400: Callback should return list"
+#~ msgstr ""
-#: ../ex_getln.c:5046
#, c-format
-msgid ""
-"\n"
-"# %s History (newest to oldest):\n"
-msgstr ""
-"\n"
-"# %s Geskiedenis (van nuutste na oudste):\n"
+#~ msgid "E5401: List item %i is not a List"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E5402: List item %i has incorrect length: %li /= 3"
+#~ msgstr ""
+
+#~ msgid "E5403: Chunk %i start %"
+#~ msgstr ""
-#: ../ex_getln.c:5047
-msgid "Command Line"
-msgstr "Bevelrel"
+#~ msgid "E5405: Chunk %i start %"
+#~ msgstr ""
-#: ../ex_getln.c:5048
-msgid "Search String"
-msgstr "Soekstring"
+#~ msgid "E5404: Chunk %i end %"
+#~ msgstr ""
-#: ../ex_getln.c:5049
-msgid "Expression"
-msgstr "Uitdrukking"
+#~ msgid "E5406: Chunk %i end %"
+#~ msgstr ""
-#: ../ex_getln.c:5050
-msgid "Input Line"
-msgstr "Invoer Lyn"
+msgid "tagname"
+msgstr "etiketnaam"
+
+msgid " kind file\n"
+msgstr " tipe ler\n"
+
+msgid "'history' option is zero"
+msgstr "'history' opsie is nul"
-#: ../ex_getln.c:5117
msgid "E198: cmd_pchar beyond the command length"
msgstr "E198: 'cmd_pchar' verby die einde van opdraglengte"
-#: ../ex_getln.c:5279
msgid "E199: Active window or buffer deleted"
msgstr "E199: Aktiewe venster of buffer geskrap"
-#: ../file_search.c:203
-msgid "E854: path too long for completion"
-msgstr ""
+#~ msgid "E854: path too long for completion"
+#~ msgstr ""
-#: ../file_search.c:446
#, c-format
msgid ""
"E343: Invalid path: '**[number]' must be at the end of the path or be "
@@ -2052,267 +1999,208 @@ msgstr ""
"E343: Ongeldige pad: '**[nommer]' moet aan die einde van 'n pad wees of "
"gevolg wees deur %s'."
-#: ../file_search.c:1505
#, c-format
msgid "E344: Can't find directory \"%s\" in cdpath"
msgstr "E344: Kan nie gids \"%s\" in 'cdpath' vind nie"
-#: ../file_search.c:1508
#, c-format
msgid "E345: Can't find file \"%s\" in path"
msgstr "E345: Kan ler \"%s\" nie vind in pad nie"
-#: ../file_search.c:1512
#, c-format
msgid "E346: No more directory \"%s\" found in cdpath"
msgstr "E346: Geen gids \"%s\" meer gevind in 'cdpath' nie"
-#: ../file_search.c:1515
#, c-format
msgid "E347: No more file \"%s\" found in path"
msgstr "E347: Geen ler \"%s\" meer gevind in pad nie"
-#: ../fileio.c:137
#, fuzzy
-msgid "E812: Autocommands changed buffer or buffer name"
-msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie"
+#~ msgid "E812: Autocommands changed buffer or buffer name"
+#~ msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie"
-#: ../fileio.c:368
msgid "Illegal file name"
msgstr "Ongeldige lernaam"
-#: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578
msgid "is a directory"
msgstr "is 'n gids"
-#: ../fileio.c:397
msgid "is not a file"
msgstr "is nie 'n ler nie"
-#: ../fileio.c:508 ../fileio.c:3522
msgid "[New File]"
msgstr "[Nuwe ler]"
-#: ../fileio.c:511
-msgid "[New DIRECTORY]"
-msgstr ""
+#~ msgid "[New DIRECTORY]"
+#~ msgstr ""
-#: ../fileio.c:529 ../fileio.c:532
-msgid "[File too big]"
-msgstr ""
+#. libuv only returns -errno in Unix and in Windows open() does not
+#. set EOVERFLOW
+#~ msgid "[File too big]"
+#~ msgstr ""
-#: ../fileio.c:534
msgid "[Permission Denied]"
msgstr "[Toestemming Geweier]"
-#: ../fileio.c:653
msgid "E200: *ReadPre autocommands made the file unreadable"
msgstr "E200: '*ReadPre' outobevele het die ler onleesbaar gemaak"
-#: ../fileio.c:655
msgid "E201: *ReadPre autocommands must not change current buffer"
msgstr "E201: '*ReadPre' outobevele mag nie die huidige buffer verander nie"
-#: ../fileio.c:672
-msgid "Nvim: Reading from stdin...\n"
-msgstr "Vim: Lees nou vanaf 'stdin'...\n"
-
#. Re-opening the original file failed!
-#: ../fileio.c:909
msgid "E202: Conversion made file unreadable!"
msgstr "E202: Omsetting het ler onleesbaar gemaak!"
#. fifo or socket
-#: ../fileio.c:1782
msgid "[fifo/socket]"
msgstr "[fifo/socket]"
#. fifo
-#: ../fileio.c:1788
msgid "[fifo]"
msgstr "[fifo]"
#. or socket
-#: ../fileio.c:1794
msgid "[socket]"
msgstr "[socket]"
#. or character special
-#: ../fileio.c:1801
#, fuzzy
-msgid "[character special]"
-msgstr "1 karakter"
+#~ msgid "[character special]"
+#~ msgstr "1 karakter"
-#: ../fileio.c:1815
msgid "[CR missing]"
msgstr "[CR ontbreek]"
-#: ../fileio.c:1819
msgid "[long lines split]"
msgstr "[lang rels verdeel]"
-#: ../fileio.c:1823 ../fileio.c:3512
msgid "[NOT converted]"
msgstr "[NIE omgesit nie]"
-#: ../fileio.c:1826 ../fileio.c:3515
msgid "[converted]"
msgstr "[omgesit]"
-#: ../fileio.c:1831
#, fuzzy, c-format
-msgid "[CONVERSION ERROR in line %<PRId64>]"
-msgstr "[ONWETTIGE GREEP in rel %<PRId64>]"
+#~ msgid "[CONVERSION ERROR in line %<PRId64>]"
+#~ msgstr "[ONWETTIGE GREEP in rel %<PRId64>]"
-#: ../fileio.c:1835
#, c-format
msgid "[ILLEGAL BYTE in line %<PRId64>]"
msgstr "[ONWETTIGE GREEP in rel %<PRId64>]"
-#: ../fileio.c:1838
msgid "[READ ERRORS]"
msgstr "[LEESFOUTE]"
-#: ../fileio.c:2104
msgid "Can't find temp file for conversion"
msgstr "Kan nie tydelike ler vir omsetting vind nie"
-#: ../fileio.c:2110
msgid "Conversion with 'charconvert' failed"
msgstr "Omsetting met 'charconvert' het gefaal"
-#: ../fileio.c:2113
msgid "can't read output of 'charconvert'"
msgstr "kan afvoer van 'charconvert' nie lees nie"
-#: ../fileio.c:2437
#, fuzzy
-msgid "E676: No matching autocommands for acwrite buffer"
-msgstr "Geen passende outobevele nie"
+#~ msgid "E676: No matching autocommands for acwrite buffer"
+#~ msgstr "Geen passende outobevele nie"
-#: ../fileio.c:2466
msgid "E203: Autocommands deleted or unloaded buffer to be written"
msgstr "E203: Outobevele het die skryfbuffer geskrap of uitgelaai"
-#: ../fileio.c:2486
msgid "E204: Autocommand changed number of lines in unexpected way"
msgstr "E204: Outobevel het etlike rels op onverwagse wyse verander "
-#: ../fileio.c:2548 ../fileio.c:2565
msgid "is not a file or writable device"
msgstr "is nie 'n ler of 'n skryfbare toestel nie"
-#: ../fileio.c:2601
msgid "is read-only (add ! to override)"
msgstr "is lees-alleen (gebruik ! om te dwing)"
-#: ../fileio.c:2886
msgid "E506: Can't write to backup file (add ! to override)"
msgstr "E506: Kan nie na rugsteunler skryf nie (gebruik ! om te dwing)"
-#: ../fileio.c:2898
-msgid "E507: Close error for backup file (add ! to override)"
-msgstr "E507: Sluitfout vir rugsteunler (gebruik ! om te dwing)"
+#, fuzzy, c-format
+#~ msgid "E507: Close error for backup file (add ! to override): %s"
+#~ msgstr "E507: Sluitfout vir rugsteunler (gebruik ! om te dwing)"
-#: ../fileio.c:2901
msgid "E508: Can't read file for backup (add ! to override)"
msgstr "E508: Kan rugsteunler nie lees nie (gebruik ! om te dwing)"
-#: ../fileio.c:2923
msgid "E509: Cannot create backup file (add ! to override)"
msgstr "E509: Kan rugsteunler nie skep nie (gebruik ! om te dwing)"
-#: ../fileio.c:3008
msgid "E510: Can't make backup file (add ! to override)"
msgstr "E510: Kan rugsteunler nie skep nie (gebruik ! om te dwing)"
#. Can't write without a tempfile!
-#: ../fileio.c:3121
msgid "E214: Can't find temp file for writing"
msgstr "E214: Kan nie tydelike ler vind vir skryf nie"
-#: ../fileio.c:3134
msgid "E213: Cannot convert (add ! to write without conversion)"
msgstr "E213: Kan nie omsit nie (gebruik ! om te skryf sonder omsetting)"
-#: ../fileio.c:3169
msgid "E166: Can't open linked file for writing"
msgstr "E166: Kan ler nie oopmaak vir skryf nie"
-#: ../fileio.c:3173
-msgid "E212: Can't open file for writing"
-msgstr "E212: Kan ler nie oopmaak vir skryf nie"
+#, fuzzy, c-format
+#~ msgid "E212: Can't open file for writing: %s"
+#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie"
-#: ../fileio.c:3363
-msgid "E667: Fsync failed"
-msgstr "E667: 'Fsync' het gefaal"
+#, fuzzy, c-format
+#~ msgid "E667: Fsync failed: %s"
+#~ msgstr "E667: 'Fsync' het gefaal"
-#: ../fileio.c:3398
-msgid "E512: Close failed"
-msgstr "E512: Sluiting gefaal"
+#, fuzzy, c-format
+#~ msgid "E512: Close failed: %s"
+#~ msgstr "E512: Sluiting gefaal"
-#: ../fileio.c:3436
#, fuzzy
-msgid "E513: write error, conversion failed (make 'fenc' empty to override)"
-msgstr "E513: skryffout, omsetting gefaal"
+#~ msgid "E513: write error, conversion failed (make 'fenc' empty to override)"
+#~ msgstr "E513: skryffout, omsetting gefaal"
-#: ../fileio.c:3441
-#, c-format
-msgid ""
-"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
-"override)"
-msgstr ""
+#, fuzzy
+#~ msgid "E513: write error, conversion failed in line %"
+#~ msgstr "E513: skryffout, omsetting gefaal"
-#: ../fileio.c:3448
msgid "E514: write error (file system full?)"
msgstr "E514: skryffout (lerstelsel vol?)"
-#: ../fileio.c:3506
msgid " CONVERSION ERROR"
msgstr " OMSETTINGSFOUT"
-#: ../fileio.c:3509
#, fuzzy, c-format
-msgid " in line %<PRId64>;"
-msgstr "rel %<PRId64>"
+#~ msgid " in line %<PRId64>;"
+#~ msgstr "rel %<PRId64>"
-#: ../fileio.c:3519
msgid "[Device]"
msgstr "[Toestel]"
-#: ../fileio.c:3522
msgid "[New]"
msgstr "[Nuut]"
-#: ../fileio.c:3535
msgid " [a]"
msgstr " [a]"
-#: ../fileio.c:3535
msgid " appended"
msgstr " bygevoeg"
-#: ../fileio.c:3537
msgid " [w]"
msgstr " [w]"
-#: ../fileio.c:3537
msgid " written"
msgstr " geskryf"
-#: ../fileio.c:3579
msgid "E205: Patchmode: can't save original file"
msgstr "E205: patchmode: kan oorspronklike ler nie stoor nie"
-#: ../fileio.c:3602
msgid "E206: patchmode: can't touch empty original file"
msgstr "E206: patchmode: kan le oorsprongler nie 'touch' nie"
-#: ../fileio.c:3616
msgid "E207: Can't delete backup file"
msgstr "E207: Kan rugsteunler nie verwyder nie"
-#: ../fileio.c:3672
+#. Set highlight for error messages.
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
@@ -2320,96 +2208,75 @@ msgstr ""
"\n"
"WAARSKUWING: Oorspronklike ler mag verlore of beskadig wees\n"
-#: ../fileio.c:3675
msgid "don't quit the editor until the file is successfully written!"
msgstr "moenie die verwerker verlaat voor die ler suksesvol geskryf is nie!"
-#: ../fileio.c:3795
-msgid "[dos]"
-msgstr "[dos]"
-
-#: ../fileio.c:3795
msgid "[dos format]"
msgstr "[dos formaat]"
-#: ../fileio.c:3801
-msgid "[mac]"
-msgstr "[mac]"
+msgid "[dos]"
+msgstr "[dos]"
-#: ../fileio.c:3801
msgid "[mac format]"
msgstr "[mac formaat]"
-#: ../fileio.c:3807
-msgid "[unix]"
-msgstr "[unix]"
+msgid "[mac]"
+msgstr "[mac]"
-#: ../fileio.c:3807
msgid "[unix format]"
msgstr "[unix formaat]"
-#: ../fileio.c:3831
+msgid "[unix]"
+msgstr "[unix]"
+
msgid "1 line, "
msgstr "1 rel, "
-#: ../fileio.c:3833
#, c-format
msgid "%<PRId64> lines, "
msgstr "%<PRId64> rels, "
-#: ../fileio.c:3836
msgid "1 character"
msgstr "1 karakter"
-#: ../fileio.c:3838
#, c-format
msgid "%<PRId64> characters"
msgstr "%<PRId64> karakters"
-#: ../fileio.c:3849
-msgid "[noeol]"
-msgstr "[noeol]"
-
-#: ../fileio.c:3849
msgid "[Incomplete last line]"
msgstr "[Onvoltooide laaste rel]"
-#. don't overwrite messages here
-#. must give this prompt
-#. don't use emsg() here, don't want to flush the buffers
-#: ../fileio.c:3865
+msgid "[noeol]"
+msgstr "[noeol]"
+
+#. Don't overwrite messages here.
+#. Must give this prompt.
+#. Don't use emsg() here, don't want to flush the buffers.
msgid "WARNING: The file has been changed since reading it!!!"
msgstr "WAARSKUWING: Die ler het verander sedert dit gelees is!!!"
-#: ../fileio.c:3867
msgid "Do you really want to write to it"
msgstr "Wil jy regtig soontoe skryf?"
-#: ../fileio.c:4648
#, c-format
msgid "E208: Error writing to \"%s\""
msgstr "E208: Kan nie skryf na \"%s\""
-#: ../fileio.c:4655
#, c-format
msgid "E209: Error closing \"%s\""
msgstr "E209: Kan \"%s\" nie sluit nie"
-#: ../fileio.c:4657
#, c-format
msgid "E210: Error reading \"%s\""
msgstr "E210: Kan \"%s\" nie lees nie"
-#: ../fileio.c:4883
msgid "E246: FileChangedShell autocommand deleted buffer"
msgstr "E246: 'FileChangedShell' outobevel het buffer verwyder"
-#: ../fileio.c:4894
#, fuzzy, c-format
-msgid "E211: File \"%s\" no longer available"
-msgstr "E211: Waarskuwing: Ler \"%s\" is nie meer beskikbaar nie"
+#~ msgid "E211: File \"%s\" no longer available"
+#~ msgstr "E211: Waarskuwing: Ler \"%s\" is nie meer beskikbaar nie"
-#: ../fileio.c:4906
#, c-format
msgid ""
"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as "
@@ -2418,42 +2285,34 @@ msgstr ""
"W12: Waarskuwing: Ler \"%s\" het verander sedert bewerking begin het en die "
"buffer in Vim het ook verander"
-#: ../fileio.c:4907
#, fuzzy
-msgid "See \":help W12\" for more info."
-msgstr "Sien \":help W11\" vir meer inligting."
+#~ msgid "See \":help W12\" for more info."
+#~ msgstr "Sien \":help W11\" vir meer inligting."
-#: ../fileio.c:4910
#, c-format
msgid "W11: Warning: File \"%s\" has changed since editing started"
msgstr "W11: Waarskuwing: Ler \"%s\" het verander sedert bewerking begin het"
-#: ../fileio.c:4911
msgid "See \":help W11\" for more info."
msgstr "Sien \":help W11\" vir meer inligting."
-#: ../fileio.c:4914
#, c-format
msgid "W16: Warning: Mode of file \"%s\" has changed since editing started"
msgstr ""
"W16: Waarskuwing: Modus van ler \"%s\" het verander sedert bewerking begin "
"het"
-#: ../fileio.c:4915
#, fuzzy
-msgid "See \":help W16\" for more info."
-msgstr "Sien \":help W11\" vir meer inligting."
+#~ msgid "See \":help W16\" for more info."
+#~ msgstr "Sien \":help W11\" vir meer inligting."
-#: ../fileio.c:4927
#, c-format
msgid "W13: Warning: File \"%s\" has been created after editing started"
msgstr "W13: Waarskuwing: Ler \"%s\" is geskep sedert bewerking begin het"
-#: ../fileio.c:4947
msgid "Warning"
msgstr "Waarskuwing"
-#: ../fileio.c:4948
msgid ""
"&OK\n"
"&Load File"
@@ -2461,48 +2320,46 @@ msgstr ""
"&OK\n"
"&Laai Ler"
-#: ../fileio.c:5065
#, c-format
msgid "E462: Could not prepare for reloading \"%s\""
msgstr "E462: Kon nie voorberei vir herlaai nie \"%s\""
-#: ../fileio.c:5078
#, c-format
msgid "E321: Could not reload \"%s\""
msgstr "E321: Kon nie \"%s\" herlaai nie"
-#: ../fileio.c:5601
msgid "--Deleted--"
msgstr "--Geskrap--"
-#: ../fileio.c:5732
#, c-format
-msgid "auto-removing autocommand: %s <buffer=%d>"
-msgstr ""
+#~ msgid "auto-removing autocommand: %s <buffer=%d>"
+#~ msgstr ""
#. the group doesn't exist
-#: ../fileio.c:5772
#, c-format
msgid "E367: No such group: \"%s\""
msgstr "E367: Geen sodanige groep nie: \"%s\""
-#: ../fileio.c:5897
+#, fuzzy
+#~ msgid "E936: Cannot delete the current group"
+#~ msgstr "E351: Kan nie vou skrap met huidige 'foldmethod' nie"
+
+#~ msgid "W19: Deleting augroup that is still in use"
+#~ msgstr ""
+
#, c-format
msgid "E215: Illegal character after *: %s"
msgstr "E215: Ongeldige karakter na *: %s"
-#: ../fileio.c:5905
#, c-format
msgid "E216: No such event: %s"
msgstr "E216: Geen sodanige gebeurtenis nie: %s"
-#: ../fileio.c:5907
#, c-format
msgid "E216: No such group or event: %s"
msgstr "E216: Geen sodanige groep of gebeurtenis nie: %s"
#. Highlight title
-#: ../fileio.c:6090
msgid ""
"\n"
"--- Auto-Commands ---"
@@ -2510,108 +2367,88 @@ msgstr ""
"\n"
"--- Outobevele ---"
-#: ../fileio.c:6293
#, fuzzy, c-format
-msgid "E680: <buffer=%d>: invalid buffer number "
-msgstr "ongeldige buffernommer"
+#~ msgid "E680: <buffer=%d>: invalid buffer number "
+#~ msgstr "ongeldige buffernommer"
-#: ../fileio.c:6370
msgid "E217: Can't execute autocommands for ALL events"
msgstr "E217: Kan nie outobevele uitvoer vir 'ALL' gebeurtenisse nie"
-#: ../fileio.c:6393
msgid "No matching autocommands"
msgstr "Geen passende outobevele nie"
-#: ../fileio.c:6831
msgid "E218: autocommand nesting too deep"
msgstr "E218: outobevele te diep genes"
-#: ../fileio.c:7143
#, c-format
msgid "%s Auto commands for \"%s\""
msgstr "%s outobevele vir \"%s\""
-#: ../fileio.c:7149
#, c-format
msgid "Executing %s"
msgstr "Voer %s uit"
-#: ../fileio.c:7211
#, c-format
msgid "autocommand %s"
msgstr "outobevel %s"
-#: ../fileio.c:7795
msgid "E219: Missing {."
msgstr "E219: Ontbrekende {."
-#: ../fileio.c:7797
msgid "E220: Missing }."
msgstr "E220: Ontbrekende }."
-#: ../fold.c:93
msgid "E490: No fold found"
msgstr "E490: Geen vou gevind nie"
-#: ../fold.c:544
msgid "E350: Cannot create fold with current 'foldmethod'"
msgstr "E350: Kan nie vou skep met huidige 'foldmethod' nie"
-#: ../fold.c:546
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: Kan nie vou skrap met huidige 'foldmethod' nie"
-#: ../fold.c:1784
-#, c-format
-msgid "+--%3ld lines folded "
-msgstr "+--%3ld rels gevou "
+#, fuzzy, c-format
+#~ msgid "+--%3ld line folded"
+#~ msgid_plural "+--%3ld lines folded "
+#~ msgstr[0] "+--%3ld rels gevou "
+#~ msgstr[1] "+--%3ld rels gevou "
#. buffer has already been read
-#: ../getchar.c:273
msgid "E222: Add to read buffer"
msgstr "E222: Voeg by leesbuffer"
-#: ../getchar.c:2040
msgid "E223: recursive mapping"
msgstr "E223: rekursiewe binding"
-#: ../getchar.c:2849
#, c-format
msgid "E224: global abbreviation already exists for %s"
msgstr "E224: globale afkorting bestaan alreeds vir %s"
-#: ../getchar.c:2852
#, c-format
msgid "E225: global mapping already exists for %s"
msgstr "E225: globale binding bestaan alreeds vir %s"
-#: ../getchar.c:2952
#, c-format
msgid "E226: abbreviation already exists for %s"
msgstr "E226: afkorting bestaan already vir %s"
-#: ../getchar.c:2955
#, c-format
msgid "E227: mapping already exists for %s"
msgstr "E227: binding bestaan alreeds vir %s"
-#: ../getchar.c:3008
msgid "No abbreviation found"
msgstr "Geen afkorting gevind nie"
-#: ../getchar.c:3010
msgid "No mapping found"
msgstr "Geen binding gevind nie"
-#: ../getchar.c:3974
msgid "E228: makemap: Illegal mode"
msgstr "E228: makemap: Ongeldige modus"
-#. key value of 'cedit' option
-#. type of cmdline window or 0
-#. result of cmdline window or 0
-#: ../globals.h:924
+#. /< key value of 'cedit' option
+#. /< type of cmdline window or 0
+#. /< result of cmdline window or 0
+#. /< cmdline recursion level
msgid "--No lines in buffer--"
msgstr "--Geen rels in buffer--"
@@ -2619,691 +2456,566 @@ msgstr "--Geen rels in buffer--"
#. * The error messages that can be shared are included here.
#. * Excluded are errors that are only used once and debugging messages.
#.
-#: ../globals.h:996
msgid "E470: Command aborted"
msgstr "E470: Bevel gekanselleer"
-#: ../globals.h:997
+#, fuzzy
+#~ msgid "E905: Cannot set this option after startup"
+#~ msgstr "E529: Kan nie 'term' stel na le string nie"
+
+#, fuzzy
+#~ msgid "E903: Could not spawn API job"
+#~ msgstr "E623: Kon nie 'cscope' proses skep nie"
+
msgid "E471: Argument required"
msgstr "E471: Parameter benodig"
-#: ../globals.h:998
msgid "E10: \\ should be followed by /, ? or &"
msgstr "E10: \\ moet gevolg word deur /, ? of &"
-#: ../globals.h:1000
msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits"
msgstr "E11: Ongeldig in bevelrel venster: <CR> voer uit, CTRL-C stop"
-#: ../globals.h:1002
msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search"
msgstr ""
"E12: Bevel uit exrc/vimrc nie toegelaat in huidige gids- of etiketsoektog nie"
-#: ../globals.h:1003
msgid "E171: Missing :endif"
msgstr "E171: Ontbrekende ':endif'"
-#: ../globals.h:1004
msgid "E600: Missing :endtry"
msgstr "E600: Ontbrekende ':endtry'"
-#: ../globals.h:1005
msgid "E170: Missing :endwhile"
msgstr "E170: Ontbrekende ':endwhile'"
-#: ../globals.h:1006
#, fuzzy
-msgid "E170: Missing :endfor"
-msgstr "E171: Ontbrekende ':endif'"
+#~ msgid "E170: Missing :endfor"
+#~ msgstr "E171: Ontbrekende ':endif'"
-#: ../globals.h:1007
msgid "E588: :endwhile without :while"
msgstr "E588: ':endwhile' sonder ':while'"
-#: ../globals.h:1008
#, fuzzy
-msgid "E588: :endfor without :for"
-msgstr "E580: ':endif' sonder ':if'"
+#~ msgid "E588: :endfor without :for"
+#~ msgstr "E580: ':endif' sonder ':if'"
-#: ../globals.h:1009
msgid "E13: File exists (add ! to override)"
msgstr "E13: Ler bestaan (gebruik ! om te dwing)"
-#: ../globals.h:1010
msgid "E472: Command failed"
msgstr "E472: Bevel het gefaal"
-#: ../globals.h:1011
msgid "E473: Internal error"
msgstr "E473: Interne fout"
-#: ../globals.h:1012
msgid "Interrupted"
msgstr "Onderbreek"
-#: ../globals.h:1013
msgid "E14: Invalid address"
msgstr "E14: Ongeldige adres"
-#: ../globals.h:1014
msgid "E474: Invalid argument"
msgstr "E474: Ongeldige parameter"
-#: ../globals.h:1015
#, c-format
msgid "E475: Invalid argument: %s"
msgstr "E475: Ongeldige parameter: %s"
-#: ../globals.h:1016
#, c-format
msgid "E15: Invalid expression: %s"
msgstr "E15: Ongeldige uitdrukking: %s"
-#: ../globals.h:1017
msgid "E16: Invalid range"
msgstr "E16: Ongeldige omvang"
-#: ../globals.h:1018
msgid "E476: Invalid command"
msgstr "E476: Ongeldige bevel"
-#: ../globals.h:1019
#, c-format
msgid "E17: \"%s\" is a directory"
msgstr "E17: \"%s\" is 'n gids"
-#: ../globals.h:1020
#, fuzzy
-msgid "E900: Invalid job id"
-msgstr "E49: Ongeldige rolgrootte"
+#~ msgid "E900: Invalid job id"
+#~ msgstr "E49: Ongeldige rolgrootte"
-#: ../globals.h:1021
-msgid "E901: Job table is full"
-msgstr ""
+#~ msgid "E901: Job table is full"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E903: Process failed to start: %s: \"%s\""
+#~ msgstr ""
+
+#~ msgid "E904: Job is not connected to a pty"
+#~ msgstr ""
-#: ../globals.h:1024
#, c-format
msgid "E364: Library call failed for \"%s()\""
msgstr "E364: Biblioteekroep het gefaal vir \"%s\"()"
-#: ../globals.h:1026
+#, fuzzy, c-format
+#~ msgid "E739: Cannot create directory %s: %s"
+#~ msgstr "Kan nie gids uitvoer nie: \"%s\""
+
msgid "E19: Mark has invalid line number"
msgstr "E19: Merker het ongeldige relnommer"
-#: ../globals.h:1027
msgid "E20: Mark not set"
msgstr "E20: Merker nie gestel nie"
-#: ../globals.h:1029
msgid "E21: Cannot make changes, 'modifiable' is off"
msgstr "E21: Kan nie wysig nie, 'modifiable' is af"
-#: ../globals.h:1030
msgid "E22: Scripts nested too deep"
msgstr "E22: Skripte te diep ge-nes"
-#: ../globals.h:1031
msgid "E23: No alternate file"
msgstr "E23: Geen alternatiewe ler nie"
-#: ../globals.h:1032
msgid "E24: No such abbreviation"
msgstr "E24: Afkorting bestaan nie"
-#: ../globals.h:1033
msgid "E477: No ! allowed"
msgstr "E477: Geen ! toegelaat nie"
-#: ../globals.h:1035
msgid "E25: Nvim does not have a built-in GUI"
msgstr "E25: GUI kan nie gebruik word nie: Nie tydens kompilering gekies nie"
-#: ../globals.h:1036
#, c-format
msgid "E28: No such highlight group name: %s"
msgstr "E28: Geen sodanige uitliggroepnaam nie: %s"
-#: ../globals.h:1037
msgid "E29: No inserted text yet"
msgstr "E29: Nog geen ingevoegde teks nie"
-#: ../globals.h:1038
msgid "E30: No previous command line"
msgstr "E30: Geen vorige bevelrel nie"
-#: ../globals.h:1039
msgid "E31: No such mapping"
msgstr "E31: Geen so 'n binding nie"
-#: ../globals.h:1040
msgid "E479: No match"
msgstr "E479: Geen treffer nie"
-#: ../globals.h:1041
#, c-format
msgid "E480: No match: %s"
msgstr "E480: Geen treffer: %s"
-#: ../globals.h:1042
msgid "E32: No file name"
msgstr "E32: Geen lernaam"
-#: ../globals.h:1044
msgid "E33: No previous substitute regular expression"
msgstr "E33: Geen vorige vervangingspatroon nie"
-#: ../globals.h:1045
msgid "E34: No previous command"
msgstr "E34: Geen vorige bevel nie"
-#: ../globals.h:1046
msgid "E35: No previous regular expression"
msgstr "E35: Geen vorige patroon nie"
-#: ../globals.h:1047
msgid "E481: No range allowed"
msgstr "E481: Geen omvang toegelaat nie"
-#: ../globals.h:1048
msgid "E36: Not enough room"
msgstr "E36: Te min plek"
-#: ../globals.h:1049
-#, c-format
-msgid "E482: Can't create file %s"
-msgstr "E482: Kan nie ler %s skep nie"
-
-#: ../globals.h:1050
msgid "E483: Can't get temp file name"
msgstr "E483: Kan nie tydelike lernaam kry nie"
-#: ../globals.h:1051
#, c-format
msgid "E484: Can't open file %s"
msgstr "E484: Kan nie ler %s oopmaak nie"
-#: ../globals.h:1052
#, c-format
msgid "E485: Can't read file %s"
msgstr "E485: Kan nie ler %s lees nie"
-#: ../globals.h:1054
msgid "E37: No write since last change (add ! to override)"
msgstr "E37: Ongeskryf sedert vorige verandering (gebruik ! om te dwing)"
-#: ../globals.h:1055
#, fuzzy
-msgid "E37: No write since last change"
-msgstr "[Ongestoor sedert vorige verandering]\n"
+#~ msgid "E37: No write since last change"
+#~ msgstr "[Ongestoor sedert vorige verandering]\n"
-#: ../globals.h:1056
msgid "E38: Null argument"
msgstr "E38: Nul parameter"
-#: ../globals.h:1057
msgid "E39: Number expected"
msgstr "E39: Nommer verwag"
-#: ../globals.h:1058
#, c-format
msgid "E40: Can't open errorfile %s"
msgstr "E40: Kan nie foutler %s oopmaak nie"
-#: ../globals.h:1059
msgid "E41: Out of memory!"
msgstr "E41: Geheue op!"
-#: ../globals.h:1060
msgid "Pattern not found"
msgstr "Patroon nie gevind nie"
-#: ../globals.h:1061
#, c-format
msgid "E486: Pattern not found: %s"
msgstr "E486: Patroon nie gevind nie: %s"
-#: ../globals.h:1062
msgid "E487: Argument must be positive"
msgstr "E487: Parameter moet positief wees"
-#: ../globals.h:1064
msgid "E459: Cannot go back to previous directory"
msgstr "E459: Kan nie terug gaan na die vorige gids nie"
-#: ../globals.h:1066
msgid "E42: No Errors"
msgstr "E42: Geen Foute"
-#: ../globals.h:1067
-msgid "E776: No location list"
-msgstr ""
+#~ msgid "E776: No location list"
+#~ msgstr ""
-#: ../globals.h:1068
msgid "E43: Damaged match string"
msgstr "E43: Beskadige trefferstring"
-#: ../globals.h:1069
msgid "E44: Corrupted regexp program"
msgstr "E44: Korrupte patroonprogram"
-#: ../globals.h:1071
msgid "E45: 'readonly' option is set (add ! to override)"
msgstr "E45: 'readonly' opsie is aan (gebruik ! om te dwing)"
-#: ../globals.h:1073
-#, fuzzy, c-format
-msgid "E46: Cannot change read-only variable \"%s\""
-msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
-
-#: ../globals.h:1075
-#, fuzzy, c-format
-msgid "E794: Cannot set variable in the sandbox: \"%s\""
-msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\""
-
-#: ../globals.h:1076
msgid "E47: Error while reading errorfile"
msgstr "E47: Fout tydens lees van 'errorfile'"
-#: ../globals.h:1078
msgid "E48: Not allowed in sandbox"
msgstr "E48: Nie toegelaat in sandput nie"
-#: ../globals.h:1080
msgid "E523: Not allowed here"
msgstr "E523: Nie hier toegelaat nie"
-#: ../globals.h:1082
msgid "E359: Screen mode setting not supported"
msgstr "E359: Skermmodus instelling nie ondersteun nie"
-#: ../globals.h:1083
msgid "E49: Invalid scroll size"
msgstr "E49: Ongeldige rolgrootte"
-#: ../globals.h:1084
msgid "E91: 'shell' option is empty"
msgstr "E91: 'shell' (dop) opsie is leeg"
-#: ../globals.h:1085
msgid "E255: Couldn't read in sign data!"
msgstr "E255: Fout -- kon nie tekendata lees nie!"
-#: ../globals.h:1086
msgid "E72: Close error on swap file"
msgstr "E72: Sluitfout met ruiller"
-#: ../globals.h:1087
msgid "E73: tag stack empty"
msgstr "E73: etiketstapel leeg"
-#: ../globals.h:1088
msgid "E74: Command too complex"
msgstr "E74: Bevel te kompleks"
-#: ../globals.h:1089
msgid "E75: Name too long"
msgstr "E75: Naam te lank"
-#: ../globals.h:1090
msgid "E76: Too many ["
msgstr "E76: Te veel ["
-#: ../globals.h:1091
msgid "E77: Too many file names"
msgstr "E77: Te veel lername"
-#: ../globals.h:1092
msgid "E488: Trailing characters"
msgstr "E488: Oorbodige karakters"
-#: ../globals.h:1093
+#, fuzzy, c-format
+#~ msgid "E488: Trailing characters: %s"
+#~ msgstr "E488: Oorbodige karakters"
+
msgid "E78: Unknown mark"
msgstr "E78: Onbekende merker"
-#: ../globals.h:1094
msgid "E79: Cannot expand wildcards"
msgstr "E79: Kan nie plekhouers uitbrei nie"
-#: ../globals.h:1096
msgid "E591: 'winheight' cannot be smaller than 'winminheight'"
msgstr "E591: 'winheight' kan nie kleiner as 'winminheight' wees nie"
-#: ../globals.h:1098
msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'"
msgstr "E592: 'winwidth' kan nie kleiner as 'winminwidth' wees nie"
-#: ../globals.h:1099
msgid "E80: Error while writing"
msgstr "E80: Fout tydens skryfoperasie"
-#: ../globals.h:1100
-msgid "Zero count"
-msgstr "Nul telling"
+#, fuzzy
+#~ msgid "E939: Positive count required"
+#~ msgstr "E397: Lernaam benodig"
-#: ../globals.h:1101
msgid "E81: Using <SID> not in a script context"
msgstr "E81: Gebruik van '<SID>' buite skripkonteks"
-#: ../globals.h:1102
#, fuzzy, c-format
-msgid "E685: Internal error: %s"
-msgstr "E473: Interne fout"
+#~ msgid "E685: Internal error: %s"
+#~ msgstr "E473: Interne fout"
-#: ../globals.h:1104
-msgid "E363: pattern uses more memory than 'maxmempattern'"
-msgstr ""
+#~ msgid "E363: pattern uses more memory than 'maxmempattern'"
+#~ msgstr ""
-#: ../globals.h:1105
#, fuzzy
-msgid "E749: empty buffer"
-msgstr "E279: Nie 'n SNiFF+ buffer nie"
+#~ msgid "E749: empty buffer"
+#~ msgstr "E279: Nie 'n SNiFF+ buffer nie"
+
+#, c-format
+msgid "E86: Buffer %<PRId64> does not exist"
+msgstr "E86: Buffer %<PRId64> bestaan nie"
-#: ../globals.h:1108
#, fuzzy
-msgid "E682: Invalid search pattern or delimiter"
-msgstr "E383: Ongeldige soekstring: %s"
+#~ msgid "E682: Invalid search pattern or delimiter"
+#~ msgstr "E383: Ongeldige soekstring: %s"
-#: ../globals.h:1109
msgid "E139: File is loaded in another buffer"
msgstr "E139: Ler is gelaai in ander buffer"
-#: ../globals.h:1110
#, fuzzy, c-format
-msgid "E764: Option '%s' is not set"
-msgstr "E236: Font \"%s\" is nie 'n vaste-wydte font nie"
+#~ msgid "E764: Option '%s' is not set"
+#~ msgstr "E236: Font \"%s\" is nie 'n vaste-wydte font nie"
-#: ../globals.h:1111
#, fuzzy
-msgid "E850: Invalid register name"
-msgstr "E354: Ongeldige registernaam: '%s'"
+#~ msgid "E850: Invalid register name"
+#~ msgstr "E354: Ongeldige registernaam: '%s'"
+
+#, fuzzy, c-format
+#~ msgid "E919: Directory not found in '%s': \"%s\""
+#~ msgstr "E161: Inspeksiepunt kon nie gevind word nie: %s"
+
+msgid "E519: Option not supported"
+msgstr "E519: Opsie is nie ondersteun nie"
+
+#, fuzzy
+#~ msgid "E856: Filename too long"
+#~ msgstr "E75: Naam te lank"
+
+#~ msgid "E806: using Float as a String"
+#~ msgstr ""
-#: ../globals.h:1114
msgid "search hit TOP, continuing at BOTTOM"
msgstr "soektog het BO getref, gaan voort van ONDER af"
-#: ../globals.h:1115
msgid "search hit BOTTOM, continuing at TOP"
msgstr "soektog het ONDER getref, gaan voort van BO af"
-#: ../hardcopy.c:240
msgid "E550: Missing colon"
msgstr "E550: Ontbrekende dubbelpunt"
-#: ../hardcopy.c:252
msgid "E551: Illegal component"
msgstr "E551: Ongeldige komponent"
-#: ../hardcopy.c:259
msgid "E552: digit expected"
msgstr "E552: syfer verwag"
-#: ../hardcopy.c:473
#, c-format
msgid "Page %d"
msgstr "Bladsy %d"
-#: ../hardcopy.c:597
msgid "No text to be printed"
msgstr "Geen teks om te druk nie"
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Druk nou bladsy %d (%d%%)"
+#, fuzzy, c-format
+#~ msgid "Printing page %d (%zu%%)"
+#~ msgstr "Druk nou bladsy %d (%d%%)"
-#: ../hardcopy.c:680
#, c-format
msgid " Copy %d of %d"
msgstr " Kopie %d van %d"
-#: ../hardcopy.c:733
#, c-format
msgid "Printed: %s"
msgstr "Gedruk: %s"
-#: ../hardcopy.c:740
msgid "Printing aborted"
msgstr "Drukkery gestaak"
-#: ../hardcopy.c:1365
msgid "E455: Error writing to PostScript output file"
msgstr "E455: Kan nie na 'PostScript' afvoerler skryf nie"
-#: ../hardcopy.c:1747
#, c-format
msgid "E624: Can't open file \"%s\""
msgstr "E624: Kan nie ler \"%s\" oopmaak nie"
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
#, c-format
msgid "E457: Can't read PostScript resource file \"%s\""
msgstr "E457: Kan nie 'PostScript' hulpbron-ler \"%s\" lees nie"
-#: ../hardcopy.c:1772
#, c-format
msgid "E618: file \"%s\" is not a PostScript resource file"
msgstr "E618: Ler \"%s\" is nie 'n 'PostScript' hulpbron-ler nie"
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
#, c-format
msgid "E619: file \"%s\" is not a supported PostScript resource file"
msgstr ""
"E619: Ler \"%s\" is nie 'n ondersteunde 'PostScript' hulpbron-ler nie"
-#: ../hardcopy.c:1856
#, c-format
msgid "E621: \"%s\" resource file has wrong version"
msgstr "E621: \"%s\" die hulpbron ler het die verkeerde weergawe"
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
+#~ msgid "E673: Incompatible multi-byte encoding and character set."
+#~ msgstr ""
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
+#~ msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
+#~ msgstr ""
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
+#~ msgid "E675: No default font specified for multi-byte printing."
+#~ msgstr ""
-#: ../hardcopy.c:2426
msgid "E324: Can't open PostScript output file"
msgstr "E324: Kan nie 'PostScript' afvoerler oopmaak nie"
-#: ../hardcopy.c:2458
#, c-format
msgid "E456: Can't open file \"%s\""
msgstr "E456: Kan nie ler %s oopmaak nie"
-#: ../hardcopy.c:2583
msgid "E456: Can't find PostScript resource file \"prolog.ps\""
msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"prolog.ps\" lees nie"
-#: ../hardcopy.c:2593
#, fuzzy
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie"
+#~ msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
+#~ msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie"
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
#, c-format
msgid "E456: Can't find PostScript resource file \"%s.ps\""
msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie"
-#: ../hardcopy.c:2654
#, fuzzy, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie"
+#~ msgid "E620: Unable to convert to print encoding \"%s\""
+#~ msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie"
-#: ../hardcopy.c:2877
msgid "Sending to printer..."
msgstr "Besig om te stuur na drukker..."
-#: ../hardcopy.c:2881
msgid "E365: Failed to print PostScript file"
msgstr "E365: Kon nie 'PostScript' ler druk nie"
-#: ../hardcopy.c:2883
msgid "Print job sent."
msgstr "Druktaak gestuur."
-#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Voeg 'n nuwe databasis by"
-#: ../if_cscope.c:87
msgid "Query for a pattern"
msgstr "Soek vir 'n patroon"
-#: ../if_cscope.c:89
msgid "Show this message"
msgstr "Wys hierdie boodskap"
-#: ../if_cscope.c:91
msgid "Kill a connection"
msgstr "Sluit 'n verbinding"
-#: ../if_cscope.c:93
msgid "Reinit all connections"
msgstr "Herstel alle verbindings"
-#: ../if_cscope.c:95
msgid "Show connections"
msgstr "Wys verbindings"
-#: ../if_cscope.c:101
#, c-format
msgid "E560: Usage: cs[cope] %s"
msgstr "E560: Gebruik: cs[cope] %s"
-#: ../if_cscope.c:225
msgid "This cscope command does not support splitting the window.\n"
msgstr ""
"Hierdie 'cscope' bevel ondersteun nie die splitsing van die venster nie.\n"
-#: ../if_cscope.c:266
msgid "E562: Usage: cstag <ident>"
msgstr "E562: Gebruik: 'cstag <ident>'"
-#: ../if_cscope.c:313
msgid "E257: cstag: tag not found"
msgstr "E257: 'cstag': etiket nie gevind nie"
-#: ../if_cscope.c:461
#, c-format
msgid "E563: stat(%s) error: %d"
msgstr "E563: 'stat(%s)' fout: %d"
-#: ../if_cscope.c:551
#, c-format
msgid "E564: %s is not a directory or a valid cscope database"
msgstr "E564: %s is nie 'n gids of 'n geldige 'cscope' databasis nie"
-#: ../if_cscope.c:566
#, c-format
msgid "Added cscope database %s"
msgstr "'cscope' databasis %s bygevoeg"
-#: ../if_cscope.c:616
-#, c-format
-msgid "E262: error reading cscope connection %<PRId64>"
-msgstr "E262: 'cscope' verbinding %<PRId64> kon nie gelees word nie"
+#, fuzzy, c-format
+#~ msgid "E262: error reading cscope connection %<PRIu64>"
+#~ msgstr "E262: 'cscope' verbinding %<PRId64> kon nie gelees word nie"
-#: ../if_cscope.c:711
msgid "E561: unknown cscope search type"
msgstr "E561: onbekende 'cscope' soektipe"
-#: ../if_cscope.c:752 ../if_cscope.c:789
msgid "E566: Could not create cscope pipes"
msgstr "E566: Kon nie 'cscope' pype skep nie"
-#: ../if_cscope.c:767
msgid "E622: Could not fork for cscope"
msgstr "E622: Kon nie vurk vir 'cscope' nie"
-#: ../if_cscope.c:849
#, fuzzy
-msgid "cs_create_connection setpgid failed"
-msgstr "'cs_create_connection' uitvoering het misluk"
+#~ msgid "cs_create_connection setpgid failed"
+#~ msgstr "'cs_create_connection' uitvoering het misluk"
-#: ../if_cscope.c:853 ../if_cscope.c:889
msgid "cs_create_connection exec failed"
msgstr "'cs_create_connection' uitvoering het misluk"
-#: ../if_cscope.c:863 ../if_cscope.c:902
msgid "cs_create_connection: fdopen for to_fp failed"
msgstr "'cs_create_connection': 'fdopen' vir 'to_fp' het misluk"
-#: ../if_cscope.c:865 ../if_cscope.c:906
msgid "cs_create_connection: fdopen for fr_fp failed"
msgstr "'cs_create_connection': 'fdopen' vir 'fr_fp' het misluk"
-#: ../if_cscope.c:890
msgid "E623: Could not spawn cscope process"
msgstr "E623: Kon nie 'cscope' proses skep nie"
-#: ../if_cscope.c:932
msgid "E567: no cscope connections"
msgstr "E567: geen 'cscope' verbindings nie"
-#: ../if_cscope.c:1009
#, c-format
msgid "E469: invalid cscopequickfix flag %c for %c"
msgstr "E469: ongeldige 'cscopequickfix' vlag %c vir %c"
-#: ../if_cscope.c:1058
#, c-format
msgid "E259: no matches found for cscope query %s of %s"
msgstr "E259: geen treffers gevind vir 'cscope' versoek %s van %s nie"
-#: ../if_cscope.c:1142
msgid "cscope commands:\n"
msgstr "'cscope' bevele:\n"
-#: ../if_cscope.c:1150
#, fuzzy, c-format
-msgid "%-5s: %s%*s (Usage: %s)"
-msgstr "%-5s: %-30s: (Gebruik: %s)"
+#~ msgid "%-5s: %s%*s (Usage: %s)"
+#~ msgstr "%-5s: %-30s: (Gebruik: %s)"
-#: ../if_cscope.c:1155
-msgid ""
-"\n"
-" c: Find functions calling this function\n"
-" d: Find functions called by this function\n"
-" e: Find this egrep pattern\n"
-" f: Find this file\n"
-" g: Find this definition\n"
-" i: Find files #including this file\n"
-" s: Find this C symbol\n"
-" t: Find this text string\n"
-msgstr ""
+#~ msgid ""
+#~ "\n"
+#~ " a: Find assignments to this symbol\n"
+#~ " c: Find functions calling this function\n"
+#~ " d: Find functions called by this function\n"
+#~ " e: Find this egrep pattern\n"
+#~ " f: Find this file\n"
+#~ " g: Find this definition\n"
+#~ " i: Find files #including this file\n"
+#~ " s: Find this C symbol\n"
+#~ " t: Find this text string\n"
+#~ msgstr ""
-#: ../if_cscope.c:1226
msgid "E568: duplicate cscope database not added"
msgstr "E568: duplikaat 'cscope' databasis nie bygevoeg nie"
-#: ../if_cscope.c:1335
#, c-format
msgid "E261: cscope connection %s not found"
msgstr "E261: 'cscope' verbinding %s nie gevind nie"
-#: ../if_cscope.c:1364
#, c-format
msgid "cscope connection %s closed"
msgstr "'cscope' verbinding %s gesluit"
#. should not reach here
-#: ../if_cscope.c:1486
msgid "E570: fatal error in cs_manage_matches"
msgstr "E570: fatale fout in 'cs_manage_matches'"
-#: ../if_cscope.c:1693
#, c-format
msgid "Cscope tag: %s"
msgstr "Cscope etiket: %s"
-#: ../if_cscope.c:1711
+#. Column headers for match number, line number and filename.
msgid ""
"\n"
" # line"
@@ -3311,329 +3023,314 @@ msgstr ""
"\n"
" # rel"
-#: ../if_cscope.c:1713
msgid "filename / context / line\n"
msgstr "lernaam / konteks / rel\n"
-#: ../if_cscope.c:1809
#, c-format
msgid "E609: Cscope error: %s"
msgstr "E609: Cscope fout: %s"
-#: ../if_cscope.c:2053
msgid "All cscope databases reset"
msgstr "Alle 'cscope' databasisse herstel"
-#: ../if_cscope.c:2123
msgid "no cscope connections\n"
msgstr "geen 'cscope' verbindings nie\n"
-#: ../if_cscope.c:2126
msgid " # pid database name prepend path\n"
msgstr " # pid databasis naam gidsvoorvoegsel\n"
-#: ../main.c:144
-#, fuzzy
-msgid "Unknown option argument"
-msgstr "Onbekende opsie"
+#, c-format
+#~ msgid "E1502: Lua failed to grow stack to %i"
+#~ msgstr ""
-#: ../main.c:146
-msgid "Too many edit arguments"
-msgstr "Te veel redigeer-parameters"
+#~ msgid ""
+#~ "E5100: Cannot convert given lua table: table should either have a sequence "
+#~ "of positive integer keys or contain only string keys"
+#~ msgstr ""
+
+#~ msgid "E5101: Cannot convert given lua type"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "E5102: Lua failed to grow stack to %i"
+#~ msgstr ""
-#: ../main.c:148
+#, fuzzy, c-format
+#~ msgid "E5104: Error while creating lua chunk: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5105: Error while calling lua chunk: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5109: Error while creating lua chunk: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5110: Error while creating lua function: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5111: Error while calling lua function: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5112: Error while creating lua chunk: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5113: Error while calling lua chunk: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5106: Error while creating vim module: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#~ msgid "E970: Failed to initialize lua interpreter"
+#~ msgstr ""
+
+#. stack: vim, error
+#, fuzzy, c-format
+#~ msgid "E5117: Error while updating package paths: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5107: Error while creating lua chunk for luaeval(): %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5108: Error while calling lua chunk for luaeval(): %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, c-format
+#~ msgid "E5114: Error while converting print argument #%i: %.*s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E5115: Error while loading debug string: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "E5116: Error while calling debug string: %.*s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+msgid "cannot save undo information"
+msgstr "kan nie herwin-inligting stoor nie"
+
+#. Error messages
msgid "Argument missing after"
msgstr "Parameter ontbreek na"
-#: ../main.c:150
#, fuzzy
-msgid "Garbage after option argument"
-msgstr "Gemors na opsie"
+#~ msgid "Garbage after option argument"
+#~ msgstr "Gemors na opsie"
+
+#, fuzzy
+#~ msgid "Unknown option argument"
+#~ msgstr "Onbekende opsie"
+
+msgid "Too many edit arguments"
+msgstr "Te veel redigeer-parameters"
-#: ../main.c:152
msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"
msgstr "Te veel \"+command\", \"-c command\" of \"--cmd command\" parameters"
-#: ../main.c:154
-msgid "Invalid argument for"
-msgstr "Ongeldige parameter vir"
-
-#: ../main.c:294
-#, c-format
-msgid "%d files to edit\n"
-msgstr "%d lers om te bewerk\n"
+#, fuzzy, c-format
+#~ msgid "E5421: Failed to open stdin: %s"
+#~ msgstr "E286: Gefaal om invoermetode oop te maak"
-#: ../main.c:1342
msgid "Attempt to open script file again: \""
msgstr "Probeer weer om skripler oop te maak: \""
-#: ../main.c:1350
msgid "Cannot open for reading: \""
msgstr "Kan nie oopmaak om te lees nie: \""
-#: ../main.c:1393
msgid "Cannot open for script output: \""
msgstr "Kan nie oopmaak vir skrip-afvoer nie: \""
-#: ../main.c:1622
msgid "Vim: Warning: Output is not to a terminal\n"
msgstr "Vim: Waarskuwing: Afvoer gaan nie na 'n terminaal nie\n"
-#: ../main.c:1624
msgid "Vim: Warning: Input is not from a terminal\n"
msgstr "Vim: Waarskuwing: Invoer kom nie vanaf 'n terminaal nie\n"
#. just in case..
-#: ../main.c:1891
msgid "pre-vimrc command line"
msgstr "vr-'vimrc' bevelrel"
-#: ../main.c:1964
#, c-format
msgid "E282: Cannot read from \"%s\""
msgstr "E282: Kan nie lees uit \"%s\" nie"
-#: ../main.c:2149
+#, fuzzy
msgid ""
"\n"
-"More info with: \"vim -h\"\n"
+"More info with \""
msgstr ""
"\n"
"Meer inligting met: \"vim -h\"\n"
-#: ../main.c:2178
-msgid "[file ..] edit specified file(s)"
-msgstr "[ler ..] bewerk ler(s)"
-
-#: ../main.c:2179
-msgid "- read text from stdin"
-msgstr "- lees teks uit 'stdin'"
-
-#: ../main.c:2180
-msgid "-t tag edit file where tag is defined"
-msgstr "-t tag bewerk ler waar etiket gedefinieer is"
+#. kill us with CTRL-C here, if you like
+#, fuzzy
+#~ msgid "Usage:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "\n"
+#~ "gebruik:"
-#: ../main.c:2181
-msgid "-q [errorfile] edit file with first error"
-msgstr "-q [foutler] bewerk ler met eerste fout"
+#, fuzzy
+#~ msgid " nvim [options] [file ...] Edit file(s)\n"
+#~ msgstr "[ler ..] bewerk ler(s)"
-#: ../main.c:2187
-msgid ""
-"\n"
-"\n"
-"usage:"
-msgstr ""
-"\n"
-"\n"
-"gebruik:"
+#, fuzzy
+#~ msgid " nvim [options] - Read text from stdin\n"
+#~ msgstr "- lees teks uit 'stdin'"
-#: ../main.c:2189
-msgid " vim [arguments] "
-msgstr " vim [parameters] "
+#, fuzzy
+#~ msgid " nvim [options] -t <tag> Edit file where tag is defined\n"
+#~ msgstr "-t tag bewerk ler waar etiket gedefinieer is"
-#: ../main.c:2193
-msgid ""
-"\n"
-" or:"
-msgstr ""
-"\n"
-" of:"
+#, fuzzy
+#~ msgid " nvim [options] -q [errorfile] Edit file with first error\n"
+#~ msgstr "-q [foutler] bewerk ler met eerste fout"
-#: ../main.c:2196
+#, fuzzy
msgid ""
"\n"
-"\n"
-"Arguments:\n"
+"Options:\n"
msgstr ""
"\n"
-"\n"
-"Parameters:\n"
-
-#: ../main.c:2197
-msgid "--\t\t\tOnly file names after this"
-msgstr "--\t\t\tSlegs lername hierna"
-
-#: ../main.c:2199
-msgid "--literal\t\tDon't expand wildcards"
-msgstr "--literal\t\tMoet nie plekhouers uitbrei nie"
-
-#: ../main.c:2201
-msgid "-v\t\t\tVi mode (like \"vi\")"
-msgstr "-v\t\t\tVi modus (soos \"vi\")"
-
-#: ../main.c:2202
-msgid "-e\t\t\tEx mode (like \"ex\")"
-msgstr "-e\t\t\tEx modus (soos \"ex\")"
-
-#: ../main.c:2203
-msgid "-E\t\t\tImproved Ex mode"
-msgstr ""
-
-#: ../main.c:2204
-msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")"
-msgstr "-s\t\t\tStil (bondel) modus (slegs vir \"ex\")"
-
-#: ../main.c:2205
-msgid "-d\t\t\tDiff mode (like \"vimdiff\")"
-msgstr "-d\t\t\tDiff modus (soos \"vimdiff\")"
-
-#: ../main.c:2206
-msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
-msgstr "-y\t\t\tEasy modus (soos \"evim\", modusloos)"
-
-#: ../main.c:2207
-msgid "-R\t\t\tReadonly mode (like \"view\")"
-msgstr "-R\t\t\tLeesalleen modus (soos \"view\")"
-
-#: ../main.c:2208
-msgid "-Z\t\t\tRestricted mode (like \"rvim\")"
-msgstr "-Z\t\t\tBeperkte modus (soos \"rvim\")"
+"--- Opsies ---"
-#: ../main.c:2209
-msgid "-m\t\t\tModifications (writing files) not allowed"
-msgstr "-m\t\t\tVeranderings (skryf van lers) nie toegelaat nie"
+#, fuzzy
+#~ msgid " -- Only file names after this\n"
+#~ msgstr "--\t\t\tSlegs lername hierna"
-#: ../main.c:2210
-msgid "-M\t\t\tModifications in text not allowed"
-msgstr "-M\t\t\tVeranderings aan teks nie toegelaat nie"
+#, fuzzy
+#~ msgid " + Start at end of file\n"
+#~ msgstr " vir twee modusse "
-#: ../main.c:2211
-msgid "-b\t\t\tBinary mode"
-msgstr "-b\t\t\tBinre modus"
+#, fuzzy
+#~ msgid " --cmd <cmd> Execute <cmd> before any config\n"
+#~ msgstr "--cmd <bevel>\tVoer <bevel> uit voor enige .vimrc-ler gelaai word"
-#: ../main.c:2212
-msgid "-l\t\t\tLisp mode"
-msgstr "-l\t\t\tLisp modus"
+#, fuzzy
+#~ msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"
+#~ msgstr "-c <bevel>\t\tVoer <bevel> uit na eerste ler gelaai is"
-#: ../main.c:2213
-msgid "-C\t\t\tCompatible with Vi: 'compatible'"
-msgstr "-C\t\t\tVersoenbaar met Vi: 'compatible'"
+#, fuzzy
+#~ msgid " -b Binary mode\n"
+#~ msgstr " vir twee modusse "
-#: ../main.c:2214
-msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'"
-msgstr "-N\t\t\tNie ten volle Vi-versoenbaar nie: 'nocompatible'"
+#, fuzzy
+#~ msgid " -d Diff mode\n"
+#~ msgstr " vir twee modusse "
-#: ../main.c:2215
-msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
-msgstr ""
+#~ msgid " -e, -E Ex mode, Improved Ex mode\n"
+#~ msgstr ""
-#: ../main.c:2216
-msgid "-D\t\t\tDebugging mode"
-msgstr "-D\t\t\tOntfoutmodus"
+#, fuzzy
+#~ msgid " -es Silent (batch) mode\n"
+#~ msgstr " vir twee modusse "
-#: ../main.c:2217
-msgid "-n\t\t\tNo swap file, use memory only"
-msgstr "-n\t\t\tGeen ruiller, gebruik slegs geheue"
+#~ msgid " -h, --help Print this help message\n"
+#~ msgstr ""
-#: ../main.c:2218
-msgid "-r\t\t\tList swap files and exit"
-msgstr "-r\t\t\tLys ruillers en verlaat vim"
+#~ msgid " -i <shada> Use this shada file\n"
+#~ msgstr ""
-#: ../main.c:2219
-msgid "-r (with file name)\tRecover crashed session"
-msgstr "-r (met ler naam)\tHerwin ineengestorte sessie"
+#, fuzzy
+#~ msgid " -m Modifications (writing files) not allowed\n"
+#~ msgstr "-m\t\t\tVeranderings (skryf van lers) nie toegelaat nie"
-#: ../main.c:2220
-msgid "-L\t\t\tSame as -r"
-msgstr "-L\t\t\tSelfde as -r"
+#, fuzzy
+#~ msgid " -M Modifications in text not allowed\n"
+#~ msgstr "-M\t\t\tVeranderings aan teks nie toegelaat nie"
-#: ../main.c:2221
-msgid "-A\t\t\tstart in Arabic mode"
-msgstr "-A\t\t\tbegin in Arabiese modus"
+#, fuzzy
+#~ msgid " -n No swap file, use memory only\n"
+#~ msgstr "-n\t\t\tGeen ruiller, gebruik slegs geheue"
-#: ../main.c:2222
-msgid "-H\t\t\tStart in Hebrew mode"
-msgstr "-H\t\t\tBegin in Hebreeuse modus"
+#, fuzzy
+#~ msgid " -o[N] Open N windows (default: one per file)\n"
+#~ msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)"
-#: ../main.c:2223
-msgid "-F\t\t\tStart in Farsi mode"
-msgstr "-F\t\t\tBegin in Farsi modus"
+#, fuzzy
+msgid ""
+" -O[N] Open N vertical windows (default: one per file)\n"
+msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)"
-#: ../main.c:2224
-msgid "-T <terminal>\tSet terminal type to <terminal>"
-msgstr "-T <terminaal>\tStel terminaaltipe na <terminaal>"
+#, fuzzy
+#~ msgid " -p[N] Open N tab pages (default: one per file)\n"
+#~ msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)"
-#: ../main.c:2225
-msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
-msgstr "-u <vimrc>\t\tGebruik <vimrc> in plaas van enige ander .vimrc"
+#~ msgid " -r, -L List swap files\n"
+#~ msgstr ""
-#: ../main.c:2226
-msgid "--noplugin\t\tDon't load plugin scripts"
-msgstr "--noplugin\t\tMoet nie inpropskripte laai nie"
+#~ msgid " -r <file> Recover edit state for this file\n"
+#~ msgstr ""
-#: ../main.c:2227
#, fuzzy
-msgid "-p[N]\t\tOpen N tab pages (default: one for each file)"
-msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)"
-
-#: ../main.c:2228
-msgid "-o[N]\t\tOpen N windows (default: one for each file)"
-msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)"
+#~ msgid " -R Read-only mode\n"
+#~ msgstr " vir twee modusse "
-#: ../main.c:2229
-msgid "-O[N]\t\tLike -o but split vertically"
-msgstr "-O[N]\t\tSoos -o maar verdeel vertikaal"
+#, fuzzy
+#~ msgid " -S <session> Source <session> after loading the first file\n"
+#~ msgstr ""
+#~ "-S <sessie>\t\tVoer bevele in ler <sessie> uit na eerste ler gelaai is"
-#: ../main.c:2230
-msgid "+\t\t\tStart at end of file"
-msgstr "+\t\t\tBegin by einde van ler"
+#, fuzzy
+#~ msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n"
+#~ msgstr "-s <skripin>\t\tLees Normale-modus bevele van ler <skripin>"
-#: ../main.c:2231
-msgid "+<lnum>\t\tStart at line <lnum>"
-msgstr "+<lnum>\t\tBegin by rel <lnum>"
+#~ msgid " -u <config> Use this config file\n"
+#~ msgstr ""
-#: ../main.c:2232
-msgid "--cmd <command>\tExecute <command> before loading any vimrc file"
-msgstr "--cmd <bevel>\tVoer <bevel> uit voor enige .vimrc-ler gelaai word"
+#, fuzzy
+#~ msgid " -v, --version Print version information\n"
+#~ msgstr "--version\t\tSkryf weergawe-inligting en sluit"
-#: ../main.c:2233
-msgid "-c <command>\t\tExecute <command> after loading the first file"
-msgstr "-c <bevel>\t\tVoer <bevel> uit na eerste ler gelaai is"
+#~ msgid " -V[N][file] Verbose [level][file]\n"
+#~ msgstr ""
-#: ../main.c:2235
-msgid "-S <session>\t\tSource file <session> after loading the first file"
-msgstr ""
-"-S <sessie>\t\tVoer bevele in ler <sessie> uit na eerste ler gelaai is"
+#, fuzzy
+#~ msgid " -Z Restricted mode\n"
+#~ msgstr " vir twee modusse "
-#: ../main.c:2236
-msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>"
-msgstr "-s <skripin>\t\tLees Normale-modus bevele van ler <skripin>"
+#~ msgid " --api-info Write msgpack-encoded API metadata to stdout\n"
+#~ msgstr ""
-#: ../main.c:2237
-msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>"
-msgstr "-w <skripuit>\tLas alle getikte bevele aan by ler <skripuit>"
+#~ msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n"
+#~ msgstr ""
-#: ../main.c:2238
-msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>"
-msgstr "-W <skripuit>\tSkryf alle getikte bevele na ler <skripuit>"
+#~ msgid " --headless Don't start a user interface\n"
+#~ msgstr ""
-#: ../main.c:2240
-msgid "--startuptime <file>\tWrite startup timing messages to <file>"
-msgstr ""
+#, fuzzy
+#~ msgid " --literal Don't expand wildcards\n"
+#~ msgstr "--literal\t\tMoet nie plekhouers uitbrei nie"
-#: ../main.c:2242
-msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
-msgstr "-i <viminfo>\t\tGebruik <viminfo> in plaas van .viminfo"
+#, fuzzy
+#~ msgid " --noplugin Don't load plugins\n"
+#~ msgstr "--noplugin\t\tMoet nie inpropskripte laai nie"
-#: ../main.c:2243
-msgid "-h or --help\tPrint Help (this message) and exit"
-msgstr "-h of --help\tSkryf Hulp (hierdie boodskap) en sluit"
+#~ msgid " --startuptime <file> Write startup timing messages to <file>\n"
+#~ msgstr ""
-#: ../main.c:2244
-msgid "--version\t\tPrint version information and exit"
-msgstr "--version\t\tSkryf weergawe-inligting en sluit"
+#~ msgid ""
+#~ "\n"
+#~ "See \":help startup-options\" for all options.\n"
+#~ msgstr ""
-#: ../mark.c:676
msgid "No marks set"
msgstr "Geen merkers gestel nie"
-#: ../mark.c:678
#, c-format
msgid "E283: No marks matching \"%s\""
msgstr "E283: Geen merkers pas op \"%s\" nie"
#. Highlight title
-#: ../mark.c:687
msgid ""
"\n"
"mark line col file/text"
@@ -3642,7 +3339,6 @@ msgstr ""
"merk rel kol ler/teks"
#. Highlight title
-#: ../mark.c:789
msgid ""
"\n"
" jump line col file/text"
@@ -3651,7 +3347,6 @@ msgstr ""
" spring rel kol ler/teks"
#. Highlight title
-#: ../mark.c:831
msgid ""
"\n"
"change line col text"
@@ -3659,110 +3354,63 @@ msgstr ""
"\n"
"verander rel kol teks"
-#: ../mark.c:1238
-msgid ""
-"\n"
-"# File marks:\n"
-msgstr ""
-"\n"
-"# Lermerkers:\n"
-
-#. Write the jumplist with -'
-#: ../mark.c:1271
-msgid ""
-"\n"
-"# Jumplist (newest first):\n"
-msgstr ""
-"\n"
-"# Springlys (nuutste eerste):\n"
-
-#: ../mark.c:1352
-msgid ""
-"\n"
-"# History of marks within files (newest to oldest):\n"
-msgstr ""
-"\n"
-"# Geskiedenis van merkers in lers (nuutste tot oudste):\n"
-
-#: ../mark.c:1431
-msgid "Missing '>'"
-msgstr "Ontbrekende '>'"
-
-#: ../memfile.c:426
msgid "E293: block was not locked"
msgstr "E293: blok was nie gesluit nie"
-#: ../memfile.c:799
msgid "E294: Seek error in swap file read"
msgstr "E294: Soekfout in lees van ruiller"
-#: ../memfile.c:803
msgid "E295: Read error in swap file"
msgstr "E295: Leesfout in ruiller"
-#: ../memfile.c:849
msgid "E296: Seek error in swap file write"
msgstr "E296: Soekfout in skryf van ruiller"
-#: ../memfile.c:865
msgid "E297: Write error in swap file"
msgstr "E297: Skryffout in ruiller"
-#: ../memfile.c:1036
msgid "E300: Swap file already exists (symlink attack?)"
msgstr "E300: Ruiller bestaan alreeds! ('symlink' probleem?)"
-#: ../memline.c:318
msgid "E298: Didn't get block nr 0?"
msgstr "E298: Het nie blok no 0 gekry nie?"
-#: ../memline.c:361
msgid "E298: Didn't get block nr 1?"
msgstr "E298: Het nie blok no 1 gekry nie?"
-#: ../memline.c:377
msgid "E298: Didn't get block nr 2?"
msgstr "E298: Het nie blok no 2 gekry nie?"
#. could not (re)open the swap file, what can we do????
-#: ../memline.c:465
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: Hiert, die ruiller is weg!!!"
-#: ../memline.c:477
msgid "E302: Could not rename swap file"
msgstr "E302: Kon nie ruiller vernoem nie"
-#: ../memline.c:554
#, c-format
msgid "E303: Unable to open swap file for \"%s\", recovery impossible"
msgstr "E303: Kon nie ruiller oopmaak vir \"%s\" nie, herwinning onmoontlik"
-#: ../memline.c:666
#, fuzzy
-msgid "E304: ml_upd_block0(): Didn't get block 0??"
-msgstr "E304: 'ml_timestamp': Het nie blok 0 gekry nie??"
+#~ msgid "E304: ml_upd_block0(): Didn't get block 0??"
+#~ msgstr "E304: 'ml_timestamp': Het nie blok 0 gekry nie??"
#. no swap files found
-#: ../memline.c:830
#, c-format
msgid "E305: No swap file found for %s"
msgstr "E305: Geen ruiller gevind vir %s nie"
-#: ../memline.c:839
msgid "Enter number of swap file to use (0 to quit): "
msgstr "Tik die nommer van die ruiller om te gebruik (0 om te stop)"
-#: ../memline.c:879
#, c-format
msgid "E306: Cannot open %s"
msgstr "E306: Kan %s nie oopmaak nie"
-#: ../memline.c:897
msgid "Unable to read block 0 from "
msgstr "Kan nie blok 0 lees vanaf "
-#: ../memline.c:900
msgid ""
"\n"
"Maybe no changes were made or Vim did not update the swap file."
@@ -3770,28 +3418,22 @@ msgstr ""
"\n"
"Vim het die ruiller nie opgedateer nie. Dalk was niks verander nie."
-#: ../memline.c:909
msgid " cannot be used with this version of Vim.\n"
msgstr " kan nie gebruik word met hierdie weergawe van Vim nie.\n"
-#: ../memline.c:911
msgid "Use Vim version 3.0.\n"
msgstr "Gebruik Vim weergawe 3.0.\n"
-#: ../memline.c:916
#, c-format
msgid "E307: %s does not look like a Vim swap file"
msgstr "E307: %s lyk nie soos 'n Vim ruiller nie"
-#: ../memline.c:922
msgid " cannot be used on this computer.\n"
msgstr " kan nie gebruik word op hierdie rekenaar nie.\n"
-#: ../memline.c:924
msgid "The file was created on "
msgstr "Die ler is geskep op "
-#: ../memline.c:928
msgid ""
",\n"
"or the file has been damaged."
@@ -3799,85 +3441,66 @@ msgstr ""
",\n"
"of die ler is beskadig."
-#: ../memline.c:945
-msgid " has been damaged (page size is smaller than minimum value).\n"
-msgstr ""
+#~ msgid " has been damaged (page size is smaller than minimum value).\n"
+#~ msgstr ""
-#: ../memline.c:974
#, c-format
msgid "Using swap file \"%s\""
msgstr "Gebruik ruiller \"%s\""
-#: ../memline.c:980
#, c-format
msgid "Original file \"%s\""
msgstr "Oorspronklike ler \"%s\""
-#: ../memline.c:995
msgid "E308: Warning: Original file may have been changed"
msgstr "E308: Waarskuwing: Oorspronklike ler is dalk gewysig"
-#: ../memline.c:1061
#, c-format
msgid "E309: Unable to read block 1 from %s"
msgstr "E309: Kan nie block 1 lees van %s"
-#: ../memline.c:1065
msgid "???MANY LINES MISSING"
msgstr "???BAIE RELS WEG"
-#: ../memline.c:1076
msgid "???LINE COUNT WRONG"
msgstr "???RELTELLING FOUTIEF"
-#: ../memline.c:1082
msgid "???EMPTY BLOCK"
msgstr "???LE BLOK"
-#: ../memline.c:1103
msgid "???LINES MISSING"
msgstr "???RELS WEG"
-#: ../memline.c:1128
#, c-format
msgid "E310: Block 1 ID wrong (%s not a .swp file?)"
msgstr "E310: Blok 1 se ID is foutief (%s nie 'n .swp ler nie?)"
-#: ../memline.c:1133
msgid "???BLOCK MISSING"
msgstr "???BLOK WEG"
-#: ../memline.c:1147
msgid "??? from here until ???END lines may be messed up"
msgstr "??? van hier tot ???END mag rels deurmekaar wees"
-#: ../memline.c:1164
msgid "??? from here until ???END lines may have been inserted/deleted"
msgstr "??? van hier tot ???END mag daar rels ingevoeg/geskrap wees"
-#: ../memline.c:1181
msgid "???END"
msgstr "???END"
-#: ../memline.c:1238
msgid "E311: Recovery Interrupted"
msgstr "E311: Herwinning onderbreek"
-#: ../memline.c:1243
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
msgstr ""
"E312: Foute raakgesien gedurende herwinning; soek vir rels wat begin met ???"
-#: ../memline.c:1245
msgid "See \":help E312\" for more information."
msgstr "Sien \":help E312\" vir meer inligting."
-#: ../memline.c:1249
msgid "Recovery completed. You should check if everything is OK."
msgstr "Herwinning is klaar. Kyk of alles reg is."
-#: ../memline.c:1251
msgid ""
"\n"
"(You might want to write out this file under another name\n"
@@ -3885,16 +3508,13 @@ msgstr ""
"\n"
"(Jy wil dalk die ler stoor onder 'n ander naam\n"
-#: ../memline.c:1252
#, fuzzy
-msgid "and run diff with the original file to check for changes)"
-msgstr "en dit \"diff\" teen die oorspronklike ler om wysigings te soek)\n"
+#~ msgid "and run diff with the original file to check for changes)"
+#~ msgstr "en dit \"diff\" teen die oorspronklike ler om wysigings te soek)\n"
-#: ../memline.c:1254
-msgid "Recovery completed. Buffer contents equals file contents."
-msgstr ""
+#~ msgid "Recovery completed. Buffer contents equals file contents."
+#~ msgstr ""
-#: ../memline.c:1255
#, fuzzy
msgid ""
"\n"
@@ -3905,51 +3525,42 @@ msgstr ""
"\n"
#. use msg() to start the scrolling properly
-#: ../memline.c:1327
msgid "Swap files found:"
msgstr "Ruillers gevind:"
-#: ../memline.c:1446
msgid " In current directory:\n"
msgstr " In huidige gids:\n"
-#: ../memline.c:1448
msgid " Using specified name:\n"
msgstr " Wat gespesifiseerde naam gebruik:\n"
-#: ../memline.c:1450
msgid " In directory "
msgstr " In gids "
-#: ../memline.c:1465
msgid " -- none --\n"
msgstr " -- geen --\n"
-#: ../memline.c:1527
msgid " owned by: "
msgstr " eienaar: "
-#: ../memline.c:1529
msgid " dated: "
msgstr " gedateer: "
-#: ../memline.c:1532 ../memline.c:3231
msgid " dated: "
msgstr " gedateer: "
-#: ../memline.c:1548
msgid " [from Vim version 3.0]"
msgstr " [van Vim weergawe 3.0]"
-#: ../memline.c:1550
msgid " [does not look like a Vim swap file]"
msgstr " [lyk nie soos 'n Vim ruiller nie]"
-#: ../memline.c:1552
+#~ msgid " [garbled strings (not nul terminated)]"
+#~ msgstr ""
+
msgid " file name: "
msgstr " lernaam: "
-#: ../memline.c:1558
msgid ""
"\n"
" modified: "
@@ -3957,15 +3568,12 @@ msgstr ""
"\n"
" gewysig: "
-#: ../memline.c:1559
msgid "YES"
msgstr "JA"
-#: ../memline.c:1559
msgid "no"
msgstr "nee"
-#: ../memline.c:1562
msgid ""
"\n"
" user name: "
@@ -3973,11 +3581,9 @@ msgstr ""
"\n"
" gebruikersnaam: "
-#: ../memline.c:1568
msgid " host name: "
msgstr " gasheernaam: "
-#: ../memline.c:1570
msgid ""
"\n"
" host name: "
@@ -3985,7 +3591,6 @@ msgstr ""
"\n"
" gasheernaam: "
-#: ../memline.c:1575
msgid ""
"\n"
" process ID: "
@@ -3993,11 +3598,9 @@ msgstr ""
"\n"
" proses ID: "
-#: ../memline.c:1579
msgid " (still running)"
msgstr " (nog steeds aan die uitvoer)"
-#: ../memline.c:1586
msgid ""
"\n"
" [not usable on this computer]"
@@ -4005,97 +3608,75 @@ msgstr ""
"\n"
" [nie bruikbaar op hierdie rekenaar nie]"
-#: ../memline.c:1590
msgid " [cannot be read]"
msgstr " [kan nie gelees word nie]"
-#: ../memline.c:1593
msgid " [cannot be opened]"
msgstr " [kan nie oopgemaak word nie]"
-#: ../memline.c:1698
msgid "E313: Cannot preserve, there is no swap file"
msgstr "E313: Kan nie bewaar nie, daar is geen ruiller nie"
-#: ../memline.c:1747
msgid "File preserved"
msgstr "Ler bewaar"
-#: ../memline.c:1749
msgid "E314: Preserve failed"
msgstr "E314: Kon nie bewaar nie"
-#: ../memline.c:1819
#, c-format
msgid "E315: ml_get: invalid lnum: %<PRId64>"
msgstr "E315: 'ml_get': ongeldige 'lnum': %<PRId64>"
-#: ../memline.c:1851
#, c-format
msgid "E316: ml_get: cannot find line %<PRId64>"
msgstr "E316: 'ml_get': kan rel %<PRId64> nie vind nie"
-#: ../memline.c:2236
msgid "E317: pointer block id wrong 3"
msgstr "E317: wyser blok id verkeerd 3"
-#: ../memline.c:2311
msgid "stack_idx should be 0"
msgstr "'stack_idx' moet 0 wees"
-#: ../memline.c:2369
msgid "E318: Updated too many blocks?"
msgstr "E318: Te veel blokke opgedateer?"
-#: ../memline.c:2511
msgid "E317: pointer block id wrong 4"
msgstr "E317: wyser blok id verkeerd 4"
-#: ../memline.c:2536
msgid "deleted block 1?"
msgstr "verwyder blok 1?"
-#: ../memline.c:2707
#, c-format
msgid "E320: Cannot find line %<PRId64>"
msgstr "E320: Kan nie rel %<PRId64> vind nie"
-#: ../memline.c:2916
msgid "E317: pointer block id wrong"
msgstr "E317: wyser blok id verkeerd"
-#: ../memline.c:2930
msgid "pe_line_count is zero"
msgstr "'pe_line_count' is nul"
-#: ../memline.c:2955
#, c-format
msgid "E322: line number out of range: %<PRId64> past the end"
msgstr "E322: relnommer buite perke: %<PRId64> verby die einde"
-#: ../memline.c:2959
#, c-format
msgid "E323: line count wrong in block %<PRId64>"
msgstr "E323: reltelling mag verkeerd wees in blok %<PRId64>"
-#: ../memline.c:2999
msgid "Stack size increases"
msgstr "Stapel grootte verhoog"
-#: ../memline.c:3038
msgid "E317: pointer block id wrong 2"
msgstr "E317: wyser blok id verkeerd 2"
-#: ../memline.c:3070
#, c-format
-msgid "E773: Symlink loop for \"%s\""
-msgstr ""
+#~ msgid "E773: Symlink loop for \"%s\""
+#~ msgstr ""
-#: ../memline.c:3221
msgid "E325: ATTENTION"
msgstr "E325: LET OP"
-#: ../memline.c:3222
msgid ""
"\n"
"Found a swap file by the name \""
@@ -4103,44 +3684,35 @@ msgstr ""
"\n"
"Het 'n ruiller gevind met die naam \""
-#: ../memline.c:3226
msgid "While opening file \""
msgstr "Tydens oopmaak van ler \""
-#: ../memline.c:3239
msgid " NEWER than swap file!\n"
msgstr " NUWER as die ruiller!\n"
-#: ../memline.c:3244
+#. Some of these messages are long to allow translation to
+#. * other languages.
#, fuzzy
msgid ""
"\n"
"(1) Another program may be editing the same file. If this is the case,\n"
" be careful not to end up with two different instances of the same\n"
-" file when making changes."
+" file when making changes. Quit, or continue with caution.\n"
msgstr ""
"\n"
"(1) 'n Ander program mag besig wees met hierdie ler.\n"
" Indien wel, pas op om nie met twee verskillende weergawes\n"
" van dieselfde ler te sit wanneer veranderinge gemaak word nie.\n"
-#: ../memline.c:3245
-#, fuzzy
-msgid " Quit, or continue with caution.\n"
-msgstr " Stop, of gaan versigtig voort.\n"
-
-#: ../memline.c:3246
#, fuzzy
-msgid "(2) An edit session for this file crashed.\n"
-msgstr ""
-"\n"
-"(2) 'n Bewerkingsessie van hierdie ler het ineengestort.\n"
+#~ msgid "(2) An edit session for this file crashed.\n"
+#~ msgstr ""
+#~ "\n"
+#~ "(2) 'n Bewerkingsessie van hierdie ler het ineengestort.\n"
-#: ../memline.c:3247
msgid " If this is the case, use \":recover\" or \"vim -r "
msgstr " Indien wel, gebruik \":recover\" of \"vim -r"
-#: ../memline.c:3249
msgid ""
"\"\n"
" to recover the changes (see \":help recovery\").\n"
@@ -4148,11 +3720,9 @@ msgstr ""
"\"\n"
" om die veranderinge te herwin (sien \":help recovery\").\n"
-#: ../memline.c:3250
msgid " If you did this already, delete the swap file \""
msgstr " Indien jy dit alreeds gedoen het, verwyder die ruiller \""
-#: ../memline.c:3252
msgid ""
"\"\n"
" to avoid this message.\n"
@@ -4160,23 +3730,15 @@ msgstr ""
"\"\n"
" om hierdie boodskap te vermy.\n"
-#: ../memline.c:3450 ../memline.c:3452
msgid "Swap file \""
msgstr "Ruiller \""
-#: ../memline.c:3451 ../memline.c:3455
msgid "\" already exists!"
msgstr "\" bestaan alreeds!"
-#: ../memline.c:3457
msgid "VIM - ATTENTION"
msgstr "VIM - LET OP"
-#: ../memline.c:3459
-msgid "Swap file already exists!"
-msgstr "Ruiller bestaan alreeds!"
-
-#: ../memline.c:3464
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
@@ -4190,7 +3752,6 @@ msgstr ""
"&Verlaat\n"
"&Stop"
-#: ../memline.c:3467
#, fuzzy
msgid ""
"&Open Read-Only\n"
@@ -4214,48 +3775,47 @@ msgstr ""
#.
#. ".s?a"
#. ".saa": tried enough, give up
-#: ../memline.c:3528
msgid "E326: Too many swap files found"
msgstr "E326: Te veel ruillers gevind"
-#: ../memory.c:227
+#, fuzzy, c-format
+msgid ""
+"E303: Unable to create directory \"%s\" for swap file, recovery impossible: "
+"%s"
+msgstr "E303: Kon nie ruiller oopmaak vir \"%s\" nie, herwinning onmoontlik"
+
+#~ msgid "Vim: Data too large to fit into virtual memory space\n"
+#~ msgstr ""
+
#, c-format
msgid "E342: Out of memory! (allocating %<PRIu64> bytes)"
msgstr "E342: Geheue is op! (ken %<PRIu64> grepe toe)"
-#: ../menu.c:62
msgid "E327: Part of menu-item path is not sub-menu"
msgstr "E327: Deel van kieslys-item pad is nie 'n sub-kieslys nie"
-#: ../menu.c:63
msgid "E328: Menu only exists in another mode"
msgstr "E328: Kieslys bestaan slegs in 'n ander modus"
-#: ../menu.c:64
#, fuzzy, c-format
-msgid "E329: No menu \"%s\""
-msgstr "E329: Geen kieslys met daardie naam nie"
+#~ msgid "E329: No menu \"%s\""
+#~ msgstr "E329: Geen kieslys met daardie naam nie"
#. Only a mnemonic or accelerator is not valid.
-#: ../menu.c:329
-msgid "E792: Empty menu name"
-msgstr ""
+#~ msgid "E792: Empty menu name"
+#~ msgstr ""
-#: ../menu.c:340
msgid "E330: Menu path must not lead to a sub-menu"
msgstr "E330: Kieslyspad moenie lei na 'n sub-kieslys nie"
-#: ../menu.c:365
msgid "E331: Must not add menu items directly to menu bar"
msgstr "E331: Moenie kieslysitems direk by kieslysstaaf voeg nie"
-#: ../menu.c:370
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: Verdeler kan nie deel wees van kieslyspad nie"
#. Now we have found the matching menu, and we list the mappings
#. Highlight title
-#: ../menu.c:762
msgid ""
"\n"
"--- Menus ---"
@@ -4263,70 +3823,49 @@ msgstr ""
"\n"
"--- Kieslyste ---"
-#: ../menu.c:1313
msgid "E333: Menu path must lead to a menu item"
msgstr "E333: Kieslyspad moet lei na 'n kieslysitem"
-#: ../menu.c:1330
#, c-format
msgid "E334: Menu not found: %s"
msgstr "E334: Kieslys nie gevind nie: %s"
-#: ../menu.c:1396
#, c-format
msgid "E335: Menu not defined for %s mode"
msgstr "E335: Kieslys nie gedefinieer vir %s modus nie"
-#: ../menu.c:1426
-msgid "E336: Menu path must lead to a sub-menu"
-msgstr "E336: Kieslyspad moet lei na 'n sub-kieslys"
-
-#: ../menu.c:1447
-msgid "E337: Menu not found - check menu names"
-msgstr "E337: Kieslys nie gevind nie - maak seker oor die kieslys name"
-
-#: ../message.c:423
#, c-format
msgid "Error detected while processing %s:"
msgstr "Fout ontdek tydens verwerking van %s: "
-#: ../message.c:445
#, c-format
msgid "line %4ld:"
msgstr "rel %4ld:"
-#: ../message.c:617
#, c-format
msgid "E354: Invalid register name: '%s'"
msgstr "E354: Ongeldige registernaam: '%s'"
-#: ../message.c:986
msgid "Interrupt: "
msgstr "Onderbreek: "
-#: ../message.c:988
#, fuzzy
-msgid "Press ENTER or type command to continue"
-msgstr "Druk ENTER of tik 'n bevel om voort te gaan"
+#~ msgid "Press ENTER or type command to continue"
+#~ msgstr "Druk ENTER of tik 'n bevel om voort te gaan"
-#: ../message.c:1843
#, fuzzy, c-format
-msgid "%s line %<PRId64>"
-msgstr "%s, rel %<PRId64>"
+#~ msgid "%s line %<PRId64>"
+#~ msgstr "%s, rel %<PRId64>"
-#: ../message.c:2392
msgid "-- More --"
msgstr "-- Meer --"
-#: ../message.c:2398
-msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr ""
+#~ msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
+#~ msgstr ""
-#: ../message.c:3021 ../message.c:3031
msgid "Question"
msgstr "Vraag"
-#: ../message.c:3023
msgid ""
"&Yes\n"
"&No"
@@ -4334,7 +3873,6 @@ msgstr ""
"&Ja\n"
"&Nee"
-#: ../message.c:3033
msgid ""
"&Yes\n"
"&No\n"
@@ -4344,7 +3882,6 @@ msgstr ""
"&Nee\n"
"&Kanselleer"
-#: ../message.c:3045
msgid ""
"&Yes\n"
"&No\n"
@@ -4358,180 +3895,131 @@ msgstr ""
"&Gooi alles weg\n"
"&Kanselleer"
-#: ../message.c:3058
-#, fuzzy
-msgid "E766: Insufficient arguments for printf()"
-msgstr "E116: Ongeldige parameters vir funksie %s"
-
-#: ../message.c:3119
-msgid "E807: Expected Float argument for printf()"
-msgstr ""
-
-#: ../message.c:3873
-#, fuzzy
-msgid "E767: Too many arguments to printf()"
-msgstr "E118: Te veel parameters vir funksie: %s"
-
-#: ../misc1.c:2256
msgid "W10: Warning: Changing a readonly file"
msgstr "W10: Waarskuwing: Jy wysig aan 'n leesalleen ler"
-#: ../misc1.c:2537
-msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr ""
+#~ msgid "Type number and <Enter> or click with mouse (empty cancels): "
+#~ msgstr ""
-#: ../misc1.c:2539
-msgid "Type number and <Enter> (empty cancels): "
-msgstr ""
+#~ msgid "Type number and <Enter> (empty cancels): "
+#~ msgstr ""
-#: ../misc1.c:2585
msgid "1 more line"
msgstr "1 rel meer"
-#: ../misc1.c:2588
msgid "1 line less"
msgstr "1 rel minder"
-#: ../misc1.c:2593
#, c-format
msgid "%<PRId64> more lines"
msgstr "%<PRId64> meer rels"
-#: ../misc1.c:2596
#, c-format
msgid "%<PRId64> fewer lines"
msgstr "%<PRId64> minder rels"
-#: ../misc1.c:2599
msgid " (Interrupted)"
msgstr " (Onderbreek)"
-#: ../misc1.c:2635
-msgid "Beep!"
-msgstr ""
+#~ msgid "Beep!"
+#~ msgstr ""
-#: ../misc2.c:738
#, c-format
msgid "Calling shell to execute: \"%s\""
msgstr "Roep dop om uit te voer: \"%s\""
-#: ../normal.c:183
+#.
+#. * nv_*(): functions called to handle Normal and Visual mode commands.
+#. * n_*(): functions called to handle Normal mode commands.
+#. * v_*(): functions called to handle Visual mode commands.
+#.
msgid "E349: No identifier under cursor"
msgstr "E349: Geen identifiseerder onder loper nie"
-#: ../normal.c:1866
#, fuzzy
-msgid "E774: 'operatorfunc' is empty"
-msgstr "E221: 'commentstring' opsie is leeg"
+#~ msgid "E774: 'operatorfunc' is empty"
+#~ msgstr "E221: 'commentstring' opsie is leeg"
-#: ../normal.c:2637
msgid "Warning: terminal cannot highlight"
msgstr "Waarskuwing: terminaal kan nie teks uitlig nie"
-#: ../normal.c:2807
msgid "E348: No string under cursor"
msgstr "E348: Geen string onder loper nie"
-#: ../normal.c:3937
msgid "E352: Cannot erase folds with current 'foldmethod'"
msgstr "E352: Kan nie voue verwyder met huidige 'foldmethod' nie"
-#: ../normal.c:5897
msgid "E664: changelist is empty"
msgstr "E664: 'changelist' is leeg"
-#: ../normal.c:5899
msgid "E662: At start of changelist"
msgstr "E662: By die begin van die veranderingslys"
-#: ../normal.c:5901
msgid "E663: At end of changelist"
msgstr "E663: By die einde van die veranderingslys"
-#: ../normal.c:7053
msgid "Type :quit<Enter> to exit Nvim"
msgstr "Tik :quit<Enter> om Vim te verlaat"
# Het te doen met < en >
-#: ../ops.c:248
#, c-format
msgid "1 line %sed 1 time"
msgstr "1 rel 1 keer ge-%s"
-#: ../ops.c:250
#, c-format
msgid "1 line %sed %d times"
msgstr "1 rel ge-%s %d keer"
-#: ../ops.c:253
#, c-format
msgid "%<PRId64> lines %sed 1 time"
msgstr "%<PRId64> rels 1 keer ge-%s"
-#: ../ops.c:256
#, c-format
msgid "%<PRId64> lines %sed %d times"
msgstr "%<PRId64> rels ge-%s %d keer"
-#: ../ops.c:592
#, c-format
msgid "%<PRId64> lines to indent... "
msgstr "%<PRId64> rels om in te keep..."
-#: ../ops.c:634
msgid "1 line indented "
msgstr "1 rel ingekeep "
-#: ../ops.c:636
#, c-format
msgid "%<PRId64> lines indented "
msgstr "%<PRId64> rels ingekeep "
-#: ../ops.c:938
#, fuzzy
-msgid "E748: No previously used register"
-msgstr "E186: Geen vorige gids nie"
-
-#. must display the prompt
-#: ../ops.c:1433
-msgid "cannot yank; delete anyway"
-msgstr "kan nie pluk nie: verwyder in elk geval"
+#~ msgid "E748: No previously used register"
+#~ msgstr "E186: Geen vorige gids nie"
-#: ../ops.c:1929
msgid "1 line changed"
msgstr "1 rel verander"
-#: ../ops.c:1931
#, c-format
msgid "%<PRId64> lines changed"
msgstr "%<PRId64> rels verander"
-#: ../ops.c:2521
#, fuzzy
-msgid "block of 1 line yanked"
-msgstr "1 rel gepluk"
+#~ msgid "block of 1 line yanked"
+#~ msgstr "1 rel gepluk"
-#: ../ops.c:2523
msgid "1 line yanked"
msgstr "1 rel gepluk"
-#: ../ops.c:2525
#, fuzzy, c-format
-msgid "block of %<PRId64> lines yanked"
-msgstr "%<PRId64> rels gepluk"
+#~ msgid "block of %<PRId64> lines yanked"
+#~ msgstr "%<PRId64> rels gepluk"
-#: ../ops.c:2528
#, c-format
msgid "%<PRId64> lines yanked"
msgstr "%<PRId64> rels gepluk"
-#: ../ops.c:2710
#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: Niks in register %s nie"
#. Highlight title
-#: ../ops.c:3185
msgid ""
"\n"
"--- Registers ---"
@@ -4539,29 +4027,15 @@ msgstr ""
"\n"
"--- Registers ---"
-#: ../ops.c:4455
-msgid "Illegal register name"
-msgstr "Ongeldige registernaam"
-
-#: ../ops.c:4533
-msgid ""
-"\n"
-"# Registers:\n"
-msgstr ""
-"\n"
-"# Registers:\n"
-
-#: ../ops.c:4575
-#, c-format
-msgid "E574: Unknown register type %d"
-msgstr "E574: Onbekende registertipe %d"
+#~ msgid ""
+#~ "E883: search pattern and expression register may not contain two or more "
+#~ "lines"
+#~ msgstr ""
-#: ../ops.c:5089
#, c-format
msgid "%<PRId64> Cols; "
msgstr "%<PRId64> Kolomme; "
-#: ../ops.c:5097
#, c-format
msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
@@ -4570,7 +4044,6 @@ msgstr ""
"%s%<PRId64> van %<PRId64> rels gekies; %<PRId64> van %<PRId64> Woorde; "
"%<PRId64> van %<PRId64> Grepe"
-#: ../ops.c:5105
#, fuzzy, c-format
msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
@@ -4580,7 +4053,6 @@ msgstr ""
"%<PRId64> van %<PRId64> Grepe"
# njj: Karakters kan meerdere grepe wees, sien ':h multibyte'
-#: ../ops.c:5123
#, c-format
msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
@@ -4590,7 +4062,6 @@ msgstr ""
"Greep %<PRId64> van %<PRId64>"
# njj: Karakters kan meerdere grepe wees, sien ':h multibyte'
-#: ../ops.c:5133
#, fuzzy, c-format
msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char "
@@ -4599,148 +4070,104 @@ msgstr ""
"Kol %s van %s; Rel %<PRId64> van %<PRId64>; Woord %<PRId64> van %<PRId64>; "
"Greep %<PRId64> van %<PRId64>"
-#: ../ops.c:5146
#, c-format
msgid "(+%<PRId64> for BOM)"
msgstr "(+%<PRId64> vir 'BOM')"
-#: ../option.c:1238
-msgid "%<%f%h%m%=Page %N"
-msgstr "%<%f%h%m%=Bladsy %N"
-
-#: ../option.c:1574
-msgid "Thanks for flying Vim"
-msgstr "Dankie dat jy vlieg met Vim"
-
#. found a mismatch: skip
-#: ../option.c:2698
msgid "E518: Unknown option"
msgstr "E518: Onbekende opsie"
-#: ../option.c:2709
-msgid "E519: Option not supported"
-msgstr "E519: Opsie is nie ondersteun nie"
-
-#: ../option.c:2740
msgid "E520: Not allowed in a modeline"
msgstr "E520: Nie toegelaat in 'n moduslyn nie"
-#: ../option.c:2815
-msgid "E846: Key code not set"
-msgstr ""
+#~ msgid "E846: Key code not set"
+#~ msgstr ""
-#: ../option.c:2924
msgid "E521: Number required after ="
msgstr "E521: Nommer vereis na ="
-#: ../option.c:3226 ../option.c:3864
-msgid "E522: Not found in termcap"
-msgstr "E522: Nie gevind in 'termcap' nie"
-
-#: ../option.c:3335
#, c-format
msgid "E539: Illegal character <%s>"
msgstr "E539: Ongeldige karakter <%s>"
-#: ../option.c:3862
-msgid "E529: Cannot set 'term' to empty string"
-msgstr "E529: Kan nie 'term' stel na le string nie"
+#, c-format
+#~ msgid "For option %s"
+#~ msgstr ""
-#: ../option.c:3885
msgid "E589: 'backupext' and 'patchmode' are equal"
msgstr "E589: 'backupext' en 'patchmode' is dieselfde"
-#: ../option.c:3964
-msgid "E834: Conflicts with value of 'listchars'"
-msgstr ""
+#~ msgid "E834: Conflicts with value of 'listchars'"
+#~ msgstr ""
-#: ../option.c:3966
-msgid "E835: Conflicts with value of 'fillchars'"
-msgstr ""
+#~ msgid "E835: Conflicts with value of 'fillchars'"
+#~ msgstr ""
-#: ../option.c:4163
msgid "E524: Missing colon"
msgstr "E524: Ontbrekende dubbelpunt"
-#: ../option.c:4165
msgid "E525: Zero length string"
msgstr "E525: Nul-lengte string"
-#: ../option.c:4220
#, c-format
msgid "E526: Missing number after <%s>"
msgstr "E526: Ontbrekende nommer na <%s>"
-#: ../option.c:4232
msgid "E527: Missing comma"
msgstr "E527: Ontbrekende komma"
-#: ../option.c:4239
msgid "E528: Must specify a ' value"
msgstr "E528: Moet 'n ' waarde spesifiseer"
-#: ../option.c:4271
msgid "E595: contains unprintable or wide character"
msgstr "E595: bevat 'n ondrukbare of wye karakter"
-#: ../option.c:4469
#, c-format
msgid "E535: Illegal character after <%c>"
msgstr "E535: Ongeldige karakter na <%c>"
-#: ../option.c:4534
msgid "E536: comma required"
msgstr "E536: komma benodig"
-#: ../option.c:4543
#, c-format
msgid "E537: 'commentstring' must be empty or contain %s"
msgstr "E537: 'commentstring' moet leeg wees of %s bevat"
-#: ../option.c:4928
msgid "E540: Unclosed expression sequence"
msgstr "E540: Onvoltooide uitdrukkingreeks"
-#: ../option.c:4932
msgid "E541: too many items"
msgstr "E541: te veel items"
-#: ../option.c:4934
msgid "E542: unbalanced groups"
msgstr "E542: ongebalanseerde groepe"
-#: ../option.c:5148
msgid "E590: A preview window already exists"
msgstr "E590: Daar bestaan reeds 'n voorskouvenster"
-#: ../option.c:5311
msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'"
msgstr "W17: Arabies benodig UTF-8, doen ':set encoding=utf-8'"
-#: ../option.c:5623
#, c-format
msgid "E593: Need at least %d lines"
msgstr "E593: Benodig ten minste %d rels"
-#: ../option.c:5631
#, c-format
msgid "E594: Need at least %d columns"
msgstr "E594: Benodig ten minste %d kolomme"
-#: ../option.c:6011
#, c-format
msgid "E355: Unknown option: %s"
msgstr "E355: Onbekende opsie: %s"
#. There's another character after zeros or the string
-#. * is empty. In both cases, we are trying to set a
-#. * num option using a string.
-#: ../option.c:6037
+#. is empty. In both cases, we are trying to set a
+#. num option using a string.
#, fuzzy, c-format
-msgid "E521: Number required: &%s = '%s'"
-msgstr "E521: Nommer vereis na ="
+#~ msgid "E521: Number required: &%s = '%s'"
+#~ msgstr "E521: Nommer vereis na ="
-#: ../option.c:6149
msgid ""
"\n"
"--- Terminal codes ---"
@@ -4748,7 +4175,6 @@ msgstr ""
"\n"
"--- Terminaal kodes ---"
-#: ../option.c:6151
msgid ""
"\n"
"--- Global option values ---"
@@ -4756,7 +4182,6 @@ msgstr ""
"\n"
"--- Globale opsie waardes ---"
-#: ../option.c:6153
msgid ""
"\n"
"--- Local option values ---"
@@ -4764,7 +4189,6 @@ msgstr ""
"\n"
"--- Lokale opsie waardes ---"
-#: ../option.c:6155
msgid ""
"\n"
"--- Options ---"
@@ -4772,29 +4196,28 @@ msgstr ""
"\n"
"--- Opsies ---"
-#: ../option.c:6816
msgid "E356: get_varp ERROR"
msgstr "E356: 'get_varp' FOUT"
-#: ../option.c:7696
#, c-format
msgid "E357: 'langmap': Matching character missing for %s"
msgstr "E357: 'langmap': Passende karakter ontbreek vir %s"
-#: ../option.c:7715
#, c-format
msgid "E358: 'langmap': Extra characters after semicolon: %s"
msgstr "E358: 'langmap: Ekstra karakters na kommapunt: %s"
-#: ../os/shell.c:194
-msgid ""
-"\n"
-"Cannot execute shell "
-msgstr ""
-"\n"
-"Kan nie dop uitvoer nie "
+#, c-format
+#~ msgid "dlerror = \"%s\""
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E5420: Failed to write to file: %s"
+#~ msgstr "E365: Kon nie 'PostScript' ler druk nie"
+
+msgid "Vim: Error reading input, exiting...\n"
+msgstr "Vim: Fout met lees van invoer, verlaat...\n"
-#: ../os/shell.c:439
msgid ""
"\n"
"shell returned "
@@ -4802,959 +4225,930 @@ msgstr ""
"\n"
"dop lewer "
-#: ../os_unix.c:465 ../os_unix.c:471
-msgid ""
-"\n"
-"Could not get security context for "
-msgstr ""
+#~ msgid ""
+#~ "\n"
+#~ "shell failed to start: "
+#~ msgstr ""
-#: ../os_unix.c:479
-msgid ""
-"\n"
-"Could not set security context for "
-msgstr ""
+#. Can happen if system() tries to send input to a shell command that was
+#. backgrounded (:call system("cat - &", "foo")). #3529 #5241
+#, fuzzy, c-format
+#~ msgid "E5677: Error writing input to shell-command: %s"
+#~ msgstr "E208: Kan nie skryf na \"%s\""
-#: ../os_unix.c:1558 ../os_unix.c:1647
-#, c-format
-msgid "dlerror = \"%s\""
-msgstr ""
+#~ msgid ""
+#~ "\n"
+#~ "Could not get security context for "
+#~ msgstr ""
+
+#~ msgid ""
+#~ "\n"
+#~ "Could not set security context for "
+#~ msgstr ""
-#: ../path.c:1449
#, c-format
msgid "E447: Can't find file \"%s\" in path"
msgstr "E447: Kan ler \"%s\" nie vind in pad nie"
-#: ../quickfix.c:359
#, c-format
msgid "E372: Too many %%%c in format string"
msgstr "E372: Te veel %%%c in formaatstring"
-#: ../quickfix.c:371
#, c-format
msgid "E373: Unexpected %%%c in format string"
msgstr "E373: Onverwagte %%%c in formaatstring"
-#: ../quickfix.c:420
msgid "E374: Missing ] in format string"
msgstr "E374: Ontbrekende ] in formaatstring"
-#: ../quickfix.c:431
#, c-format
msgid "E375: Unsupported %%%c in format string"
msgstr "E375: Ongesteunde %%%c in formaatstring"
-#: ../quickfix.c:448
#, c-format
msgid "E376: Invalid %%%c in format string prefix"
msgstr "E376: Ongeldige %%%c in formaatstringvoorvoegsel"
-#: ../quickfix.c:454
#, c-format
msgid "E377: Invalid %%%c in format string"
msgstr "E377: Ongeldige %%%c in formaatstring"
#. nothing found
-#: ../quickfix.c:477
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: 'errorformat' bevat geen patroon nie"
-#: ../quickfix.c:695
msgid "E379: Missing or empty directory name"
msgstr "E379: Ontbrekende of le gidsnaam"
-#: ../quickfix.c:1305
msgid "E553: No more items"
msgstr "E553: Geen items meer nie"
-#: ../quickfix.c:1674
+#~ msgid "E924: Current window was closed"
+#~ msgstr ""
+
+#~ msgid "E925: Current quickfix was changed"
+#~ msgstr ""
+
+#~ msgid "E926: Current location list was changed"
+#~ msgstr ""
+
#, c-format
msgid "(%d of %d)%s%s: "
msgstr "(%d van %d)%s%s: "
-#: ../quickfix.c:1676
msgid " (line deleted)"
msgstr " (rel verwyder)"
-#: ../quickfix.c:1863
+#, fuzzy, c-format
+#~ msgid "%serror list %d of %d; %d errors "
+#~ msgstr "foutelys %d van %d; %d foute"
+
msgid "E380: At bottom of quickfix stack"
msgstr "E380: Onder aan 'quickfix' stapel"
-#: ../quickfix.c:1869
msgid "E381: At top of quickfix stack"
msgstr "E381: Bo aan 'quickfix' stapel"
-#: ../quickfix.c:1880
-#, c-format
-msgid "error list %d of %d; %d errors"
-msgstr "foutelys %d van %d; %d foute"
+#~ msgid "No entries"
+#~ msgstr ""
-#: ../quickfix.c:2427
msgid "E382: Cannot write, 'buftype' option is set"
msgstr "E382: Kan nie skryf nie, 'buftype' opsie is aan"
-#: ../quickfix.c:2812
-msgid "E683: File name missing or invalid pattern"
-msgstr ""
+#~ msgid "E683: File name missing or invalid pattern"
+#~ msgstr ""
-#: ../quickfix.c:2911
#, fuzzy, c-format
-msgid "Cannot open file \"%s\""
-msgstr "Kan nie ler %s oopmaak nie"
+#~ msgid "Cannot open file \"%s\""
+#~ msgstr "Kan nie ler %s oopmaak nie"
-#: ../quickfix.c:3429
#, fuzzy
-msgid "E681: Buffer is not loaded"
-msgstr "1 buffer uitgelaai"
+#~ msgid "E681: Buffer is not loaded"
+#~ msgstr "1 buffer uitgelaai"
-#: ../quickfix.c:3487
#, fuzzy
-msgid "E777: String or List expected"
-msgstr "E548: syfer verwag"
+#~ msgid "E777: String or List expected"
+#~ msgstr "E548: syfer verwag"
-#: ../regexp.c:359
#, c-format
msgid "E369: invalid item in %s%%[]"
msgstr "E369: ongeldige item in %s%%[]"
-#: ../regexp.c:374
#, fuzzy, c-format
-msgid "E769: Missing ] after %s["
-msgstr "E69: Ontbrekende ] na %s%%["
+#~ msgid "E769: Missing ] after %s["
+#~ msgstr "E69: Ontbrekende ] na %s%%["
-#: ../regexp.c:375
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: Onpaar %s%%("
-#: ../regexp.c:376
#, c-format
msgid "E54: Unmatched %s("
msgstr "E54: Onpaar %s("
-#: ../regexp.c:377
#, c-format
msgid "E55: Unmatched %s)"
msgstr "E55: Onpaar %s)"
-#: ../regexp.c:378
msgid "E66: \\z( not allowed here"
msgstr "E66: \\z( nie hier toegelaat nie"
-#: ../regexp.c:379
msgid "E67: \\z1 et al. not allowed here"
msgstr "E67: \\z1 e.a. nie hier toegelaat nie"
-#: ../regexp.c:380
#, c-format
msgid "E69: Missing ] after %s%%["
msgstr "E69: Ontbrekende ] na %s%%["
-#: ../regexp.c:381
#, c-format
msgid "E70: Empty %s%%[]"
msgstr "E70: Le %s%%[]"
-#: ../regexp.c:1209 ../regexp.c:1224
msgid "E339: Pattern too long"
msgstr "E339: Patroon te lank"
-#: ../regexp.c:1371
msgid "E50: Too many \\z("
msgstr "E50: Te veel \\z("
-#: ../regexp.c:1378
#, c-format
msgid "E51: Too many %s("
msgstr "E51: Te veel %s("
-#: ../regexp.c:1427
msgid "E52: Unmatched \\z("
msgstr "E52: Onpaar \\z("
-#: ../regexp.c:1637
#, c-format
msgid "E59: invalid character after %s@"
msgstr "E59: ongeldige karakter na %s@"
-#: ../regexp.c:1672
#, c-format
msgid "E60: Too many complex %s{...}s"
msgstr "E60: Te veel komplekse %s{...}ies"
-#: ../regexp.c:1687
#, c-format
msgid "E61: Nested %s*"
msgstr "E61: Geneste %s*"
-#: ../regexp.c:1690
#, c-format
msgid "E62: Nested %s%c"
msgstr "E62: Geneste %s%c"
-#: ../regexp.c:1800
msgid "E63: invalid use of \\_"
msgstr "E63: ongeldige gebruik van \\_"
-#: ../regexp.c:1850
#, c-format
msgid "E64: %s%c follows nothing"
msgstr "E64: %s%c volg niks"
-#: ../regexp.c:1902
msgid "E65: Illegal back reference"
msgstr "E65: Ongeldige tru-verwysing"
-#: ../regexp.c:1943
msgid "E68: Invalid character after \\z"
msgstr "E68: ongeldige karakter na \\z"
-#: ../regexp.c:2049 ../regexp_nfa.c:1296
#, fuzzy, c-format
-msgid "E678: Invalid character after %s%%[dxouU]"
-msgstr "E71: Ongeldige karakter na %s%%"
+#~ msgid "E678: Invalid character after %s%%[dxouU]"
+#~ msgstr "E71: Ongeldige karakter na %s%%"
-#: ../regexp.c:2107
#, c-format
msgid "E71: Invalid character after %s%%"
msgstr "E71: Ongeldige karakter na %s%%"
-#: ../regexp.c:3017
+#, fuzzy, c-format
+#~ msgid "E888: (NFA regexp) cannot repeat %s"
+#~ msgstr "E50: Te veel \\z("
+
#, c-format
msgid "E554: Syntax error in %s{...}"
msgstr "E554: Sintaksfout in %s{...}"
-#: ../regexp.c:3805
msgid "External submatches:\n"
msgstr "Eksterne subtreffers:\n"
-#: ../regexp.c:7022
-msgid ""
-"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
-"used "
-msgstr ""
-
-#: ../regexp_nfa.c:239
-msgid "E865: (NFA) Regexp end encountered prematurely"
-msgstr ""
-
-#: ../regexp_nfa.c:240
-#, c-format
-msgid "E866: (NFA regexp) Misplaced %c"
-msgstr ""
-
-#: ../regexp_nfa.c:242
-#, c-format
-msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr ""
-
-#: ../regexp_nfa.c:1261
-#, c-format
-msgid "E867: (NFA) Unknown operator '\\z%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1387
-#, c-format
-msgid "E867: (NFA) Unknown operator '\\%%%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1802
-#, c-format
-msgid "E869: (NFA) Unknown operator '\\@%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1831
-msgid "E870: (NFA regexp) Error reading repetition limits"
-msgstr ""
-
-#. Can't have a multi follow a multi.
-#: ../regexp_nfa.c:1895
-msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
-msgstr ""
-
-#. Too many `('
-#: ../regexp_nfa.c:2037
-msgid "E872: (NFA regexp) Too many '('"
-msgstr ""
-
-#: ../regexp_nfa.c:2042
-#, fuzzy
-msgid "E879: (NFA regexp) Too many \\z("
-msgstr "E50: Te veel \\z("
-
-#: ../regexp_nfa.c:2066
-msgid "E873: (NFA regexp) proper termination error"
-msgstr ""
-
-#: ../regexp_nfa.c:2599
-msgid "E874: (NFA) Could not pop the stack !"
-msgstr ""
-
-#: ../regexp_nfa.c:3298
-msgid ""
-"E875: (NFA regexp) (While converting from postfix to NFA), too many states "
-"left on stack"
-msgstr ""
-
-#: ../regexp_nfa.c:3302
-msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr ""
-
-#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869
-msgid ""
-"Could not open temporary log file for writing, displaying on stderr ... "
-msgstr ""
+#~ msgid ""
+#~ "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
+#~ "used "
+#~ msgstr ""
-#: ../regexp_nfa.c:4840
-#, c-format
-msgid "(NFA) COULD NOT OPEN %s !"
-msgstr ""
+#~ msgid "Switching to backtracking RE engine for pattern: "
+#~ msgstr ""
-#: ../regexp_nfa.c:6049
-#, fuzzy
-msgid "Could not open temporary log file for writing "
-msgstr "E214: Kan nie tydelike ler vind vir skryf nie"
+#~ msgid " TERMINAL"
+#~ msgstr ""
-#: ../screen.c:7435
msgid " VREPLACE"
msgstr " VVERVANG"
-#: ../screen.c:7437
msgid " REPLACE"
msgstr " VERVANG"
-#: ../screen.c:7440
msgid " REVERSE"
msgstr " OMKEER"
-#: ../screen.c:7441
msgid " INSERT"
msgstr " INVOEG"
-#: ../screen.c:7443
msgid " (insert)"
msgstr " (invoeg)"
-#: ../screen.c:7445
msgid " (replace)"
msgstr " (vervang)"
-#: ../screen.c:7447
msgid " (vreplace)"
msgstr " (vvervang)"
-#: ../screen.c:7449
msgid " Hebrew"
msgstr " Hebreeus"
-#: ../screen.c:7454
msgid " Arabic"
msgstr " Arabies"
-#: ../screen.c:7456
-msgid " (lang)"
-msgstr " (taal)"
-
-#: ../screen.c:7459
msgid " (paste)"
msgstr " (plak)"
-#: ../screen.c:7469
msgid " VISUAL"
msgstr " VISUELE"
-#: ../screen.c:7470
msgid " VISUAL LINE"
msgstr " VISUELE REL"
-#: ../screen.c:7471
msgid " VISUAL BLOCK"
msgstr " VISUELE BLOK"
-#: ../screen.c:7472
msgid " SELECT"
msgstr " KIES"
-#: ../screen.c:7473
msgid " SELECT LINE"
msgstr " KIES REL"
-#: ../screen.c:7474
msgid " SELECT BLOCK"
msgstr " KIES BLOK"
-#: ../screen.c:7486 ../screen.c:7541
msgid "recording"
msgstr "besig om op te neem"
-#: ../search.c:487
#, c-format
msgid "E383: Invalid search string: %s"
msgstr "E383: Ongeldige soekstring: %s"
-#: ../search.c:832
#, c-format
msgid "E384: search hit TOP without match for: %s"
msgstr "E384: soektog het BO getref sonder treffer vir: %s"
-#: ../search.c:835
#, c-format
msgid "E385: search hit BOTTOM without match for: %s"
msgstr "E385: soektog het ONDER getref sonder treffer vir: %s"
-#: ../search.c:1200
msgid "E386: Expected '?' or '/' after ';'"
msgstr "E386: Verwag '?' of '/' na ';'"
-#: ../search.c:4085
msgid " (includes previously listed match)"
msgstr " (sluit in vorige gelyste treffer)"
#. cursor at status line
-#: ../search.c:4104
msgid "--- Included files "
msgstr "--- Ingeslote lers"
-#: ../search.c:4106
msgid "not found "
msgstr "nie gevind nie "
-#: ../search.c:4107
msgid "in path ---\n"
msgstr "in pad ---\n"
-#: ../search.c:4168
msgid " (Already listed)"
msgstr " (Alreeds gelys)"
-#: ../search.c:4170
msgid " NOT FOUND"
msgstr " NIE GEVIND NIE"
-#: ../search.c:4211
#, c-format
msgid "Scanning included file: %s"
msgstr "Deursoek ingeslote ler: %s"
-#: ../search.c:4216
#, fuzzy, c-format
-msgid "Searching included file %s"
-msgstr "Deursoek ingeslote ler: %s"
+#~ msgid "Searching included file %s"
+#~ msgstr "Deursoek ingeslote ler: %s"
-#: ../search.c:4405
msgid "E387: Match is on current line"
msgstr "E387: Treffer is op huidige rel"
-#: ../search.c:4517
msgid "All included files were found"
msgstr "Alle ingeslote lers is gevind"
-#: ../search.c:4519
msgid "No included files"
msgstr "Geen ingeslote lers nie"
-#: ../search.c:4527
msgid "E388: Couldn't find definition"
msgstr "E388: Kon definisie nie vind nie"
-#: ../search.c:4529
msgid "E389: Couldn't find pattern"
msgstr "E389: Kon patroon nie vind nie"
-#: ../search.c:4668
-#, fuzzy
-msgid "Substitute "
-msgstr "1 vervanging"
+#~ msgid "too few bytes read"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "System error while skipping in ShaDa file: %s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
-#: ../search.c:4681
#, c-format
-msgid ""
-"\n"
-"# Last %sSearch Pattern:\n"
-"~"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: last entry specified that it occupies "
+#~ "%<PRIu64> bytes, but file ended earlier"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "System error while closing ShaDa file: %s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "System error while writing ShaDa file: %s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "Reading ShaDa file \"%s\"%s%s%s"
+#~ msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees"
+
+msgid " info"
+msgstr " inligting"
+
+msgid " marks"
+msgstr " merkers"
-#: ../spell.c:951
#, fuzzy
-msgid "E759: Format error in spell file"
-msgstr "E297: Skryffout in ruiller"
+#~ msgid " oldfiles"
+#~ msgstr "Geen ingeslote lers nie"
-#: ../spell.c:952
-msgid "E758: Truncated spell file"
-msgstr ""
+msgid " FAILED"
+msgstr " GEFAAL"
-#: ../spell.c:953
#, c-format
-msgid "Trailing text in %s line %d: %s"
-msgstr ""
+#~ msgid "System error while opening ShaDa file %s for reading: %s"
+#~ msgstr ""
+
+#~ msgid "additional elements of ShaDa "
+#~ msgstr ""
+
+#~ msgid "additional data of ShaDa "
+#~ msgstr ""
-#: ../spell.c:954
#, c-format
-msgid "Affix name too long in %s line %d: %s"
-msgstr ""
+#~ msgid "Failed to write variable %s"
+#~ msgstr ""
-#: ../spell.c:955
-#, fuzzy
-msgid "E761: Format error in affix file FOL, LOW or UPP"
-msgstr "E431: Formaatfout in etiketler \"%s\""
+#, c-format
+#~ msgid ""
+#~ "Failed to parse ShaDa file due to a msgpack parser error at position "
+#~ "%<PRIu64>"
+#~ msgstr ""
-#: ../spell.c:957
-msgid "E762: Character in FOL, LOW or UPP is out of range"
-msgstr ""
+#, c-format
+#~ msgid ""
+#~ "Failed to parse ShaDa file: incomplete msgpack string at position %<PRIu64>"
+#~ msgstr ""
-#: ../spell.c:958
-msgid "Compressing word tree..."
-msgstr ""
+#, c-format
+#~ msgid ""
+#~ "Failed to parse ShaDa file: extra bytes in msgpack string at position "
+#~ "%<PRIu64>"
+#~ msgstr ""
-#: ../spell.c:1951
-msgid "E756: Spell checking is not enabled"
-msgstr ""
+#, c-format
+#~ msgid ""
+#~ "System error while opening ShaDa file %s for reading to merge before writing "
+#~ "it: %s"
+#~ msgstr ""
-#: ../spell.c:2249
+#. Tried names from .tmp.a to .tmp.z, all failed. Something must be
+#. wrong then.
#, c-format
-msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr ""
+#~ msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!"
+#~ msgstr ""
-#: ../spell.c:2473
#, fuzzy, c-format
-msgid "Reading spell file \"%s\""
-msgstr "Gebruik ruiller \"%s\""
+#~ msgid "System error while opening temporary ShaDa file %s for writing: %s"
+#~ msgstr "E214: Kan nie tydelike ler vind vir skryf nie"
-#: ../spell.c:2496
-#, fuzzy
-msgid "E757: This does not look like a spell file"
-msgstr "E307: %s lyk nie soos 'n Vim ruiller nie"
+#, c-format
+#~ msgid "Failed to create directory %s for writing ShaDa file: %s"
+#~ msgstr ""
-#: ../spell.c:2501
-msgid "E771: Old spell file, needs to be updated"
-msgstr ""
+#, c-format
+#~ msgid "System error while opening ShaDa file %s for writing: %s"
+#~ msgstr ""
-#: ../spell.c:2504
-msgid "E772: Spell file is for newer version of Vim"
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "Writing ShaDa file \"%s\""
+#~ msgstr "Besig om viminfo ler \"%s\" te stoor"
-#: ../spell.c:2602
-#, fuzzy
-msgid "E770: Unsupported section in spell file"
-msgstr "E297: Skryffout in ruiller"
+#, fuzzy, c-format
+#~ msgid "Failed setting uid and gid for file %s: %s"
+#~ msgstr "%s klaar uitgevoer"
-#: ../spell.c:3762
#, fuzzy, c-format
-msgid "Warning: region %s not supported"
-msgstr "E519: Opsie is nie ondersteun nie"
+#~ msgid "E137: ShaDa file is not writable: %s"
+#~ msgstr "E137: Viminfo ler is nie skryfbaar nie: %s"
+
+#, c-format
+#~ msgid "Can't rename ShaDa file from %s to %s!"
+#~ msgstr ""
-#: ../spell.c:4550
#, fuzzy, c-format
-msgid "Reading affix file %s ..."
-msgstr "Deursoek etiketler %s"
+#~ msgid "Did not rename %s because %s does not look like a ShaDa file"
+#~ msgstr " [lyk nie soos 'n Vim ruiller nie]"
-#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
#, c-format
-msgid "Conversion failure for word in %s line %d: %s"
-msgstr ""
+#~ msgid "Did not rename %s to %s because there were errors during writing it"
+#~ msgstr ""
-#: ../spell.c:4630 ../spell.c:6170
#, c-format
-msgid "Conversion in %s not supported: from %s to %s"
-msgstr ""
+#~ msgid "Do not forget to remove %s or rename it manually to %s."
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "System error while reading ShaDa file: %s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "System error while reading integer from ShaDa file: %s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
-#: ../spell.c:4642
#, c-format
-msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: expected positive integer at position "
+#~ "%<PRIu64>, but got nothing"
+#~ msgstr ""
-#: ../spell.c:4655
#, c-format
-msgid "FLAG after using flags in %s line %d: %s"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: expected positive integer at position "
+#~ "%<PRIu64>"
+#~ msgstr ""
-#: ../spell.c:4723
#, c-format
-msgid ""
-"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
-"%d"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: there is an item at position %<PRIu64> that "
+#~ "is stated to be too long"
+#~ msgstr ""
-#: ../spell.c:4731
+#. kSDItemUnknown cannot possibly pass that far because it is -1 and that
+#. will fail in msgpack_read_uint64. But kSDItemMissing may and it will
+#. otherwise be skipped because (1 << 0) will never appear in flags.
#, c-format
-msgid ""
-"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
-"%d"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: there is an item at position %<PRIu64> that "
+#~ "must not be there: Missing items are for internal uses only"
+#~ msgstr ""
-#: ../spell.c:4747
#, c-format
-msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+#~ "entry that is not a dictionary"
+#~ msgstr ""
-#: ../spell.c:4771
#, c-format
-msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+#~ "entry with invalid line number"
+#~ msgstr ""
-#: ../spell.c:4777
#, c-format
-msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+#~ "entry with invalid column number"
+#~ msgstr ""
-#: ../spell.c:4783
#, c-format
-msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr ""
+#~ msgid ""
+#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+#~ "entry that does not have a file name"
+#~ msgstr ""
+
+#. values for ts_isdiff
+#. no different byte (yet)
+#. different byte found
+#. inserting character
+#. values for ts_flags
+#. already checked that prefix is OK
+#. tried split at this point
+#. did a delete, "ts_delidx" has index
+#. special values ts_prefixdepth
+#. not using prefixes
+#. walking through the prefix tree
+#. highest value that's not special
+#. mode values for find_word
+#. find word case-folded
+#. find keep-case word
+#. find word after prefix
+#. find case-folded compound word
+#. find keep-case compound word
+#, fuzzy
+#~ msgid "E759: Format error in spell file"
+#~ msgstr "E297: Skryffout in ruiller"
+
+#~ msgid "E756: Spell checking is not enabled"
+#~ msgstr ""
-#: ../spell.c:4795
#, c-format
-msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr ""
+#~ msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E797: SpellFileMissing autocommand deleted buffer"
+#~ msgstr "E246: 'FileChangedShell' outobevel het buffer verwyder"
+
+#. This is probably an error. Give a warning and
+#. accept the words anyway.
+#, fuzzy, c-format
+#~ msgid "Warning: region %s not supported"
+#~ msgstr "E519: Opsie is nie ondersteun nie"
+
+#~ msgid "Sorry, no suggestions"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "Sorry, only %<PRId64> suggestions"
+#~ msgstr " op %<PRId64> rels"
+
+#. for when 'cmdheight' > 1
+#. avoid more prompt
+#, fuzzy, c-format
+#~ msgid "Change \"%.*s\" to:"
+#~ msgstr "Stoor veranderinge na \"%.*s\"?"
-#: ../spell.c:4847
#, c-format
-msgid "Different combining flag in continued affix block in %s line %d: %s"
-msgstr ""
+#~ msgid " < \"%.*s\""
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E752: No previous spell replacement"
+#~ msgstr "E35: Geen vorige patroon nie"
-#: ../spell.c:4850
#, fuzzy, c-format
-msgid "Duplicate affix in %s line %d: %s"
-msgstr "E154: Duplikaat etiket \"%s\" in ler %s/%s"
+#~ msgid "E753: Not found: %s"
+#~ msgstr "E334: Kieslys nie gevind nie: %s"
+
+#~ msgid "E758: Truncated spell file"
+#~ msgstr ""
-#: ../spell.c:4871
#, c-format
-msgid ""
-"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
-"line %d: %s"
-msgstr ""
+#~ msgid "Trailing text in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:4893
#, c-format
-msgid "Expected Y or N in %s line %d: %s"
-msgstr ""
+#~ msgid "Affix name too long in %s line %d: %s"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E761: Format error in affix file FOL, LOW or UPP"
+#~ msgstr "E431: Formaatfout in etiketler \"%s\""
+
+#~ msgid "E762: Character in FOL, LOW or UPP is out of range"
+#~ msgstr ""
+
+#~ msgid "Compressing word tree..."
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "Reading spell file \"%s\""
+#~ msgstr "Gebruik ruiller \"%s\""
+
+#, fuzzy
+#~ msgid "E757: This does not look like a spell file"
+#~ msgstr "E307: %s lyk nie soos 'n Vim ruiller nie"
+
+#, fuzzy, c-format
+#~ msgid "E5042: Failed to read spell file %s: %s"
+#~ msgstr "E482: Kan nie ler %s skep nie"
+
+#~ msgid "E771: Old spell file, needs to be updated"
+#~ msgstr ""
+
+#~ msgid "E772: Spell file is for newer version of Vim"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E770: Unsupported section in spell file"
+#~ msgstr "E297: Skryffout in ruiller"
+
+#, fuzzy, c-format
+#~ msgid "E778: This does not look like a .sug file: %s"
+#~ msgstr "E307: %s lyk nie soos 'n Vim ruiller nie"
-#: ../spell.c:4968
#, c-format
-msgid "Broken condition in %s line %d: %s"
-msgstr ""
+#~ msgid "E779: Old .sug file, needs to be updated: %s"
+#~ msgstr ""
-#: ../spell.c:5091
#, c-format
-msgid "Expected REP(SAL) count in %s line %d"
-msgstr ""
+#~ msgid "E780: .sug file is for newer version of Vim: %s"
+#~ msgstr ""
-#: ../spell.c:5120
#, c-format
-msgid "Expected MAP count in %s line %d"
-msgstr ""
+#~ msgid "E781: .sug file doesn't match .spl file: %s"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "E782: error while reading .sug file: %s"
+#~ msgstr "E47: Fout tydens lees van 'errorfile'"
+
+#, fuzzy, c-format
+#~ msgid "Reading affix file %s ..."
+#~ msgstr "Deursoek etiketler %s"
-#: ../spell.c:5132
#, c-format
-msgid "Duplicate character in MAP in %s line %d"
-msgstr ""
+#~ msgid "Conversion failure for word in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5176
#, c-format
-msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr ""
+#~ msgid "Conversion in %s not supported: from %s to %s"
+#~ msgstr ""
-#: ../spell.c:5197
#, c-format
-msgid "Missing FOL/LOW/UPP line in %s"
-msgstr ""
+#~ msgid "Invalid value for FLAG in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5220
-msgid "COMPOUNDSYLMAX used without SYLLABLE"
-msgstr ""
+#, c-format
+#~ msgid "FLAG after using flags in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5236
-#, fuzzy
-msgid "Too many postponed prefixes"
-msgstr "Te veel redigeer-parameters"
+#, c-format
+#~ msgid ""
+#~ "Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
+#~ "%d"
+#~ msgstr ""
-#: ../spell.c:5238
-#, fuzzy
-msgid "Too many compound flags"
-msgstr "Te veel redigeer-parameters"
+#, c-format
+#~ msgid ""
+#~ "Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
+#~ "%d"
+#~ msgstr ""
-#: ../spell.c:5240
-msgid "Too many postponed prefixes and/or compound flags"
-msgstr ""
+#, c-format
+#~ msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5250
#, c-format
-msgid "Missing SOFO%s line in %s"
-msgstr ""
+#~ msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5253
#, c-format
-msgid "Both SAL and SOFO lines in %s"
-msgstr ""
+#~ msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5331
#, c-format
-msgid "Flag is not a number in %s line %d: %s"
-msgstr ""
+#~ msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5334
#, c-format
-msgid "Illegal flag in %s line %d: %s"
-msgstr ""
+#~ msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5493 ../spell.c:5501
#, c-format
-msgid "%s value differs from what is used in another .aff file"
-msgstr ""
+#~ msgid "Different combining flag in continued affix block in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5602
#, fuzzy, c-format
-msgid "Reading dictionary file %s ..."
-msgstr "Deursoek woordeboek: %s"
+#~ msgid "Duplicate affix in %s line %d: %s"
+#~ msgstr "E154: Duplikaat etiket \"%s\" in ler %s/%s"
-#: ../spell.c:5611
#, c-format
-msgid "E760: No word count in %s"
-msgstr ""
+#~ msgid ""
+#~ "Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGESTin %s "
+#~ "line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5669
#, c-format
-msgid "line %6d, word %6d - %s"
-msgstr ""
-
-#: ../spell.c:5691
-#, fuzzy, c-format
-msgid "Duplicate word in %s line %d: %s"
-msgstr "Patroon gevind in elke rel: %s"
+#~ msgid "Expected Y or N in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5694
#, c-format
-msgid "First duplicate word in %s line %d: %s"
-msgstr ""
+#~ msgid "Broken condition in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:5746
#, c-format
-msgid "%d duplicate word(s) in %s"
-msgstr ""
+#~ msgid "Expected REP(SAL) count in %s line %d"
+#~ msgstr ""
-#: ../spell.c:5748
#, c-format
-msgid "Ignored %d word(s) with non-ASCII characters in %s"
-msgstr ""
+#~ msgid "Expected MAP count in %s line %d"
+#~ msgstr ""
-#: ../spell.c:6115
-#, fuzzy, c-format
-msgid "Reading word file %s ..."
-msgstr "Lees nou vanaf stdin... "
+#, c-format
+#~ msgid "Duplicate character in MAP in %s line %d"
+#~ msgstr ""
-#: ../spell.c:6155
#, c-format
-msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr ""
+#~ msgid "Unrecognized or duplicate item in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:6159
#, c-format
-msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr ""
+#~ msgid "Missing FOL/LOW/UPP line in %s"
+#~ msgstr ""
+
+#~ msgid "COMPOUNDSYLMAX used without SYLLABLE"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "Too many postponed prefixes"
+#~ msgstr "Te veel redigeer-parameters"
+
+#, fuzzy
+#~ msgid "Too many compound flags"
+#~ msgstr "Te veel redigeer-parameters"
+
+#~ msgid "Too many postponed prefixes and/or compound flags"
+#~ msgstr ""
-#: ../spell.c:6180
#, c-format
-msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr ""
+#~ msgid "Missing SOFO%s line in %s"
+#~ msgstr ""
-#: ../spell.c:6185
#, c-format
-msgid "Too many regions in %s line %d: %s"
-msgstr ""
+#~ msgid "Both SAL and SOFO lines in %s"
+#~ msgstr ""
-#: ../spell.c:6198
#, c-format
-msgid "/ line ignored in %s line %d: %s"
-msgstr ""
+#~ msgid "Flag is not a number in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:6224
-#, fuzzy, c-format
-msgid "Invalid region nr in %s line %d: %s"
-msgstr "E573: Ongeldige bediener-id gebruik: %s"
+#, c-format
+#~ msgid "Illegal flag in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:6230
#, c-format
-msgid "Unrecognized flags in %s line %d: %s"
-msgstr ""
+#~ msgid "%s value differs from what is used in another .aff file"
+#~ msgstr ""
+
+#, fuzzy, c-format
+#~ msgid "Reading dictionary file %s ..."
+#~ msgstr "Deursoek woordeboek: %s"
-#: ../spell.c:6257
#, c-format
-msgid "Ignored %d words with non-ASCII characters"
-msgstr ""
+#~ msgid "E760: No word count in %s"
+#~ msgstr ""
-#: ../spell.c:6656
#, c-format
-msgid "Compressed %d of %d nodes; %d (%d%%) remaining"
-msgstr ""
+#~ msgid "line %6d, word %6d - %s"
+#~ msgstr ""
-#: ../spell.c:7340
-msgid "Reading back spell file..."
-msgstr ""
+#, fuzzy, c-format
+#~ msgid "Duplicate word in %s line %d: %s"
+#~ msgstr "Patroon gevind in elke rel: %s"
-#. Go through the trie of good words, soundfold each word and add it to
-#. the soundfold trie.
-#: ../spell.c:7357
-msgid "Performing soundfolding..."
-msgstr ""
+#, c-format
+#~ msgid "First duplicate word in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:7368
#, c-format
-msgid "Number of words after soundfolding: %<PRId64>"
-msgstr ""
+#~ msgid "%d duplicate word(s) in %s"
+#~ msgstr ""
-#: ../spell.c:7476
#, c-format
-msgid "Total number of words: %d"
-msgstr ""
+#~ msgid "Ignored %d word(s) with non-ASCII characters in %s"
+#~ msgstr ""
-#: ../spell.c:7655
#, fuzzy, c-format
-msgid "Writing suggestion file %s ..."
-msgstr "Besig om viminfo ler \"%s\" te stoor"
+#~ msgid "Reading word file %s ..."
+#~ msgstr "Lees nou vanaf stdin... "
-#: ../spell.c:7707 ../spell.c:7927
#, c-format
-msgid "Estimated runtime memory use: %d bytes"
-msgstr ""
+#~ msgid "Duplicate /encoding= line ignored in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:7820
-msgid "E751: Output file name must not have region name"
-msgstr ""
+#, c-format
+#~ msgid "/encoding= line after word ignored in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:7822
-#, fuzzy
-msgid "E754: Only up to 8 regions supported"
-msgstr "E519: Opsie is nie ondersteun nie"
+#, c-format
+#~ msgid "Duplicate /regions= line ignored in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:7846
-#, fuzzy, c-format
-msgid "E755: Invalid region in %s"
-msgstr "E15: Ongeldige uitdrukking: %s"
+#, c-format
+#~ msgid "Too many regions in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:7907
-msgid "Warning: both compounding and NOBREAK specified"
-msgstr ""
+#, c-format
+#~ msgid "/ line ignored in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:7920
#, fuzzy, c-format
-msgid "Writing spell file %s ..."
-msgstr "Besig om viminfo ler \"%s\" te stoor"
+#~ msgid "Invalid region nr in %s line %d: %s"
+#~ msgstr "E573: Ongeldige bediener-id gebruik: %s"
-#: ../spell.c:7925
-msgid "Done!"
-msgstr ""
-
-#: ../spell.c:8034
#, c-format
-msgid "E765: 'spellfile' does not have %<PRId64> entries"
-msgstr ""
+#~ msgid "Unrecognized flags in %s line %d: %s"
+#~ msgstr ""
-#: ../spell.c:8074
#, c-format
-msgid "Word '%.*s' removed from %s"
-msgstr ""
+#~ msgid "Ignored %d words with non-ASCII characters"
+#~ msgstr ""
-#: ../spell.c:8117
#, c-format
-msgid "Word '%.*s' added to %s"
-msgstr ""
+#~ msgid "Compressed %d of %d nodes; %d (%d%%) remaining"
+#~ msgstr ""
-#: ../spell.c:8381
-msgid "E763: Word characters differ between spell files"
-msgstr ""
+#~ msgid "Reading back spell file..."
+#~ msgstr ""
-#: ../spell.c:8684
-msgid "Sorry, no suggestions"
-msgstr ""
+#. Go through the trie of good words, soundfold each word and add it to
+#. the soundfold trie.
+#~ msgid "Performing soundfolding..."
+#~ msgstr ""
-#: ../spell.c:8687
-#, fuzzy, c-format
-msgid "Sorry, only %<PRId64> suggestions"
-msgstr " op %<PRId64> rels"
+#, c-format
+#~ msgid "Number of words after soundfolding: %<PRId64>"
+#~ msgstr ""
+
+#, c-format
+#~ msgid "Total number of words: %d"
+#~ msgstr ""
-#. for when 'cmdheight' > 1
-#. avoid more prompt
-#: ../spell.c:8704
#, fuzzy, c-format
-msgid "Change \"%.*s\" to:"
-msgstr "Stoor veranderinge na \"%.*s\"?"
+#~ msgid "Writing suggestion file %s ..."
+#~ msgstr "Besig om viminfo ler \"%s\" te stoor"
-#: ../spell.c:8737
#, c-format
-msgid " < \"%.*s\""
-msgstr ""
+#~ msgid "Estimated runtime memory use: %d bytes"
+#~ msgstr ""
+
+#~ msgid "E751: Output file name must not have region name"
+#~ msgstr ""
-#: ../spell.c:8882
#, fuzzy
-msgid "E752: No previous spell replacement"
-msgstr "E35: Geen vorige patroon nie"
+#~ msgid "E754: Only up to 8 regions supported"
+#~ msgstr "E519: Opsie is nie ondersteun nie"
-#: ../spell.c:8925
#, fuzzy, c-format
-msgid "E753: Not found: %s"
-msgstr "E334: Kieslys nie gevind nie: %s"
+#~ msgid "E755: Invalid region in %s"
+#~ msgstr "E15: Ongeldige uitdrukking: %s"
+
+#~ msgid "Warning: both compounding and NOBREAK specified"
+#~ msgstr ""
-#: ../spell.c:9276
#, fuzzy, c-format
-msgid "E778: This does not look like a .sug file: %s"
-msgstr "E307: %s lyk nie soos 'n Vim ruiller nie"
+#~ msgid "Writing spell file %s ..."
+#~ msgstr "Besig om viminfo ler \"%s\" te stoor"
+
+#~ msgid "Done!"
+#~ msgstr ""
-#: ../spell.c:9282
#, c-format
-msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr ""
+#~ msgid "E765: 'spellfile' does not have %<PRId64> entries"
+#~ msgstr ""
-#: ../spell.c:9286
#, c-format
-msgid "E780: .sug file is for newer version of Vim: %s"
-msgstr ""
+#~ msgid "Word '%.*s' removed from %s"
+#~ msgstr ""
-#: ../spell.c:9295
#, c-format
-msgid "E781: .sug file doesn't match .spl file: %s"
-msgstr ""
+#~ msgid "Word '%.*s' added to %s"
+#~ msgstr ""
-#: ../spell.c:9305
-#, fuzzy, c-format
-msgid "E782: error while reading .sug file: %s"
-msgstr "E47: Fout tydens lees van 'errorfile'"
+#~ msgid "E763: Word characters differ between spell files"
+#~ msgstr ""
#. This should have been checked when generating the .spl
#. file.
-#: ../spell.c:11575
-msgid "E783: duplicate char in MAP entry"
-msgstr ""
+#~ msgid "E783: duplicate char in MAP entry"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E766: Insufficient arguments for printf()"
+#~ msgstr "E116: Ongeldige parameters vir funksie %s"
+
+#~ msgid "E807: Expected Float argument for printf()"
+#~ msgstr ""
+
+#, fuzzy
+#~ msgid "E767: Too many arguments to printf()"
+#~ msgstr "E118: Te veel parameters vir funksie: %s"
-#: ../syntax.c:266
msgid "No Syntax items defined for this buffer"
msgstr "Geen Sintaks-items gedefinieer vir hierdie buffer nie"
-#: ../syntax.c:3083 ../syntax.c:3104 ../syntax.c:3127
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Ongeldige parameter: %s"
-#: ../syntax.c:3299
+#~ msgid "syntax iskeyword "
+#~ msgstr ""
+
#, c-format
msgid "E391: No such syntax cluster: %s"
msgstr "E391: Geen sodanige sintakskluster nie: %s"
-#: ../syntax.c:3433
msgid "syncing on C-style comments"
msgstr "sinchroniseer met C-styl kommentaar"
-#: ../syntax.c:3439
msgid "no syncing"
msgstr "geen sinchronisering"
-#: ../syntax.c:3441
msgid "syncing starts "
msgstr "sinchronisasie begin "
-#: ../syntax.c:3443 ../syntax.c:3506
msgid " lines before top line"
msgstr " rels voor boonste lyn"
-#: ../syntax.c:3448
msgid ""
"\n"
"--- Syntax sync items ---"
@@ -5762,7 +5156,6 @@ msgstr ""
"\n"
"--- Sintaks sync items ---"
-#: ../syntax.c:3452
msgid ""
"\n"
"syncing on items"
@@ -5770,7 +5163,6 @@ msgstr ""
"\n"
"sinchronisering met items"
-#: ../syntax.c:3457
msgid ""
"\n"
"--- Syntax items ---"
@@ -5778,275 +5170,216 @@ msgstr ""
"\n"
"--- Sintaks items ---"
-#: ../syntax.c:3475
#, c-format
msgid "E392: No such syntax cluster: %s"
msgstr "E392: Geen sodanige sintakskluster nie: %s"
-#: ../syntax.c:3497
msgid "minimal "
msgstr "minimaal "
-#: ../syntax.c:3503
msgid "maximal "
msgstr "maksimaal "
-#: ../syntax.c:3513
msgid "; match "
msgstr "; treffer "
-#: ../syntax.c:3515
msgid " line breaks"
msgstr " rel breuke"
-#: ../syntax.c:4076
msgid "E395: contains argument not accepted here"
msgstr "E395: bevat parameters nie hier aanvaar nie"
-#: ../syntax.c:4096
#, fuzzy
-msgid "E844: invalid cchar value"
-msgstr "E474: Ongeldige parameter"
+#~ msgid "E844: invalid cchar value"
+#~ msgstr "E474: Ongeldige parameter"
-#: ../syntax.c:4107
msgid "E393: group[t]here not accepted here"
msgstr "E393: 'group[t]here' nie hier aanvaar nie"
-#: ../syntax.c:4126
#, c-format
msgid "E394: Didn't find region item for %s"
msgstr "E394: Kon nie omgewingsitem vind vir %s nie"
-#: ../syntax.c:4188
msgid "E397: Filename required"
msgstr "E397: Lernaam benodig"
-#: ../syntax.c:4221
#, fuzzy
-msgid "E847: Too many syntax includes"
-msgstr "E77: Te veel lername"
+#~ msgid "E847: Too many syntax includes"
+#~ msgstr "E77: Te veel lername"
-#: ../syntax.c:4303
#, fuzzy, c-format
-msgid "E789: Missing ']': %s"
-msgstr "E398: Ontbrekende '=': %s"
+#~ msgid "E789: Missing ']': %s"
+#~ msgstr "E398: Ontbrekende '=': %s"
+
+#, fuzzy, c-format
+#~ msgid "E890: trailing char after ']': %s]%s"
+#~ msgstr "E488: Oorbodige karakters"
-#: ../syntax.c:4531
#, c-format
msgid "E398: Missing '=': %s"
msgstr "E398: Ontbrekende '=': %s"
-#: ../syntax.c:4666
#, c-format
msgid "E399: Not enough arguments: syntax region %s"
msgstr "E399: Nie genoeg parameters nie: sintaksomgewing %s"
-#: ../syntax.c:4870
#, fuzzy
-msgid "E848: Too many syntax clusters"
-msgstr "E391: Geen sodanige sintakskluster nie: %s"
+#~ msgid "E848: Too many syntax clusters"
+#~ msgstr "E391: Geen sodanige sintakskluster nie: %s"
-#: ../syntax.c:4954
msgid "E400: No cluster specified"
msgstr "E400: Geen kluster gespesifiseer nie"
#. end delimiter not found
-#: ../syntax.c:4986
#, c-format
msgid "E401: Pattern delimiter not found: %s"
msgstr "E401: Patroonbegrenser nie gevind nie: %s"
-#: ../syntax.c:5049
#, c-format
msgid "E402: Garbage after pattern: %s"
msgstr "E402: Gemors na patroon: %s"
-#: ../syntax.c:5120
msgid "E403: syntax sync: line continuations pattern specified twice"
msgstr "E403: sintaks sync: relvoortgaanpatroon twee keer gespesifiseer"
-#: ../syntax.c:5169
#, c-format
msgid "E404: Illegal arguments: %s"
msgstr "E404: Ongeldige parameters: %s"
-#: ../syntax.c:5217
#, c-format
msgid "E405: Missing equal sign: %s"
msgstr "E405: Ontbrekende gelykaanteken: %s"
-#: ../syntax.c:5222
#, c-format
msgid "E406: Empty argument: %s"
msgstr "E406: Le parameter: %s"
-#: ../syntax.c:5240
#, c-format
msgid "E407: %s not allowed here"
msgstr "E407: %s nie toegelaat hier nie"
-#: ../syntax.c:5246
#, c-format
msgid "E408: %s must be first in contains list"
msgstr "E408: %s moet vr in 'contains' lys wees"
-#: ../syntax.c:5304
#, c-format
msgid "E409: Unknown group name: %s"
msgstr "E409: Onbekende groepnaam: %s"
-#: ../syntax.c:5512
#, c-format
msgid "E410: Invalid :syntax subcommand: %s"
msgstr "E410: Ongeldige :syntax subbevel %s"
-#: ../syntax.c:5854
-msgid ""
-" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
-msgstr ""
+#~ msgid ""
+#~ " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
+#~ msgstr ""
-#: ../syntax.c:6146
msgid "E679: recursive loop loading syncolor.vim"
msgstr "E679: rekursiewe lus gedurende laai van syncolor.vim"
-#: ../syntax.c:6256
#, c-format
msgid "E411: highlight group not found: %s"
msgstr "E411: uitliggroep nie gevind nie: %s"
-#: ../syntax.c:6278
#, c-format
msgid "E412: Not enough arguments: \":highlight link %s\""
msgstr "E412: Te min parameters: \":highlight link %s\""
-#: ../syntax.c:6284
#, c-format
msgid "E413: Too many arguments: \":highlight link %s\""
msgstr "E413: Te veel parameters: \":highlight link %s\""
-#: ../syntax.c:6302
msgid "E414: group has settings, highlight link ignored"
msgstr ""
"E414: groep het instellings, uitligskakel ('highlight link') gegnoreer"
-#: ../syntax.c:6367
#, c-format
msgid "E415: unexpected equal sign: %s"
msgstr "E415: onverwagte gelykaanteken: %s"
-#: ../syntax.c:6395
#, c-format
msgid "E416: missing equal sign: %s"
msgstr "E416: ontbrekende gelykaanteken: %s"
-#: ../syntax.c:6418
#, c-format
msgid "E417: missing argument: %s"
msgstr "E417: ontbrekende parameter: %s"
-#: ../syntax.c:6446
#, c-format
msgid "E418: Illegal value: %s"
msgstr "E418: Ongeldige waarde: %s"
-#: ../syntax.c:6496
msgid "E419: FG color unknown"
msgstr "E419: FG kleur onbekend"
-#: ../syntax.c:6504
msgid "E420: BG color unknown"
msgstr "E420: BG kleur onbekend"
-#: ../syntax.c:6564
#, c-format
msgid "E421: Color name or number not recognized: %s"
msgstr "E421: Kleurnaam of -nommer nie herken nie: %s"
-#: ../syntax.c:6714
-#, c-format
-msgid "E422: terminal code too long: %s"
-msgstr "E422: terminaalkode te lank: %s"
-
-#: ../syntax.c:6753
#, c-format
msgid "E423: Illegal argument: %s"
msgstr "E423: Ongeldige parameter: %s"
-#: ../syntax.c:6925
msgid "E424: Too many different highlighting attributes in use"
msgstr "E424: Te veel verskillende uitlig-eienskappe in gebruik"
-#: ../syntax.c:7427
msgid "E669: Unprintable character in group name"
msgstr "E669: Onvertoonbare karakter in groepnaam"
-#: ../syntax.c:7434
msgid "W18: Invalid character in group name"
msgstr "W18: Ongeldige karakter groepnaam"
-#: ../syntax.c:7448
-msgid "E849: Too many highlight and syntax groups"
-msgstr ""
+#~ msgid "E849: Too many highlight and syntax groups"
+#~ msgstr ""
-#: ../tag.c:104
msgid "E555: at bottom of tag stack"
msgstr "E555: onderaan etiketstapel"
-#: ../tag.c:105
msgid "E556: at top of tag stack"
msgstr "E556: bo-aan etiketstapel"
-#: ../tag.c:380
msgid "E425: Cannot go before first matching tag"
msgstr "E425: Kan nie vr eerste etiket-treffer gaan nie"
-#: ../tag.c:504
#, c-format
msgid "E426: tag not found: %s"
msgstr "E426: etiket nie gevind nie: %s"
-#: ../tag.c:528
msgid " # pri kind tag"
msgstr " # pri tipe etiket"
-#: ../tag.c:531
msgid "file\n"
msgstr "ler\n"
-#: ../tag.c:829
msgid "E427: There is only one matching tag"
msgstr "E427: Daar is slegs een etiket-treffer"
-#: ../tag.c:831
msgid "E428: Cannot go beyond last matching tag"
msgstr "E428: Kan nie verby laaste etiket-treffer gaan nie"
-#: ../tag.c:850
#, c-format
msgid "File \"%s\" does not exist"
msgstr "Ler \"%s\" bestaan nie"
#. Give an indication of the number of matching tags
-#: ../tag.c:859
#, c-format
msgid "tag %d of %d%s"
msgstr "etiket %d van %d%s"
-#: ../tag.c:862
msgid " or more"
msgstr " of meer"
-#: ../tag.c:864
msgid " Using tag with different case!"
msgstr " Gaan etiket met ander kas gebruik!"
-#: ../tag.c:909
#, c-format
msgid "E429: File \"%s\" does not exist"
msgstr "E429: Ler \"%s\" bestaan nie"
#. Highlight title
-#: ../tag.c:960
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -6054,275 +5387,173 @@ msgstr ""
"\n"
" # NA etiket VAN rel in ler/teks"
-#: ../tag.c:1303
#, c-format
msgid "Searching tags file %s"
msgstr "Deursoek etiketler %s"
-#: ../tag.c:1545
-msgid "Ignoring long line in tags file"
-msgstr ""
+#~ msgid "Ignoring long line in tags file"
+#~ msgstr ""
-#: ../tag.c:1915
#, c-format
msgid "E431: Format error in tags file \"%s\""
msgstr "E431: Formaatfout in etiketler \"%s\""
-#: ../tag.c:1917
#, c-format
msgid "Before byte %<PRId64>"
msgstr "Voor greep %<PRId64>"
-#: ../tag.c:1929
#, c-format
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Etiketler ongesorteer: %s"
#. never opened any tags file
-#: ../tag.c:1960
msgid "E433: No tags file"
msgstr "E433: Geen etiketler nie"
-#: ../tag.c:2536
msgid "E434: Can't find tag pattern"
msgstr "E434: Kan nie etiketpatroon vind nie"
-#: ../tag.c:2544
msgid "E435: Couldn't find tag, just guessing!"
msgstr "E435: Kon nie etiket vind nie, ek raai maar!"
-#: ../tag.c:2797
#, fuzzy, c-format
-msgid "Duplicate field name: %s"
-msgstr "gelaaide fontnaam: %s"
-
-#: ../term.c:1442
-msgid "' not known. Available builtin terminals are:"
-msgstr "' onbekend. Beskikbare ingeboude terminale is:"
-
-#: ../term.c:1463
-msgid "defaulting to '"
-msgstr "gebruik verstek '"
-
-#: ../term.c:1731
-msgid "E557: Cannot open termcap file"
-msgstr "E557: Kan nie 'termcap'-ler oopmaak nie"
-
-#: ../term.c:1735
-msgid "E558: Terminal entry not found in terminfo"
-msgstr "E558: Terminaalinskrywing nie in 'terminfo' gevind nie"
-
-#: ../term.c:1737
-msgid "E559: Terminal entry not found in termcap"
-msgstr "E559: Terminaalinskrywing nie in 'termcap' gevind nie"
-
-#: ../term.c:1878
-#, c-format
-msgid "E436: No \"%s\" entry in termcap"
-msgstr "E436: Geen \"%s\" inskrywing in termcap nie"
-
-#: ../term.c:2249
-msgid "E437: terminal capability \"cm\" required"
-msgstr "E437: terminaalvermo \"cm\" vereis"
-
-#. Highlight title
-#: ../term.c:4376
-msgid ""
-"\n"
-"--- Terminal keys ---"
-msgstr ""
-"\n"
-"--- Terminaal sleutels ---"
-
-#: ../ui.c:481
-msgid "Vim: Error reading input, exiting...\n"
-msgstr "Vim: Fout met lees van invoer, verlaat...\n"
+#~ msgid "Duplicate field name: %s"
+#~ msgstr "gelaaide fontnaam: %s"
#. This happens when the FileChangedRO autocommand changes the
#. * file in a way it becomes shorter.
-#: ../undo.c:379
-msgid "E881: Line count changed unexpectedly"
-msgstr ""
+#~ msgid "E881: Line count changed unexpectedly"
+#~ msgstr ""
-#: ../undo.c:627
#, fuzzy, c-format
-msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: Kan ler nie oopmaak vir skryf nie"
+#~ msgid "E828: Cannot open undo file for writing: %s"
+#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie"
+
+#, fuzzy, c-format
+#~ msgid "E5003: Unable to create directory \"%s\" for undo file: %s"
+#~ msgstr "E346: Geen gids \"%s\" meer gevind in 'cdpath' nie"
-#: ../undo.c:717
#, c-format
-msgid "E825: Corrupted undo file (%s): %s"
-msgstr ""
+#~ msgid "E825: Corrupted undo file (%s): %s"
+#~ msgstr ""
-#: ../undo.c:1039
-msgid "Cannot write undo file in any directory in 'undodir'"
-msgstr ""
+#~ msgid "Cannot write undo file in any directory in 'undodir'"
+#~ msgstr ""
-#: ../undo.c:1074
#, c-format
-msgid "Will not overwrite with undo file, cannot read: %s"
-msgstr ""
+#~ msgid "Will not overwrite with undo file, cannot read: %s"
+#~ msgstr ""
-#: ../undo.c:1092
#, c-format
-msgid "Will not overwrite, this is not an undo file: %s"
-msgstr ""
+#~ msgid "Will not overwrite, this is not an undo file: %s"
+#~ msgstr ""
-#: ../undo.c:1108
-msgid "Skipping undo file write, nothing to undo"
-msgstr ""
+#~ msgid "Skipping undo file write, nothing to undo"
+#~ msgstr ""
-#: ../undo.c:1121
#, fuzzy, c-format
-msgid "Writing undo file: %s"
-msgstr "Besig om viminfo ler \"%s\" te stoor"
+#~ msgid "Writing undo file: %s"
+#~ msgstr "Besig om viminfo ler \"%s\" te stoor"
-#: ../undo.c:1213
#, fuzzy, c-format
-msgid "E829: write error in undo file: %s"
-msgstr "E297: Skryffout in ruiller"
+#~ msgid "E829: write error in undo file: %s"
+#~ msgstr "E297: Skryffout in ruiller"
-#: ../undo.c:1280
#, c-format
-msgid "Not reading undo file, owner differs: %s"
-msgstr ""
+#~ msgid "Not reading undo file, owner differs: %s"
+#~ msgstr ""
-#: ../undo.c:1292
#, fuzzy, c-format
-msgid "Reading undo file: %s"
-msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees"
+#~ msgid "Reading undo file: %s"
+#~ msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees"
-#: ../undo.c:1299
#, fuzzy, c-format
-msgid "E822: Cannot open undo file for reading: %s"
-msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie"
+#~ msgid "E822: Cannot open undo file for reading: %s"
+#~ msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie"
-#: ../undo.c:1308
#, fuzzy, c-format
-msgid "E823: Not an undo file: %s"
-msgstr "E484: Kan nie ler %s oopmaak nie"
+#~ msgid "E823: Not an undo file: %s"
+#~ msgstr "E484: Kan nie ler %s oopmaak nie"
-#: ../undo.c:1313
#, fuzzy, c-format
-msgid "E824: Incompatible undo file: %s"
-msgstr "E484: Kan nie ler %s oopmaak nie"
+#~ msgid "E824: Incompatible undo file: %s"
+#~ msgstr "E484: Kan nie ler %s oopmaak nie"
-#: ../undo.c:1328
-msgid "File contents changed, cannot use undo info"
-msgstr ""
+#~ msgid "File contents changed, cannot use undo info"
+#~ msgstr ""
-#: ../undo.c:1497
#, fuzzy, c-format
-msgid "Finished reading undo file %s"
-msgstr "%s klaar uitgevoer"
+#~ msgid "Finished reading undo file %s"
+#~ msgstr "%s klaar uitgevoer"
-#: ../undo.c:1586 ../undo.c:1812
-msgid "Already at oldest change"
-msgstr ""
+#~ msgid "Already at oldest change"
+#~ msgstr ""
-#: ../undo.c:1597 ../undo.c:1814
-msgid "Already at newest change"
-msgstr ""
+#~ msgid "Already at newest change"
+#~ msgstr ""
-#: ../undo.c:1806
#, fuzzy, c-format
-msgid "E830: Undo number %<PRId64> not found"
-msgstr "E92: buffer %<PRId64> kon nie gevind word nie"
+#~ msgid "E830: Undo number %<PRId64> not found"
+#~ msgstr "E92: buffer %<PRId64> kon nie gevind word nie"
-#: ../undo.c:1979
msgid "E438: u_undo: line numbers wrong"
msgstr "E438: u_undo: relnommers foutief"
-#: ../undo.c:2183
#, fuzzy
-msgid "more line"
-msgstr "1 rel meer"
+#~ msgid "more line"
+#~ msgstr "1 rel meer"
-#: ../undo.c:2185
#, fuzzy
-msgid "more lines"
-msgstr "1 rel meer"
+#~ msgid "more lines"
+#~ msgstr "1 rel meer"
-#: ../undo.c:2187
#, fuzzy
-msgid "line less"
-msgstr "1 rel minder"
+#~ msgid "line less"
+#~ msgstr "1 rel minder"
-#: ../undo.c:2189
#, fuzzy
-msgid "fewer lines"
-msgstr "%<PRId64> minder rels"
+#~ msgid "fewer lines"
+#~ msgstr "%<PRId64> minder rels"
-#: ../undo.c:2193
#, fuzzy
-msgid "change"
-msgstr "1 verandering"
+#~ msgid "change"
+#~ msgstr "1 verandering"
-#: ../undo.c:2195
#, fuzzy
-msgid "changes"
-msgstr "1 verandering"
+#~ msgid "changes"
+#~ msgstr "1 verandering"
-#: ../undo.c:2225
#, fuzzy, c-format
-msgid "%<PRId64> %s; %s #%<PRId64> %s"
-msgstr "%<PRId64>R, %<PRId64>K"
+#~ msgid "%<PRId64> %s; %s #%<PRId64> %s"
+#~ msgstr "%<PRId64>R, %<PRId64>K"
-#: ../undo.c:2228
-msgid "before"
-msgstr ""
+#~ msgid "after"
+#~ msgstr ""
-#: ../undo.c:2228
-msgid "after"
-msgstr ""
+#~ msgid "before"
+#~ msgstr ""
-#: ../undo.c:2325
#, fuzzy
-msgid "Nothing to undo"
-msgstr "Geen binding gevind nie"
+#~ msgid "Nothing to undo"
+#~ msgstr "Geen binding gevind nie"
-#: ../undo.c:2330
-msgid "number changes when saved"
-msgstr ""
+#~ msgid "number changes when saved"
+#~ msgstr ""
-#: ../undo.c:2360
#, fuzzy, c-format
-msgid "%<PRId64> seconds ago"
-msgstr "%<PRId64> Kolomme; "
+#~ msgid "%<PRId64> seconds ago"
+#~ msgstr "%<PRId64> Kolomme; "
-#: ../undo.c:2372
#, fuzzy
-msgid "E790: undojoin is not allowed after undo"
-msgstr "E407: %s nie toegelaat hier nie"
+#~ msgid "E790: undojoin is not allowed after undo"
+#~ msgstr "E407: %s nie toegelaat hier nie"
-#: ../undo.c:2466
msgid "E439: undo list corrupt"
msgstr "E439: herstellys korrup"
-#: ../undo.c:2495
msgid "E440: undo line missing"
msgstr "E440: herstelrel ontbreek"
-#: ../version.c:600
-msgid ""
-"\n"
-"Included patches: "
-msgstr ""
-"\n"
-"Ingeslote laslappies:"
-
-#: ../version.c:627
-#, fuzzy
-msgid ""
-"\n"
-"Extra patches: "
-msgstr "Eksterne subtreffers:\n"
-
-#: ../version.c:639 ../version.c:864
-msgid "Modified by "
-msgstr "Gewysig deur "
-
-#: ../version.c:646
msgid ""
"\n"
"Compiled "
@@ -6330,933 +5561,919 @@ msgstr ""
"\n"
"Gekompileer op "
-#: ../version.c:649
msgid "by "
msgstr "deur "
-#: ../version.c:660
-msgid ""
-"\n"
-"Huge version "
-msgstr ""
-"\n"
-"Enorme weergawe "
-
-#: ../version.c:661
-msgid "without GUI."
-msgstr "sonder GUI."
-
-#: ../version.c:662
-msgid " Features included (+) or not (-):\n"
-msgstr " Kenmerke in- (+) of uitgesluit (-):\n"
+#~ msgid ""
+#~ "\n"
+#~ "\n"
+#~ "Features: "
+#~ msgstr ""
-#: ../version.c:667
msgid " system vimrc file: \""
msgstr " stelsel vimrc-ler: \""
-#: ../version.c:672
-msgid " user vimrc file: \""
-msgstr " gebruiker vimrc-ler: \""
-
-#: ../version.c:677
-msgid " 2nd user vimrc file: \""
-msgstr " 2de gebruiker vimrc-ler \""
-
-#: ../version.c:682
-msgid " 3rd user vimrc file: \""
-msgstr " 3de gebruiker vimrc-ler \""
-
-#: ../version.c:687
-msgid " user exrc file: \""
-msgstr " gebruiker exrc-ler: \""
-
-#: ../version.c:692
-msgid " 2nd user exrc file: \""
-msgstr " 2de gebruiker exrc-ler: \""
-
-#: ../version.c:699
msgid " fall-back for $VIM: \""
msgstr " bystand vir $VIM: \""
-#: ../version.c:705
msgid " f-b for $VIMRUNTIME: \""
msgstr " bystand vir $VIMRUNTIME: \""
-#: ../version.c:709
-msgid "Compilation: "
-msgstr "Kompilering: "
-
-#: ../version.c:712
-msgid "Linking: "
-msgstr "Koppeling: "
+#, fuzzy
+#~ msgid "Nvim is open source and freely distributable"
+#~ msgstr "Vim is vryekode, en vrylik verspreibaar"
-#: ../version.c:717
-msgid " DEBUG BUILD"
-msgstr " ONTFOUTINGS-KOMPILERING"
+#~ msgid "https://neovim.io/community"
+#~ msgstr ""
-#: ../version.c:767
-msgid "VIM - Vi IMproved"
-msgstr "VIM - Vi Met skop"
+#, fuzzy
+#~ msgid "type :help nvim<Enter> if you are new! "
+#~ msgstr "tik :help uganda<Enter> as jy hou van Vim "
-# njj: :))
-#: ../version.c:769
-msgid "version "
-msgstr "Weergawe "
+#, fuzzy
+#~ msgid "type :checkhealth<Enter> to optimize Nvim"
+#~ msgstr "Tik :quit<Enter> om Vim te verlaat"
-#: ../version.c:770
-msgid "by Bram Moolenaar et al."
-msgstr "deur Bram Moolenaar et al."
+msgid "type :q<Enter> to exit "
+msgstr "tik :q<Enter> om program verlaat "
-#: ../version.c:774
-msgid "Vim is open source and freely distributable"
-msgstr "Vim is vryekode, en vrylik verspreibaar"
+#, fuzzy
+#~ msgid "type :help<Enter> for help "
+#~ msgstr "tik :q<Enter> om program verlaat "
-#: ../version.c:776
msgid "Help poor children in Uganda!"
msgstr "Help arm kinders in Uganda!"
-#: ../version.c:777
msgid "type :help iccf<Enter> for information "
msgstr "tik :help iccf<Enter> vir meer inligting hieroor "
-#: ../version.c:779
-msgid "type :q<Enter> to exit "
-msgstr "tik :q<Enter> om program verlaat "
-
-#: ../version.c:780
-msgid "type :help<Enter> or <F1> for on-line help"
-msgstr "tik :help<Enter> of <F1> vir aanlyn hulp "
-
-#: ../version.c:781
-msgid "type :help version7<Enter> for version info"
-msgstr "tik :help version7<Enter> vir weergawe-inligting"
-
-#: ../version.c:784
-msgid "Running in Vi compatible mode"
-msgstr "Voer tans uit in Vi-versoenbare modus"
-
-#: ../version.c:785
-msgid "type :set nocp<Enter> for Vim defaults"
-msgstr "tik :set nocp<Enter> vir Vim verstekwaardes "
-
-#: ../version.c:786
-msgid "type :help cp-default<Enter> for info on this"
-msgstr "tik :help cp-default<Enter> vir meer inligting hieroor"
-
-#: ../version.c:827
msgid "Sponsor Vim development!"
msgstr "Borg Vim ontwikkeling!"
-#: ../version.c:828
msgid "Become a registered Vim user!"
msgstr "Word 'n geregistreerde Vim gebruiker!"
-#: ../version.c:831
msgid "type :help sponsor<Enter> for information "
msgstr "tik :help sponsor<Enter> vir meer inligting hieroor "
-#: ../version.c:832
msgid "type :help register<Enter> for information "
msgstr "tik :help register<Enter> vir meer inligting hieroor "
-#: ../version.c:834
msgid "menu Help->Sponsor/Register for information "
msgstr "menu Hulp->Borg/Registreer vir meer inligting"
-#: ../window.c:119
msgid "Already only one window"
msgstr "Daar is alreeds slegs een venster"
-#: ../window.c:224
msgid "E441: There is no preview window"
msgstr "E441: Daar is nie 'n voorskou-venster nie"
-#: ../window.c:559
msgid "E442: Can't split topleft and botright at the same time"
msgstr "E442: Kan nie bo-links en onder-regs terselfdertyd verdeel nie"
-#: ../window.c:1228
msgid "E443: Cannot rotate when another window is split"
msgstr "E443: Kan nie roteer terwyl 'n ander venster verdeel is nie"
-#: ../window.c:1803
msgid "E444: Cannot close last window"
msgstr "E444: Kan nie laaste venster toemaak nie"
-#: ../window.c:1810
#, fuzzy
-msgid "E813: Cannot close autocmd window"
-msgstr "E444: Kan nie laaste venster toemaak nie"
+#~ msgid "E813: Cannot close autocmd window"
+#~ msgstr "E444: Kan nie laaste venster toemaak nie"
-#: ../window.c:1814
#, fuzzy
-msgid "E814: Cannot close window, only autocmd window would remain"
-msgstr "E444: Kan nie laaste venster toemaak nie"
+#~ msgid "E814: Cannot close window, only autocmd window would remain"
+#~ msgstr "E444: Kan nie laaste venster toemaak nie"
-#: ../window.c:2717
msgid "E445: Other window contains changes"
msgstr "E445: Die ander venster bevat veranderinge"
-#: ../window.c:4805
msgid "E446: No file name under cursor"
msgstr "E446: Geen lernaam onder loper"
-#~ msgid "[Error List]"
-#~ msgstr "[Foutlys]"
-
-#~ msgid "[No File]"
-#~ msgstr "[Geen ler]"
+#, c-format
+#~ msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
+#~ msgstr ""
-#~ msgid "Patch file"
-#~ msgstr "Laslap ler"
+#, fuzzy, c-format
+#~ msgid "E801: ID already taken: %<PRId64>"
+#~ msgstr "E157: Ongeldige teken ID: %<PRId64>"
-#~ msgid "E106: Unknown variable: \"%s\""
-#~ msgstr "E106: Onbekende veranderlike: \"%s\""
+#, fuzzy
+#~ msgid "List or number required"
+#~ msgstr "E471: Parameter benodig"
-#~ msgid ""
-#~ "&OK\n"
-#~ "&Cancel"
+#, c-format
+#~ msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
#~ msgstr ""
-#~ "&OK\n"
-#~ "&Kanselleer"
-#~ msgid "E240: No connection to Vim server"
-#~ msgstr "E240: Geen verbinding met Vim bediener"
-
-#~ msgid "E277: Unable to read a server reply"
-#~ msgstr "E277: Kon bediener-terugvoer nie lees nie"
+#, fuzzy, c-format
+#~ msgid "E803: ID not found: %<PRId64>"
+#~ msgstr "E320: Kan nie rel %<PRId64> vind nie"
-#~ msgid "E241: Unable to send to %s"
-#~ msgstr "E241: Kan nie na %s stuur nie"
+#~ msgid "WARNING: tag command changed a buffer!!!"
+#~ msgstr "WAARSKUWING: etiketbevel het buffer verander!!!"
-#~ msgid "E130: Undefined function: %s"
-#~ msgstr "E130: Ongedefinieerde funksie: %s"
+#~ msgid "Leave: %s"
+#~ msgstr "Verlaat: %s"
-#~ msgid "Save As"
-#~ msgstr "Stoor As"
+#~ msgid "File name '%s' is valid"
+#~ msgstr "lernaam '%s is ongeldig"
-#~ msgid "Source Vim script"
-#~ msgstr "Voer Vim skrip uit"
+#~ msgid "Not a proper file name: '%s'"
+#~ msgstr "Nie 'n geldige lernaam nie: '%s'"
-#~ msgid "Edit File"
-#~ msgstr "Verander ler"
+#~ msgid "Change dir debugging enabled."
+#~ msgstr "Verandergids ontfouting in staat gestel"
-#~ msgid " (NOT FOUND)"
-#~ msgstr " (NIE GEVIND NIE)"
+#~ msgid "Warning: %s option changed from modeline"
+#~ msgstr "Waarskuwing: %s opsie verander vanaf moduslyn"
-#~ msgid "Edit File in new window"
-#~ msgstr "Bewerk ler in nuwe venster"
+#~ msgid "Security error: shell command output is a symbolic link"
+#~ msgstr "Sekuriteitsfout: Dop-bevel afvoer is 'n simboliese skakel"
-#~ msgid "Append File"
-#~ msgstr "Las aan by ler"
+#~ msgid "No fold at this line"
+#~ msgstr "Geen vou by hierdie rel nie"
-#~ msgid "Window position: X %d, Y %d"
-#~ msgstr "Vensterposisie: X %d, Y %d"
+#~ msgid "Fold must be at least two lines"
+#~ msgstr "'n Vou moet ten minste 2 rels wees"
-#~ msgid "Save Redirection"
-#~ msgstr "Stoor Herversturing"
+#~ msgid "Security error: filter input is a symbolic link: %s"
+#~ msgstr "Sekuriteitsfout: filter invoer is 'n simboliese skakel"
-#~ msgid "Save View"
-#~ msgstr "Stoor Oorsig"
+#~ msgid "Security error: 'charconvert' output is a symbolic link"
+#~ msgstr "Sekuriteitsfout: 'charconvert' afvoer is 'n simboliese skakel"
-#~ msgid "Save Session"
-#~ msgstr "Stoor Sessie"
+#~ msgid "Security error: filter output is a symbolic link: %s"
+#~ msgstr "Sekuriteitsfout: filter afvoer is 'n simboliese skakel"
-#~ msgid "Save Setup"
-#~ msgstr "Stoor konfigurasie"
+#~ msgid "makeef option not set"
+#~ msgstr "'makeef' opsie nie aan nie"
-#~ msgid "E196: No digraphs in this version"
-#~ msgstr "E196: Geen digrawe in hierdie weergawe nie"
+#~ msgid "line ~%<PRId64>: %s"
+#~ msgstr "rel ~%<PRId64>: %s"
-#~ msgid "[NL found]"
-#~ msgstr "[NL gevind]"
+#~ msgid "Security error: new viminfo file is a symbolic link"
+#~ msgstr "Sekuriteitsfout: nuwe viminfo ler is a simboliese skakel"
-#~ msgid "[crypted]"
-#~ msgstr "[gekodeer]"
+#~ msgid " PPC has a much better architecture. "
+#~ msgstr " PPC het 'n veel beter argitektuur. "
-#~ msgid "[CONVERSION ERROR]"
-#~ msgstr "[OMSETTINGSFOUT]"
+#~ msgid " WARNING: Intel CPU detected. "
+#~ msgstr " WAARSKUWING: Intel SVE bespeur. "
-#~ msgid "NetBeans disallows writes of unmodified buffers"
-#~ msgstr "NetBeans laat nie skryf toe van onveranderde buffers nie"
+#~ msgid "Unexpected magic character; check META."
+#~ msgstr "Onverwagte toorkarakter; kyk na META."
-#~ msgid "Partial writes disallowed for NetBeans buffers"
-#~ msgstr "Gedeeltelike skryf word nie toegelaat vir NetBeans buffers nie"
+#~ msgid "\\* follows nothing"
+#~ msgstr "\\* volg niks"
-#~ msgid "E460: The resource fork would be lost (add ! to override)"
-#~ msgstr "E460: Die hulpbronvurk sal verlore gaan (gebruik ! om te dwing)"
+#~ msgid "\\{ follows nothing"
+#~ msgstr "\\{ volg niks"
-#~ msgid "<cannot open> "
-#~ msgstr "<kan nie oopmaak nie> "
+#~ msgid "\\@ follows nothing"
+#~ msgstr "\\@ volg niks"
-#~ msgid "E616: vim_SelFile: can't get font %s"
-#~ msgstr "E616: 'vim_SelFile': kan font %s nie kry nie"
+#~ msgid "\\+ follows nothing"
+#~ msgstr "\\+ volg niks"
-#~ msgid "E614: vim_SelFile: can't return to current directory"
-#~ msgstr "E614: 'vim_SelFile': Kan nie terugkeer na huidige gids nie"
+#~ msgid "\\= follows nothing"
+#~ msgstr "\\= volg niks"
-#~ msgid "Pathname:"
-#~ msgstr "Gidsnaam:"
+#~ msgid "Nested *, \\=, \\+, \\! or \\{"
+#~ msgstr "Geneste *, \\=, \\+, \\! of \\{"
-#~ msgid "E615: vim_SelFile: can't get current directory"
-#~ msgstr "E615: vim_SelFile: Kan nie huidige gids verkry nie"
+#~ msgid "Unmatched \\("
+#~ msgstr "Onpaar \\("
-#~ msgid "OK"
-#~ msgstr "OK"
+#~ msgid "Too many \\("
+#~ msgstr "Te veel \\("
-#~ msgid "Cancel"
-#~ msgstr "Kanselleer"
+#~ msgid "Ambiguous mapping, conflicts with \"%s\""
+#~ msgstr "Dubbelsinnige binding, bots met \"%s\""
-#~ msgid "Vim dialog"
-#~ msgstr "Vim dialooghokkie"
+#~ msgid "Ambiguous mapping"
+#~ msgstr "Dubbelsinnige binding"
-#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
-#~ msgstr ""
-#~ "Rolstaafelement: Kon nie pikselmatriks-duimnael se geometrie kry nie"
+#~ msgid "Command too long"
+#~ msgstr "Bevel te lank"
-#~ msgid "E232: Cannot create BalloonEval with both message and callback"
-#~ msgstr "E232: Kan nie BalloonEval skep met beide boodskap en terugroep nie"
+#~ msgid "GUI is not running"
+#~ msgstr "GUI voer nie uit nie"
-#~ msgid "E229: Cannot start the GUI"
-#~ msgstr "E229: Kan nie die GUI begin nie"
+#~ msgid "Cannot clear all highlight groups"
+#~ msgstr "Kan nie alle uitliggroepe leegmaak nie"
-#~ msgid "E230: Cannot read from \"%s\""
-#~ msgstr "E230: Kan nie lees uit \"%s\" nie"
+#~ msgid "Library call failed"
+#~ msgstr "Biblioteekfunksieroep het gefaal"
-#~ msgid "E665: Cannot start GUI, no valid font found"
-#~ msgstr "E665: Kan nie GUI begin nie, geen geldige font gevind nie"
+#~ msgid "some"
+#~ msgstr "sommige"
-#~ msgid "E231: 'guifontwide' invalid"
-#~ msgstr "E231: 'guifontwide' ongeldig"
+#~ msgid "deadly signal"
+#~ msgstr "dodelike sein"
-#~ msgid "E599: Value of 'imactivatekey' is invalid"
-#~ msgstr "E599: Waarde van 'imactivatekey' is ongeldig"
+#~ msgid "Unsupported screen mode"
+#~ msgstr "Ongesteunde skermmodus"
-#~ msgid "E254: Cannot allocate color %s"
-#~ msgstr "E254: Kan nie kleur %s toeken nie"
+#~ msgid "PC (16 bits Vim)"
+#~ msgstr "PC (16 bisse Vim)"
-#~ msgid "Vim dialog..."
-#~ msgstr "Vim dialooghokkie..."
+#~ msgid "PC (32 bits Vim)"
+#~ msgstr "PC (32 bisse Vim)"
-#~ msgid "Input _Methods"
-#~ msgstr "Invoer _Metodes"
+#~ msgid "Out of memory"
+#~ msgstr "Geheue op"
-#~ msgid "VIM - Search and Replace..."
-#~ msgstr "VIM - Soek en Vervang..."
+#~ msgid "Sorry, deleting a menu is not possible in the Athena version"
+#~ msgstr ""
+#~ "Jammer, in die Athena weergawe is dit onmoontlik om 'n kieslys te skrap"
-#~ msgid "VIM - Search..."
-#~ msgstr "VIM - Soek..."
+#~ msgid "Can't create input context."
+#~ msgstr "Kan nie invoerkonteks skep nie."
-#~ msgid "Find what:"
-#~ msgstr "Soek na:"
+#~ msgid "Unrecognized sniff request [%s]"
+#~ msgstr "Onbekende sniff versoek [%s]"
-#~ msgid "Replace with:"
-#~ msgstr "Vervang met:"
+#~ msgid "-- SNiFF+ commands --"
+#~ msgstr "-- SNiFF+ bevele --"
-#~ msgid "Match whole word only"
-#~ msgstr "Tref slegs presiese woord"
+#~ msgid "Retrieve next symbol"
+#~ msgstr "Kry volgende simbool"
-#~ msgid "Match case"
-#~ msgstr "Tref kas"
+#~ msgid "cs_add_common: alloc fail #4"
+#~ msgstr "'cs_add_common': toeken onsuksesvol #4"
-#~ msgid "Direction"
-#~ msgstr "Rigting"
+#~ msgid "cs_add_common: alloc fail #3"
+#~ msgstr "'cs_add_common': toeken onsuksesvol #3"
-#~ msgid "Up"
-#~ msgstr "Op"
+#~ msgid "cs_add_common: alloc fail #2"
+#~ msgstr "'cs_add_common': toeken onsuksesvol #2"
-#~ msgid "Down"
-#~ msgstr "Af"
+#~ msgid "cs_add_common: alloc fail #1"
+#~ msgstr "'cs_add_common': toeken onsuksesvol #1"
-#~ msgid "Find Next"
-#~ msgstr "Vind volgende"
+#~ msgid "automata ERROR: internal"
+#~ msgstr "automata FOUT: intern"
-#~ msgid "Replace"
-#~ msgstr "Vervang"
+#~ msgid "Your language Font missing"
+#~ msgstr "Jou taal Font ontbreek"
-#~ msgid "Replace All"
-#~ msgstr "Vervang alles"
+#~ msgid "fontset name: %s"
+#~ msgstr "fontstel naam: %s"
-#~ msgid "Vim: Received \"die\" request from session manager\n"
-#~ msgstr "Vim: Het die \"die\" opdrag ontvang van sessiebestuurder\n"
+#~ msgid " sh : export LANG=ko"
+#~ msgstr " sh: export LANG=af"
-#~ msgid "Vim: Main window unexpectedly destroyed\n"
-#~ msgstr "Vim: Hoofvenster onverwags verwoes\n"
+#~ msgid " csh: setenv LANG ko"
+#~ msgstr " csh: setenv LANG af"
-#~ msgid "Font Selection"
-#~ msgstr "Fontkeuse"
+#~ msgid "For korean:"
+#~ msgstr "Vir Afrikaans:"
-#~ msgid "Used CUT_BUFFER0 instead of empty selection"
-#~ msgstr "'CUT_BUFFER0' is gebruik in plaas van le seleksie"
+#~ msgid "locale is not set correctly"
+#~ msgstr "lokaal is nie korrek gestel nie"
-#~ msgid "Filter"
-#~ msgstr "Filter"
+#~ msgid "Error: During loading fontset %s"
+#~ msgstr "Fout: Gedurende die laai van fontstel %s"
-#~ msgid "Directories"
-#~ msgstr "Gidse"
+#~ msgid "Topic:"
+#~ msgstr "Onderwerp:"
-#~ msgid "Help"
-#~ msgstr "Hulp"
+#~ msgid "VIM - Help on..."
+#~ msgstr "VIM - Hulp met.."
-#~ msgid "Files"
-#~ msgstr "Lers"
+#~ msgid "Cannot use :normal from event handler"
+#~ msgstr "Kan ':normal' nie vanuit gebeurtenishanteerder gebruik nie"
-#~ msgid "Selection"
-#~ msgstr "Seleksie"
+#~ msgid "Invalid line number: %<PRId64>"
+#~ msgstr "Ongeldige relnommer: %<PRId64>"
-#~ msgid "Undo"
-#~ msgstr "Herroep"
+#~ msgid "Missing filename"
+#~ msgstr "Ontbrekende lernaam"
-#~ msgid "E671: Cannot find window title \"%s\""
-#~ msgstr "E671: Kan nie venster titel vind nie \"%s\""
+#~ msgid "No servers found for this display"
+#~ msgstr "Geen bedieners gevind vir die 'display' nie"
-#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
-#~ msgstr "E243: Parameter nie bekend: \"-%s\"; Gebruik die OLE weergawe."
+#~ msgid "E258: no matches found in cscope connections"
+#~ msgstr "E258: geen treffers gevind in 'cscope' verbindings nie"
-#~ msgid "E672: Unable to open window inside MDI application"
-#~ msgstr "E672: Kon nie venster oopmaak binne 'n MDI toepassing nie"
+#~ msgid "Binary tag search"
+#~ msgstr "Binre etiketsoek"
-#~ msgid "Find string (use '\\\\' to find a '\\')"
-#~ msgstr "Vind string (gebruik '\\\\' om 'n '\\' te vind"
+#~ msgid "Linear tag search"
+#~ msgstr "Linire etiketsoek"
-#~ msgid "Find & Replace (use '\\\\' to find a '\\')"
-#~ msgstr "Vind & vervang string (gebruik '\\\\' om 'n '\\' te vind"
+#~ msgid " LINE"
+#~ msgstr " REL"
-#~ msgid ""
-#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
-#~ msgstr ""
-#~ "Vim E458: Kan nie kleurkaart-inskrywing toeken nie, sommige kleure mag "
-#~ "verkeerd wees"
+#~ msgid " BLOCK"
+#~ msgstr " BLOK"
-#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:"
-#~ msgstr ""
-#~ "E250: Fonte vir die volgende karakterstelle ontbreek in fontversameling "
-#~ "%s:"
+#~ msgid "%<PRId64> lines ~ed"
+#~ msgstr "%<PRId64> rels ge-~"
-#~ msgid "E252: Fontset name: %s"
-#~ msgstr "E252: Fontstel naam: %s"
+#~ msgid "1 line ~ed"
+#~ msgstr "1 rel ge-~"
-#~ msgid "Font '%s' is not fixed-width"
-#~ msgstr "Font '%s' is nie 'n vaste-wydte font nie"
+#~ msgid "--help\t\tShow Gnome arguments"
+#~ msgstr "--help\t\tWys Gnome parameters"
-#~ msgid "E253: Fontset name: %s\n"
-#~ msgstr "E253: Fonstel naam: %s\n"
+#~ msgid "\"\n"
+#~ msgstr "\"\n"
-#~ msgid "Font0: %s\n"
-#~ msgstr "Font0: %s\n"
+#~ msgid "E249: couldn't read VIM instance registry property"
+#~ msgstr "E249: kon nie VIM instansie register-kenmerk lees nie"
-#~ msgid "Font1: %s\n"
-#~ msgstr "Font1: %s\n"
+#~ msgid "%2d %-5ld %-34s <none>\n"
+#~ msgstr "%2d %-5ld %-34s <geen>\n"
-#~ msgid "Font%<PRId64> width is not twice that of font0\n"
-#~ msgstr "Font%<PRId64> wydte is nie twee keer de van font0 nie\n"
+# njj: dalk 'verbinding' ipv 'verbinding' orals?
+#~ msgid "couldn't malloc\n"
+#~ msgstr "kon nie 'malloc' nie\n"
-#~ msgid "Font0 width: %<PRId64>\n"
-#~ msgstr "Font0 wydte: %<PRId64>\n"
+#~ msgid "E260: cscope connection not found"
+#~ msgstr "E260: 'cscope' verbinding nie gevind nie"
-#~ msgid ""
-#~ "Font1 width: %<PRId64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "Font1 wydte: %<PRId64>\n"
-#~ "\n"
+#~ msgid "error reading cscope connection %d"
+#~ msgstr "'cscope' verbinding %d kon nie gelees word nie"
-#~ msgid "E256: Hangul automata ERROR"
-#~ msgstr "E256: Hangul outomatiserings FOUT"
+#~ msgid "Run Macro"
+#~ msgstr "Voer Makro uit"
-#~ msgid "E563: stat error"
-#~ msgstr "E563: 'stat' fout"
+#~ msgid "function "
+#~ msgstr "funksie "
-#~ msgid "E625: cannot open cscope database: %s"
-#~ msgstr "E625: Kon nie 'cscope' databasis oopmaak nie: %s"
+#~ msgid "E463: Region is guarded, cannot modify"
+#~ msgstr "E463: Omgewing is onder bewaking, kan nie verander nie"
-#~ msgid "E626: cannot get cscope database information"
-#~ msgstr "E626: kan nie 'cscope' databasisinligting kry nie"
+#~ msgid "E233: cannot open display"
+#~ msgstr "E233: kan nie vertoonskerm oopmaak nie"
-#~ msgid "E569: maximum number of cscope connections reached"
-#~ msgstr "E569: maksimum aantal 'cscope' verbindings bereik"
+#~ msgid "E247: no registered server named \"%s\""
+#~ msgstr "E247: geen geregistreerde bediener genaamd \"%s\""
-#~ msgid ""
-#~ "E263: Sorry, this command is disabled, the Python library could not be "
-#~ "loaded."
+#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
#~ msgstr ""
-#~ "E263: Jammer, hierdie bevel is afgeskakel, die Python biblioteek ler kon "
-#~ "nie gelaai word nie."
+#~ "E800: Arabies kan nie gebruik word nie: Nie tydens kompilering gekies "
+#~ "nie\n"
-#~ msgid "E659: Cannot invoke Python recursively"
-#~ msgstr "E659: Kan nie Python rekursief roep nie"
+#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
+#~ msgstr ""
+#~ "E27: Farsi kan nie gebruik word nie: Nie tydens kompilering gekies nie\n"
-#~ msgid "can't delete OutputObject attributes"
-#~ msgstr "kan nie 'OutputObject' eienskappe skrap nie"
+#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
+#~ msgstr ""
+#~ "E26: Hebreeus kan nie gebruik word nie: Nie tydens kompilering gekies "
+#~ "nie\n"
-#~ msgid "softspace must be an integer"
-#~ msgstr "'softspace' moet 'n heelgetal wees"
+#~ msgid "E448: Could not load library function %s"
+#~ msgstr "E448: Kon nie biblioteek funksie laai nie %s"
-#~ msgid "invalid attribute"
-#~ msgstr "ongeldige eienskap"
+#~ msgid "E234: Unknown fontset: %s"
+#~ msgstr "E234: Onbekende fontstel: %s"
-#~ msgid "writelines() requires list of strings"
-#~ msgstr "'writelines()' benodig 'n lys van stringe"
+#~ msgid "Path length too long!"
+#~ msgstr "Pad-lengte te lank"
-#~ msgid "E264: Python: Error initialising I/O objects"
-#~ msgstr "E264: Python: Kon nie I/O objekte inwy nie"
+#~ msgid "gvimext.dll error"
+#~ msgstr "'gvimext.dll' fout"
-# njj: net 'n voorstel ..
-#~ msgid "invalid expression"
-#~ msgstr "ongeldige uitdrukking"
+#~ msgid "Error creating process: Check if gvim is in your path!"
+#~ msgstr "FOut met die skep van proses: Kyk of gvim in jou pad is!"
-#~ msgid "expressions disabled at compile time"
-#~ msgstr "uitdrukkings afgeskakel tydens kompilering"
+#~ msgid "Edits the selected file(s) with Vim"
+#~ msgstr "Wysig die gekose ler(s) met Vim"
-#~ msgid "attempt to refer to deleted buffer"
-#~ msgstr "poging om na 'n geskrapte buffer te verwys"
+#~ msgid "Edit with existing Vim - &"
+#~ msgstr "Wysig met bestaande Vim - &"
-#~ msgid "line number out of range"
-#~ msgstr "relnommer buite omvang"
+#~ msgid "Edit with &Vim"
+#~ msgstr "Wysig met &Vim"
-#~ msgid "<buffer object (deleted) at %8lX>"
-#~ msgstr "<buffervoorwerp (geskrap) by %8lX>"
+#~ msgid "&Diff with Vim"
+#~ msgstr "Wys verskille ('&diff') met Vim"
-#~ msgid "invalid mark name"
-#~ msgstr "onbekende merknaam"
+#~ msgid "Edit with single &Vim"
+#~ msgstr "Wysig met 'n enkel &Vim"
-#~ msgid "no such buffer"
-#~ msgstr "buffer bestaan nie"
+#~ msgid "Edit with &multiple Vims"
+#~ msgstr "Wysig met &meer as een Vim"
-#~ msgid "attempt to refer to deleted window"
-#~ msgstr "poging om na geskrapte venster te verwys"
+#~ msgid "E299: Perl evaluation forbidden in sandbox without the Safe module"
+#~ msgstr ""
+#~ "E299: Perl evaluasie verbied in die sandput sonder die 'Safe' module"
-#~ msgid "readonly attribute"
-#~ msgstr "leesalleen eienskap"
+#~ msgid ""
+#~ "Sorry, this command is disabled: the Perl library could not be loaded."
+#~ msgstr ""
+#~ "Jammer, hierdie bevel is afgeskakel: die Perl biblioteek kon nie gelaai "
+#~ "word nie."
-#~ msgid "cursor position outside buffer"
-#~ msgstr "loperposisie buite buffer"
+#~ msgid "E370: Could not load library %s"
+#~ msgstr "E370: Kon nie biblioteek laai nie %s"
-#~ msgid "<window object (deleted) at %.8lX>"
-#~ msgstr "<venster voorwerp (geskrap) by %.8lX>"
+#~ msgid "type :help windows95<Enter> for info on this"
+#~ msgstr "tik :help windows95<Enter> vir meer inligting hieroor"
-#~ msgid "<window object (unknown) at %.8lX>"
-#~ msgstr "<verwyder voorwerp (onbekend) by %.8lX>"
+#~ msgid "WARNING: Windows 95/98/ME detected"
+#~ msgstr "WAARSKUWING: Windows 95/98/ME bespeur"
-#~ msgid "<window %d>"
-#~ msgstr "<venster %d>"
+#~ msgid " for Vim defaults "
+#~ msgstr " vir Vim verstekwaardes"
-#~ msgid "no such window"
-#~ msgstr "geen sodanige venster nie"
+#~ msgid "menu Edit->Global Settings->Toggle Vi Compatible"
+#~ msgstr "menu Redigeer->Global verstellings->Stel en herstel Vi Versoenbaar"
-#~ msgid "cannot save undo information"
-#~ msgstr "kan nie herwin-inligting stoor nie"
+#~ msgid "menu Edit->Global Settings->Toggle Insert Mode "
+#~ msgstr "menu Redigeer->Globale verstellings->Stel en herstel Invoeg Modus"
-#~ msgid "cannot delete line"
-#~ msgstr "kan rel nie verwyder nie"
+#~ msgid "Running modeless, typed text is inserted"
+#~ msgstr "Voer modus-loos uit, getikte teks word ingevoeg"
-#~ msgid "cannot replace line"
-#~ msgstr "kan rel nie vervang nie"
+#~ msgid "menu Help->Orphans for information "
+#~ msgstr "menu Hulp->Weeskinders vir meer inligting hieroor "
-#~ msgid "cannot insert line"
-#~ msgstr "kan rel nie byvoeg nie"
+#~ msgid "Compiler: "
+#~ msgstr "Kompileerder: "
-#~ msgid "string cannot contain newlines"
-#~ msgstr "string kan nie 'newlines' bevat nie"
+#~ msgid " system menu file: \""
+#~ msgstr " stelsel kieslys-ler: \""
-#~ msgid ""
-#~ "E266: Sorry, this command is disabled, the Ruby library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E266: Jammer, hierdie bevel is afgeskakel, die Ruby biblioteekler kon "
-#~ "nie gelaai word nie."
+#~ msgid "3rd user gvimrc file: \""
+#~ msgstr "3de gebruiker gvimrc-ler: \""
-#~ msgid "E273: unknown longjmp status %d"
-#~ msgstr "E273: Onbekende 'longjmp' status %d"
+#~ msgid "2nd user gvimrc file: \""
+#~ msgstr "2de gebruiker gvimrc-ler: \""
-#~ msgid "Toggle implementation/definition"
-#~ msgstr "Stel en herstel implimentasie/definisie"
+#~ msgid " user gvimrc file: \""
+#~ msgstr " gebruiker gvimrc-ler: \""
-#~ msgid "Show base class of"
-#~ msgstr "Wys basisklas van"
+#~ msgid " system gvimrc file: \""
+#~ msgstr " stelsel gvimrc-ler: \""
-#~ msgid "Show overridden member function"
-#~ msgstr "Wys vervangde lidfunksie"
+#~ msgid "with (classic) GUI."
+#~ msgstr "met (klassieke) GUI."
-#~ msgid "Retrieve from file"
-#~ msgstr "Gaan haal uit ler"
+#~ msgid "with Cocoa GUI."
+#~ msgstr "met Cocoa GUI."
-#~ msgid "Retrieve from project"
-#~ msgstr "Gaan haal uit projek"
+#~ msgid "with Carbon GUI."
+#~ msgstr "met Carbon GUI."
-#~ msgid "Retrieve from all projects"
-#~ msgstr "Gaan haal uit alle projekte"
+#~ msgid "with GUI."
+#~ msgstr "met GUI."
-#~ msgid "Retrieve"
-#~ msgstr "Gaan haal"
+#~ msgid "with Photon GUI."
+#~ msgstr "met Photon GUI."
-#~ msgid "Show source of"
-#~ msgstr "Wys kode van"
+#~ msgid "with BeOS GUI."
+#~ msgstr "met BeOS GUI"
-#~ msgid "Find symbol"
-#~ msgstr "Vind simbool"
+#~ msgid "with X11-Athena GUI."
+#~ msgstr "met X11-Athena GUI"
-#~ msgid "Browse class"
-#~ msgstr "Kyk klas deur"
+#~ msgid "with X11-neXtaw GUI."
+#~ msgstr "met X11-neXtaw GUI"
-#~ msgid "Show class in hierarchy"
-#~ msgstr "Wys klas in hirargie"
+#~ msgid "with X11-Motif GUI."
+#~ msgstr "met X11-Motif GUI."
-#~ msgid "Show class in restricted hierarchy"
-#~ msgstr "Wys klas in beperkte hirargie"
+#~ msgid "with GTK GUI."
+#~ msgstr "met GTK GUI"
-#~ msgid "Xref refers to"
-#~ msgstr "Xref verwys na"
+#~ msgid "with GTK2 GUI."
+#~ msgstr "met GTK2 GUI"
-#~ msgid "Xref referred by"
-#~ msgstr "Xref verwys deur"
+#~ msgid "with GTK-GNOME GUI."
+#~ msgstr "met GTK-GNOME GUI."
-#~ msgid "Xref has a"
-#~ msgstr "Xref het 'n"
+#~ msgid "with GTK2-GNOME GUI."
+#~ msgstr "met GTK2-GNOME GUI."
-#~ msgid "Xref used by"
-#~ msgstr "Xref gebruik deur"
+#~ msgid ""
+#~ "\n"
+#~ "Tiny version "
+#~ msgstr ""
+#~ "\n"
+#~ "Piepklein weergawe "
-#~ msgid "Show docu of"
-#~ msgstr "Wys 'docu' van"
+#~ msgid ""
+#~ "\n"
+#~ "Small version "
+#~ msgstr ""
+#~ "\n"
+#~ "Klein weergawe "
-#~ msgid "Generate docu for"
-#~ msgstr "Genereer 'docu' vir"
+#~ msgid ""
+#~ "\n"
+#~ "Normal version "
+#~ msgstr ""
+#~ "\n"
+#~ "Normale weergawe "
#~ msgid ""
-#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in "
-#~ "$PATH).\n"
+#~ "\n"
+#~ "Big version "
#~ msgstr ""
-#~ "Kan nie 'n verbinding met 'SNiFF+' maak nie. Kyk of die omgewing reg is "
-#~ "('sniffemacs' moet in '$PATH' gevind word).\n"
+#~ "\n"
+#~ "Groot weergawe "
-#~ msgid "E274: Sniff: Error during read. Disconnected"
-#~ msgstr "E274: Sniff: Fout gedurende lees. Verbinding gebreek"
+#~ msgid ""
+#~ "\n"
+#~ "RISC OS version"
+#~ msgstr ""
+#~ "\n"
+#~ "RISC OS weergawe"
-#~ msgid "SNiFF+ is currently "
-#~ msgstr "SNiFF+ is tans"
+#~ msgid ""
+#~ "\n"
+#~ "MacOS version"
+#~ msgstr ""
+#~ "\n"
+#~ "MacOS weergawe"
-#~ msgid "not "
-#~ msgstr "nie "
+#~ msgid ""
+#~ "\n"
+#~ "MacOS X version"
+#~ msgstr ""
+#~ "\n"
+#~ "MacOS X weergawe"
-#~ msgid "connected"
-#~ msgstr "gekonnekteer"
+#~ msgid ""
+#~ "\n"
+#~ "MacOS X (unix) version"
+#~ msgstr ""
+#~ "\n"
+#~ "MacOS X (unix) weergawe"
-#~ msgid "E275: Unknown SNiFF+ request: %s"
-#~ msgstr "E275: Onbekende SNiFF+ versoek: %s"
+#~ msgid ""
+#~ "\n"
+#~ "16-bit MS-DOS version"
+#~ msgstr ""
+#~ "\n"
+#~ "16-bis MS-DOS weergawe"
-#~ msgid "E276: Error connecting to SNiFF+"
-#~ msgstr "E276: Fout in konnekteer met SNiFF+"
+#~ msgid ""
+#~ "\n"
+#~ "32-bit MS-DOS version"
+#~ msgstr ""
+#~ "\n"
+#~ "32-bis MS-DOS weergawe"
-#~ msgid "E278: SNiFF+ not connected"
-#~ msgstr "E278: SNiFF+ is nie gekonnekteer nie"
+#~ msgid ""
+#~ "\n"
+#~ "MS-Windows 16-bit version"
+#~ msgstr ""
+#~ "\n"
+#~ "MS-Windows 16-bis weergawe"
-#~ msgid "Sniff: Error during write. Disconnected"
-#~ msgstr "Sniff: Fout gedurende stoor. Verbinding gebreek"
+#~ msgid ""
+#~ "\n"
+#~ "MS-Windows 32-bit console version"
+#~ msgstr ""
+#~ "\n"
+#~ "MS-Windows 32-bis konsole weergawe"
-#~ msgid "not implemented yet"
-#~ msgstr "nog nie gemplementeer nie"
+#~ msgid " with OLE support"
+#~ msgstr " met OLE ondersteuning"
-#~ msgid "unknown option"
-#~ msgstr "onbekende opsie"
+#~ msgid " in Win32s mode"
+#~ msgstr " in Win32s modus"
-#~ msgid "cannot set line(s)"
-#~ msgstr "kan nie rel(s) stel nie"
+#~ msgid ""
+#~ "\n"
+#~ "MS-Windows 32-bit GUI version"
+#~ msgstr ""
+#~ "\n"
+#~ "MS-Windows 32-bis GUI version"
-#~ msgid "mark not set"
-#~ msgstr "merker nie gestel nie"
+#~ msgid ""
+#~ "\n"
+#~ "MS-Windows 16/32-bit GUI version"
+#~ msgstr ""
+#~ "\n"
+#~ "MS-Windows 16/32-bis GUI weergawe"
-#~ msgid "row %d column %d"
-#~ msgstr "ry %d kolom %d"
+#~ msgid "No undo possible; continue anyway"
+#~ msgstr "Geen herstel moontlik; gaan in elk geval voort"
-#~ msgid "cannot insert/append line"
-#~ msgstr "kan nie rel invoeg/aanlas nie"
+#~ msgid "new shell started\n"
+#~ msgstr "nuwe dop begin\n"
-#~ msgid "unknown flag: "
-#~ msgstr "onbekende vlag: "
+#~ msgid "E430: Tag file path truncated for %s\n"
+#~ msgstr "E430: Etiketlergids afgekap vir %s\n"
-#~ msgid "unknown vimOption"
-#~ msgstr "onbekende 'vimOption'"
+#~ msgid "Enter nr of choice (<CR> to abort): "
+#~ msgstr "Sleutel nommer van keuse in (<CR> om te stop): "
-#~ msgid "keyboard interrupt"
-#~ msgstr "sleutelbordonderbreking"
+#~ msgid "E396: containedin argument not accepted here"
+#~ msgstr "E396: 'containedin' parameter nie hier aanvaar nie"
-#~ msgid "vim error"
-#~ msgstr "vim fout"
+#~ msgid "E363: pattern caused out-of-stack error"
+#~ msgstr "E363: patroon het le-stapel fout veroorsaak"
-#~ msgid "cannot create buffer/window command: object is being deleted"
-#~ msgstr "kan nie buffer/venster bevel skep nie: voorwerp word geskrap"
+#~ msgid "E361: Crash intercepted; regexp too complex?"
+#~ msgstr "E361: Ineenstorting onderskep. Patroon te kompleks?"
-#~ msgid ""
-#~ "cannot register callback command: buffer/window is already being deleted"
-#~ msgstr ""
-#~ "kan nie terugroepbevel registreer nie: buffer/venster word alreeds geskrap"
+#~ msgid "E58: %s{ operand could be empty"
+#~ msgstr "E58: %s{ operand mag leeg wees"
-#~ msgid ""
-#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-"
-#~ "dev@vim.org"
-#~ msgstr ""
-#~ "E280: TCL FATALE FOUT: verwlys korrup!? Rapporteer dit asb. aan <vim-"
-#~ "dev@vim.org>"
+#~ msgid "E57: %s+ operand could be empty"
+#~ msgstr "E57: %s+ operand mag leeg wees"
-#~ msgid "cannot register callback command: buffer/window reference not found"
-#~ msgstr ""
-#~ "kan terugroepbevel nie registreer nie: buffer/vensterverwysing nie gevind "
-#~ "nie"
+#~ msgid "E56: %s* operand could be empty"
+#~ msgstr "E56: %s* operand mag leeg wees"
-#~ msgid ""
-#~ "E571: Sorry, this command is disabled: the Tcl library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E571: Jammer, hierdie bevel is afgeskakel, die Tcl biblioteek kon nie "
-#~ "gelaai word nie."
+#~ msgid "Vim Warning"
+#~ msgstr "Vim Waarskuwing"
#~ msgid ""
-#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim."
-#~ "org"
+#~ "VIMRUN.EXE not found in your $PATH.\n"
+#~ "External commands will not pause after completion.\n"
+#~ "See :help win32-vimrun for more information."
#~ msgstr ""
-#~ "E281: TCL FOUT: verlaatkode is nie 'n 'int'!? Rapporteer dit asb. aan "
-#~ "<vim-dev@vim.org>"
-
-#~ msgid "cannot get line"
-#~ msgstr "kan nie rel kry nie"
-
-#~ msgid "Unable to register a command server name"
-#~ msgstr "Kon nie bevelbediener naam registreer nie"
-
-#~ msgid "E248: Failed to send command to the destination program"
-#~ msgstr "E248: Het gefaal om bevel na doel program te stuur"
+#~ "'VIMRUN.EXE' nie gevind in '$PATH' nie.\n"
+#~ "Eksterne opdragte sal nie wag na voltooiing nie\n"
+#~ "Sien ':help win32-vimrun' vir meer inligting."
-#~ msgid "E251: VIM instance registry property is badly formed. Deleted!"
-#~ msgstr "E251: VIM instansie register-kenmerk is swak gevorm. Geskrap!"
+#~ msgid "E371: Command not found"
+#~ msgstr "E371: Bevel nie gevind nie"
-#~ msgid "This Vim was not compiled with the diff feature."
-#~ msgstr "Hierdie Vim is nie gekompileer met 'diff' funksionaliteit nie."
+#~ msgid "shutdown"
+#~ msgstr "sit af"
-#~ msgid "-register\t\tRegister this gvim for OLE"
-#~ msgstr "-register\t\tRegistreer hierdie gvim vir OLE"
+#~ msgid "logoff"
+#~ msgstr "teken uit"
-#~ msgid "-unregister\t\tUnregister gvim for OLE"
-#~ msgstr "-unregister\t\tOnregistreer gvim vir OLE"
+#~ msgid "close"
+#~ msgstr "maak toe"
-#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")"
-#~ msgstr "-g\t\t\tVoer uit met die GUI (soos \"gvim\")"
+#~ msgid "Vim: Caught %s event\n"
+#~ msgstr "Vim: Het %s gebeurtenis gevang\n"
-#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
-#~ msgstr "-f of --nofork\tVoorgrond: Moenie vurk wanneer GUI begin nie"
+#~ msgid "shell returned %d"
+#~ msgstr "dop het %d gelewer"
-#~ msgid "-V[N]\t\tVerbose level"
-#~ msgstr "-V[N]\t\tOmslagtigheidsgraad"
+#~ msgid "Could not fix up function pointers to the DLL!"
+#~ msgstr "Kon nie funksiewysers na die DLL opstel nie!"
-#~ msgid "-f\t\t\tDon't use newcli to open window"
-#~ msgstr "-f\t\t\tMoet nie 'newcli' gebruik om venster oop te maak nie"
+#~ msgid "Could not load vim32.dll!"
+#~ msgstr "Kon nie 'vim32.dll' laai nie!"
-#~ msgid "-dev <device>\t\tUse <device> for I/O"
-#~ msgstr "-dev <toestel>\t\tGebruik <toestel> vir I/O"
+#~ msgid "VIM Error"
+#~ msgstr "VIM Fout"
-#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
-#~ msgstr "-U <gvimrc>\t\tGebruik <gvimrc> in plaas van enige .gvimrc"
+#~ msgid "Could not allocate memory for command line."
+#~ msgstr "Kan nie geheue toeken vir bevelrel nie"
-#~ msgid "-x\t\t\tEdit encrypted files"
-#~ msgstr "-x\t\t\tBewerk genkripteerde lers"
+#~ msgid "At line"
+#~ msgstr "By rel"
-#~ msgid "-display <display>\tConnect vim to this particular X-server"
-#~ msgstr "-display <display>\tKoppel vim aan hierdie X-bediener"
+#~ msgid "XSMP ICE connection watch failed"
+#~ msgstr "XSMP ICE konneksie beloer het gefaal"
-#~ msgid "-X\t\t\tDo not connect to X server"
-#~ msgstr "-X\t\t\tMoet nie verbinding met X-bediener maak nie"
+#~ msgid "XSMP opening connection"
+#~ msgstr "XSMP maak nou konneksie oop"
-#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible"
-#~ msgstr ""
-#~ "--remote <lers>\tWysig die <lers> in a Vim bediener indien moontlik"
+#~ msgid "XSMP handling save-yourself request"
+#~ msgstr "XSMP hanteer 'save-yourself' versoek"
-#~ msgid "--remote-silent <files> Same, don't complain if there is no server"
-#~ msgstr ""
-#~ "--remote-silent <lers> Dieselfde, moet nie kla as daar nie so 'n "
-#~ "bediener is nie"
+#~ msgid "Opening the X display failed"
+#~ msgstr "Oopmaak van die X vertoonskerm het gefaal"
-#~ msgid ""
-#~ "--remote-wait <files> As --remote but wait for files to have been edited"
-#~ msgstr ""
-#~ "--remote-wait <lers> Soos '--remote', maar wag vir lers om gewysig te "
-#~ "word"
+#~ msgid "XSMP lost ICE connection"
+#~ msgstr "XSMP het ICE konneksie verloor"
#~ msgid ""
-#~ "--remote-wait-silent <files> Same, don't complain if there is no server"
-#~ msgstr ""
-#~ "--remote-wait-silent <lers> Dieselfde, moet nie kla as daar nie so 'n "
-#~ "bediener is nie"
-
-#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
+#~ "\n"
+#~ "Command terminated\n"
#~ msgstr ""
-#~ "--remote-send <sleutels>\tStuur <sleutels> na 'n Vim-bediener en verlaat"
+#~ "\n"
+#~ "Bevel beindig\n"
#~ msgid ""
-#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
+#~ "\n"
+#~ "Cannot fork\n"
#~ msgstr ""
-#~ "--remote-expr <expr>\tEvalueer <expr> in 'n Vim-bediener en druk resultaat"
-
-#~ msgid "--serverlist\t\tList available Vim server names and exit"
-#~ msgstr "--serverlist\t\tLys beskikbare Vim-bediener name en verlaat"
-
-#~ msgid "--servername <name>\tSend to/become the Vim server <name>"
-#~ msgstr "--servername <naam>\tStuur na/word die Vim-bediener <naam>"
+#~ "\n"
+#~ "Kan nie vurk nie\n"
#~ msgid ""
#~ "\n"
-#~ "Arguments recognised by gvim (Motif version):\n"
+#~ "Cannot create pipes\n"
#~ msgstr ""
#~ "\n"
-#~ "Parameters deur gvim herken (Motif weergawe):\n"
+#~ "Kan nie pype skep nie\n"
#~ msgid ""
#~ "\n"
-#~ "Arguments recognised by gvim (neXtaw version):\n"
+#~ "Cannot execute shell sh\n"
#~ msgstr ""
#~ "\n"
-#~ "Parameters deur gvim herken (neXtaw weergawe):\n"
+#~ "Kan nie dop 'sh' uitvoer nie\n"
+
+#~ msgid "Opening the X display timed out"
+#~ msgstr "Oopmaak van die X-vertoonskerm het uitgetel"
+
+#~ msgid "Testing the X display failed"
+#~ msgstr "Toetsing van die X-vertoonskerm het gefaal"
#~ msgid ""
#~ "\n"
-#~ "Arguments recognised by gvim (Athena version):\n"
+#~ "Vim: Got X error\n"
#~ msgstr ""
#~ "\n"
-#~ "Parameters deur gvim herken (Athena weergawe):\n"
+#~ "Vim: Het X fout ontvang\n"
-#~ msgid "-display <display>\tRun vim on <display>"
-#~ msgstr "-display <display>\tVoer vim op <display> uit"
+#~ msgid "Opening the X display took %<PRId64> msec"
+#~ msgstr "Om die X-vertoonskerm oop te maak het %<PRId64> msek gevat"
-#~ msgid "-iconic\t\tStart vim iconified"
-#~ msgstr "-iconic\t\tBegin vim as ikoon"
+#~ msgid "Vim: Caught deadly signal\n"
+#~ msgstr "Vim: Het dodelike sein gevang\n"
-#~ msgid "-name <name>\t\tUse resource as if vim was <name>"
-#~ msgstr "-name <name>\t\tGebruik hulpbron asof vim <name> was"
+#~ msgid "Vim: Caught deadly signal %s\n"
+#~ msgstr "Vim: Het dodelike sein %s gevang\n"
-#~ msgid "\t\t\t (Unimplemented)\n"
-#~ msgstr "\t\t\t (Nog nie gemplementeer nie)\n"
+#~ msgid "Vim: Double signal, exiting\n"
+#~ msgstr "Vim: Dubbel sein, staak\n"
-#~ msgid "-background <color>\tUse <color> for the background (also: -bg)"
-#~ msgstr "-background <kleur>\tGebruik <kleur> vir die agtergrond (ook: -bg)"
+#~ msgid "E245: Illegal char '%c' in font name \"%s\""
+#~ msgstr "E245: Ongeldige karakter '%c' in fontnaam \"%s\""
-#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
-#~ msgstr "-voorgrond <kleur>\tGebruik <kleur> vir normale teks (ook: -fg)"
+#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
+#~ msgstr "E244: Ongeldige karakterstelnaam \"%s\" in fontnaam \"%s\""
-#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
-#~ msgstr "-font <font>\t\tGebruik <font> vir normale teks (ook -fn)"
+#~ msgid "Printing '%s'"
+#~ msgstr "Druk nou '%s'"
-#~ msgid "-boldfont <font>\tUse <font> for bold text"
-#~ msgstr "boldfont <font>\t Gebruik <font> vir vetletter teks"
+#~ msgid "E238: Print error: %s"
+#~ msgstr "E238: Drukfout: %s"
-#~ msgid "-italicfont <font>\tUse <font> for italic text"
-#~ msgstr "-italicfont <font>\tGebruik <font> vir kursiewe teks"
+#~ msgid "E613: Unknown printer font: %s"
+#~ msgstr "E613: Onbekende drukker font: %s"
-#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
-#~ msgstr "-geometry <geom>\tGebruik <geom> vir aanvanklike geometrie"
+#~ msgid "to %s on %s"
+#~ msgstr "na %s op %s"
-#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
-#~ msgstr "-borderwidth <wydte>\tGebruik 'n grenswydte van <wydte> (ook: -bw)"
+#~ msgid "'columns' is not 80, cannot execute external commands"
+#~ msgstr "'columns' is nie 80 nie, kan nie eksterne bevele uitvoer nie"
-#~ msgid ""
-#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
-#~ msgstr ""
-#~ "-scrollbarwidth <wydte>\tGebruik 'n rolstaafwydte van <wydte> (ook: -sw>"
+#~ msgid "...(truncated)"
+#~ msgstr "...(afgekap)"
-#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
-#~ msgstr ""
-#~ "-menuheight <hoogte>\tGebruik a kieslysstaafhoogte van <hoogte> (ook: -mh)"
+#~ msgid "I/O ERROR"
+#~ msgstr "I/O FOUT"
-#~ msgid "-reverse\t\tUse reverse video (also: -rv)"
-#~ msgstr "-reverse\t\tGebruik tru-video (ook: -rv)"
+#~ msgid "ANCHOR_BUF_SIZE too small."
+#~ msgstr "'ANCHOR_BUF_SIZE' is te klein"
-#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)"
-#~ msgstr "+reverse\t\tMoet nie tru-video gebruik nie (ook: +rv)"
+#~ msgid " returned\n"
+#~ msgstr " teruggekeer\n"
-#~ msgid "-xrm <resource>\tSet the specified resource"
-#~ msgstr "-xrm <hulpbron>\tStel die gespesifiseerde hulpbron"
+#~ msgid "shell "
+#~ msgstr "dop "
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (RISC OS version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Parameters wat gvim verstaan (RISC OS weergawe):\n"
+#~ msgid "Cannot execute "
+#~ msgstr "Kan nie uitvoer nie "
-#~ msgid "--columns <number>\tInitial width of window in columns"
-#~ msgstr "--columns <aantal>\tAanvanklike wydte van venster in kolomme"
+#~ msgid "mch_get_shellsize: not a console??\n"
+#~ msgstr "'mch_get_shellsize': nie 'n konsole nie??\n"
-#~ msgid "--rows <number>\tInitial height of window in rows"
-#~ msgstr "--rows <aantal>\tAanvanklike hoogte van venster in rye"
+#~ msgid "cannot change console mode ?!\n"
+#~ msgstr "kan konsole-modus nie verander nie ?!\n"
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (GTK+ version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Parameters wat gvim verstaan (GTK+ weergawe):\n"
+#~ msgid "Vim exiting with %d\n"
+#~ msgstr "Vim stop met %d\n"
-#~ msgid "-display <display>\tRun vim on <display> (also: --display)"
-#~ msgstr "-display <skerm>\tVoer vim op <skerm> uit: (ook --display)"
+#~ msgid "Cannot create "
+#~ msgstr "Kan nie skep nie: "
-#~ msgid "--role <role>\tSet a unique role to identify the main window"
-#~ msgstr "--role <rol>\tStel 'n unieke rol om die hoofvenster te identifiseer"
+#~ msgid "Cannot open NIL:\n"
+#~ msgstr "Kan nie NIL: oopmaak nie\n"
-#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
-#~ msgstr "--socketid <xid>\tMaak Vim in 'n ander GTK element oop"
+#~ msgid "Need %s version %<PRId64>\n"
+#~ msgstr "Benodig %s weergawe %<PRId64>\n"
-#~ msgid "-P <parent title>\tOpen Vim inside parent application"
-#~ msgstr "-P <ouer title>\tMaak Vim oop binne 'n ouer toepassing"
+#~ msgid "Need Amigados version 2.04 or later\n"
+#~ msgstr "Benodig Amigados weergawe 2.04 of later\n"
-#~ msgid "No display"
-#~ msgstr "Geen vertoonskerm"
+#~ msgid "VIM: Can't open window!\n"
+#~ msgstr "VIM: Kan nie venster oopmaak nie!\n"
-#~ msgid ": Send failed.\n"
-#~ msgstr ": Stuur het gefaal.\n"
+#~ msgid "cannot open "
+#~ msgstr "kan nie oopmaak nie "
-#~ msgid ": Send failed. Trying to execute locally\n"
-#~ msgstr ": Stuur het gefaal. Probeer om lokaal uit te voer\n"
+#~ msgid "E538: No mouse support"
+#~ msgstr "E538: Geen muisondersteuning nie"
-#~ msgid "%d of %d edited"
-#~ msgstr "%d van %d lers bewerk"
+#~ msgid "E534: Invalid wide font"
+#~ msgstr "E534: Ongeldige wye font"
-#~ msgid "No display: Send expression failed.\n"
-#~ msgstr "Geen vertoonskerm: Stuur van uitdrukking het gefaal.\n"
+#~ msgid "E533: can't select wide font"
+#~ msgstr "E533: kan nie wye font kies nie"
-#~ msgid ": Send expression failed.\n"
-#~ msgstr ": Stuur van uitdrukking het gefaal.\n"
+#~ msgid "E598: Invalid fontset"
+#~ msgstr "E598: Ongeldige fontstel"
-#~ msgid "E543: Not a valid codepage"
-#~ msgstr "E543: Nie 'n geldige kodeblad nie"
+#~ msgid "E597: can't select fontset"
+#~ msgstr "E597: kan nie fontstel kies nie"
-#~ msgid "E285: Failed to create input context"
-#~ msgstr "E285: Gefaal met die skep van invoerkonteks"
+#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI"
+#~ msgstr "E617: Kan nie 'term' verander in die GTK+ 2 GUI nie"
-#~ msgid "E286: Failed to open input method"
-#~ msgstr "E286: Gefaal om invoermetode oop te maak"
+#~ msgid "E531: Use \":gui\" to start the GUI"
+#~ msgstr "E531: Gebruik \":gui\" om die GUI te begin"
-#~ msgid "E287: Warning: Could not set destroy callback to IM"
-#~ msgstr "E287: Waarskuwing: Kon nie uitwis-terugroep na IM stel nie"
+#~ msgid "E530: Cannot change term in GUI"
+#~ msgstr "E530: Kan nie 'term' verander in GUI nie"
-#~ msgid "E288: input method doesn't support any style"
-#~ msgstr "E288: invoermetode ondersteun geen styl nie"
+#~ msgid "freeing %<PRId64> lines"
+#~ msgstr "laat %<PRId64> rels gaan"
-#~ msgid "E289: input method doesn't support my preedit type"
-#~ msgstr "E289: invoermetode ondersteun nie my voor-bewerking tipe nie"
+#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>"
+#~ msgstr "E658: NetBeans konneksie vir buffer %<PRId64> verloor"
-#~ msgid "E290: over-the-spot style requires fontset"
-#~ msgstr "E290: oor-die-plek styl vereis fontstel"
+#~ msgid "read from Netbeans socket"
+#~ msgstr "lees vanaf Netbeans 'socket'"
-#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
-#~ msgstr "E291: Jou GTK+ is ouer as 1.2.3. Statusarea afgeskakel"
+#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
+#~ msgstr ""
+#~ "E668: Verkeerde toegangsmodue vir NetBeans konneksie inligtingsler: \"%s"
+#~ "\""
-#~ msgid "E292: Input Method Server is not running"
-#~ msgstr "E292: Invoermetodebediener voer nie uit nie"
+#~ msgid "Cannot connect to Netbeans"
+#~ msgstr "Kan nie aan Netbeans koppel nie"
+
+#~ msgid "Cannot connect to Netbeans #2"
+#~ msgstr "Kan nie aan Netbeans #2 koppel nie"
+
+#~ msgid "Keys don't match!"
+#~ msgstr "Sleutels verskil!"
+
+#~ msgid "Enter same key again: "
+#~ msgstr "Voer die sleutel weer in: "
+
+#~ msgid "Enter encryption key: "
+#~ msgstr "Voer enkripsie-sleutel in: "
+
+#~ msgid "E547: Illegal mouseshape"
+#~ msgstr "E547: Ongeldige muisvorm"
+
+#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
+#~ msgstr "E341: Interne fout: 'lalloc(%<PRId64>, )'"
+
+#~ msgid "E340: Line is becoming too long"
+#~ msgstr "E340: Rel word te lank"
#~ msgid ""
+#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n"
#~ "\n"
-#~ " [not usable with this version of Vim]"
#~ msgstr ""
+#~ "[roepe] totaal re/malloc()'s %<PRIu64>, totale free()'s %<PRIu64>\n"
#~ "\n"
-#~ " [nie bruikbaar met hierdie weergawe van Vim nie]"
+
+#~ msgid ""
+#~ "\n"
+#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use "
+#~ "%<PRIu64>\n"
+#~ msgstr ""
+#~ "\n"
+#~ "[grepe] totaal 'alloc'-vrygelaat %<PRIu64>-%<PRIu64>, in gebruik "
+#~ "%<PRIu64>, piekgebruik %<PRIu64>\n"
+
+#~ msgid "ERROR: "
+#~ msgstr "FOUT: "
+
+#~ msgid "Vim: Finished.\n"
+#~ msgstr "Vim: Klaar.\n"
+
+#~ msgid "Vim: preserving files...\n"
+#~ msgstr "Vim: bewaar lers...\n"
+
+#~ msgid "E338: Sorry, no file browser in console mode"
+#~ msgstr "E338: Jammer, lerblaaier nie beskikbaar in konsole-modus nie"
+
+#~ msgid "Open File dialog"
+#~ msgstr "Maak ler oop dialooghokkie"
+
+#~ msgid "Save File dialog"
+#~ msgstr "Stoor Ler dialooghokkie"
+
+#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)"
+#~ msgstr " (RET: rel, SPACE: bladsy, d: halwe bladsy, q: los dit"
+
+#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
+#~ msgstr " (RET/BS: rel, SPACE/b: bladsy, d/u: halwe bladsy, q: los dit"
+
+#~ msgid "Hit ENTER to continue"
+#~ msgstr "Druk ENTER om voort te gaan"
+
+#~ msgid "[string too long]"
+#~ msgstr "[string te lank]"
+
+#~ msgid "Tear off this menu"
+#~ msgstr "Skeur die kieslys af"
#~ msgid ""
#~ "&Open Read-Only\n"
@@ -7273,824 +6490,1139 @@ msgstr "E446: Geen lernaam onder loper"
#~ "&Stop\n"
#~ "S&krap dit"
-#~ msgid "Tear off this menu"
-#~ msgstr "Skeur die kieslys af"
+#~ msgid ""
+#~ "\n"
+#~ " [not usable with this version of Vim]"
+#~ msgstr ""
+#~ "\n"
+#~ " [nie bruikbaar met hierdie weergawe van Vim nie]"
-#~ msgid "[string too long]"
-#~ msgstr "[string te lank]"
+#~ msgid "E292: Input Method Server is not running"
+#~ msgstr "E292: Invoermetodebediener voer nie uit nie"
-#~ msgid "Hit ENTER to continue"
-#~ msgstr "Druk ENTER om voort te gaan"
+#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
+#~ msgstr "E291: Jou GTK+ is ouer as 1.2.3. Statusarea afgeskakel"
-#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
-#~ msgstr " (RET/BS: rel, SPACE/b: bladsy, d/u: halwe bladsy, q: los dit"
+#~ msgid "E290: over-the-spot style requires fontset"
+#~ msgstr "E290: oor-die-plek styl vereis fontstel"
-#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)"
-#~ msgstr " (RET: rel, SPACE: bladsy, d: halwe bladsy, q: los dit"
+#~ msgid "E289: input method doesn't support my preedit type"
+#~ msgstr "E289: invoermetode ondersteun nie my voor-bewerking tipe nie"
-#~ msgid "Save File dialog"
-#~ msgstr "Stoor Ler dialooghokkie"
+#~ msgid "E288: input method doesn't support any style"
+#~ msgstr "E288: invoermetode ondersteun geen styl nie"
-#~ msgid "Open File dialog"
-#~ msgstr "Maak ler oop dialooghokkie"
+#~ msgid "E287: Warning: Could not set destroy callback to IM"
+#~ msgstr "E287: Waarskuwing: Kon nie uitwis-terugroep na IM stel nie"
-#~ msgid "E338: Sorry, no file browser in console mode"
-#~ msgstr "E338: Jammer, lerblaaier nie beskikbaar in konsole-modus nie"
+#~ msgid "E285: Failed to create input context"
+#~ msgstr "E285: Gefaal met die skep van invoerkonteks"
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim: bewaar lers...\n"
+#~ msgid "E543: Not a valid codepage"
+#~ msgstr "E543: Nie 'n geldige kodeblad nie"
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim: Klaar.\n"
+#~ msgid ": Send expression failed.\n"
+#~ msgstr ": Stuur van uitdrukking het gefaal.\n"
-#~ msgid "ERROR: "
-#~ msgstr "FOUT: "
+#~ msgid "No display: Send expression failed.\n"
+#~ msgstr "Geen vertoonskerm: Stuur van uitdrukking het gefaal.\n"
+
+#~ msgid "%d of %d edited"
+#~ msgstr "%d van %d lers bewerk"
+
+#~ msgid ": Send failed. Trying to execute locally\n"
+#~ msgstr ": Stuur het gefaal. Probeer om lokaal uit te voer\n"
+
+#~ msgid ": Send failed.\n"
+#~ msgstr ": Stuur het gefaal.\n"
+
+#~ msgid "No display"
+#~ msgstr "Geen vertoonskerm"
+
+#~ msgid "-P <parent title>\tOpen Vim inside parent application"
+#~ msgstr "-P <ouer title>\tMaak Vim oop binne 'n ouer toepassing"
+
+#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
+#~ msgstr "--socketid <xid>\tMaak Vim in 'n ander GTK element oop"
+
+#~ msgid "--role <role>\tSet a unique role to identify the main window"
+#~ msgstr "--role <rol>\tStel 'n unieke rol om die hoofvenster te identifiseer"
+
+#~ msgid "-display <display>\tRun vim on <display> (also: --display)"
+#~ msgstr "-display <skerm>\tVoer vim op <skerm> uit: (ook --display)"
#~ msgid ""
#~ "\n"
-#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use "
-#~ "%<PRIu64>\n"
+#~ "Arguments recognised by gvim (GTK+ version):\n"
#~ msgstr ""
#~ "\n"
-#~ "[grepe] totaal 'alloc'-vrygelaat %<PRIu64>-%<PRIu64>, in gebruik "
-#~ "%<PRIu64>, piekgebruik %<PRIu64>\n"
+#~ "Parameters wat gvim verstaan (GTK+ weergawe):\n"
+
+#~ msgid "--rows <number>\tInitial height of window in rows"
+#~ msgstr "--rows <aantal>\tAanvanklike hoogte van venster in rye"
+
+#~ msgid "--columns <number>\tInitial width of window in columns"
+#~ msgstr "--columns <aantal>\tAanvanklike wydte van venster in kolomme"
#~ msgid ""
-#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n"
#~ "\n"
+#~ "Arguments recognised by gvim (RISC OS version):\n"
#~ msgstr ""
-#~ "[roepe] totaal re/malloc()'s %<PRIu64>, totale free()'s %<PRIu64>\n"
#~ "\n"
+#~ "Parameters wat gvim verstaan (RISC OS weergawe):\n"
-#~ msgid "E340: Line is becoming too long"
-#~ msgstr "E340: Rel word te lank"
-
-#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
-#~ msgstr "E341: Interne fout: 'lalloc(%<PRId64>, )'"
-
-#~ msgid "E547: Illegal mouseshape"
-#~ msgstr "E547: Ongeldige muisvorm"
-
-#~ msgid "Enter encryption key: "
-#~ msgstr "Voer enkripsie-sleutel in: "
-
-#~ msgid "Enter same key again: "
-#~ msgstr "Voer die sleutel weer in: "
+#~ msgid "-xrm <resource>\tSet the specified resource"
+#~ msgstr "-xrm <hulpbron>\tStel die gespesifiseerde hulpbron"
-#~ msgid "Keys don't match!"
-#~ msgstr "Sleutels verskil!"
+#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)"
+#~ msgstr "+reverse\t\tMoet nie tru-video gebruik nie (ook: +rv)"
-#~ msgid "Cannot connect to Netbeans #2"
-#~ msgstr "Kan nie aan Netbeans #2 koppel nie"
+#~ msgid "-reverse\t\tUse reverse video (also: -rv)"
+#~ msgstr "-reverse\t\tGebruik tru-video (ook: -rv)"
-#~ msgid "Cannot connect to Netbeans"
-#~ msgstr "Kan nie aan Netbeans koppel nie"
+#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
+#~ msgstr ""
+#~ "-menuheight <hoogte>\tGebruik a kieslysstaafhoogte van <hoogte> (ook: -mh)"
-#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
+#~ msgid ""
+#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
#~ msgstr ""
-#~ "E668: Verkeerde toegangsmodue vir NetBeans konneksie inligtingsler: \"%s"
-#~ "\""
+#~ "-scrollbarwidth <wydte>\tGebruik 'n rolstaafwydte van <wydte> (ook: -sw>"
-#~ msgid "read from Netbeans socket"
-#~ msgstr "lees vanaf Netbeans 'socket'"
+#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
+#~ msgstr "-borderwidth <wydte>\tGebruik 'n grenswydte van <wydte> (ook: -bw)"
-#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>"
-#~ msgstr "E658: NetBeans konneksie vir buffer %<PRId64> verloor"
+#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
+#~ msgstr "-geometry <geom>\tGebruik <geom> vir aanvanklike geometrie"
-#~ msgid "freeing %<PRId64> lines"
-#~ msgstr "laat %<PRId64> rels gaan"
+#~ msgid "-italicfont <font>\tUse <font> for italic text"
+#~ msgstr "-italicfont <font>\tGebruik <font> vir kursiewe teks"
-#~ msgid "E530: Cannot change term in GUI"
-#~ msgstr "E530: Kan nie 'term' verander in GUI nie"
+#~ msgid "-boldfont <font>\tUse <font> for bold text"
+#~ msgstr "boldfont <font>\t Gebruik <font> vir vetletter teks"
-#~ msgid "E531: Use \":gui\" to start the GUI"
-#~ msgstr "E531: Gebruik \":gui\" om die GUI te begin"
+#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
+#~ msgstr "-font <font>\t\tGebruik <font> vir normale teks (ook -fn)"
-#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI"
-#~ msgstr "E617: Kan nie 'term' verander in die GTK+ 2 GUI nie"
+#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
+#~ msgstr "-voorgrond <kleur>\tGebruik <kleur> vir normale teks (ook: -fg)"
-#~ msgid "E597: can't select fontset"
-#~ msgstr "E597: kan nie fontstel kies nie"
+#~ msgid "-background <color>\tUse <color> for the background (also: -bg)"
+#~ msgstr "-background <kleur>\tGebruik <kleur> vir die agtergrond (ook: -bg)"
-#~ msgid "E598: Invalid fontset"
-#~ msgstr "E598: Ongeldige fontstel"
+#~ msgid "\t\t\t (Unimplemented)\n"
+#~ msgstr "\t\t\t (Nog nie gemplementeer nie)\n"
-#~ msgid "E533: can't select wide font"
-#~ msgstr "E533: kan nie wye font kies nie"
+#~ msgid "-name <name>\t\tUse resource as if vim was <name>"
+#~ msgstr "-name <name>\t\tGebruik hulpbron asof vim <name> was"
-#~ msgid "E534: Invalid wide font"
-#~ msgstr "E534: Ongeldige wye font"
+#~ msgid "-iconic\t\tStart vim iconified"
+#~ msgstr "-iconic\t\tBegin vim as ikoon"
-#~ msgid "E538: No mouse support"
-#~ msgstr "E538: Geen muisondersteuning nie"
+#~ msgid "-display <display>\tRun vim on <display>"
+#~ msgstr "-display <display>\tVoer vim op <display> uit"
-#~ msgid "cannot open "
-#~ msgstr "kan nie oopmaak nie "
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (Athena version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Parameters deur gvim herken (Athena weergawe):\n"
-#~ msgid "VIM: Can't open window!\n"
-#~ msgstr "VIM: Kan nie venster oopmaak nie!\n"
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (neXtaw version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Parameters deur gvim herken (neXtaw weergawe):\n"
-#~ msgid "Need Amigados version 2.04 or later\n"
-#~ msgstr "Benodig Amigados weergawe 2.04 of later\n"
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (Motif version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Parameters deur gvim herken (Motif weergawe):\n"
-#~ msgid "Need %s version %<PRId64>\n"
-#~ msgstr "Benodig %s weergawe %<PRId64>\n"
+#~ msgid "--servername <name>\tSend to/become the Vim server <name>"
+#~ msgstr "--servername <naam>\tStuur na/word die Vim-bediener <naam>"
-#~ msgid "Cannot open NIL:\n"
-#~ msgstr "Kan nie NIL: oopmaak nie\n"
+#~ msgid "--serverlist\t\tList available Vim server names and exit"
+#~ msgstr "--serverlist\t\tLys beskikbare Vim-bediener name en verlaat"
-#~ msgid "Cannot create "
-#~ msgstr "Kan nie skep nie: "
+#~ msgid ""
+#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
+#~ msgstr ""
+#~ "--remote-expr <expr>\tEvalueer <expr> in 'n Vim-bediener en druk resultaat"
-#~ msgid "Vim exiting with %d\n"
-#~ msgstr "Vim stop met %d\n"
+#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
+#~ msgstr ""
+#~ "--remote-send <sleutels>\tStuur <sleutels> na 'n Vim-bediener en verlaat"
-#~ msgid "cannot change console mode ?!\n"
-#~ msgstr "kan konsole-modus nie verander nie ?!\n"
+#~ msgid ""
+#~ "--remote-wait-silent <files> Same, don't complain if there is no server"
+#~ msgstr ""
+#~ "--remote-wait-silent <lers> Dieselfde, moet nie kla as daar nie so 'n "
+#~ "bediener is nie"
-#~ msgid "mch_get_shellsize: not a console??\n"
-#~ msgstr "'mch_get_shellsize': nie 'n konsole nie??\n"
+#~ msgid ""
+#~ "--remote-wait <files> As --remote but wait for files to have been edited"
+#~ msgstr ""
+#~ "--remote-wait <lers> Soos '--remote', maar wag vir lers om gewysig te "
+#~ "word"
-#~ msgid "Cannot execute "
-#~ msgstr "Kan nie uitvoer nie "
+#~ msgid "--remote-silent <files> Same, don't complain if there is no server"
+#~ msgstr ""
+#~ "--remote-silent <lers> Dieselfde, moet nie kla as daar nie so 'n "
+#~ "bediener is nie"
-#~ msgid "shell "
-#~ msgstr "dop "
+#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible"
+#~ msgstr ""
+#~ "--remote <lers>\tWysig die <lers> in a Vim bediener indien moontlik"
-#~ msgid " returned\n"
-#~ msgstr " teruggekeer\n"
+#~ msgid "-X\t\t\tDo not connect to X server"
+#~ msgstr "-X\t\t\tMoet nie verbinding met X-bediener maak nie"
-#~ msgid "ANCHOR_BUF_SIZE too small."
-#~ msgstr "'ANCHOR_BUF_SIZE' is te klein"
+#~ msgid "-display <display>\tConnect vim to this particular X-server"
+#~ msgstr "-display <display>\tKoppel vim aan hierdie X-bediener"
-#~ msgid "I/O ERROR"
-#~ msgstr "I/O FOUT"
+#~ msgid "-x\t\t\tEdit encrypted files"
+#~ msgstr "-x\t\t\tBewerk genkripteerde lers"
-#~ msgid "...(truncated)"
-#~ msgstr "...(afgekap)"
+#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
+#~ msgstr "-U <gvimrc>\t\tGebruik <gvimrc> in plaas van enige .gvimrc"
-#~ msgid "'columns' is not 80, cannot execute external commands"
-#~ msgstr "'columns' is nie 80 nie, kan nie eksterne bevele uitvoer nie"
+#~ msgid "-dev <device>\t\tUse <device> for I/O"
+#~ msgstr "-dev <toestel>\t\tGebruik <toestel> vir I/O"
-#~ msgid "to %s on %s"
-#~ msgstr "na %s op %s"
+#~ msgid "-f\t\t\tDon't use newcli to open window"
+#~ msgstr "-f\t\t\tMoet nie 'newcli' gebruik om venster oop te maak nie"
-#~ msgid "E613: Unknown printer font: %s"
-#~ msgstr "E613: Onbekende drukker font: %s"
+#~ msgid "-V[N]\t\tVerbose level"
+#~ msgstr "-V[N]\t\tOmslagtigheidsgraad"
-#~ msgid "E238: Print error: %s"
-#~ msgstr "E238: Drukfout: %s"
+#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
+#~ msgstr "-f of --nofork\tVoorgrond: Moenie vurk wanneer GUI begin nie"
-#~ msgid "Printing '%s'"
-#~ msgstr "Druk nou '%s'"
+#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")"
+#~ msgstr "-g\t\t\tVoer uit met die GUI (soos \"gvim\")"
-#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
-#~ msgstr "E244: Ongeldige karakterstelnaam \"%s\" in fontnaam \"%s\""
+#~ msgid "-unregister\t\tUnregister gvim for OLE"
+#~ msgstr "-unregister\t\tOnregistreer gvim vir OLE"
-#~ msgid "E245: Illegal char '%c' in font name \"%s\""
-#~ msgstr "E245: Ongeldige karakter '%c' in fontnaam \"%s\""
+#~ msgid "-register\t\tRegister this gvim for OLE"
+#~ msgstr "-register\t\tRegistreer hierdie gvim vir OLE"
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim: Dubbel sein, staak\n"
+#~ msgid "This Vim was not compiled with the diff feature."
+#~ msgstr "Hierdie Vim is nie gekompileer met 'diff' funksionaliteit nie."
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim: Het dodelike sein %s gevang\n"
+#~ msgid "E251: VIM instance registry property is badly formed. Deleted!"
+#~ msgstr "E251: VIM instansie register-kenmerk is swak gevorm. Geskrap!"
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim: Het dodelike sein gevang\n"
+#~ msgid "E248: Failed to send command to the destination program"
+#~ msgstr "E248: Het gefaal om bevel na doel program te stuur"
-#~ msgid "Opening the X display took %<PRId64> msec"
-#~ msgstr "Om die X-vertoonskerm oop te maak het %<PRId64> msek gevat"
+#~ msgid "Unable to register a command server name"
+#~ msgstr "Kon nie bevelbediener naam registreer nie"
+
+#~ msgid "cannot get line"
+#~ msgstr "kan nie rel kry nie"
#~ msgid ""
-#~ "\n"
-#~ "Vim: Got X error\n"
+#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim."
+#~ "org"
#~ msgstr ""
-#~ "\n"
-#~ "Vim: Het X fout ontvang\n"
-
-#~ msgid "Testing the X display failed"
-#~ msgstr "Toetsing van die X-vertoonskerm het gefaal"
-
-#~ msgid "Opening the X display timed out"
-#~ msgstr "Oopmaak van die X-vertoonskerm het uitgetel"
+#~ "E281: TCL FOUT: verlaatkode is nie 'n 'int'!? Rapporteer dit asb. aan "
+#~ "<vim-dev@vim.org>"
#~ msgid ""
-#~ "\n"
-#~ "Cannot execute shell sh\n"
+#~ "E571: Sorry, this command is disabled: the Tcl library could not be "
+#~ "loaded."
#~ msgstr ""
-#~ "\n"
-#~ "Kan nie dop 'sh' uitvoer nie\n"
+#~ "E571: Jammer, hierdie bevel is afgeskakel, die Tcl biblioteek kon nie "
+#~ "gelaai word nie."
-#~ msgid ""
-#~ "\n"
-#~ "Cannot create pipes\n"
+#~ msgid "cannot register callback command: buffer/window reference not found"
#~ msgstr ""
-#~ "\n"
-#~ "Kan nie pype skep nie\n"
+#~ "kan terugroepbevel nie registreer nie: buffer/vensterverwysing nie gevind "
+#~ "nie"
#~ msgid ""
-#~ "\n"
-#~ "Cannot fork\n"
+#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-"
+#~ "dev@vim.org"
#~ msgstr ""
-#~ "\n"
-#~ "Kan nie vurk nie\n"
+#~ "E280: TCL FATALE FOUT: verwlys korrup!? Rapporteer dit asb. aan <vim-"
+#~ "dev@vim.org>"
#~ msgid ""
-#~ "\n"
-#~ "Command terminated\n"
+#~ "cannot register callback command: buffer/window is already being deleted"
#~ msgstr ""
-#~ "\n"
-#~ "Bevel beindig\n"
+#~ "kan nie terugroepbevel registreer nie: buffer/venster word alreeds geskrap"
-#~ msgid "XSMP lost ICE connection"
-#~ msgstr "XSMP het ICE konneksie verloor"
+#~ msgid "cannot create buffer/window command: object is being deleted"
+#~ msgstr "kan nie buffer/venster bevel skep nie: voorwerp word geskrap"
-#~ msgid "Opening the X display failed"
-#~ msgstr "Oopmaak van die X vertoonskerm het gefaal"
+#~ msgid "vim error"
+#~ msgstr "vim fout"
-#~ msgid "XSMP handling save-yourself request"
-#~ msgstr "XSMP hanteer 'save-yourself' versoek"
+#~ msgid "keyboard interrupt"
+#~ msgstr "sleutelbordonderbreking"
-#~ msgid "XSMP opening connection"
-#~ msgstr "XSMP maak nou konneksie oop"
+#~ msgid "unknown vimOption"
+#~ msgstr "onbekende 'vimOption'"
-#~ msgid "XSMP ICE connection watch failed"
-#~ msgstr "XSMP ICE konneksie beloer het gefaal"
+#~ msgid "unknown flag: "
+#~ msgstr "onbekende vlag: "
-#~ msgid "XSMP SmcOpenConnection failed: %s"
-#~ msgstr "XSMP 'SmcOpenConnection' het gefaal: %s"
+#~ msgid "cannot insert/append line"
+#~ msgstr "kan nie rel invoeg/aanlas nie"
-#~ msgid "At line"
-#~ msgstr "By rel"
+#~ msgid "row %d column %d"
+#~ msgstr "ry %d kolom %d"
-#~ msgid "Could not allocate memory for command line."
-#~ msgstr "Kan nie geheue toeken vir bevelrel nie"
+#~ msgid "mark not set"
+#~ msgstr "merker nie gestel nie"
-#~ msgid "VIM Error"
-#~ msgstr "VIM Fout"
+#~ msgid "cannot set line(s)"
+#~ msgstr "kan nie rel(s) stel nie"
-#~ msgid "Could not load vim32.dll!"
-#~ msgstr "Kon nie 'vim32.dll' laai nie!"
+#~ msgid "unknown option"
+#~ msgstr "onbekende opsie"
-#~ msgid "Could not fix up function pointers to the DLL!"
-#~ msgstr "Kon nie funksiewysers na die DLL opstel nie!"
+#~ msgid "not implemented yet"
+#~ msgstr "nog nie gemplementeer nie"
-#~ msgid "shell returned %d"
-#~ msgstr "dop het %d gelewer"
+#~ msgid "Sniff: Error during write. Disconnected"
+#~ msgstr "Sniff: Fout gedurende stoor. Verbinding gebreek"
-#~ msgid "Vim: Caught %s event\n"
-#~ msgstr "Vim: Het %s gebeurtenis gevang\n"
+#~ msgid "E278: SNiFF+ not connected"
+#~ msgstr "E278: SNiFF+ is nie gekonnekteer nie"
-#~ msgid "close"
-#~ msgstr "maak toe"
+#~ msgid "E276: Error connecting to SNiFF+"
+#~ msgstr "E276: Fout in konnekteer met SNiFF+"
-#~ msgid "logoff"
-#~ msgstr "teken uit"
+#~ msgid "E275: Unknown SNiFF+ request: %s"
+#~ msgstr "E275: Onbekende SNiFF+ versoek: %s"
-#~ msgid "shutdown"
-#~ msgstr "sit af"
+#~ msgid "connected"
+#~ msgstr "gekonnekteer"
-#~ msgid "E371: Command not found"
-#~ msgstr "E371: Bevel nie gevind nie"
+#~ msgid "not "
+#~ msgstr "nie "
+
+#~ msgid "SNiFF+ is currently "
+#~ msgstr "SNiFF+ is tans"
+
+#~ msgid "E274: Sniff: Error during read. Disconnected"
+#~ msgstr "E274: Sniff: Fout gedurende lees. Verbinding gebreek"
#~ msgid ""
-#~ "VIMRUN.EXE not found in your $PATH.\n"
-#~ "External commands will not pause after completion.\n"
-#~ "See :help win32-vimrun for more information."
+#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in "
+#~ "$PATH).\n"
#~ msgstr ""
-#~ "'VIMRUN.EXE' nie gevind in '$PATH' nie.\n"
-#~ "Eksterne opdragte sal nie wag na voltooiing nie\n"
-#~ "Sien ':help win32-vimrun' vir meer inligting."
+#~ "Kan nie 'n verbinding met 'SNiFF+' maak nie. Kyk of die omgewing reg is "
+#~ "('sniffemacs' moet in '$PATH' gevind word).\n"
-#~ msgid "Vim Warning"
-#~ msgstr "Vim Waarskuwing"
+#~ msgid "Generate docu for"
+#~ msgstr "Genereer 'docu' vir"
-#~ msgid "E56: %s* operand could be empty"
-#~ msgstr "E56: %s* operand mag leeg wees"
+#~ msgid "Show docu of"
+#~ msgstr "Wys 'docu' van"
-#~ msgid "E57: %s+ operand could be empty"
-#~ msgstr "E57: %s+ operand mag leeg wees"
+#~ msgid "Xref used by"
+#~ msgstr "Xref gebruik deur"
-#~ msgid "E58: %s{ operand could be empty"
-#~ msgstr "E58: %s{ operand mag leeg wees"
+#~ msgid "Xref has a"
+#~ msgstr "Xref het 'n"
-#~ msgid "E361: Crash intercepted; regexp too complex?"
-#~ msgstr "E361: Ineenstorting onderskep. Patroon te kompleks?"
+#~ msgid "Xref referred by"
+#~ msgstr "Xref verwys deur"
-#~ msgid "E363: pattern caused out-of-stack error"
-#~ msgstr "E363: patroon het le-stapel fout veroorsaak"
+#~ msgid "Xref refers to"
+#~ msgstr "Xref verwys na"
-#~ msgid "E396: containedin argument not accepted here"
-#~ msgstr "E396: 'containedin' parameter nie hier aanvaar nie"
+#~ msgid "Show class in restricted hierarchy"
+#~ msgstr "Wys klas in beperkte hirargie"
-#~ msgid "Enter nr of choice (<CR> to abort): "
-#~ msgstr "Sleutel nommer van keuse in (<CR> om te stop): "
+#~ msgid "Show class in hierarchy"
+#~ msgstr "Wys klas in hirargie"
-#~ msgid "E430: Tag file path truncated for %s\n"
-#~ msgstr "E430: Etiketlergids afgekap vir %s\n"
+#~ msgid "Browse class"
+#~ msgstr "Kyk klas deur"
-#~ msgid "new shell started\n"
-#~ msgstr "nuwe dop begin\n"
+#~ msgid "Find symbol"
+#~ msgstr "Vind simbool"
-#~ msgid "No undo possible; continue anyway"
-#~ msgstr "Geen herstel moontlik; gaan in elk geval voort"
+#~ msgid "Show source of"
+#~ msgstr "Wys kode van"
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 16/32-bit GUI version"
-#~ msgstr ""
-#~ "\n"
-#~ "MS-Windows 16/32-bis GUI weergawe"
+#~ msgid "Retrieve"
+#~ msgstr "Gaan haal"
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 32-bit GUI version"
-#~ msgstr ""
-#~ "\n"
-#~ "MS-Windows 32-bis GUI version"
+#~ msgid "Retrieve from all projects"
+#~ msgstr "Gaan haal uit alle projekte"
-#~ msgid " in Win32s mode"
-#~ msgstr " in Win32s modus"
+#~ msgid "Retrieve from project"
+#~ msgstr "Gaan haal uit projek"
-#~ msgid " with OLE support"
-#~ msgstr " met OLE ondersteuning"
+#~ msgid "Retrieve from file"
+#~ msgstr "Gaan haal uit ler"
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 32-bit console version"
-#~ msgstr ""
-#~ "\n"
-#~ "MS-Windows 32-bis konsole weergawe"
+#~ msgid "Show overridden member function"
+#~ msgstr "Wys vervangde lidfunksie"
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 16-bit version"
-#~ msgstr ""
-#~ "\n"
-#~ "MS-Windows 16-bis weergawe"
+#~ msgid "Show base class of"
+#~ msgstr "Wys basisklas van"
-#~ msgid ""
-#~ "\n"
-#~ "32-bit MS-DOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "32-bis MS-DOS weergawe"
+#~ msgid "Toggle implementation/definition"
+#~ msgstr "Stel en herstel implimentasie/definisie"
-#~ msgid ""
-#~ "\n"
-#~ "16-bit MS-DOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "16-bis MS-DOS weergawe"
+#~ msgid "E273: unknown longjmp status %d"
+#~ msgstr "E273: Onbekende 'longjmp' status %d"
#~ msgid ""
-#~ "\n"
-#~ "MacOS X (unix) version"
+#~ "E266: Sorry, this command is disabled, the Ruby library could not be "
+#~ "loaded."
#~ msgstr ""
-#~ "\n"
-#~ "MacOS X (unix) weergawe"
+#~ "E266: Jammer, hierdie bevel is afgeskakel, die Ruby biblioteekler kon "
+#~ "nie gelaai word nie."
-#~ msgid ""
-#~ "\n"
-#~ "MacOS X version"
-#~ msgstr ""
-#~ "\n"
-#~ "MacOS X weergawe"
+#~ msgid "string cannot contain newlines"
+#~ msgstr "string kan nie 'newlines' bevat nie"
-#~ msgid ""
-#~ "\n"
-#~ "MacOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "MacOS weergawe"
+#~ msgid "cannot insert line"
+#~ msgstr "kan rel nie byvoeg nie"
+
+#~ msgid "cannot replace line"
+#~ msgstr "kan rel nie vervang nie"
+
+#~ msgid "cannot delete line"
+#~ msgstr "kan rel nie verwyder nie"
+
+#~ msgid "no such window"
+#~ msgstr "geen sodanige venster nie"
+
+#~ msgid "<window %d>"
+#~ msgstr "<venster %d>"
+
+#~ msgid "<window object (unknown) at %.8lX>"
+#~ msgstr "<verwyder voorwerp (onbekend) by %.8lX>"
+
+#~ msgid "<window object (deleted) at %.8lX>"
+#~ msgstr "<venster voorwerp (geskrap) by %.8lX>"
+
+#~ msgid "cursor position outside buffer"
+#~ msgstr "loperposisie buite buffer"
+
+#~ msgid "readonly attribute"
+#~ msgstr "leesalleen eienskap"
+
+#~ msgid "attempt to refer to deleted window"
+#~ msgstr "poging om na geskrapte venster te verwys"
+
+#~ msgid "no such buffer"
+#~ msgstr "buffer bestaan nie"
+
+#~ msgid "invalid mark name"
+#~ msgstr "onbekende merknaam"
+
+#~ msgid "<buffer object (deleted) at %8lX>"
+#~ msgstr "<buffervoorwerp (geskrap) by %8lX>"
+
+#~ msgid "line number out of range"
+#~ msgstr "relnommer buite omvang"
+
+#~ msgid "attempt to refer to deleted buffer"
+#~ msgstr "poging om na 'n geskrapte buffer te verwys"
+
+#~ msgid "expressions disabled at compile time"
+#~ msgstr "uitdrukkings afgeskakel tydens kompilering"
+
+# njj: net 'n voorstel ..
+#~ msgid "invalid expression"
+#~ msgstr "ongeldige uitdrukking"
+
+#~ msgid "E264: Python: Error initialising I/O objects"
+#~ msgstr "E264: Python: Kon nie I/O objekte inwy nie"
+
+#~ msgid "writelines() requires list of strings"
+#~ msgstr "'writelines()' benodig 'n lys van stringe"
+
+#~ msgid "invalid attribute"
+#~ msgstr "ongeldige eienskap"
+
+#~ msgid "softspace must be an integer"
+#~ msgstr "'softspace' moet 'n heelgetal wees"
+
+#~ msgid "can't delete OutputObject attributes"
+#~ msgstr "kan nie 'OutputObject' eienskappe skrap nie"
+
+#~ msgid "E659: Cannot invoke Python recursively"
+#~ msgstr "E659: Kan nie Python rekursief roep nie"
#~ msgid ""
-#~ "\n"
-#~ "RISC OS version"
+#~ "E263: Sorry, this command is disabled, the Python library could not be "
+#~ "loaded."
#~ msgstr ""
-#~ "\n"
-#~ "RISC OS weergawe"
+#~ "E263: Jammer, hierdie bevel is afgeskakel, die Python biblioteek ler kon "
+#~ "nie gelaai word nie."
+
+#~ msgid "E569: maximum number of cscope connections reached"
+#~ msgstr "E569: maksimum aantal 'cscope' verbindings bereik"
+
+#~ msgid "E626: cannot get cscope database information"
+#~ msgstr "E626: kan nie 'cscope' databasisinligting kry nie"
+
+#~ msgid "E625: cannot open cscope database: %s"
+#~ msgstr "E625: Kon nie 'cscope' databasis oopmaak nie: %s"
+
+#~ msgid "E563: stat error"
+#~ msgstr "E563: 'stat' fout"
+
+#~ msgid "E256: Hangul automata ERROR"
+#~ msgstr "E256: Hangul outomatiserings FOUT"
#~ msgid ""
+#~ "Font1 width: %<PRId64>\n"
#~ "\n"
-#~ "Big version "
#~ msgstr ""
+#~ "Font1 wydte: %<PRId64>\n"
#~ "\n"
-#~ "Groot weergawe "
-#~ msgid ""
-#~ "\n"
-#~ "Normal version "
+#~ msgid "Font0 width: %<PRId64>\n"
+#~ msgstr "Font0 wydte: %<PRId64>\n"
+
+#~ msgid "Font%<PRId64> width is not twice that of font0\n"
+#~ msgstr "Font%<PRId64> wydte is nie twee keer de van font0 nie\n"
+
+#~ msgid "Font1: %s\n"
+#~ msgstr "Font1: %s\n"
+
+#~ msgid "Font0: %s\n"
+#~ msgstr "Font0: %s\n"
+
+#~ msgid "E253: Fontset name: %s\n"
+#~ msgstr "E253: Fonstel naam: %s\n"
+
+#~ msgid "Font '%s' is not fixed-width"
+#~ msgstr "Font '%s' is nie 'n vaste-wydte font nie"
+
+#~ msgid "E252: Fontset name: %s"
+#~ msgstr "E252: Fontstel naam: %s"
+
+#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:"
#~ msgstr ""
-#~ "\n"
-#~ "Normale weergawe "
+#~ "E250: Fonte vir die volgende karakterstelle ontbreek in fontversameling "
+#~ "%s:"
#~ msgid ""
-#~ "\n"
-#~ "Small version "
+#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
#~ msgstr ""
-#~ "\n"
-#~ "Klein weergawe "
+#~ "Vim E458: Kan nie kleurkaart-inskrywing toeken nie, sommige kleure mag "
+#~ "verkeerd wees"
-#~ msgid ""
-#~ "\n"
-#~ "Tiny version "
+#~ msgid "Find & Replace (use '\\\\' to find a '\\')"
+#~ msgstr "Vind & vervang string (gebruik '\\\\' om 'n '\\' te vind"
+
+#~ msgid "Find string (use '\\\\' to find a '\\')"
+#~ msgstr "Vind string (gebruik '\\\\' om 'n '\\' te vind"
+
+#~ msgid "E672: Unable to open window inside MDI application"
+#~ msgstr "E672: Kon nie venster oopmaak binne 'n MDI toepassing nie"
+
+#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
+#~ msgstr "E243: Parameter nie bekend: \"-%s\"; Gebruik die OLE weergawe."
+
+#~ msgid "Undo"
+#~ msgstr "Herroep"
+
+#~ msgid "Selection"
+#~ msgstr "Seleksie"
+
+#~ msgid "Files"
+#~ msgstr "Lers"
+
+#~ msgid "Help"
+#~ msgstr "Hulp"
+
+#~ msgid "Directories"
+#~ msgstr "Gidse"
+
+#~ msgid "Filter"
+#~ msgstr "Filter"
+
+#~ msgid "Used CUT_BUFFER0 instead of empty selection"
+#~ msgstr "'CUT_BUFFER0' is gebruik in plaas van le seleksie"
+
+#~ msgid "Font Selection"
+#~ msgstr "Fontkeuse"
+
+#~ msgid "Vim: Main window unexpectedly destroyed\n"
+#~ msgstr "Vim: Hoofvenster onverwags verwoes\n"
+
+#~ msgid "Vim: Received \"die\" request from session manager\n"
+#~ msgstr "Vim: Het die \"die\" opdrag ontvang van sessiebestuurder\n"
+
+#~ msgid "Replace All"
+#~ msgstr "Vervang alles"
+
+#~ msgid "Replace"
+#~ msgstr "Vervang"
+
+#~ msgid "Find Next"
+#~ msgstr "Vind volgende"
+
+#~ msgid "Down"
+#~ msgstr "Af"
+
+#~ msgid "Up"
+#~ msgstr "Op"
+
+#~ msgid "Direction"
+#~ msgstr "Rigting"
+
+#~ msgid "Match case"
+#~ msgstr "Tref kas"
+
+#~ msgid "Match whole word only"
+#~ msgstr "Tref slegs presiese woord"
+
+#~ msgid "Replace with:"
+#~ msgstr "Vervang met:"
+
+#~ msgid "Find what:"
+#~ msgstr "Soek na:"
+
+#~ msgid "VIM - Search..."
+#~ msgstr "VIM - Soek..."
+
+#~ msgid "VIM - Search and Replace..."
+#~ msgstr "VIM - Soek en Vervang..."
+
+#~ msgid "Input _Methods"
+#~ msgstr "Invoer _Metodes"
+
+#~ msgid "Vim dialog..."
+#~ msgstr "Vim dialooghokkie..."
+
+#~ msgid "E599: Value of 'imactivatekey' is invalid"
+#~ msgstr "E599: Waarde van 'imactivatekey' is ongeldig"
+
+#~ msgid "E231: 'guifontwide' invalid"
+#~ msgstr "E231: 'guifontwide' ongeldig"
+
+#~ msgid "E665: Cannot start GUI, no valid font found"
+#~ msgstr "E665: Kan nie GUI begin nie, geen geldige font gevind nie"
+
+#~ msgid "E230: Cannot read from \"%s\""
+#~ msgstr "E230: Kan nie lees uit \"%s\" nie"
+
+#~ msgid "E229: Cannot start the GUI"
+#~ msgstr "E229: Kan nie die GUI begin nie"
+
+#~ msgid "E232: Cannot create BalloonEval with both message and callback"
+#~ msgstr "E232: Kan nie BalloonEval skep met beide boodskap en terugroep nie"
+
+#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
#~ msgstr ""
-#~ "\n"
-#~ "Piepklein weergawe "
+#~ "Rolstaafelement: Kon nie pikselmatriks-duimnael se geometrie kry nie"
-#~ msgid "with GTK2-GNOME GUI."
-#~ msgstr "met GTK2-GNOME GUI."
+#~ msgid "Vim dialog"
+#~ msgstr "Vim dialooghokkie"
-#~ msgid "with GTK-GNOME GUI."
-#~ msgstr "met GTK-GNOME GUI."
+#~ msgid "Cancel"
+#~ msgstr "Kanselleer"
-#~ msgid "with GTK2 GUI."
-#~ msgstr "met GTK2 GUI"
+#~ msgid "OK"
+#~ msgstr "OK"
-#~ msgid "with GTK GUI."
-#~ msgstr "met GTK GUI"
+#~ msgid "E615: vim_SelFile: can't get current directory"
+#~ msgstr "E615: vim_SelFile: Kan nie huidige gids verkry nie"
-#~ msgid "with X11-Motif GUI."
-#~ msgstr "met X11-Motif GUI."
+#~ msgid "Pathname:"
+#~ msgstr "Gidsnaam:"
-#~ msgid "with X11-neXtaw GUI."
-#~ msgstr "met X11-neXtaw GUI"
+#~ msgid "E614: vim_SelFile: can't return to current directory"
+#~ msgstr "E614: 'vim_SelFile': Kan nie terugkeer na huidige gids nie"
-#~ msgid "with X11-Athena GUI."
-#~ msgstr "met X11-Athena GUI"
+#~ msgid "E616: vim_SelFile: can't get font %s"
+#~ msgstr "E616: 'vim_SelFile': kan font %s nie kry nie"
-#~ msgid "with BeOS GUI."
-#~ msgstr "met BeOS GUI"
+#~ msgid "<cannot open> "
+#~ msgstr "<kan nie oopmaak nie> "
-#~ msgid "with Photon GUI."
-#~ msgstr "met Photon GUI."
+#~ msgid "E460: The resource fork would be lost (add ! to override)"
+#~ msgstr "E460: Die hulpbronvurk sal verlore gaan (gebruik ! om te dwing)"
-#~ msgid "with GUI."
-#~ msgstr "met GUI."
+#~ msgid "Partial writes disallowed for NetBeans buffers"
+#~ msgstr "Gedeeltelike skryf word nie toegelaat vir NetBeans buffers nie"
-#~ msgid "with Carbon GUI."
-#~ msgstr "met Carbon GUI."
+#~ msgid "NetBeans disallows writes of unmodified buffers"
+#~ msgstr "NetBeans laat nie skryf toe van onveranderde buffers nie"
-#~ msgid "with Cocoa GUI."
-#~ msgstr "met Cocoa GUI."
+#~ msgid "[CONVERSION ERROR]"
+#~ msgstr "[OMSETTINGSFOUT]"
-#~ msgid "with (classic) GUI."
-#~ msgstr "met (klassieke) GUI."
+#~ msgid "[crypted]"
+#~ msgstr "[gekodeer]"
-#~ msgid " system gvimrc file: \""
-#~ msgstr " stelsel gvimrc-ler: \""
+#~ msgid "[NL found]"
+#~ msgstr "[NL gevind]"
-#~ msgid " user gvimrc file: \""
-#~ msgstr " gebruiker gvimrc-ler: \""
+#~ msgid "E196: No digraphs in this version"
+#~ msgstr "E196: Geen digrawe in hierdie weergawe nie"
-#~ msgid "2nd user gvimrc file: \""
-#~ msgstr "2de gebruiker gvimrc-ler: \""
+#~ msgid "Save Setup"
+#~ msgstr "Stoor konfigurasie"
-#~ msgid "3rd user gvimrc file: \""
-#~ msgstr "3de gebruiker gvimrc-ler: \""
+#~ msgid "Save Session"
+#~ msgstr "Stoor Sessie"
-#~ msgid " system menu file: \""
-#~ msgstr " stelsel kieslys-ler: \""
+#~ msgid "Save View"
+#~ msgstr "Stoor Oorsig"
-#~ msgid "Compiler: "
-#~ msgstr "Kompileerder: "
+#~ msgid "Save Redirection"
+#~ msgstr "Stoor Herversturing"
-#~ msgid "menu Help->Orphans for information "
-#~ msgstr "menu Hulp->Weeskinders vir meer inligting hieroor "
+#~ msgid "Window position: X %d, Y %d"
+#~ msgstr "Vensterposisie: X %d, Y %d"
-#~ msgid "Running modeless, typed text is inserted"
-#~ msgstr "Voer modus-loos uit, getikte teks word ingevoeg"
+#~ msgid "Append File"
+#~ msgstr "Las aan by ler"
-#~ msgid "menu Edit->Global Settings->Toggle Insert Mode "
-#~ msgstr "menu Redigeer->Globale verstellings->Stel en herstel Invoeg Modus"
+#~ msgid "Edit File in new window"
+#~ msgstr "Bewerk ler in nuwe venster"
-#~ msgid " for two modes "
-#~ msgstr " vir twee modusse "
+#~ msgid " (NOT FOUND)"
+#~ msgstr " (NIE GEVIND NIE)"
-#~ msgid "menu Edit->Global Settings->Toggle Vi Compatible"
-#~ msgstr "menu Redigeer->Global verstellings->Stel en herstel Vi Versoenbaar"
+#~ msgid "Edit File"
+#~ msgstr "Verander ler"
-#~ msgid " for Vim defaults "
-#~ msgstr " vir Vim verstekwaardes"
+#~ msgid "Source Vim script"
+#~ msgstr "Voer Vim skrip uit"
-#~ msgid "WARNING: Windows 95/98/ME detected"
-#~ msgstr "WAARSKUWING: Windows 95/98/ME bespeur"
+#~ msgid "Save As"
+#~ msgstr "Stoor As"
-#~ msgid "type :help windows95<Enter> for info on this"
-#~ msgstr "tik :help windows95<Enter> vir meer inligting hieroor"
+#~ msgid "E130: Undefined function: %s"
+#~ msgstr "E130: Ongedefinieerde funksie: %s"
-#~ msgid "E370: Could not load library %s"
-#~ msgstr "E370: Kon nie biblioteek laai nie %s"
+#~ msgid "E277: Unable to read a server reply"
+#~ msgstr "E277: Kon bediener-terugvoer nie lees nie"
+
+#~ msgid "E240: No connection to Vim server"
+#~ msgstr "E240: Geen verbinding met Vim bediener"
#~ msgid ""
-#~ "Sorry, this command is disabled: the Perl library could not be loaded."
+#~ "&OK\n"
+#~ "&Cancel"
#~ msgstr ""
-#~ "Jammer, hierdie bevel is afgeskakel: die Perl biblioteek kon nie gelaai "
-#~ "word nie."
+#~ "&OK\n"
+#~ "&Kanselleer"
-#~ msgid "E299: Perl evaluation forbidden in sandbox without the Safe module"
-#~ msgstr ""
-#~ "E299: Perl evaluasie verbied in die sandput sonder die 'Safe' module"
+#~ msgid "E106: Unknown variable: \"%s\""
+#~ msgstr "E106: Onbekende veranderlike: \"%s\""
-#~ msgid "Edit with &multiple Vims"
-#~ msgstr "Wysig met &meer as een Vim"
+#~ msgid "Patch file"
+#~ msgstr "Laslap ler"
-#~ msgid "Edit with single &Vim"
-#~ msgstr "Wysig met 'n enkel &Vim"
+#~ msgid "[No File]"
+#~ msgstr "[Geen ler]"
-#~ msgid "&Diff with Vim"
-#~ msgstr "Wys verskille ('&diff') met Vim"
+#~ msgid "[Error List]"
+#~ msgstr "[Foutlys]"
-#~ msgid "Edit with &Vim"
-#~ msgstr "Wysig met &Vim"
+#~ msgid "type :help cp-default<Enter> for info on this"
+#~ msgstr "tik :help cp-default<Enter> vir meer inligting hieroor"
-#~ msgid "Edit with existing Vim - &"
-#~ msgstr "Wysig met bestaande Vim - &"
+#~ msgid "type :set nocp<Enter> for Vim defaults"
+#~ msgstr "tik :set nocp<Enter> vir Vim verstekwaardes "
-#~ msgid "Edits the selected file(s) with Vim"
-#~ msgstr "Wysig die gekose ler(s) met Vim"
+#~ msgid "Running in Vi compatible mode"
+#~ msgstr "Voer tans uit in Vi-versoenbare modus"
-#~ msgid "Error creating process: Check if gvim is in your path!"
-#~ msgstr "FOut met die skep van proses: Kyk of gvim in jou pad is!"
+#~ msgid "type :help version7<Enter> for version info"
+#~ msgstr "tik :help version7<Enter> vir weergawe-inligting"
-#~ msgid "gvimext.dll error"
-#~ msgstr "'gvimext.dll' fout"
+#~ msgid "type :help<Enter> or <F1> for on-line help"
+#~ msgstr "tik :help<Enter> of <F1> vir aanlyn hulp "
-#~ msgid "Path length too long!"
-#~ msgstr "Pad-lengte te lank"
+#~ msgid "by Bram Moolenaar et al."
+#~ msgstr "deur Bram Moolenaar et al."
-#~ msgid "E234: Unknown fontset: %s"
-#~ msgstr "E234: Onbekende fontstel: %s"
+# njj: :))
+#~ msgid "version "
+#~ msgstr "Weergawe "
-#~ msgid "E235: Unknown font: %s"
-#~ msgstr "E235: Onbekende font: %s"
+#~ msgid "VIM - Vi IMproved"
+#~ msgstr "VIM - Vi Met skop"
-#~ msgid "E448: Could not load library function %s"
-#~ msgstr "E448: Kon nie biblioteek funksie laai nie %s"
+#~ msgid " DEBUG BUILD"
+#~ msgstr " ONTFOUTINGS-KOMPILERING"
-#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
+#~ msgid "Linking: "
+#~ msgstr "Koppeling: "
+
+#~ msgid "Compilation: "
+#~ msgstr "Kompilering: "
+
+#~ msgid " 2nd user exrc file: \""
+#~ msgstr " 2de gebruiker exrc-ler: \""
+
+#~ msgid " user exrc file: \""
+#~ msgstr " gebruiker exrc-ler: \""
+
+#~ msgid " 3rd user vimrc file: \""
+#~ msgstr " 3de gebruiker vimrc-ler \""
+
+#~ msgid " 2nd user vimrc file: \""
+#~ msgstr " 2de gebruiker vimrc-ler \""
+
+#~ msgid " user vimrc file: \""
+#~ msgstr " gebruiker vimrc-ler: \""
+
+#~ msgid " Features included (+) or not (-):\n"
+#~ msgstr " Kenmerke in- (+) of uitgesluit (-):\n"
+
+#~ msgid "without GUI."
+#~ msgstr "sonder GUI."
+
+#~ msgid ""
+#~ "\n"
+#~ "Huge version "
#~ msgstr ""
-#~ "E26: Hebreeus kan nie gebruik word nie: Nie tydens kompilering gekies "
-#~ "nie\n"
+#~ "\n"
+#~ "Enorme weergawe "
-#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
+#~ msgid "Modified by "
+#~ msgstr "Gewysig deur "
+
+#, fuzzy
+#~ msgid ""
+#~ "\n"
+#~ "Extra patches: "
+#~ msgstr "Eksterne subtreffers:\n"
+
+#~ msgid ""
+#~ "\n"
+#~ "Included patches: "
#~ msgstr ""
-#~ "E27: Farsi kan nie gebruik word nie: Nie tydens kompilering gekies nie\n"
+#~ "\n"
+#~ "Ingeslote laslappies:"
-#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
+#~ msgid ""
+#~ "\n"
+#~ "--- Terminal keys ---"
#~ msgstr ""
-#~ "E800: Arabies kan nie gebruik word nie: Nie tydens kompilering gekies "
-#~ "nie\n"
+#~ "\n"
+#~ "--- Terminaal sleutels ---"
-#~ msgid "E247: no registered server named \"%s\""
-#~ msgstr "E247: geen geregistreerde bediener genaamd \"%s\""
+#~ msgid "E437: terminal capability \"cm\" required"
+#~ msgstr "E437: terminaalvermo \"cm\" vereis"
-#~ msgid "E233: cannot open display"
-#~ msgstr "E233: kan nie vertoonskerm oopmaak nie"
+#~ msgid "E436: No \"%s\" entry in termcap"
+#~ msgstr "E436: Geen \"%s\" inskrywing in termcap nie"
-#~ msgid "E463: Region is guarded, cannot modify"
-#~ msgstr "E463: Omgewing is onder bewaking, kan nie verander nie"
+#~ msgid "E559: Terminal entry not found in termcap"
+#~ msgstr "E559: Terminaalinskrywing nie in 'termcap' gevind nie"
-#~ msgid "function "
-#~ msgstr "funksie "
+#~ msgid "E558: Terminal entry not found in terminfo"
+#~ msgstr "E558: Terminaalinskrywing nie in 'terminfo' gevind nie"
-#~ msgid "Run Macro"
-#~ msgstr "Voer Makro uit"
+#~ msgid "E557: Cannot open termcap file"
+#~ msgstr "E557: Kan nie 'termcap'-ler oopmaak nie"
-#~ msgid "E242: Color name not recognized: %s"
-#~ msgstr "E242: Kleurnaam is onbekend: %s"
+#~ msgid "defaulting to '"
+#~ msgstr "gebruik verstek '"
-#~ msgid "error reading cscope connection %d"
-#~ msgstr "'cscope' verbinding %d kon nie gelees word nie"
+#~ msgid "' not known. Available builtin terminals are:"
+#~ msgstr "' onbekend. Beskikbare ingeboude terminale is:"
-#~ msgid "E260: cscope connection not found"
-#~ msgstr "E260: 'cscope' verbinding nie gevind nie"
+#~ msgid "E422: terminal code too long: %s"
+#~ msgstr "E422: terminaalkode te lank: %s"
-#~ msgid "cscope connection closed"
-#~ msgstr "'cscope' verbinding gesluit"
+#, fuzzy
+#~ msgid "Substitute "
+#~ msgstr "1 vervanging"
-# njj: dalk 'verbinding' ipv 'verbinding' orals?
-#~ msgid "couldn't malloc\n"
-#~ msgstr "kon nie 'malloc' nie\n"
+#~ msgid " (lang)"
+#~ msgstr " (taal)"
-#~ msgid "%2d %-5ld %-34s <none>\n"
-#~ msgstr "%2d %-5ld %-34s <geen>\n"
+#~ msgid ""
+#~ "\n"
+#~ "Cannot execute shell "
+#~ msgstr ""
+#~ "\n"
+#~ "Kan nie dop uitvoer nie "
-#~ msgid "E249: couldn't read VIM instance registry property"
-#~ msgstr "E249: kon nie VIM instansie register-kenmerk lees nie"
+#~ msgid "E522: Not found in termcap"
+#~ msgstr "E522: Nie gevind in 'termcap' nie"
-#~ msgid "\"\n"
-#~ msgstr "\"\n"
+#~ msgid "Thanks for flying Vim"
+#~ msgstr "Dankie dat jy vlieg met Vim"
-#~ msgid "--help\t\tShow Gnome arguments"
-#~ msgstr "--help\t\tWys Gnome parameters"
+#~ msgid "%<%f%h%m%=Page %N"
+#~ msgstr "%<%f%h%m%=Bladsy %N"
-#~ msgid "1 line ~ed"
-#~ msgstr "1 rel ge-~"
+#~ msgid "E574: Unknown register type %d"
+#~ msgstr "E574: Onbekende registertipe %d"
-#~ msgid "%<PRId64> lines ~ed"
-#~ msgstr "%<PRId64> rels ge-~"
+#~ msgid ""
+#~ "\n"
+#~ "# Registers:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# Registers:\n"
-#~ msgid " BLOCK"
-#~ msgstr " BLOK"
+#~ msgid "Illegal register name"
+#~ msgstr "Ongeldige registernaam"
-#~ msgid " LINE"
-#~ msgstr " REL"
+#~ msgid "cannot yank; delete anyway"
+#~ msgstr "kan nie pluk nie: verwyder in elk geval"
-#~ msgid "Linear tag search"
-#~ msgstr "Linire etiketsoek"
+#~ msgid "E337: Menu not found - check menu names"
+#~ msgstr "E337: Kieslys nie gevind nie - maak seker oor die kieslys name"
-#~ msgid "Binary tag search"
-#~ msgstr "Binre etiketsoek"
+#~ msgid "E336: Menu path must lead to a sub-menu"
+#~ msgstr "E336: Kieslyspad moet lei na 'n sub-kieslys"
-#~ msgid "E258: no matches found in cscope connections"
-#~ msgstr "E258: geen treffers gevind in 'cscope' verbindings nie"
+#~ msgid "Swap file already exists!"
+#~ msgstr "Ruiller bestaan alreeds!"
-#~ msgid "No servers found for this display"
-#~ msgstr "Geen bedieners gevind vir die 'display' nie"
+#, fuzzy
+#~ msgid " Quit, or continue with caution.\n"
+#~ msgstr " Stop, of gaan versigtig voort.\n"
-#~ msgid "Missing filename"
-#~ msgstr "Ontbrekende lernaam"
+#~ msgid "Missing '>'"
+#~ msgstr "Ontbrekende '>'"
-#~ msgid "Invalid line number: %<PRId64>"
-#~ msgstr "Ongeldige relnommer: %<PRId64>"
+#~ msgid ""
+#~ "\n"
+#~ "# History of marks within files (newest to oldest):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# Geskiedenis van merkers in lers (nuutste tot oudste):\n"
-#~ msgid "Cannot use :normal from event handler"
-#~ msgstr "Kan ':normal' nie vanuit gebeurtenishanteerder gebruik nie"
+#~ msgid ""
+#~ "\n"
+#~ "# Jumplist (newest first):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# Springlys (nuutste eerste):\n"
-#~ msgid "VIM - Help on..."
-#~ msgstr "VIM - Hulp met.."
+#~ msgid ""
+#~ "\n"
+#~ "# File marks:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# Lermerkers:\n"
-#~ msgid "Topic:"
-#~ msgstr "Onderwerp:"
+#~ msgid "-h or --help\tPrint Help (this message) and exit"
+#~ msgstr "-h of --help\tSkryf Hulp (hierdie boodskap) en sluit"
-#~ msgid "Error: During loading fontset %s"
-#~ msgstr "Fout: Gedurende die laai van fontstel %s"
+#~ msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
+#~ msgstr "-i <viminfo>\t\tGebruik <viminfo> in plaas van .viminfo"
-#~ msgid "locale is not set correctly"
-#~ msgstr "lokaal is nie korrek gestel nie"
+#~ msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>"
+#~ msgstr "-W <skripuit>\tSkryf alle getikte bevele na ler <skripuit>"
-#~ msgid "For korean:"
-#~ msgstr "Vir Afrikaans:"
+#~ msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>"
+#~ msgstr "-w <skripuit>\tLas alle getikte bevele aan by ler <skripuit>"
-#~ msgid " csh: setenv LANG ko"
-#~ msgstr " csh: setenv LANG af"
+#~ msgid "+<lnum>\t\tStart at line <lnum>"
+#~ msgstr "+<lnum>\t\tBegin by rel <lnum>"
-#~ msgid " sh : export LANG=ko"
-#~ msgstr " sh: export LANG=af"
+#~ msgid "+\t\t\tStart at end of file"
+#~ msgstr "+\t\t\tBegin by einde van ler"
-#~ msgid "fontset name: %s"
-#~ msgstr "fontstel naam: %s"
+#~ msgid "-O[N]\t\tLike -o but split vertically"
+#~ msgstr "-O[N]\t\tSoos -o maar verdeel vertikaal"
-#~ msgid "Your language Font missing"
-#~ msgstr "Jou taal Font ontbreek"
+#~ msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
+#~ msgstr "-u <vimrc>\t\tGebruik <vimrc> in plaas van enige ander .vimrc"
-#~ msgid "automata ERROR: internal"
-#~ msgstr "automata FOUT: intern"
+#~ msgid "-T <terminal>\tSet terminal type to <terminal>"
+#~ msgstr "-T <terminaal>\tStel terminaaltipe na <terminaal>"
-#~ msgid "cs_add_common: alloc fail #1"
-#~ msgstr "'cs_add_common': toeken onsuksesvol #1"
+#~ msgid "-F\t\t\tStart in Farsi mode"
+#~ msgstr "-F\t\t\tBegin in Farsi modus"
-#~ msgid "cs_add_common: alloc fail #2"
-#~ msgstr "'cs_add_common': toeken onsuksesvol #2"
+#~ msgid "-H\t\t\tStart in Hebrew mode"
+#~ msgstr "-H\t\t\tBegin in Hebreeuse modus"
-#~ msgid "cs_add_common: alloc fail #3"
-#~ msgstr "'cs_add_common': toeken onsuksesvol #3"
+#~ msgid "-A\t\t\tstart in Arabic mode"
+#~ msgstr "-A\t\t\tbegin in Arabiese modus"
-#~ msgid "cs_add_common: alloc fail #4"
-#~ msgstr "'cs_add_common': toeken onsuksesvol #4"
+#~ msgid "-L\t\t\tSame as -r"
+#~ msgstr "-L\t\t\tSelfde as -r"
-#~ msgid "Retrieve next symbol"
-#~ msgstr "Kry volgende simbool"
+#~ msgid "-r (with file name)\tRecover crashed session"
+#~ msgstr "-r (met ler naam)\tHerwin ineengestorte sessie"
-#~ msgid "-- SNiFF+ commands --"
-#~ msgstr "-- SNiFF+ bevele --"
+#~ msgid "-r\t\t\tList swap files and exit"
+#~ msgstr "-r\t\t\tLys ruillers en verlaat vim"
-#~ msgid "Unrecognized sniff request [%s]"
-#~ msgstr "Onbekende sniff versoek [%s]"
+#~ msgid "-D\t\t\tDebugging mode"
+#~ msgstr "-D\t\t\tOntfoutmodus"
-#~ msgid "Can't create input context."
-#~ msgstr "Kan nie invoerkonteks skep nie."
+#~ msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'"
+#~ msgstr "-N\t\t\tNie ten volle Vi-versoenbaar nie: 'nocompatible'"
-#~ msgid "Sorry, deleting a menu is not possible in the Athena version"
-#~ msgstr ""
-#~ "Jammer, in die Athena weergawe is dit onmoontlik om 'n kieslys te skrap"
+#~ msgid "-C\t\t\tCompatible with Vi: 'compatible'"
+#~ msgstr "-C\t\t\tVersoenbaar met Vi: 'compatible'"
-#~ msgid "Out of memory"
-#~ msgstr "Geheue op"
+#~ msgid "-l\t\t\tLisp mode"
+#~ msgstr "-l\t\t\tLisp modus"
-#~ msgid "PC (32 bits Vim)"
-#~ msgstr "PC (32 bisse Vim)"
+#~ msgid "-b\t\t\tBinary mode"
+#~ msgstr "-b\t\t\tBinre modus"
-#~ msgid "PC (16 bits Vim)"
-#~ msgstr "PC (16 bisse Vim)"
+#~ msgid "-Z\t\t\tRestricted mode (like \"rvim\")"
+#~ msgstr "-Z\t\t\tBeperkte modus (soos \"rvim\")"
-#~ msgid "Unsupported screen mode"
-#~ msgstr "Ongesteunde skermmodus"
+#~ msgid "-R\t\t\tReadonly mode (like \"view\")"
+#~ msgstr "-R\t\t\tLeesalleen modus (soos \"view\")"
-#~ msgid "deadly signal"
-#~ msgstr "dodelike sein"
+#~ msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
+#~ msgstr "-y\t\t\tEasy modus (soos \"evim\", modusloos)"
-#~ msgid "some"
-#~ msgstr "sommige"
+#~ msgid "-d\t\t\tDiff mode (like \"vimdiff\")"
+#~ msgstr "-d\t\t\tDiff modus (soos \"vimdiff\")"
-#~ msgid "Library call failed"
-#~ msgstr "Biblioteekfunksieroep het gefaal"
+#~ msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")"
+#~ msgstr "-s\t\t\tStil (bondel) modus (slegs vir \"ex\")"
-#~ msgid "Cannot clear all highlight groups"
-#~ msgstr "Kan nie alle uitliggroepe leegmaak nie"
+#~ msgid "-e\t\t\tEx mode (like \"ex\")"
+#~ msgstr "-e\t\t\tEx modus (soos \"ex\")"
-#~ msgid "GUI is not running"
-#~ msgstr "GUI voer nie uit nie"
+#~ msgid "-v\t\t\tVi mode (like \"vi\")"
+#~ msgstr "-v\t\t\tVi modus (soos \"vi\")"
-#~ msgid "Command too long"
-#~ msgstr "Bevel te lank"
+#~ msgid ""
+#~ "\n"
+#~ "\n"
+#~ "Arguments:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "\n"
+#~ "Parameters:\n"
-#~ msgid "Ambiguous mapping"
-#~ msgstr "Dubbelsinnige binding"
+#~ msgid ""
+#~ "\n"
+#~ " or:"
+#~ msgstr ""
+#~ "\n"
+#~ " of:"
-#~ msgid "Ambiguous mapping, conflicts with \"%s\""
-#~ msgstr "Dubbelsinnige binding, bots met \"%s\""
+#~ msgid " vim [arguments] "
+#~ msgstr " vim [parameters] "
-#~ msgid "Too many \\("
-#~ msgstr "Te veel \\("
+#~ msgid "%d files to edit\n"
+#~ msgstr "%d lers om te bewerk\n"
-#~ msgid "Unmatched \\("
-#~ msgstr "Onpaar \\("
+#~ msgid "Invalid argument for"
+#~ msgstr "Ongeldige parameter vir"
-#~ msgid "Nested *, \\=, \\+, \\! or \\{"
-#~ msgstr "Geneste *, \\=, \\+, \\! of \\{"
+#~ msgid "Zero count"
+#~ msgstr "Nul telling"
-#~ msgid "\\= follows nothing"
-#~ msgstr "\\= volg niks"
+#~ msgid "Nvim: Reading from stdin...\n"
+#~ msgstr "Vim: Lees nou vanaf 'stdin'...\n"
-#~ msgid "\\+ follows nothing"
-#~ msgstr "\\+ volg niks"
+#~ msgid "Input Line"
+#~ msgstr "Invoer Lyn"
-#~ msgid "\\@ follows nothing"
-#~ msgstr "\\@ volg niks"
+#~ msgid "Expression"
+#~ msgstr "Uitdrukking"
-#~ msgid "\\{ follows nothing"
-#~ msgstr "\\{ volg niks"
+#~ msgid "Search String"
+#~ msgstr "Soekstring"
-#~ msgid "\\* follows nothing"
-#~ msgstr "\\* volg niks"
+#~ msgid "Command Line"
+#~ msgstr "Bevelrel"
-#~ msgid "Unexpected magic character; check META."
-#~ msgstr "Onverwagte toorkarakter; kyk na META."
+#~ msgid ""
+#~ "\n"
+#~ "# %s History (newest to oldest):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# %s Geskiedenis (van nuutste na oudste):\n"
-#~ msgid "type :help uganda<Enter> if you like Vim "
-#~ msgstr "tik :help uganda<Enter> as jy hou van Vim "
+#~ msgid "E195: Cannot open viminfo file for reading"
+#~ msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie"
-#~ msgid " WARNING: Intel CPU detected. "
-#~ msgstr " WAARSKUWING: Intel SVE bespeur. "
+#~ msgid "E466: :winpos requires two number arguments"
+#~ msgstr "E466: :winpos benodig twee parameters"
-#~ msgid " PPC has a much better architecture. "
-#~ msgstr " PPC het 'n veel beter argitektuur. "
+#~ msgid "E188: Obtaining window position not implemented for this platform"
+#~ msgstr ""
+#~ "E188: Verkryging van vensterposisie is nie vir hierdie platform "
+#~ "gemplementeer nie"
-#~ msgid "Security error: new viminfo file is a symbolic link"
-#~ msgstr "Sekuriteitsfout: nuwe viminfo ler is a simboliese skakel"
+#, fuzzy
+#~ msgid ""
+#~ "E747: Cannot change directory, buffer is modified (add ! to override)"
+#~ msgstr "E509: Kan rugsteunler nie skep nie (gebruik ! om te dwing)"
-#~ msgid "line ~%<PRId64>: %s"
-#~ msgstr "rel ~%<PRId64>: %s"
+#~ msgid "E172: Only one file name allowed"
+#~ msgstr "E172: Slegs een lernaam toegelaat"
-#~ msgid "makeef option not set"
-#~ msgstr "'makeef' opsie nie aan nie"
+#~ msgid ""
+#~ "\n"
+#~ "# Last Substitute String:\n"
+#~ "$"
+#~ msgstr ""
+#~ "\n"
+#~ "# Vorige Vervangstring:\n"
+#~ "$"
-#~ msgid "Security error: filter output is a symbolic link: %s"
-#~ msgstr "Sekuriteitsfout: filter afvoer is 'n simboliese skakel"
+#~ msgid "Illegal starting char"
+#~ msgstr "Ongeldige beginkarakter"
-#~ msgid "Security error: 'charconvert' output is a symbolic link"
-#~ msgstr "Sekuriteitsfout: 'charconvert' afvoer is 'n simboliese skakel"
+#~ msgid "# Value of 'encoding' when this file was written\n"
+#~ msgstr "# Waarde van 'encoding' toe hierdie ler gestoor is\n"
-#~ msgid "Security error: filter input is a symbolic link: %s"
-#~ msgstr "Sekuriteitsfout: filter invoer is 'n simboliese skakel"
+#~ msgid ""
+#~ "# You may edit it if you're careful!\n"
+#~ "\n"
+#~ msgstr ""
+#~ "# Jy mag dit wysig as jy versigtig is!\n"
+#~ "\n"
-#~ msgid "Fold must be at least two lines"
-#~ msgstr "'n Vou moet ten minste 2 rels wees"
+#~ msgid "# This viminfo file was generated by Vim %s.\n"
+#~ msgstr "# Hierdie viminfo ler is gegenereer deur Vim %s.\n"
-#~ msgid "No fold at this line"
-#~ msgstr "Geen vou by hierdie rel nie"
+#~ msgid "E138: Can't write viminfo file %s!"
+#~ msgstr "E138: Kan nie viminfo ler %s stoor nie!"
-#~ msgid "Security error: shell command output is a symbolic link"
-#~ msgstr "Sekuriteitsfout: Dop-bevel afvoer is 'n simboliese skakel"
+#~ msgid "E136: viminfo: Too many errors, skipping rest of file"
+#~ msgstr "E136: viminfo: Te veel foute, slaan die res van die ler oor"
-#~ msgid "Warning: %s option changed from modeline"
-#~ msgstr "Waarskuwing: %s opsie verander vanaf moduslyn"
+#~ msgid "%sviminfo: %s in line: "
+#~ msgstr "%sviminfo: %s in rel: "
-#~ msgid "Change dir debugging enabled."
-#~ msgstr "Verandergids ontfouting in staat gestel"
+#~ msgid ""
+#~ "\n"
+#~ "# global variables:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# globale veranderlikes:\n"
-#~ msgid "Not a proper file name: '%s'"
-#~ msgstr "Nie 'n geldige lernaam nie: '%s'"
+#, fuzzy
+#~ msgid "E706: Variable type mismatch for: %s"
+#~ msgstr "E93: Meer as een treffer vir %s"
-#~ msgid "File name '%s' is valid"
-#~ msgstr "lernaam '%s is ongeldig"
+#, fuzzy
+#~ msgid "E724: variable nested too deep for displaying"
+#~ msgstr "E22: Skripte te diep ge-nes"
-#~ msgid "Leave: %s"
-#~ msgstr "Verlaat: %s"
+#~ msgid ""
+#~ "\n"
+#~ "# Buffer list:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# Buffer lys:\n"
-#~ msgid "WARNING: tag command changed a buffer!!!"
-#~ msgstr "WAARSKUWING: etiketbevel het buffer verander!!!"
+#, fuzzy
+#~ msgid "Unable to get option value"
+#~ msgstr "E258: Kan nie na klint stuur nie"
diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po
index 7d32db9f97..b485b61491 100644
--- a/src/nvim/po/ca.po
+++ b/src/nvim/po/ca.po
@@ -299,7 +299,7 @@ msgstr "E100: No hi ha cap altre buffer en mode diff"
#: ../diff.c:2112
msgid "E101: More than two buffers in diff mode, don't know which one to use"
-msgstr "E101: Hi ha ms de 2 buffers en mode diff, no se sap quin usar"
+msgstr "E101: Hi ha ms de 2 buffers en mode diff"
#: ../diff.c:2141
#, c-format
@@ -1095,7 +1095,7 @@ msgstr "%<PRId64> lnies filtrades"
#: ../ex_cmds.c:1194
msgid "E135: *Filter* Autocommands must not change current buffer"
-msgstr "E135: Les auto-ordres *Filter* no poden canviar el buffer actual"
+msgstr "E135: Les ordres automtiques *Filter* han de no modificar el buffer"
#: ../ex_cmds.c:1244
msgid "[No write since last change]\n"
@@ -1582,7 +1582,7 @@ msgstr "E605: No s'ha interceptat l'excepci: %s"
#: ../ex_docmd.c:1085
msgid "End of sourced file"
-msgstr "Final del fitxer interpretat"
+msgstr "Final de l'script"
#: ../ex_docmd.c:1086
msgid "End of function"
@@ -1881,7 +1881,7 @@ msgstr "%s s'ha descartat"
#: ../ex_eval.c:708
msgid "Exception"
-msgstr "Exepci"
+msgstr "Excepci"
#: ../ex_eval.c:713
msgid "Error and interrupt"
@@ -2162,7 +2162,7 @@ msgstr "[ERRORS DE LECTURA]"
#: ../fileio.c:2104
msgid "Can't find temp file for conversion"
-msgstr "No s'ha trobat el fitxer temporal per la conversi"
+msgstr "No s'ha trobat el fitxer temporal per a fer la conversi"
#: ../fileio.c:2110
msgid "Conversion with 'charconvert' failed"
@@ -2298,7 +2298,7 @@ msgstr "E205: patchmode: no s'ha pogut desar el fitxer original"
#: ../fileio.c:3602
msgid "E206: patchmode: can't touch empty original file"
-msgstr "E206: patchmode: no s'ha pogut tocar el fitxer original buit"
+msgstr "E206: patchmode: no s'ha pogut fer un toc al fitxer original buit"
#: ../fileio.c:3616
msgid "E207: Can't delete backup file"
@@ -2308,9 +2308,7 @@ msgstr "E207: No s'ha pogut eliminar la cpia de seguretat"
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
-msgstr ""
-"\n"
-"ATENCI: El fitxer original es pot haver fet malb\n"
+msgstr "\nATENCI: El fitxer original es pot haver perdut o fet malb\n"
#: ../fileio.c:3675
msgid "don't quit the editor until the file is successfully written!"
@@ -2460,7 +2458,7 @@ msgstr ""
#: ../fileio.c:5065
#, c-format
msgid "E462: Could not prepare for reloading \"%s\""
-msgstr "E462: No s'han pogut fer les preparacions per rellegir \"%s\""
+msgstr "E462: No s'han pogut fer les preparacions per a rellegir \"%s\""
#: ../fileio.c:5078
#, c-format
@@ -2536,7 +2534,7 @@ msgstr "Executant %s"
#: ../fileio.c:7211
#, c-format
msgid "autocommand %s"
-msgstr "auto-ordre %s"
+msgstr "ordre automtica %s"
#: ../fileio.c:7795
msgid "E219: Missing {."
@@ -3043,7 +3041,7 @@ msgstr "No hi ha text per imprimir"
#: ../hardcopy.c:668
#, c-format
msgid "Printing page %d (%d%%)"
-msgstr "S'est imprimint la pgina %d (%d%%)"
+msgstr "Imprimint la pgina %d (%d%%)"
#: ../hardcopy.c:680
#, c-format
@@ -3057,7 +3055,7 @@ msgstr "S'ha imprs: %s"
#: ../hardcopy.c:740
msgid "Printing aborted"
-msgstr "S'ha avortat l'impressi"
+msgstr "S'ha avortat la impressi"
#: ../hardcopy.c:1365
msgid "E455: Error writing to PostScript output file"
@@ -3314,7 +3312,7 @@ msgstr "E609: Error de cscope: %s"
#: ../if_cscope.c:2053
msgid "All cscope databases reset"
-msgstr "S'han reiniciat totes les bases de dades cscope"
+msgstr "S'han restablert totes les bases de dades cscope"
#: ../if_cscope.c:2123
msgid "no cscope connections\n"
@@ -3517,7 +3515,7 @@ msgstr "-n\t\t\tNo usa fitxers d'intercanvi, noms memria"
#: ../main.c:2218
msgid "-r\t\t\tList swap files and exit"
-msgstr "-r\t\t\tLlista els fitxers d'intercanvi i surt"
+msgstr "-r\t\t\tLlistat dels fitxers d'intercanvi"
#: ../main.c:2219
msgid "-r (with file name)\tRecover crashed session"
@@ -3549,7 +3547,7 @@ msgstr "-u <vimrc>\t\tUsa <vimrc> en lloc de qualsevol altre .vimrc"
#: ../main.c:2226
msgid "--noplugin\t\tDon't load plugin scripts"
-msgstr "--noplugin\t\tNo carrega cap plugin"
+msgstr "--noplugin\t\tNo carrega plugins"
#: ../main.c:2227
msgid "-p[N]\t\tOpen N tab pages (default: one for each file)"
@@ -3561,7 +3559,7 @@ msgstr "-o[N]\t\tObre N finestres (per omissi: una per fitxer)"
#: ../main.c:2229
msgid "-O[N]\t\tLike -o but split vertically"
-msgstr "-O[N]\t\tCom -o per amb divisions verticals"
+msgstr "-O[N]\t\tCom -o per amb divisi vertical"
#: ../main.c:2230
msgid "+\t\t\tStart at end of file"
@@ -3739,7 +3737,7 @@ msgstr "E305: No s'ha trobat el fitxer d'intercanvi de %s"
#: ../memline.c:839
msgid "Enter number of swap file to use (0 to quit): "
-msgstr "Entreu el nmero del fitxer d'intercanvi a utilitzar (0 per sortir): "
+msgstr "Entreu el nmero del fitxer .swp a utilitzar (0 per a sortir): "
#: ../memline.c:879
#, c-format
@@ -3807,6 +3805,30 @@ msgstr "E308: Atenci: El fitxer original pot haver canviat"
#: ../memline.c:1061
#, c-format
+msgid "Swap file is encrypted: \"%s\""
+msgstr "El fitxer d'intercanvi est xifrat: \"%s\""
+
+msgid ""
+"\n"
+"If you entered a new crypt key but did not write the text file,"
+msgstr "\nSi vau entrar una nova clau de xifrat per no vau desar el fitxer,"
+
+msgid ""
+"\n"
+"enter the new crypt key."
+msgstr "\nentreu la nova clau."
+
+msgid ""
+"\n"
+"If you wrote the text file after changing the crypt key press enter"
+msgstr "\nSi vau desar el fitxer desprs de canviar la clau, premeu Entrar per a"
+
+msgid ""
+"\n"
+"to use the same key for text file and swap file"
+msgstr "\nusar la mateixa clau per al fitxer de text i per al fitxer d'intercanvi."
+
+#, c-format
msgid "E309: Unable to read block 1 from %s"
msgstr "E309: No s'ha pogut llegir el bloc 1 de %s"
@@ -3931,7 +3953,7 @@ msgstr " [del Vim versi 3.0]"
#: ../memline.c:1550
msgid " [does not look like a Vim swap file]"
-msgstr " [no sembla un fitxer d'intercanvi de Vim]"
+msgstr " [no sembla un fitxer .swp de Vim]"
#: ../memline.c:1552
msgid " file name: "
@@ -4003,7 +4025,7 @@ msgstr " [no es pot obrir]"
#: ../memline.c:1698
msgid "E313: Cannot preserve, there is no swap file"
-msgstr "E313: No s'ha pogut preservar, no hi ha fitxer d'intercanvi"
+msgstr "E313: No s'ha pogut preservar, no existeix cap fitxer d'intercanvi"
#: ../memline.c:1747
msgid "File preserved"
@@ -4025,7 +4047,7 @@ msgstr "E316: ml_get: no s'ha trobat la lnia %<PRId64>"
#: ../memline.c:2236
msgid "E317: pointer block id wrong 3"
-msgstr "E317: Punter a la id d'un bloc incorrecte 3"
+msgstr "E317: punter a id de bloc incorrecte 3"
#: ../memline.c:2311
msgid "stack_idx should be 0"
@@ -4037,7 +4059,7 @@ msgstr "E318: S'han actualitzat massa blocs?"
#: ../memline.c:2511
msgid "E317: pointer block id wrong 4"
-msgstr "E317: Punter a la id d'un bloc incorrecte 4"
+msgstr "E317: Punter a id de bloc incorrecte 4"
#: ../memline.c:2536
msgid "deleted block 1?"
@@ -4087,9 +4109,7 @@ msgstr "E325: ATENCI"
msgid ""
"\n"
"Found a swap file by the name \""
-msgstr ""
-"\n"
-"S'ha trobat un fitxer d'intercanvi de nom \""
+msgstr "\nS'ha trobat un fitxer d'intercanvi amb nom \""
#: ../memline.c:3226
msgid "While opening file \""
@@ -4134,7 +4154,7 @@ msgid ""
" to recover the changes (see \":help recovery\").\n"
msgstr ""
"\"\n"
-" per recuperar els canvis (vegeu \":help recovery\").\n"
+" per a recuperar els canvis (vegeu \":help recovery\").\n"
#: ../memline.c:3250
msgid " If you did this already, delete the swap file \""
@@ -4146,7 +4166,7 @@ msgid ""
" to avoid this message.\n"
msgstr ""
"\"\n"
-" per evitar aquest missatge.\n"
+" per a evitar aquest missatge.\n"
#: ../memline.c:3450 ../memline.c:3452
msgid "Swap file \""
@@ -4172,7 +4192,7 @@ msgid ""
"&Quit\n"
"&Abort"
msgstr ""
-"&Obrir noms-lectura\n"
+"&Obrir amb noms lectura\n"
"&Editar igualment\n"
"&Recuperar\n"
"&Sortir\n"
@@ -4840,7 +4860,7 @@ msgstr "E377: %%%c no vlid a la cadena de format"
#. nothing found
#: ../quickfix.c:477
msgid "E378: 'errorformat' contains no pattern"
-msgstr "E378: L'opci 'errorformat' no cont cap patr"
+msgstr "E378: 'errorformat' no cont cap patr"
#: ../quickfix.c:695
msgid "E379: Missing or empty directory name"
@@ -5234,7 +5254,7 @@ msgstr "S'han trobat tots els fitxers inclosos"
#: ../search.c:4519
msgid "No included files"
-msgstr "No hi han fitxers inclosos"
+msgstr "No hi ha fitxers inclosos"
#: ../search.c:4527
msgid "E388: Couldn't find definition"
@@ -5605,7 +5625,7 @@ msgstr "Escrivint el fitxer de suggeriments %s ..."
#: ../spell.c:7707 ../spell.c:7927
#, c-format
msgid "Estimated runtime memory use: %d bytes"
-msgstr "s estimat de memria en funcionament: %d octets"
+msgstr "s estimat de memria durant l'execuci: %d octets"
#: ../spell.c:7820
msgid "E751: Output file name must not have region name"
@@ -5622,7 +5642,7 @@ msgstr "E755: Regi no vlida a %s"
#: ../spell.c:7907
msgid "Warning: both compounding and NOBREAK specified"
-msgstr "Atenci: heu especificat composici i NOBREAK alhora"
+msgstr "Atenci: s'ha especificat composici i NOBREAK alhora"
#: ../spell.c:7920
#, c-format
@@ -6373,7 +6393,7 @@ msgstr " alternativa per a $VIM: \""
# 29 carcters fins el ":" (incls)
#: ../version.c:705
msgid " f-b for $VIMRUNTIME: \""
-msgstr " alt per a $VIMRUNTIME: \""
+msgstr " altern. per a $VIMRUNTIME: \""
#: ../version.c:709
msgid "Compilation: "
@@ -6401,7 +6421,7 @@ msgstr "per Bram Moolenaar et al."
#: ../version.c:774
msgid "Vim is open source and freely distributable"
-msgstr "Vim s un programa obert i lliure distribuci"
+msgstr "Vim s un programa obert i de lliure distribuci"
#: ../version.c:776
msgid "Help poor children in Uganda!"
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index f04d3be4b4..3986a796d8 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -12,7 +12,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-06-26 15:13+0200\n"
"PO-Revision-Date: 2008-05-24 17:26+0200\n"
-"Last-Translator: Georg Dahn <georg.dahn@gmail.com>\n"
+"Last-Translator: was: Georg Dahn\n"
"Language-Team: German <de@li.org>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
@@ -377,7 +377,7 @@ msgstr " Definitions-Ergnzung (^D^N^P)"
#: ../edit.c:92
msgid " Dictionary completion (^K^N^P)"
-msgstr " Wrterbuch-Ergnzung (^K^N^P) "
+msgstr " Dictionary-Ergnzung (^K^N^P) "
#: ../edit.c:93
msgid " Thesaurus completion (^T^N^P)"
@@ -401,7 +401,7 @@ msgstr " Vorschlag der Rechtschreibprfung (s^N^P)"
#: ../edit.c:98
msgid " Keyword Local completion (^N^P)"
-msgstr " Lokale Schlsselwort-Ergnzung(^N^P)"
+msgstr " Lokale Stichwort-Ergnzung(^N^P)"
#: ../edit.c:101
msgid "Hit end of paragraph"
@@ -443,7 +443,7 @@ msgstr "Durchsuche: %s"
#: ../edit.c:3513
msgid "Scanning tags."
-msgstr "Durchsuchen von Tags."
+msgstr "Durchsuche Tags"
#: ../edit.c:4418
msgid " Adding"
@@ -633,6 +633,9 @@ msgstr ""
#: ../ex_cmds.c:2433
#, c-format
+msgid "Pattern not found: %s"
+msgstr "Muster nicht gefunden: %s"
+
msgid ""
"File permissions of \"%s\" are read-only.\n"
"It may still be possible to write it.\n"
@@ -713,11 +716,6 @@ msgstr "E148: Regulrer Ausdruck fehlt in global"
msgid "Pattern found in every line: %s"
msgstr "Muster in jeder Zeile gefunden: %s"
-#: ../ex_cmds.c:4504
-#, c-format
-msgid "Pattern not found: %s"
-msgstr "Muster nicht gefunden: %s"
-
#: ../ex_cmds.c:4581
msgid ""
"\n"
@@ -6488,7 +6486,7 @@ msgid ""
"\tLast set from "
msgstr ""
"\n"
-"\tZuletzt gesetzt von "
+"\tZuletzt gesetzt in "
#: ../eval.c:18682
msgid "No old files"
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 3fb300c63f..cc0893de3a 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -13,18 +13,12 @@
# Komputeko: http://komputeko.net/index_eo.php
# Komputada leksikono: http://bertilow.com/div/komputada_leksikono/
#
-# Lasta versio:
-# http://dominique.pelle.free.fr/vim-eo.php
-#
-# Ĉiu komento estas bonvenata...
-# Every remark is welcome...
-#
msgid ""
msgstr ""
"Project-Id-Version: Vim(Esperanto)\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-01-16 00:30+0100\n"
-"PO-Revision-Date: 2017-01-16 01:14+0100\n"
+"POT-Creation-Date: 2017-10-02 22:42+0200\n"
+"PO-Revision-Date: 2017-10-02 22:57+0200\n"
"Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n"
"Language-Team: \n"
"Language: eo\n"
@@ -105,7 +99,6 @@ msgstr "E90: Ne eblas malŝargi la lastan bufron"
msgid "E84: No modified buffer found"
msgstr "E84: Neniu modifita bufro trovita"
-#. back where we started, didn't find anything.
msgid "E85: There is no listed buffer"
msgstr "E85: Estas neniu listigita bufro"
@@ -121,6 +114,18 @@ msgstr ""
"E89: Neniu skribo de post la lasta ŝanĝo de la bufro %ld (aldonu ! por "
"transpasi)"
+msgid "E948: Job still running (add ! to end the job)"
+msgstr "E948: Tasko akoraŭ aktiva (aldonu ! por fini la taskon)"
+
+msgid "E37: No write since last change (add ! to override)"
+msgstr "E37: Neniu skribo de post lasta ŝanĝo (aldonu ! por transpasi)"
+
+msgid "E948: Job still running"
+msgstr "E948: Tasko ankoraŭ aktiva"
+
+msgid "E37: No write since last change"
+msgstr "E37: Neniu skribo de post lasta ŝanĝo"
+
msgid "W14: Warning: List of file names overflow"
msgstr "W14: Averto: Listo de dosiernomoj troas"
@@ -176,7 +181,6 @@ msgstr "linio %ld de %ld --%d%%-- kol "
msgid "[No Name]"
msgstr "[Neniu nomo]"
-#. must be a help buffer
msgid "help"
msgstr "helpo"
@@ -202,6 +206,9 @@ msgstr ""
"\n"
"# Listo de bufroj:\n"
+msgid "E382: Cannot write, 'buftype' option is set"
+msgstr "E382: Ne eblas skribi, opcio 'buftype' estas ŝaltita"
+
msgid "[Scratch]"
msgstr "[Malneto]"
@@ -369,7 +376,6 @@ msgstr "E791: Malplena rikordo en klavmapo"
msgid " Keyword completion (^N^P)"
msgstr " Kompletigo de ŝlosilvorto (^N^P)"
-#. ctrl_x_mode == 0, ^P/^N compl.
msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
msgstr " Reĝimo ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
@@ -442,13 +448,12 @@ msgstr "Analizas: %s"
msgid "Scanning tags."
msgstr "Analizas etikedojn."
+msgid "match in file"
+msgstr "kongruo en dosiero"
+
msgid " Adding"
msgstr " Aldonanta"
-#. showmode might reset the internal line pointers, so it must
-#. * be called before line = ml_get(), or when this address is no
-#. * longer needed. -- Acevedo.
-#.
msgid "-- Searching..."
msgstr "-- Serĉanta..."
@@ -469,7 +474,6 @@ msgstr "kongruo %d de %d"
msgid "match %d"
msgstr "kongruo %d"
-#. maximum nesting of lists and dicts
msgid "E18: Unexpected characters in :let"
msgstr "E18: Neatenditaj signoj en \":let\""
@@ -523,17 +527,21 @@ msgid "E711: List value has not enough items"
msgstr "E711: Lista valoro ne havas sufiĉe da eroj"
msgid "E690: Missing \"in\" after :for"
-msgstr "E690: \"in\" mankas post \":for\""
+msgstr "E690: \"in\" mankas malantaŭ \":for\""
#, c-format
msgid "E108: No such variable: \"%s\""
msgstr "E108: Ne estas tia variablo: \"%s\""
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: Ne eblas ŝlosi aŭ malŝlosi variablon %s"
+
msgid "E743: variable nested too deep for (un)lock"
-msgstr "E743: variablo ingita tro profunde por malŝlosi"
+msgstr "E743: variablo ingita tro profunde por (mal)ŝlosi"
msgid "E109: Missing ':' after '?'"
-msgstr "E109: Mankas ':' post '?'"
+msgstr "E109: Mankas ':' malantaŭ '?'"
msgid "E691: Can only compare List with List"
msgstr "E691: Eblas nur kompari Liston kun Listo"
@@ -697,21 +705,10 @@ msgstr "argumento de add()"
msgid "E785: complete() can only be used in Insert mode"
msgstr "E785: complete() uzeblas nur en Enmeta reĝimo"
-#.
-#. * Yes this is ugly, I don't particularly like it either. But doing it
-#. * this way has the compelling advantage that translations need not to
-#. * be touched at all. See below what 'ok' and 'ync' are used for.
-#.
msgid "&Ok"
msgstr "&Bone"
#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld linio: "
-msgstr[1] "+-%s%3ld linioj: "
-
-#, c-format
msgid "E700: Unknown function: %s"
msgstr "E700: Nekonata funkcio: %s"
@@ -756,8 +753,8 @@ msgstr "E727: Komenco preter fino"
msgid "<empty>"
msgstr "<malplena>"
-msgid "E240: No connection to Vim server"
-msgstr "E240: Neniu konekto al Vim-servilo"
+msgid "E240: No connection to the X server"
+msgstr "E240: Neniu konekto al X-servilo"
#, c-format
msgid "E241: Unable to send to %s"
@@ -766,6 +763,12 @@ msgstr "E241: Ne eblas sendi al %s"
msgid "E277: Unable to read a server reply"
msgstr "E277: Ne eblas legi respondon de servilo"
+msgid "E941: already started a server"
+msgstr "E941: servilo jam lanĉita"
+
+msgid "E942: +clientserver feature not available"
+msgstr "E942: la eblo +clientserver ne disponeblas"
+
msgid "remove() argument"
msgstr "argumento de remove()"
@@ -862,7 +865,6 @@ msgstr " malnovaj dosieroj"
msgid " FAILED"
msgstr " MALSUKCESIS"
-#. avoid a wait_return for this message, it's annoying
#, c-format
msgid "E137: Viminfo file is not writable: %s"
msgstr "E137: Dosiero viminfo ne skribeblas: %s"
@@ -883,7 +885,6 @@ msgstr "Skribas dosieron viminfo \"%s\""
msgid "E886: Can't rename viminfo file to %s!"
msgstr "E886: Ne eblas renomi dosieron viminfo al %s!"
-#. Write the info:
#, c-format
msgid "# This viminfo file was generated by Vim %s.\n"
msgstr "# Tiu dosiero viminfo estis kreita de Vim %s.\n"
@@ -1002,8 +1003,8 @@ msgstr " en 1 linio"
msgid " on %ld lines"
msgstr " en %ld linioj"
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: Ne eblas fari \":global\" rekursie"
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: Ne eblas fari \":global\" rekursie kun amplekso"
# DP: global estas por ":global" do mi ne tradukis ĝin
msgid "E148: Regular expression missing from global"
@@ -1154,8 +1155,9 @@ msgstr "E750: Uzu unue \":profile start {dosiernomo}\""
msgid "Save changes to \"%s\"?"
msgstr "Ĉu konservi ŝanĝojn al \"%s\"?"
-msgid "Untitled"
-msgstr "Sen titolo"
+#, c-format
+msgid "E947: Job still running in buffer \"%s\""
+msgstr "E947: Tasko ankoraŭ aktiva en la bufro \"%s\""
#, c-format
msgid "E162: No write since last change for buffer \"%s\""
@@ -1189,6 +1191,14 @@ msgstr "Serĉado de \"%s\""
msgid "not found in '%s': \"%s\""
msgstr "ne trovita en '%s: \"%s\""
+#, c-format
+msgid "W20: Required python version 2.x not supported, ignoring file: %s"
+msgstr "W20: Pitono versio 2.x bezonata sed nesubtenata, ignoro de dosiero: %s"
+
+#, c-format
+msgid "W21: Required python version 3.x not supported, ignoring file: %s"
+msgstr "W21: pitono versio 3.x bezonata sed nesubtenata, ignoro de dosiero: %s"
+
msgid "Source Vim script"
msgstr "Ruli Vim-skripton"
@@ -1286,6 +1296,9 @@ msgstr "Inversa amplekso donita, permuteblas"
msgid "E494: Use w or w>>"
msgstr "E494: Uzu w aŭ w>>"
+msgid "E943: Command table needs to be updated, run 'make cmdidxs'"
+msgstr "E943: Tabulo de komandoj estas ĝisdatigenda, lanĉu 'make cmdidx'"
+
msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: Bedaŭrinde, tiu komando ne haveblas en tiu versio"
@@ -1451,7 +1464,6 @@ msgstr "E189: \"%s\" ekzistas (aldonu ! por transpasi)"
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: Ne eblas malfermi \"%s\" por skribi"
-#. set mark
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr "E191: Argumento devas esti litero, citilo aŭ retrocitilo"
@@ -1493,13 +1505,15 @@ msgstr "E500: Liveras malplenan ĉenon"
msgid "E195: Cannot open viminfo file for reading"
msgstr "E195: Ne eblas malfermi dosieron viminfo en lega reĝimo"
+msgid "Untitled"
+msgstr "Sen titolo"
+
msgid "E196: No digraphs in this version"
msgstr "E196: Neniu duliteraĵo en tiu versio"
msgid "E608: Cannot :throw exceptions with 'Vim' prefix"
msgstr "E608: Ne eblas lanĉi (:throw) escepton kun prefikso 'Vim'"
-#. always scroll up, don't overwrite
#, c-format
msgid "Exception thrown: %s"
msgstr "Escepto lanĉita: %s"
@@ -1516,7 +1530,6 @@ msgstr "Escepto ne konservita: %s"
msgid "%s, line %ld"
msgstr "%s, linio %ld"
-#. always scroll up, don't overwrite
#, c-format
msgid "Exception caught: %s"
msgstr "Kaptis escepton: %s"
@@ -1542,7 +1555,6 @@ msgstr "Eraro kaj interrompo"
msgid "Error"
msgstr "Eraro"
-#. if (pending & CSTP_INTERRUPT)
msgid "Interrupt"
msgstr "Interrompo"
@@ -1562,7 +1574,7 @@ msgid "E583: multiple :else"
msgstr "E583: pluraj \":else\""
msgid "E584: :elseif after :else"
-msgstr "E584: \":elseif\" post \":else\""
+msgstr "E584: \":elseif\" malantaŭ \":else\""
msgid "E585: :while/:for nesting too deep"
msgstr "E585: \":while/:for\" ingita tro profunde"
@@ -1585,15 +1597,12 @@ msgstr "E601: \":try\" ingita tro profunde"
msgid "E603: :catch without :try"
msgstr "E603: \":catch\" sen \":try\""
-#. Give up for a ":catch" after ":finally" and ignore it.
-#. * Just parse.
msgid "E604: :catch after :finally"
-msgstr "E604: \":catch\" post \":finally\""
+msgstr "E604: \":catch\" malantaŭ \":finally\""
msgid "E606: :finally without :try"
msgstr "E606: \":finally\" sen \":try\""
-#. Give up for a multiple ":finally" and ignore it.
msgid "E607: multiple :finally"
msgstr "E607: pluraj \":finally\""
@@ -1686,7 +1695,6 @@ msgstr "Vim: Legado el stdin...\n"
msgid "Reading from stdin..."
msgstr "Legado el stdin..."
-#. Re-opening the original file failed!
msgid "E202: Conversion made file unreadable!"
msgstr "E202: Konverto igis la dosieron nelegebla!"
@@ -1892,9 +1900,6 @@ msgstr "[sen EOL]"
msgid "[Incomplete last line]"
msgstr "[Nekompleta lasta linio]"
-#. don't overwrite messages here
-#. must give this prompt
-#. don't use emsg() here, don't want to flush the buffers
msgid "WARNING: The file has been changed since reading it!!!"
msgstr "AVERTO: La dosiero estas ŝanĝita de post kiam ĝi estis legita!!!"
@@ -1973,7 +1978,6 @@ msgstr "--Forviŝita--"
msgid "auto-removing autocommand: %s <buffer=%d>"
msgstr "aŭto-forviŝas aŭtokomandon: %s <bufro=%d>"
-#. the group doesn't exist
#, c-format
msgid "E367: No such group: \"%s\""
msgstr "E367: Ne ekzistas tia grupo: \"%s\""
@@ -1986,7 +1990,7 @@ msgstr "W19: Forviŝo de augroup kiu estas ankoraŭ uzata"
#, c-format
msgid "E215: Illegal character after *: %s"
-msgstr "E215: Nevalida signo post *: %s"
+msgstr "E215: Nevalida signo malantaŭ *: %s"
#, c-format
msgid "E216: No such event: %s"
@@ -1996,7 +2000,6 @@ msgstr "E216: Ne estas tia evento: %s"
msgid "E216: No such group or event: %s"
msgstr "E216: Ne ekzistas tia grupo aŭ evento: %s"
-#. Highlight title
msgid ""
"\n"
"--- Auto-Commands ---"
@@ -2044,12 +2047,6 @@ msgstr "E350: Ne eblas krei faldon per la aktuala 'foldmethod'"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: Ne eblas forviŝi faldon per la aktuala 'foldmethod'"
-#, c-format
-msgid "+--%3ld line folded "
-msgid_plural "+--%3ld lines folded "
-msgstr[0] "+--%3ld linio faldita "
-msgstr[1] "+--%3ld linioj falditaj "
-
msgid "E222: Add to read buffer"
msgstr "E222: Aldoni al lega bufro"
@@ -2186,18 +2183,15 @@ msgstr "Serĉi kion:"
msgid "Replace with:"
msgstr "Anstataŭigi per:"
-#. whole word only button
msgid "Match whole word only"
msgstr "Kongrui kun nur plena vorto"
-#. match case button
msgid "Match case"
msgstr "Uskleca kongruo"
msgid "Direction"
msgstr "Direkto"
-#. 'Up' and 'Down' buttons
msgid "Up"
msgstr "Supren"
@@ -2276,8 +2270,6 @@ msgstr "Trovi ĉenon (uzu '\\\\' por trovi '\\')"
msgid "Find & Replace (use '\\\\' to find a '\\')"
msgstr "Trovi kaj anstataŭigi (uzu '\\\\' por trovi '\\')"
-#. We fake this: Use a filter that doesn't select anything and a default
-#. * file name that won't be used.
msgid "Not Used"
msgstr "Ne uzata"
@@ -2351,7 +2343,6 @@ msgstr "Vim - Elektilo de tiparo"
msgid "Name:"
msgstr "Nomo:"
-#. create toggle button
msgid "Show size in Points"
msgstr "Montri grandon en punktoj"
@@ -2597,7 +2588,6 @@ msgstr "E261: konekto cscope %s netrovita"
msgid "cscope connection %s closed"
msgstr "konekto cscope %s fermita"
-#. should not reach here
msgid "E570: fatal error in cs_manage_matches"
msgstr "E570: neriparebla eraro en cs_manage_matches"
@@ -2759,7 +2749,6 @@ msgstr "nevalida numero de bufro"
msgid "not implemented yet"
msgstr "ankoraŭ ne realigita"
-#. ???
msgid "cannot set line(s)"
msgstr "ne eblas meti la linio(j)n"
@@ -2800,7 +2789,6 @@ msgid ""
msgstr ""
"ne eblas registri postalvokan komandon: bufro/fenestro estas jam forviŝiĝanta"
-#. This should never happen. Famous last word?
msgid ""
"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim."
"org"
@@ -2859,10 +2847,10 @@ msgid "Too many edit arguments"
msgstr "Tro da argumentoj de redakto"
msgid "Argument missing after"
-msgstr "Argumento mankas post"
+msgstr "Argumento mankas malantaŭ"
msgid "Garbage after option argument"
-msgstr "Forĵetindaĵo post argumento de opcio"
+msgstr "Forĵetindaĵo malantaŭ argumento de opcio"
msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"
msgstr "Tro da argumentoj \"+komando\", \"-c komando\" aŭ \"--cmd komando\""
@@ -2904,7 +2892,6 @@ msgstr "Vim: Averto: Eligo ne estas al terminalo\n"
msgid "Vim: Warning: Input is not from a terminal\n"
msgstr "Vim: Averto: Enigo ne estas el terminalo\n"
-#. just in case..
msgid "pre-vimrc command line"
msgstr "komanda linio pre-vimrc"
@@ -2968,7 +2955,7 @@ msgstr ""
"Argumentoj:\n"
msgid "--\t\t\tOnly file names after this"
-msgstr "--\t\t\tNur dosiernomoj post tio"
+msgstr "--\t\t\tNur dosiernomoj malantaŭ tio"
msgid "--literal\t\tDon't expand wildcards"
msgstr "--literal\t\tNe malvolvi ĵokerojn"
@@ -3069,8 +3056,7 @@ msgstr ""
"--not-a-term\t\tPreterpasi averton por enigo/eligo, kiu ne estas terminalo"
msgid "--ttyfail\t\tExit if input or output is not a terminal"
-msgstr ""
-"--ttyfail\t\tEliri se le eniro aŭ eliro ne estas terminalo"
+msgstr "--ttyfail\t\tEliri se la eniro aŭ eliro ne estas terminalo"
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\t\tUzi <vimrc> anstataŭ iun ajn .vimrc"
@@ -3171,6 +3157,9 @@ msgstr ""
msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
msgstr "-i <viminfo>\t\tUzi <viminfo> anstataŭ .viminfo"
+msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo"
+msgstr "--clean\t\t'nocompatible', defaŭltaj agordoj de Vim, neniu viminfo"
+
msgid "-h or --help\tPrint Help (this message) and exit"
msgstr "-h aŭ --help\tAfiŝi Helpon (tiun mesaĝon) kaj eliri"
@@ -3271,11 +3260,9 @@ msgstr "--windowid <HWND>\tMalfermi Vim en alia win32 fenestraĵo"
msgid "No display"
msgstr "Neniu ekrano"
-#. Failed to send, abort.
msgid ": Send failed.\n"
msgstr ": Sendo malsukcesis.\n"
-#. Let vim start normally.
msgid ": Send failed. Trying to execute locally\n"
msgstr ": Sendo malsukcesis. Provo de loka plenumo\n"
@@ -3296,7 +3283,6 @@ msgstr "Neniu marko"
msgid "E283: No marks matching \"%s\""
msgstr "E283: Neniu marko kongruas kun \"%s\""
-#. Highlight title
msgid ""
"\n"
"mark line col file/text"
@@ -3304,7 +3290,6 @@ msgstr ""
"\n"
"mark linio kol dosiero/teksto"
-#. Highlight title
msgid ""
"\n"
" jump line col file/text"
@@ -3312,7 +3297,6 @@ msgstr ""
"\n"
" salt linio kol dosiero/teksto"
-#. Highlight title
msgid ""
"\n"
"change line col text"
@@ -3327,7 +3311,6 @@ msgstr ""
"\n"
"# Markoj de dosiero:\n"
-#. Write the jumplist with -'
msgid ""
"\n"
"# Jumplist (newest first):\n"
@@ -3397,7 +3380,6 @@ msgstr "E298: Ĉu ne akiris blokon n-ro 2?"
msgid "E843: Error while updating swap file crypt"
msgstr "E843: Eraro dum ĝisdatigo de ĉifrada permutodosiero .swp"
-#. could not (re)open the swap file, what can we do????
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: Ve, perdis la permutodosieron .swp!!!"
@@ -3581,7 +3563,6 @@ msgid "Using crypt key from swap file for the text file.\n"
msgstr ""
"Uzas ŝlosilon de ĉifrado el permuto dosiero .swp por la teksta dosiero.\n"
-#. use msg() to start the scrolling properly
msgid "Swap files found:"
msgstr "Permutodosiero .swp trovita:"
@@ -3751,8 +3732,6 @@ msgstr "Dum malfermo de dosiero \""
msgid " NEWER than swap file!\n"
msgstr " PLI NOVA ol permutodosiero .swp!\n"
-#. Some of these messages are long to allow translation to
-#. * other languages.
msgid ""
"\n"
"(1) Another program may be editing the same file. If this is the case,\n"
@@ -3842,7 +3821,6 @@ msgstr "E328: Menuo nur ekzistas en alia reĝimo"
msgid "E329: No menu \"%s\""
msgstr "E329: Neniu menuo \"%s\""
-#. Only a mnemonic or accelerator is not valid.
msgid "E792: Empty menu name"
msgstr "E792: Malplena nomo de menuo"
@@ -3855,8 +3833,6 @@ msgstr "E331: Aldono de menueroj direkte al menuzono estas malpermesita"
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: Disigilo ne rajtas esti ero de vojo de menuo"
-#. Now we have found the matching menu, and we list the mappings
-#. Highlight title
msgid ""
"\n"
"--- Menus ---"
@@ -3867,6 +3843,10 @@ msgstr ""
msgid "Tear off this menu"
msgstr "Disigi tiun menuon"
+#, c-format
+msgid "E335: Menu not defined for %s mode"
+msgstr "E335: Menuo ne estas difinita por reĝimo %s"
+
msgid "E333: Menu path must lead to a menu item"
msgstr "E333: Vojo de menuo devas konduki al menuero"
@@ -3874,10 +3854,6 @@ msgstr "E333: Vojo de menuo devas konduki al menuero"
msgid "E334: Menu not found: %s"
msgstr "E334: Menuo netrovita: %s"
-#, c-format
-msgid "E335: Menu not defined for %s mode"
-msgstr "E335: Menuo ne estas difinita por reĝimo %s"
-
msgid "E336: Menu path must lead to a sub-menu"
msgstr "E336: Vojo de menuo devas konduki al sub-menuo"
@@ -3949,7 +3925,6 @@ msgstr "Dialogujo de dosiera konservo"
msgid "Open File dialog"
msgstr "Dialogujo de dosiera malfermo"
-#. TODO: non-GUI file selector here
msgid "E338: Sorry, no file browser in console mode"
msgstr "E338: Bedaŭrinde ne estas dosierfoliumilo en konzola reĝimo"
@@ -4116,8 +4091,10 @@ msgstr "E662: Ĉe komenco de ŝanĝlisto"
msgid "E663: At end of changelist"
msgstr "E663: Ĉe fino de ŝanĝlisto"
-msgid "Type :quit<Enter> to exit Vim"
-msgstr "Tajpu \":quit<Enenklavo>\" por eliri el Vim"
+msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim"
+msgstr ""
+"Tajpu :qa! kaj premu <Enenklavon> por forlasi ĉiujn ŝanĝojn kaj eliri el "
+"Vim"
#, c-format
msgid "1 line %sed 1 time"
@@ -4149,7 +4126,6 @@ msgstr "%ld linioj krommarĝenitaj "
msgid "E748: No previously used register"
msgstr "E748: Neniu reĝistro antaŭe uzata"
-#. must display the prompt
msgid "cannot yank; delete anyway"
msgstr "ne eblas kopii; tamen forviŝi"
@@ -4164,25 +4140,30 @@ msgstr "%ld linioj ŝanĝitaj"
msgid "freeing %ld lines"
msgstr "malokupas %ld liniojn"
-msgid "block of 1 line yanked"
-msgstr "bloko de 1 linio kopiita"
+#, c-format
+msgid " into \"%c"
+msgstr " en \"%c"
-msgid "1 line yanked"
-msgstr "1 linio kopiita"
+#, c-format
+msgid "block of 1 line yanked%s"
+msgstr "bloko de 1 linio kopiita%s"
#, c-format
-msgid "block of %ld lines yanked"
-msgstr "bloko de %ld linioj kopiita"
+msgid "1 line yanked%s"
+msgstr "1 linio kopiita%s"
#, c-format
-msgid "%ld lines yanked"
-msgstr "%ld linioj kopiitaj"
+msgid "block of %ld lines yanked%s"
+msgstr "bloko de %ld linioj kopiita%s"
+
+#, c-format
+msgid "%ld lines yanked%s"
+msgstr "%ld linioj kopiitaj%s"
#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: Nenio en reĝistro %s"
-#. Highlight title
msgid ""
"\n"
"--- Registers ---"
@@ -4243,9 +4224,6 @@ msgstr ""
msgid "(+%ld for BOM)"
msgstr "(+%ld por BOM)"
-msgid "%<%f%h%m%=Page %N"
-msgstr "%<%f%h%m%=Folio %N"
-
msgid "Thanks for flying Vim"
msgstr "Dankon pro flugi per Vim"
@@ -4262,7 +4240,7 @@ msgid "E846: Key code not set"
msgstr "E846: Klavkodo ne agordita"
msgid "E521: Number required after ="
-msgstr "E521: Nombro bezonata post ="
+msgstr "E521: Nombro bezonata malantaŭ ="
msgid "E522: Not found in termcap"
msgstr "E522: Netrovita en termcap"
@@ -4304,7 +4282,7 @@ msgstr "E525: Ĉeno de nula longo"
#, c-format
msgid "E526: Missing number after <%s>"
-msgstr "E526: Mankas nombro post <%s>"
+msgstr "E526: Mankas nombro malantaŭ <%s>"
msgid "E527: Missing comma"
msgstr "E527: Mankas komo"
@@ -4332,7 +4310,7 @@ msgstr "E534: Nevalida larĝa tiparo"
#, c-format
msgid "E535: Illegal character after <%c>"
-msgstr "E535: Nevalida signo post <%c>"
+msgstr "E535: Nevalida signo malantaŭ <%c>"
msgid "E536: comma required"
msgstr "E536: komo bezonata"
@@ -4353,6 +4331,9 @@ msgstr "E541: tro da elementoj"
msgid "E542: unbalanced groups"
msgstr "E542: misekvilibraj grupoj"
+msgid "E946: Cannot make a terminal with running job modifiable"
+msgstr "E946: Ne eblas igi modifebla terminalon kun aktiva tasko"
+
msgid "E590: A preview window already exists"
msgstr "E590: Antaŭvida fenestro jam ekzistas"
@@ -4371,9 +4352,6 @@ msgstr "E594: Bezonas almenaŭ %d kolumnojn"
msgid "E355: Unknown option: %s"
msgstr "E355: Nekonata opcio: %s"
-#. There's another character after zeros or the string
-#. * is empty. In both cases, we are trying to set a
-#. * num option using a string.
#, c-format
msgid "E521: Number required: &%s = '%s'"
msgstr "E521: Nombro bezonata: &%s = '%s'"
@@ -4415,7 +4393,7 @@ msgstr "E357: 'langmap': Kongrua signo mankas por %s"
#, c-format
msgid "E358: 'langmap': Extra characters after semicolon: %s"
-msgstr "E358: 'langmap': Ekstraj signoj post punktokomo: %s"
+msgstr "E358: 'langmap': Ekstraj signoj malantaŭ punktokomo: %s"
msgid "cannot open "
msgstr "ne eblas malfermi "
@@ -4446,7 +4424,6 @@ msgstr "ne eblas ŝanĝi reĝimon de konzolo?!\n"
msgid "mch_get_shellsize: not a console??\n"
msgstr "mch_get_shellsize: ne estas konzolo??\n"
-#. if Vim opened a window: Executing a shell may cause crashes
msgid "E360: Cannot execute shell with -f option"
msgstr "E360: Ne eblas plenumi ŝelon kun opcio -f"
@@ -4672,7 +4649,6 @@ msgstr "E376: Nevalida %%%c en prefikso de formata ĉeno"
msgid "E377: Invalid %%%c in format string"
msgstr "E377: Nevalida %%%c en formata ĉeno"
-#. nothing found
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: 'errorformat' enhavas neniun ŝablonon"
@@ -4711,9 +4687,6 @@ msgstr "E381: Ĉe la supro de stako de rapidriparo"
msgid "No entries"
msgstr "Neniu ano"
-msgid "E382: Cannot write, 'buftype' option is set"
-msgstr "E382: Ne eblas skribi, opcio 'buftype' estas ŝaltita"
-
msgid "Error file"
msgstr "Erara Dosiero"
@@ -4736,7 +4709,13 @@ msgstr "E369: nevalida ano en %s%%[]"
#, c-format
msgid "E769: Missing ] after %s["
-msgstr "E769: Mankas ] post %s["
+msgstr "E769: Mankas ] malantaŭ %s["
+
+msgid "E944: Reverse range in character class"
+msgstr "E944: Inversa amplekso en klaso de signoj"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: tro larga amplekso de klaso de signoj"
#, c-format
msgid "E53: Unmatched %s%%("
@@ -4759,12 +4738,15 @@ msgstr "E67: \\z1 kaj aliaj estas nepermeseblaj tie"
#, c-format
msgid "E69: Missing ] after %s%%["
-msgstr "E69: Mankas ] post %s%%["
+msgstr "E69: Mankas ] malantaŭ %s%%["
#, c-format
msgid "E70: Empty %s%%[]"
msgstr "E70: Malplena %s%%[]"
+msgid "E65: Illegal back reference"
+msgstr "E65: Nevalida retro-referenco"
+
msgid "E339: Pattern too long"
msgstr "E339: Ŝablono tro longa"
@@ -4780,7 +4762,7 @@ msgstr "E52: Neekvilibra \\z("
#, c-format
msgid "E59: invalid character after %s@"
-msgstr "E59: nevalida signo post %s@"
+msgstr "E59: nevalida signo malantaŭ %s@"
#, c-format
msgid "E60: Too many complex %s{...}s"
@@ -4801,19 +4783,16 @@ msgstr "E63: nevalida uzo de \\_"
msgid "E64: %s%c follows nothing"
msgstr "E64: %s%c sekvas nenion"
-msgid "E65: Illegal back reference"
-msgstr "E65: Nevalida retro-referenco"
-
msgid "E68: Invalid character after \\z"
-msgstr "E68: Nevalida signo post \\z"
+msgstr "E68: Nevalida signo malantaŭ \\z"
#, c-format
msgid "E678: Invalid character after %s%%[dxouU]"
-msgstr "E678: Nevalida signo post %s%%[dxouU]"
+msgstr "E678: Nevalida signo malantaŭ %s%%[dxouU]"
#, c-format
msgid "E71: Invalid character after %s%%"
-msgstr "E71: Nevalida signo post %s%%"
+msgstr "E71: Nevalida signo malantaŭ %s%%"
#, c-format
msgid "E554: Syntax error in %s{...}"
@@ -4845,7 +4824,7 @@ msgstr "E866: (NFA-regulesprimo) Mispoziciigita %c"
#, c-format
msgid "E877: (NFA regexp) Invalid character class: %ld"
-msgstr "E877: (NFA-regulesprimo) Nevalida klaso de signo: %ld"
+msgstr "E877: (NFA-regulesprimo) Nevalida klaso de signoj: %ld"
#, c-format
msgid "E867: (NFA) Unknown operator '\\z%c'"
@@ -4855,7 +4834,6 @@ msgstr "E867: (NFA) Nekonata operatoro '\\z%c'"
msgid "E867: (NFA) Unknown operator '\\%%%c'"
msgstr "E867: (NFA) Nekonata operatoro '\\%%%c'"
-#. should never happen
msgid "E868: Error building NFA with equivalence class!"
msgstr "E868: Eraro dum prekomputado de NFA kun ekvivalentoklaso!"
@@ -4866,13 +4844,11 @@ msgstr "E869: (NFA) Nekonata operatoro '\\@%c'"
msgid "E870: (NFA regexp) Error reading repetition limits"
msgstr "E870: (NFS-regulesprimo) Eraro dum legado de limoj de ripeto"
-#. Can't have a multi follow a multi.
msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
msgstr ""
"E871: (NFA-regulesprimo) Ne povas havi mult-selekton tuj post alia mult-"
"selekto!"
-#. Too many `('
msgid "E872: (NFA regexp) Too many '('"
msgstr "E872: (NFA-regulesprimo) tro da '('"
@@ -4893,7 +4869,7 @@ msgstr ""
"statoj en la staplo"
msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr "E876: (NFA-regulesprimo) ne sufiĉa spaco por enmomorigi la tutan NFA "
+msgstr "E876: (NFA-regulesprimo) ne sufiĉa spaco por enmemorigi la tutan NFA "
msgid "E878: (NFA) Could not allocate memory for branch traversal!"
msgstr "E878: (NFA) Ne povis asigni memoron por traigi branĉojn!"
@@ -4975,12 +4951,11 @@ msgid "E385: search hit BOTTOM without match for: %s"
msgstr "E385: serĉo atingis SUBON sen trovi: %s"
msgid "E386: Expected '?' or '/' after ';'"
-msgstr "E386: Atendis '?' aŭ '/' post ';'"
+msgstr "E386: Atendis '?' aŭ '/' malantaŭ ';'"
msgid " (includes previously listed match)"
msgstr " (enhavas antaŭe listigitajn kongruojn)"
-#. cursor at status line
msgid "--- Included files "
msgstr "--- Inkluzivitaj dosieroj "
@@ -5057,8 +5032,6 @@ msgstr "Bedaŭrinde ne estas sugestoj"
msgid "Sorry, only %ld suggestions"
msgstr "Bedaŭrinde estas nur %ld sugestoj"
-#. for when 'cmdheight' > 1
-#. avoid more prompt
#, c-format
msgid "Change \"%.*s\" to:"
msgstr "Anstataŭigi \"%.*s\" per:"
@@ -5340,10 +5313,6 @@ msgstr "Densigis %d de %d nodoj; %d (%d%%) restantaj"
msgid "Reading back spell file..."
msgstr "Relegas la dosieron de literumo..."
-#.
-#. * Go through the trie of good words, soundfold each word and add it to
-#. * the soundfold trie.
-#.
msgid "Performing soundfolding..."
msgstr "Fonetika analizado..."
@@ -5398,18 +5367,38 @@ msgstr "Vorto '%.*s' aldonita al %s"
msgid "E763: Word characters differ between spell files"
msgstr "E763: Signoj de vorto malsamas tra literumaj dosieroj"
-#. This should have been checked when generating the .spl
-#. * file.
msgid "E783: duplicate char in MAP entry"
msgstr "E783: ripetita signo en rikordo MAP"
msgid "No Syntax items defined for this buffer"
msgstr "Neniu sintaksa elemento difinita por tiu bufro"
+msgid "syntax conceal on"
+msgstr "sintakso de conceal ŝaltata"
+
+msgid "syntax conceal off"
+msgstr "sintakso de conceal malŝaltita"
+
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Nevalida argumento: %s"
+msgid "syntax case ignore"
+msgstr "sintakso ignoras usklecon"
+
+msgid "syntax case match"
+msgstr "sintakso konsideras usklecon"
+
+msgid "syntax spell toplevel"
+msgstr "literumado en teksto sen sintaksa grupo"
+
+msgid "syntax spell notoplevel"
+msgstr "sen literumado en teksto sen sintaksa grupo"
+
+msgid "syntax spell default"
+msgstr ""
+"literumado en teksto sen sintaksa grupo, nur se ne estas @Spell aŭ @NoSpell"
+
msgid "syntax iskeyword "
msgstr "sintakso iskeyword "
@@ -5491,7 +5480,7 @@ msgstr "E789: Mankas ']': %s"
#, c-format
msgid "E890: trailing char after ']': %s]%s"
-msgstr "E890: vosta signo post ']': %s]%s"
+msgstr "E890: vosta signo malantaŭ ']': %s]%s"
#, c-format
msgid "E398: Missing '=': %s"
@@ -5513,7 +5502,7 @@ msgstr "E401: Disigilo de ŝablono netrovita: %s"
#, c-format
msgid "E402: Garbage after pattern: %s"
-msgstr "E402: Forĵetindaĵo post ŝablono: %s"
+msgstr "E402: Forĵetindaĵo malantaŭ ŝablono: %s"
msgid "E403: syntax sync: line continuations pattern specified twice"
msgstr "E403: sintaksa sinkronigo: ŝablono de linia daŭrigo specifita dufoje"
@@ -5645,7 +5634,6 @@ msgstr "E428: Ne eblas iri preter lastan kongruan etikedon"
msgid "File \"%s\" does not exist"
msgstr "La dosiero \"%s\" ne ekzistas"
-#. Give an indication of the number of matching tags
#, c-format
msgid "tag %d of %d%s"
msgstr "etikedo %d de %d%s"
@@ -5660,7 +5648,6 @@ msgstr " Uzo de etikedo kun malsama uskleco!"
msgid "E429: File \"%s\" does not exist"
msgstr "E429: Dosiero \"%s\" ne ekzistas"
-#. Highlight title
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -5691,7 +5678,6 @@ msgstr "Antaŭ bajto %ld"
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Etikeda dosiero ne estas ordigita: %s"
-#. never opened any tags file
msgid "E433: No tags file"
msgstr "E433: Neniu etikeda dosiero"
@@ -5727,7 +5713,6 @@ msgstr "E436: Neniu rikordo \"%s\" en termcap"
msgid "E437: terminal capability \"cm\" required"
msgstr "E437: kapablo de terminalo \"cm\" bezonata"
-#. Highlight title
msgid ""
"\n"
"--- Terminal keys ---"
@@ -5738,6 +5723,21 @@ msgstr ""
msgid "Cannot open $VIMRUNTIME/rgb.txt"
msgstr "Ne povas malfermi $VIMRUNTIME/rgb.txt"
+msgid "Terminal"
+msgstr "Terminalo"
+
+msgid "Terminal-finished"
+msgstr "Terminalo-finiĝis"
+
+msgid "active"
+msgstr "aktiva"
+
+msgid "running"
+msgstr "ruliĝas"
+
+msgid "finished"
+msgstr "finiĝis"
+
msgid "new shell started\n"
msgstr "nova ŝelo lanĉita\n"
@@ -5747,12 +5747,9 @@ msgstr "Vim: Eraro dum legado de eniro, elironta...\n"
msgid "Used CUT_BUFFER0 instead of empty selection"
msgstr "Uzis CUT_BUFFER0 anstataŭ malplenan apartigon"
-#. This happens when the FileChangedRO autocommand changes the
-#. * file in a way it becomes shorter.
msgid "E881: Line count changed unexpectedly"
msgstr "E881: Nombro de linioj ŝanĝiĝis neatendite"
-#. must display the prompt
msgid "No undo possible; continue anyway"
msgstr "Malfaro neebla; tamen daŭrigi"
@@ -5987,6 +5984,10 @@ msgid "E126: Missing :endfunction"
msgstr "E126: Mankas \":endfunction\""
#, c-format
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: Teksto trovita malantaŭ :endfunction: %s"
+
+#, c-format
msgid "E707: Function name conflicts with variable: %s"
msgstr "E707: Nomo de funkcio konfliktas kun variablo: %s"
@@ -6382,7 +6383,6 @@ msgstr "Kompari per Vim"
msgid "Edit with &Vim"
msgstr "Redakti per &Vim"
-#. Now concatenate
msgid "Edit with existing Vim - "
msgstr "Redakti per ekzistanta Vim - "
@@ -6401,10 +6401,6 @@ msgstr "Serĉvojo estas tro longa!"
msgid "--No lines in buffer--"
msgstr "--Neniu linio en bufro--"
-#.
-#. * The error messages that can be shared are included here.
-#. * Excluded are errors that are only used once and debugging messages.
-#.
msgid "E470: Command aborted"
msgstr "E470: komando ĉesigita"
@@ -6590,12 +6586,6 @@ msgstr "E484: Ne eblas malfermi dosieron %s"
msgid "E485: Can't read file %s"
msgstr "E485: Ne eblas legi dosieron %s"
-msgid "E37: No write since last change (add ! to override)"
-msgstr "E37: Neniu skribo de post lasta ŝanĝo (aldonu ! por transpasi)"
-
-msgid "E37: No write since last change"
-msgstr "E37: Neniu skribo de post lasta ŝanĝo"
-
msgid "E38: Null argument"
msgstr "E38: Nula argumento"
@@ -6730,8 +6720,8 @@ msgstr "E592: 'winwidth' ne rajtas esti malpli ol 'winminwidth'"
msgid "E80: Error while writing"
msgstr "E80: Eraro dum skribado"
-msgid "Zero count"
-msgstr "Nul kvantoro"
+msgid "E939: Positive count required"
+msgstr "E939: Pozitiva kvantoro bezonata"
msgid "E81: Using <SID> not in a script context"
msgstr "E81: Uzo de <SID> ekster kunteksto de skripto"
@@ -6875,7 +6865,6 @@ msgstr "konstruilo de listo ne akceptas ŝlosilvortajn argumentojn"
msgid "list index out of range"
msgstr "indekso de listo ekster limoj"
-#. No more suitable format specifications in python-2.3
#, c-format
msgid "internal error: failed to get vim list item %d"
msgstr "interna eraro: obteno de vim-a listero %d malsukcesis"
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index ed96e4de94..10b661a5d2 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -1197,7 +1197,7 @@ msgstr "E141: No existe un nombre de archivo para el búfer %<PRId64>"
#: ../ex_cmds.c:2412
msgid "E142: File not written: Writing is disabled by 'write' option"
msgstr ""
-"E142: No se ha escrito el archivo: escritura desactivada por \n"
+"E142: No se ha escrito el archivo: escritura desactivada por "
"la opción 'write'"
#: ../ex_cmds.c:2434
@@ -2209,7 +2209,7 @@ msgstr "es de solo lectura (añada ! para sobreescribir)"
#: ../fileio.c:2886
msgid "E506: Can't write to backup file (add ! to override)"
msgstr ""
-"E506: No se pudo escribir en el archivo de recuperación\n"
+"E506: No se pudo escribir en el archivo de recuperación "
"(añada ! para forzar la orden)"
#: ../fileio.c:2898
@@ -3136,7 +3136,7 @@ msgstr ""
#: ../hardcopy.c:2254
msgid "E675: No default font specified for multi-byte printing."
msgstr ""
-"E675: No se ha definido un tipo de letra predeterminado para impresión\n"
+"E675: No se ha definido un tipo de letra predeterminado para impresión "
"multi-byte"
#: ../hardcopy.c:2426
@@ -3393,7 +3393,7 @@ msgstr "Basura después de la opción"
#: ../main.c:152
msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"
msgstr ""
-"Demasiados argumentos tales como: \"+orden\", \"-c orden\" \n"
+"Demasiados argumentos tales como: \"+orden\", \"-c orden\" "
"o \"--cmd orden\""
#: ../main.c:154
@@ -3797,7 +3797,7 @@ msgstr "E302: No pude cambiar el nombre del archivo de intercambio"
#, c-format
msgid "E303: Unable to open swap file for \"%s\", recovery impossible"
msgstr ""
-"E303: Incapaz de abrir el archivo de intercambio para %s,\n"
+"E303: Incapaz de abrir el archivo de intercambio para %s, "
"recuperación imposible"
#: ../memline.c:666
@@ -3916,7 +3916,7 @@ msgstr "??? desde aquí hasta ???FIN las líneas pueden estar desordenadas"
#: ../memline.c:1164
msgid "??? from here until ???END lines may have been inserted/deleted"
msgstr ""
-"??? desde aquí hasta ???FIN las líneas pueden haber sido\n"
+"??? desde aquí hasta ???FIN las líneas pueden haber sido "
"insertadas/borradas"
#: ../memline.c:1181
@@ -3931,7 +3931,7 @@ msgstr "E311: Recuperación interrumpida"
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
msgstr ""
-"E312: Se han detectado errores al recuperar; busque líneas que\n"
+"E312: Se han detectado errores al recuperar; busque líneas que "
"empiecen con ???"
#: ../memline.c:1245
@@ -5382,7 +5382,7 @@ msgstr "E756: La corrección ortográfica está desactivada"
#, c-format
msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
msgstr ""
-"Advertencia: No se pudo hallar la lista de palabras \"%s.%s.spl\" \n"
+"Advertencia: No se pudo hallar la lista de palabras \"%s.%s.spl\" "
"or \"%s.ascii.spl\""
#: ../spell.c:2473
@@ -5443,8 +5443,7 @@ msgid ""
"%d"
msgstr ""
"Definir COMPOUNDFORBIDFLAG después de un elemento PFX puede dar resultados "
-"erróneos\n"
-"en %s línea %d"
+"erróneos en %s línea %d"
#: ../spell.c:4731
#, c-format
@@ -5453,8 +5452,7 @@ msgid ""
"%d"
msgstr ""
"Definir COMPOUNDPERMITFLAG después de un ítem PFX puede dar resultados "
-"erróneos\n"
-"en %s línea %d"
+"erróneos en %s línea %d"
#: ../spell.c:4747
#, c-format
@@ -5485,7 +5483,7 @@ msgstr "Valor equivocado de CHECKCOMPOUNDPATTERN en %s línea %d: %s"
#, c-format
msgid "Different combining flag in continued affix block in %s line %d: %s"
msgstr ""
-"Marca de combinación diferente en el bloque de afijos continuo\n"
+"Marca de combinación diferente en el bloque de afijos continuo "
"en %s línea %d: %s"
#: ../spell.c:4850
@@ -5500,8 +5498,7 @@ msgid ""
"line %d: %s"
msgstr ""
"Afijo usado también para BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST "
-"en\n"
-"%s línea %d: %s"
+"en %s línea %d: %s"
#: ../spell.c:4893
#, c-format
@@ -5942,7 +5939,7 @@ msgstr "E402: Basura después del patrón: %s"
#: ../syntax.c:5120
msgid "E403: syntax sync: line continuations pattern specified twice"
msgstr ""
-"E403: Sincronización de sintaxis: Se especificó dos veces un\n"
+"E403: Sincronización de sintaxis: Se especificó dos veces un "
"patrón de continuación de línea"
#: ../syntax.c:5169
@@ -6569,7 +6566,7 @@ msgstr "E813: No se puede cerrar la ventana de autocmd"
#: ../window.c:1814
msgid "E814: Cannot close window, only autocmd window would remain"
msgstr ""
-"E814: No se pudo cerrar la última ventana, solo quedará\n"
+"E814: No se pudo cerrar la última ventana, solo quedará "
"la ventana de autocmd"
#: ../window.c:2717
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index 3551c07ff2..8fa0cc48d4 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -1,6 +1,6 @@
# Finnish translation for Vim.
# Copyright (C) 2003-2006 Free Software Foundation, Inc.
-# 2007-2016, Flammie Pirinen <flammie@iki.fi>
+# 2007-2018, Flammie Pirinen <flammie@iki.fi>
#
# Jargonia ei ole yritetty suotta kotoperäistää missä teknisempi lainasanasto
# tulee paremmin kyseeseen.
@@ -313,7 +313,6 @@ msgstr "E90: Ei voi vapauttaa viimeistä puskuria"
msgid "E84: No modified buffer found"
msgstr "E84: Ei muokattuja puskureita"
-#. back where we started, didn't find anything.
msgid "E85: There is no listed buffer"
msgstr "E85: Luetteloitua puskuria ei ole"
@@ -390,7 +389,6 @@ msgstr "1 rivi --%d %%--"
msgid "[No Name]"
msgstr "[Nimetön]"
-#. must be a help buffer
msgid "help"
msgstr "ohje"
@@ -495,7 +493,6 @@ msgstr "E791: Tyhjä keymap-kenttä"
msgid " Keyword completion (^N^P)"
msgstr " Avainsanatäydennys (^N^P)"
-#. ctrl_x_mode == 0, ^P/^N compl.
msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
msgstr " ^X-tila (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
@@ -570,10 +567,6 @@ msgstr "Luetaan tägejä."
msgid " Adding"
msgstr " Lisätään"
-#. showmode might reset the internal line pointers, so it must
-#. * be called before line = ml_get(), or when this address is no
-#. * longer needed. -- Acevedo.
-#.
msgid "-- Searching..."
msgstr "-- Haetaan..."
@@ -1418,6 +1411,18 @@ msgstr "E907: Käytettiin erikoisarvoa Floattina"
msgid "E808: Number or Float required"
msgstr "E808: Number tai Float vaaditaan"
+#, c-format
+msgid "line %ld: %s"
+msgstr "rivi %ld: %s"
+
+#, c-format
+msgid "Breakpoint in \"%s%s\" line %ld"
+msgstr "Katkaisukohta %s%s rivillä %ld"
+
+#, c-format
+msgid "%3d %s %s line %ld"
+msgstr "%3d %s %s rivi %ld"
+
# puhutaan merkin ulkoasusta snprintf(..., c, c, c, c)
#, c-format
msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
@@ -1545,7 +1550,7 @@ msgstr " 1 rivillä"
#~ msgid " on %<PRId64> lines"
#~ msgstr " %ld rivillä"
-msgid "E147: Cannot do :global recursive"
+msgid "E147: Cannot do :global recursive with a range"
msgstr "E147: :globalia ei voi suorittaa rekursiivisesti"
msgid "E148: Regular expression missing from global"
@@ -1575,6 +1580,10 @@ msgid "Sorry, help file \"%s\" not found"
msgstr "ohjetiedostoa %s ei löydy"
#, c-format
+msgid "E151: No match: %s"
+msgstr "E151: Ei täsmää: %s"
+
+#, c-format
msgid "E152: Cannot open %s for writing"
msgstr "E152: Ei voi avata tiedostoa %s kirjoittamista varten"
@@ -1926,7 +1935,6 @@ msgstr "E189: %s on jo olemassa (lisää komentoon ! ohittaaksesi)"
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: Tiedostoa %s ei voitu avata kirjoittamista varten"
-#. set mark
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr "E191: Argumentin eteen- tai taaksepäin lainaukseen pitää olla kirjain"
@@ -1979,7 +1987,6 @@ msgstr "Poikkeus poistettu: %s"
#~ msgid "%s, line %<PRId64>"
#~ msgstr "%s, rivi %ld"
-#. always scroll up, don't overwrite
#, c-format
msgid "Exception caught: %s"
msgstr "Poikkeus otettu kiinni: %s"
@@ -2005,7 +2012,6 @@ msgstr "Virhe ja keskeytys"
msgid "Error"
msgstr "Virhe"
-#. if (pending & CSTP_INTERRUPT)
msgid "Interrupt"
msgstr "Keskeytys"
@@ -2048,15 +2054,12 @@ msgstr "E601: liian monta tasoa :try-komennossa"
msgid "E603: :catch without :try"
msgstr "E603: :catch ilman komentoa :try"
-#. Give up for a ":catch" after ":finally" and ignore it.
-#. * Just parse.
msgid "E604: :catch after :finally"
msgstr "E604: :catch ilman komentoa :finally"
msgid "E606: :finally without :try"
msgstr "E606: :finally ilman komentoa :try"
-#. Give up for a multiple ":finally" and ignore it.
msgid "E607: multiple :finally"
msgstr "E607: :finally monta kertaa"
@@ -2147,7 +2150,6 @@ msgstr ""
msgid "E201: *ReadPre autocommands must not change current buffer"
msgstr "E201: *ReadPre-autocommand-komennot eivät saa muuttaa puskuria"
-#. Re-opening the original file failed!
msgid "E202: Conversion made file unreadable!"
msgstr "E202: Muunnos teki tiedostosta lukukelvottoman."
@@ -2469,7 +2471,6 @@ msgstr "E216: Eventtiä ei ole: %s"
msgid "E216: No such group or event: %s"
msgstr "E216: Ryhmää tai eventtiä ei ole: %s"
-#. Highlight title
msgid ""
"\n"
"--- Auto-Commands ---"
@@ -3113,7 +3114,6 @@ msgstr "E261: cscope-yhteys %s puuttuu"
msgid "cscope connection %s closed"
msgstr "cscope-yhteys %s on katkaistu"
-#. should not reach here
msgid "E570: fatal error in cs_manage_matches"
msgstr "E570: kriittinen virhe cs_manage_matches-funktiossa"
@@ -3176,7 +3176,6 @@ msgstr "Vim: Varoitus: Tuloste ei mene terminaalille\n"
msgid "Vim: Warning: Input is not from a terminal\n"
msgstr "Vim: Varoitus: Syöte ei tule terminaalilta\n"
-#. just in case..
msgid "pre-vimrc command line"
msgstr "esi-vimrc-komentorivi"
@@ -3394,7 +3393,6 @@ msgstr "Ei asetettuja merkkejä"
msgid "E283: No marks matching \"%s\""
msgstr "E283: Mikään merkki ei täsmää ilmaukseen \"%s\""
-#. Highlight title
msgid ""
"\n"
"mark line col file/text"
@@ -3402,7 +3400,6 @@ msgstr ""
"\n"
"merkki rivi sarake tiedosto/teksti"
-#. Highlight title
msgid ""
"\n"
" jump line col file/text"
@@ -3410,7 +3407,6 @@ msgstr ""
"\n"
"hyppy rivi sarake tiedosto/teksti"
-#. Highlight title
msgid ""
"\n"
"change line col text"
@@ -3445,7 +3441,6 @@ msgstr "E298: Lohko 1:tä ei saatu?"
msgid "E298: Didn't get block nr 2?"
msgstr "E298: Lohko 2:ta ei saatu?"
-#. could not (re)open the swap file, what can we do????
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: Hups, swap-tiedosto hävisi!"
@@ -3585,7 +3580,6 @@ msgstr ""
"Voit poistaa .swp-tiedosto nyt.\n"
"\n"
-#. use msg() to start the scrolling properly
msgid "Swap files found:"
msgstr "Swap-tiedostoja löytyi:"
@@ -3751,8 +3745,6 @@ msgstr "Avattaessa tiedostoa "
msgid " NEWER than swap file!\n"
msgstr " joka on UUDEMPI kuin swap-tiedosto!\n"
-#. Some of these messages are long to allow translation to
-#. * other languages.
msgid ""
"\n"
"(1) Another program may be editing the same file. If this is the case,\n"
@@ -3859,7 +3851,6 @@ msgstr "E328: Valikko on olemassa vain toisessa tilassa"
msgid "E329: No menu \"%s\""
msgstr "E329: Ei valikkoa %s"
-#. Only a mnemonic or accelerator is not valid.
msgid "E792: Empty menu name"
msgstr "E792: tyhjä valikkonimi"
@@ -3872,8 +3863,6 @@ msgstr "E331: Valikkokohtia ei saa lisätä suoraan valikkopalkkiin"
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: Erotin ei voi olla valikkopolun osa"
-#. Now we have found the matching menu, and we list the mappings
-#. Highlight title
msgid ""
"\n"
"--- Menus ---"
@@ -4354,7 +4343,6 @@ msgstr "E376: Virheellinen %%%c muotoilumerkkijonon alussa"
msgid "E377: Invalid %%%c in format string"
msgstr "E377: Virheellinen %%%c muotoilumerkkijonossa"
-#. nothing found
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: errorformatissa ei ole kuvioita"
@@ -4479,9 +4467,6 @@ msgstr "E63: väärinkäytetty \\_"
msgid "E64: %s%c follows nothing"
msgstr "E64: %s%c jälkeen ei minkään"
-msgid "E65: Illegal back reference"
-msgstr "E65: Virheellinen täsmäysviittaus"
-
msgid "E68: Invalid character after \\z"
msgstr "E68: Virheellinen merkki ilmauksen \\z jälkeen"
@@ -4587,7 +4572,6 @@ msgstr "E386: ;:n jälkeen pitää olla ? tai /"
msgid " (includes previously listed match)"
msgstr " (sisältää viimeksi luetellun täsmäyksen)"
-#. cursor at status line
msgid "--- Included files "
msgstr "--- Sisällytetyt tiedostot "
@@ -4735,7 +4719,7 @@ msgstr " EPÄONNISTUI"
#~ msgstr "E886: Viminfo-tiedostoa ei voit uudelleennimetä nimelle %s"
#, fuzzy, c-format
-#~ msgid "Did not rename %s because %s does not looks like a ShaDa file"
+#~ msgid "Did not rename %s because %s does not look like a ShaDa file"
#~ msgstr " [ei näytä Vimin swap-tiedostolta]"
#, c-format
@@ -4841,8 +4825,6 @@ msgstr "ei ehdotuksia"
#~ msgid "Sorry, only %<PRId64> suggestions"
#~ msgstr "vain %ld ehdotusta"
-#. for when 'cmdheight' > 1
-#. avoid more prompt
#, c-format
msgid "Change \"%.*s\" to:"
msgstr "Muuta %.*s:"
@@ -5430,7 +5412,6 @@ msgstr "E428: Ei voida edetä viimeisen täsmäävän tägin ohi"
msgid "File \"%s\" does not exist"
msgstr "Tiedostoa %s ei ole"
-#. Give an indication of the number of matching tags
#, c-format
msgid "tag %d of %d%s"
msgstr "tägi %d/%d%s"
@@ -5445,7 +5426,6 @@ msgstr " Tägissä eri kirjaintaso"
msgid "E429: File \"%s\" does not exist"
msgstr "E429: Tiedostoa %s ei ole"
-#. Highlight title
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -5472,7 +5452,6 @@ msgstr "E431: Muotovirh tägitiedostossa %s"
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Tägitiedosto ei ole järjestetty: %s"
-#. never opened any tags file
msgid "E433: No tags file"
msgstr "E433: Ei tägitiedostoja"
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index c0df5f2170..4fa4ef8f8f 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -4,20 +4,17 @@
# Do ":help uganda" in Vim to read copying and usage conditions.
# Do ":help credits" in Vim to see a list of people who contributed.
#
-# FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000.
-# SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003.
-# THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008.
-# FOURTH AUTHOR Dominique Pell <dominique.pelle@gmail.com> 2008, 2017.
-#
-# Latest translation available at:
-# http://dominique.pelle.free.fr/vim-fr.php
+# FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000.
+# SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003.
+# THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008.
+# FOURTH AUTHOR Dominique Pell <dominique.pelle@gmail.com> 2008, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Vim(Franais)\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-01-16 00:30+0100\n"
-"PO-Revision-Date: 2017-01-16 00:51+0100\n"
+"POT-Creation-Date: 2017-10-04 23:32+0200\n"
+"PO-Revision-Date: 2017-10-04 23:44+0200\n"
"Last-Translator: Dominique Pell <dominique.pelle@gmail.com>\n"
"Language-Team: \n"
"Language: fr\n"
@@ -104,7 +101,6 @@ msgstr "E90: Impossible de dcharger le dernier tampon"
msgid "E84: No modified buffer found"
msgstr "E84: Aucun tampon n'est modifi"
-#. back where we started, didn't find anything.
msgid "E85: There is no listed buffer"
msgstr "E85: Aucun tampon n'est list"
@@ -121,6 +117,18 @@ msgid "E89: No write since last change for buffer %ld (add ! to override)"
msgstr ""
"E89: Le tampon %ld n'a pas t enregistr (ajoutez ! pour passer outre)"
+msgid "E948: Job still running (add ! to end the job)"
+msgstr "E948: Tche en cours d'excution (ajouter ! pour terminer la tche)"
+
+msgid "E37: No write since last change (add ! to override)"
+msgstr "E37: Modifications non enregistres (ajoutez ! pour passer outre)"
+
+msgid "E948: Job still running"
+msgstr "E948: Tche en cours d'excution"
+
+msgid "E37: No write since last change"
+msgstr "E37: Modifications non enregistres"
+
msgid "W14: Warning: List of file names overflow"
msgstr "W14: Alerte : La liste des noms de fichier dborde"
@@ -187,7 +195,6 @@ msgstr "ligne %ld sur %ld --%d%%-- col "
msgid "[No Name]"
msgstr "[Aucun nom]"
-#. must be a help buffer
msgid "help"
msgstr "aide"
@@ -218,6 +225,9 @@ msgstr ""
"\n"
"# Liste des tampons :\n"
+msgid "E382: Cannot write, 'buftype' option is set"
+msgstr "E382: criture impossible, l'option 'buftype' est active"
+
msgid "[Scratch]"
msgstr "[Brouillon]"
@@ -396,7 +406,6 @@ msgid " Keyword completion (^N^P)"
msgstr " Compltement de mot-cl (^N^P)"
# DB - todo : Faut-il une majuscule "mode" ?
-#. ctrl_x_mode == 0, ^P/^N compl.
msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
msgstr " mode ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
@@ -481,6 +490,9 @@ msgstr "Examen : %s"
msgid "Scanning tags."
msgstr "Examen des marqueurs."
+msgid "match in file"
+msgstr "correspondance dans le fichier"
+
# AB - Cette chane de caractres est ajoute en dbut de ligne lorsqu'une
# opration de compltion est rpte (typiquement avec CTRL-X CTRL-N).
# Que ce soit en anglais ou en franais, il y a un problme de majuscules.
@@ -488,10 +500,6 @@ msgstr "Examen des marqueurs."
msgid " Adding"
msgstr " Ajout"
-#. showmode might reset the internal line pointers, so it must
-#. * be called before line = ml_get(), or when this address is no
-#. * longer needed. -- Acevedo.
-#.
msgid "-- Searching..."
msgstr "-- Recherche en cours..."
@@ -522,7 +530,6 @@ msgstr "Correspondance %d sur %d"
msgid "match %d"
msgstr "Correspondance %d"
-#. maximum nesting of lists and dicts
msgid "E18: Unexpected characters in :let"
msgstr "E18: Caractres inattendus avant '='"
@@ -584,6 +591,10 @@ msgstr "E690: \"in\" manquant aprs :for"
msgid "E108: No such variable: \"%s\""
msgstr "E108: Variable inexistante : %s"
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: Impossible de (d)verrouiler la variable %s"
+
msgid "E743: variable nested too deep for (un)lock"
msgstr "E743: variable trop imbrique pour la (d)verrouiller"
@@ -765,21 +776,10 @@ msgstr "E785: complete() n'est utilisable que dans le mode Insertion"
# AB - Texte par dfaut du bouton de la bote de dialogue affiche par la
# fonction confirm().
-#.
-#. * Yes this is ugly, I don't particularly like it either. But doing it
-#. * this way has the compelling advantage that translations need not to
-#. * be touched at all. See below what 'ok' and 'ync' are used for.
-#.
msgid "&Ok"
msgstr "&Ok"
#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld ligne : "
-msgstr[1] "+-%s%3ld lignes : "
-
-#, c-format
msgid "E700: Unknown function: %s"
msgstr "E700: Fonction inconnue : %s"
@@ -827,9 +827,7 @@ msgstr "E727: Dbut au-del de la fin"
msgid "<empty>"
msgstr "<vide>"
-# AB - mon avis, la version anglaise est errone.
-# DB : Vrifier
-msgid "E240: No connection to Vim server"
+msgid "E240: No connection to the X server"
msgstr "E240: Pas de connexion au serveur X"
# AB - La version franaise est meilleure que la version anglaise.
@@ -840,6 +838,12 @@ msgstr "E241: L'envoi au serveur %s a chou"
msgid "E277: Unable to read a server reply"
msgstr "E277: Impossible de lire la rponse du serveur"
+msgid "E941: already started a server"
+msgstr "E941: serveur dj dmarr"
+
+msgid "E942: +clientserver feature not available"
+msgstr "E942: La fonctionnalit +clientserver n'est pas disponible"
+
msgid "remove() argument"
msgstr "argument de remove()"
@@ -965,7 +969,6 @@ msgstr " CHEC"
# ses droits d'accs.
# AB - Le mot "viminfo" a t retir pour que le message ne dpasse pas 80
# caractres dans le cas courant o %s = /home/12345678/.viminfo
-#. avoid a wait_return for this message, it's annoying
#, c-format
msgid "E137: Viminfo file is not writable: %s"
msgstr "E137: L'criture dans le fichier %s est interdite"
@@ -990,7 +993,6 @@ msgstr "criture du fichier viminfo \"%s\""
msgid "E886: Can't rename viminfo file to %s!"
msgstr "E886: Impossible de renommer viminfo en %s"
-#. Write the info:
#, c-format
msgid "# This viminfo file was generated by Vim %s.\n"
msgstr "# Ce fichier viminfo a t gnr par Vim %s.\n"
@@ -1137,8 +1139,8 @@ msgstr " sur %ld lignes"
# AB - Il faut respecter l'esprit plus que la lettre.
# AB - Ce message devrait contenir une rfrence :vglobal.
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: :global ne peut pas excuter :global"
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: :global ne peut pas excuter :global avec une plage"
# AB - Ce message devrait contenir une rfrence :vglobal.
msgid "E148: Regular expression missing from global"
@@ -1317,10 +1319,9 @@ msgstr "E750: Utilisez d'abord \":profile start {nomfichier}\""
msgid "Save changes to \"%s\"?"
msgstr "Enregistrer \"%s\" ?"
-# AB - Si les parenthses posent problme, il faudra remettre les guillemets
-# ci-dessus.
-msgid "Untitled"
-msgstr "(sans titre)"
+#, c-format
+msgid "E947: Job still running in buffer \"%s\""
+msgstr "E947: Tche en cours d'excution dans le buffer \"%s\""
# AB - Il faut respecter l'esprit plus que la lettre.
# AB - Ce message est similaire au message E89.
@@ -1357,6 +1358,14 @@ msgstr "Recherche de \"%s\""
msgid "not found in '%s': \"%s\""
msgstr "introuvable dans '%s' : \"%s\""
+#, c-format
+msgid "W20: Required python version 2.x not supported, ignoring file: %s"
+msgstr "W20: Python version 2.x non support, fichier %s ignor"
+
+#, c-format
+msgid "W21: Required python version 3.x not supported, ignoring file: %s"
+msgstr "W21: Python 3.x non support, fichier %s ignor"
+
msgid "Source Vim script"
msgstr "Sourcer un script - Vim"
@@ -1457,6 +1466,10 @@ msgstr "La plage spcifie est inverse, OK pour l'inverser"
msgid "E494: Use w or w>>"
msgstr "E494: Utilisez w ou w>>"
+msgid "E943: Command table needs to be updated, run 'make cmdidxs'"
+msgstr ""
+"E943: La table des commandes doit tre mise jour, lancez 'make cmdidxs'"
+
msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: Dsol, cette commande n'est pas disponible dans cette version"
@@ -1622,7 +1635,6 @@ msgstr "E189: \"%s\" existe (ajoutez ! pour passer outre)"
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: Impossible d'ouvrir \"%s\" pour y crire"
-#. set mark
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr "E191: L'argument doit tre une lettre ou une (contre-)apostrophe"
@@ -1660,13 +1672,17 @@ msgstr "E500: valu en une chane vide"
msgid "E195: Cannot open viminfo file for reading"
msgstr "E195: Impossible d'ouvrir le viminfo en lecture"
+# AB - Si les parenthses posent problme, il faudra remettre les guillemets
+# ci-dessus.
+msgid "Untitled"
+msgstr "(sans titre)"
+
msgid "E196: No digraphs in this version"
msgstr "E196: Pas de digraphes dans cette version"
msgid "E608: Cannot :throw exceptions with 'Vim' prefix"
msgstr "E608: Impossible d'mettre des exceptions avec 'Vim' comme prfixe"
-#. always scroll up, don't overwrite
#, c-format
msgid "Exception thrown: %s"
msgstr "Exception mise : %s"
@@ -1683,7 +1699,6 @@ msgstr "Exception limine : %s"
msgid "%s, line %ld"
msgstr "%s, ligne %ld"
-#. always scroll up, don't overwrite
#, c-format
msgid "Exception caught: %s"
msgstr "Exception intercepte : %s"
@@ -1710,7 +1725,6 @@ msgstr "Erreur et interruption"
msgid "Error"
msgstr "Erreur"
-#. if (pending & CSTP_INTERRUPT)
msgid "Interrupt"
msgstr "Interruption"
@@ -1753,15 +1767,12 @@ msgstr "E601: Imbrication de :try trop importante"
msgid "E603: :catch without :try"
msgstr "E603: :catch sans :try"
-#. Give up for a ":catch" after ":finally" and ignore it.
-#. * Just parse.
msgid "E604: :catch after :finally"
msgstr "E604: :catch aprs :finally"
msgid "E606: :finally without :try"
msgstr "E606: :finally sans :try"
-#. Give up for a multiple ":finally" and ignore it.
msgid "E607: multiple :finally"
msgstr "E607: Il ne peut y avoir qu'un seul :finally"
@@ -1862,7 +1873,6 @@ msgstr "Vim : Lecture de stdin...\n"
msgid "Reading from stdin..."
msgstr "Lecture de stdin..."
-#. Re-opening the original file failed!
msgid "E202: Conversion made file unreadable!"
msgstr "E202: La conversion a rendu le fichier illisible !"
@@ -2077,9 +2087,6 @@ msgstr "[noeol]"
msgid "[Incomplete last line]"
msgstr "[Dernire ligne incomplte]"
-#. don't overwrite messages here
-#. must give this prompt
-#. don't use emsg() here, don't want to flush the buffers
msgid "WARNING: The file has been changed since reading it!!!"
msgstr "ALERTE : Le fichier a t modifi depuis que Vim l'a lu !"
@@ -2161,7 +2168,6 @@ msgstr "--Effac--"
msgid "auto-removing autocommand: %s <buffer=%d>"
msgstr "Autocommandes marques pour auto-suppression : %s <tampon=%d>"
-#. the group doesn't exist
#, c-format
msgid "E367: No such group: \"%s\""
msgstr "E367: Aucun groupe \"%s\""
@@ -2184,7 +2190,6 @@ msgstr "E216: Aucun vnement %s"
msgid "E216: No such group or event: %s"
msgstr "E216: Aucun vnement ou groupe %s"
-#. Highlight title
msgid ""
"\n"
"--- Auto-Commands ---"
@@ -2233,12 +2238,6 @@ msgstr "E350: Impossible de crer un repli avec la 'foldmethod'e actuelle"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: Impossible de supprimer un repli avec la 'foldmethod'e actuelle"
-#, c-format
-msgid "+--%3ld line folded "
-msgid_plural "+--%3ld lines folded "
-msgstr[0] "+--%3ld ligne replie "
-msgstr[1] "+--%3ld lignes replies "
-
msgid "E222: Add to read buffer"
msgstr "E222: Ajout au tampon de lecture"
@@ -2377,18 +2376,15 @@ msgstr "Rechercher :"
msgid "Replace with:"
msgstr "Remplacer par :"
-#. whole word only button
msgid "Match whole word only"
msgstr "Mots entiers seulement"
-#. match case button
msgid "Match case"
msgstr "Respecter la casse"
msgid "Direction"
msgstr "Direction"
-#. 'Up' and 'Down' buttons
msgid "Up"
msgstr "Haut"
@@ -2472,8 +2468,6 @@ msgstr "Chercher et remplacer (utilisez '\\\\' pour trouver un '\\')"
# DB - Traduction non indispensable puisque le code indique qu'il s'agit d'un
# paramtrage bidon afin de slectionner un rpertoire plutt qu'un
# fichier.
-#. We fake this: Use a filter that doesn't select anything and a default
-#. * file name that won't be used.
msgid "Not Used"
msgstr "Non utilis"
@@ -2554,7 +2548,6 @@ msgstr "Choisir une police - Vim"
msgid "Name:"
msgstr "Nom :"
-#. create toggle button
msgid "Show size in Points"
msgstr "Afficher la taille en Points"
@@ -2805,7 +2798,6 @@ msgstr "E261: Connexion cscope %s introuvable"
msgid "cscope connection %s closed"
msgstr "connexion cscope %s ferme"
-#. should not reach here
msgid "E570: fatal error in cs_manage_matches"
msgstr "E570: erreur fatale dans cs_manage_matches"
@@ -2970,7 +2962,6 @@ msgid "not implemented yet"
msgstr "pas encore implment"
# DB - TODO : le contexte est celui d'une annulation.
-#. ???
msgid "cannot set line(s)"
msgstr "Impossible de remettre la/les ligne(s)"
@@ -3011,7 +3002,6 @@ msgid ""
msgstr ""
"Impossible d'inscrire la commande de rappel : tampon/fentre en effacement"
-#. This should never happen. Famous last word?
msgid ""
"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim."
"org"
@@ -3114,7 +3104,6 @@ msgstr "Vim : Alerte : La sortie ne s'effectue pas sur un terminal\n"
msgid "Vim: Warning: Input is not from a terminal\n"
msgstr "Vim : Alerte : L'entre ne se fait pas sur un terminal\n"
-#. just in case..
msgid "pre-vimrc command line"
msgstr "ligne de commande pre-vimrc"
@@ -3281,8 +3270,7 @@ msgstr ""
"--no-a-term\t\tAucun avertissement si l'entre/sortie n'est pas un terminal"
msgid "--ttyfail\t\tExit if input or output is not a terminal"
-msgstr ""
-"--ttyfail\t\tQuitte si l'entre ou la sortie ne sont pas un terminal"
+msgstr "--ttyfail\t\tQuitte si l'entre ou la sortie ne sont pas un terminal"
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\tUtiliser <vimrc> au lieu du vimrc habituel"
@@ -3382,6 +3370,9 @@ msgstr ""
msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
msgstr "-i <viminfo>\t\tUtiliser <viminfo> au lieu du viminfo habituel"
+msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo"
+msgstr "--clean\t\t'nocompatible', rglages par dfaut, aucun greffon ni viminfo"
+
msgid "-h or --help\tPrint Help (this message) and exit"
msgstr "-h ou --help\t\tAfficher l'aide (ce message) puis quitter"
@@ -3484,11 +3475,9 @@ msgstr "--windowid <HWND>\tOuvrir Vim dans un autre widget win32"
msgid "No display"
msgstr "Aucun display"
-#. Failed to send, abort.
msgid ": Send failed.\n"
msgstr " : L'envoi a chou.\n"
-#. Let vim start normally.
msgid ": Send failed. Trying to execute locally\n"
msgstr " : L'envoi a chou. Tentative d'excution locale\n"
@@ -3509,7 +3498,6 @@ msgstr "Aucune marque positionne"
msgid "E283: No marks matching \"%s\""
msgstr "E283: Aucune marque ne correspond \"%s\""
-#. Highlight title
msgid ""
"\n"
"mark line col file/text"
@@ -3517,7 +3505,6 @@ msgstr ""
"\n"
"marq ligne col fichier/texte"
-#. Highlight title
msgid ""
"\n"
" jump line col file/text"
@@ -3525,7 +3512,6 @@ msgstr ""
"\n"
" saut ligne col fichier/texte"
-#. Highlight title
msgid ""
"\n"
"change line col text"
@@ -3540,7 +3526,6 @@ msgstr ""
"\n"
"# Marques dans le fichier :\n"
-#. Write the jumplist with -'
msgid ""
"\n"
"# Jumplist (newest first):\n"
@@ -3612,7 +3597,6 @@ msgstr "E298: Bloc n2 non rcupr ?"
msgid "E843: Error while updating swap file crypt"
msgstr "E843: Erreur lors de la mise jour du fichier d'change crypt"
-#. could not (re)open the swap file, what can we do????
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: Oups, le fichier d'change a disparu !"
@@ -3801,7 +3785,6 @@ msgstr ""
"Utilisation de la cl de chiffrement du fichier d'change pour le fichier "
"texte.\n"
-#. use msg() to start the scrolling properly
msgid "Swap files found:"
msgstr "Fichiers d'change trouvs :"
@@ -3971,8 +3954,6 @@ msgstr "Lors de l'ouverture du fichier \""
msgid " NEWER than swap file!\n"
msgstr " PLUS RCENT que le fichier d'change !\n"
-#. Some of these messages are long to allow translation to
-#. * other languages.
msgid ""
"\n"
"(1) Another program may be editing the same file. If this is the case,\n"
@@ -4063,7 +4044,6 @@ msgstr "E328: Le menu n'existe que dans un autre mode"
msgid "E329: No menu \"%s\""
msgstr "E329: Aucun menu \"%s\""
-#. Only a mnemonic or accelerator is not valid.
msgid "E792: Empty menu name"
msgstr "E792: Nom de menu vide"
@@ -4076,8 +4056,6 @@ msgstr "E331: Ajout d'lments de menu directement dans barre de menu interdit"
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: Un sparateur ne peut faire partie d'un chemin de menu"
-#. Now we have found the matching menu, and we list the mappings
-#. Highlight title
msgid ""
"\n"
"--- Menus ---"
@@ -4088,6 +4066,10 @@ msgstr ""
msgid "Tear off this menu"
msgstr "Dtacher ce menu"
+#, c-format
+msgid "E335: Menu not defined for %s mode"
+msgstr "E335: Le menu n'est pas dfini pour le mode %s"
+
msgid "E333: Menu path must lead to a menu item"
msgstr "E333: Le chemin du menu doit conduire un lment de menu"
@@ -4095,10 +4077,6 @@ msgstr "E333: Le chemin du menu doit conduire un lment de menu"
msgid "E334: Menu not found: %s"
msgstr "E334: Menu introuvable : %s"
-#, c-format
-msgid "E335: Menu not defined for %s mode"
-msgstr "E335: Le menu n'est pas dfini pour le mode %s"
-
msgid "E336: Menu path must lead to a sub-menu"
msgstr "E336: Le chemin du menu doit conduire un sous-menu"
@@ -4172,7 +4150,6 @@ msgstr "Enregistrer un fichier"
msgid "Open File dialog"
msgstr "Ouvrir un fichier"
-#. TODO: non-GUI file selector here
msgid "E338: Sorry, no file browser in console mode"
msgstr "E338: Dsol, pas de slecteur de fichiers en mode console"
@@ -4338,8 +4315,10 @@ msgstr "E662: Au dbut de la liste des modifications"
msgid "E663: At end of changelist"
msgstr "E663: la fin de la liste des modifications"
-msgid "Type :quit<Enter> to exit Vim"
-msgstr "tapez :q<Entre> pour quitter Vim"
+msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim"
+msgstr ""
+"Tapez :qa! puis <Entre> pour abandonner tous les changements et quitter "
+"Vim"
#, c-format
msgid "1 line %sed 1 time"
@@ -4372,7 +4351,6 @@ msgid "E748: No previously used register"
msgstr "E748: Aucun registre n'a t prcdemment utilis"
# DB - Question O/N.
-#. must display the prompt
msgid "cannot yank; delete anyway"
msgstr "impossible de raliser une copie ; effacer tout de mme"
@@ -4387,25 +4365,30 @@ msgstr "%ld lignes modifies"
msgid "freeing %ld lines"
msgstr "libration de %ld lignes"
-msgid "block of 1 line yanked"
-msgstr "bloc de 1 ligne copi"
+#, c-format
+msgid " into \"%c"
+msgstr " dans \"%c"
-msgid "1 line yanked"
-msgstr "1 ligne copie"
+#, c-format
+msgid "block of 1 line yanked%s"
+msgstr "bloc de 1 ligne copi%s"
+
+#, c-format
+msgid "1 line yanked%s"
+msgstr "1 ligne copie%s"
#, c-format
-msgid "block of %ld lines yanked"
-msgstr "bloc de %ld lignes copi"
+msgid "block of %ld lines yanked%s"
+msgstr "bloc de %ld lignes copi%s"
#, c-format
-msgid "%ld lines yanked"
-msgstr "%ld lignes copies"
+msgid "%ld lines yanked%s"
+msgstr "%ld lignes copies%s"
#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: Le registre %s est vide"
-#. Highlight title
msgid ""
"\n"
"--- Registers ---"
@@ -4469,9 +4452,6 @@ msgstr ""
msgid "(+%ld for BOM)"
msgstr "(+%ld pour le BOM)"
-msgid "%<%f%h%m%=Page %N"
-msgstr "%<%f%h%m%=Page %N"
-
msgid "Thanks for flying Vim"
msgstr "Merci d'avoir choisi Vim"
@@ -4582,6 +4562,9 @@ msgstr "E541: trop d'lments"
msgid "E542: unbalanced groups"
msgstr "E542: parenthses non quilibres"
+msgid "E946: Cannot make a terminal with running job modifiable"
+msgstr "E946: terminal avec tche en cours d'excution ne peut pas tre modifiable"
+
msgid "E590: A preview window already exists"
msgstr "E590: Il existe dj une fentre de prvisualisation"
@@ -4600,9 +4583,6 @@ msgstr "E594: Au moins %d colonnes sont ncessaires"
msgid "E355: Unknown option: %s"
msgstr "E355: Option inconnue : %s"
-#. There's another character after zeros or the string
-#. * is empty. In both cases, we are trying to set a
-#. * num option using a string.
#, c-format
msgid "E521: Number required: &%s = '%s'"
msgstr "E521: Nombre requis : &%s = '%s'"
@@ -4675,7 +4655,6 @@ msgstr "Impossible de modifier le mode de la console ?!\n"
msgid "mch_get_shellsize: not a console??\n"
msgstr "mch_get_shellsize : pas une console ?!\n"
-#. if Vim opened a window: Executing a shell may cause crashes
msgid "E360: Cannot execute shell with -f option"
msgstr "E360: Impossible d'excuter un shell avec l'option -f"
@@ -4902,7 +4881,6 @@ msgstr "E376: %%%c invalide dans le prfixe de la chane de format"
msgid "E377: Invalid %%%c in format string"
msgstr "E377: %%%c invalide dans la chane de format"
-#. nothing found
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: 'errorformat' ne contient aucun motif"
@@ -4941,9 +4919,6 @@ msgstr "E381: Au sommet de la pile quickfix"
msgid "No entries"
msgstr "Aucune entre"
-msgid "E382: Cannot write, 'buftype' option is set"
-msgstr "E382: criture impossible, l'option 'buftype' est active"
-
msgid "Error file"
msgstr "Fichier d'erreurs"
@@ -4968,6 +4943,12 @@ msgstr "E369: lment invalide dans %s%%[]"
msgid "E769: Missing ] after %s["
msgstr "E769: ']' manquant aprs %s["
+msgid "E944: Reverse range in character class"
+msgstr "E944: Classe de caractres inverse"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: Plage de classe de caractres trop large"
+
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: Pas de correspondance pour %s%%("
@@ -4994,6 +4975,9 @@ msgstr "E69: ']' manquant aprs %s%%["
msgid "E70: Empty %s%%[]"
msgstr "E70: %s%%[] vide"
+msgid "E65: Illegal back reference"
+msgstr "E65: post-rfrence invalide"
+
msgid "E339: Pattern too long"
msgstr "E339: Motif trop long"
@@ -5030,9 +5014,6 @@ msgstr "E63: utilisation invalide de \\_"
msgid "E64: %s%c follows nothing"
msgstr "E64: %s%c ne suit aucun atome"
-msgid "E65: Illegal back reference"
-msgstr "E65: post-rfrence invalide"
-
msgid "E68: Invalid character after \\z"
msgstr "E68: Caractre invalide aprs \\z"
@@ -5084,7 +5065,6 @@ msgstr "E867: (NFA) Oprateur inconnu '\\z%c'"
msgid "E867: (NFA) Unknown operator '\\%%%c'"
msgstr "E867: (NFA) Oprateur inconnu '\\%%%c'"
-#. should never happen
msgid "E868: Error building NFA with equivalence class!"
msgstr "E868: Erreur lors de la construction du NFA avec classe d'quivalence"
@@ -5095,11 +5075,9 @@ msgstr "E869: (NFA) Oprateur inconnu '\\@%c'"
msgid "E870: (NFA regexp) Error reading repetition limits"
msgstr "E870: (regexp NFA) Erreur la lecture des limites de rptition"
-#. Can't have a multi follow a multi.
msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
msgstr "E871: (regexp NFA) Un multi ne peut pas suivre un multi !"
-#. Too many `('
msgid "E872: (NFA regexp) Too many '('"
msgstr "E872: (regexp NFA) Trop de '('"
@@ -5209,7 +5187,6 @@ msgstr "E386: '?' ou '/' attendu aprs ';'"
msgid " (includes previously listed match)"
msgstr " (inclut des correspondances listes prcdemment)"
-#. cursor at status line
msgid "--- Included files "
msgstr "--- Fichiers inclus "
@@ -5286,8 +5263,6 @@ msgstr "Dsol, aucune suggestion"
msgid "Sorry, only %ld suggestions"
msgstr "Dsol, seulement %ld suggestions"
-#. for when 'cmdheight' > 1
-#. avoid more prompt
#, c-format
msgid "Change \"%.*s\" to:"
msgstr "Remplacer \"%.*s\" par :"
@@ -5574,10 +5549,6 @@ msgstr "%d noeuds compresss sur %d ; %d (%d%%) restants "
msgid "Reading back spell file..."
msgstr "Relecture du fichier orthographique"
-#.
-#. * Go through the trie of good words, soundfold each word and add it to
-#. * the soundfold trie.
-#.
msgid "Performing soundfolding..."
msgstr "Analyse phontique en cours..."
@@ -5634,18 +5605,39 @@ msgid "E763: Word characters differ between spell files"
msgstr ""
"E763: Les caractres de mots diffrent entre les fichiers orthographiques"
-#. This should have been checked when generating the .spl
-#. * file.
msgid "E783: duplicate char in MAP entry"
msgstr "E783: caractre dupliqu dans l'entre MAP"
msgid "No Syntax items defined for this buffer"
msgstr "Aucun lment de syntaxe dfini pour ce tampon"
+msgid "syntax conceal on"
+msgstr "\"syntax conceal\" active"
+
+msgid "syntax conceal off"
+msgstr "\"syntax conceal\" dsactive"
+
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Argument invalide : %s"
+msgid "syntax case ignore"
+msgstr "syntaxe ignore la casse"
+
+msgid "syntax case match"
+msgstr "syntaxe respecte la casse"
+
+msgid "syntax spell toplevel"
+msgstr "contrle orthographique dans le texte sans groupe syntaxique"
+
+msgid "syntax spell notoplevel"
+msgstr "pas de contrle orthographique dans le texte sans groupe syntaxique"
+
+msgid "syntax spell default"
+msgstr ""
+"contrle orthographique dans le texte sans groupe syntaxique, sauf si @Spell/"
+"@NoSpell"
+
msgid "syntax iskeyword "
msgstr "syntaxe iskeyword "
@@ -5885,7 +5877,6 @@ msgstr "E428: Impossible d'aller au-del du dernier marqueur correspondant"
msgid "File \"%s\" does not exist"
msgstr "Le fichier \"%s\" n'existe pas"
-#. Give an indication of the number of matching tags
#, c-format
msgid "tag %d of %d%s"
msgstr "marqueur %d sur %d%s"
@@ -5900,7 +5891,6 @@ msgstr " Utilisation d'un marqueur avec une casse diffrente !"
msgid "E429: File \"%s\" does not exist"
msgstr "E429: Le fichier \"%s\" n'existe pas"
-#. Highlight title
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -5931,7 +5921,6 @@ msgstr "Avant l'octet %ld"
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Le fichier de marqueurs %s n'est pas ordonn"
-#. never opened any tags file
msgid "E433: No tags file"
msgstr "E433: Aucun fichier de marqueurs"
@@ -5968,7 +5957,6 @@ msgstr "E436: Aucune entre \"%s\" dans termcap"
msgid "E437: terminal capability \"cm\" required"
msgstr "E437: capacit de terminal \"cm\" requise"
-#. Highlight title
msgid ""
"\n"
"--- Terminal keys ---"
@@ -5979,6 +5967,21 @@ msgstr ""
msgid "Cannot open $VIMRUNTIME/rgb.txt"
msgstr "Impossible d'ouvrir $VIMRUNTIME/rgb.txt"
+msgid "Terminal"
+msgstr "Terminal"
+
+msgid "Terminal-finished"
+msgstr "Terminal-fini"
+
+msgid "active"
+msgstr "actif"
+
+msgid "running"
+msgstr "en cours"
+
+msgid "finished"
+msgstr "fini"
+
msgid "new shell started\n"
msgstr "nouveau shell dmarr\n"
@@ -5989,13 +5992,10 @@ msgstr "Vim : Erreur lors de la lecture de l'entre, sortie...\n"
msgid "Used CUT_BUFFER0 instead of empty selection"
msgstr "CUT_BUFFER0 utilis plutt qu'une slection vide"
-#. This happens when the FileChangedRO autocommand changes the
-#. * file in a way it becomes shorter.
msgid "E881: Line count changed unexpectedly"
msgstr "E881: Le nombre de lignes a t chang inopinment"
# DB - Question O/N.
-#. must display the prompt
msgid "No undo possible; continue anyway"
msgstr "Annulation impossible ; continuer"
@@ -6249,6 +6249,10 @@ msgid "E126: Missing :endfunction"
msgstr "E126: Il manque :endfunction"
#, c-format
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: Texte trouv aprs :endfunction: %s"
+
+#, c-format
msgid "E707: Function name conflicts with variable: %s"
msgstr "E707: Le nom de fonction entre en conflit avec la variable : %s"
@@ -6640,7 +6644,6 @@ msgstr "&Comparer avec Vim"
msgid "Edit with &Vim"
msgstr "diter dans &Vim"
-#. Now concatenate
msgid "Edit with existing Vim - "
msgstr "diter dans le Vim existant - "
@@ -6664,10 +6667,6 @@ msgstr "Le chemin est trop long !"
msgid "--No lines in buffer--"
msgstr "--Le tampon est vide--"
-#.
-#. * The error messages that can be shared are included here.
-#. * Excluded are errors that are only used once and debugging messages.
-#.
msgid "E470: Command aborted"
msgstr "E470: Commande annule"
@@ -6854,12 +6853,6 @@ msgstr "E484: Impossible d'ouvrir le fichier \"%s\""
msgid "E485: Can't read file %s"
msgstr "E485: Impossible de lire le fichier %s"
-msgid "E37: No write since last change (add ! to override)"
-msgstr "E37: Modifications non enregistres (ajoutez ! pour passer outre)"
-
-msgid "E37: No write since last change"
-msgstr "E37: Modifications non enregistres"
-
msgid "E38: Null argument"
msgstr "E38: Argument null"
@@ -6997,8 +6990,8 @@ msgstr "E592: 'winwidth' ne peut pas tre plus petit que 'winminwidth'"
msgid "E80: Error while writing"
msgstr "E80: Erreur lors de l'criture"
-msgid "Zero count"
-msgstr "Le quantificateur est nul"
+msgid "E939: Positive count required"
+msgstr "E939: Quantificateur positif requis"
msgid "E81: Using <SID> not in a script context"
msgstr "E81: <SID> utilis en dehors d'un script"
@@ -7151,7 +7144,6 @@ msgstr "le constructeur de liste n'accepte pas les arguments nomms"
msgid "list index out of range"
msgstr "index de liste hors limites"
-#. No more suitable format specifications in python-2.3
#, c-format
msgid "internal error: failed to get vim list item %d"
msgstr "erreur interne : accs un lment %d de liste a chou"
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index abb3565077..d409560f6b 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: vim 7.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-10-25 09:31-0500\n"
+"POT-Creation-Date: 2017-07-11 15:45-0500\n"
"PO-Revision-Date: 2010-04-14 10:01-0500\n"
"Last-Translator: Kevin Patrick Scannell <kscanne@gmail.com>\n"
"Language-Team: Irish <gaeilge-gnulinux@lists.sourceforge.net>\n"
@@ -239,7 +239,8 @@ msgid "E917: Cannot use a callback with %s()"
msgstr "E917: N fidir aisghlaoch a sid le %s()"
msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel"
-msgstr "E912: n fidir ch_evalexpr()/ch_sendexpr() a sid le cainal raw n nl"
+msgstr ""
+"E912: n fidir ch_evalexpr()/ch_sendexpr() a sid le cainal raw n nl"
msgid "E906: not an open channel"
msgstr "E906: n cainal oscailte "
@@ -426,6 +427,9 @@ msgstr "%s scanadh"
msgid "Scanning tags."
msgstr "Clibeanna scanadh."
+msgid "match in file"
+msgstr "meaitseil sa chomhad"
+
msgid " Adding"
msgstr " Mad"
@@ -513,6 +517,12 @@ msgstr "E690: \"in\" ar iarraidh i ndiaidh :for"
msgid "E108: No such variable: \"%s\""
msgstr "E108: Nl a leithid d'athrg: \"%s\""
+#. For historic reasons this error is not given for a list or dict.
+#. * E.g., the b: dict could be locked/unlocked.
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: N fidir athrg %s a ghlasil n a dhghlasil"
+
msgid "E743: variable nested too deep for (un)lock"
msgstr "E743: athrg neadaithe rdhomhain chun a (d)ghlasil"
@@ -691,15 +701,6 @@ msgid "&Ok"
msgstr "&Ok"
#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld lne: "
-msgstr[1] "+-%s%3ld lne: "
-msgstr[2] "+-%s%3ld lne: "
-msgstr[3] "+-%s%3ld lne: "
-msgstr[4] "+-%s%3ld lne: "
-
-#, c-format
msgid "E700: Unknown function: %s"
msgstr "E700: Feidhm anaithnid: %s"
@@ -707,7 +708,9 @@ msgid "E922: expected a dict"
msgstr "E922: bhothas ag sil le foclir"
msgid "E923: Second argument of function() must be a list or a dict"
-msgstr "E923: Caithfidh an dara hargint de function() a bheith ina liosta n ina foclir"
+msgstr ""
+"E923: Caithfidh an dara hargint de function() a bheith ina liosta n ina "
+"foclir"
msgid ""
"&OK\n"
@@ -744,8 +747,8 @@ msgstr "E727: Tosach thar dheireadh"
msgid "<empty>"
msgstr "<folamh>"
-msgid "E240: No connection to Vim server"
-msgstr "E240: Nl aon nasc le freastala Vim"
+msgid "E240: No connection to the X server"
+msgstr "E240: Nl aon cheangal leis an bhfreastala X"
#, c-format
msgid "E241: Unable to send to %s"
@@ -754,6 +757,12 @@ msgstr "E241: N fidir aon rud a sheoladh chuig %s"
msgid "E277: Unable to read a server reply"
msgstr "E277: N fidir freagra n fhreastala a lamh"
+msgid "E941: already started a server"
+msgstr "E941: tosaodh freastala cheana"
+
+msgid "E942: +clientserver feature not available"
+msgstr "E942: nl an ghn +clientserver ar fil"
+
msgid "remove() argument"
msgstr "argint remove()"
@@ -993,8 +1002,9 @@ msgstr " ar lne amhin"
msgid " on %ld lines"
msgstr " ar %ld lne"
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: N cheadatear :global go hathchrsach"
+#. will increment global_busy to break out of the loop
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: N cheadatear :global athchrsach le raon"
# should have ":"
msgid "E148: Regular expression missing from global"
@@ -1179,6 +1189,14 @@ msgstr "Ag danamh cuardach ar \"%s\""
msgid "not found in '%s': \"%s\""
msgstr "gan aimsi in '%s': \"%s\""
+#, c-format
+msgid "W20: Required python version 2.x not supported, ignoring file: %s"
+msgstr "W20: Nl leagan 2.x de Python ar fil; ag danamh neamhaird de %s"
+
+#, c-format
+msgid "W21: Required python version 3.x not supported, ignoring file: %s"
+msgstr "W21: Nl leagan 3.x de Python ar fil; ag danamh neamhaird de %s"
+
msgid "Source Vim script"
msgstr "Foinsigh script Vim"
@@ -1278,6 +1296,9 @@ msgstr "Raon droim ar ais, babhtil"
msgid "E494: Use w or w>>"
msgstr "E494: Bain sid as w n w>>"
+msgid "E943: Command table needs to be updated, run 'make cmdidxs'"
+msgstr "E943: Caithfear tbla na n-orduithe a nuashonr; rith 'make cmdidxs'"
+
msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: T brn orm, nl an t-ord ar fil sa leagan seo"
@@ -1391,7 +1412,7 @@ msgid "No swap file"
msgstr "Nl aon chomhad babhtla ann"
msgid "Append File"
-msgstr "Cuir Comhad i nDeireadh"
+msgstr "Ceangail Comhad ag an Deireadh"
msgid "E747: Cannot change directory, buffer is modified (add ! to override)"
msgstr ""
@@ -1412,10 +1433,10 @@ msgid "Window position: X %d, Y %d"
msgstr "Ionad na fuinneoige: X %d, Y %d"
msgid "E188: Obtaining window position not implemented for this platform"
-msgstr "E188: N fidir ionad na fuinneoige a fhil amach ar an chras seo"
+msgstr "E188: N fidir ionad na fuinneoige a fhil amach ar an gcras seo"
msgid "E466: :winpos requires two number arguments"
-msgstr "E466: n folir dh argint uimhrila le :winpos"
+msgstr "E466: dh argint uimhrila de dhth le :winpos"
msgid "E930: Cannot use :redir inside execute()"
msgstr "E930: N fidir :redir a sid laistigh de execute()"
@@ -2045,15 +2066,6 @@ msgstr "E350: N fidir filleadh a chruth leis an 'foldmethod' reatha"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: N fidir filleadh a scriosadh leis an 'foldmethod' reatha"
-#, c-format
-msgid "+--%3ld line folded "
-msgid_plural "+--%3ld lines folded "
-msgstr[0] "+--%3ld lne fillte "
-msgstr[1] "+--%3ld lne fillte "
-msgstr[2] "+--%3ld lne fillte "
-msgstr[3] "+--%3ld lne fillte "
-msgstr[4] "+--%3ld lne fillte "
-
msgid "E222: Add to read buffer"
msgstr "E222: Cuir leis an maoln lite"
@@ -2652,8 +2664,8 @@ msgid ""
"E895: Sorry, this command is disabled, the MzScheme's racket/base module "
"could not be loaded."
msgstr ""
-"E895: r leithscal, t an t-ord seo dchumasaithe; norbh fhidir "
-"modl racket/base MzScheme a lucht."
+"E895: r leithscal, t an t-ord seo dchumasaithe; norbh fhidir modl "
+"racket/base MzScheme a lucht."
msgid "invalid expression"
msgstr "slonn neamhbhail"
@@ -2846,6 +2858,10 @@ msgid "E251: VIM instance registry property is badly formed. Deleted!"
msgstr "E251: Air mchumtha sa chlrlann isc VIM. Scriosta!"
#, c-format
+msgid "E938: Duplicate key in JSON: \"%s\""
+msgstr "E938: Eochair dhblach in JSON: \"%s\""
+
+#, c-format
msgid "E696: Missing comma in List: %s"
msgstr "E696: Camg ar iarraidh i Liosta: %s"
@@ -2898,7 +2914,8 @@ msgid "Vim: Error: Failure to start gvim from NetBeans\n"
msgstr "Vim: Earrid: Theip ar thos gvim NetBeans\n"
msgid "Vim: Error: This version of Vim does not run in a Cygwin terminal\n"
-msgstr "Vim: Earrid: N fidir an leagan seo de Vim a rith i dteirminal Cygwin\n"
+msgstr ""
+"Vim: Earrid: N fidir an leagan seo de Vim a rith i dteirminal Cygwin\n"
msgid "Vim: Warning: Output is not to a terminal\n"
msgstr "Vim: Rabhadh: Nl an t-aschur ag dul chuig teirminal\n"
@@ -3067,7 +3084,12 @@ msgid "-T <terminal>\tSet terminal type to <terminal>"
msgstr "-T <teirminal>\tSocraigh cinel teirminal"
msgid "--not-a-term\t\tSkip warning for input/output not being a terminal"
-msgstr "--not-a-term\t\tN bac le rabhadh faoi ionchur/aschur gan a bheith n teirminal"
+msgstr ""
+"--not-a-term\t\tN bac le rabhadh faoi ionchur/aschur gan a bheith n "
+"teirminal"
+
+msgid "--ttyfail\t\tExit if input or output is not a terminal"
+msgstr "--ttyfail\t\tScoir mura bhfuil ionchur agus aschur ina dteirminil"
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\t\tsid <vimrc> in ionad aon .vimrc"
@@ -3435,7 +3457,7 @@ msgid ""
"Maybe no changes were made or Vim did not update the swap file."
msgstr ""
"\n"
-"B'fhidir nach raibh aon athr dhanamh, n t an comhad\n"
+"B'fhidir nach raibh aon athr dhanamh, n t an comhad "
"babhtla as dta."
msgid " cannot be used with this version of Vim.\n"
@@ -4122,8 +4144,8 @@ msgstr "E662: Ag tosach liosta na n-athruithe"
msgid "E663: At end of changelist"
msgstr "E663: Ag deireadh liosta na n-athruithe"
-msgid "Type :quit<Enter> to exit Vim"
-msgstr "Clscrobh :quit<Enter> chun Vim a scor"
+msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim"
+msgstr "Clscrobh :qa! agus brigh <Enter> le fgil Vim gan athruithe a shbhil"
# ouch - English -ed ?
#, c-format
@@ -4234,7 +4256,8 @@ msgid ""
"Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of "
"%lld Bytes"
msgstr ""
-"Roghnaodh %s%ld as %ld Lne; %lld as %lld Focal; %lld as %lld Carachtar; %lld as %lld Beart"
+"Roghnaodh %s%ld as %ld Lne; %lld as %lld Focal; %lld as %lld Carachtar; "
+"%lld as %lld Beart"
#, c-format
msgid "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld"
@@ -4245,7 +4268,8 @@ msgid ""
"Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte "
"%lld of %lld"
msgstr ""
-"Col %s as %s; Lne %ld as %ld; Focal %lld as %lld; Carachtar %lld as %lld; Beart %lld as %lld"
+"Col %s as %s; Lne %ld as %ld; Focal %lld as %lld; Carachtar %lld as %lld; "
+"Beart %lld as %lld"
#, c-format
msgid "(+%ld for BOM)"
@@ -4507,8 +4531,7 @@ msgstr ""
#, c-format
msgid "E244: Illegal quality name \"%s\" in font name \"%s\""
-msgstr ""
-"E244: Ainm neamhcheadaithe ar chilocht \"%s\" in ainm cl \"%s\""
+msgstr "E244: Ainm neamhcheadaithe ar chilocht \"%s\" in ainm cl \"%s\""
#, c-format
msgid "E245: Illegal char '%c' in font name \"%s\""
@@ -4551,7 +4574,8 @@ msgstr "Norbh fhidir comhthacs slndla %s a shocr le haghaidh %s"
#, c-format
msgid "Could not get security context %s for %s. Removing it!"
-msgstr "Norbh fhidir comhthacs slndla %s a fhil le haghaidh %s. bhaint!"
+msgstr ""
+"Norbh fhidir comhthacs slndla %s a fhil le haghaidh %s. bhaint!"
msgid ""
"\n"
@@ -4752,6 +4776,12 @@ msgstr "E369: mr neamhbhail i %s%%[]"
msgid "E769: Missing ] after %s["
msgstr "E769: ] ar iarraidh i ndiaidh %s["
+msgid "E944: Reverse range in character class"
+msgstr "E944: Raon aisiompaithe in aicme carachtar"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: Raon rmhr in aicme carachtar"
+
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: %s%%( corr"
@@ -4778,6 +4808,9 @@ msgstr "E69: ] ar iarraidh i ndiaidh %s%%["
msgid "E70: Empty %s%%[]"
msgstr "E70: %s%%[] folamh"
+msgid "E65: Illegal back reference"
+msgstr "E65: Cltagairt neamhbhail"
+
msgid "E339: Pattern too long"
msgstr "E339: Slonn rfhada"
@@ -4814,9 +4847,6 @@ msgstr "E63: sid neamhbhail de \\_"
msgid "E64: %s%c follows nothing"
msgstr "E64: nl aon rud roimh %s%c"
-msgid "E65: Illegal back reference"
-msgstr "E65: Cltagairt neamhbhail"
-
msgid "E68: Invalid character after \\z"
msgstr "E68: Carachtar neamhbhail i ndiaidh \\z"
@@ -5424,12 +5454,33 @@ msgstr "E783: carachtar dblach in iontril MAP"
msgid "No Syntax items defined for this buffer"
msgstr "Nl aon mhr chomhrire sainmhnithe le haghaidh an mhaolin seo"
+msgid "syntax conceal on"
+msgstr "syntax conceal on"
+
+msgid "syntax conceal off"
+msgstr "syntax conceal off"
+
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Argint neamhcheadaithe: %s"
+msgid "syntax case ignore"
+msgstr "syntax case ignore"
+
+msgid "syntax case match"
+msgstr "syntax case match"
+
+msgid "syntax spell toplevel"
+msgstr "syntax spell toplevel"
+
+msgid "syntax spell notoplevel"
+msgstr "syntax spell notoplevel"
+
+msgid "syntax spell default"
+msgstr "syntax spell default"
+
msgid "syntax iskeyword "
-msgstr "comhrir iskeyword "
+msgstr "syntax iskeyword "
#, c-format
msgid "E391: No such syntax cluster: %s"
@@ -6006,6 +6057,10 @@ msgid "E126: Missing :endfunction"
msgstr "E126: :endfunction ar iarraidh"
#, c-format
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: Aimsodh tacs tar is :endfunction: %s"
+
+#, c-format
msgid "E707: Function name conflicts with variable: %s"
msgstr "E707: Tagann ainm na feidhme salach ar athrg: %s"
@@ -6475,6 +6530,10 @@ msgstr "E236: N cl aonleithid \"%s\""
msgid "E473: Internal error"
msgstr "E473: Earrid inmhenach"
+#, c-format
+msgid "E685: Internal error: %s"
+msgstr "E685: Earrid inmhenach: %s"
+
msgid "Interrupted"
msgstr "Idirbhriste"
@@ -6635,7 +6694,7 @@ msgid "E486: Pattern not found: %s"
msgstr "E486: Patrn gan aimsi: %s"
msgid "E487: Argument must be positive"
-msgstr "E487: N folir argint dheimhneach"
+msgstr "E487: Argint dheimhneach de dhth"
msgid "E459: Cannot go back to previous directory"
msgstr "E459: N fidir a fhilleadh ar an chomhadlann roimhe seo"
@@ -6745,8 +6804,8 @@ msgstr "E592: n cheadatear 'winwidth' a bheith nos l n 'winminwidth'"
msgid "E80: Error while writing"
msgstr "E80: Earrid agus scrobh"
-msgid "Zero count"
-msgstr "Nialas"
+msgid "E939: Positive count required"
+msgstr "E939: Uimhir dheimhneach de dhth"
msgid "E81: Using <SID> not in a script context"
msgstr "E81: <SID> sid nach i gcomhthacs scripte"
@@ -6760,10 +6819,6 @@ msgstr "E463: Rigin cosanta, n fidir a athr"
msgid "E744: NetBeans does not allow changes in read-only files"
msgstr "E744: N cheadaonn NetBeans aon athr i gcomhaid inlite amhin"
-#, c-format
-msgid "E685: Internal error: %s"
-msgstr "E685: Earrid inmhenach: %s"
-
msgid "E363: pattern uses more memory than 'maxmempattern'"
msgstr "E363: sideann an patrn nos m cuimhne n 'maxmempattern'"
@@ -7062,6 +7117,28 @@ msgstr ""
"Norbh fhidir an chonair a shocr: n liosta sys.path\n"
"Ba chir duit vim.VIM_SPECIAL_PATH a cheangal le deireadh sys.path"
+#~ msgid "+-%s%3ld line: "
+#~ msgid_plural "+-%s%3ld lines: "
+#~ msgstr[0] "+-%s%3ld lne: "
+#~ msgstr[1] "+-%s%3ld lne: "
+#~ msgstr[2] "+-%s%3ld lne: "
+#~ msgstr[3] "+-%s%3ld lne: "
+#~ msgstr[4] "+-%s%3ld lne: "
+
+#~ msgid "+--%3ld line folded "
+#~ msgid_plural "+--%3ld lines folded "
+#~ msgstr[0] "+--%3ld lne fillte "
+#~ msgstr[1] "+--%3ld lne fillte "
+#~ msgstr[2] "+--%3ld lne fillte "
+#~ msgstr[3] "+--%3ld lne fillte "
+#~ msgstr[4] "+--%3ld lne fillte "
+
+#~ msgid "Type :quit<Enter> to exit Vim"
+#~ msgstr "Clscrobh :quit<Enter> chun Vim a scor"
+
+#~ msgid "Zero count"
+#~ msgstr "Nialas"
+
#~ msgid "E693: Can only compare Funcref with Funcref"
#~ msgstr "E693: Is fidir Funcref a chur i gcomparid le Funcref eile amhin"
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index b8b119ade6..c95faf1dce 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -11,7 +11,7 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: vim 7.4\n"
+"Project-Id-Version: vim 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-02-11 12:10+0100\n"
"PO-Revision-Date: 2016-02-11 14:42+0200\n"
@@ -1402,6 +1402,33 @@ msgstr "com: %s"
msgid "frame is zero"
msgstr "al livello zero"
+msgid "E901: gethostbyname() in channel_open()"
+msgstr "E901: gethostbyname() in channel_open()"
+
+msgid "E898: socket() in channel_open()"
+msgstr "E898: socket() in channel_open()"
+
+msgid "E903: received command with non-string argument"
+msgstr "E903: il comando ricevuto non aveva come argomento una stringa"
+
+msgid "E904: last argument for expr/call must be a number"
+msgstr "E904: l'ultimo argomento per espressione/chiamata dev'essere numerico"
+
+msgid "E904: third argument for call must be a list"
+msgstr "E904: il terzo argomento della chiamata dev'essere una Lista"
+
+#, c-format
+msgid "E905: received unknown command: %s"
+msgstr "E905: recevuto comando non conosciuto: %s"
+
+#, c-format
+msgid "E630: %s(): write while not connected"
+msgstr "E630: %s(): scrittura in mancanza di connessione"
+
+#, c-format
+msgid "E631: %s(): write failed"
+msgstr "E631: %s(): scrittura non riuscita"
+
#, c-format
msgid "frame at highest level: %d"
msgstr "al livello pi alto: %d"
@@ -4813,9 +4840,16 @@ msgstr ""
"Non posso impostare il contesto di sicurezza per "
#, c-format
+msgid "E151: No match: %s"
+msgstr "E151: Nessuna corrispondenza: %s"
+
+#, c-format
msgid "Could not set security context %s for %s"
msgstr "Non posso impostare il contesto di sicurezza %s per %s"
+msgid "E934: Cannot jump to a buffer that does not have a name"
+msgstr "E934: Impossibile passare a un buffer che non ha un nome"
+
#, c-format
msgid "Could not get security context %s for %s. Removing it!"
msgstr "Non posso ottenere il contesto di sicurezza %s per %s. Lo rimuovo!"
@@ -5351,10 +5385,15 @@ msgstr "E772: Il file ortografico per versioni di Vim pi recenti"
msgid "E770: Unsupported section in spell file"
msgstr "E770: Sezione non supportata nel file ortografico"
-#: ../spell.c:3762
+msgid "E944: Reverse range in character class"
+msgstr "E944: Intervallo invertito nella classe di caratteri"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: Intervallo troppo ampio nella classe di caratteri"
+
#, c-format
-msgid "Warning: region %s not supported"
-msgstr "Avviso: regione %s non supportata"
+msgid "E779: Old .sug file, needs to be updated: %s"
+msgstr "E779: File .sug obsoleto, necessario aggiornarlo: %s"
#: ../spell.c:4550
#, c-format
@@ -5714,11 +5753,6 @@ msgstr "E753: Non trovato: %s"
msgid "E778: This does not look like a .sug file: %s"
msgstr "E778: Questo non sembra un file .sug: %s"
-#: ../spell.c:9282
-#, c-format
-msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr "E779: File .sug obsoleto, necessario aggiornarlo: %s"
-
#: ../spell.c:9286
#, c-format
msgid "E780: .sug file is for newer version of Vim: %s"
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index 4b32096f1a..10d7342430 100644
--- a/src/nvim/po/ja.euc-jp.po
+++ b/src/nvim/po/ja.euc-jp.po
@@ -13,10 +13,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Vim 7.4\n"
+"Project-Id-Version: Vim 8.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-09-10 21:10+0900\n"
-"PO-Revision-Date: 2016-09-10 21:20+0900\n"
+"POT-Creation-Date: 2017-07-03 23:05+0900\n"
+"PO-Revision-Date: 2017-07-12 20:45+0900\n"
"Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n"
"Language-Team: vim-jp (https://github.com/vim-jp/lang-ja)\n"
"Language: Japanese\n"
@@ -245,7 +245,8 @@ msgid "E917: Cannot use a callback with %s()"
msgstr "E917: %s() ˥ХåϻȤޤ"
msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel"
-msgstr "E912: nl ͥ ch_evalexpr()/ch_sendexpr ϻȤޤ"
+msgstr ""
+"E912: raw nl ⡼ɤΥͥ ch_evalexpr()/ch_sendexpr() ϻȤޤ"
msgid "E906: not an open channel"
msgstr "E906: ƤʤͥǤ"
@@ -431,6 +432,9 @@ msgstr ": %s"
msgid "Scanning tags."
msgstr "򥹥."
+msgid "match in file"
+msgstr "եΥޥå"
+
msgid " Adding"
msgstr " ɲ"
@@ -519,6 +523,12 @@ msgstr "E690: :for θ \"in\" ޤ"
msgid "E108: No such variable: \"%s\""
msgstr "E108: ѿϤޤ: \"%s\""
+#. For historic reasons this error is not given for a list or dict.
+#. * E.g., the b: dict could be locked/unlocked.
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: ѿ %s ϥåޤϥåǤޤ"
+
msgid "E743: variable nested too deep for (un)lock"
msgstr "E743: ()åˤѿҤ᤮ޤ"
@@ -697,11 +707,6 @@ msgid "&Ok"
msgstr "&Ok"
#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld : "
-
-#, c-format
msgid "E700: Unknown function: %s"
msgstr "E700: ̤ΤδؿǤ: %s"
@@ -746,8 +751,8 @@ msgstr "E727: ϰ֤λ֤ۤޤ"
msgid "<empty>"
msgstr "<>"
-msgid "E240: No connection to Vim server"
-msgstr "E240: Vim Сؤ³ޤ"
+msgid "E240: No connection to the X server"
+msgstr "E240: X Сؤ³ޤ"
#, c-format
msgid "E241: Unable to send to %s"
@@ -756,6 +761,12 @@ msgstr "E241: %s 뤳ȤǤޤ"
msgid "E277: Unable to read a server reply"
msgstr "E277: Сαޤ"
+msgid "E941: already started a server"
+msgstr "E941: СϤǤ˳ϤƤޤ"
+
+msgid "E942: +clientserver feature not available"
+msgstr "E942: +clientserver ǽ̵ˤʤäƤޤ"
+
msgid "remove() argument"
msgstr "remove() ΰ"
@@ -993,8 +1004,9 @@ msgstr " ( 1 )"
msgid " on %ld lines"
msgstr " ( %ld )"
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: :global ƵŪˤϻȤޤ"
+#. will increment global_busy to break out of the loop
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: :global ϰդǺƵŪˤϻȤޤ"
msgid "E148: Regular expression missing from global"
msgstr "E148: globalޥɤɽꤵƤޤ"
@@ -1180,6 +1192,14 @@ msgstr "\"%s\" 򸡺"
msgid "not found in '%s': \"%s\""
msgstr "'%s' ˤϤޤ: \"%s\""
+#, c-format
+msgid "W20: Required python version 2.x not supported, ignoring file: %s"
+msgstr "W20: ׵ᤵ줿python 2.xбƤޤ󡢥ե̵뤷ޤ: %s"
+
+#, c-format
+msgid "W21: Required python version 3.x not supported, ignoring file: %s"
+msgstr "W21: ׵ᤵ줿python 3.xбƤޤ󡢥ե̵뤷ޤ: %s"
+
msgid "Source Vim script"
msgstr "VimץȤμ"
@@ -1278,6 +1298,11 @@ msgstr "դޤϰϤꤵޤ, ؤޤ?"
msgid "E494: Use w or w>>"
msgstr "E494: w ⤷ w>> ѤƤ"
+msgid "E943: Command table needs to be updated, run 'make cmdidxs'"
+msgstr ""
+"E943: ޥɥơ֥򹹿ɬפޤ'make cmdidxs' ¹ԤƤ"
+""
+
msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: ΥСǤϤΥޥɤѤǤޤ, ʤ"
@@ -2030,11 +2055,6 @@ msgstr "E350: ߤ 'foldmethod' Ǥ޾ߤǤޤ"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: ߤ 'foldmethod' Ǥ޾ߤǤޤ"
-#, c-format
-msgid "+--%3ld line folded "
-msgid_plural "+--%3ld lines folded "
-msgstr[0] "+--%3ld Ԥ޾ޤޤ "
-
msgid "E222: Add to read buffer"
msgstr "E222: ɹХåեɲ"
@@ -2819,6 +2839,10 @@ msgid "E251: VIM instance registry property is badly formed. Deleted!"
msgstr "E251: VIM ΤϿץѥƥǤ. õޤ!"
#, c-format
+msgid "E938: Duplicate key in JSON: \"%s\""
+msgstr "E938: JSON˽ʣޤ: \"%s\""
+
+#, c-format
msgid "E696: Missing comma in List: %s"
msgstr "E696: ꥹȷ˥ޤޤ: %s"
@@ -2858,7 +2882,7 @@ msgid "This Vim was not compiled with the diff feature."
msgstr "Vimˤdiffǽޤ(ѥ)."
msgid "Attempt to open script file again: \""
-msgstr "ץȥեƤӳƤߤޤ: \""
+msgstr "ץȥեƤӳȤޤ: \""
msgid "Cannot open for reading: \""
msgstr "ɹѤȤƳޤ"
@@ -3039,6 +3063,9 @@ msgstr "-T <terminal>\tü <terminal> ꤹ"
msgid "--not-a-term\t\tSkip warning for input/output not being a terminal"
msgstr "--not-a-term\t\tϤüǤʤȤηٹ򥹥åפ"
+msgid "--ttyfail\t\tExit if input or output is not a terminal"
+msgstr "--ttyfail\t\tϤüǤʤнλ"
+
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\t\t.vimrc <vimrc> Ȥ"
@@ -3906,7 +3933,7 @@ msgid "E766: Insufficient arguments for printf()"
msgstr "E766: printf() ΰԽʬǤ"
msgid "E807: Expected Float argument for printf()"
-msgstr "E807: printf() ΰˤưԤƤޤ"
+msgstr "E807: printf() ΰˤưԤƤޤ"
msgid "E767: Too many arguments to printf()"
msgstr "E767: printf() ΰ¿᤮ޤ"
@@ -4063,8 +4090,10 @@ msgstr "E662: ѹꥹȤƬ"
msgid "E663: At end of changelist"
msgstr "E663: ѹꥹȤ"
-msgid "Type :quit<Enter> to exit Vim"
-msgstr "Vimλˤ :quit<Enter> ϤƤ"
+msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim"
+msgstr ""
+"٤Ƥѹ˴Vimλˤ :qa! Ϥ <Enter> 򲡤Ƥ"
+""
#, c-format
msgid "1 line %sed 1 time"
@@ -4413,9 +4442,6 @@ msgstr "ϥ顼"
msgid "Message"
msgstr "å"
-msgid "'columns' is not 80, cannot execute external commands"
-msgstr "'columns' 80 ǤϤʤᡢޥɤ¹ԤǤޤ"
-
msgid "E237: Printer selection failed"
msgstr "E237: ץ󥿤˼Ԥޤ"
@@ -4685,6 +4711,12 @@ msgstr "E369: ̵ʹܤǤ: %s%%[]"
msgid "E769: Missing ] after %s["
msgstr "E769: %s[ θ ] ޤ"
+msgid "E944: Reverse range in character class"
+msgstr "E944: ʸ饹ϰϤդǤ"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: ʸ饹ϰϤ礭ޤ"
+
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: %s%%( äƤޤ"
@@ -4714,6 +4746,10 @@ msgstr "E69: %s%%[ θ ] ޤ"
msgid "E70: Empty %s%%[]"
msgstr "E70: %s%%[] Ǥ"
+#
+msgid "E65: Illegal back reference"
+msgstr "E65: ʸȤǤ"
+
msgid "E339: Pattern too long"
msgstr "E339: ѥĹ᤮ޤ"
@@ -4752,10 +4788,6 @@ msgid "E64: %s%c follows nothing"
msgstr "E64:%s%c θˤʤˤ⤢ޤ"
#
-msgid "E65: Illegal back reference"
-msgstr "E65: ʸȤǤ"
-
-#
msgid "E68: Invalid character after \\z"
msgstr "E68: \\z θʸޤ"
@@ -5363,12 +5395,33 @@ msgstr "E783: MAP ȥ˽ʣʸ¸ߤޤ"
msgid "No Syntax items defined for this buffer"
msgstr "ΥХåե줿ʸǤϤޤ"
+msgid "syntax conceal on"
+msgstr "ʸ conceal ϸ on Ǥ"
+
+msgid "syntax conceal off"
+msgstr "ʸ conceal ϸ off Ǥ"
+
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: ʰǤ: %s"
+msgid "syntax case ignore"
+msgstr "ʸʸʸϸ ignore Ǥ"
+
+msgid "syntax case match"
+msgstr "ʸʸʸϸ match Ǥ"
+
+msgid "syntax spell toplevel"
+msgstr "ʸ spell ϸ toplevel Ǥ"
+
+msgid "syntax spell notoplevel"
+msgstr "ʸ spell ϸ notoplevel Ǥ"
+
+msgid "syntax spell default"
+msgstr "ʸ spell ϸ default Ǥ"
+
msgid "syntax iskeyword "
-msgstr "󥿥å iskeyword "
+msgstr "ʸ iskeyword "
#, c-format
msgid "E391: No such syntax cluster: %s"
@@ -5579,7 +5632,7 @@ msgid "E556: at top of tag stack"
msgstr "E556: åƬǤ"
msgid "E425: Cannot go before first matching tag"
-msgstr "E425: ǽγĶ뤳ȤϤǤޤ"
+msgstr "E425: ǽγۤ뤳ȤϤǤޤ"
#, c-format
msgid "E426: tag not found: %s"
@@ -5595,7 +5648,7 @@ msgid "E427: There is only one matching tag"
msgstr "E427: 1Ĥޤ"
msgid "E428: Cannot go beyond last matching tag"
-msgstr "E428: Ǹ˳륿ĶƿʤळȤϤǤޤ"
+msgstr "E428: ǸγۤƿʤळȤϤǤޤ"
#, c-format
msgid "File \"%s\" does not exist"
@@ -5943,6 +5996,10 @@ msgid "E126: Missing :endfunction"
msgstr "E126: :endfunction ޤ"
#, c-format
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: :endfunction θʸޤ: %s"
+
+#, c-format
msgid "E707: Function name conflicts with variable: %s"
msgstr "E707: ؿ̾ѿ̾Ⱦͤޤ: %s"
@@ -5965,14 +6022,6 @@ msgstr "E133: ؿ :return ޤ"
msgid "E107: Missing parentheses: %s"
msgstr "E107: å '(' ޤ: %s"
-#. Only MS VC 4.1 and earlier can do Win32s
-msgid ""
-"\n"
-"MS-Windows 16/32-bit GUI version"
-msgstr ""
-"\n"
-"MS-Windows 16/32 ӥå GUI "
-
msgid ""
"\n"
"MS-Windows 64-bit GUI version"
@@ -6256,12 +6305,6 @@ msgstr "ܺ٤ʾ :help register<Enter> "
msgid "menu Help->Sponsor/Register for information "
msgstr "ܺ٤ϥ˥塼 إ->ݥ󥵡/Ͽ 򻲾ȤƲ"
-msgid "WARNING: Windows 95/98/ME detected"
-msgstr "ٹ: Windows 95/98/ME 򸡽Фޤ"
-
-msgid "type :help windows95<Enter> for info on this"
-msgstr "ܺ٤ʾ :help windows95<Enter>"
-
msgid "Already only one window"
msgstr "˥ɥ1Ĥޤ"
@@ -6415,6 +6458,10 @@ msgstr "E236: ե \"%s\" ϸǤϤޤ"
msgid "E473: Internal error"
msgstr "E473: 顼Ǥ"
+#, c-format
+msgid "E685: Internal error: %s"
+msgstr "E685: 顼Ǥ: %s"
+
msgid "Interrupted"
msgstr "ޤޤ"
@@ -6680,8 +6727,8 @@ msgstr "E592: 'winwidth' 'winminwidth' 꾮Ǥޤ"
msgid "E80: Error while writing"
msgstr "E80: Υ顼"
-msgid "Zero count"
-msgstr ""
+msgid "E939: Positive count required"
+msgstr "E939: ΥȤɬפǤ"
msgid "E81: Using <SID> not in a script context"
msgstr "E81: ץȰʳ<SID>Ȥޤ"
@@ -6695,10 +6742,6 @@ msgstr "E463: ΰ褬ݸƤΤ, ѹǤޤ"
msgid "E744: NetBeans does not allow changes in read-only files"
msgstr "E744: NetBeans ɹѥեѹ뤳Ȥޤ"
-#, c-format
-msgid "E685: Internal error: %s"
-msgstr "E685: 顼Ǥ: %s"
-
msgid "E363: pattern uses more memory than 'maxmempattern'"
msgstr "E363: ѥ 'maxmempattern' ʾΥѤޤ"
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index 5cb789c93e..39b4a89517 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -13,10 +13,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Vim 7.4\n"
+"Project-Id-Version: Vim 8.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-09-10 21:10+0900\n"
-"PO-Revision-Date: 2016-09-10 21:20+0900\n"
+"POT-Creation-Date: 2017-07-03 23:05+0900\n"
+"PO-Revision-Date: 2017-07-12 20:45+0900\n"
"Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n"
"Language-Team: vim-jp (https://github.com/vim-jp/lang-ja)\n"
"Language: Japanese\n"
@@ -245,7 +245,8 @@ msgid "E917: Cannot use a callback with %s()"
msgstr "E917: %s() にコールバックは使えません"
msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel"
-msgstr "E912: 生や nl チャンネルに ch_evalexpr()/ch_sendexpr は使えません"
+msgstr ""
+"E912: raw や nl モードのチャンネルに ch_evalexpr()/ch_sendexpr() は使えません"
msgid "E906: not an open channel"
msgstr "E906: 開いていないチャンネルです"
@@ -431,6 +432,9 @@ msgstr "スキャン中: %s"
msgid "Scanning tags."
msgstr "タグをスキャン中."
+msgid "match in file"
+msgstr "ファイル内のマッチ"
+
msgid " Adding"
msgstr " 追加中"
@@ -519,6 +523,12 @@ msgstr "E690: :for の後に \"in\" がありません"
msgid "E108: No such variable: \"%s\""
msgstr "E108: その変数はありません: \"%s\""
+#. For historic reasons this error is not given for a list or dict.
+#. * E.g., the b: dict could be locked/unlocked.
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: 変数 %s はロックまたはアンロックできません"
+
msgid "E743: variable nested too deep for (un)lock"
msgstr "E743: (アン)ロックするには変数の入れ子が深過ぎます"
@@ -697,11 +707,6 @@ msgid "&Ok"
msgstr "&Ok"
#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld 行: "
-
-#, c-format
msgid "E700: Unknown function: %s"
msgstr "E700: 未知の関数です: %s"
@@ -746,8 +751,8 @@ msgstr "E727: 開始位置が終了位置を越えました"
msgid "<empty>"
msgstr "<空>"
-msgid "E240: No connection to Vim server"
-msgstr "E240: Vim サーバーへの接続がありません"
+msgid "E240: No connection to the X server"
+msgstr "E240: X サーバーへの接続がありません"
#, c-format
msgid "E241: Unable to send to %s"
@@ -756,6 +761,12 @@ msgstr "E241: %s へ送ることができません"
msgid "E277: Unable to read a server reply"
msgstr "E277: サーバーの応答がありません"
+msgid "E941: already started a server"
+msgstr "E941: サーバーはすでに開始しています"
+
+msgid "E942: +clientserver feature not available"
+msgstr "E942: +clientserver 機能が無効になっています"
+
msgid "remove() argument"
msgstr "remove() の引数"
@@ -993,8 +1004,9 @@ msgstr " (計 1 行内)"
msgid " on %ld lines"
msgstr " (計 %ld 行内)"
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: :global を再帰的には使えません"
+#. will increment global_busy to break out of the loop
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: :global を範囲付きで再帰的には使えません"
msgid "E148: Regular expression missing from global"
msgstr "E148: globalコマンドに正規表現が指定されていません"
@@ -1180,6 +1192,14 @@ msgstr "\"%s\" を検索中"
msgid "not found in '%s': \"%s\""
msgstr "'%s' の中にはありません: \"%s\""
+#, c-format
+msgid "W20: Required python version 2.x not supported, ignoring file: %s"
+msgstr "W20: 要求されたpython 2.xは対応していません、ファイルを無視します: %s"
+
+#, c-format
+msgid "W21: Required python version 3.x not supported, ignoring file: %s"
+msgstr "W21: 要求されたpython 3.xは対応していません、ファイルを無視します: %s"
+
msgid "Source Vim script"
msgstr "Vimスクリプトの取込み"
@@ -1278,6 +1298,11 @@ msgstr "逆さまの範囲が指定されました, 入替えますか?"
msgid "E494: Use w or w>>"
msgstr "E494: w もしくは w>> を使用してください"
+msgid "E943: Command table needs to be updated, run 'make cmdidxs'"
+msgstr ""
+"E943: コマンドテーブルを更新する必要があります、'make cmdidxs' を実行してくだ"
+"さい"
+
msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: このバージョンではこのコマンドは利用できません, ごめんなさい"
@@ -2030,11 +2055,6 @@ msgstr "E350: 現在の 'foldmethod' では折畳みを作成できません"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: 現在の 'foldmethod' では折畳みを削除できません"
-#, c-format
-msgid "+--%3ld line folded "
-msgid_plural "+--%3ld lines folded "
-msgstr[0] "+--%3ld 行が折畳まれました "
-
msgid "E222: Add to read buffer"
msgstr "E222: 読込バッファへ追加"
@@ -2819,6 +2839,10 @@ msgid "E251: VIM instance registry property is badly formed. Deleted!"
msgstr "E251: VIM 実体の登録プロパティが不正です. 消去しました!"
#, c-format
+msgid "E938: Duplicate key in JSON: \"%s\""
+msgstr "E938: JSONに重複キーがあります: \"%s\""
+
+#, c-format
msgid "E696: Missing comma in List: %s"
msgstr "E696: リスト型にカンマがありません: %s"
@@ -2858,7 +2882,7 @@ msgid "This Vim was not compiled with the diff feature."
msgstr "このVimにはdiff機能がありません(コンパイル時設定)."
msgid "Attempt to open script file again: \""
-msgstr "スクリプトファイルを再び開いてみます: \""
+msgstr "スクリプトファイルを再び開こうとしました: \""
msgid "Cannot open for reading: \""
msgstr "読込用として開けません"
@@ -3039,6 +3063,9 @@ msgstr "-T <terminal>\t端末を <terminal> に設定する"
msgid "--not-a-term\t\tSkip warning for input/output not being a terminal"
msgstr "--not-a-term\t\t入出力が端末でないとの警告をスキップする"
+msgid "--ttyfail\t\tExit if input or output is not a terminal"
+msgstr "--ttyfail\t\t入出力が端末でなければ終了する"
+
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\t\t.vimrcの代わりに <vimrc> を使う"
@@ -3906,7 +3933,7 @@ msgid "E766: Insufficient arguments for printf()"
msgstr "E766: printf() の引数が不十分です"
msgid "E807: Expected Float argument for printf()"
-msgstr "E807: printf() の引数には浮動少数点数が期待されています"
+msgstr "E807: printf() の引数には浮動小数点数が期待されています"
msgid "E767: Too many arguments to printf()"
msgstr "E767: printf() の引数が多過ぎます"
@@ -4063,8 +4090,10 @@ msgstr "E662: 変更リストの先頭"
msgid "E663: At end of changelist"
msgstr "E663: 変更リストの末尾"
-msgid "Type :quit<Enter> to exit Vim"
-msgstr "Vimを終了するには :quit<Enter> と入力してください"
+msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim"
+msgstr ""
+"すべての変更を破棄し、Vimを終了するには :qa! と入力し <Enter> を押してくだ"
+"さい"
#, c-format
msgid "1 line %sed 1 time"
@@ -4413,9 +4442,6 @@ msgstr "入出力エラー"
msgid "Message"
msgstr "メッセージ"
-msgid "'columns' is not 80, cannot execute external commands"
-msgstr "'columns' が 80 ではないため、外部コマンドを実行できません"
-
msgid "E237: Printer selection failed"
msgstr "E237: プリンタの選択に失敗しました"
@@ -4685,6 +4711,12 @@ msgstr "E369: 無効な項目です: %s%%[]"
msgid "E769: Missing ] after %s["
msgstr "E769: %s[ の後に ] がありません"
+msgid "E944: Reverse range in character class"
+msgstr "E944: 文字クラスの範囲が逆です"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: 文字クラスの範囲が大きすぎます"
+
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: %s%%( が釣り合っていません"
@@ -4714,6 +4746,10 @@ msgstr "E69: %s%%[ の後に ] がありません"
msgid "E70: Empty %s%%[]"
msgstr "E70: %s%%[] が空です"
+#
+msgid "E65: Illegal back reference"
+msgstr "E65: 不正な後方参照です"
+
msgid "E339: Pattern too long"
msgstr "E339: パターンが長過ぎます"
@@ -4752,10 +4788,6 @@ msgid "E64: %s%c follows nothing"
msgstr "E64:%s%c の後になにもありません"
#
-msgid "E65: Illegal back reference"
-msgstr "E65: 不正な後方参照です"
-
-#
msgid "E68: Invalid character after \\z"
msgstr "E68: \\z の後に不正な文字がありました"
@@ -5363,12 +5395,33 @@ msgstr "E783: MAP エントリに重複文字が存在します"
msgid "No Syntax items defined for this buffer"
msgstr "このバッファに定義された構文要素はありません"
+msgid "syntax conceal on"
+msgstr "構文の conceal は現在 on です"
+
+msgid "syntax conceal off"
+msgstr "構文の conceal は現在 off です"
+
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: 不正な引数です: %s"
+msgid "syntax case ignore"
+msgstr "構文の大文字小文字は現在 ignore です"
+
+msgid "syntax case match"
+msgstr "構文の大文字小文字は現在 match です"
+
+msgid "syntax spell toplevel"
+msgstr "構文の spell は現在 toplevel です"
+
+msgid "syntax spell notoplevel"
+msgstr "構文の spell は現在 notoplevel です"
+
+msgid "syntax spell default"
+msgstr "構文の spell は現在 default です"
+
msgid "syntax iskeyword "
-msgstr "シンタックス用 iskeyword "
+msgstr "構文用 iskeyword "
#, c-format
msgid "E391: No such syntax cluster: %s"
@@ -5579,7 +5632,7 @@ msgid "E556: at top of tag stack"
msgstr "E556: タグスタックの先頭です"
msgid "E425: Cannot go before first matching tag"
-msgstr "E425: 最初の該当タグを超えて戻ることはできません"
+msgstr "E425: 最初の該当タグを越えて戻ることはできません"
#, c-format
msgid "E426: tag not found: %s"
@@ -5595,7 +5648,7 @@ msgid "E427: There is only one matching tag"
msgstr "E427: 該当タグが1つだけしかありません"
msgid "E428: Cannot go beyond last matching tag"
-msgstr "E428: 最後に該当するタグを超えて進むことはできません"
+msgstr "E428: 最後の該当タグを越えて進むことはできません"
#, c-format
msgid "File \"%s\" does not exist"
@@ -5943,6 +5996,10 @@ msgid "E126: Missing :endfunction"
msgstr "E126: :endfunction がありません"
#, c-format
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: :endfunction の後に文字があります: %s"
+
+#, c-format
msgid "E707: Function name conflicts with variable: %s"
msgstr "E707: 関数名が変数名と衝突します: %s"
@@ -5965,14 +6022,6 @@ msgstr "E133: 関数外に :return がありました"
msgid "E107: Missing parentheses: %s"
msgstr "E107: カッコ '(' がありません: %s"
-#. Only MS VC 4.1 and earlier can do Win32s
-msgid ""
-"\n"
-"MS-Windows 16/32-bit GUI version"
-msgstr ""
-"\n"
-"MS-Windows 16/32 ビット GUI 版"
-
msgid ""
"\n"
"MS-Windows 64-bit GUI version"
@@ -6256,12 +6305,6 @@ msgstr "詳細な情報は :help register<Enter> "
msgid "menu Help->Sponsor/Register for information "
msgstr "詳細はメニューの ヘルプ->スポンサー/登録 を参照して下さい"
-msgid "WARNING: Windows 95/98/ME detected"
-msgstr "警告: Windows 95/98/ME を検出しました"
-
-msgid "type :help windows95<Enter> for info on this"
-msgstr "詳細な情報は :help windows95<Enter>"
-
msgid "Already only one window"
msgstr "既にウィンドウは1つしかありません"
@@ -6415,6 +6458,10 @@ msgstr "E236: フォント \"%s\" は固定幅ではありません"
msgid "E473: Internal error"
msgstr "E473: 内部エラーです"
+#, c-format
+msgid "E685: Internal error: %s"
+msgstr "E685: 内部エラーです: %s"
+
msgid "Interrupted"
msgstr "割込まれました"
@@ -6680,8 +6727,8 @@ msgstr "E592: 'winwidth' は 'winminwidth' より小さくできません"
msgid "E80: Error while writing"
msgstr "E80: 書込み中のエラー"
-msgid "Zero count"
-msgstr "ゼロカウント"
+msgid "E939: Positive count required"
+msgstr "E939: 正のカウントが必要です"
msgid "E81: Using <SID> not in a script context"
msgstr "E81: スクリプト以外で<SID>が使われました"
@@ -6695,10 +6742,6 @@ msgstr "E463: 領域が保護されているので, 変更できません"
msgid "E744: NetBeans does not allow changes in read-only files"
msgstr "E744: NetBeans は読込専用ファイルを変更することを許しません"
-#, c-format
-msgid "E685: Internal error: %s"
-msgstr "E685: 内部エラーです: %s"
-
msgid "E363: pattern uses more memory than 'maxmempattern'"
msgstr "E363: パターンが 'maxmempattern' 以上のメモリを使用します"
diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po
index 7afa507edb..6173283135 100644
--- a/src/nvim/po/ko.UTF-8.po
+++ b/src/nvim/po/ko.UTF-8.po
@@ -1111,6 +1111,10 @@ msgstr "E137: Viminfo 파일의 쓰기 권한이 없습니다: %s"
#: ../ex_cmds.c:1626
#, c-format
+msgid "E929: Too many viminfo temp files, like %s!"
+msgstr "E929: 너무 많은 viminfo 임시 파일들, 가령 %s!"
+
+#, c-format
msgid "E138: Can't write viminfo file %s!"
msgstr "E138: Viminfo 파일 %s을(를) 쓸 수 없습니다!"
@@ -1119,6 +1123,10 @@ msgstr "E138: Viminfo 파일 %s을(를) 쓸 수 없습니다!"
msgid "Writing viminfo file \"%s\""
msgstr "Viminfo 파일 \"%s\"을(를) 쓰는 중"
+#, c-format
+msgid "E886: Can't rename viminfo file to %s!"
+msgstr "E886: viminfo 파일명을 %s(으)로 변경할 수 없습니다!"
+
#. Write the info:
#: ../ex_cmds.c:1720
#, c-format
@@ -1300,8 +1308,8 @@ msgstr "미안합니다, 도움말 파일 \"%s\"을(를) 찾을 수 없습니다
#: ../ex_cmds.c:5323
#, c-format
-msgid "E150: Not a directory: %s"
-msgstr "E150: 디렉토리가 아님: %s"
+msgid "E151: No match: %s"
+msgstr "E151: 맞지 않음: %s"
#: ../ex_cmds.c:5446
#, c-format
@@ -1325,6 +1333,10 @@ msgstr "E154: \"%s\" 태그가 %s/%s 파일에서 중복되었습니다"
#: ../ex_cmds.c:5687
#, c-format
+msgid "E150: Not a directory: %s"
+msgstr "E150: 디렉토리가 아님: %s"
+
+#, c-format
msgid "E160: Unknown sign command: %s"
msgstr "E160: 모르는 sign 명령: %s"
@@ -1452,8 +1464,16 @@ msgstr "\"%s\"을(를) 찾는 중"
#: ../ex_cmds2.c:2307
#, c-format
-msgid "not found in 'runtimepath': \"%s\""
-msgstr "'runtimepath'에서 찾을 수 없음: \"%s\""
+msgid "not found in '%s': \"%s\""
+msgstr "'%s'에서 찾을 수 없음: \"%s\""
+
+#, c-format
+msgid "W20: Required python version 2.x not supported, ignoring file: %s"
+msgstr "W20: 요구되는 파이선 버젼 2.x는 지원되지 않음, 파일을 무시: %s"
+
+#, c-format
+msgid "W21: Required python version 3.x not supported, ignoring file: %s"
+msgstr "W21: 요구되는 파이선 버젼 3.x는 지원되지 않음, 파일을 무시: %s"
#: ../ex_cmds2.c:2472
#, c-format
@@ -1609,10 +1629,10 @@ msgstr "E174: 명령이 이미 존재합니다: 바꾸려면 !을 더하세요"
#: ../ex_docmd.c:4432
msgid ""
"\n"
-" Name Args Range Complete Definition"
+" Name Args Address Complete Definition"
msgstr ""
"\n"
-" 이름 인자 범위 완성 정의"
+" 이름 인자 주소 완성 정의"
#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
@@ -1662,6 +1682,10 @@ msgstr "E184: 그런 사용자 정의 명령 없음: %s"
#: ../ex_docmd.c:5219
#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: 잘못된 주소 형식 값: %s"
+
+#, c-format
msgid "E180: Invalid complete value: %s"
msgstr "E180: 잘못된 끝내기 값: %s"
@@ -1953,7 +1977,7 @@ msgstr ""
#: ../ex_getln.c:5047
msgid "Command Line"
-msgstr "명령 줄"
+msgstr "명령 행"
#: ../ex_getln.c:5048
msgid "Search String"
@@ -1965,7 +1989,10 @@ msgstr "표현"
#: ../ex_getln.c:5050
msgid "Input Line"
-msgstr "입력 줄"
+msgstr "입력 행"
+
+msgid "Debug Line"
+msgstr "디버그 행"
#: ../ex_getln.c:5117
msgid "E198: cmd_pchar beyond the command length"
@@ -3190,6 +3217,7 @@ msgstr "%-5s: %s%*s (사용법: %s)"
#: ../if_cscope.c:1155
msgid ""
"\n"
+" a: Find assignments to this symbol\n"
" c: Find functions calling this function\n"
" d: Find functions called by this function\n"
" e: Find this egrep pattern\n"
@@ -3200,13 +3228,14 @@ msgid ""
" t: Find this text string\n"
msgstr ""
"\n"
+" a: 이 기호에 대한 할당 찾기\n"
" c: 이 함수를 부르는 함수들 찾기\n"
" d: 이 함수에 의해 불려지는 함수들 찾기\n"
" e: 이 egrep 패턴 찾기\n"
" f: 이 파일 찾기\n"
" g: 이 정의 찾기\n"
-" i: 이 파일을 포함하는 파일들 찾기\n"
-" s: 이 C 심볼 찾기\n"
+" i: 이 파일을 #include하는 파일들 찾기\n"
+" s: 이 C 기호 찾기\n"
" t: 이 문자열 찾기\n"
#: ../if_cscope.c:1226
@@ -4740,6 +4769,10 @@ msgstr "E447: path에서 \"%s\" 파일을 찾을 수 없습니다"
#: ../quickfix.c:359
#, c-format
+msgid "shell returned %d"
+msgstr "쉘이 %d을(를) 돌려주었습니다"
+
+#, c-format
msgid "E372: Too many %%%c in format string"
msgstr "E372: 형식 문자열에 %%%c이(가) 너무 많습니다"
@@ -5247,11 +5280,6 @@ msgstr "E772: Spell 파일이 새 버젼의 Vim용입니다"
msgid "E770: Unsupported section in spell file"
msgstr "E770: spell 파일에 지원되지 않는 섹션"
-#: ../spell.c:3762
-#, c-format
-msgid "Warning: region %s not supported"
-msgstr "경고: %s 영역은 지원되지 않습니다"
-
#: ../spell.c:4550
#, c-format
msgid "Reading affix file %s ..."
@@ -6312,7 +6340,7 @@ msgstr "by Bram Moolenaar et al."
#: ../version.c:774
msgid "Vim is open source and freely distributable"
-msgstr "빔은 소스가 열려 있고 공짜로 배포됩니다"
+msgstr "빔은 누구나 소스를 볼 수 있고 공짜로 배포됩니다"
#: ../version.c:776
msgid "Help poor children in Uganda!"
@@ -7478,9 +7506,6 @@ msgstr "E446: 커서 밑에 파일 이름이 없습니다"
#~ msgid "Could not fix up function pointers to the DLL!"
#~ msgstr "함수 포인터를 DLL로 바꿀 수 없습니다!"
-#~ msgid "shell returned %d"
-#~ msgstr "쉘이 %d을(를) 돌려주었습니다"
-
#~ msgid "Vim: Caught %s event\n"
#~ msgstr "빔: %s 이벤트를 잡았습니다\n"
diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po
index 0757afd3ae..c65602344e 100644
--- a/src/nvim/po/pl.UTF-8.po
+++ b/src/nvim/po/pl.UTF-8.po
@@ -3550,7 +3550,7 @@ msgstr ""
#: ../main.c:2240
msgid "--startuptime <file>\tWrite startup timing messages to <file>"
msgstr ""
-"--startuptime <plik>\n"
+"--startuptime <plik> "
"Zapisz wiadomości o długości startu do <plik>"
#: ../main.c:2242
diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po
index 05986cf097..f8d6ac3011 100644
--- a/src/nvim/po/pt_BR.po
+++ b/src/nvim/po/pt_BR.po
@@ -24,7 +24,7 @@ msgstr "[Ajuda]"
#: ../screen.c:4815 ../buffer.c:3244
msgid "[Preview]"
-msgstr "[Visualizao]"
+msgstr "[Visualização]"
#: ../screen.c:4823 ../fileio.c:1855 ../buffer.c:2496 ../buffer.c:3207
msgid "[RO]"
@@ -4859,7 +4859,7 @@ msgstr " tipo arquivo\n"
#: ../ex_getln.c:4762
msgid "'history' option is zero"
-msgstr "opo 'history' vale zero"
+msgstr "opção 'history' vale zero"
#: ../ex_getln.c:5008
#, c-format
@@ -6203,7 +6203,7 @@ msgstr "--\t\t\tApenas nomes de arquivo depois daqui"
#: ../main.c:2177
msgid "--literal\t\tDon't expand wildcards"
-msgstr "--literal\t\tNo expandir caracteres-curinga"
+msgstr "--literal\t\tNão expandir caracteres-curinga"
#: ../main.c:2179
msgid "-v\t\t\tVi mode (like \"vi\")"
@@ -6227,7 +6227,7 @@ msgstr "-d\t\t\tModo diff (como \"vimdiff\")"
#: ../main.c:2184
msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
-msgstr "-y\t\t\tModo fcil (como \"evim\", o Vim no modal)"
+msgstr "-y\t\t\tModo fácil (como \"evim\", o Vim não modal)"
#: ../main.c:2185
msgid "-R\t\t\tReadonly mode (like \"view\")"
@@ -6341,8 +6341,8 @@ msgstr ""
#: ../main.c:2213
msgid "-S <session>\t\tSource file <session> after loading the first file"
msgstr ""
-"-S <sesso>\t\tExecutar o arquivo <sesso> depois de carregar o\n"
-"\t\t\tprimeiro arquivo"
+"-S <sesso>\t\tExecutar o arquivo <sesso> depois de carregar o "
+"primeiro arquivo"
#: ../main.c:2214
msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>"
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index a4668743ba..12022d02c8 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -3376,8 +3376,7 @@ msgstr "-t метка редактирование файла с указан
#: ../main.c:2181
msgid "-q [errorfile] edit file with first error"
msgstr ""
-"-q [файл-ошибок]\n"
-"\t\t\t\t редактирование файла с первой ошибкой"
+"-q [файл-ошибок] редактирование файла с первой ошибкой"
#: ../main.c:2187
msgid ""
@@ -3479,8 +3478,8 @@ msgstr "-N\t\t\tРежим неполной совместимости с Vi: 'n
#: ../main.c:2215
msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
msgstr ""
-"-V[N][файл]\t\tВыводить дополнительные сообщения\n"
-"\t\t\t\t[уровень N] [записывать в файл]"
+"-V[N][файл]\t\tВыводить дополнительные сообщения "
+"[уровень N] [записывать в файл]"
#: ../main.c:2216
msgid "-D\t\t\tDebugging mode"
@@ -4105,7 +4104,6 @@ msgstr ""
#: ../memline.c:3245
msgid " Quit, or continue with caution.\n"
msgstr ""
-" \n"
" Завершите работу или продолжайте с осторожностью.\n"
#: ../memline.c:3246
diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po
index 4b1e64bd02..bf205938fa 100644
--- a/src/nvim/po/sk.cp1250.po
+++ b/src/nvim/po/sk.cp1250.po
@@ -3870,6 +3870,7 @@ msgid ""
"You may want to delete the .swp file now.\n"
"\n"
msgstr "Potom vymate odkladac sbor s prponou .swp.\n"
+"\n"
#. use msg() to start the scrolling properly
#: ../memline.c:1327
diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po
index e48a5de927..3c92ec3c34 100644
--- a/src/nvim/po/sk.po
+++ b/src/nvim/po/sk.po
@@ -3870,6 +3870,7 @@ msgid ""
"You may want to delete the .swp file now.\n"
"\n"
msgstr "Potom vymate odkladac sbor s prponou .swp.\n"
+"\n"
#. use msg() to start the scrolling properly
#: ../memline.c:1327
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index 2c203f808f..1ac2d2247a 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -1,6 +1,8 @@
#
# Ukrainian Vim translation [uk]
#
+# Original translations
+#
# Copyright (C) 2001 Bohdan Vlasyuk <bohdan@vstu.edu.ua>
# Bohdan donated this work to be distributed with Vim under the Vim license.
#
@@ -510,6 +512,9 @@ msgstr "Пошук у: %s"
msgid "Scanning tags."
msgstr "Пошук серед теґів."
+msgid "match in file"
+msgstr "збіг у файлі"
+
msgid " Adding"
msgstr " Додається"
@@ -642,6 +647,12 @@ msgstr "E107: Пропущено дужки: %s"
msgid "E108: No such variable: \"%s\""
msgstr "E108: Змінної немає: «%s»"
+#. For historic reasons this error is not given for a list or dict.
+#. * E.g., the b: dict could be locked/unlocked.
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: Неможливо заблокувати чи розблокувати змінну %s"
+
msgid "E743: variable nested too deep for (un)lock"
msgstr "E743: Змінна має забагато вкладень щоб бути за-/відкритою."
@@ -1392,8 +1403,9 @@ msgstr " в одному рядку"
msgid " on %<PRId64> lines"
msgstr " в %<PRId64> рядках"
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: :global не можна вживати рекурсивно"
+#. will increment global_busy to break out of the loop
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: :global не можна вживати рекурсивно з діапазоном"
msgid "E148: Regular expression missing from global"
msgstr "E148: У global бракує зразка"
@@ -3516,7 +3528,6 @@ msgid ""
msgstr ""
"»,\n"
" щоб позбутися цього повідомлення.\n"
-"\n"
msgid "Swap file \""
msgstr "Файл обміну «"
@@ -4130,6 +4141,12 @@ msgstr "E369: Некоректний елемент у %s%%[]"
msgid "E769: Missing ] after %s["
msgstr "E769: Бракує ] після %s["
+msgid "E944: Reverse range in character class"
+msgstr "E944: Зворотній діапазон у класі символів"
+
+msgid "E945: Range too large in character class"
+msgstr "E945: Завеликий діапазон у класі символів"
+
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: Немає пари %s%%("
@@ -4456,7 +4473,7 @@ msgid "Can't rename ShaDa file from %s to %s!"
msgstr "Не вдалося перейменувати файл ShaDa з %s у %s!"
#, c-format
-msgid "Did not rename %s because %s does not looks like a ShaDa file"
+msgid "Did not rename %s because %s does not look like a ShaDa file"
msgstr "Не перейменував %s, тому що %s не схожий на файл ShaDa"
#, c-format
@@ -4888,10 +4905,31 @@ msgstr "E783: Повторено символ у елементі MAP"
msgid "No Syntax items defined for this buffer"
msgstr "Для буфера не визначено елементів синтаксису"
+msgid "syntax conceal on"
+msgstr "маскування синтаксису увімк"
+
+msgid "syntax conceal off"
+msgstr "маскування синтаксису вимк"
+
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Неправильний аргумент: %s"
+msgid "syntax case ignore"
+msgstr "синтаксис ігнорувати регістр"
+
+msgid "syntax case match"
+msgstr "синтаксис дотримуватися регістру"
+
+msgid "syntax spell toplevel"
+msgstr "синтаксис перевіряти всюди"
+
+msgid "syntax spell notoplevel"
+msgstr "синтаксис не перевіряти"
+
+msgid "syntax spell default"
+msgstr "синтаксис початково"
+
msgid "syntax iskeyword "
msgstr "синтаксис iskeyword "
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 2462975c9b..348daf028a 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -567,6 +567,7 @@ static int pum_set_selected(int n, int repeat)
&& (repeat <= 1)
&& (vim_strchr(p_cot, 'p') != NULL)) {
win_T *curwin_save = curwin;
+ tabpage_T *curtab_save = curtab;
int res = OK;
// Open a preview window. 3 lines by default. Prefer
@@ -593,7 +594,7 @@ static int pum_set_selected(int n, int repeat)
&& (curbuf->b_p_bt[2] == 'f')
&& (curbuf->b_p_bh[0] == 'w')) {
// Already a "wipeout" buffer, make it empty.
- while (!bufempty()) {
+ while (!BUFEMPTY()) {
ml_delete((linenr_T)1, FALSE);
}
} else {
@@ -647,7 +648,12 @@ static int pum_set_selected(int n, int repeat)
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
- if ((curwin != curwin_save) && win_valid(curwin_save)) {
+ if ((curwin != curwin_save && win_valid(curwin_save))
+ || (curtab != curtab_save && valid_tabpage(curtab_save))) {
+ if (curtab != curtab_save && valid_tabpage(curtab_save)) {
+ goto_tabpage_tp(curtab_save, false, false);
+ }
+
// When the first completion is done and the preview
// window is not resized, skip the preview window's
// status line redrawing.
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index bd5dfa92cc..2d8c353f92 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -76,17 +76,25 @@ struct qfline_S {
*/
#define LISTCOUNT 10
+/// Quickfix/Location list definition
+///
+/// Usually the list contains one or more entries. But an empty list can be
+/// created using setqflist()/setloclist() with a title and/or user context
+/// information and entries can be added later using setqflist()/setloclist().
typedef struct qf_list_S {
- qfline_T *qf_start; // pointer to the first error
- qfline_T *qf_last; // pointer to the last error
- qfline_T *qf_ptr; // pointer to the current error
- int qf_count; // number of errors (0 means no error list)
- int qf_index; // current index in the error list
- int qf_nonevalid; // TRUE if not a single valid entry found
- char_u *qf_title; // title derived from the command that created
- // the error list
+ qfline_T *qf_start; ///< pointer to the first error
+ qfline_T *qf_last; ///< pointer to the last error
+ qfline_T *qf_ptr; ///< pointer to the current error
+ int qf_count; ///< number of errors (0 means empty list)
+ int qf_index; ///< current index in the error list
+ int qf_nonevalid; ///< TRUE if not a single valid entry found
+ char_u *qf_title; ///< title derived from the command that created
+ ///< the error list or set by setqflist
+ typval_T *qf_ctx; ///< context set by setqflist/setloclist
} qf_list_T;
+/// Quickfix/Location list stack definition
+/// Contains a list of quickfix/location lists (qf_list_T)
struct qf_info_S {
/*
* Count of references to this list. Used only for location lists.
@@ -156,10 +164,12 @@ typedef struct {
FILE *fd;
typval_T *tv;
char_u *p_str;
+ list_T *p_list;
listitem_T *p_li;
buf_T *buf;
linenr_T buflnum;
linenr_T lnumlast;
+ vimconv_T vc;
} qfstate_T;
typedef struct {
@@ -193,19 +203,19 @@ typedef struct {
static char_u *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = { NULL, 0, 0 };
-/*
- * Read the errorfile "efile" into memory, line by line, building the error
- * list. Set the error list's title to qf_title.
- * Return -1 for error, number of errors for success.
- */
-int
-qf_init (
- win_T *wp,
- char_u *efile,
- char_u *errorformat,
- int newlist, /* TRUE: start a new error list */
- char_u *qf_title
-)
+/// Read the errorfile "efile" into memory, line by line, building the error
+/// list. Set the error list's title to qf_title.
+///
+/// @params wp If non-NULL, make a location list
+/// @params efile If non-NULL, errorfile to parse
+/// @params errorformat 'errorformat' string used to parse the error lines
+/// @params newlist If true, create a new error list
+/// @params qf_title If non-NULL, title of the error list
+/// @params enc If non-NULL, encoding used to parse errors
+///
+/// @returns -1 for error, number of errors for success.
+int qf_init(win_T *wp, char_u *efile, char_u *errorformat, int newlist,
+ char_u *qf_title, char_u *enc)
{
qf_info_T *qi = &ql_info;
@@ -214,8 +224,8 @@ qf_init (
}
return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist,
- (linenr_T)0, (linenr_T)0,
- qf_title);
+ (linenr_T)0, (linenr_T)0,
+ qf_title, enc);
}
// Maximum number of bytes allowed per line while reading an errorfile.
@@ -383,6 +393,8 @@ static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr,
return 0;
}
+static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
+
static void free_efm_list(efm_T **efm_first)
{
for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) {
@@ -390,6 +402,8 @@ static void free_efm_list(efm_T **efm_first)
vim_regfree(efm_ptr->prog);
xfree(efm_ptr);
}
+
+ fmt_start = NULL;
}
// Parse 'errorformat' option
@@ -512,17 +526,17 @@ static int qf_get_next_list_line(qfstate_T *state)
// Get the next line from the supplied list
while (p_li != NULL
- && (p_li->li_tv.v_type != VAR_STRING
- || p_li->li_tv.vval.v_string == NULL)) {
- p_li = p_li->li_next; // Skip non-string items
+ && (TV_LIST_ITEM_TV(p_li)->v_type != VAR_STRING
+ || TV_LIST_ITEM_TV(p_li)->vval.v_string == NULL)) {
+ p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); // Skip non-string items.
}
- if (p_li == NULL) { // End of the list
+ if (p_li == NULL) { // End of the list.
state->p_li = NULL;
return QF_END_OF_INPUT;
}
- len = STRLEN(p_li->li_tv.vval.v_string);
+ len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string);
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
@@ -530,9 +544,10 @@ static int qf_get_next_list_line(qfstate_T *state)
state->linelen = len;
}
- STRLCPY(state->linebuf, p_li->li_tv.vval.v_string, state->linelen + 1);
+ STRLCPY(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string,
+ state->linelen + 1);
- state->p_li = p_li->li_next; // next item
+ state->p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li);
return QF_OK;
}
@@ -566,7 +581,12 @@ static int qf_get_next_file_line(qfstate_T *state)
{
size_t growbuflen;
+retry:
+ errno = 0;
if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) {
+ if (errno == EINTR) {
+ goto retry;
+ }
return QF_END_OF_INPUT;
}
@@ -586,8 +606,12 @@ static int qf_get_next_file_line(qfstate_T *state)
growbuflen = state->linelen;
for (;;) {
+ errno = 0;
if (fgets((char *)state->growbuf + growbuflen,
(int)(state->growbufsiz - growbuflen), state->fd) == NULL) {
+ if (errno == EINTR) {
+ continue;
+ }
break;
}
state->linelen = STRLEN(state->growbuf + growbuflen);
@@ -608,9 +632,14 @@ static int qf_get_next_file_line(qfstate_T *state)
while (discard) {
// The current line is longer than LINE_MAXLEN, continue reading but
// discard everything until EOL or EOF is reached.
- if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL
- || STRLEN(IObuff) < IOSIZE - 1
- || IObuff[IOSIZE - 1] == '\n') {
+ errno = 0;
+ if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) {
+ if (errno == EINTR) {
+ continue;
+ }
+ break;
+ }
+ if (STRLEN(IObuff) < IOSIZE - 1 || IObuff[IOSIZE - 1] == '\n') {
break;
}
}
@@ -620,6 +649,22 @@ static int qf_get_next_file_line(qfstate_T *state)
} else {
state->linebuf = IObuff;
}
+
+ // Convert a line if it contains a non-ASCII character
+ if (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf)) {
+ char_u *line = string_convert(&state->vc, state->linebuf, &state->linelen);
+ if (line != NULL) {
+ if (state->linelen < IOSIZE) {
+ STRLCPY(state->linebuf, line, state->linelen + 1);
+ xfree(line);
+ } else {
+ xfree(state->growbuf);
+ state->linebuf = state->growbuf = line;
+ state->growbufsiz = state->linelen < LINE_MAXLEN
+ ? state->linelen : LINE_MAXLEN;
+ }
+ }
+ }
return QF_OK;
}
@@ -671,7 +716,6 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen,
efm_T *fmt_first, qffields_T *fields)
{
efm_T *fmt_ptr;
- static efm_T *fmt_start = NULL; // cached across calls
size_t len;
int i;
int idx = 0;
@@ -766,7 +810,7 @@ restofline:
fields->type = *regmatch.startp[i];
}
if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+
- if (linelen > fields->errmsglen) {
+ if (linelen >= fields->errmsglen) {
// linelen + null terminator
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
fields->errmsglen = linelen + 1;
@@ -777,7 +821,7 @@ restofline:
continue;
}
len = (size_t)(regmatch.endp[i] - regmatch.startp[i]);
- if (len > fields->errmsglen) {
+ if (len >= fields->errmsglen) {
// len + null terminator
fields->errmsg = xrealloc(fields->errmsg, len + 1);
fields->errmsglen = len + 1;
@@ -854,7 +898,7 @@ restofline:
fields->namebuf[0] = NUL; // no match found, remove file name
fields->lnum = 0; // don't jump to this line
fields->valid = false;
- if (linelen > fields->errmsglen) {
+ if (linelen >= fields->errmsglen) {
// linelen + null terminator
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
fields->errmsglen = linelen + 1;
@@ -875,36 +919,38 @@ restofline:
qi->qf_multiignore = false; // reset continuation
} else if (vim_strchr((char_u *)"CZ", idx)
!= NULL) { // continuation of multi-line msg
- qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last;
- if (qfprev == NULL) {
- return QF_FAIL;
- }
- if (*fields->errmsg && !qi->qf_multiignore) {
- size_t len = STRLEN(qfprev->qf_text);
- qfprev->qf_text = xrealloc(qfprev->qf_text,
- len + STRLEN(fields->errmsg) + 2);
- qfprev->qf_text[len] = '\n';
- STRCPY(qfprev->qf_text + len + 1, fields->errmsg);
- }
- if (qfprev->qf_nr == -1) {
- qfprev->qf_nr = fields->enr;
- }
- if (vim_isprintc(fields->type) && !qfprev->qf_type) {
- qfprev->qf_type = fields->type; // only printable chars allowed
- }
- if (!qfprev->qf_lnum) {
- qfprev->qf_lnum = fields->lnum;
- }
- if (!qfprev->qf_col) {
- qfprev->qf_col = fields->col;
- }
- qfprev->qf_viscol = fields->use_viscol;
- if (!qfprev->qf_fnum) {
- qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory,
- *fields->namebuf || qi->qf_directory
- ? fields->namebuf
- : qi->qf_currfile && fields->valid
- ? qi->qf_currfile : 0);
+ if (!qi->qf_multiignore) {
+ qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last;
+ if (qfprev == NULL) {
+ return QF_FAIL;
+ }
+ if (*fields->errmsg && !qi->qf_multiignore) {
+ size_t len = STRLEN(qfprev->qf_text);
+ qfprev->qf_text = xrealloc(qfprev->qf_text,
+ len + STRLEN(fields->errmsg) + 2);
+ qfprev->qf_text[len] = '\n';
+ STRCPY(qfprev->qf_text + len + 1, fields->errmsg);
+ }
+ if (qfprev->qf_nr == -1) {
+ qfprev->qf_nr = fields->enr;
+ }
+ if (vim_isprintc(fields->type) && !qfprev->qf_type) {
+ qfprev->qf_type = fields->type; // only printable chars allowed
+ }
+ if (!qfprev->qf_lnum) {
+ qfprev->qf_lnum = fields->lnum;
+ }
+ if (!qfprev->qf_col) {
+ qfprev->qf_col = fields->col;
+ }
+ qfprev->qf_viscol = fields->use_viscol;
+ if (!qfprev->qf_fnum) {
+ qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory,
+ *fields->namebuf || qi->qf_directory
+ ? fields->namebuf
+ : qi->qf_currfile && fields->valid
+ ? qi->qf_currfile : 0);
+ }
}
if (idx == 'Z') {
qi->qf_multiline = qi->qf_multiignore = false;
@@ -957,16 +1003,17 @@ qf_init_ext(
buf_T *buf,
typval_T *tv,
char_u *errorformat,
- int newlist, /* TRUE: start a new error list */
- linenr_T lnumfirst, /* first line number to use */
- linenr_T lnumlast, /* last line number to use */
- char_u *qf_title
+ int newlist, // TRUE: start a new error list
+ linenr_T lnumfirst, // first line number to use
+ linenr_T lnumlast, // last line number to use
+ char_u *qf_title,
+ char_u *enc
)
{
- qfstate_T state = { NULL, 0, NULL, 0, NULL, NULL, NULL, NULL,
- NULL, 0, 0 };
- qffields_T fields = { NULL, NULL, 0, 0L, 0, false, NULL, 0, 0, 0 };
+ qfstate_T state;
+ qffields_T fields;
qfline_T *old_last = NULL;
+ bool adding = false;
static efm_T *fmt_first = NULL;
char_u *efm;
static char_u *last_efm = NULL;
@@ -977,6 +1024,13 @@ qf_init_ext(
xfree(qf_last_bufname);
qf_last_bufname = NULL;
+ memset(&state, 0, sizeof(state));
+ memset(&fields, 0, sizeof(fields));
+ state.vc.vc_type = CONV_NONE;
+ if (enc != NULL && *enc != NUL) {
+ convert_setup(&state.vc, enc, p_enc);
+ }
+
fields.namebuf = xmalloc(CMDBUFFSIZE + 1);
fields.errmsglen = CMDBUFFSIZE + 1;
fields.errmsg = xmalloc(fields.errmsglen);
@@ -992,6 +1046,7 @@ qf_init_ext(
qf_new_list(qi, qf_title);
} else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
// Adding to existing list, use last entry.
+ adding = true;
old_last = qi->qf_lists[qi->qf_curlist].qf_last;
}
@@ -1043,7 +1098,8 @@ qf_init_ext(
if (tv->v_type == VAR_STRING) {
state.p_str = tv->vval.v_string;
} else if (tv->v_type == VAR_LIST) {
- state.p_li = tv->vval.v_list->lv_first;
+ state.p_list = tv->vval.v_list;
+ state.p_li = tv_list_first(tv->vval.v_list);
}
state.tv = tv;
}
@@ -1072,6 +1128,7 @@ qf_init_ext(
}
if (qf_add_entry(qi,
+ qi->qf_curlist,
qi->qf_directory,
(*fields.namebuf || qi->qf_directory)
? fields.namebuf : ((qi->qf_currfile && fields.valid)
@@ -1108,10 +1165,12 @@ qf_init_ext(
}
EMSG(_(e_readerrf));
error2:
- qf_free(qi, qi->qf_curlist);
- qi->qf_listcount--;
- if (qi->qf_curlist > 0) {
- qi->qf_curlist--;
+ if (!adding) {
+ qf_free(qi, qi->qf_curlist);
+ qi->qf_listcount--;
+ if (qi->qf_curlist > 0) {
+ qi->qf_curlist--;
+ }
}
qf_init_end:
if (state.fd != NULL) {
@@ -1124,16 +1183,24 @@ qf_init_end:
qf_update_buffer(qi, old_last);
+ if (state.vc.vc_type != CONV_NONE) {
+ convert_setup(&state.vc, NULL, NULL);
+ }
+
return retval;
}
-static void qf_store_title(qf_info_T *qi, char_u *title)
+static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title)
{
+ xfree(qi->qf_lists[qf_idx].qf_title);
+ qi->qf_lists[qf_idx].qf_title = NULL;
+
if (title != NULL) {
- char_u *p = xmalloc(STRLEN(title) + 2);
+ size_t len = STRLEN(title) + 1;
+ char_u *p = xmallocz(len);
- qi->qf_lists[qi->qf_curlist].qf_title = p;
- sprintf((char *)p, ":%s", (char *)title);
+ qi->qf_lists[qf_idx].qf_title = p;
+ snprintf((char *)p, len + 1, ":%s", (char *)title);
}
}
@@ -1162,7 +1229,7 @@ static void qf_new_list(qf_info_T *qi, char_u *qf_title)
} else
qi->qf_curlist = qi->qf_listcount++;
memset(&qi->qf_lists[qi->qf_curlist], 0, (size_t)(sizeof(qf_list_T)));
- qf_store_title(qi, qf_title);
+ qf_store_title(qi, qi->qf_curlist, qf_title);
}
/*
@@ -1205,6 +1272,7 @@ void qf_free_all(win_T *wp)
/// Add an entry to the end of the list of errors.
///
/// @param qi quickfix list
+/// @param qf_idx list index
/// @param dir optional directory name
/// @param fname file name or NULL
/// @param bufnum buffer number or zero
@@ -1218,9 +1286,10 @@ void qf_free_all(win_T *wp)
/// @param valid valid entry
///
/// @returns OK or FAIL.
-static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum,
- char_u *mesg, long lnum, int col, char_u vis_col,
- char_u *pattern, int nr, char_u type, char_u valid)
+static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
+ int bufnum, char_u *mesg, long lnum, int col,
+ char_u vis_col, char_u *pattern, int nr, char_u type,
+ char_u valid)
{
qfline_T *qfp = xmalloc(sizeof(qfline_T));
qfline_T **lastp; // pointer to qf_last or NULL
@@ -1251,12 +1320,12 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum,
qfp->qf_type = (char_u)type;
qfp->qf_valid = valid;
- lastp = &qi->qf_lists[qi->qf_curlist].qf_last;
- if (qi->qf_lists[qi->qf_curlist].qf_count == 0) {
- /* first element in the list */
- qi->qf_lists[qi->qf_curlist].qf_start = qfp;
- qi->qf_lists[qi->qf_curlist].qf_ptr = qfp;
- qi->qf_lists[qi->qf_curlist].qf_index = 0;
+ lastp = &qi->qf_lists[qf_idx].qf_last;
+ if (qi->qf_lists[qf_idx].qf_count == 0) {
+ // first element in the list
+ qi->qf_lists[qf_idx].qf_start = qfp;
+ qi->qf_lists[qf_idx].qf_ptr = qfp;
+ qi->qf_lists[qf_idx].qf_index = 0;
qfp->qf_prev = NULL;
} else {
assert(*lastp);
@@ -1266,12 +1335,11 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum,
qfp->qf_next = NULL;
qfp->qf_cleared = false;
*lastp = qfp;
- qi->qf_lists[qi->qf_curlist].qf_count++;
- if (qi->qf_lists[qi->qf_curlist].qf_index == 0 && qfp->qf_valid) {
- /* first valid entry */
- qi->qf_lists[qi->qf_curlist].qf_index =
- qi->qf_lists[qi->qf_curlist].qf_count;
- qi->qf_lists[qi->qf_curlist].qf_ptr = qfp;
+ qi->qf_lists[qf_idx].qf_count++;
+ if (qi->qf_lists[qf_idx].qf_index == 0 && qfp->qf_valid) {
+ // first valid entry
+ qi->qf_lists[qf_idx].qf_index = qi->qf_lists[qf_idx].qf_count;
+ qi->qf_lists[qf_idx].qf_ptr = qfp;
}
return OK;
@@ -1358,6 +1426,13 @@ void copy_loclist(win_T *from, win_T *to)
else
to_qfl->qf_title = NULL;
+ if (from_qfl->qf_ctx != NULL) {
+ to_qfl->qf_ctx = xcalloc(1, sizeof(typval_T));
+ tv_copy(from_qfl->qf_ctx, to_qfl->qf_ctx);
+ } else {
+ to_qfl->qf_ctx = NULL;
+ }
+
if (from_qfl->qf_count) {
qfline_T *from_qfp;
qfline_T *prevp;
@@ -1367,6 +1442,7 @@ void copy_loclist(win_T *from, win_T *to)
i < from_qfl->qf_count && from_qfp != NULL;
i++, from_qfp = from_qfp->qf_next) {
if (qf_add_entry(to->w_llist,
+ to->w_llist->qf_curlist,
NULL,
NULL,
0,
@@ -1408,7 +1484,7 @@ void copy_loclist(win_T *from, win_T *to)
to->w_llist->qf_curlist = qi->qf_curlist; /* current list */
}
-// Get buffer number for file "directory.fname".
+// Get buffer number for file "directory/fname".
// Also sets the b_has_qf_entry flag.
static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname)
{
@@ -1869,7 +1945,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;
@@ -2330,8 +2406,9 @@ void qf_history(exarg_T *eap)
}
}
-/// Free all the entries in the error list "idx".
-static void qf_free(qf_info_T *qi, int idx)
+/// Free all the entries in the error list "idx". Note that other information
+/// associated with the list like context and title are not freed.
+static void qf_free_items(qf_info_T *qi, int idx)
{
qfline_T *qfp;
qfline_T *qfpnext;
@@ -2355,20 +2432,41 @@ static void qf_free(qf_info_T *qi, int idx)
qi->qf_lists[idx].qf_start = qfpnext;
qi->qf_lists[idx].qf_count--;
}
- xfree(qi->qf_lists[idx].qf_title);
+
qi->qf_lists[idx].qf_start = NULL;
qi->qf_lists[idx].qf_ptr = NULL;
- qi->qf_lists[idx].qf_title = NULL;
qi->qf_lists[idx].qf_index = 0;
+ qi->qf_lists[idx].qf_start = NULL;
+ qi->qf_lists[idx].qf_last = NULL;
+ qi->qf_lists[idx].qf_ptr = NULL;
+ qi->qf_lists[idx].qf_nonevalid = true;
qf_clean_dir_stack(&qi->qf_dir_stack);
+ qi->qf_directory = NULL;
qf_clean_dir_stack(&qi->qf_file_stack);
+ qi->qf_currfile = NULL;
+ qi->qf_multiline = false;
+ qi->qf_multiignore = false;
+ qi->qf_multiscan = false;
+}
+
+/// Free error list "idx". Frees all the entries in the quickfix list,
+/// associated context information and the title.
+static void qf_free(qf_info_T *qi, int idx)
+{
+ qf_free_items(qi, idx);
+
+ xfree(qi->qf_lists[idx].qf_title);
+ qi->qf_lists[idx].qf_title = NULL;
+ tv_free(qi->qf_lists[idx].qf_ctx);
+ qi->qf_lists[idx].qf_ctx = NULL;
}
/*
* qf_mark_adjust: adjust marks
*/
-void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after)
+bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount,
+ long amount_after)
{
int i;
qfline_T *qfp;
@@ -2378,11 +2476,12 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long
int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
if (!(curbuf->b_has_qf_entry & buf_has_flag)) {
- return;
+ return false;
}
if (wp != NULL) {
- if (wp->w_llist == NULL)
- return;
+ if (wp->w_llist == NULL) {
+ return false;
+ }
qi = wp->w_llist;
}
@@ -2403,9 +2502,7 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long
}
}
- if (!found_one) {
- curbuf->b_has_qf_entry &= ~buf_has_flag;
- }
+ return found_one;
}
/*
@@ -2828,7 +2925,7 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
if (old_last == NULL) {
if (buf != curbuf) {
- EMSG2(_(e_intern2), "qf_fill_buffer()");
+ internal_error("qf_fill_buffer()");
return;
}
@@ -2999,6 +3096,7 @@ void ex_make(exarg_T *eap)
qf_info_T *qi = &ql_info;
int res;
char_u *au_name = NULL;
+ char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
/* Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". */
if (grep_internal(eap->cmdidx)) {
@@ -3050,19 +3148,18 @@ void ex_make(exarg_T *eap)
}
msg_start();
MSG_PUTS(":!");
- msg_outtrans((char_u *) cmd); // show what we are doing
+ msg_outtrans((char_u *)cmd); // show what we are doing
- // let the shell know if we are redirecting output or not
- do_shell((char_u *) cmd, *p_sp != NUL ? kShellOptDoOut : 0);
+ do_shell((char_u *)cmd, 0);
res = qf_init(wp, fname, (eap->cmdidx != CMD_make
&& eap->cmdidx != CMD_lmake) ? p_gefm : p_efm,
- (eap->cmdidx != CMD_grepadd
- && eap->cmdidx != CMD_lgrepadd),
- *eap->cmdlinep);
- if (wp != NULL)
+ (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd),
+ *eap->cmdlinep, enc);
+ if (wp != NULL) {
qi = GET_LOC_LIST(wp);
+ }
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, TRUE, curbuf);
@@ -3116,7 +3213,7 @@ static char_u *get_mef_name(void)
STRCPY(name, p_mef);
sprintf((char *)name + (p - p_mef), "%d%d", start, off);
STRCAT(name, p + 2);
- // Don't accept a symbolic link, its a security risk.
+ // Don't accept a symbolic link, it's a security risk.
FileInfo file_info;
bool file_or_link_found = os_fileinfo_link((char *)name, &file_info);
if (!file_or_link_found) {
@@ -3404,19 +3501,18 @@ void ex_cfile(exarg_T *eap)
if (*eap->arg != NUL)
set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0);
- /*
- * This function is used by the :cfile, :cgetfile and :caddfile
- * commands.
- * :cfile always creates a new quickfix list and jumps to the
- * first error.
- * :cgetfile creates a new quickfix list but doesn't jump to the
- * first error.
- * :caddfile adds to an existing quickfix list. If there is no
- * quickfix list then a new list is created.
- */
+ char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
+ // This function is used by the :cfile, :cgetfile and :caddfile
+ // commands.
+ // :cfile always creates a new quickfix list and jumps to the
+ // first error.
+ // :cgetfile creates a new quickfix list but doesn't jump to the
+ // first error.
+ // :caddfile adds to an existing quickfix list. If there is no
+ // quickfix list then a new list is created.
if (qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
&& eap->cmdidx != CMD_laddfile),
- *eap->cmdlinep) > 0
+ *eap->cmdlinep, enc) > 0
&& (eap->cmdidx == CMD_cfile
|| eap->cmdidx == CMD_lfile)) {
if (au_name != NULL)
@@ -3553,8 +3649,8 @@ void ex_vimgrep(exarg_T *eap)
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
seconds = (time_t)0;
- for (fi = 0; fi < fcount && !got_int && tomatch > 0; ++fi) {
- fname = path_shorten_fname_if_possible(fnames[fi]);
+ for (fi = 0; fi < fcount && !got_int && tomatch > 0; fi++) {
+ fname = path_try_shorten_fname(fnames[fi]);
if (time(NULL) > seconds) {
/* Display the file name every second or so, show the user we are
* working on it. */
@@ -3631,6 +3727,7 @@ void ex_vimgrep(exarg_T *eap)
// dummy buffer, unless duplicate_name is set, then the
// buffer will be wiped out below.
if (qf_add_entry(qi,
+ qi->qf_curlist,
NULL, // dir
fname,
duplicate_name ? 0 : buf->b_fnum,
@@ -3812,6 +3909,7 @@ load_dummy_buffer (
bufref_T newbuf_to_wipe;
int failed = true;
aco_save_T aco;
+ int readfile_result;
// Allocate a buffer without putting it in the buffer list.
newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
@@ -3825,7 +3923,9 @@ load_dummy_buffer (
/* need to open the memfile before putting the buffer in a window */
if (ml_open(newbuf) == OK) {
- /* set curwin/curbuf to buf and save a few things */
+ // Make sure this buffer isn't wiped out by auto commands.
+ newbuf->b_locked++;
+ // set curwin/curbuf to buf and save a few things
aucmd_prepbuf(&aco, newbuf);
/* Need to set the filename for autocommands. */
@@ -3839,9 +3939,11 @@ load_dummy_buffer (
curbuf->b_flags &= ~BF_DUMMY;
newbuf_to_wipe.br_buf = NULL;
- if (readfile(fname, NULL,
- (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
- NULL, READ_NEW | READ_DUMMY) == OK
+ readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0,
+ (linenr_T)MAXLNUM, NULL,
+ READ_NEW | READ_DUMMY);
+ newbuf->b_locked--;
+ if (readfile_result == OK
&& !got_int
&& !(curbuf->b_flags & BF_NEW)) {
failed = FALSE;
@@ -4000,6 +4102,7 @@ enum {
QF_GETLIST_ITEMS = 0x2,
QF_GETLIST_NR = 0x4,
QF_GETLIST_WINID = 0x8,
+ QF_GETLIST_CONTEXT = 0x10,
QF_GETLIST_ALL = 0xFF
};
@@ -4009,42 +4112,72 @@ enum {
int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
{
qf_info_T *qi = &ql_info;
+ dictitem_T *di;
if (wp != NULL) {
qi = GET_LOC_LIST(wp);
if (qi == NULL) {
+ // If querying for the size of the location list, return 0
+ if (((di = tv_dict_find(what, S_LEN("nr"))) != NULL)
+ && (di->di_tv.v_type == VAR_STRING)
+ && strequal((const char *)di->di_tv.vval.v_string, "$")) {
+ return tv_dict_add_nr(retdict, S_LEN("nr"), 0);
+ }
return FAIL;
}
}
int status = OK;
- dictitem_T *di;
int flags = QF_GETLIST_NONE;
int qf_idx = qi->qf_curlist; // default is the current list
if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {
// Use the specified quickfix/location list
if (di->di_tv.v_type == VAR_NUMBER) {
- qf_idx = (int)di->di_tv.vval.v_number - 1;
- if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
+ // for zero use the current list
+ if (di->di_tv.vval.v_number != 0) {
+ qf_idx = (int)di->di_tv.vval.v_number - 1;
+ if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
+ return FAIL;
+ }
+ } else if (qi->qf_listcount == 0) { // stack is empty
return FAIL;
}
flags |= QF_GETLIST_NR;
+ } else if (di->di_tv.v_type == VAR_STRING
+ && strequal((const char *)di->di_tv.vval.v_string, "$")) {
+ // Get the last quickfix list number
+ if (qi->qf_listcount > 0) {
+ qf_idx = qi->qf_listcount - 1;
+ } else {
+ qf_idx = -1; // Quickfix stack is empty
+ }
+ flags |= QF_GETLIST_NR;
} else {
return FAIL;
}
}
- if (tv_dict_find(what, S_LEN("all")) != NULL) {
- flags |= QF_GETLIST_ALL;
- }
+ if (qf_idx != -1) {
+ if (tv_dict_find(what, S_LEN("all")) != NULL) {
+ flags |= QF_GETLIST_ALL;
+ }
- if (tv_dict_find(what, S_LEN("title")) != NULL) {
- flags |= QF_GETLIST_TITLE;
- }
+ if (tv_dict_find(what, S_LEN("title")) != NULL) {
+ flags |= QF_GETLIST_TITLE;
+ }
+
+ if (tv_dict_find(what, S_LEN("winid")) != NULL) {
+ flags |= QF_GETLIST_WINID;
+ }
+
+ if (tv_dict_find(what, S_LEN("context")) != NULL) {
+ flags |= QF_GETLIST_CONTEXT;
+ }
- if (tv_dict_find(what, S_LEN("winid")) != NULL) {
- flags |= QF_GETLIST_WINID;
+ if (tv_dict_find(what, S_LEN("items")) != NULL) {
+ flags |= QF_GETLIST_ITEMS;
+ }
}
if (flags & QF_GETLIST_TITLE) {
@@ -4063,39 +4196,60 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle);
}
}
+ if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
+ list_T *l = tv_list_alloc(kListLenMayKnow);
+ (void)get_errorlist(wp, qf_idx, l);
+ tv_dict_add_list(retdict, S_LEN("items"), l);
+ }
+
+ if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
+ if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
+ di = tv_dict_item_alloc_len(S_LEN("context"));
+ if (di != NULL) {
+ tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
+ if (tv_dict_add(retdict, di) == FAIL) {
+ tv_dict_item_free(di);
+ }
+ }
+ } else {
+ status = tv_dict_add_str(retdict, S_LEN("context"), "");
+ }
+ }
return status;
}
/// Add list of entries to quickfix/location list. Each list entry is
/// a dictionary with item information.
-static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title,
- int action)
+static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
+ char_u *title, int action)
{
- listitem_T *li;
dict_T *d;
qfline_T *old_last = NULL;
int retval = OK;
bool did_bufnr_emsg = false;
- if (action == ' ' || qi->qf_curlist == qi->qf_listcount) {
+ if (action == ' ' || qf_idx == qi->qf_listcount) {
// make place for a new list
qf_new_list(qi, title);
- } else if (action == 'a' && qi->qf_lists[qi->qf_curlist].qf_count > 0) {
+ qf_idx = qi->qf_curlist;
+ } else if (action == 'a' && qi->qf_lists[qf_idx].qf_count > 0) {
// Adding to existing list, use last entry.
- old_last = qi->qf_lists[qi->qf_curlist].qf_last;
+ old_last = qi->qf_lists[qf_idx].qf_last;
} else if (action == 'r') {
- qf_free(qi, qi->qf_curlist);
- qf_store_title(qi, title);
+ qf_free_items(qi, qf_idx);
+ qf_store_title(qi, qf_idx, title);
}
- for (li = list->lv_first; li != NULL; li = li->li_next) {
- if (li->li_tv.v_type != VAR_DICT)
- continue; /* Skip non-dict items */
+ TV_LIST_ITER_CONST(list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) {
+ continue; // Skip non-dict items.
+ }
- d = li->li_tv.vval.v_dict;
- if (d == NULL)
+ d = TV_LIST_ITEM_TV(li)->vval.v_dict;
+ if (d == NULL) {
continue;
+ }
char *const filename = tv_dict_get_string(d, "filename", true);
int bufnum = (int)tv_dict_get_number(d, "bufnr");
@@ -4126,7 +4280,13 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title,
bufnum = 0;
}
+ // If the 'valid' field is present it overrules the detected value.
+ if (tv_dict_find(d, "valid", -1) != NULL) {
+ valid = (int)tv_dict_get_number(d, "valid");
+ }
+
int status = qf_add_entry(qi,
+ qf_idx,
NULL, // dir
(char_u *)filename,
bufnum,
@@ -4147,18 +4307,18 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title,
retval = FAIL;
break;
}
- }
+ });
- if (qi->qf_lists[qi->qf_curlist].qf_index == 0) {
+ if (qi->qf_lists[qf_idx].qf_index == 0) {
// no valid entry
- qi->qf_lists[qi->qf_curlist].qf_nonevalid = true;
+ qi->qf_lists[qf_idx].qf_nonevalid = true;
} else {
- qi->qf_lists[qi->qf_curlist].qf_nonevalid = false;
+ qi->qf_lists[qf_idx].qf_nonevalid = false;
}
if (action != 'a') {
- qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
- if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
- qi->qf_lists[qi->qf_curlist].qf_index = 1;
+ qi->qf_lists[qf_idx].qf_ptr = qi->qf_lists[qf_idx].qf_start;
+ if (qi->qf_lists[qf_idx].qf_count > 0) {
+ qi->qf_lists[qf_idx].qf_index = 1;
}
}
@@ -4181,14 +4341,28 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action)
if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {
// Use the specified quickfix/location list
if (di->di_tv.v_type == VAR_NUMBER) {
- qf_idx = (int)di->di_tv.vval.v_number - 1;
- if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
+ // for zero use the current list
+ if (di->di_tv.vval.v_number != 0) {
+ qf_idx = (int)di->di_tv.vval.v_number - 1;
+ }
+
+ if ((action == ' ' || action == 'a') && qf_idx == qi->qf_listcount) {
+ // When creating a new list, accept qf_idx pointing to the next
+ // non-available list
+ newlist = true;
+ } else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
return FAIL;
+ } else {
+ newlist = false; // use the specified list
}
+ } else if (di->di_tv.v_type == VAR_STRING
+ && strequal((const char *)di->di_tv.vval.v_string, "$")
+ && qi->qf_listcount > 0) {
+ qf_idx = qi->qf_listcount - 1;
+ newlist = false;
} else {
return FAIL;
}
- newlist = false; // use the specified list
}
if (newlist) {
@@ -4207,10 +4381,85 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action)
retval = OK;
}
}
+ if ((di = tv_dict_find(what, S_LEN("items"))) != NULL) {
+ if (di->di_tv.v_type == VAR_LIST) {
+ char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title);
+
+ retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list,
+ title_save, action == ' ' ? 'a' : action);
+ xfree(title_save);
+ }
+ }
+
+ if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) {
+ tv_free(qi->qf_lists[qf_idx].qf_ctx);
+
+ typval_T *ctx = xcalloc(1, sizeof(typval_T));
+ tv_copy(&di->di_tv, ctx);
+ qi->qf_lists[qf_idx].qf_ctx = ctx;
+ }
return retval;
}
+// Find the non-location list window with the specified location list.
+static win_T * find_win_with_ll(qf_info_T *qi)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) {
+ return wp;
+ }
+ }
+
+ return NULL;
+}
+
+// Free the entire quickfix/location list stack.
+// If the quickfix/location list window is open, then clear it.
+static void qf_free_stack(win_T *wp, qf_info_T *qi)
+{
+ win_T *qfwin = qf_find_win(qi);
+
+ if (qfwin != NULL) {
+ // If the quickfix/location list window is open, then clear it
+ if (qi->qf_curlist < qi->qf_listcount) {
+ qf_free(qi, qi->qf_curlist);
+ }
+ qf_update_buffer(qi, NULL);
+ }
+
+ win_T *llwin = NULL;
+ win_T *orig_wp = wp;
+ if (wp != NULL && IS_LL_WINDOW(wp)) {
+ // If in the location list window, then use the non-location list
+ // window with this location list (if present)
+ llwin = find_win_with_ll(qi);
+ if (llwin != NULL) {
+ wp = llwin;
+ }
+ }
+
+ qf_free_all(wp);
+ if (wp == NULL) {
+ // quickfix list
+ qi->qf_curlist = 0;
+ qi->qf_listcount = 0;
+ } else if (IS_LL_WINDOW(orig_wp)) {
+ // If the location list window is open, then create a new empty location
+ // list
+ qf_info_T *new_ll = ll_new_list();
+
+ // first free the list reference in the location list window
+ ll_free_all(&orig_wp->w_llist_ref);
+
+ orig_wp->w_llist_ref = new_ll;
+ if (llwin != NULL) {
+ llwin->w_llist = new_ll;
+ new_ll->qf_refcount++;
+ }
+ }
+}
+
// Populate the quickfix list with the items supplied in the list
// of dictionaries. "title" will be copied to w:quickfix_title
// "action" is 'a' for add, 'r' for replace. Otherwise create a new list.
@@ -4224,15 +4473,54 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,
qi = ll_get_or_alloc_list(wp);
}
- if (what != NULL) {
+ if (action == 'f') {
+ // Free the entire quickfix or location list stack
+ qf_free_stack(wp, qi);
+ } else if (what != NULL) {
retval = qf_set_properties(qi, what, action);
} else {
- retval = qf_add_entries(qi, list, title, action);
+ retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
}
return retval;
}
+static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
+{
+ bool abort = false;
+
+ for (int i = 0; i < LISTCOUNT && !abort; i++) {
+ typval_T *ctx = qi->qf_lists[i].qf_ctx;
+ if (ctx != NULL && ctx->v_type != VAR_NUMBER
+ && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) {
+ abort = set_ref_in_item(ctx, copyID, NULL, NULL);
+ }
+ }
+
+ return abort;
+}
+
+/// Mark the context of the quickfix list and the location lists (if present) as
+/// "in use". So that garabage collection doesn't free the context.
+bool set_ref_in_quickfix(int copyID)
+{
+ bool abort = mark_quickfix_ctx(&ql_info, copyID);
+ if (abort) {
+ return abort;
+ }
+
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_llist != NULL) {
+ abort = mark_quickfix_ctx(win->w_llist, copyID);
+ if (abort) {
+ return abort;
+ }
+ }
+ }
+
+ return abort;
+}
+
/*
* ":[range]cbuffer [bufnr]" command.
* ":[range]caddbuffer [bufnr]" command.
@@ -4310,7 +4598,7 @@ void ex_cbuffer(exarg_T *eap)
if (qf_init_ext(qi, NULL, buf, NULL, p_efm,
(eap->cmdidx != CMD_caddbuffer
&& eap->cmdidx != CMD_laddbuffer),
- eap->line1, eap->line2, qf_title) > 0) {
+ eap->line1, eap->line2, qf_title, NULL) > 0) {
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
curbuf->b_fname, true, curbuf);
@@ -4371,11 +4659,11 @@ void ex_cexpr(exarg_T *eap)
typval_T tv;
if (eval0(eap->arg, &tv, NULL, true) != FAIL) {
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
- || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) {
+ || tv.v_type == VAR_LIST) {
if (qf_init_ext(qi, NULL, NULL, &tv, p_efm,
(eap->cmdidx != CMD_caddexpr
&& eap->cmdidx != CMD_laddexpr),
- (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) {
+ (linenr_T)0, (linenr_T)0, *eap->cmdlinep, NULL) > 0) {
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
curbuf->b_fname, true, curbuf);
@@ -4446,6 +4734,9 @@ void ex_helpgrep(exarg_T *eap)
}
}
+ // Autocommands may change the list. Save it for later comparison
+ qf_info_T *save_qi = qi;
+
regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING);
regmatch.rm_ic = FALSE;
if (regmatch.regprog != NULL) {
@@ -4506,6 +4797,7 @@ void ex_helpgrep(exarg_T *eap)
line[--l] = NUL;
if (qf_add_entry(qi,
+ qi->qf_curlist,
NULL, // dir
fnames[fi],
0,
@@ -4558,10 +4850,11 @@ void ex_helpgrep(exarg_T *eap)
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
- curbuf->b_fname, TRUE, curbuf);
- if (!new_qi && qi != &ql_info && qf_find_buf(qi) == NULL)
- /* autocommands made "qi" invalid */
+ curbuf->b_fname, true, curbuf);
+ if (!new_qi && qi != save_qi && qf_find_buf(qi) == NULL) {
+ // autocommands made "qi" invalid
return;
+ }
}
/* Jump to first match. */
diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c
index 18d453cbe9..df9394fbb2 100644
--- a/src/nvim/rbuffer.c
+++ b/src/nvim/rbuffer.c
@@ -121,7 +121,7 @@ char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL
{
if (!buf->size) {
*read_count = 0;
- return NULL;
+ return buf->read_ptr;
}
if (buf->read_ptr < buf->write_ptr) {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 5448cc7131..e4de43b49e 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -232,17 +232,17 @@
#define LAST_NL NUPPER + ADD_NL
#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
-#define MOPEN 80 /* -89 Mark this point in input as start of
- * \( subexpr. MOPEN + 0 marks start of
- * match. */
-#define MCLOSE 90 /* -99 Analogous to MOPEN. MCLOSE + 0 marks
- * end of match. */
-#define BACKREF 100 /* -109 node Match same string again \1-\9 */
-
-# define ZOPEN 110 /* -119 Mark this point in input as start of
- * \z( subexpr. */
-# define ZCLOSE 120 /* -129 Analogous to ZOPEN. */
-# define ZREF 130 /* -139 node Match external submatch \z1-\z9 */
+#define MOPEN 80 // -89 Mark this point in input as start of
+ // \( … \) subexpr. MOPEN + 0 marks start of
+ // match.
+#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks
+ // end of match.
+#define BACKREF 100 // -109 node Match same string again \1-\9.
+
+# define ZOPEN 110 // -119 Mark this point in input as start of
+ // \z( … \) subexpr.
+# define ZCLOSE 120 // -129 Analogous to ZOPEN.
+# define ZREF 130 // -139 node Match external submatch \z1-\z9
#define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */
@@ -458,18 +458,15 @@ static int toggle_Magic(int x)
/* Used for an error (down from) vim_regcomp(): give the error message, set
* rc_did_emsg and return NULL */
-#define EMSG_RET_NULL(m) return (EMSG(m), rc_did_emsg = TRUE, (void *)NULL)
-#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = TRUE, FAIL)
-#define EMSG2_RET_NULL(m, \
- c) return (EMSG2((m), \
- (c) ? "" : "\\"), rc_did_emsg = TRUE, \
- (void *)NULL)
-#define EMSG2_RET_FAIL(m, \
- c) return (EMSG2((m), \
- (c) ? "" : "\\"), rc_did_emsg = TRUE, \
- FAIL)
+#define EMSG_RET_NULL(m) return (EMSG(m), rc_did_emsg = true, (void *)NULL)
+#define IEMSG_RET_NULL(m) return (IEMSG(m), rc_did_emsg = true, (void *)NULL)
+#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = true, FAIL)
+#define EMSG2_RET_NULL(m, c) \
+ return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
+#define EMSG2_RET_FAIL(m, c) \
+ return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
#define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \
- "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
+ "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
#define MAX_LIMIT (32767L << 16L)
@@ -1891,8 +1888,8 @@ static char_u *regatom(int *flagp)
case Magic(')'):
if (one_exactly)
EMSG_ONE_RET_NULL;
- EMSG_RET_NULL(_(e_internal)); /* Supposed to be caught earlier. */
- /* NOTREACHED */
+ IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier.
+ // NOTREACHED
case Magic('='):
case Magic('?'):
@@ -3172,61 +3169,56 @@ static int need_clear_zsubexpr = FALSE; /* extmatch subexpressions
int regnarrate = 0;
#endif
-/*
- * Internal copy of 'ignorecase'. It is set at each call to vim_regexec().
- * Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern
- * contains '\c' or '\C' the value is overruled.
- */
-static int ireg_ic;
-
-/*
- * Similar to ireg_ic, but only for 'combining' characters. Set with \Z flag
- * in the regexp. Defaults to false, always.
- */
-static int ireg_icombine;
-
-/*
- * Copy of "rmm_maxcol": maximum column to search for a match. Zero when
- * there is no maximum.
- */
-static colnr_T ireg_maxcol;
-
-/*
- * Sometimes need to save a copy of a line. Since alloc()/free() is very
- * slow, we keep one allocated piece of memory and only re-allocate it when
- * it's too small. It's freed in bt_regexec_both() when finished.
- */
+// Sometimes need to save a copy of a line. Since alloc()/free() is very
+// slow, we keep one allocated piece of memory and only re-allocate it when
+// it's too small. It's freed in bt_regexec_both() when finished.
static char_u *reg_tofree = NULL;
static unsigned reg_tofreelen;
-/*
- * These variables are set when executing a regexp to speed up the execution.
- * Which ones are set depends on whether a single-line or multi-line match is
- * done:
- * single-line multi-line
- * reg_match &regmatch_T NULL
- * reg_mmatch NULL &regmmatch_T
- * reg_startp reg_match->startp <invalid>
- * reg_endp reg_match->endp <invalid>
- * reg_startpos <invalid> reg_mmatch->startpos
- * reg_endpos <invalid> reg_mmatch->endpos
- * reg_win NULL window in which to search
- * reg_buf curbuf buffer in which to search
- * reg_firstlnum <invalid> first line in which to search
- * reg_maxline 0 last line nr
- * reg_line_lbr FALSE or TRUE FALSE
- */
-static regmatch_T *reg_match;
-static regmmatch_T *reg_mmatch;
-static char_u **reg_startp = NULL;
-static char_u **reg_endp = NULL;
-static lpos_T *reg_startpos = NULL;
-static lpos_T *reg_endpos = NULL;
-static win_T *reg_win;
-static buf_T *reg_buf;
-static linenr_T reg_firstlnum;
-static linenr_T reg_maxline;
-static int reg_line_lbr; /* "\n" in string is line break */
+// Structure used to store the execution state of the regex engine.
+// Which ones are set depends on whether a single-line or multi-line match is
+// done:
+// single-line multi-line
+// reg_match &regmatch_T NULL
+// reg_mmatch NULL &regmmatch_T
+// reg_startp reg_match->startp <invalid>
+// reg_endp reg_match->endp <invalid>
+// reg_startpos <invalid> reg_mmatch->startpos
+// reg_endpos <invalid> reg_mmatch->endpos
+// reg_win NULL window in which to search
+// reg_buf curbuf buffer in which to search
+// reg_firstlnum <invalid> first line in which to search
+// reg_maxline 0 last line nr
+// reg_line_lbr false or true false
+typedef struct {
+ regmatch_T *reg_match;
+ regmmatch_T *reg_mmatch;
+ char_u **reg_startp;
+ char_u **reg_endp;
+ lpos_T *reg_startpos;
+ lpos_T *reg_endpos;
+ win_T *reg_win;
+ buf_T *reg_buf;
+ linenr_T reg_firstlnum;
+ linenr_T reg_maxline;
+ bool reg_line_lbr; // "\n" in string is line break
+
+ // Internal copy of 'ignorecase'. It is set at each call to vim_regexec().
+ // Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern
+ // contains '\c' or '\C' the value is overruled.
+ bool reg_ic;
+
+ // Similar to rex.reg_ic, but only for 'combining' characters. Set with \Z
+ // flag in the regexp. Defaults to false, always.
+ bool reg_icombine;
+
+ // Copy of "rmm_maxcol": maximum column to search for a match. Zero when
+ // there is no maximum.
+ colnr_T reg_maxcol;
+} regexec_T;
+
+static regexec_T rex;
+static bool rex_in_use = false;
/*
* "regstack" and "backpos" are used by regmatch(). They are kept over calls
@@ -3268,14 +3260,16 @@ void free_regexp_stuff(void)
*/
static char_u *reg_getline(linenr_T lnum)
{
- /* when looking behind for a match/no-match lnum is negative. But we
- * can't go before line 1 */
- if (reg_firstlnum + lnum < 1)
+ // when looking behind for a match/no-match lnum is negative. But we
+ // can't go before line 1
+ if (rex.reg_firstlnum + lnum < 1) {
return NULL;
- if (lnum > reg_maxline)
- /* Must have matched the "\n" in the last line. */
+ }
+ if (lnum > rex.reg_maxline) {
+ // Must have matched the "\n" in the last line.
return (char_u *)"";
- return ml_get_buf(reg_buf, reg_firstlnum + lnum, FALSE);
+ }
+ return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false);
}
static regsave_T behind_pos;
@@ -3285,9 +3279,8 @@ static char_u *reg_endzp[NSUBEXP]; /* and end of \z(...\) matches */
static lpos_T reg_startzpos[NSUBEXP]; /* idem, beginning pos */
static lpos_T reg_endzpos[NSUBEXP]; /* idem, end pos */
-/* TRUE if using multi-line regexp. */
-#define REG_MULTI (reg_match == NULL)
-
+// TRUE if using multi-line regexp.
+#define REG_MULTI (rex.reg_match == NULL)
/*
* Match a regexp against a string.
@@ -3305,21 +3298,62 @@ bt_regexec_nl (
bool line_lbr
)
{
- reg_match = rmp;
- reg_mmatch = NULL;
- reg_maxline = 0;
- reg_line_lbr = line_lbr;
- reg_buf = curbuf;
- reg_win = NULL;
- ireg_ic = rmp->rm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = 0;
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_line_lbr = line_lbr;
+ rex.reg_buf = curbuf;
+ rex.reg_win = NULL;
+ rex.reg_ic = rmp->rm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = 0;
long r = bt_regexec_both(line, col, NULL);
assert(r <= INT_MAX);
return (int)r;
}
+/// Wrapper around strchr which accounts for case-insensitive searches and
+/// non-ASCII characters.
+///
+/// This function is used a lot for simple searches, keep it fast!
+///
+/// @param s string to search
+/// @param c character to find in @a s
+///
+/// @return NULL if no match, otherwise pointer to the position in @a s
+static inline char_u *cstrchr(const char_u *const s, const int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (!rex.reg_ic) {
+ return vim_strchr(s, c);
+ }
+
+ // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is
+ // expected to be highly optimized.
+ if (c > 0x80) {
+ const int folded_c = utf_fold(c);
+ for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) {
+ if (utf_fold(utf_ptr2char(p)) == folded_c) {
+ return (char_u *)p;
+ }
+ }
+ return NULL;
+ }
+
+ int cc;
+ if (ASCII_ISUPPER(c)) {
+ cc = TOLOWER_ASC(c);
+ } else if (ASCII_ISLOWER(c)) {
+ cc = TOUPPER_ASC(c);
+ } else {
+ return vim_strchr(s, c);
+ }
+
+ char tofind[] = { (char)c, (char)cc, NUL };
+ return (char_u *)strpbrk((const char *)s, tofind);
+}
/// Matches a regexp against multiple lines.
/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
@@ -3336,16 +3370,16 @@ bt_regexec_nl (
static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
linenr_T lnum, colnr_T col, proftime_T *tm)
{
- reg_match = NULL;
- reg_mmatch = rmp;
- reg_buf = buf;
- reg_win = win;
- reg_firstlnum = lnum;
- reg_maxline = reg_buf->b_ml.ml_line_count - lnum;
- reg_line_lbr = FALSE;
- ireg_ic = rmp->rmm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = rmp->rmm_maxcol;
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = buf;
+ rex.reg_win = win;
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ rex.reg_ic = rmp->rmm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = rmp->rmm_maxcol;
return bt_regexec_both(NULL, col, tm);
}
@@ -3383,14 +3417,14 @@ static long bt_regexec_both(char_u *line,
}
if (REG_MULTI) {
- prog = (bt_regprog_T *)reg_mmatch->regprog;
+ prog = (bt_regprog_T *)rex.reg_mmatch->regprog;
line = reg_getline((linenr_T)0);
- reg_startpos = reg_mmatch->startpos;
- reg_endpos = reg_mmatch->endpos;
+ rex.reg_startpos = rex.reg_mmatch->startpos;
+ rex.reg_endpos = rex.reg_mmatch->endpos;
} else {
- prog = (bt_regprog_T *)reg_match->regprog;
- reg_startp = reg_match->startp;
- reg_endp = reg_match->endp;
+ prog = (bt_regprog_T *)rex.reg_match->regprog;
+ rex.reg_startp = rex.reg_match->startp;
+ rex.reg_endp = rex.reg_match->endp;
}
/* Be paranoid... */
@@ -3403,19 +3437,22 @@ static long bt_regexec_both(char_u *line,
if (prog_magic_wrong())
goto theend;
- /* If the start column is past the maximum column: no need to try. */
- if (ireg_maxcol > 0 && col >= ireg_maxcol)
+ // If the start column is past the maximum column: no need to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
goto theend;
+ }
- /* If pattern contains "\c" or "\C": overrule value of ireg_ic */
- if (prog->regflags & RF_ICASE)
- ireg_ic = TRUE;
- else if (prog->regflags & RF_NOICASE)
- ireg_ic = FALSE;
+ // If pattern contains "\c" or "\C": overrule value of rex.reg_ic
+ if (prog->regflags & RF_ICASE) {
+ rex.reg_ic = true;
+ } else if (prog->regflags & RF_NOICASE) {
+ rex.reg_ic = false;
+ }
- /* If pattern contains "\Z" overrule value of ireg_icombine */
- if (prog->regflags & RF_ICOMBINE)
- ireg_icombine = TRUE;
+ // If pattern contains "\Z" overrule value of rex.reg_icombine
+ if (prog->regflags & RF_ICOMBINE) {
+ rex.reg_icombine = true;
+ }
/* If there is a "must appear" string, look for it. */
if (prog->regmust != NULL) {
@@ -3429,7 +3466,7 @@ static long bt_regexec_both(char_u *line,
// This is used very often, esp. for ":global". Use two versions of
// the loop to avoid overhead of conditions.
- if (!ireg_ic) {
+ if (!rex.reg_ic) {
while ((s = vim_strchr(s, c)) != NULL) {
if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) {
break; // Found it.
@@ -3463,7 +3500,7 @@ static long bt_regexec_both(char_u *line,
c = regline[col];
if (prog->regstart == NUL
|| prog->regstart == c
- || (ireg_ic
+ || (rex.reg_ic
&& (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c)))
|| (c < 255 && prog->regstart < 255
&& mb_tolower(prog->regstart) == mb_tolower(c))))) {
@@ -3485,8 +3522,8 @@ static long bt_regexec_both(char_u *line,
col = (int)(s - regline);
}
- /* Check for maximum column to try. */
- if (ireg_maxcol > 0 && col >= ireg_maxcol) {
+ // Check for maximum column to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
retval = 0;
break;
}
@@ -3583,21 +3620,24 @@ static long regtry(bt_regprog_T *prog, colnr_T col)
cleanup_subexpr();
if (REG_MULTI) {
- if (reg_startpos[0].lnum < 0) {
- reg_startpos[0].lnum = 0;
- reg_startpos[0].col = col;
+ if (rex.reg_startpos[0].lnum < 0) {
+ rex.reg_startpos[0].lnum = 0;
+ rex.reg_startpos[0].col = col;
+ }
+ if (rex.reg_endpos[0].lnum < 0) {
+ rex.reg_endpos[0].lnum = reglnum;
+ rex.reg_endpos[0].col = (int)(reginput - regline);
+ } else {
+ // Use line number of "\ze".
+ reglnum = rex.reg_endpos[0].lnum;
}
- if (reg_endpos[0].lnum < 0) {
- reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = (int)(reginput - regline);
- } else
- /* Use line number of "\ze". */
- reglnum = reg_endpos[0].lnum;
} else {
- if (reg_startp[0] == NULL)
- reg_startp[0] = regline + col;
- if (reg_endp[0] == NULL)
- reg_endp[0] = reginput;
+ if (rex.reg_startp[0] == NULL) {
+ rex.reg_startp[0] = regline + col;
+ }
+ if (rex.reg_endp[0] == NULL) {
+ rex.reg_endp[0] = reginput;
+ }
}
/* Package any found \z(...\) matches for export. Default is none. */
unref_extmatch(re_extmatch_out);
@@ -3632,36 +3672,33 @@ static long regtry(bt_regprog_T *prog, colnr_T col)
}
-/*
- * Get class of previous character.
- */
+// Get class of previous character.
static int reg_prev_class(void)
{
if (reginput > regline) {
return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline,
reginput - 1),
- reg_buf->b_chartab);
+ rex.reg_buf->b_chartab);
}
return -1;
}
-/*
- * Return TRUE if the current reginput position matches the Visual area.
- */
+// Return TRUE if the current reginput position matches the Visual area.
static int reg_match_visual(void)
{
pos_T top, bot;
linenr_T lnum;
colnr_T col;
- win_T *wp = reg_win == NULL ? curwin : reg_win;
+ win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win;
int mode;
colnr_T start, end;
colnr_T start2, end2;
- /* Check if the buffer is the current buffer. */
- if (reg_buf != curbuf || VIsual.lnum == 0)
- return FALSE;
+ // Check if the buffer is the current buffer.
+ if (rex.reg_buf != curbuf || VIsual.lnum == 0) {
+ return false;
+ }
if (VIsual_active) {
if (lt(VIsual, wp->w_cursor)) {
@@ -3682,9 +3719,10 @@ static int reg_match_visual(void)
}
mode = curbuf->b_visual.vi_mode;
}
- lnum = reglnum + reg_firstlnum;
- if (lnum < top.lnum || lnum > bot.lnum)
- return FALSE;
+ lnum = reglnum + rex.reg_firstlnum;
+ if (lnum < top.lnum || lnum > bot.lnum) {
+ return false;
+ }
if (mode == 'v') {
col = (colnr_T)(reginput - regline);
@@ -3803,11 +3841,11 @@ regmatch (
next = regnext(scan);
op = OP(scan);
- /* Check for character class with NL added. */
- if (!reg_line_lbr && WITH_NL(op) && REG_MULTI
- && *reginput == NUL && reglnum <= reg_maxline) {
+ // Check for character class with NL added.
+ if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI
+ && *reginput == NUL && reglnum <= rex.reg_maxline) {
reg_nextline();
- } else if (reg_line_lbr && WITH_NL(op) && *reginput == '\n') {
+ } else if (rex.reg_line_lbr && WITH_NL(op) && *reginput == '\n') {
ADVANCE_REGINPUT();
} else {
if (WITH_NL(op))
@@ -3828,26 +3866,29 @@ regmatch (
break;
case RE_BOF:
- /* We're not at the beginning of the file when below the first
- * line where we started, not at the start of the line or we
- * didn't start at the first line of the buffer. */
+ // We're not at the beginning of the file when below the first
+ // line where we started, not at the start of the line or we
+ // didn't start at the first line of the buffer.
if (reglnum != 0 || reginput != regline
- || (REG_MULTI && reg_firstlnum > 1))
+ || (REG_MULTI && rex.reg_firstlnum > 1)) {
status = RA_NOMATCH;
+ }
break;
case RE_EOF:
- if (reglnum != reg_maxline || c != NUL)
+ if (reglnum != rex.reg_maxline || c != NUL) {
status = RA_NOMATCH;
+ }
break;
case CURSOR:
- /* Check if the buffer is in a window and compare the
- * reg_win->w_cursor position to the match position. */
- if (reg_win == NULL
- || (reglnum + reg_firstlnum != reg_win->w_cursor.lnum)
- || ((colnr_T)(reginput - regline) != reg_win->w_cursor.col))
+ // Check if the buffer is in a window and compare the
+ // rex.reg_win->w_cursor position to the match position.
+ if (rex.reg_win == NULL
+ || (reglnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum)
+ || ((colnr_T)(reginput - regline) != rex.reg_win->w_cursor.col)) {
status = RA_NOMATCH;
+ }
break;
case RE_MARK:
@@ -3857,19 +3898,20 @@ regmatch (
int cmp = OPERAND(scan)[1];
pos_T *pos;
- pos = getmark_buf(reg_buf, mark, FALSE);
- if (pos == NULL /* mark doesn't exist */
- || pos->lnum <= 0 /* mark isn't set in reg_buf */
- || (pos->lnum == reglnum + reg_firstlnum
+ pos = getmark_buf(rex.reg_buf, mark, false);
+ if (pos == NULL // mark doesn't exist
+ || pos->lnum <= 0 // mark isn't set in reg_buf
+ || (pos->lnum == reglnum + rex.reg_firstlnum
? (pos->col == (colnr_T)(reginput - regline)
? (cmp == '<' || cmp == '>')
: (pos->col < (colnr_T)(reginput - regline)
? cmp != '>'
: cmp != '<'))
- : (pos->lnum < reglnum + reg_firstlnum
+ : (pos->lnum < reglnum + rex.reg_firstlnum
? cmp != '>'
- : cmp != '<')))
+ : cmp != '<'))) {
status = RA_NOMATCH;
+ }
}
break;
@@ -3879,11 +3921,12 @@ regmatch (
break;
case RE_LNUM:
- assert(reglnum + reg_firstlnum >= 0
- && (uintmax_t)(reglnum + reg_firstlnum) <= UINT32_MAX);
- if (!REG_MULTI || !re_num_cmp((uint32_t)(reglnum + reg_firstlnum),
- scan))
+ assert(reglnum + rex.reg_firstlnum >= 0
+ && (uintmax_t)(reglnum + rex.reg_firstlnum) <= UINT32_MAX);
+ if (!REG_MULTI
+ || !re_num_cmp((uint32_t)(reglnum + rex.reg_firstlnum), scan)) {
status = RA_NOMATCH;
+ }
break;
case RE_COL:
@@ -3894,11 +3937,13 @@ regmatch (
break;
case RE_VCOL:
- if (!re_num_cmp(win_linetabsize(reg_win == NULL ? curwin : reg_win,
+ if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
+ ? curwin : rex.reg_win,
regline,
(colnr_T)(reginput - regline)) + 1,
- scan))
+ scan)) {
status = RA_NOMATCH;
+ }
break;
case BOW: /* \<word; reginput points to w */
@@ -3908,17 +3953,18 @@ regmatch (
int this_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
if (this_class <= 1) {
status = RA_NOMATCH; // Not on a word at all.
} else if (reg_prev_class() == this_class) {
status = RA_NOMATCH; // Previous char is in same word.
}
} else {
- if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline
- && vim_iswordc_buf(reginput[-1
- ], reg_buf)))
+ if (!vim_iswordc_buf(c, rex.reg_buf)
+ || (reginput > regline
+ && vim_iswordc_buf(reginput[-1], rex.reg_buf))) {
status = RA_NOMATCH;
+ }
}
break;
@@ -3929,15 +3975,16 @@ regmatch (
int this_class, prev_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
prev_class = reg_prev_class();
if (this_class == prev_class
|| prev_class == 0 || prev_class == 1)
status = RA_NOMATCH;
} else {
- if (!vim_iswordc_buf(reginput[-1], reg_buf)
- || (reginput[0] != NUL && vim_iswordc_buf(c, reg_buf)))
+ if (!vim_iswordc_buf(reginput[-1], rex.reg_buf)
+ || (reginput[0] != NUL && vim_iswordc_buf(c, rex.reg_buf))) {
status = RA_NOMATCH;
+ }
}
break; /* Matched with EOW */
@@ -3964,17 +4011,20 @@ regmatch (
break;
case KWORD:
- if (!vim_iswordp_buf(reginput, reg_buf))
+ if (!vim_iswordp_buf(reginput, rex.reg_buf)) {
status = RA_NOMATCH;
- else
+ } else {
ADVANCE_REGINPUT();
+ }
break;
case SKWORD:
- if (ascii_isdigit(*reginput) || !vim_iswordp_buf(reginput, reg_buf))
+ if (ascii_isdigit(*reginput)
+ || !vim_iswordp_buf(reginput, rex.reg_buf)) {
status = RA_NOMATCH;
- else
+ } else {
ADVANCE_REGINPUT();
+ }
break;
case FNAME:
@@ -4139,7 +4189,7 @@ regmatch (
opnd = OPERAND(scan);
// Inline the first byte, for speed.
if (*opnd != *reginput
- && (!ireg_ic
+ && (!rex.reg_ic
|| (!enc_utf8
&& mb_tolower(*opnd) != mb_tolower(*reginput)))) {
status = RA_NOMATCH;
@@ -4147,8 +4197,8 @@ regmatch (
// match empty string always works; happens when "~" is
// empty.
} else {
- if (opnd[1] == NUL && !(enc_utf8 && ireg_ic)) {
- len = 1; /* matched a single byte above */
+ if (opnd[1] == NUL && !(enc_utf8 && rex.reg_ic)) {
+ len = 1; // matched a single byte above
} else {
// Need to match first byte again for multi-byte.
len = (int)STRLEN(opnd);
@@ -4160,7 +4210,7 @@ regmatch (
// follows (skips over all composing chars).
if (status != RA_NOMATCH && enc_utf8
&& UTF_COMPOSINGLIKE(reginput, reginput + len)
- && !ireg_icombine
+ && !rex.reg_icombine
&& OP(next) != RE_COMPOSING) {
// raaron: This code makes a composing character get
// ignored, which is the correct behavior (sometimes)
@@ -4284,9 +4334,9 @@ regmatch (
status = RA_FAIL;
else {
rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &reg_startpos[no],
- &reg_startp[no]);
- /* We simply continue and handle the result when done. */
+ save_se(&rp->rs_un.sesave, &rex.reg_startpos[no],
+ &rex.reg_startp[no]);
+ // We simply continue and handle the result when done.
}
}
break;
@@ -4336,12 +4386,12 @@ regmatch (
no = op - MCLOSE;
cleanup_subexpr();
rp = regstack_push(RS_MCLOSE, scan);
- if (rp == NULL)
+ if (rp == NULL) {
status = RA_FAIL;
- else {
+ } else {
rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &reg_endpos[no], &reg_endp[no]);
- /* We simply continue and handle the result when done. */
+ save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]);
+ // We simply continue and handle the result when done.
}
}
break;
@@ -4384,41 +4434,40 @@ regmatch (
no = op - BACKREF;
cleanup_subexpr();
- if (!REG_MULTI) { /* Single-line regexp */
- if (reg_startp[no] == NULL || reg_endp[no] == NULL) {
- /* Backref was not set: Match an empty string. */
+ if (!REG_MULTI) { // Single-line regexp
+ if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) {
+ // Backref was not set: Match an empty string.
len = 0;
} else {
- /* Compare current input with back-ref in the same
- * line. */
- len = (int)(reg_endp[no] - reg_startp[no]);
- if (cstrncmp(reg_startp[no], reginput, &len) != 0)
+ // Compare current input with back-ref in the same line.
+ len = (int)(rex.reg_endp[no] - rex.reg_startp[no]);
+ if (cstrncmp(rex.reg_startp[no], reginput, &len) != 0) {
status = RA_NOMATCH;
+ }
}
- } else { /* Multi-line regexp */
- if (reg_startpos[no].lnum < 0 || reg_endpos[no].lnum < 0) {
- /* Backref was not set: Match an empty string. */
+ } else { // Multi-line regexp
+ if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) {
+ // Backref was not set: Match an empty string.
len = 0;
} else {
- if (reg_startpos[no].lnum == reglnum
- && reg_endpos[no].lnum == reglnum) {
- /* Compare back-ref within the current line. */
- len = reg_endpos[no].col - reg_startpos[no].col;
- if (cstrncmp(regline + reg_startpos[no].col,
- reginput, &len) != 0)
+ if (rex.reg_startpos[no].lnum == reglnum
+ && rex.reg_endpos[no].lnum == reglnum) {
+ // Compare back-ref within the current line.
+ len = rex.reg_endpos[no].col - rex.reg_startpos[no].col;
+ if (cstrncmp(regline + rex.reg_startpos[no].col,
+ reginput, &len) != 0) {
status = RA_NOMATCH;
+ }
} else {
- /* Messy situation: Need to compare between two
- * lines. */
- int r = match_with_backref(
- reg_startpos[no].lnum,
- reg_startpos[no].col,
- reg_endpos[no].lnum,
- reg_endpos[no].col,
- &len);
-
- if (r != RA_MATCH)
+ // Messy situation: Need to compare between two lines.
+ int r = match_with_backref(rex.reg_startpos[no].lnum,
+ rex.reg_startpos[no].col,
+ rex.reg_endpos[no].lnum,
+ rex.reg_endpos[no].col,
+ &len);
+ if (r != RA_MATCH) {
status = r;
+ }
}
}
}
@@ -4482,7 +4531,7 @@ regmatch (
brace_max[no] = OPERAND_MAX(scan);
brace_count[no] = 0;
} else {
- EMSG(_(e_internal)); /* Shouldn't happen */
+ internal_error("BRACE_LIMITS");
status = RA_FAIL;
}
}
@@ -4558,7 +4607,7 @@ regmatch (
*/
if (OP(next) == EXACTLY) {
rst.nextb = *OPERAND(next);
- if (ireg_ic) {
+ if (rex.reg_ic) {
if (mb_isupper(rst.nextb)) {
rst.nextb_ic = mb_tolower(rst.nextb);
} else {
@@ -4665,13 +4714,14 @@ regmatch (
break;
case NEWL:
- if ((c != NUL || !REG_MULTI || reglnum > reg_maxline
- || reg_line_lbr) && (c != '\n' || !reg_line_lbr))
+ if ((c != NUL || !REG_MULTI || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) {
status = RA_NOMATCH;
- else if (reg_line_lbr)
+ } else if (rex.reg_line_lbr) {
ADVANCE_REGINPUT();
- else
+ } else {
reg_nextline();
+ }
break;
case END:
@@ -4710,10 +4760,11 @@ regmatch (
break;
case RS_MOPEN:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- restore_se(&rp->rs_un.sesave, &reg_startpos[rp->rs_no],
- &reg_startp[rp->rs_no]);
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no],
+ &rex.reg_startp[rp->rs_no]);
+ }
regstack_pop(&scan);
break;
@@ -4726,10 +4777,11 @@ regmatch (
break;
case RS_MCLOSE:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- restore_se(&rp->rs_un.sesave, &reg_endpos[rp->rs_no],
- &reg_endp[rp->rs_no]);
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no],
+ &rex.reg_endp[rp->rs_no]);
+ }
regstack_pop(&scan);
break;
@@ -5109,10 +5161,11 @@ regrepeat (
++count;
mb_ptr_adv(scan);
}
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr || count == maxcount)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr || count == maxcount) {
break;
- ++count; /* count the line-break */
+ }
+ count++; // count the line-break
reg_nextline();
scan = reginput;
if (got_int)
@@ -5130,17 +5183,19 @@ regrepeat (
if (vim_isIDc(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
} else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
if (got_int)
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
+ }
++count;
}
break;
@@ -5152,22 +5207,25 @@ regrepeat (
case SKWORD:
case SKWORD + ADD_NL:
while (count < maxcount) {
- if (vim_iswordp_buf(scan, reg_buf)
+ if (vim_iswordp_buf(scan, rex.reg_buf)
&& (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
} else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
- ++count;
+ }
+ count++;
}
break;
@@ -5181,18 +5239,21 @@ regrepeat (
if (vim_isfilec(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
} else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
- ++count;
+ }
+ count++;
}
break;
@@ -5204,21 +5265,24 @@ regrepeat (
case SPRINT + ADD_NL:
while (count < maxcount) {
if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
+ }
} else if (vim_isprintc(PTR2CHAR(scan)) == 1
&& (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
- ++count;
+ }
+ count++;
}
break;
@@ -5229,9 +5293,10 @@ do_class:
while (count < maxcount) {
int l;
if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
if (got_int)
@@ -5240,12 +5305,13 @@ do_class:
if (testval != 0)
break;
scan += l;
- } else if ((class_tab[*scan] & mask) == testval)
- ++scan;
- else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ } else if ((class_tab[*scan] & mask) == testval) {
+ scan++;
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
+ }
++count;
}
break;
@@ -5323,10 +5389,10 @@ do_class:
{
int cu, cl;
- /* This doesn't do a multi-byte character, because a MULTIBYTECODE
- * would have been used for it. It does handle single-byte
- * characters, such as latin1. */
- if (ireg_ic) {
+ // This doesn't do a multi-byte character, because a MULTIBYTECODE
+ // would have been used for it. It does handle single-byte
+ // characters, such as latin1.
+ if (rex.reg_ic) {
cu = mb_toupper(*opnd);
cl = mb_tolower(*opnd);
while (count < maxcount && (*scan == cu || *scan == cl)) {
@@ -5350,17 +5416,19 @@ do_class:
/* Safety check (just in case 'encoding' was changed since
* compiling the program). */
if ((len = (*mb_ptr2len)(opnd)) > 1) {
- if (ireg_ic && enc_utf8)
+ if (rex.reg_ic && enc_utf8) {
cf = utf_fold(utf_ptr2char(opnd));
+ }
while (count < maxcount && (*mb_ptr2len)(scan) >= len) {
for (i = 0; i < len; ++i) {
if (opnd[i] != scan[i]) {
break;
}
}
- if (i < len && (!ireg_ic || !enc_utf8
- || utf_fold(utf_ptr2char(scan)) != cf))
+ if (i < len && (!rex.reg_ic || !enc_utf8
+ || utf_fold(utf_ptr2char(scan)) != cf)) {
break;
+ }
scan += len;
++count;
}
@@ -5378,18 +5446,21 @@ do_class:
while (count < maxcount) {
int len;
if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else if (has_mbyte && (len = (*mb_ptr2len)(scan)) > 1) {
- if ((cstrchr(opnd, (*mb_ptr2char)(scan)) == NULL) == testval)
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else if (has_mbyte && (len = (*mb_ptr2len)(scan)) > 1) {
+ if ((cstrchr(opnd, (*mb_ptr2char)(scan)) == NULL) == testval) {
break;
+ }
scan += len;
} else {
if ((cstrchr(opnd, *scan) == NULL) == testval)
@@ -5402,13 +5473,14 @@ do_class:
case NEWL:
while (count < maxcount
- && ((*scan == NUL && reglnum <= reg_maxline && !reg_line_lbr
- && REG_MULTI) || (*scan == '\n' && reg_line_lbr))) {
+ && ((*scan == NUL && reglnum <= rex.reg_maxline && !rex.reg_line_lbr
+ && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) {
count++;
- if (reg_line_lbr)
+ if (rex.reg_line_lbr) {
ADVANCE_REGINPUT();
- else
+ } else {
reg_nextline();
+ }
scan = reginput;
if (got_int)
break;
@@ -5458,10 +5530,11 @@ static int prog_magic_wrong(void)
{
regprog_T *prog;
- prog = REG_MULTI ? reg_mmatch->regprog : reg_match->regprog;
- if (prog->engine == &nfa_regengine)
- /* For NFA matcher we don't check the magic */
- return FALSE;
+ prog = REG_MULTI ? rex.reg_mmatch->regprog : rex.reg_match->regprog;
+ if (prog->engine == &nfa_regengine) {
+ // For NFA matcher we don't check the magic
+ return false;
+ }
if (UCHARAT(((bt_regprog_T *)prog)->program) != REGMAGIC) {
EMSG(_(e_re_corr));
@@ -5479,12 +5552,12 @@ static void cleanup_subexpr(void)
{
if (need_clear_subexpr) {
if (REG_MULTI) {
- /* Use 0xff to set lnum to -1 */
- memset(reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP);
- memset(reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ // Use 0xff to set lnum to -1
+ memset(rex.reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ memset(rex.reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP);
} else {
- memset(reg_startp, 0, sizeof(char_u *) * NSUBEXP);
- memset(reg_endp, 0, sizeof(char_u *) * NSUBEXP);
+ memset(rex.reg_startp, 0, sizeof(char_u *) * NSUBEXP);
+ memset(rex.reg_endp, 0, sizeof(char_u *) * NSUBEXP);
}
need_clear_subexpr = FALSE;
}
@@ -5513,17 +5586,17 @@ static void save_subexpr(regbehind_T *bp)
{
int i;
- /* When "need_clear_subexpr" is set we don't need to save the values, only
- * remember that this flag needs to be set again when restoring. */
+ // When "need_clear_subexpr" is set we don't need to save the values, only
+ // remember that this flag needs to be set again when restoring.
bp->save_need_clear_subexpr = need_clear_subexpr;
if (!need_clear_subexpr) {
for (i = 0; i < NSUBEXP; ++i) {
if (REG_MULTI) {
- bp->save_start[i].se_u.pos = reg_startpos[i];
- bp->save_end[i].se_u.pos = reg_endpos[i];
+ bp->save_start[i].se_u.pos = rex.reg_startpos[i];
+ bp->save_end[i].se_u.pos = rex.reg_endpos[i];
} else {
- bp->save_start[i].se_u.ptr = reg_startp[i];
- bp->save_end[i].se_u.ptr = reg_endp[i];
+ bp->save_start[i].se_u.ptr = rex.reg_startp[i];
+ bp->save_end[i].se_u.ptr = rex.reg_endp[i];
}
}
}
@@ -5541,11 +5614,11 @@ static void restore_subexpr(regbehind_T *bp)
if (!need_clear_subexpr) {
for (i = 0; i < NSUBEXP; ++i) {
if (REG_MULTI) {
- reg_startpos[i] = bp->save_start[i].se_u.pos;
- reg_endpos[i] = bp->save_end[i].se_u.pos;
+ rex.reg_startpos[i] = bp->save_start[i].se_u.pos;
+ rex.reg_endpos[i] = bp->save_end[i].se_u.pos;
} else {
- reg_startp[i] = bp->save_start[i].se_u.ptr;
- reg_endp[i] = bp->save_end[i].se_u.ptr;
+ rex.reg_startp[i] = bp->save_start[i].se_u.ptr;
+ rex.reg_endp[i] = bp->save_end[i].se_u.ptr;
}
}
}
@@ -5681,10 +5754,12 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
return RA_NOMATCH; /* doesn't match */
if (bytelen != NULL)
*bytelen += len;
- if (clnum == end_lnum)
- break; /* match and at end! */
- if (reglnum >= reg_maxline)
- return RA_NOMATCH; /* text too short */
+ if (clnum == end_lnum) {
+ break; // match and at end!
+ }
+ if (reglnum >= rex.reg_maxline) {
+ return RA_NOMATCH; // text too short
+ }
/* Advance to next line. */
reg_nextline();
@@ -6232,24 +6307,22 @@ static void mb_decompose(int c, int *c1, int *c2, int *c3)
}
}
-/*
- * Compare two strings, ignore case if ireg_ic set.
- * Return 0 if strings match, non-zero otherwise.
- * Correct the length "*n" when composing characters are ignored.
- */
+// Compare two strings, ignore case if rex.reg_ic set.
+// Return 0 if strings match, non-zero otherwise.
+// Correct the length "*n" when composing characters are ignored.
static int cstrncmp(char_u *s1, char_u *s2, int *n)
{
int result;
- if (!ireg_ic)
+ if (!rex.reg_ic) {
result = STRNCMP(s1, s2, *n);
- else {
+ } else {
assert(*n >= 0);
result = mb_strnicmp(s1, s2, (size_t)*n);
}
- /* if it failed and it's utf8 and we want to combineignore: */
- if (result != 0 && enc_utf8 && ireg_icombine) {
+ // if it failed and it's utf8 and we want to combineignore:
+ if (result != 0 && enc_utf8 && rex.reg_icombine) {
char_u *str1, *str2;
int c1, c2, c11, c12;
int junk;
@@ -6266,14 +6339,15 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
/* decompose the character if necessary, into 'base' characters
* because I don't care about Arabic, I will hard-code the Hebrew
* which I *do* care about! So sue me... */
- if (c1 != c2 && (!ireg_ic || utf_fold(c1) != utf_fold(c2))) {
- /* decomposition necessary? */
+ if (c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) {
+ // decomposition necessary?
mb_decompose(c1, &c11, &junk, &junk);
mb_decompose(c2, &c12, &junk, &junk);
c1 = c11;
c2 = c12;
- if (c11 != c12 && (!ireg_ic || utf_fold(c11) != utf_fold(c12)))
+ if (c11 != c12 && (!rex.reg_ic || utf_fold(c11) != utf_fold(c12))) {
break;
+ }
}
}
result = c2 - c1;
@@ -6284,42 +6358,6 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
return result;
}
-/*
- * cstrchr: This function is used a lot for simple searches, keep it fast!
- */
-static inline char_u *cstrchr(const char_u *const s, const int c)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
- FUNC_ATTR_ALWAYS_INLINE
-{
- if (!ireg_ic) {
- return vim_strchr(s, c);
- }
-
- // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is
- // expected to be highly optimized.
- if (c > 0x80) {
- const int folded_c = utf_fold(c);
- for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) {
- if (utf_fold(utf_ptr2char(p)) == folded_c) {
- return (char_u *)p;
- }
- }
- return NULL;
- }
-
- int cc;
- if (ASCII_ISUPPER(c)) {
- cc = TOLOWER_ASC(c);
- } else if (ASCII_ISLOWER(c)) {
- cc = TOUPPER_ASC(c);
- } else {
- return vim_strchr(s, c);
- }
-
- char tofind[] = { (char)c, (char)cc, NUL };
- return (char_u *)strpbrk((const char *)s, tofind);
-}
-
/***************************************************************
* regsub stuff *
***************************************************************/
@@ -6419,54 +6457,52 @@ char_u *regtilde(char_u *source, int magic)
static int can_f_submatch = FALSE; /* TRUE when submatch() can be used */
-/* These pointers are used instead of reg_match and reg_mmatch for
- * reg_submatch(). Needed for when the substitution string is an expression
- * that contains a call to substitute() and submatch(). */
-static regmatch_T *submatch_match;
-static regmmatch_T *submatch_mmatch;
-static linenr_T submatch_firstlnum;
-static linenr_T submatch_maxline;
-static int submatch_line_lbr;
+// These pointers are used for reg_submatch(). Needed for when the
+// substitution string is an expression that contains a call to substitute()
+// and submatch().
+typedef struct {
+ regmatch_T *sm_match;
+ regmmatch_T *sm_mmatch;
+ linenr_T sm_firstlnum;
+ linenr_T sm_maxline;
+ int sm_line_lbr;
+} regsubmatch_T;
+
+static regsubmatch_T rsm; // can only be used when can_f_submatch is true
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
/// vim_regsub_both().
static int fill_submatch_list(int argc, typval_T *argv, int argcount)
{
- listitem_T *li;
- int i;
- char_u *s;
-
if (argcount == 0) {
// called function doesn't take an argument
return 0;
}
// Relies on sl_list to be the first item in staticList10_T.
- init_static_list((staticList10_T *)(argv->vval.v_list));
+ tv_list_init_static10((staticList10_T *)argv->vval.v_list);
// There are always 10 list items in staticList10_T.
- li = argv->vval.v_list->lv_first;
- for (i = 0; i < 10; i++) {
- s = submatch_match->startp[i];
- if (s == NULL || submatch_match->endp[i] == NULL) {
+ listitem_T *li = tv_list_first(argv->vval.v_list);
+ for (int i = 0; i < 10; i++) {
+ char_u *s = rsm.sm_match->startp[i];
+ if (s == NULL || rsm.sm_match->endp[i] == NULL) {
s = NULL;
} else {
- s = vim_strnsave(s, (int)(submatch_match->endp[i] - s));
+ s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s));
}
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.vval.v_string = s;
- li = li->li_next;
+ TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
+ TV_LIST_ITEM_TV(li)->vval.v_string = s;
+ li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li);
}
return 1;
}
static void clear_submatch_list(staticList10_T *sl)
{
- int i;
-
- for (i = 0; i < 10; i++) {
- xfree(sl->sl_items[i].li_tv.vval.v_string);
- }
+ TV_LIST_ITER(&sl->sl_list, li, {
+ xfree(TV_LIST_ITEM_TV(li)->vval.v_string);
+ });
}
/// vim_regsub() - perform substitutions after a vim_regexec() or
@@ -6488,23 +6524,55 @@ static void clear_submatch_list(staticList10_T *sl)
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
int copy, int magic, int backslash)
{
- reg_match = rmp;
- reg_mmatch = NULL;
- reg_maxline = 0;
- reg_buf = curbuf;
- reg_line_lbr = true;
- return vim_regsub_both(source, expr, dest, copy, magic, backslash);
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_buf = curbuf;
+ rex.reg_line_lbr = true;
+ int result = vim_regsub_both(source, expr, dest, copy, magic, backslash);
+
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
+ return result;
}
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash)
{
- reg_match = NULL;
- reg_mmatch = rmp;
- reg_buf = curbuf; /* always works on the current buffer! */
- reg_firstlnum = lnum;
- reg_maxline = curbuf->b_ml.ml_line_count - lnum;
- reg_line_lbr = false;
- return vim_regsub_both(source, NULL, dest, copy, magic, backslash);
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = curbuf; // always works on the current buffer!
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash);
+
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
+ return result;
}
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
@@ -6533,8 +6601,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
dst = dest;
// When the substitute part starts with "\=" evaluate it as an expression.
- if (expr != NULL || (source[0] == '\\' && source[1] == '='
- && !can_f_submatch)) { // can't do this recursively
+ if (expr != NULL || (source[0] == '\\' && source[1] == '=')) {
// To make sure that the length doesn't change between checking the
// length and copying the string, and to speed up things, the
// resulting string is saved from the call with "copy" == FALSE to the
@@ -6547,56 +6614,50 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
eval_result = NULL;
}
} else {
- win_T *save_reg_win;
- int save_ireg_ic;
- bool prev_can_f_submatch = can_f_submatch;
+ int prev_can_f_submatch = can_f_submatch;
+ regsubmatch_T rsm_save;
xfree(eval_result);
- /* The expression may contain substitute(), which calls us
- * recursively. Make sure submatch() gets the text from the first
- * level. Don't need to save "reg_buf", because
- * vim_regexec_multi() can't be called recursively. */
- submatch_match = reg_match;
- submatch_mmatch = reg_mmatch;
- submatch_firstlnum = reg_firstlnum;
- submatch_maxline = reg_maxline;
- submatch_line_lbr = reg_line_lbr;
- save_reg_win = reg_win;
- save_ireg_ic = ireg_ic;
+ // The expression may contain substitute(), which calls us
+ // recursively. Make sure submatch() gets the text from the first
+ // level.
+ if (can_f_submatch) {
+ rsm_save = rsm;
+ }
can_f_submatch = true;
+ rsm.sm_match = rex.reg_match;
+ rsm.sm_mmatch = rex.reg_mmatch;
+ rsm.sm_firstlnum = rex.reg_firstlnum;
+ rsm.sm_maxline = rex.reg_maxline;
+ rsm.sm_line_lbr = rex.reg_line_lbr;
if (expr != NULL) {
typval_T argv[2];
int dummy;
typval_T rettv;
- staticList10_T matchList;
+ staticList10_T matchList = TV_LIST_STATIC10_INIT;
rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL;
- if (prev_can_f_submatch) {
- // can't do this recursively
- } else {
- argv[0].v_type = VAR_LIST;
- argv[0].vval.v_list = &matchList.sl_list;
- matchList.sl_list.lv_len = 0;
- if (expr->v_type == VAR_FUNC) {
- s = expr->vval.v_string;
- call_func(s, (int)STRLEN(s), &rettv, 1, argv,
- fill_submatch_list, 0L, 0L, &dummy,
- true, NULL, NULL);
- } else if (expr->v_type == VAR_PARTIAL) {
- partial_T *partial = expr->vval.v_partial;
-
- s = partial_name(partial);
- call_func(s, (int)STRLEN(s), &rettv, 1, argv,
- fill_submatch_list, 0L, 0L, &dummy,
- true, partial, NULL);
- }
- if (matchList.sl_list.lv_len > 0) {
- // fill_submatch_list() was called.
- clear_submatch_list(&matchList);
- }
+ argv[0].v_type = VAR_LIST;
+ argv[0].vval.v_list = &matchList.sl_list;
+ if (expr->v_type == VAR_FUNC) {
+ s = expr->vval.v_string;
+ call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ fill_submatch_list, 0L, 0L, &dummy,
+ true, NULL, NULL);
+ } else if (expr->v_type == VAR_PARTIAL) {
+ partial_T *partial = expr->vval.v_partial;
+
+ s = partial_name(partial);
+ call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ fill_submatch_list, 0L, 0L, &dummy,
+ true, partial, NULL);
+ }
+ if (tv_list_len(&matchList.sl_list) > 0) {
+ // fill_submatch_list() was called.
+ clear_submatch_list(&matchList);
}
char buf[NUMBUFLEN];
eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf);
@@ -6612,22 +6673,23 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
int had_backslash = FALSE;
for (s = eval_result; *s != NUL; mb_ptr_adv(s)) {
- /* Change NL to CR, so that it becomes a line break,
- * unless called from vim_regexec_nl().
- * Skip over a backslashed character. */
- if (*s == NL && !submatch_line_lbr)
+ // Change NL to CR, so that it becomes a line break,
+ // unless called from vim_regexec_nl().
+ // Skip over a backslashed character.
+ if (*s == NL && !rsm.sm_line_lbr) {
*s = CAR;
- else if (*s == '\\' && s[1] != NUL) {
- ++s;
+ } else if (*s == '\\' && s[1] != NUL) {
+ s++;
/* Change NL to CR here too, so that this works:
* :s/abc\\\ndef/\="aaa\\\nbbb"/ on text:
* abc\
* def
* Not when called from vim_regexec_nl().
*/
- if (*s == NL && !submatch_line_lbr)
+ if (*s == NL && !rsm.sm_line_lbr) {
*s = CAR;
- had_backslash = TRUE;
+ }
+ had_backslash = true;
}
}
if (had_backslash && backslash) {
@@ -6640,14 +6702,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
dst += STRLEN(eval_result);
}
- reg_match = submatch_match;
- reg_mmatch = submatch_mmatch;
- reg_firstlnum = submatch_firstlnum;
- reg_maxline = submatch_maxline;
- reg_line_lbr = submatch_line_lbr;
- reg_win = save_reg_win;
- ireg_ic = save_ireg_ic;
- can_f_submatch = FALSE;
+ can_f_submatch = prev_can_f_submatch;
+ if (can_f_submatch) {
+ rsm = rsm_save;
+ }
}
} else
while ((c = *src++) != NUL) {
@@ -6745,43 +6803,50 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
dst++;
} else {
if (REG_MULTI) {
- clnum = reg_mmatch->startpos[no].lnum;
- if (clnum < 0 || reg_mmatch->endpos[no].lnum < 0)
+ clnum = rex.reg_mmatch->startpos[no].lnum;
+ if (clnum < 0 || rex.reg_mmatch->endpos[no].lnum < 0) {
s = NULL;
- else {
- s = reg_getline(clnum) + reg_mmatch->startpos[no].col;
- if (reg_mmatch->endpos[no].lnum == clnum)
- len = reg_mmatch->endpos[no].col
- - reg_mmatch->startpos[no].col;
- else
+ } else {
+ s = reg_getline(clnum) + rex.reg_mmatch->startpos[no].col;
+ if (rex.reg_mmatch->endpos[no].lnum == clnum) {
+ len = rex.reg_mmatch->endpos[no].col
+ - rex.reg_mmatch->startpos[no].col;
+ } else {
len = (int)STRLEN(s);
+ }
}
} else {
- s = reg_match->startp[no];
- if (reg_match->endp[no] == NULL)
+ s = rex.reg_match->startp[no];
+ if (rex.reg_match->endp[no] == NULL) {
s = NULL;
- else
- len = (int)(reg_match->endp[no] - s);
+ } else {
+ len = (int)(rex.reg_match->endp[no] - s);
+ }
}
if (s != NULL) {
for (;; ) {
if (len == 0) {
if (REG_MULTI) {
- if (reg_mmatch->endpos[no].lnum == clnum)
+ if (rex.reg_mmatch->endpos[no].lnum == clnum) {
break;
- if (copy)
+ }
+ if (copy) {
*dst = CAR;
- ++dst;
+ }
+ dst++;
s = reg_getline(++clnum);
- if (reg_mmatch->endpos[no].lnum == clnum)
- len = reg_mmatch->endpos[no].col;
- else
+ if (rex.reg_mmatch->endpos[no].lnum == clnum) {
+ len = rex.reg_mmatch->endpos[no].col;
+ } else {
len = (int)STRLEN(s);
- } else
+ }
+ } else {
break;
- } else if (*s == NUL) { /* we hit NUL. */
- if (copy)
+ }
+ } else if (*s == NUL) { // we hit NUL.
+ if (copy) {
EMSG(_(e_re_damg));
+ }
goto exit;
} else {
if (backslash && (*s == CAR || *s == '\\')) {
@@ -6855,16 +6920,16 @@ exit:
static char_u *reg_getline_submatch(linenr_T lnum)
{
char_u *s;
- linenr_T save_first = reg_firstlnum;
- linenr_T save_max = reg_maxline;
+ linenr_T save_first = rex.reg_firstlnum;
+ linenr_T save_max = rex.reg_maxline;
- reg_firstlnum = submatch_firstlnum;
- reg_maxline = submatch_maxline;
+ rex.reg_firstlnum = rsm.sm_firstlnum;
+ rex.reg_maxline = rsm.sm_maxline;
s = reg_getline(lnum);
- reg_firstlnum = save_first;
- reg_maxline = save_max;
+ rex.reg_firstlnum = save_first;
+ rex.reg_maxline = save_max;
return s;
}
@@ -6883,39 +6948,41 @@ char_u *reg_submatch(int no)
if (!can_f_submatch || no < 0)
return NULL;
- if (submatch_match == NULL) {
+ if (rsm.sm_match == NULL) {
ssize_t len;
/*
* First round: compute the length and allocate memory.
* Second round: copy the text.
*/
- for (round = 1; round <= 2; ++round) {
- lnum = submatch_mmatch->startpos[no].lnum;
- if (lnum < 0 || submatch_mmatch->endpos[no].lnum < 0)
+ for (round = 1; round <= 2; round++) {
+ lnum = rsm.sm_mmatch->startpos[no].lnum;
+ if (lnum < 0 || rsm.sm_mmatch->endpos[no].lnum < 0) {
return NULL;
+ }
- s = reg_getline_submatch(lnum) + submatch_mmatch->startpos[no].col;
- if (s == NULL) /* anti-crash check, cannot happen? */
+ s = reg_getline_submatch(lnum) + rsm.sm_mmatch->startpos[no].col;
+ if (s == NULL) { // anti-crash check, cannot happen?
break;
- if (submatch_mmatch->endpos[no].lnum == lnum) {
- /* Within one line: take form start to end col. */
- len = submatch_mmatch->endpos[no].col
- - submatch_mmatch->startpos[no].col;
- if (round == 2)
+ }
+ if (rsm.sm_mmatch->endpos[no].lnum == lnum) {
+ // Within one line: take form start to end col.
+ len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col;
+ if (round == 2) {
STRLCPY(retval, s, len + 1);
- ++len;
+ }
+ len++;
} else {
- /* Multiple lines: take start line from start col, middle
- * lines completely and end line up to end col. */
- len = STRLEN(s);
+ // Multiple lines: take start line from start col, middle
+ // lines completely and end line up to end col.
+ len = (ssize_t)STRLEN(s);
if (round == 2) {
STRCPY(retval, s);
retval[len] = '\n';
}
- ++len;
- ++lnum;
- while (lnum < submatch_mmatch->endpos[no].lnum) {
+ len++;
+ lnum++;
+ while (lnum < rsm.sm_mmatch->endpos[no].lnum) {
s = reg_getline_submatch(lnum++);
if (round == 2)
STRCPY(retval + len, s);
@@ -6924,13 +6991,15 @@ char_u *reg_submatch(int no)
retval[len] = '\n';
++len;
}
- if (round == 2)
+ if (round == 2) {
STRNCPY(retval + len, reg_getline_submatch(lnum),
- submatch_mmatch->endpos[no].col);
- len += submatch_mmatch->endpos[no].col;
- if (round == 2)
- retval[len] = NUL;
- ++len;
+ rsm.sm_mmatch->endpos[no].col);
+ }
+ len += rsm.sm_mmatch->endpos[no].col;
+ if (round == 2) {
+ retval[len] = NUL; // -V595
+ }
+ len++;
}
if (retval == NULL) {
@@ -6938,11 +7007,12 @@ char_u *reg_submatch(int no)
}
}
} else {
- s = submatch_match->startp[no];
- if (s == NULL || submatch_match->endp[no] == NULL)
+ s = rsm.sm_match->startp[no];
+ if (s == NULL || rsm.sm_match->endp[no] == NULL) {
retval = NULL;
- else
- retval = vim_strnsave(s, (int)(submatch_match->endp[no] - s));
+ } else {
+ retval = vim_strnsave(s, (int)(rsm.sm_match->endp[no] - s));
+ }
}
return retval;
@@ -6964,17 +7034,17 @@ list_T *reg_submatch_list(int no)
list_T *list;
const char *s;
- if (submatch_match == NULL) {
- slnum = submatch_mmatch->startpos[no].lnum;
- elnum = submatch_mmatch->endpos[no].lnum;
+ if (rsm.sm_match == NULL) {
+ slnum = rsm.sm_mmatch->startpos[no].lnum;
+ elnum = rsm.sm_mmatch->endpos[no].lnum;
if (slnum < 0 || elnum < 0) {
return NULL;
}
- colnr_T scol = submatch_mmatch->startpos[no].col;
- colnr_T ecol = submatch_mmatch->endpos[no].col;
+ colnr_T scol = rsm.sm_mmatch->startpos[no].col;
+ colnr_T ecol = rsm.sm_mmatch->endpos[no].col;
- list = tv_list_alloc();
+ list = tv_list_alloc(elnum - slnum + 1);
s = (const char *)reg_getline_submatch(slnum) + scol;
if (slnum == elnum) {
@@ -6989,12 +7059,12 @@ list_T *reg_submatch_list(int no)
tv_list_append_string(list, s, ecol);
}
} else {
- s = (const char *)submatch_match->startp[no];
- if (s == NULL || submatch_match->endp[no] == NULL) {
+ s = (const char *)rsm.sm_match->startp[no];
+ if (s == NULL || rsm.sm_match->endp[no] == NULL) {
return NULL;
}
- list = tv_list_alloc();
- tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s);
+ list = tv_list_alloc(1);
+ tv_list_append_string(list, s, (const char *)rsm.sm_match->endp[no] - s);
}
return list;
@@ -7148,6 +7218,19 @@ static void report_re_switch(char_u *pat)
/// @return TRUE if there is a match, FALSE if not.
static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
{
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+ rex.reg_startp = NULL;
+ rex.reg_endp = NULL;
+ rex.reg_startpos = NULL;
+ rex.reg_endpos = NULL;
+
int result = rmp->regprog->engine->regexec_nl(rmp, line, col, nl);
// NFA engine aborted because it's very slow, use backtracking engine instead.
@@ -7169,6 +7252,11 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
p_re = save_p_re;
}
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
return result > 0;
}
@@ -7216,6 +7304,15 @@ long vim_regexec_multi(
proftime_T *tm /* timeout limit or NULL */
)
{
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+
int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
tm);
@@ -7239,5 +7336,10 @@ long vim_regexec_multi(
p_re = save_p_re;
}
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
return result <= 0 ? 0 : result;
}
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index 6426ee441b..b5d56e07fc 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -15,6 +15,8 @@
#include <stdbool.h>
#include "nvim/pos.h"
+#include "nvim/types.h"
+#include "nvim/profile.h"
/*
* The number of sub-matches is limited to 10.
@@ -41,18 +43,36 @@
#define NFA_ENGINE 2
typedef struct regengine regengine_T;
+typedef struct regprog regprog_T;
+typedef struct reg_extmatch reg_extmatch_T;
+
+/// Structure to be used for multi-line matching.
+/// Sub-match "no" starts in line "startpos[no].lnum" column "startpos[no].col"
+/// and ends in line "endpos[no].lnum" just before column "endpos[no].col".
+/// The line numbers are relative to the first line, thus startpos[0].lnum is
+/// always 0.
+/// When there is no match, the line number is -1.
+typedef struct {
+ regprog_T *regprog;
+ lpos_T startpos[NSUBEXP];
+ lpos_T endpos[NSUBEXP];
+ int rmm_ic;
+ colnr_T rmm_maxcol; /// when not zero: maximum column
+} regmmatch_T;
+
+#include "nvim/buffer_defs.h"
/*
* Structure returned by vim_regcomp() to pass on to vim_regexec().
* This is the general structure. For the actual matcher, two specific
* structures are used. See code below.
*/
-typedef struct regprog {
+struct regprog {
regengine_T *engine;
unsigned regflags;
unsigned re_engine; ///< Automatic, backtracking or NFA engine.
unsigned re_flags; ///< Second argument for vim_regcomp().
-} regprog_T;
+};
/*
* Structure used by the back track matcher.
@@ -126,30 +146,14 @@ typedef struct {
} regmatch_T;
/*
- * Structure to be used for multi-line matching.
- * Sub-match "no" starts in line "startpos[no].lnum" column "startpos[no].col"
- * and ends in line "endpos[no].lnum" just before column "endpos[no].col".
- * The line numbers are relative to the first line, thus startpos[0].lnum is
- * always 0.
- * When there is no match, the line number is -1.
- */
-typedef struct {
- regprog_T *regprog;
- lpos_T startpos[NSUBEXP];
- lpos_T endpos[NSUBEXP];
- int rmm_ic;
- colnr_T rmm_maxcol; /* when not zero: maximum column */
-} regmmatch_T;
-
-/*
* Structure used to store external references: "\z\(\)" to "\z\1".
* Use a reference count to avoid the need to copy this around. When it goes
* from 1 to zero the matches need to be freed.
*/
-typedef struct {
- short refcnt;
+struct reg_extmatch {
+ int16_t refcnt;
char_u *matches[NSUBEXP];
-} reg_extmatch_T;
+};
struct regengine {
regprog_T *(*regcomp)(char_u*, int);
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 24c156d2ba..c520ef5fb9 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -56,13 +56,13 @@ enum {
NFA_RANGE_MIN, /* low end of a range */
NFA_RANGE_MAX, /* high end of a range */
- NFA_CONCAT, /* concatenate two previous items (postfix
- * only) */
- NFA_OR, /* \| (postfix only) */
- NFA_STAR, /* greedy * (posfix only) */
- NFA_STAR_NONGREEDY, /* non-greedy * (postfix only) */
- NFA_QUEST, /* greedy \? (postfix only) */
- NFA_QUEST_NONGREEDY, /* non-greedy \? (postfix only) */
+ NFA_CONCAT, // concatenate two previous items (postfix
+ // only)
+ NFA_OR, // \| (postfix only)
+ NFA_STAR, // greedy * (postfix only)
+ NFA_STAR_NONGREEDY, // non-greedy * (postfix only)
+ NFA_QUEST, // greedy \? (postfix only)
+ NFA_QUEST_NONGREEDY, // non-greedy \? (postfix only)
NFA_BOL, /* ^ Begin line */
NFA_EOL, /* $ End line */
@@ -1263,7 +1263,7 @@ static int nfa_regatom(void)
rc_did_emsg = TRUE;
return FAIL;
}
- EMSGN("INTERNAL: Unknown character class char: %" PRId64, c);
+ IEMSGN("INTERNAL: Unknown character class char: %" PRId64, c);
return FAIL;
}
/* When '.' is followed by a composing char ignore the dot, so that
@@ -1988,7 +1988,7 @@ static int nfa_regpiece(void)
// The engine is very inefficient (uses too many states) when the maximum
// is much larger than the minimum and when the maximum is large. Bail out
// if we can use the other engine.
- if ((nfa_re_flags & RE_AUTO) && (maxval > minval + 200 || maxval > 500)) {
+ if ((nfa_re_flags & RE_AUTO) && (maxval > 500 || maxval > minval + 200)) {
return FAIL;
}
@@ -3913,7 +3913,7 @@ addstate (
int k;
int found = FALSE;
nfa_thread_T *thread;
- lpos_T save_lpos;
+ struct multipos save_multipos;
int save_in_use;
char_u *save_ptr;
int i;
@@ -4127,15 +4127,13 @@ skip_add:
/* avoid compiler warnings */
save_ptr = NULL;
- save_lpos.lnum = 0;
- save_lpos.col = 0;
+ memset(&save_multipos, 0, sizeof(save_multipos));
/* Set the position (with "off" added) in the subexpression. Save
* and restore it when it was in use. Otherwise fill any gap. */
if (REG_MULTI) {
if (subidx < sub->in_use) {
- save_lpos.lnum = sub->list.multi[subidx].start_lnum;
- save_lpos.col = sub->list.multi[subidx].start_col;
+ save_multipos = sub->list.multi[subidx];
save_in_use = -1;
} else {
save_in_use = sub->in_use;
@@ -4178,9 +4176,8 @@ skip_add:
sub = &subs->norm;
if (save_in_use == -1) {
- if (REG_MULTI){
- sub->list.multi[subidx].start_lnum = save_lpos.lnum;
- sub->list.multi[subidx].start_col = save_lpos.col;
+ if (REG_MULTI) {
+ sub->list.multi[subidx] = save_multipos;
}
else
sub->list.line[subidx].start = save_ptr;
@@ -4234,8 +4231,7 @@ skip_add:
if (sub->in_use <= subidx)
sub->in_use = subidx + 1;
if (REG_MULTI) {
- save_lpos.lnum = sub->list.multi[subidx].end_lnum;
- save_lpos.col = sub->list.multi[subidx].end_col;
+ save_multipos = sub->list.multi[subidx];
if (off == -1) {
sub->list.multi[subidx].end_lnum = reglnum + 1;
sub->list.multi[subidx].end_col = 0;
@@ -4249,9 +4245,8 @@ skip_add:
} else {
save_ptr = sub->list.line[subidx].end;
sub->list.line[subidx].end = reginput + off;
- /* avoid compiler warnings */
- save_lpos.lnum = 0;
- save_lpos.col = 0;
+ // avoid compiler warnings
+ memset(&save_multipos, 0, sizeof(save_multipos));
}
subs = addstate(l, state->out, subs, pim, off_arg);
@@ -4261,9 +4256,8 @@ skip_add:
else
sub = &subs->norm;
- if (REG_MULTI){
- sub->list.multi[subidx].end_lnum = save_lpos.lnum;
- sub->list.multi[subidx].end_col = save_lpos.col;
+ if (REG_MULTI) {
+ sub->list.multi[subidx] = save_multipos;
}
else
sub->list.line[subidx].end = save_ptr;
@@ -4419,8 +4413,8 @@ static int check_char_class(int class, int c)
break;
default:
- /* should not be here :P */
- EMSGN(_(e_ill_char_class), class);
+ // should not be here :P
+ IEMSGN(_(e_ill_char_class), class);
return FAIL;
}
return FAIL;
@@ -4888,7 +4882,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
int c2_len = PTR2LEN(s2);
int c2 = PTR2CHAR(s2);
- if ((c1 != c2 && (!ireg_ic || mb_tolower(c1) != mb_tolower(c2)))
+ if ((c1 != c2 && (!rex.reg_ic || mb_tolower(c1) != mb_tolower(c2)))
|| c1_len != c2_len) {
match = false;
break;
@@ -4901,13 +4895,13 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
&& !(enc_utf8 && utf_iscomposing(PTR2CHAR(s2)))) {
cleanup_subexpr();
if (REG_MULTI) {
- reg_startpos[0].lnum = reglnum;
- reg_startpos[0].col = col;
- reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = s2 - regline;
+ rex.reg_startpos[0].lnum = reglnum;
+ rex.reg_startpos[0].col = col;
+ rex.reg_endpos[0].lnum = reglnum;
+ rex.reg_endpos[0].col = s2 - regline;
} else {
- reg_startp[0] = regline + col;
- reg_endp[0] = s2;
+ rex.reg_startp[0] = regline + col;
+ rex.reg_endp[0] = s2;
}
return 1L;
}
@@ -5122,8 +5116,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_MATCH:
{
// If the match ends before a composing characters and
- // ireg_icombine is not set, that is not really a match.
- if (enc_utf8 && !ireg_icombine && utf_iscomposing(curc)) {
+ // rex.reg_icombine is not set, that is not really a match.
+ if (enc_utf8 && !rex.reg_icombine && utf_iscomposing(curc)) {
break;
}
nfa_match = true;
@@ -5406,15 +5400,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
int this_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
if (this_class <= 1) {
result = false;
} else if (reg_prev_class() == this_class) {
result = false;
}
- } else if (!vim_iswordc_buf(curc, reg_buf)
+ } else if (!vim_iswordc_buf(curc, rex.reg_buf)
|| (reginput > regline
- && vim_iswordc_buf(reginput[-1], reg_buf))) {
+ && vim_iswordc_buf(reginput[-1], rex.reg_buf))) {
result = false;
}
if (result) {
@@ -5431,15 +5425,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
int this_class, prev_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
prev_class = reg_prev_class();
if (this_class == prev_class
|| prev_class == 0 || prev_class == 1) {
result = false;
}
- } else if (!vim_iswordc_buf(reginput[-1], reg_buf)
+ } else if (!vim_iswordc_buf(reginput[-1], rex.reg_buf)
|| (reginput[0] != NUL
- && vim_iswordc_buf(curc, reg_buf))) {
+ && vim_iswordc_buf(curc, rex.reg_buf))) {
result = false;
}
if (result) {
@@ -5450,14 +5444,14 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_BOF:
if (reglnum == 0 && reginput == regline
- && (!REG_MULTI || reg_firstlnum == 1)) {
+ && (!REG_MULTI || rex.reg_firstlnum == 1)) {
add_here = true;
add_state = t->state->out;
}
break;
case NFA_EOF:
- if (reglnum == reg_maxline && curc == NUL) {
+ if (reglnum == rex.reg_maxline && curc == NUL) {
add_here = true;
add_state = t->state->out;
}
@@ -5481,7 +5475,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// (no preceding character).
len += mb_char2len(mc);
}
- if (ireg_icombine && len == 0) {
+ if (rex.reg_icombine && len == 0) {
// If \Z was present, then ignore composing characters.
// When ignoring the base character this always matches.
if (sta->c != curc) {
@@ -5532,14 +5526,14 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
case NFA_NEWL:
- if (curc == NUL && !reg_line_lbr && REG_MULTI
- && reglnum <= reg_maxline) {
+ if (curc == NUL && !rex.reg_line_lbr && REG_MULTI
+ && reglnum <= rex.reg_maxline) {
go_to_nextline = true;
// Pass -1 for the offset, which means taking the position
// at the start of the next line.
add_state = t->state->out;
add_off = -1;
- } else if (curc == '\n' && reg_line_lbr) {
+ } else if (curc == '\n' && rex.reg_line_lbr) {
// match \n as if it is an ordinary character
add_state = t->state->out;
add_off = 1;
@@ -5580,7 +5574,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
result = result_if_matched;
break;
}
- if (ireg_ic) {
+ if (rex.reg_ic) {
int curc_low = mb_tolower(curc);
int done = false;
@@ -5597,7 +5591,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
} else if (state->c < 0 ? check_char_class(state->c, curc)
: (curc == state->c
- || (ireg_ic && mb_tolower(curc)
+ || (rex.reg_ic && mb_tolower(curc)
== mb_tolower(state->c)))) {
result = result_if_matched;
break;
@@ -5645,13 +5639,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break;
case NFA_KWORD: // \k
- result = vim_iswordp_buf(reginput, reg_buf);
+ result = vim_iswordp_buf(reginput, rex.reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_SKWORD: // \K
result = !ascii_isdigit(curc)
- && vim_iswordp_buf(reginput, reg_buf);
+ && vim_iswordp_buf(reginput, rex.reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
@@ -5766,24 +5760,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break;
case NFA_LOWER_IC: // [a-z]
- result = ri_lower(curc) || (ireg_ic && ri_upper(curc));
+ result = ri_lower(curc) || (rex.reg_ic && ri_upper(curc));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_NLOWER_IC: // [^a-z]
result = curc != NUL
- && !(ri_lower(curc) || (ireg_ic && ri_upper(curc)));
+ && !(ri_lower(curc) || (rex.reg_ic && ri_upper(curc)));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_UPPER_IC: // [A-Z]
- result = ri_upper(curc) || (ireg_ic && ri_lower(curc));
+ result = ri_upper(curc) || (rex.reg_ic && ri_lower(curc));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_NUPPER_IC: // [^A-Z]
result = curc != NUL
- && !(ri_upper(curc) || (ireg_ic && ri_lower(curc)));
+ && !(ri_upper(curc) || (rex.reg_ic && ri_lower(curc)));
ADD_STATE_IF_MATCH(t->state);
break;
@@ -5857,13 +5851,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_LNUM_GT:
case NFA_LNUM_LT:
assert(t->state->val >= 0
- && !((reg_firstlnum > 0 && reglnum > LONG_MAX - reg_firstlnum)
- || (reg_firstlnum <0 && reglnum < LONG_MIN + reg_firstlnum))
- && reglnum + reg_firstlnum >= 0);
+ && !((rex.reg_firstlnum > 0
+ && reglnum > LONG_MAX - rex.reg_firstlnum)
+ || (rex.reg_firstlnum < 0
+ && reglnum < LONG_MIN + rex.reg_firstlnum))
+ && reglnum + rex.reg_firstlnum >= 0);
result = (REG_MULTI
&& nfa_re_num_cmp((uintmax_t)t->state->val,
t->state->c - NFA_LNUM,
- (uintmax_t)(reglnum + reg_firstlnum)));
+ (uintmax_t)(reglnum + rex.reg_firstlnum)));
if (result) {
add_here = true;
add_state = t->state->out;
@@ -5899,7 +5895,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
result = false;
- win_T *wp = reg_win == NULL ? curwin : reg_win;
+ win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win;
if (op == 1 && col - 1 > t->state->val && col > 100) {
long ts = wp->w_buffer->b_p_ts;
@@ -5926,18 +5922,18 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_MARK_GT:
case NFA_MARK_LT:
{
- pos_T *pos = getmark_buf(reg_buf, t->state->val, FALSE);
+ pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false);
// Compare the mark position to the match position.
result = (pos != NULL // mark doesn't exist
&& pos->lnum > 0 // mark isn't set in reg_buf
- && (pos->lnum == reglnum + reg_firstlnum
+ && (pos->lnum == reglnum + rex.reg_firstlnum
? (pos->col == (colnr_T)(reginput - regline)
? t->state->c == NFA_MARK
: (pos->col < (colnr_T)(reginput - regline)
? t->state->c == NFA_MARK_GT
: t->state->c == NFA_MARK_LT))
- : (pos->lnum < reglnum + reg_firstlnum
+ : (pos->lnum < reglnum + rex.reg_firstlnum
? t->state->c == NFA_MARK_GT
: t->state->c == NFA_MARK_LT)));
if (result) {
@@ -5948,10 +5944,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
case NFA_CURSOR:
- result = (reg_win != NULL
- && (reglnum + reg_firstlnum == reg_win->w_cursor.lnum)
+ result = (rex.reg_win != NULL
+ && (reglnum + rex.reg_firstlnum == rex.reg_win->w_cursor.lnum)
&& ((colnr_T)(reginput - regline)
- == reg_win->w_cursor.col));
+ == rex.reg_win->w_cursor.col));
if (result) {
add_here = true;
add_state = t->state->out;
@@ -5996,18 +5992,19 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
int c = t->state->c;
#ifdef REGEXP_DEBUG
- if (c < 0)
- EMSGN("INTERNAL: Negative state char: %" PRId64, c);
+ if (c < 0) {
+ IEMSGN("INTERNAL: Negative state char: %" PRId64, c);
+ }
#endif
result = (c == curc);
- if (!result && ireg_ic) {
+ if (!result && rex.reg_ic) {
result = mb_tolower(c) == mb_tolower(curc);
}
- // If ireg_icombine is not set only skip over the character
+ // If rex.reg_icombine is not set only skip over the character
// itself. When it is set skip over composing characters.
- if (result && enc_utf8 && !ireg_icombine) {
+ if (result && enc_utf8 && !rex.reg_icombine) {
clen = utf_ptr2len(reginput);
}
@@ -6115,8 +6112,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
&& ((toplevel
&& reglnum == 0
&& clen != 0
- && (ireg_maxcol == 0
- || (colnr_T)(reginput - regline) < ireg_maxcol))
+ && (rex.reg_maxcol == 0
+ || (colnr_T)(reginput - regline) < rex.reg_maxcol))
|| (nfa_endp != NULL
&& (REG_MULTI
? (reglnum < nfa_endp->se_u.pos.lnum
@@ -6151,7 +6148,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// Checking if the required start character matches is
// cheaper than adding a state that won't match.
c = PTR2CHAR(reginput + clen);
- if (c != prog->regstart && (!ireg_ic || mb_tolower(c)
+ if (c != prog->regstart && (!rex.reg_ic || mb_tolower(c)
!= mb_tolower(prog->regstart))) {
#ifdef REGEXP_DEBUG
fprintf(log_fd,
@@ -6277,34 +6274,37 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
cleanup_subexpr();
if (REG_MULTI) {
for (i = 0; i < subs.norm.in_use; i++) {
- reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum;
- reg_startpos[i].col = subs.norm.list.multi[i].start_col;
+ rex.reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum;
+ rex.reg_startpos[i].col = subs.norm.list.multi[i].start_col;
- reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum;
- reg_endpos[i].col = subs.norm.list.multi[i].end_col;
+ rex.reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum;
+ rex.reg_endpos[i].col = subs.norm.list.multi[i].end_col;
}
- if (reg_startpos[0].lnum < 0) {
- reg_startpos[0].lnum = 0;
- reg_startpos[0].col = col;
+ if (rex.reg_startpos[0].lnum < 0) {
+ rex.reg_startpos[0].lnum = 0;
+ rex.reg_startpos[0].col = col;
+ }
+ if (rex.reg_endpos[0].lnum < 0) {
+ // pattern has a \ze but it didn't match, use current end
+ rex.reg_endpos[0].lnum = reglnum;
+ rex.reg_endpos[0].col = (int)(reginput - regline);
+ } else {
+ // Use line number of "\ze".
+ reglnum = rex.reg_endpos[0].lnum;
}
- if (reg_endpos[0].lnum < 0) {
- /* pattern has a \ze but it didn't match, use current end */
- reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = (int)(reginput - regline);
- } else
- /* Use line number of "\ze". */
- reglnum = reg_endpos[0].lnum;
} else {
for (i = 0; i < subs.norm.in_use; i++) {
- reg_startp[i] = subs.norm.list.line[i].start;
- reg_endp[i] = subs.norm.list.line[i].end;
+ rex.reg_startp[i] = subs.norm.list.line[i].start;
+ rex.reg_endp[i] = subs.norm.list.line[i].end;
}
- if (reg_startp[0] == NULL)
- reg_startp[0] = regline + col;
- if (reg_endp[0] == NULL)
- reg_endp[0] = reginput;
+ if (rex.reg_startp[0] == NULL) {
+ rex.reg_startp[0] = regline + col;
+ }
+ if (rex.reg_endp[0] == NULL) {
+ rex.reg_endp[0] = reginput;
+ }
}
/* Package any found \z(...\) matches for export. Default is none. */
@@ -6358,14 +6358,14 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
colnr_T col = startcol;
if (REG_MULTI) {
- prog = (nfa_regprog_T *)reg_mmatch->regprog;
- line = reg_getline((linenr_T)0); /* relative to the cursor */
- reg_startpos = reg_mmatch->startpos;
- reg_endpos = reg_mmatch->endpos;
+ prog = (nfa_regprog_T *)rex.reg_mmatch->regprog;
+ line = reg_getline((linenr_T)0); // relative to the cursor
+ rex.reg_startpos = rex.reg_mmatch->startpos;
+ rex.reg_endpos = rex.reg_mmatch->endpos;
} else {
- prog = (nfa_regprog_T *)reg_match->regprog;
- reg_startp = reg_match->startp;
- reg_endp = reg_match->endp;
+ prog = (nfa_regprog_T *)rex.reg_match->regprog;
+ rex.reg_startp = rex.reg_match->startp;
+ rex.reg_endp = rex.reg_match->endp;
}
/* Be paranoid... */
@@ -6374,15 +6374,17 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
goto theend;
}
- /* If pattern contains "\c" or "\C": overrule value of ireg_ic */
- if (prog->regflags & RF_ICASE)
- ireg_ic = TRUE;
- else if (prog->regflags & RF_NOICASE)
- ireg_ic = FALSE;
+ // If pattern contains "\c" or "\C": overrule value of rex.reg_ic
+ if (prog->regflags & RF_ICASE) {
+ rex.reg_ic = true;
+ } else if (prog->regflags & RF_NOICASE) {
+ rex.reg_ic = false;
+ }
- /* If pattern contains "\Z" overrule value of ireg_icombine */
- if (prog->regflags & RF_ICOMBINE)
- ireg_icombine = TRUE;
+ // If pattern contains "\Z" overrule value of rex.reg_icombine
+ if (prog->regflags & RF_ICOMBINE) {
+ rex.reg_icombine = true;
+ }
regline = line;
reglnum = 0; /* relative to line */
@@ -6411,17 +6413,17 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
if (skip_to_start(prog->regstart, &col) == FAIL)
return 0L;
- /* If match_text is set it contains the full text that must match.
- * Nothing else to try. Doesn't handle combining chars well. */
- if (prog->match_text != NULL
- && !ireg_icombine
- )
+ // If match_text is set it contains the full text that must match.
+ // Nothing else to try. Doesn't handle combining chars well.
+ if (prog->match_text != NULL && !rex.reg_icombine) {
return find_match_text(col, prog->regstart, prog->match_text);
+ }
}
- /* If the start column is past the maximum column: no need to try. */
- if (ireg_maxcol > 0 && col >= ireg_maxcol)
+ // If the start column is past the maximum column: no need to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
goto theend;
+ }
nstate = prog->nstate;
for (i = 0; i < nstate; ++i) {
@@ -6461,12 +6463,13 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
* (and count its size). */
postfix = re2post();
if (postfix == NULL) {
- /* TODO: only give this error for debugging? */
- if (post_ptr >= post_end)
- EMSGN("Internal error: estimated max number "
- "of states insufficient: %" PRId64,
- post_end - post_start);
- goto fail; /* Cascaded (syntax?) error */
+ // TODO(vim): only give this error for debugging?
+ if (post_ptr >= post_end) {
+ IEMSGN("Internal error: estimated max number "
+ "of states insufficient: %" PRId64,
+ post_end - post_start);
+ }
+ goto fail; // Cascaded (syntax?) error
}
/*
@@ -6573,15 +6576,15 @@ nfa_regexec_nl (
bool line_lbr
)
{
- reg_match = rmp;
- reg_mmatch = NULL;
- reg_maxline = 0;
- reg_line_lbr = line_lbr;
- reg_buf = curbuf;
- reg_win = NULL;
- ireg_ic = rmp->rm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = 0;
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_line_lbr = line_lbr;
+ rex.reg_buf = curbuf;
+ rex.reg_win = NULL;
+ rex.reg_ic = rmp->rm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = 0;
return nfa_regexec_both(line, col, NULL);
}
@@ -6622,16 +6625,16 @@ nfa_regexec_nl (
static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
linenr_T lnum, colnr_T col, proftime_T *tm)
{
- reg_match = NULL;
- reg_mmatch = rmp;
- reg_buf = buf;
- reg_win = win;
- reg_firstlnum = lnum;
- reg_maxline = reg_buf->b_ml.ml_line_count - lnum;
- reg_line_lbr = FALSE;
- ireg_ic = rmp->rmm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = rmp->rmm_maxcol;
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = buf;
+ rex.reg_win = win;
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ rex.reg_ic = rmp->rmm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = rmp->rmm_maxcol;
return nfa_regexec_both(NULL, col, tm);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2f7fa8724f..22de08041a 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -86,6 +86,7 @@
#include <stdbool.h>
#include <string.h>
+#include "nvim/log.h"
#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/arabic.h"
@@ -138,11 +139,6 @@
* doesn't fit. */
#define W_ENDCOL(wp) (wp->w_wincol + wp->w_width)
-/*
- * The attributes that are actually active for writing to the screen.
- */
-static int screen_attr = 0;
-
static match_T search_hl; /* used for 'hlsearch' highlight matching */
static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */
@@ -188,8 +184,6 @@ void redraw_win_later(win_T *wp, int type)
void redraw_later_clear(void)
{
redraw_all_later(CLEAR);
- /* Use attributes that is very unlikely to appear in text. */
- screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE;
}
/*
@@ -344,8 +338,9 @@ void update_screen(int type)
if (need_highlight_changed)
highlight_changed();
- if (type == CLEAR) { /* first clear screen */
- screenclear(); /* will reset clear_cmdline */
+ if (type == CLEAR) { // first clear screen
+ screenclear(); // will reset clear_cmdline
+ cmdline_screen_cleared(); // clear external cmdline state
type = NOT_VALID;
}
@@ -691,12 +686,18 @@ static void win_update(win_T *wp)
if (wp->w_nrwidth != i) {
type = NOT_VALID;
wp->w_nrwidth = i;
- } else if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0) {
- /*
- * When there are both inserted/deleted lines and specific lines to be
- * redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
- * everything (only happens when redrawing is off for while).
- */
+
+ if (buf->terminal) {
+ terminal_resize(buf->terminal,
+ (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))),
+ (uint16_t)wp->w_height);
+ }
+ } else if (buf->b_mod_set
+ && buf->b_mod_xlines != 0
+ && wp->w_redraw_top != 0) {
+ // When there are both inserted/deleted lines and specific lines to be
+ // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
+ // everything (only happens when redrawing is off for while).
type = NOT_VALID;
} else {
/*
@@ -991,7 +992,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. */
@@ -1581,6 +1582,20 @@ static void win_update(win_T *wp)
got_int = save_got_int;
}
+/// Returns width of the signcolumn that should be used for the whole window
+///
+/// @param wp window we want signcolumn width from
+/// @return max width of signcolumn (cell unit)
+///
+/// @note Returns a constant for now but hopefully we can improve neovim so that
+/// the returned value width adapts to the maximum number of marks to draw
+/// for the window
+/// TODO(teto)
+int win_signcol_width(win_T *wp)
+{
+ // 2 is vim default value
+ return 2;
+}
/*
* Clear the rest of the window and mark the unused lines with "c1". use "c2"
@@ -1608,7 +1623,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
}
if (signcolumn_on(wp)) {
- int nn = n + 2;
+ int nn = n + win_signcol_width(wp);
/* draw the sign column left of the fold column */
if (nn > wp->w_width) {
@@ -1649,7 +1664,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
}
if (signcolumn_on(wp)) {
- int nn = n + 2;
+ int nn = n + win_signcol_width(wp);
/* draw the sign column after the fold column */
if (nn > wp->w_width) {
@@ -1761,12 +1776,13 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
* text */
RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_width - col);
- // If signs are being displayed, add two spaces.
+ // If signs are being displayed, add spaces.
if (signcolumn_on(wp)) {
len = wp->w_width - col;
if (len > 0) {
- if (len > 2) {
- len = 2;
+ int len_max = win_signcol_width(wp);
+ if (len > len_max) {
+ len = len_max;
}
copy_text_attr(off + col, (char_u *)" ", len,
win_hl_attr(wp, HLF_FL));
@@ -1923,10 +1939,15 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
if (fill_fold >= 0x80) {
ScreenLinesUC[off + col] = fill_fold;
ScreenLinesC[0][off + col] = 0;
- } else
+ ScreenLines[off + col] = 0x80; // avoid storing zero
+ } else {
ScreenLinesUC[off + col] = 0;
+ ScreenLines[off + col] = fill_fold;
+ }
+ col++;
+ } else {
+ ScreenLines[off + col++] = fill_fold;
}
- ScreenLines[off + col++] = fill_fold;
}
if (text != buf)
@@ -2111,16 +2132,16 @@ win_line (
bool nochange /* not updating for changed text */
)
{
- int col; /* visual column on screen */
- unsigned off; /* offset in ScreenLines/ScreenAttrs */
- int c = 0; /* init for GCC */
- long vcol = 0; /* virtual column (for tabs) */
+ int col = 0; // visual column on screen
+ unsigned off; // offset in ScreenLines/ScreenAttrs
+ int c = 0; // init for GCC
+ long vcol = 0; // virtual column (for tabs)
long vcol_sbr = -1; // virtual column after showbreak
- long vcol_prev = -1; /* "vcol" of previous character */
- char_u *line; /* current line */
- char_u *ptr; /* current position in "line" */
- int row; /* row in the window, excl w_winrow */
- int screen_row; /* row on the screen, incl w_winrow */
+ long vcol_prev = -1; // "vcol" of previous character
+ char_u *line; // current line
+ char_u *ptr; // current position in "line"
+ int row; // row in the window, excl w_winrow
+ int screen_row; // row on the screen, incl w_winrow
char_u extra[18]; /* line number and 'fdc' must fit in here */
int n_extra = 0; /* number of extra chars */
@@ -2194,17 +2215,17 @@ win_line (
int change_start = MAXCOL; /* first col of changed area */
int change_end = -1; /* last col of changed area */
colnr_T trailcol = MAXCOL; /* start of trailing spaces */
- int need_showbreak = FALSE;
- int line_attr = 0; /* attribute for the whole line */
- matchitem_T *cur; /* points to the match list */
- match_T *shl; /* points to search_hl or a match */
- int shl_flag; /* flag to indicate whether search_hl
- has been processed or not */
- int prevcol_hl_flag; /* flag to indicate whether prevcol
- equals startcol of search_hl or one
- of the matches */
- int prev_c = 0; /* previous Arabic character */
- int prev_c1 = 0; /* first composing char for prev_c */
+ int need_showbreak = false; // overlong line, skip first x chars
+ int line_attr = 0; // attribute for the whole line
+ matchitem_T *cur; // points to the match list
+ match_T *shl; // points to search_hl or a match
+ int shl_flag; // flag to indicate whether search_hl
+ // has been processed or not
+ int prevcol_hl_flag; // flag to indicate whether prevcol
+ // equals startcol of search_hl or one
+ // of the matches
+ int prev_c = 0; // previous Arabic character
+ int prev_c1 = 0; // first composing char for prev_c
int did_line_attr = 0;
bool search_attr_from_match = false; // if search_attr is from :match
@@ -2421,10 +2442,11 @@ win_line (
filler_lines = wp->w_topfill;
filler_todo = filler_lines;
- /* If this line has a sign with line highlighting set line_attr. */
+ // If this line has a sign with line highlighting set line_attr.
v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
- if (v != 0)
- line_attr = sign_get_attr((int)v, TRUE);
+ if (v != 0) {
+ line_attr = sign_get_attr((int)v, true);
+ }
// Highlight the current line in the quickfix window.
if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
@@ -2521,7 +2543,11 @@ win_line (
if (vcol > v) {
vcol -= c;
ptr = prev_ptr;
- n_skip = v - vcol;
+ // If the character fits on the screen, don't need to skip it.
+ // Except for a TAB.
+ if (((*mb_ptr2cells)(ptr) >= c || *ptr == TAB) && col == 0) {
+ n_skip = v - vcol;
+ }
}
/*
@@ -2653,9 +2679,9 @@ win_line (
cur = cur->next;
}
- /* Cursor line highlighting for 'cursorline' in the current window. Not
- * when Visual mode is active, because it's not clear what is selected
- * then. */
+ // Cursor line highlighting for 'cursorline' in the current window. Not
+ // when Visual mode is active, because it's not clear what is selected
+ // then.
if (wp->w_p_cul && lnum == wp->w_cursor.lnum
&& !(wp == curwin && VIsual_active)) {
if (line_attr != 0 && !(State & INSERT) && bt_quickfix(wp->w_buffer)
@@ -2704,11 +2730,14 @@ win_line (
draw_state = WL_FOLD;
if (fdc > 0) {
- // Draw the 'foldcolumn'.
- fill_foldcolumn(extra, wp, false, lnum);
+ // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
+ // already be in use.
+ xfree(p_extra_free);
+ p_extra_free = xmalloc(12 + 1);
+ fill_foldcolumn(p_extra_free, wp, false, lnum);
n_extra = fdc;
- p_extra = extra;
- p_extra[n_extra] = NUL;
+ p_extra_free[n_extra] = NUL;
+ p_extra = p_extra_free;
c_extra = NUL;
char_attr = win_hl_attr(wp, HLF_FC);
}
@@ -2721,18 +2750,26 @@ win_line (
* buffer or when using Netbeans. */
if (signcolumn_on(wp)) {
int text_sign;
- /* Draw two cells with the sign value or blank. */
+ // Draw cells with the sign value or blank.
c_extra = ' ';
char_attr = win_hl_attr(wp, HLF_SC);
n_extra = 2;
+ n_extra = win_signcol_width(wp);
if (row == startrow + filler_lines && filler_todo <= 0) {
text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT);
if (text_sign != 0) {
p_extra = sign_get_text(text_sign);
+ int symbol_blen = (int)STRLEN(p_extra);
if (p_extra != NULL) {
c_extra = NUL;
- n_extra = (int)STRLEN(p_extra);
+ // symbol(s) bytes + (filling spaces) (one byte each)
+ n_extra = symbol_blen +
+ (win_signcol_width(wp) - mb_string2cells(p_extra));
+ memset(extra, ' ', sizeof(extra));
+ STRNCPY(extra, p_extra, STRLEN(p_extra));
+ p_extra = extra;
+ p_extra[n_extra] = NUL;
}
char_attr = sign_get_attr(text_sign, FALSE);
}
@@ -2804,8 +2841,10 @@ win_line (
// draw 'breakindent': indent wrapped text accodringly
if (draw_state == WL_BRI - 1 && n_extra == 0) {
draw_state = WL_BRI;
- if (wp->w_p_bri && row != startrow && filler_lines == 0) {
- char_attr = wp->w_hl_attr_normal; // was: hl_attr(HLF_AT);
+ // if need_showbreak is set, breakindent also applies
+ if (wp->w_p_bri && (row != startrow || need_showbreak)
+ && filler_lines == 0) {
+ char_attr = wp->w_hl_attr_normal;
if (diff_hlf != (hlf_T)0) {
char_attr = win_hl_attr(wp, diff_hlf);
@@ -3120,14 +3159,15 @@ win_line (
}
--n_extra;
} else {
+ int c0;
+
if (p_extra_free != NULL) {
xfree(p_extra_free);
p_extra_free = NULL;
}
- /*
- * Get a character from the line itself.
- */
- c = *ptr;
+
+ // Get a character from the line itself.
+ c0 = c = *ptr;
if (has_mbyte) {
mb_c = c;
if (enc_utf8) {
@@ -3137,11 +3177,12 @@ win_line (
mb_utf8 = FALSE;
if (mb_l > 1) {
mb_c = utfc_ptr2char(ptr, u8cc);
- /* Overlong encoded ASCII or ASCII with composing char
- * is displayed normally, except a NUL. */
- if (mb_c < 0x80)
- c = mb_c;
- mb_utf8 = TRUE;
+ // Overlong encoded ASCII or ASCII with composing char
+ // is displayed normally, except a NUL.
+ if (mb_c < 0x80) {
+ c0 = c = mb_c;
+ }
+ mb_utf8 = true;
/* At start of the line we can have a composing char.
* Draw it as a space with a composing char. */
@@ -3335,10 +3376,11 @@ win_line (
/* Use nextline[] if possible, it has the start of the
* next line concatenated. */
- if ((prev_ptr - line) - nextlinecol >= 0)
- p = nextline + (prev_ptr - line) - nextlinecol;
- else
+ if ((prev_ptr - line) - nextlinecol >= 0) {
+ p = nextline + ((prev_ptr - line) - nextlinecol);
+ } else {
p = prev_ptr;
+ }
cap_col -= (int)(prev_ptr - line);
size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange);
assert(tmplen <= INT_MAX);
@@ -3405,10 +3447,8 @@ win_line (
char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);
}
- /*
- * Found last space before word: check for line break.
- */
- if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)) {
+ // Found last space before word: check for line break.
+ if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak(*ptr)) {
int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0;
char_u *p = ptr - (mb_off + 1);
// TODO: is passing p for start of the line OK?
@@ -3509,6 +3549,7 @@ win_line (
p = xmalloc(len + 1);
memset(p, ' ', len);
p[len] = NUL;
+ xfree(p_extra_free);
p_extra_free = p;
for (i = 0; i < tab_len; i++) {
mb_char2bytes(lcs_tab2, p);
@@ -3578,15 +3619,13 @@ win_line (
&& lcs_eol_one > 0) {
// Display a '$' after the line or highlight an extra
// character if the line break is included.
- // For a diff line the highlighting continues after the
- // "$".
+ // For a diff line the highlighting continues after the "$".
if (diff_hlf == (hlf_T)0 && line_attr == 0) {
- /* In virtualedit, visual selections may extend
- * beyond end of line. */
+ // In virtualedit, visual selections may extend beyond end of line.
if (area_highlighting && virtual_active()
- && tocol != MAXCOL && vcol < tocol)
+ && tocol != MAXCOL && vcol < tocol) {
n_extra = 0;
- else {
+ } else {
p_extra = at_end_str;
n_extra = 1;
c_extra = NUL;
@@ -3624,6 +3663,7 @@ win_line (
memset(p, ' ', n_extra);
STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1);
p[n_extra] = NUL;
+ xfree(p_extra_free);
p_extra_free = p_extra = p;
} else {
n_extra = byte2cells(c) - 1;
@@ -4018,10 +4058,10 @@ win_line (
if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
&& lnum != wp->w_cursor.lnum) {
vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_CUC));
+ char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
} else if (draw_color_col && VCOL_HLC == *color_cols) {
vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_MC));
+ char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
}
}
@@ -4230,7 +4270,6 @@ win_line (
* (regardless of the xn,am settings).
* Only do this if the cursor is on the current line
* (something has been written in it).
- * Don't do this for the GUI.
* Don't do this for double-width characters.
* Don't do this for a window not at the right screen border.
*/
@@ -4298,6 +4337,7 @@ win_line (
cap_col = 0;
}
+ xfree(p_extra_free);
return row;
}
@@ -4344,7 +4384,8 @@ static int char_needs_redraw(int off_from, int off_to, int cols)
&& comp_char_differs(off_from, off_to))
|| ((*mb_off2cells)(off_from, off_from + cols) > 1
&& ScreenLines[off_from + 1]
- != ScreenLines[off_to + 1])))));
+ != ScreenLines[off_to + 1])))
+ || p_wd < 0));
}
/*
@@ -4875,11 +4916,14 @@ void win_redr_status(win_T *wp)
int this_ru_col;
static int busy = FALSE;
- /* It's possible to get here recursively when 'statusline' (indirectly)
- * invokes ":redrawstatus". Simply ignore the call then. */
- if (busy)
+ // May get here recursively when 'statusline' (indirectly)
+ // invokes ":redrawstatus". Simply ignore the call then.
+ if (busy
+ // Also ignore if wildmenu is showing.
+ || (wild_menu_showing != 0 && !ui_is_external(kUIWildmenu))) {
return;
- busy = TRUE;
+ }
+ busy = true;
wp->w_redr_status = FALSE;
if (wp->w_status_height == 0) {
@@ -5821,52 +5865,29 @@ next_search_hl_pos(
return 0;
}
-static void screen_start_highlight(int attr)
-{
- screen_attr = attr;
- ui_start_highlight(attr);
-}
-
-void screen_stop_highlight(void)
-{
- ui_stop_highlight();
- screen_attr = 0;
-}
-
/*
* Put character ScreenLines["off"] on the screen at position "row" and "col",
* using the attributes from ScreenAttrs["off"].
*/
static void screen_char(unsigned off, int row, int col)
{
- int attr;
-
- /* Check for illegal values, just in case (could happen just after
- * resizing). */
- if (row >= screen_Rows || col >= screen_Columns)
+ // Check for illegal values, just in case (could happen just after resizing).
+ if (row >= screen_Rows || col >= screen_Columns) {
return;
+ }
- /* Outputting the last character on the screen may scrollup the screen.
- * Don't to it! Mark the character invalid (update it when scrolled up) */
+ // Outputting the last character on the screen may scrollup the screen.
+ // Don't to it! Mark the character invalid (update it when scrolled up)
+ // FIXME: The premise here is not actually true (cf. deferred wrap).
if (row == screen_Rows - 1 && col == screen_Columns - 1
- /* account for first command-line character in rightleft mode */
- && !cmdmsg_rl
- ) {
+ // account for first command-line character in rightleft mode
+ && !cmdmsg_rl) {
ScreenAttrs[off] = (sattr_T)-1;
return;
}
- /*
- * Stop highlighting first, so it's easier to move the cursor.
- */
- attr = ScreenAttrs[off];
- if (screen_attr != attr)
- screen_stop_highlight();
-
ui_cursor_goto(row, col);
-
- if (screen_attr != attr)
- screen_start_highlight(attr);
+ ui_set_highlight(ScreenAttrs[off]);
if (enc_utf8 && ScreenLinesUC[off] != 0) {
char_u buf[MB_MAXBYTES + 1];
@@ -5975,7 +5996,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
++off;
if (off < end_off) { /* something to be cleared */
col = off - LineOffset[row];
- screen_stop_highlight();
+ ui_clear_highlight();
ui_cursor_goto(row, col); // clear rest of this screen line
ui_call_eol_clear();
col = end_col - col;
@@ -6357,8 +6378,7 @@ static void screenclear2(void)
return;
}
- screen_stop_highlight(); /* don't want highlighting here */
-
+ ui_clear_highlight(); // don't want highlighting here
/* blank out ScreenLines */
for (i = 0; i < Rows; ++i) {
@@ -6442,125 +6462,39 @@ void setcursor(void)
}
}
-/*
- * insert 'line_count' lines at 'row' in window 'wp'
- * if 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated.
- * if 'mayclear' is TRUE the screen will be cleared if it is faster than
- * scrolling.
- * Returns FAIL if the lines are not inserted, OK for success.
- */
+/// Insert 'line_count' lines at 'row' in window 'wp'.
+/// If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated.
+/// If 'mayclear' is TRUE the screen will be cleared if it is faster than
+/// scrolling.
+/// Returns FAIL if the lines are not inserted, OK for success.
int win_ins_lines(win_T *wp, int row, int line_count, int invalid, int mayclear)
{
- int did_delete;
- int nextrow;
- int lastrow;
- int retval;
-
- if (invalid)
- wp->w_lines_valid = 0;
-
- if (wp->w_height < 5)
- return FAIL;
-
- if (line_count > wp->w_height - row)
- line_count = wp->w_height - row;
-
- retval = win_do_lines(wp, row, line_count, mayclear, FALSE);
- if (retval != MAYBE)
- return retval;
-
- /*
- * If there is a next window or a status line, we first try to delete the
- * lines at the bottom to avoid messing what is after the window.
- * If this fails and there are following windows, don't do anything to avoid
- * messing up those windows, better just redraw.
- */
- did_delete = FALSE;
- if (wp->w_next != NULL || wp->w_status_height) {
- if (screen_del_lines(0, wp->w_winrow + wp->w_height - line_count,
- line_count, (int)Rows, NULL) == OK)
- did_delete = TRUE;
- else if (wp->w_next)
- return FAIL;
- }
- /*
- * if no lines deleted, blank the lines that will end up below the window
- */
- if (!did_delete) {
- wp->w_redr_status = TRUE;
- redraw_cmdline = TRUE;
- nextrow = wp->w_winrow + wp->w_height + wp->w_status_height;
- lastrow = nextrow + line_count;
- if (lastrow > Rows)
- lastrow = Rows;
- screen_fill(nextrow - line_count, lastrow - line_count,
- wp->w_wincol, W_ENDCOL(wp),
- ' ', ' ', 0);
- }
-
- if (screen_ins_lines(0, wp->w_winrow + row, line_count, (int)Rows, NULL)
- == FAIL) {
- /* deletion will have messed up other windows */
- if (did_delete) {
- wp->w_redr_status = TRUE;
- win_rest_invalid(wp->w_next);
- }
+ if (wp->w_height < 5) {
return FAIL;
}
- return OK;
+ return win_do_lines(wp, row, line_count, invalid, mayclear, false);
}
-/*
- * delete "line_count" window lines at "row" in window "wp"
- * If "invalid" is TRUE curwin->w_lines[] is invalidated.
- * If "mayclear" is TRUE the screen will be cleared if it is faster than
- * scrolling
- * Return OK for success, FAIL if the lines are not deleted.
- */
+/// Delete "line_count" window lines at "row" in window "wp".
+/// If "invalid" is TRUE curwin->w_lines[] is invalidated.
+/// If "mayclear" is TRUE the screen will be cleared if it is faster than
+/// scrolling
+/// Return OK for success, FAIL if the lines are not deleted.
int win_del_lines(win_T *wp, int row, int line_count, int invalid, int mayclear)
{
- int retval;
-
- if (invalid)
- wp->w_lines_valid = 0;
-
- if (line_count > wp->w_height - row)
- line_count = wp->w_height - row;
-
- retval = win_do_lines(wp, row, line_count, mayclear, TRUE);
- if (retval != MAYBE)
- return retval;
-
- if (screen_del_lines(0, wp->w_winrow + row, line_count,
- (int)Rows, NULL) == FAIL) {
- return FAIL;
- }
-
- /*
- * If there are windows or status lines below, try to put them at the
- * correct place. If we can't do that, they have to be redrawn.
- */
- if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1) {
- if (screen_ins_lines(0, wp->w_winrow + wp->w_height - line_count,
- line_count, (int)Rows, NULL) == FAIL) {
- wp->w_redr_status = TRUE;
- win_rest_invalid(wp->w_next);
- }
- }
- /*
- * If this is the last window and there is no status line, redraw the
- * command line later.
- */
- else
- redraw_cmdline = TRUE;
- return OK;
+ return win_do_lines(wp, row, line_count, invalid, mayclear, true);
}
// Common code for win_ins_lines() and win_del_lines().
// Returns OK or FAIL when the work has been done.
-static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del)
+static int win_do_lines(win_T *wp, int row, int line_count,
+ int invalid, int mayclear, int del)
{
+ if (invalid) {
+ wp->w_lines_valid = 0;
+ }
+
if (!redrawing() || line_count <= 0) {
return FAIL;
}
@@ -6814,16 +6748,20 @@ int showmode(void)
if (p_ri)
MSG_PUTS_ATTR(_(" REVERSE"), attr);
MSG_PUTS_ATTR(_(" INSERT"), attr);
- } else if (restart_edit == 'I')
+ } else if (restart_edit == 'I' || restart_edit == 'i'
+ || restart_edit == 'a') {
MSG_PUTS_ATTR(_(" (insert)"), attr);
- else if (restart_edit == 'R')
+ } else if (restart_edit == 'R') {
MSG_PUTS_ATTR(_(" (replace)"), attr);
- else if (restart_edit == 'V')
+ } else if (restart_edit == 'V') {
MSG_PUTS_ATTR(_(" (vreplace)"), attr);
- if (p_hkmap)
+ }
+ if (p_hkmap) {
MSG_PUTS_ATTR(_(" Hebrew"), attr);
- if (p_fkmap)
+ }
+ if (p_fkmap) {
MSG_PUTS_ATTR(farsi_text_5, attr);
+ }
if (State & LANGMAP) {
if (curwin->w_p_arab) {
MSG_PUTS_ATTR(_(" Arabic"), attr);
@@ -7161,7 +7099,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;
}
@@ -7178,11 +7116,7 @@ static int fillchar_status(int *attr, win_T *wp)
static int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
- if (*attr == 0 && fill_vert == ' ') {
- return '|';
- } else {
- return fill_vert;
- }
+ return fill_vert;
}
/*
@@ -7424,13 +7358,7 @@ int number_width(win_T *wp)
return n;
}
-/*
- * Set size of the Vim shell.
- * If 'mustset' is TRUE, we must set Rows and Columns, do not get the real
- * window size (this is used for the :win command).
- * If 'mustset' is FALSE, we may try to get the real window size and if
- * it fails use 'width' and 'height'.
- */
+/// Set dimensions of the Nvim application "shell".
void screen_resize(int width, int height)
{
static int busy = FALSE;
@@ -7515,8 +7443,8 @@ void screen_resize(int width, int height)
--busy;
}
-// Check if the new shell size is valid, correct it if it's too small or way
-// too big.
+/// Check if the new Nvim application "shell" dimensions are valid.
+/// Correct it if it's too small or way too big.
void check_shellsize(void)
{
if (Rows < min_rows()) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 1bf2317d2a..1943e2ca43 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -96,6 +96,9 @@ static int lastc_bytelen = 1; /* >1 for multi-byte char */
/* copy of spats[], for keeping the search patterns while executing autocmds */
static struct spat saved_spats[2];
+// copy of spats[RE_SEARCH], for keeping the search patterns while incremental
+// searching
+static struct spat saved_last_search_spat;
static int saved_last_idx = 0;
static int saved_no_hlsearch = 0;
@@ -305,6 +308,36 @@ void free_search_patterns(void)
#endif
+/// Save and restore the search pattern for incremental highlight search
+/// feature.
+///
+/// It's similar but different from save_search_patterns() and
+/// restore_search_patterns(), because the search pattern must be restored when
+/// cancelling incremental searching even if it's called inside user functions.
+void save_last_search_pattern(void)
+{
+ saved_last_search_spat = spats[RE_SEARCH];
+ if (spats[RE_SEARCH].pat != NULL) {
+ saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
+ }
+ saved_last_idx = last_idx;
+ saved_no_hlsearch = no_hlsearch;
+}
+
+void restore_last_search_pattern(void)
+{
+ xfree(spats[RE_SEARCH].pat);
+ spats[RE_SEARCH] = saved_last_search_spat;
+ set_vv_searchforward();
+ last_idx = saved_last_idx;
+ SET_NO_HLSEARCH(saved_no_hlsearch);
+}
+
+char_u *last_search_pattern(void)
+{
+ return spats[RE_SEARCH].pat;
+}
+
/*
* Return TRUE when case should be ignored for search pattern "pat".
* Uses the 'ignorecase' and 'smartcase' options.
@@ -742,12 +775,17 @@ int searchit(
}
}
if (ptr[matchcol] == NUL
- || (nmatched = vim_regexec_multi(&regmatch,
- win, buf, lnum + matchpos.lnum,
- matchcol,
- tm
- )) == 0)
- break;
+ || (nmatched = vim_regexec_multi(
+ &regmatch, win, buf, lnum + matchpos.lnum, matchcol,
+ tm)) == 0) {
+ // If the search timed out, we did find a match
+ // but it might be the wrong one, so that's not
+ // OK.
+ if (tm != NULL && profile_passed_limit(*tm)) {
+ match_ok = false;
+ }
+ break;
+ }
/* Need to get the line pointer again, a
* multi-line search may have made it invalid. */
@@ -1342,13 +1380,15 @@ int searchc(cmdarg_T *cap, int t_cmd)
lastc_bytelen += (*mb_char2bytes)(cap->ncharC2, lastc_bytes + lastc_bytelen);
}
}
- } else { /* repeat previous search */
- if (*lastc == NUL)
+ } else { // repeat previous search
+ if (*lastc == NUL && lastc_bytelen == 1) {
return FAIL;
- if (dir) /* repeat in opposite direction */
+ }
+ if (dir) { // repeat in opposite direction
dir = -lastcdir;
- else
+ } else {
dir = lastcdir;
+ }
t_cmd = last_t_cmd;
c = *lastc;
/* For multi-byte re-use last lastc_bytes[] and lastc_bytelen. */
@@ -1382,13 +1422,13 @@ int searchc(cmdarg_T *cap, int t_cmd)
col -= (*mb_head_off)(p, p + col - 1) + 1;
}
if (lastc_bytelen == 1) {
- if (p[col] == c && stop)
+ if (p[col] == c && stop) {
break;
- } else {
- if (memcmp(p + col, lastc_bytes, lastc_bytelen) == 0 && stop)
+ }
+ } else if (STRNCMP(p + col, lastc_bytes, lastc_bytelen) == 0 && stop) {
break;
}
- stop = TRUE;
+ stop = true;
}
} else {
for (;; ) {
@@ -2227,10 +2267,11 @@ int findsent(int dir, long count)
break;
found_dot = TRUE;
}
- if (decl(&pos) == -1)
+ if (decl(&pos) == -1) {
break;
- /* when going forward: Stop in front of empty line */
- if (lineempty(pos.lnum) && dir == FORWARD) {
+ }
+ // when going forward: Stop in front of empty line
+ if (LINEEMPTY(pos.lnum) && dir == FORWARD) {
incl(&pos);
goto found;
}
@@ -2534,10 +2575,12 @@ int bck_word(long count, int bigword, int stop)
*/
while (cls() == 0) {
if (curwin->w_cursor.col == 0
- && lineempty(curwin->w_cursor.lnum))
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
goto finished;
- if (dec_cursor() == -1) /* hit start of file, stop here */
+ }
+ if (dec_cursor() == -1) { // hit start of file, stop here
return OK;
+ }
}
/*
@@ -2601,10 +2644,12 @@ int end_word(long count, int bigword, int stop, int empty)
*/
while (cls() == 0) {
if (empty && curwin->w_cursor.col == 0
- && lineempty(curwin->w_cursor.lnum))
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
goto finished;
- if (inc_cursor() == -1) /* hit end of file, stop here */
+ }
+ if (inc_cursor() == -1) { // hit end of file, stop here
return FAIL;
+ }
}
/*
@@ -2657,10 +2702,12 @@ bckend_word (
* Move backward to end of the previous word
*/
while (cls() == 0) {
- if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
+ if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) {
break;
- if ((i = dec_cursor()) == -1 || (eol && i == 1))
+ }
+ if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
return OK;
+ }
}
}
return OK;
@@ -3557,11 +3604,15 @@ extend:
--start_lnum;
if (VIsual_active) {
- /* Problem: when doing "Vipipip" nothing happens in a single white
- * line, we get stuck there. Trap this here. */
- if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
+ // Problem: when doing "Vipipip" nothing happens in a single white
+ // line, we get stuck there. Trap this here.
+ if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) {
goto extend;
- VIsual.lnum = start_lnum;
+ }
+ if (VIsual.lnum != start_lnum) {
+ VIsual.lnum = start_lnum;
+ VIsual.col = 0;
+ }
VIsual_mode = 'V';
redraw_curbuf_later(INVERTED); /* update the inversion */
showmode();
@@ -3663,11 +3714,25 @@ current_quote (
int selected_quote = FALSE; /* Has quote inside selection */
int i;
- /* Correct cursor when 'selection' is exclusive */
+ // Correct cursor when 'selection' is "exclusive".
if (VIsual_active) {
+ // this only works within one line
+ if (VIsual.lnum != curwin->w_cursor.lnum) {
+ return false;
+ }
+
vis_bef_curs = lt(VIsual, curwin->w_cursor);
- if (*p_sel == 'e' && vis_bef_curs)
+ if (*p_sel == 'e') {
+ if (!vis_bef_curs) {
+ // VIsual needs to be start of Visual selection.
+ pos_T t = curwin->w_cursor;
+
+ curwin->w_cursor = VIsual;
+ VIsual = t;
+ vis_bef_curs = true;
+ }
dec_cursor();
+ }
vis_empty = equalpos(VIsual, curwin->w_cursor);
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 4788b1e7d0..605d9c30a6 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -76,8 +76,8 @@ KHASH_SET_INIT_STR(strset)
(vim_rename((char_u *)a, (char_u *)b))
#define mb_strnicmp(a, b, c) \
(mb_strnicmp((char_u *)a, (char_u *)b, c))
-#define path_shorten_fname_if_possible(b) \
- ((char *)path_shorten_fname_if_possible((char_u *)b))
+#define path_try_shorten_fname(b) \
+ ((char *)path_try_shorten_fname((char_u *)b))
#define buflist_new(ffname, sfname, ...) \
(buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__))
#define os_isdir(f) (os_isdir((char_u *) f))
@@ -1180,8 +1180,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES);
const bool force = flags & kShaDaForceit;
const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit)
- && (force || oldfiles_list == NULL
- || oldfiles_list->lv_len == 0));
+ && (force || tv_list_len(oldfiles_list) == 0));
const bool want_marks = flags & kShaDaWantMarks;
const unsigned srni_flags = (unsigned) (
(flags & kShaDaWantInfo
@@ -1218,7 +1217,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs);
khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset);
if (get_old_files && (oldfiles_list == NULL || force)) {
- oldfiles_list = tv_list_alloc();
+ oldfiles_list = tv_list_alloc(kListLenUnknown);
set_vim_var_list(VV_OLDFILES, oldfiles_list);
}
ShaDaReadResult srni_ret;
@@ -1398,7 +1397,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
}
case kSDItemBufferList: {
for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) {
- char *const sfname = path_shorten_fname_if_possible(
+ char *const sfname = path_try_shorten_fname(
cur_entry.data.buffer_list.buffers[i].fname);
buf_T *const buf = buflist_new(
cur_entry.data.buffer_list.buffers[i].fname, sfname, 0,
@@ -1599,13 +1598,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
#define DUMP_ADDITIONAL_ELEMENTS(src, what) \
do { \
if ((src) != NULL) { \
- for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \
- if (encode_vim_to_msgpack(spacker, &li->li_tv, \
+ TV_LIST_ITER((src), li, { \
+ if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \
_("additional elements of ShaDa " what)) \
== FAIL) { \
goto shada_pack_entry_error; \
} \
- } \
+ }); \
} \
} while (0)
#define DUMP_ADDITIONAL_DATA(src, what) \
@@ -1647,25 +1646,21 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
case kSDItemHistoryEntry: {
const bool is_hist_search =
entry.data.history_item.histtype == HIST_SEARCH;
- const size_t arr_size = 2 + (size_t) is_hist_search + (size_t) (
- entry.data.history_item.additional_elements == NULL
- ? 0
- : entry.data.history_item.additional_elements->lv_len);
+ const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)(
+ tv_list_len(entry.data.history_item.additional_elements));
msgpack_pack_array(spacker, arr_size);
msgpack_pack_uint8(spacker, entry.data.history_item.histtype);
PACK_BIN(cstr_as_string(entry.data.history_item.string));
if (is_hist_search) {
- msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep);
+ msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep);
}
DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements,
"history entry item");
break;
}
case kSDItemVariable: {
- const size_t arr_size = 2 + (size_t) (
- entry.data.global_var.additional_elements == NULL
- ? 0
- : entry.data.global_var.additional_elements->lv_len);
+ const size_t arr_size = 2 + (size_t)(
+ tv_list_len(entry.data.global_var.additional_elements));
msgpack_pack_array(spacker, arr_size);
const String varname = cstr_as_string(entry.data.global_var.name);
PACK_BIN(varname);
@@ -1684,10 +1679,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
break;
}
case kSDItemSubString: {
- const size_t arr_size = 1 + (size_t) (
- entry.data.sub_string.additional_elements == NULL
- ? 0
- : entry.data.sub_string.additional_elements->lv_len);
+ const size_t arr_size = 1 + (size_t)(
+ tv_list_len(entry.data.sub_string.additional_elements));
msgpack_pack_array(spacker, arr_size);
PACK_BIN(cstr_as_string(entry.data.sub_string.sub));
DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements,
@@ -2564,6 +2557,12 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
xfmark_T fm;
jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
+ if (fm.fmark.mark.lnum == 0) {
+ iemsgf("ShaDa: mark lnum zero (ji:%p, js:%p, len:%i)",
+ (void *)jump_iter, (void *)&curwin->w_jumplist[0],
+ curwin->w_jumplistlen);
+ continue;
+ }
const buf_T *const buf = (fm.fmark.fnum == 0
? NULL
: buflist_findnr(fm.fmark.fnum));
@@ -2993,7 +2992,7 @@ shada_write_file_nomerge: {}
} else {
if (sw_ret == kSDWriteReadNotShada) {
EMSG3(_(RNERR "Did not rename %s because %s "
- "does not looks like a ShaDa file"), tempname, fname);
+ "does not look like a ShaDa file"), tempname, fname);
} else {
EMSG3(_(RNERR "Did not rename %s to %s because there were errors "
"during writing it"), tempname, fname);
@@ -3413,8 +3412,16 @@ shada_read_next_item_start:
return mru_ret;
}
- const size_t length = (size_t) length_u64;
- entry->timestamp = (Timestamp) timestamp_u64;
+ if (length_u64 > PTRDIFF_MAX) {
+ emsgf(_(RCERR "Error while reading ShaDa file: "
+ "there is an item at position %" PRIu64 " "
+ "that is stated to be too long"),
+ initial_fpos);
+ return kSDReadStatusNotShaDa;
+ }
+
+ const size_t length = (size_t)length_u64;
+ entry->timestamp = (Timestamp)timestamp_u64;
if (type_u64 == 0) {
// kSDItemUnknown cannot possibly pass that far because it is -1 and that
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 25ae562e65..34eb2fdf1b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1433,12 +1433,10 @@ spell_move_to (
// the cursor.
if (dir == BACKWARD
|| lnum != wp->w_cursor.lnum
- || (lnum == wp->w_cursor.lnum
- && (wrapped
- || ((colnr_T)(curline
- ? p - buf + (ptrdiff_t)len
- : p - buf)
- > wp->w_cursor.col)))) {
+ || wrapped
+ || ((colnr_T)(curline
+ ? p - buf + (ptrdiff_t)len
+ : p - buf) > wp->w_cursor.col)) {
if (has_syntax) {
col = (int)(p - buf);
(void)syn_get_id(wp, lnum, (colnr_T)col,
@@ -1486,21 +1484,23 @@ spell_move_to (
return found_len;
}
- if (curline)
+ if (curline) {
break; // only check cursor line
+ }
+
+ // If we are back at the starting line and searched it again there
+ // is no match, give up.
+ if (lnum == wp->w_cursor.lnum && wrapped) {
+ break;
+ }
// Advance to next line.
if (dir == BACKWARD) {
- // If we are back at the starting line and searched it again there
- // is no match, give up.
- if (lnum == wp->w_cursor.lnum && wrapped)
- break;
-
- if (lnum > 1)
- --lnum;
- else if (!p_ws)
+ if (lnum > 1) {
+ lnum--;
+ } else if (!p_ws) {
break; // at first line and 'nowrapscan'
- else {
+ } else {
// Wrap around to the end of the buffer. May search the
// starting line again and accept the last match.
lnum = wp->w_buffer->b_ml.ml_line_count;
@@ -1525,8 +1525,9 @@ spell_move_to (
// If we are back at the starting line and there is no match then
// give up.
- if (lnum == wp->w_cursor.lnum && (!found_one || wrapped))
+ if (lnum == wp->w_cursor.lnum && !found_one) {
break;
+ }
// Skip the characters at the start of the next line that were
// included in a match crossing line boundaries.
@@ -2070,7 +2071,7 @@ char_u *did_set_spelllang(win_T *wp)
// destroying the buffer we are using...
if (!bufref_valid(&bufref)) {
ret_msg =
- (char_u *)"E797: SpellFileMissing autocommand deleted buffer";
+ (char_u *)N_("E797: SpellFileMissing autocommand deleted buffer");
goto theend;
}
}
@@ -2567,7 +2568,7 @@ static bool spell_iswordp(char_u *p, win_T *wp)
int c;
if (has_mbyte) {
- l = MB_BYTE2LEN(*p);
+ l = MB_PTR2LEN(p);
s = p;
if (l == 1) {
// be quick for ASCII
@@ -3138,8 +3139,13 @@ spell_find_suggest (
if (su->su_badlen >= MAXWLEN)
su->su_badlen = MAXWLEN - 1; // just in case
STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
- (void)spell_casefold(su->su_badptr, su->su_badlen,
- su->su_fbadword, MAXWLEN);
+ (void)spell_casefold(su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN);
+
+ // TODO(vim): make this work if the case-folded text is longer than the
+ // original text. Currently an illegal byte causes wrong pointer
+ // computations.
+ su->su_fbadword[su->su_badlen] = NUL;
+
// get caps flags for bad word
su->su_badflags = badword_captype(su->su_badptr,
su->su_badptr + su->su_badlen);
@@ -3215,26 +3221,25 @@ spell_find_suggest (
// Find suggestions by evaluating expression "expr".
static void spell_suggest_expr(suginfo_T *su, char_u *expr)
{
- list_T *list;
- listitem_T *li;
int score;
const char *p;
// The work is split up in a few parts to avoid having to export
// suginfo_T.
// First evaluate the expression and get the resulting list.
- list = eval_spell_expr(su->su_badword, expr);
+ list_T *const list = eval_spell_expr(su->su_badword, expr);
if (list != NULL) {
// Loop over the items in the list.
- for (li = list->lv_first; li != NULL; li = li->li_next)
- if (li->li_tv.v_type == VAR_LIST) {
+ TV_LIST_ITER(list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
// Get the word and the score from the items.
- score = get_spellword(li->li_tv.vval.v_list, &p);
+ score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p);
if (score >= 0 && score <= su->su_maxscore) {
add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen,
score, 0, true, su->su_sallang, false);
}
}
+ });
tv_list_unref(list);
}
@@ -3635,7 +3640,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// word).
depth = 0;
sp = &stack[0];
- memset(sp, 0, sizeof(trystate_T));
+ memset(sp, 0, sizeof(trystate_T)); // -V512
sp->ts_curi = 1;
if (soundfold) {
@@ -4110,10 +4115,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
&& goodword_ends) {
int l;
- if (has_mbyte)
- l = MB_BYTE2LEN(fword[sp->ts_fidx]);
- else
- l = 1;
+ l = MB_PTR2LEN(fword + sp->ts_fidx);
if (fword_ends) {
// Copy the skipped character to preword.
memmove(preword + sp->ts_prewordlen,
@@ -4259,8 +4261,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Correct ts_fidx for the byte length of the
// character (we didn't check that before).
sp->ts_fidx = sp->ts_fcharstart
- + MB_BYTE2LEN(
- fword[sp->ts_fcharstart]);
+ + MB_PTR2LEN(fword + sp->ts_fcharstart);
// For changing a composing character adjust
// the score from SCORE_SUBST to
@@ -4366,11 +4367,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// results.
if (has_mbyte) {
c = mb_ptr2char(fword + sp->ts_fidx);
- stack[depth].ts_fidx += MB_BYTE2LEN(fword[sp->ts_fidx]);
- if (enc_utf8 && utf_iscomposing(c))
+ stack[depth].ts_fidx += MB_PTR2LEN(fword + sp->ts_fidx);
+ if (enc_utf8 && utf_iscomposing(c)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP;
- else if (c == mb_ptr2char(fword + stack[depth].ts_fidx))
+ } else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP;
+ }
} else {
++stack[depth].ts_fidx;
if (fword[sp->ts_fidx] == fword[sp->ts_fidx + 1])
@@ -4552,9 +4554,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Undo the STATE_SWAP swap: "21" -> "12".
p = fword + sp->ts_fidx;
if (has_mbyte) {
- n = MB_BYTE2LEN(*p);
+ n = MB_PTR2LEN(p);
c = mb_ptr2char(p + n);
- memmove(p + MB_BYTE2LEN(p[n]), p, n);
+ memmove(p + MB_PTR2LEN(p + n), p, n);
mb_char2bytes(c, p);
} else {
c = *p;
@@ -4627,11 +4629,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Undo STATE_SWAP3: "321" -> "123"
p = fword + sp->ts_fidx;
if (has_mbyte) {
- n = MB_BYTE2LEN(*p);
+ n = MB_PTR2LEN(p);
c2 = mb_ptr2char(p + n);
- fl = MB_BYTE2LEN(p[n]);
+ fl = MB_PTR2LEN(p + n);
c = mb_ptr2char(p + n + fl);
- tl = MB_BYTE2LEN(p[n + fl]);
+ tl = MB_PTR2LEN(p + n + fl);
memmove(p + fl + tl, p, n);
mb_char2bytes(c, p);
mb_char2bytes(c2, p + tl);
@@ -4690,10 +4692,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Undo ROT3L: "231" -> "123"
p = fword + sp->ts_fidx;
if (has_mbyte) {
- n = MB_BYTE2LEN(*p);
- n += MB_BYTE2LEN(p[n]);
+ n = MB_PTR2LEN(p);
+ n += MB_PTR2LEN(p + n);
c = mb_ptr2char(p + n);
- tl = MB_BYTE2LEN(p[n]);
+ tl = MB_PTR2LEN(p + n);
memmove(p + tl, p, n);
mb_char2bytes(c, p);
} else {
@@ -4743,9 +4745,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
p = fword + sp->ts_fidx;
if (has_mbyte) {
c = mb_ptr2char(p);
- tl = MB_BYTE2LEN(*p);
- n = MB_BYTE2LEN(p[tl]);
- n += MB_BYTE2LEN(p[tl + n]);
+ tl = MB_PTR2LEN(p);
+ n = MB_PTR2LEN(p + tl);
+ n += MB_PTR2LEN(p + tl + n);
memmove(p, p + tl, n);
mb_char2bytes(c, p + n);
} else {
@@ -5357,7 +5359,7 @@ add_sound_suggest (
// Find the word nr in the soundfold tree.
sfwordnr = soundfold_find(slang, goodword);
if (sfwordnr < 0) {
- EMSG2(_(e_intern2), "add_sound_suggest()");
+ internal_error("add_sound_suggest()");
return;
}
@@ -7145,7 +7147,7 @@ void ex_spelldump(exarg_T *eap)
set_option_value("spl", dummy, (char *)spl, OPT_LOCAL);
xfree(spl);
- if (!bufempty()) {
+ if (!BUFEMPTY()) {
return;
}
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 1f7f616782..f5d5d408a1 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -228,7 +228,6 @@
#include <stdio.h>
#include <stdint.h>
#include <wctype.h>
-#include <strings.h>
#include "nvim/vim.h"
#include "nvim/spell_defs.h"
@@ -3656,7 +3655,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
flags |= WF_REGION;
l = *p - '0';
- if (l > spin->si_region_count) {
+ if (l == 0 || l > spin->si_region_count) {
smsg(_("Invalid region nr in %s line %d: %s"),
fname, lnum, p);
break;
diff --git a/src/nvim/state.c b/src/nvim/state.c
index be6aa21664..a4b394c9e4 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -6,6 +6,7 @@
#include "nvim/lib/kvec.h"
#include "nvim/ascii.h"
+#include "nvim/log.h"
#include "nvim/state.h"
#include "nvim/vim.h"
#include "nvim/main.h"
@@ -13,6 +14,8 @@
#include "nvim/option_defs.h"
#include "nvim/ui.h"
#include "nvim/os/input.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/edit.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "state.c.generated.h"
@@ -25,10 +28,11 @@ void state_enter(VimState *s)
int check_result = s->check ? s->check(s) : 1;
if (!check_result) {
- break;
+ break; // Terminate this state.
} else if (check_result == -1) {
- continue;
+ continue; // check() again.
}
+ // Execute this state.
int key;
@@ -47,11 +51,13 @@ getkey:
ui_flush();
// Call `os_inchar` directly to block for events or user input without
// consuming anything from `input_buffer`(os/input.c) or calling the
- // mapping engine. If an event was put into the queue, we send K_EVENT
- // directly.
+ // mapping engine.
(void)os_inchar(NULL, 0, -1, 0);
input_disable_events();
- key = !multiqueue_empty(main_loop.events) ? K_EVENT : safe_vgetc();
+ // If an event was put into the queue, we send K_EVENT directly.
+ key = !multiqueue_empty(main_loop.events)
+ ? K_EVENT
+ : safe_vgetc();
}
if (key == K_EVENT) {
@@ -123,19 +129,25 @@ char *get_mode(void)
if (State & VREPLACE_FLAG) {
buf[0] = 'R';
buf[1] = 'v';
- } else if (State & REPLACE_FLAG) {
- buf[0] = 'R';
} else {
- buf[0] = 'i';
+ if (State & REPLACE_FLAG) {
+ buf[0] = 'R';
+ } else {
+ buf[0] = 'i';
+ }
+ if (ins_compl_active()) {
+ buf[1] = 'c';
+ } else if (ctrl_x_mode == 1) {
+ buf[1] = 'x';
+ }
}
- } else if (State & CMDLINE) {
+ } else if ((State & CMDLINE) || exmode_active) {
buf[0] = 'c';
- if (exmode_active) {
+ if (exmode_active == EXMODE_VIM) {
buf[1] = 'v';
+ } else if (exmode_active == EXMODE_NORMAL) {
+ buf[1] = 'e';
}
- } else if (exmode_active) {
- buf[0] = 'c';
- buf[1] = 'e';
} else if (State & TERM_FOCUS) {
buf[0] = 't';
} else {
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 687f734742..e3f6a8cbf6 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -50,6 +50,13 @@
#include "nvim/os/shell.h"
#include "nvim/eval/encode.h"
+#ifdef __MINGW32__
+# undef fpclassify
+# define fpclassify __fpclassify
+# undef isnan
+# define isnan _isnan
+#endif
+
/*
* Copy "string" into newly allocated memory.
*/
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index a4bb260183..8ec393e568 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -42,6 +42,7 @@
#include "nvim/ui.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+#include "nvim/api/private/helpers.h"
static bool did_syntax_onoff = false;
@@ -51,6 +52,7 @@ static bool did_syntax_onoff = false;
struct hl_group {
char_u *sg_name; ///< highlight group name
char_u *sg_name_u; ///< uppercase of sg_name
+ int sg_cleared; ///< "hi clear" was used
int sg_attr; ///< Screen attr @see ATTR_ENTRY
int sg_link; ///< link to this highlight group ID
int sg_set; ///< combination of flags in \ref SG_SET
@@ -62,7 +64,7 @@ struct hl_group {
int sg_cterm_bold; ///< bold attr was set for light color
// for RGB UIs
int sg_gui; ///< "gui=" highlighting attributes
- ///< (combination of \ref HL_ATTRIBUTES)
+ ///< (combination of \ref HlAttrFlags)
RgbValue sg_rgb_fg; ///< RGB foreground color
RgbValue sg_rgb_bg; ///< RGB background color
RgbValue sg_rgb_sp; ///< RGB special color
@@ -78,10 +80,13 @@ struct hl_group {
#define SG_LINK 8 // link has been set
/// @}
-// highlight groups for 'highlight' option
+// builtin |highlight-groups|
static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
-#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
+static inline struct hl_group * HL_TABLE(void)
+{
+ return ((struct hl_group *)((highlight_ga.ga_data)));
+}
#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
@@ -100,10 +105,8 @@ static int include_none = 0; /* when 1 include "nvim/None" */
static int include_default = 0; /* when 1 include "nvim/default" */
static int include_link = 0; /* when 2 include "nvim/link" and "clear" */
-/*
- * The "term", "cterm" and "gui" arguments can be any combination of the
- * following names, separated by commas (but no spaces!).
- */
+/// The "term", "cterm" and "gui" arguments can be any combination of the
+/// following names, separated by commas (but no spaces!).
static char *(hl_name_table[]) =
{"bold", "standout", "underline", "undercurl",
"italic", "reverse", "inverse", "NONE"};
@@ -878,7 +881,8 @@ static void syn_start_line(void)
}
next_match_idx = -1;
- ++current_line_id;
+ current_line_id++;
+ next_seqnr = 1;
}
/*
@@ -1596,6 +1600,7 @@ get_syntax_attr (
current_id = 0;
current_trans_id = 0;
current_flags = 0;
+ current_seqnr = 0;
return 0;
}
@@ -1666,8 +1671,9 @@ syn_current_attr (
* If we found a match after the last column, use it.
*/
if (next_match_idx >= 0 && next_match_col >= (int)current_col
- && next_match_col != MAXCOL)
- (void)push_next_match(NULL);
+ && next_match_col != MAXCOL) {
+ (void)push_next_match();
+ }
current_finished = TRUE;
current_state_stored = FALSE;
@@ -1774,8 +1780,9 @@ syn_current_attr (
cur_si->si_trans_id = CUR_STATE(
current_state.ga_len - 2).si_trans_id;
}
- } else
+ } else {
cur_si->si_attr = syn_id2attr(syn_id);
+ }
cur_si->si_cont_list = NULL;
cur_si->si_next_list = next_list;
check_keepend();
@@ -1985,9 +1992,10 @@ syn_current_attr (
* endless loop). */
GA_APPEND(int, &zero_width_next_ga, next_match_idx);
next_match_idx = -1;
- } else
- cur_si = push_next_match(cur_si);
- found_match = TRUE;
+ } else {
+ cur_si = push_next_match();
+ }
+ found_match = true;
}
}
}
@@ -2036,6 +2044,7 @@ syn_current_attr (
current_id = 0;
current_trans_id = 0;
current_flags = 0;
+ current_seqnr = 0;
if (cur_si != NULL) {
for (int idx = current_state.ga_len - 1; idx >= 0; --idx) {
sip = &CUR_STATE(idx);
@@ -2124,9 +2133,11 @@ syn_current_attr (
/* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
if (current_next_list != NULL
- && syn_getcurline()[current_col + 1] == NUL
- && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
+ && (line = syn_getcurline())[current_col] != NUL
+ && line[current_col + 1] == NUL
+ && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) {
current_next_list = NULL;
+ }
if (!GA_EMPTY(&zero_width_next_ga))
ga_clear(&zero_width_next_ga);
@@ -2167,9 +2178,10 @@ static int did_match_already(int idx, garray_T *gap)
/*
* Push the next match onto the stack.
*/
-static stateitem_T *push_next_match(stateitem_T *cur_si)
+static stateitem_T *push_next_match(void)
{
- synpat_T *spp;
+ stateitem_T *cur_si;
+ synpat_T *spp;
int save_flags;
spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
@@ -3013,12 +3025,19 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
return;
next = skiptowhite(arg);
- if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
- curwin->w_s->b_syn_conceal = TRUE;
- else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
- curwin->w_s->b_syn_conceal = FALSE;
- else
+ if (*arg == NUL) {
+ if (curwin->w_s->b_syn_conceal) {
+ MSG(_("syn conceal on"));
+ } else {
+ MSG(_("syn conceal off"));
+ }
+ } else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) {
+ curwin->w_s->b_syn_conceal = true;
+ } else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) {
+ curwin->w_s->b_syn_conceal = false;
+ } else {
EMSG2(_("E390: Illegal argument: %s"), arg);
+ }
}
/*
@@ -3034,12 +3053,19 @@ static void syn_cmd_case(exarg_T *eap, int syncing)
return;
next = skiptowhite(arg);
- if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
- curwin->w_s->b_syn_ic = FALSE;
- else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
- curwin->w_s->b_syn_ic = TRUE;
- else
+ if (*arg == NUL) {
+ if (curwin->w_s->b_syn_ic) {
+ MSG(_("syntax case ignore"));
+ } else {
+ MSG(_("syntax case match"));
+ }
+ } else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) {
+ curwin->w_s->b_syn_ic = false;
+ } else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) {
+ curwin->w_s->b_syn_ic = true;
+ } else {
EMSG2(_("E390: Illegal argument: %s"), arg);
+ }
}
/*
@@ -3055,7 +3081,15 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
return;
next = skiptowhite(arg);
- if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) {
+ if (*arg == NUL) {
+ if (curwin->w_s->b_syn_spell == SYNSPL_TOP) {
+ MSG(_("syntax spell toplevel"));
+ } else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) {
+ MSG(_("syntax spell notoplevel"));
+ } else {
+ MSG(_("syntax spell default"));
+ }
+ } else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) {
curwin->w_s->b_syn_spell = SYNSPL_TOP;
} else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) {
curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
@@ -3115,10 +3149,11 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
*/
void syntax_clear(synblock_T *block)
{
- block->b_syn_error = FALSE; /* clear previous error */
- block->b_syn_ic = FALSE; /* Use case, by default */
- block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
- block->b_syn_containedin = FALSE;
+ block->b_syn_error = false; // clear previous error
+ block->b_syn_ic = false; // Use case, by default
+ block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
+ block->b_syn_containedin = false;
+ block->b_syn_conceal = false;
/* free the keywords */
clear_keywtab(&block->b_keywtab);
@@ -3995,18 +4030,19 @@ get_group_name (
* Return NULL for any error;
*/
static char_u *
-get_syn_options (
- char_u *arg, /* next argument to be checked */
- syn_opt_arg_T *opt, /* various things */
- int *conceal_char
+get_syn_options(
+ char_u *arg, // next argument to be checked
+ syn_opt_arg_T *opt, // various things
+ int *conceal_char,
+ int skip // TRUE if skipping over command
)
{
char_u *gname_start, *gname;
int syn_id;
- int len;
+ int len = 0;
char *p;
int fidx;
- static struct flag {
+ static const struct flag {
char *name;
int argtype;
int flags;
@@ -4029,7 +4065,7 @@ get_syn_options (
{"cCoOnNtTaAiInNsS", 1, 0},
{"cCoOnNtTaAiInNeEdDiInN", 2, 0},
{"nNeExXtTgGrRoOuUpP", 3, 0},};
- static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
+ static const char *const first_letters = "cCoOkKeEtTsSgGdDfFnN";
if (arg == NULL) /* already detected error */
return NULL;
@@ -4049,9 +4085,10 @@ get_syn_options (
for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0; ) {
p = flagtab[fidx].name;
int i;
- for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
+ for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) {
if (arg[len] != p[i] && arg[len] != p[i + 1])
break;
+ }
if (p[i] == NUL && (ascii_iswhite(arg[len])
|| (flagtab[fidx].argtype > 0
? arg[len] == '='
@@ -4073,14 +4110,17 @@ get_syn_options (
EMSG(_("E395: contains argument not accepted here"));
return NULL;
}
- if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
+ if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL) {
return NULL;
+ }
} else if (flagtab[fidx].argtype == 2) {
- if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
+ if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL) {
return NULL;
+ }
} else if (flagtab[fidx].argtype == 3) {
- if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
+ if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL) {
return NULL;
+ }
} else if (flagtab[fidx].argtype == 11 && arg[5] == '=') {
/* cchar=? */
if (has_mbyte) {
@@ -4199,11 +4239,11 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
*/
eap->argt |= (XFILE | NOSPC);
separate_nextcmd(eap);
- if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute_path(eap->arg)) {
- /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
- * file. Need to expand the file name first. In other cases
- * ":runtime!" is used. */
- source = TRUE;
+ if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(eap->arg)) {
+ // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
+ // file. Need to expand the file name first. In other cases
+ // ":runtime!" is used.
+ source = true;
if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL) {
if (errormsg != NULL)
EMSG(errormsg);
@@ -4250,7 +4290,11 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
rest = get_group_name(arg, &group_name_end);
if (rest != NULL) {
- syn_id = syn_check_group(arg, (int)(group_name_end - arg));
+ if (eap->skip) {
+ syn_id = -1;
+ } else {
+ syn_id = syn_check_group(arg, (int)(group_name_end - arg));
+ }
if (syn_id != 0) {
// Allocate a buffer, for removing backslashes in the keyword.
keyword_copy = xmalloc(STRLEN(rest) + 1);
@@ -4269,7 +4313,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
cnt = 0;
p = keyword_copy;
for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) {
- rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
+ rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
if (rest == NULL || ends_excmd(*rest)) {
break;
}
@@ -4368,17 +4412,18 @@ syn_cmd_match (
syn_opt_arg.cont_list = NULL;
syn_opt_arg.cont_in_list = NULL;
syn_opt_arg.next_list = NULL;
- rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
+ rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
/* get the pattern. */
init_syn_patterns();
memset(&item, 0, sizeof(item));
rest = get_syn_pattern(rest, &item);
- if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
+ if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) {
syn_opt_arg.flags |= HL_HAS_EOL;
+ }
- /* Get options after the pattern */
- rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
+ // Get options after the pattern
+ rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
if (rest != NULL) { /* all arguments are valid */
/*
@@ -4490,14 +4535,13 @@ syn_cmd_region (
syn_opt_arg.cont_in_list = NULL;
syn_opt_arg.next_list = NULL;
- /*
- * get the options, patterns and matchgroup.
- */
+ // get the options, patterns and matchgroup.
while (rest != NULL && !ends_excmd(*rest)) {
- /* Check for option arguments */
- rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
- if (rest == NULL || ends_excmd(*rest))
+ // Check for option arguments
+ rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
+ if (rest == NULL || ends_excmd(*rest)) {
break;
+ }
/* must be a pattern or matchgroup then */
key_end = rest;
@@ -4919,13 +4963,17 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing)
break;
clstr_list = NULL;
- if (get_id_list(&rest, opt_len, &clstr_list) == FAIL) {
+ if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL) {
EMSG2(_(e_invarg2), rest);
break;
}
- syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
- &clstr_list, list_op);
- got_clstr = TRUE;
+ if (scl_id >= 0) {
+ syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
+ &clstr_list, list_op);
+ } else {
+ xfree(clstr_list);
+ }
+ got_clstr = true;
}
if (got_clstr) {
@@ -5173,9 +5221,10 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
static int
get_id_list (
char_u **arg,
- int keylen, /* length of keyword */
- short **list /* where to store the resulting list, if not
- NULL, the list is silently skipped! */
+ int keylen, // length of keyword
+ int16_t **list, // where to store the resulting list, if not
+ // NULL, the list is silently skipped!
+ int skip
)
{
char_u *p = NULL;
@@ -5231,8 +5280,9 @@ get_id_list (
break;
}
if (count != 0) {
- EMSG2(_("E408: %s must be first in contains list"), name + 1);
- failed = TRUE;
+ EMSG2(_("E408: %s must be first in contains list"),
+ name + 1);
+ failed = true;
xfree(name);
break;
}
@@ -5244,17 +5294,19 @@ get_id_list (
id = SYNID_CONTAINED;
id += current_syn_inc_tag;
} else if (name[1] == '@') {
- id = syn_check_cluster(name + 2, (int)(end - p - 1));
+ if (skip) {
+ id = -1;
+ } else {
+ id = syn_check_cluster(name + 2, (int)(end - p - 1));
+ }
} else {
/*
* Handle full group name.
*/
- if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
+ if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) {
id = syn_check_group(name + 1, (int)(end - p));
- else {
- /*
- * Handle match of regexp with group names.
- */
+ } else {
+ // Handle match of regexp with group names.
*name = '^';
STRCAT(name, "$");
regmatch.regprog = vim_regcomp(name, RE_MAGIC);
@@ -5564,8 +5616,10 @@ bool syntax_present(win_T *win)
static enum {
- EXP_SUBCMD, /* expand ":syn" sub-commands */
- EXP_CASE /* expand ":syn case" arguments */
+ EXP_SUBCMD, // expand ":syn" sub-commands
+ EXP_CASE, // expand ":syn case" arguments
+ EXP_SPELL, // expand ":syn spell" arguments
+ EXP_SYNC // expand ":syn sync" arguments
} expand_what;
/*
@@ -5609,6 +5663,10 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
xp->xp_context = EXPAND_NOTHING;
} else if (STRNICMP(arg, "case", p - arg) == 0) {
expand_what = EXP_CASE;
+ } else if (STRNICMP(arg, "spell", p - arg) == 0) {
+ expand_what = EXP_SPELL;
+ } else if (STRNICMP(arg, "sync", p - arg) == 0) {
+ expand_what = EXP_SYNC;
} else if (STRNICMP(arg, "keyword", p - arg) == 0
|| STRNICMP(arg, "region", p - arg) == 0
|| STRNICMP(arg, "match", p - arg) == 0
@@ -5621,17 +5679,33 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
}
}
-static char *(case_args[]) = {"match", "ignore", NULL};
-
/*
* Function given to ExpandGeneric() to obtain the list syntax names for
* expansion.
*/
char_u *get_syntax_name(expand_T *xp, int idx)
{
- if (expand_what == EXP_SUBCMD)
- return (char_u *)subcommands[idx].name;
- return (char_u *)case_args[idx];
+ switch (expand_what) {
+ case EXP_SUBCMD:
+ return (char_u *)subcommands[idx].name;
+ case EXP_CASE: {
+ static char *case_args[] = { "match", "ignore", NULL };
+ return (char_u *)case_args[idx];
+ }
+ case EXP_SPELL: {
+ static char *spell_args[] =
+ { "toplevel", "notoplevel", "default", NULL };
+ return (char_u *)spell_args[idx];
+ }
+ case EXP_SYNC: {
+ static char *sync_args[] =
+ { "ccomment", "clear", "fromstart",
+ "linebreaks=", "linecont", "lines=", "match",
+ "maxlines=", "minlines=", "region", NULL };
+ return (char_u *)sync_args[idx];
+ }
+ }
+ return NULL;
}
@@ -5842,9 +5916,12 @@ static void syntime_report(void)
}
}
- /* sort on total time */
- qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
- syn_compare_syntime);
+ // Sort on total time. Skip if there are no items to avoid passing NULL
+ // pointer to qsort().
+ if (ga.ga_len > 1) {
+ qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
+ syn_compare_syntime);
+ }
MSG_PUTS_TITLE(_(
" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
@@ -5900,9 +5977,9 @@ static void syntime_report(void)
//
// When making changes here, also change runtime/colors/default.vim!
-static char *highlight_init_both[] =
-{
- "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey",
+static const char *highlight_init_both[] = {
+ "Conceal "
+ "ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey",
"Cursor guibg=fg guifg=bg",
"lCursor guibg=fg guifg=bg",
"DiffText cterm=bold ctermbg=Red gui=bold guibg=Red",
@@ -5925,8 +6002,7 @@ static char *highlight_init_both[] =
NULL
};
-static char *highlight_init_light[] =
-{
+static const char *highlight_init_light[] = {
"ColorColumn ctermbg=LightRed guibg=LightRed",
"CursorColumn ctermbg=LightGrey guibg=Grey90",
"CursorLine cterm=underline guibg=Grey90",
@@ -5955,11 +6031,11 @@ static char *highlight_init_light[] =
"Title ctermfg=DarkMagenta gui=bold guifg=Magenta",
"Visual guibg=LightGrey",
"WarningMsg ctermfg=DarkRed guifg=Red",
+ "Normal gui=NONE",
NULL
};
-static char *highlight_init_dark[] =
-{
+static const char *highlight_init_dark[] = {
"ColorColumn ctermbg=DarkRed guibg=DarkRed",
"CursorColumn ctermbg=DarkGrey guibg=Grey40",
"CursorLine cterm=underline guibg=Grey40",
@@ -5988,23 +6064,230 @@ static char *highlight_init_dark[] =
"Title ctermfg=LightMagenta gui=bold guifg=Magenta",
"Visual guibg=DarkGrey",
"WarningMsg ctermfg=LightRed guifg=Red",
+ "Normal gui=NONE",
NULL
};
-void
-init_highlight (
- int both, /* include groups where 'bg' doesn't matter */
- int reset /* clear group first */
-)
+const char *const highlight_init_cmdline[] = {
+ // XXX When modifying a list modify it in both valid and invalid halfs.
+ // TODO(ZyX-I): merge valid and invalid groups via a macros.
+
+ // NvimInternalError should appear only when highlighter has a bug.
+ "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
+
+ // Highlight groups (links) used by parser:
+
+ "default link NvimAssignment Operator",
+ "default link NvimPlainAssignment NvimAssignment",
+ "default link NvimAugmentedAssignment NvimAssignment",
+ "default link NvimAssignmentWithAddition NvimAugmentedAssignment",
+ "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment",
+ "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment",
+
+ "default link NvimOperator Operator",
+
+ "default link NvimUnaryOperator NvimOperator",
+ "default link NvimUnaryPlus NvimUnaryOperator",
+ "default link NvimUnaryMinus NvimUnaryOperator",
+ "default link NvimNot NvimUnaryOperator",
+
+ "default link NvimBinaryOperator NvimOperator",
+ "default link NvimComparison NvimBinaryOperator",
+ "default link NvimComparisonModifier NvimComparison",
+ "default link NvimBinaryPlus NvimBinaryOperator",
+ "default link NvimBinaryMinus NvimBinaryOperator",
+ "default link NvimConcat NvimBinaryOperator",
+ "default link NvimConcatOrSubscript NvimConcat",
+ "default link NvimOr NvimBinaryOperator",
+ "default link NvimAnd NvimBinaryOperator",
+ "default link NvimMultiplication NvimBinaryOperator",
+ "default link NvimDivision NvimBinaryOperator",
+ "default link NvimMod NvimBinaryOperator",
+
+ "default link NvimTernary NvimOperator",
+ "default link NvimTernaryColon NvimTernary",
+
+ "default link NvimParenthesis Delimiter",
+ "default link NvimLambda NvimParenthesis",
+ "default link NvimNestingParenthesis NvimParenthesis",
+ "default link NvimCallingParenthesis NvimParenthesis",
+
+ "default link NvimSubscript NvimParenthesis",
+ "default link NvimSubscriptBracket NvimSubscript",
+ "default link NvimSubscriptColon NvimSubscript",
+ "default link NvimCurly NvimSubscript",
+
+ "default link NvimContainer NvimParenthesis",
+ "default link NvimDict NvimContainer",
+ "default link NvimList NvimContainer",
+
+ "default link NvimIdentifier Identifier",
+ "default link NvimIdentifierScope NvimIdentifier",
+ "default link NvimIdentifierScopeDelimiter NvimIdentifier",
+ "default link NvimIdentifierName NvimIdentifier",
+ "default link NvimIdentifierKey NvimIdentifier",
+
+ "default link NvimColon Delimiter",
+ "default link NvimComma Delimiter",
+ "default link NvimArrow Delimiter",
+
+ "default link NvimRegister SpecialChar",
+ "default link NvimNumber Number",
+ "default link NvimFloat NvimNumber",
+ "default link NvimNumberPrefix Type",
+
+ "default link NvimOptionSigil Type",
+ "default link NvimOptionName NvimIdentifier",
+ "default link NvimOptionScope NvimIdentifierScope",
+ "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter",
+
+ "default link NvimEnvironmentSigil NvimOptionSigil",
+ "default link NvimEnvironmentName NvimIdentifier",
+
+ "default link NvimString String",
+ "default link NvimStringBody NvimString",
+ "default link NvimStringQuote NvimString",
+ "default link NvimStringSpecial SpecialChar",
+
+ "default link NvimSingleQuote NvimStringQuote",
+ "default link NvimSingleQuotedBody NvimStringBody",
+ "default link NvimSingleQuotedQuote NvimStringSpecial",
+
+ "default link NvimDoubleQuote NvimStringQuote",
+ "default link NvimDoubleQuotedBody NvimStringBody",
+ "default link NvimDoubleQuotedEscape NvimStringSpecial",
+
+ "default link NvimFigureBrace NvimInternalError",
+ "default link NvimSingleQuotedUnknownEscape NvimInternalError",
+
+ "default link NvimSpacing Normal",
+
+ // NvimInvalid groups:
+
+ "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError",
+
+ "default link NvimInvalid Error",
+
+ "default link NvimInvalidAssignment NvimInvalid",
+ "default link NvimInvalidPlainAssignment NvimInvalidAssignment",
+ "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment",
+ "default link NvimInvalidAssignmentWithAddition "
+ "NvimInvalidAugmentedAssignment",
+ "default link NvimInvalidAssignmentWithSubtraction "
+ "NvimInvalidAugmentedAssignment",
+ "default link NvimInvalidAssignmentWithConcatenation "
+ "NvimInvalidAugmentedAssignment",
+
+ "default link NvimInvalidOperator NvimInvalid",
+
+ "default link NvimInvalidUnaryOperator NvimInvalidOperator",
+ "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator",
+ "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator",
+ "default link NvimInvalidNot NvimInvalidUnaryOperator",
+
+ "default link NvimInvalidBinaryOperator NvimInvalidOperator",
+ "default link NvimInvalidComparison NvimInvalidBinaryOperator",
+ "default link NvimInvalidComparisonModifier NvimInvalidComparison",
+ "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator",
+ "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator",
+ "default link NvimInvalidConcat NvimInvalidBinaryOperator",
+ "default link NvimInvalidConcatOrSubscript NvimInvalidConcat",
+ "default link NvimInvalidOr NvimInvalidBinaryOperator",
+ "default link NvimInvalidAnd NvimInvalidBinaryOperator",
+ "default link NvimInvalidMultiplication NvimInvalidBinaryOperator",
+ "default link NvimInvalidDivision NvimInvalidBinaryOperator",
+ "default link NvimInvalidMod NvimInvalidBinaryOperator",
+
+ "default link NvimInvalidTernary NvimInvalidOperator",
+ "default link NvimInvalidTernaryColon NvimInvalidTernary",
+
+ "default link NvimInvalidDelimiter NvimInvalid",
+
+ "default link NvimInvalidParenthesis NvimInvalidDelimiter",
+ "default link NvimInvalidLambda NvimInvalidParenthesis",
+ "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis",
+ "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis",
+
+ "default link NvimInvalidSubscript NvimInvalidParenthesis",
+ "default link NvimInvalidSubscriptBracket NvimInvalidSubscript",
+ "default link NvimInvalidSubscriptColon NvimInvalidSubscript",
+ "default link NvimInvalidCurly NvimInvalidSubscript",
+
+ "default link NvimInvalidContainer NvimInvalidParenthesis",
+ "default link NvimInvalidDict NvimInvalidContainer",
+ "default link NvimInvalidList NvimInvalidContainer",
+
+ "default link NvimInvalidValue NvimInvalid",
+
+ "default link NvimInvalidIdentifier NvimInvalidValue",
+ "default link NvimInvalidIdentifierScope NvimInvalidIdentifier",
+ "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier",
+ "default link NvimInvalidIdentifierName NvimInvalidIdentifier",
+ "default link NvimInvalidIdentifierKey NvimInvalidIdentifier",
+
+ "default link NvimInvalidColon NvimInvalidDelimiter",
+ "default link NvimInvalidComma NvimInvalidDelimiter",
+ "default link NvimInvalidArrow NvimInvalidDelimiter",
+
+ "default link NvimInvalidRegister NvimInvalidValue",
+ "default link NvimInvalidNumber NvimInvalidValue",
+ "default link NvimInvalidFloat NvimInvalidNumber",
+ "default link NvimInvalidNumberPrefix NvimInvalidNumber",
+
+ "default link NvimInvalidOptionSigil NvimInvalidIdentifier",
+ "default link NvimInvalidOptionName NvimInvalidIdentifier",
+ "default link NvimInvalidOptionScope NvimInvalidIdentifierScope",
+ "default link NvimInvalidOptionScopeDelimiter "
+ "NvimInvalidIdentifierScopeDelimiter",
+
+ "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil",
+ "default link NvimInvalidEnvironmentName NvimInvalidIdentifier",
+
+ // Invalid string bodies and specials are still highlighted as valid ones to
+ // minimize the red area.
+ "default link NvimInvalidString NvimInvalidValue",
+ "default link NvimInvalidStringBody NvimStringBody",
+ "default link NvimInvalidStringQuote NvimInvalidString",
+ "default link NvimInvalidStringSpecial NvimStringSpecial",
+
+ "default link NvimInvalidSingleQuote NvimInvalidStringQuote",
+ "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody",
+ "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial",
+
+ "default link NvimInvalidDoubleQuote NvimInvalidStringQuote",
+ "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody",
+ "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial",
+ "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue",
+
+ "default link NvimInvalidFigureBrace NvimInvalidDelimiter",
+
+ "default link NvimInvalidSpacing ErrorMsg",
+
+ // Not actually invalid, but we highlight user that he is doing something
+ // wrong.
+ "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue",
+ NULL,
+};
+
+/// Create default links for Nvim* highlight groups used for cmdline coloring
+void syn_init_cmdline_highlight(bool reset, bool init)
{
- int i;
- char **pp;
- static int had_both = FALSE;
+ for (size_t i = 0 ; highlight_init_cmdline[i] != NULL ; i++) {
+ do_highlight(highlight_init_cmdline[i], reset, init);
+ }
+}
- /*
- * Try finding the color scheme file. Used when a color file was loaded
- * and 'background' or 't_Co' is changed.
- */
+/// Load colors from a file if "g:colors_name" is set, otherwise load builtin
+/// colors
+///
+/// @param both include groups where 'bg' doesn't matter
+/// @param reset clear groups first
+void init_highlight(bool both, bool reset)
+{
+ static int had_both = false;
+
+ // Try finding the color scheme file. Used when a color file was loaded
+ // and 'background' or 't_Co' is changed.
char_u *p = get_var_value("g:colors_name");
if (p != NULL) {
// Value of g:colors_name could be freed in load_colors() and make
@@ -6021,39 +6304,40 @@ init_highlight (
* Didn't use a color file, use the compiled-in colors.
*/
if (both) {
- had_both = TRUE;
- pp = highlight_init_both;
- for (i = 0; pp[i] != NULL; ++i)
- do_highlight((char_u *)pp[i], reset, TRUE);
- } else if (!had_both)
- /* Don't do anything before the call with both == TRUE from main().
- * Not everything has been setup then, and that call will overrule
- * everything anyway. */
+ had_both = true;
+ const char *const *const pp = highlight_init_both;
+ for (size_t i = 0; pp[i] != NULL; i++) {
+ do_highlight(pp[i], reset, true);
+ }
+ } else if (!had_both) {
+ // Don't do anything before the call with both == TRUE from main().
+ // Not everything has been setup then, and that call will overrule
+ // everything anyway.
return;
+ }
- if (*p_bg == 'l')
- pp = highlight_init_light;
- else
- pp = highlight_init_dark;
- for (i = 0; pp[i] != NULL; ++i)
- do_highlight((char_u *)pp[i], reset, TRUE);
+ const char *const *const pp = ((*p_bg == 'l')
+ ? highlight_init_light
+ : highlight_init_dark);
+ for (size_t i = 0; pp[i] != NULL; i++) {
+ do_highlight(pp[i], reset, true);
+ }
/* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
* depend on the number of colors available.
* With 8 colors brown is equal to yellow, need to use black for Search fg
* to avoid Statement highlighted text disappears.
* Clear the attributes, needed when changing the t_Co value. */
- if (t_colors > 8)
+ if (t_colors > 8) {
do_highlight(
- (char_u *)(*p_bg == 'l'
- ? "Visual cterm=NONE ctermbg=LightGrey"
- : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE,
- TRUE);
- else {
- do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
- FALSE, TRUE);
- if (*p_bg == 'l')
- do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
+ (*p_bg == 'l'
+ ? "Visual cterm=NONE ctermbg=LightGrey"
+ : "Visual cterm=NONE ctermbg=DarkGrey"), false, true);
+ } else {
+ do_highlight("Visual cterm=reverse ctermbg=NONE", false, true);
+ if (*p_bg == 'l') {
+ do_highlight("Search ctermfg=black", false, true);
+ }
}
/*
@@ -6070,6 +6354,7 @@ init_highlight (
recursive--;
}
}
+ syn_init_cmdline_highlight(false, false);
}
/*
@@ -6104,22 +6389,22 @@ int load_colors(char_u *name)
}
-/// Handle the ":highlight .." command.
-/// When using ":hi clear" this is called recursively for each group with
-/// "forceit" and "init" both TRUE.
-/// @param init TRUE when called for initializing
-void
-do_highlight(
- char_u *line,
- int forceit,
- int init
-)
+/// Handle ":highlight" command
+///
+/// When using ":highlight clear" this is called recursively for each group with
+/// forceit and init being both true.
+///
+/// @param[in] line Command arguments.
+/// @param[in] forceit True when bang is given, allows to link group even if
+/// it has its own settings.
+/// @param[in] init True when initializing.
+void do_highlight(const char *line, const bool forceit, const bool init)
+ FUNC_ATTR_NONNULL_ALL
{
- char_u *name_end;
- char_u *linep;
- char_u *key_start;
- char_u *arg_start;
- char_u *key = NULL, *arg = NULL;
+ const char *name_end;
+ const char *linep;
+ const char *key_start;
+ const char *arg_start;
long i;
int off;
int len;
@@ -6131,162 +6416,154 @@ do_highlight(
int dolink = FALSE;
int error = FALSE;
int color;
- int is_normal_group = FALSE; /* "Normal" group */
+ bool is_normal_group = false; // "Normal" group
- /*
- * If no argument, list current highlighting.
- */
- if (ends_excmd(*line)) {
- for (int i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
- /* TODO: only call when the group has attributes set */
+ // If no argument, list current highlighting.
+ if (ends_excmd((uint8_t)(*line))) {
+ for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
+ // TODO(brammool): only call when the group has attributes set
highlight_list_one(i);
+ }
return;
}
- /*
- * Isolate the name.
- */
- name_end = skiptowhite(line);
- linep = skipwhite(name_end);
+ // Isolate the name.
+ name_end = (const char *)skiptowhite((const char_u *)line);
+ linep = (const char *)skipwhite((const char_u *)name_end);
- /*
- * Check for "default" argument.
- */
- if (STRNCMP(line, "default", name_end - line) == 0) {
- dodefault = TRUE;
+ // Check for "default" argument.
+ if (strncmp(line, "default", name_end - line) == 0) {
+ dodefault = true;
line = linep;
- name_end = skiptowhite(line);
- linep = skipwhite(name_end);
+ name_end = (const char *)skiptowhite((const char_u *)line);
+ linep = (const char *)skipwhite((const char_u *)name_end);
}
- /*
- * Check for "clear" or "link" argument.
- */
- if (STRNCMP(line, "clear", name_end - line) == 0)
- doclear = TRUE;
- if (STRNCMP(line, "link", name_end - line) == 0)
- dolink = TRUE;
+ // Check for "clear" or "link" argument.
+ if (strncmp(line, "clear", name_end - line) == 0) {
+ doclear = true;
+ } else if (strncmp(line, "link", name_end - line) == 0) {
+ dolink = true;
+ }
- /*
- * ":highlight {group-name}": list highlighting for one group.
- */
- if (!doclear && !dolink && ends_excmd(*linep)) {
- id = syn_namen2id(line, (int)(name_end - line));
- if (id == 0)
- EMSG2(_("E411: highlight group not found: %s"), line);
- else
+ // ":highlight {group-name}": list highlighting for one group.
+ if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) {
+ id = syn_namen2id((const char_u *)line, (int)(name_end - line));
+ if (id == 0) {
+ emsgf(_("E411: highlight group not found: %s"), line);
+ } else {
highlight_list_one(id);
+ }
return;
}
- /*
- * Handle ":highlight link {from} {to}" command.
- */
+ // Handle ":highlight link {from} {to}" command.
if (dolink) {
- char_u *from_start = linep;
- char_u *from_end;
- char_u *to_start;
- char_u *to_end;
+ const char *from_start = linep;
+ const char *from_end;
+ const char *to_start;
+ const char *to_end;
int from_id;
int to_id;
- from_end = skiptowhite(from_start);
- to_start = skipwhite(from_end);
- to_end = skiptowhite(to_start);
+ from_end = (const char *)skiptowhite((const char_u *)from_start);
+ to_start = (const char *)skipwhite((const char_u *)from_end);
+ to_end = (const char *)skiptowhite((const char_u *)to_start);
- if (ends_excmd(*from_start) || ends_excmd(*to_start)) {
- EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
- from_start);
+ if (ends_excmd((uint8_t)(*from_start))
+ || ends_excmd((uint8_t)(*to_start))) {
+ emsgf(_("E412: Not enough arguments: \":highlight link %s\""),
+ from_start);
return;
}
- if (!ends_excmd(*skipwhite(to_end))) {
- EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
+ if (!ends_excmd(*skipwhite((const char_u *)to_end))) {
+ emsgf(_("E413: Too many arguments: \":highlight link %s\""), from_start);
return;
}
- from_id = syn_check_group(from_start, (int)(from_end - from_start));
- if (STRNCMP(to_start, "NONE", 4) == 0)
+ from_id = syn_check_group((const char_u *)from_start,
+ (int)(from_end - from_start));
+ if (strncmp(to_start, "NONE", 4) == 0) {
to_id = 0;
- else
- to_id = syn_check_group(to_start, (int)(to_end - to_start));
+ } else {
+ to_id = syn_check_group((const char_u *)to_start,
+ (int)(to_end - to_start));
+ }
if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) {
- /*
- * Don't allow a link when there already is some highlighting
- * for the group, unless '!' is used
- */
+ // Don't allow a link when there already is some highlighting
+ // for the group, unless '!' is used
if (to_id > 0 && !forceit && !init
&& hl_has_settings(from_id - 1, dodefault)) {
- if (sourcing_name == NULL && !dodefault)
+ if (sourcing_name == NULL && !dodefault) {
EMSG(_("E414: group has settings, highlight link ignored"));
+ }
} else {
if (!init)
HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
HL_TABLE()[from_id - 1].sg_link = to_id;
HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
+ HL_TABLE()[from_id - 1].sg_cleared = false;
redraw_all_later(SOME_VALID);
}
}
- /* Only call highlight_changed() once, after sourcing a syntax file */
- need_highlight_changed = TRUE;
+ // Only call highlight_changed() once, after sourcing a syntax file.
+ need_highlight_changed = true;
return;
}
if (doclear) {
- /*
- * ":highlight clear [group]" command.
- */
+ // ":highlight clear [group]" command.
line = linep;
- if (ends_excmd(*line)) {
+ if (ends_excmd((uint8_t)(*line))) {
do_unlet(S_LEN("colors_name"), true);
restore_cterm_colors();
- /*
- * Clear all default highlight groups and load the defaults.
- */
- for (int idx = 0; idx < highlight_ga.ga_len; ++idx) {
+ // Clear all default highlight groups and load the defaults.
+ for (int idx = 0; idx < highlight_ga.ga_len; idx++) {
highlight_clear(idx);
}
- init_highlight(TRUE, TRUE);
+ init_highlight(true, true);
highlight_changed();
redraw_later_clear();
return;
}
- name_end = skiptowhite(line);
- linep = skipwhite(name_end);
+ name_end = (const char *)skiptowhite((const char_u *)line);
+ linep = (const char *)skipwhite((const char_u *)name_end);
}
- /*
- * Find the group name in the table. If it does not exist yet, add it.
- */
- id = syn_check_group(line, (int)(name_end - line));
- if (id == 0) /* failed (out of memory) */
+ // Find the group name in the table. If it does not exist yet, add it.
+ id = syn_check_group((const char_u *)line, (int)(name_end - line));
+ if (id == 0) { // Failed (out of memory).
return;
- idx = id - 1; /* index is ID minus one */
+ }
+ idx = id - 1; // Index is ID minus one.
- /* Return if "default" was used and the group already has settings. */
- if (dodefault && hl_has_settings(idx, TRUE))
+ // Return if "default" was used and the group already has settings
+ if (dodefault && hl_has_settings(idx, true)) {
return;
+ }
- if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
- is_normal_group = TRUE;
+ is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0);
- /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
+ // Clear the highlighting for ":hi clear {group}" and ":hi clear".
if (doclear || (forceit && init)) {
highlight_clear(idx);
if (!doclear)
HL_TABLE()[idx].sg_set = 0;
}
- if (!doclear)
- while (!ends_excmd(*linep)) {
+ char *key = NULL;
+ char *arg = NULL;
+ if (!doclear) {
+ while (!ends_excmd((uint8_t)(*linep))) {
key_start = linep;
if (*linep == '=') {
- EMSG2(_("E415: unexpected equal sign: %s"), key_start);
- error = TRUE;
+ emsgf(_("E415: unexpected equal sign: %s"), key_start);
+ error = true;
break;
}
@@ -6296,61 +6573,58 @@ do_highlight(
linep++;
}
xfree(key);
- key = vim_strnsave_up(key_start, (int)(linep - key_start));
- linep = skipwhite(linep);
+ key = (char *)vim_strnsave_up((const char_u *)key_start,
+ (int)(linep - key_start));
+ linep = (const char *)skipwhite((const char_u *)linep);
- if (STRCMP(key, "NONE") == 0) {
+ if (strcmp(key, "NONE") == 0) {
if (!init || HL_TABLE()[idx].sg_set == 0) {
- if (!init)
+ if (!init) {
HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI;
+ }
highlight_clear(idx);
}
continue;
}
- /*
- * Check for the equal sign.
- */
+ // Check for the equal sign.
if (*linep != '=') {
- EMSG2(_("E416: missing equal sign: %s"), key_start);
- error = TRUE;
+ emsgf(_("E416: missing equal sign: %s"), key_start);
+ error = true;
break;
}
- ++linep;
+ linep++;
- /*
- * Isolate the argument.
- */
- linep = skipwhite(linep);
- if (*linep == '\'') { /* guifg='color name' */
+ // Isolate the argument.
+ linep = (const char *)skipwhite((const char_u *)linep);
+ if (*linep == '\'') { // guifg='color name'
arg_start = ++linep;
- linep = vim_strchr(linep, '\'');
+ linep = strchr(linep, '\'');
if (linep == NULL) {
- EMSG2(_(e_invarg2), key_start);
- error = TRUE;
+ emsgf(_(e_invarg2), key_start);
+ error = true;
break;
}
} else {
arg_start = linep;
- linep = skiptowhite(linep);
+ linep = (const char *)skiptowhite((const char_u *)linep);
}
if (linep == arg_start) {
- EMSG2(_("E417: missing argument: %s"), key_start);
- error = TRUE;
+ emsgf(_("E417: missing argument: %s"), key_start);
+ error = true;
break;
}
xfree(arg);
- arg = vim_strnsave(arg_start, (int)(linep - arg_start));
+ arg = xstrndup(arg_start, (size_t)(linep - arg_start));
- if (*linep == '\'')
- ++linep;
+ if (*linep == '\'') {
+ linep++;
+ }
- /*
- * Store the argument.
- */
- if ( STRCMP(key, "TERM") == 0
- || STRCMP(key, "CTERM") == 0
- || STRCMP(key, "GUI") == 0) {
+ // Store the argument.
+ if (strcmp(key, "TERM") == 0
+ || strcmp(key, "CTERM") == 0
+ || strcmp(key, "GUI") == 0) {
attr = 0;
off = 0;
while (arg[off] != NUL) {
@@ -6363,36 +6637,40 @@ do_highlight(
}
}
if (i < 0) {
- EMSG2(_("E418: Illegal value: %s"), arg);
- error = TRUE;
+ emsgf(_("E418: Illegal value: %s"), arg);
+ error = true;
break;
}
- if (arg[off] == ',') /* another one follows */
- ++off;
+ if (arg[off] == ',') { // Another one follows.
+ off++;
+ }
}
- if (error)
+ if (error) {
break;
+ }
if (*key == 'C') {
if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
- if (!init)
+ if (!init) {
HL_TABLE()[idx].sg_set |= SG_CTERM;
+ }
HL_TABLE()[idx].sg_cterm = attr;
HL_TABLE()[idx].sg_cterm_bold = FALSE;
}
} else if (*key == 'G') {
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
- if (!init)
+ if (!init) {
HL_TABLE()[idx].sg_set |= SG_GUI;
+ }
HL_TABLE()[idx].sg_gui = attr;
}
}
} else if (STRCMP(key, "FONT") == 0) {
- /* in non-GUI fonts are simply ignored */
- } else if (STRCMP(key,
- "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
+ // in non-GUI fonts are simply ignored
+ } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
- if (!init)
+ if (!init) {
HL_TABLE()[idx].sg_set |= SG_CTERM;
+ }
/* When setting the foreground color, and previously the "bold"
* flag was set for a light color, reset it now */
@@ -6401,14 +6679,14 @@ do_highlight(
HL_TABLE()[idx].sg_cterm_bold = FALSE;
}
- if (ascii_isdigit(*arg))
+ if (ascii_isdigit(*arg)) {
color = atoi((char *)arg);
- else if (STRICMP(arg, "fg") == 0) {
- if (cterm_normal_fg_color)
+ } else if (STRICMP(arg, "fg") == 0) {
+ if (cterm_normal_fg_color) {
color = cterm_normal_fg_color - 1;
- else {
+ } else {
EMSG(_("E419: FG color unknown"));
- error = TRUE;
+ error = true;
break;
}
} else if (STRICMP(arg, "bg") == 0) {
@@ -6416,79 +6694,94 @@ do_highlight(
color = cterm_normal_bg_color - 1;
else {
EMSG(_("E420: BG color unknown"));
- error = TRUE;
+ error = true;
break;
}
} else {
- static char *(color_names[28]) = {
+ static const char *color_names[] = {
"Black", "DarkBlue", "DarkGreen", "DarkCyan",
"DarkRed", "DarkMagenta", "Brown", "DarkYellow",
"Gray", "Grey",
"LightGray", "LightGrey", "DarkGray", "DarkGrey",
"Blue", "LightBlue", "Green", "LightGreen",
"Cyan", "LightCyan", "Red", "LightRed", "Magenta",
- "LightMagenta", "Yellow", "LightYellow", "White", "NONE"
+ "LightMagenta", "Yellow", "LightYellow", "White",
+ "NONE"
};
- static int color_numbers_16[28] = {0, 1, 2, 3,
- 4, 5, 6, 6,
- 7, 7,
- 7, 7, 8, 8,
- 9, 9, 10, 10,
- 11, 11, 12, 12, 13,
- 13, 14, 14, 15, -1};
- /* for xterm with 88 colors... */
- static int color_numbers_88[28] = {0, 4, 2, 6,
- 1, 5, 32, 72,
- 84, 84,
- 7, 7, 82, 82,
- 12, 43, 10, 61,
- 14, 63, 9, 74, 13,
- 75, 11, 78, 15, -1};
- /* for xterm with 256 colors... */
- static int color_numbers_256[28] = {0, 4, 2, 6,
- 1, 5, 130, 130,
- 248, 248,
- 7, 7, 242, 242,
- 12, 81, 10, 121,
- 14, 159, 9, 224, 13,
- 225, 11, 229, 15, -1};
- /* for terminals with less than 16 colors... */
- static int color_numbers_8[28] = {0, 4, 2, 6,
- 1, 5, 3, 3,
- 7, 7,
- 7, 7, 0+8, 0+8,
- 4+8, 4+8, 2+8, 2+8,
- 6+8, 6+8, 1+8, 1+8, 5+8,
- 5+8, 3+8, 3+8, 7+8, -1};
-
- /* reduce calls to STRICMP a bit, it can be slow */
+ static const int color_numbers_16[] = {
+ 0, 1, 2, 3,
+ 4, 5, 6, 6,
+ 7, 7,
+ 7, 7, 8, 8,
+ 9, 9, 10, 10,
+ 11, 11, 12, 12, 13,
+ 13, 14, 14, 15,
+ -1
+ };
+ // For xterm with 88 colors:
+ static int color_numbers_88[] = {
+ 0, 4, 2, 6,
+ 1, 5, 32, 72,
+ 84, 84,
+ 7, 7, 82, 82,
+ 12, 43, 10, 61,
+ 14, 63, 9, 74, 13,
+ 75, 11, 78, 15,
+ -1
+ };
+ // For xterm with 256 colors:
+ static int color_numbers_256[] = {
+ 0, 4, 2, 6,
+ 1, 5, 130, 130,
+ 248, 248,
+ 7, 7, 242, 242,
+ 12, 81, 10, 121,
+ 14, 159, 9, 224, 13,
+ 225, 11, 229, 15,
+ -1
+ };
+ // For terminals with less than 16 colors:
+ static int color_numbers_8[28] = {
+ 0, 4, 2, 6,
+ 1, 5, 3, 3,
+ 7, 7,
+ 7, 7, 0+8, 0+8,
+ 4+8, 4+8, 2+8, 2+8,
+ 6+8, 6+8, 1+8, 1+8, 5+8,
+ 5+8, 3+8, 3+8, 7+8,
+ -1
+ };
+
+ // Reduce calls to STRICMP a bit, it can be slow.
off = TOUPPER_ASC(*arg);
- for (i = ARRAY_SIZE(color_names); --i >= 0; )
+ for (i = ARRAY_SIZE(color_names); --i >= 0; ) {
if (off == color_names[i][0]
- && STRICMP(arg + 1, color_names[i] + 1) == 0)
+ && STRICMP(arg + 1, color_names[i] + 1) == 0) {
break;
+ }
+ }
if (i < 0) {
- EMSG2(_(
- "E421: Color name or number not recognized: %s"),
- key_start);
- error = TRUE;
+ emsgf(_("E421: Color name or number not recognized: %s"),
+ key_start);
+ error = true;
break;
}
- /* Use the _16 table to check if its a valid color name. */
+ // Use the _16 table to check if its a valid color name.
color = color_numbers_16[i];
if (color >= 0) {
if (t_colors == 8) {
- /* t_Co is 8: use the 8 colors table */
+ // t_Co is 8: use the 8 colors table.
color = color_numbers_8[i];
if (key[5] == 'F') {
/* set/reset bold attribute to get light foreground
* colors (on some terminals, e.g. "linux") */
if (color & 8) {
HL_TABLE()[idx].sg_cterm |= HL_BOLD;
- HL_TABLE()[idx].sg_cterm_bold = TRUE;
- } else
+ HL_TABLE()[idx].sg_cterm_bold = true;
+ } else {
HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
+ }
}
color &= 7; // truncate to 8 colors
} else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) {
@@ -6502,16 +6795,13 @@ do_highlight(
}
}
}
- /* Add one to the argument, to avoid zero. Zero is used for
- * "NONE", then "color" is -1. */
+ // Add one to the argument, to avoid zero. Zero is used for
+ // "NONE", then "color" is -1.
if (key[5] == 'F') {
HL_TABLE()[idx].sg_cterm_fg = color + 1;
if (is_normal_group) {
cterm_normal_fg_color = color + 1;
- cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
- {
- must_redraw = CLEAR;
- }
+ must_redraw = CLEAR;
}
} else {
HL_TABLE()[idx].sg_cterm_bg = color + 1;
@@ -6535,15 +6825,15 @@ do_highlight(
}
}
}
- } else if (STRCMP(key, "GUIFG") == 0) {
+ } else if (strcmp(key, "GUIFG") == 0) {
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
if (!init)
HL_TABLE()[idx].sg_set |= SG_GUI;
xfree(HL_TABLE()[idx].sg_rgb_fg_name);
- if (STRCMP(arg, "NONE")) {
- HL_TABLE()[idx].sg_rgb_fg_name = (uint8_t *)xstrdup((char *)arg);
- HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg);
+ if (strcmp(arg, "NONE")) {
+ HL_TABLE()[idx].sg_rgb_fg_name = (char_u *)xstrdup((char *)arg);
+ HL_TABLE()[idx].sg_rgb_fg = name_to_color((const char_u *)arg);
} else {
HL_TABLE()[idx].sg_rgb_fg_name = NULL;
HL_TABLE()[idx].sg_rgb_fg = -1;
@@ -6560,8 +6850,8 @@ do_highlight(
xfree(HL_TABLE()[idx].sg_rgb_bg_name);
if (STRCMP(arg, "NONE") != 0) {
- HL_TABLE()[idx].sg_rgb_bg_name = (uint8_t *)xstrdup((char *)arg);
- HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg);
+ HL_TABLE()[idx].sg_rgb_bg_name = (char_u *)xstrdup((char *)arg);
+ HL_TABLE()[idx].sg_rgb_bg = name_to_color((const char_u *)arg);
} else {
HL_TABLE()[idx].sg_rgb_bg_name = NULL;
HL_TABLE()[idx].sg_rgb_bg = -1;
@@ -6571,15 +6861,15 @@ do_highlight(
if (is_normal_group) {
normal_bg = HL_TABLE()[idx].sg_rgb_bg;
}
- } else if (STRCMP(key, "GUISP") == 0) {
+ } else if (strcmp(key, "GUISP") == 0) {
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
if (!init)
HL_TABLE()[idx].sg_set |= SG_GUI;
xfree(HL_TABLE()[idx].sg_rgb_sp_name);
- if (STRCMP(arg, "NONE") != 0) {
- HL_TABLE()[idx].sg_rgb_sp_name = (uint8_t *)xstrdup((char *)arg);
- HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg);
+ if (strcmp(arg, "NONE") != 0) {
+ HL_TABLE()[idx].sg_rgb_sp_name = (char_u *)xstrdup((char *)arg);
+ HL_TABLE()[idx].sg_rgb_sp = name_to_color((const char_u *)arg);
} else {
HL_TABLE()[idx].sg_rgb_sp_name = NULL;
HL_TABLE()[idx].sg_rgb_sp = -1;
@@ -6589,49 +6879,46 @@ do_highlight(
if (is_normal_group) {
normal_sp = HL_TABLE()[idx].sg_rgb_sp;
}
- } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) {
+ } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) {
// Ignored for now
} else {
- EMSG2(_("E423: Illegal argument: %s"), key_start);
- error = TRUE;
+ emsgf(_("E423: Illegal argument: %s"), key_start);
+ error = true;
break;
}
+ HL_TABLE()[idx].sg_cleared = false;
- /*
- * When highlighting has been given for a group, don't link it.
- */
- if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
+ // When highlighting has been given for a group, don't link it.
+ if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) {
HL_TABLE()[idx].sg_link = 0;
+ }
- /*
- * Continue with next argument.
- */
- linep = skipwhite(linep);
+ // Continue with next argument.
+ linep = (const char *)skipwhite((const char_u *)linep);
}
+ }
- /*
- * If there is an error, and it's a new entry, remove it from the table.
- */
- if (error && idx == highlight_ga.ga_len)
+ // If there is an error, and it's a new entry, remove it from the table.
+ if (error && idx == highlight_ga.ga_len) {
syn_unadd_group();
- else {
- if (is_normal_group) {
- HL_TABLE()[idx].sg_attr = 0;
+ } else {
+ if (!error && is_normal_group) {
// Need to update all groups, because they might be using "bg" and/or
// "fg", which have been changed now.
highlight_attr_set_all();
// If the normal group has changed, it is simpler to refresh every UI
ui_refresh();
- } else
+ } else {
set_hl_attr(idx);
+ }
HL_TABLE()[idx].sg_scriptID = current_SID;
redraw_all_later(NOT_VALID);
}
xfree(key);
xfree(arg);
- /* Only call highlight_changed() once, after sourcing a syntax file */
- need_highlight_changed = TRUE;
+ // Only call highlight_changed() once, after sourcing a syntax file
+ need_highlight_changed = true;
}
#if defined(EXITFREE)
@@ -6657,7 +6944,6 @@ void restore_cterm_colors(void)
normal_bg = -1;
normal_sp = -1;
cterm_normal_fg_color = 0;
- cterm_normal_fg_bold = 0;
cterm_normal_bg_color = 0;
}
@@ -6681,6 +6967,8 @@ static int hl_has_settings(int idx, int check_link)
*/
static void highlight_clear(int idx)
{
+ HL_TABLE()[idx].sg_cleared = true;
+
HL_TABLE()[idx].sg_attr = 0;
HL_TABLE()[idx].sg_cterm = 0;
HL_TABLE()[idx].sg_cterm_bold = FALSE;
@@ -6704,37 +6992,36 @@ static void highlight_clear(int idx)
}
-/*
- * Table with the specifications for an attribute number.
- * Note that this table is used by ALL buffers. This is required because the
- * GUI can redraw at any time for any buffer.
- */
+/// Table with the specifications for an attribute number.
+/// Note that this table is used by ALL buffers. This is required because the
+/// GUI can redraw at any time for any buffer.
static garray_T attr_table = GA_EMPTY_INIT_VALUE;
-#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx]
+static inline HlAttrs * ATTR_ENTRY(int idx)
+{
+ return &((HlAttrs *)attr_table.ga_data)[idx];
+}
/// Return the attr number for a set of colors and font.
/// Add a new entry to the term_attr_table, attr_table or gui_attr_table
/// if the combination is new.
/// @return 0 for error.
-int get_attr_entry(attrentry_T *aep)
+int get_attr_entry(HlAttrs *aep)
{
garray_T *table = &attr_table;
- attrentry_T *taep;
- static int recursive = FALSE;
+ HlAttrs *taep;
+ static int recursive = false;
/*
* Init the table, in case it wasn't done yet.
*/
- table->ga_itemsize = sizeof(attrentry_T);
+ table->ga_itemsize = sizeof(HlAttrs);
ga_set_growsize(table, 7);
- /*
- * Try to find an entry with the same specifications.
- */
- for (int i = 0; i < table->ga_len; ++i) {
- taep = &(((attrentry_T *)table->ga_data)[i]);
+ // Try to find an entry with the same specifications.
+ for (int i = 0; i < table->ga_len; i++) {
+ taep = &(((HlAttrs *)table->ga_data)[i]);
if (aep->cterm_ae_attr == taep->cterm_ae_attr
&& aep->cterm_fg_color == taep->cterm_fg_color
&& aep->cterm_bg_color == taep->cterm_bg_color
@@ -6771,7 +7058,7 @@ int get_attr_entry(attrentry_T *aep)
// This is a new combination of colors and font, add an entry.
- taep = GA_APPEND_VIA_PTR(attrentry_T, table);
+ taep = GA_APPEND_VIA_PTR(HlAttrs, table);
memset(taep, 0, sizeof(*taep));
taep->cterm_ae_attr = aep->cterm_ae_attr;
taep->cterm_fg_color = aep->cterm_fg_color;
@@ -6799,9 +7086,9 @@ void clear_hl_tables(void)
// Return the resulting attributes.
int hl_combine_attr(int char_attr, int prim_attr)
{
- attrentry_T *char_aep = NULL;
- attrentry_T *spell_aep;
- attrentry_T new_en;
+ HlAttrs *char_aep = NULL;
+ HlAttrs *spell_aep;
+ HlAttrs new_en = HLATTRS_INIT;
if (char_attr == 0) {
return prim_attr;
@@ -6817,8 +7104,6 @@ int hl_combine_attr(int char_attr, int prim_attr)
if (char_aep != NULL) {
// Copy all attributes from char_aep to the new entry
new_en = *char_aep;
- } else {
- memset(&new_en, 0, sizeof(new_en));
}
spell_aep = syn_cterm_attr2entry(prim_attr);
@@ -6849,17 +7134,25 @@ int hl_combine_attr(int char_attr, int prim_attr)
return get_attr_entry(&new_en);
}
-attrentry_T *syn_cterm_attr2entry(int attr)
+/// \note this function does not apply exclusively to cterm attr contrary
+/// to what its name implies
+/// \warn don't call it with attr 0 (i.e., the null attribute)
+HlAttrs *syn_cterm_attr2entry(int attr)
{
attr -= ATTR_OFF;
- if (attr >= attr_table.ga_len) /* did ":syntax clear" */
+ if (attr >= attr_table.ga_len) {
+ // did ":syntax clear"
return NULL;
- return &(ATTR_ENTRY(attr));
+ }
+ return ATTR_ENTRY(attr);
}
+/// \addtogroup LIST_XXX
+/// @{
#define LIST_ATTR 1
#define LIST_STRING 2
#define LIST_INT 3
+/// @}
static void highlight_list_one(int id)
{
@@ -6898,7 +7191,13 @@ static void highlight_list_one(int id)
last_set_msg(sgp->sg_scriptID);
}
-static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name)
+/// Outputs a highlight when doing ":hi MyHighlight"
+///
+/// @param type one of \ref LIST_XXX
+/// @param iarg integer argument used if \p type == LIST_INT
+/// @param sarg string used if \p type == LIST_STRING
+static int highlight_list_arg(int id, int didh, int type, int iarg,
+ char_u *sarg, const char *name)
{
char_u buf[100];
char_u *ts;
@@ -7038,24 +7337,23 @@ const char *highlight_color(const int id, const char *const what,
return NULL;
}
-/*
- * Output the syntax list header.
- * Return TRUE when started a new line.
- */
-static int
-syn_list_header (
- int did_header, /* did header already */
- int outlen, /* length of string that comes */
- int id /* highlight group id */
-)
+/// Output the syntax list header.
+///
+/// @param did_header did header already
+/// @param outlen length of string that comes
+/// @param id highlight group id
+/// @return true when started a new line.
+static int
+syn_list_header(int did_header, int outlen, int id)
{
int endcol = 19;
int newline = TRUE;
if (!did_header) {
msg_putchar('\n');
- if (got_int)
- return TRUE;
+ if (got_int) {
+ return true;
+ }
msg_outtrans(HL_TABLE()[id - 1].sg_name);
endcol = 15;
} else if (msg_col + outlen + 1 >= Columns) {
@@ -7083,21 +7381,14 @@ syn_list_header (
return newline;
}
-/*
- * Set the attribute numbers for a highlight group.
- * Called after one of the attributes has changed.
- */
-static void
-set_hl_attr (
- int idx /* index in array */
-)
+/// Set the attribute numbers for a highlight group.
+/// Called after one of the attributes has changed.
+/// @param idx corrected highlight index
+static void set_hl_attr(int idx)
{
- attrentry_T at_en;
+ HlAttrs at_en = HLATTRS_INIT;
struct hl_group *sgp = HL_TABLE() + idx;
- /* The "Normal" group doesn't need an attribute number */
- if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
- return;
at_en.cterm_ae_attr = sgp->sg_cterm;
at_en.cterm_fg_color = sgp->sg_cterm_fg;
@@ -7121,10 +7412,10 @@ set_hl_attr (
}
}
-/*
- * Lookup a highlight group name and return it's ID.
- * If it is not found, 0 is returned.
- */
+/// Lookup a highlight group name and return its ID.
+///
+/// @param highlight name e.g. 'Cursor', 'Normal'
+/// @return the highlight id, else 0 if \p name does not exist
int syn_name2id(const char_u *name)
{
int i;
@@ -7164,7 +7455,7 @@ char_u *syn_id2name(int id)
/*
* Like syn_name2id(), but take a pointer + length argument.
*/
-int syn_namen2id(char_u *linep, int len)
+int syn_namen2id(const char_u *linep, int len)
{
char_u *name = vim_strnsave(linep, len);
int id = syn_name2id(name);
@@ -7173,14 +7464,14 @@ int syn_namen2id(char_u *linep, int len)
return id;
}
-/// Find highlight group name in the table and return it's ID.
+/// Find highlight group name in the table and return its ID.
/// If it doesn't exist yet, a new entry is created.
///
/// @param pp Highlight group name
/// @param len length of \p pp
///
/// @return 0 for failure else the id of the group
-int syn_check_group(char_u *pp, int len)
+int syn_check_group(const char_u *pp, int len)
{
char_u *name = vim_strnsave(pp, len);
int id = syn_name2id(name);
@@ -7192,11 +7483,11 @@ int syn_check_group(char_u *pp, int len)
return id;
}
-/*
- * Add new highlight group and return it's ID.
- * "name" must be an allocated string, it will be consumed.
- * Return 0 for failure.
- */
+/// Add new highlight group and return it's ID.
+///
+/// @param name must be an allocated string, it will be consumed.
+/// @return 0 for failure, else the allocated group id
+/// @see syn_check_group syn_unadd_group
static int syn_add_group(char_u *name)
{
char_u *p;
@@ -7234,25 +7525,26 @@ static int syn_add_group(char_u *name)
struct hl_group* hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga);
memset(hlgp, 0, sizeof(*hlgp));
hlgp->sg_name = name;
+ hlgp->sg_rgb_bg = -1;
+ hlgp->sg_rgb_fg = -1;
+ hlgp->sg_rgb_sp = -1;
hlgp->sg_name_u = vim_strsave_up(name);
return highlight_ga.ga_len; /* ID is index plus one */
}
-/*
- * When, just after calling syn_add_group(), an error is discovered, this
- * function deletes the new name.
- */
+/// When, just after calling syn_add_group(), an error is discovered, this
+/// function deletes the new name.
static void syn_unadd_group(void)
{
- --highlight_ga.ga_len;
+ highlight_ga.ga_len--;
xfree(HL_TABLE()[highlight_ga.ga_len].sg_name);
xfree(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
}
-/*
- * Translate a group ID to highlight attributes.
- */
+
+/// Translate a group ID to highlight attributes.
+/// @see syn_cterm_attr2entry
int syn_id2attr(int hl_id)
{
struct hl_group *sgp;
@@ -7466,14 +7758,28 @@ static void highlight_list_two(int cnt, int attr)
}
-/*
- * Function given to ExpandGeneric() to obtain the list of group names.
- * Also used for synIDattr() function.
- */
-const char *get_highlight_name(expand_T *const xp, const int idx)
+/// Function given to ExpandGeneric() to obtain the list of group names.
+const char *get_highlight_name(expand_T *const xp, int idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
- // TODO(justinmk): 'xp' is unused
+ return get_highlight_name_ext(xp, idx, true);
+}
+
+
+/// Obtain a highlight group name.
+/// When "skip_cleared" is TRUE don't return a cleared entry.
+const char *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (idx < 0) {
+ return NULL;
+ }
+
+ // Items are never removed from the table, skip the ones that were cleared.
+ if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) {
+ return "";
+ }
+
if (idx == highlight_ga.ga_len && include_none != 0) {
return "none";
} else if (idx == highlight_ga.ga_len + include_none
@@ -7485,7 +7791,7 @@ const char *get_highlight_name(expand_T *const xp, const int idx)
} else if (idx == highlight_ga.ga_len + include_none + include_default + 1
&& include_link != 0) {
return "clear";
- } else if (idx < 0 || idx >= highlight_ga.ga_len) {
+ } else if (idx >= highlight_ga.ga_len) {
return NULL;
}
return (const char *)HL_TABLE()[idx].sg_name;
@@ -7493,685 +7799,685 @@ const char *get_highlight_name(expand_T *const xp, const int idx)
color_name_table_T color_name_table[] = {
// Colors from rgb.txt
- { "AliceBlue", RGB(0xf0, 0xf8, 0xff) },
- { "AntiqueWhite", RGB(0xfa, 0xeb, 0xd7) },
- { "AntiqueWhite1", RGB(0xff, 0xef, 0xdb) },
- { "AntiqueWhite2", RGB(0xee, 0xdf, 0xcc) },
- { "AntiqueWhite3", RGB(0xcd, 0xc0, 0xb0) },
- { "AntiqueWhite4", RGB(0x8b, 0x83, 0x78) },
- { "Aqua", RGB(0x00, 0xff, 0xff) },
- { "Aquamarine", RGB(0x7f, 0xff, 0xd4) },
- { "Aquamarine1", RGB(0x7f, 0xff, 0xd4) },
- { "Aquamarine2", RGB(0x76, 0xee, 0xc6) },
- { "Aquamarine3", RGB(0x66, 0xcd, 0xaa) },
- { "Aquamarine4", RGB(0x45, 0x8b, 0x74) },
- { "Azure", RGB(0xf0, 0xff, 0xff) },
- { "Azure1", RGB(0xf0, 0xff, 0xff) },
- { "Azure2", RGB(0xe0, 0xee, 0xee) },
- { "Azure3", RGB(0xc1, 0xcd, 0xcd) },
- { "Azure4", RGB(0x83, 0x8b, 0x8b) },
- { "Beige", RGB(0xf5, 0xf5, 0xdc) },
- { "Bisque", RGB(0xff, 0xe4, 0xc4) },
- { "Bisque1", RGB(0xff, 0xe4, 0xc4) },
- { "Bisque2", RGB(0xee, 0xd5, 0xb7) },
- { "Bisque3", RGB(0xcd, 0xb7, 0x9e) },
- { "Bisque4", RGB(0x8b, 0x7d, 0x6b) },
- { "Black", RGB(0x00, 0x00, 0x00) },
- { "BlanchedAlmond", RGB(0xff, 0xeb, 0xcd) },
- { "Blue", RGB(0x00, 0x00, 0xff) },
- { "Blue1", RGB(0x0, 0x0, 0xff) },
- { "Blue2", RGB(0x0, 0x0, 0xee) },
- { "Blue3", RGB(0x0, 0x0, 0xcd) },
- { "Blue4", RGB(0x0, 0x0, 0x8b) },
- { "BlueViolet", RGB(0x8a, 0x2b, 0xe2) },
- { "Brown", RGB(0xa5, 0x2a, 0x2a) },
- { "Brown1", RGB(0xff, 0x40, 0x40) },
- { "Brown2", RGB(0xee, 0x3b, 0x3b) },
- { "Brown3", RGB(0xcd, 0x33, 0x33) },
- { "Brown4", RGB(0x8b, 0x23, 0x23) },
- { "BurlyWood", RGB(0xde, 0xb8, 0x87) },
- { "Burlywood1", RGB(0xff, 0xd3, 0x9b) },
- { "Burlywood2", RGB(0xee, 0xc5, 0x91) },
- { "Burlywood3", RGB(0xcd, 0xaa, 0x7d) },
- { "Burlywood4", RGB(0x8b, 0x73, 0x55) },
- { "CadetBlue", RGB(0x5f, 0x9e, 0xa0) },
- { "CadetBlue1", RGB(0x98, 0xf5, 0xff) },
- { "CadetBlue2", RGB(0x8e, 0xe5, 0xee) },
- { "CadetBlue3", RGB(0x7a, 0xc5, 0xcd) },
- { "CadetBlue4", RGB(0x53, 0x86, 0x8b) },
- { "ChartReuse", RGB(0x7f, 0xff, 0x00) },
- { "Chartreuse1", RGB(0x7f, 0xff, 0x0) },
- { "Chartreuse2", RGB(0x76, 0xee, 0x0) },
- { "Chartreuse3", RGB(0x66, 0xcd, 0x0) },
- { "Chartreuse4", RGB(0x45, 0x8b, 0x0) },
- { "Chocolate", RGB(0xd2, 0x69, 0x1e) },
- { "Chocolate1", RGB(0xff, 0x7f, 0x24) },
- { "Chocolate2", RGB(0xee, 0x76, 0x21) },
- { "Chocolate3", RGB(0xcd, 0x66, 0x1d) },
- { "Chocolate4", RGB(0x8b, 0x45, 0x13) },
- { "Coral", RGB(0xff, 0x7f, 0x50) },
- { "Coral1", RGB(0xff, 0x72, 0x56) },
- { "Coral2", RGB(0xee, 0x6a, 0x50) },
- { "Coral3", RGB(0xcd, 0x5b, 0x45) },
- { "Coral4", RGB(0x8b, 0x3e, 0x2f) },
- { "CornFlowerBlue", RGB(0x64, 0x95, 0xed) },
- { "Cornsilk", RGB(0xff, 0xf8, 0xdc) },
- { "Cornsilk1", RGB(0xff, 0xf8, 0xdc) },
- { "Cornsilk2", RGB(0xee, 0xe8, 0xcd) },
- { "Cornsilk3", RGB(0xcd, 0xc8, 0xb1) },
- { "Cornsilk4", RGB(0x8b, 0x88, 0x78) },
- { "Crimson", RGB(0xdc, 0x14, 0x3c) },
- { "Cyan", RGB(0x00, 0xff, 0xff) },
- { "Cyan1", RGB(0x0, 0xff, 0xff) },
- { "Cyan2", RGB(0x0, 0xee, 0xee) },
- { "Cyan3", RGB(0x0, 0xcd, 0xcd) },
- { "Cyan4", RGB(0x0, 0x8b, 0x8b) },
- { "DarkBlue", RGB(0x00, 0x00, 0x8b) },
- { "DarkCyan", RGB(0x00, 0x8b, 0x8b) },
- { "DarkGoldenRod", RGB(0xb8, 0x86, 0x0b) },
- { "DarkGoldenrod1", RGB(0xff, 0xb9, 0xf) },
- { "DarkGoldenrod2", RGB(0xee, 0xad, 0xe) },
- { "DarkGoldenrod3", RGB(0xcd, 0x95, 0xc) },
- { "DarkGoldenrod4", RGB(0x8b, 0x65, 0x8) },
- { "DarkGray", RGB(0xa9, 0xa9, 0xa9) },
- { "DarkGreen", RGB(0x00, 0x64, 0x00) },
- { "DarkGrey", RGB(0xa9, 0xa9, 0xa9) },
- { "DarkKhaki", RGB(0xbd, 0xb7, 0x6b) },
- { "DarkMagenta", RGB(0x8b, 0x00, 0x8b) },
- { "DarkOliveGreen", RGB(0x55, 0x6b, 0x2f) },
- { "DarkOliveGreen1", RGB(0xca, 0xff, 0x70) },
- { "DarkOliveGreen2", RGB(0xbc, 0xee, 0x68) },
- { "DarkOliveGreen3", RGB(0xa2, 0xcd, 0x5a) },
- { "DarkOliveGreen4", RGB(0x6e, 0x8b, 0x3d) },
- { "DarkOrange", RGB(0xff, 0x8c, 0x00) },
- { "DarkOrange1", RGB(0xff, 0x7f, 0x0) },
- { "DarkOrange2", RGB(0xee, 0x76, 0x0) },
- { "DarkOrange3", RGB(0xcd, 0x66, 0x0) },
- { "DarkOrange4", RGB(0x8b, 0x45, 0x0) },
- { "DarkOrchid", RGB(0x99, 0x32, 0xcc) },
- { "DarkOrchid1", RGB(0xbf, 0x3e, 0xff) },
- { "DarkOrchid2", RGB(0xb2, 0x3a, 0xee) },
- { "DarkOrchid3", RGB(0x9a, 0x32, 0xcd) },
- { "DarkOrchid4", RGB(0x68, 0x22, 0x8b) },
- { "DarkRed", RGB(0x8b, 0x00, 0x00) },
- { "DarkSalmon", RGB(0xe9, 0x96, 0x7a) },
- { "DarkSeaGreen", RGB(0x8f, 0xbc, 0x8f) },
- { "DarkSeaGreen1", RGB(0xc1, 0xff, 0xc1) },
- { "DarkSeaGreen2", RGB(0xb4, 0xee, 0xb4) },
- { "DarkSeaGreen3", RGB(0x9b, 0xcd, 0x9b) },
- { "DarkSeaGreen4", RGB(0x69, 0x8b, 0x69) },
- { "DarkSlateBlue", RGB(0x48, 0x3d, 0x8b) },
- { "DarkSlateGray", RGB(0x2f, 0x4f, 0x4f) },
- { "DarkSlateGray1", RGB(0x97, 0xff, 0xff) },
- { "DarkSlateGray2", RGB(0x8d, 0xee, 0xee) },
- { "DarkSlateGray3", RGB(0x79, 0xcd, 0xcd) },
- { "DarkSlateGray4", RGB(0x52, 0x8b, 0x8b) },
- { "DarkSlateGrey", RGB(0x2f, 0x4f, 0x4f) },
- { "DarkTurquoise", RGB(0x00, 0xce, 0xd1) },
- { "DarkViolet", RGB(0x94, 0x00, 0xd3) },
- { "DarkYellow", RGB(0xbb, 0xbb, 0x00) },
- { "DeepPink", RGB(0xff, 0x14, 0x93) },
- { "DeepPink1", RGB(0xff, 0x14, 0x93) },
- { "DeepPink2", RGB(0xee, 0x12, 0x89) },
- { "DeepPink3", RGB(0xcd, 0x10, 0x76) },
- { "DeepPink4", RGB(0x8b, 0xa, 0x50) },
- { "DeepSkyBlue", RGB(0x00, 0xbf, 0xff) },
- { "DeepSkyBlue1", RGB(0x0, 0xbf, 0xff) },
- { "DeepSkyBlue2", RGB(0x0, 0xb2, 0xee) },
- { "DeepSkyBlue3", RGB(0x0, 0x9a, 0xcd) },
- { "DeepSkyBlue4", RGB(0x0, 0x68, 0x8b) },
- { "DimGray", RGB(0x69, 0x69, 0x69) },
- { "DimGrey", RGB(0x69, 0x69, 0x69) },
- { "DodgerBlue", RGB(0x1e, 0x90, 0xff) },
- { "DodgerBlue1", RGB(0x1e, 0x90, 0xff) },
- { "DodgerBlue2", RGB(0x1c, 0x86, 0xee) },
- { "DodgerBlue3", RGB(0x18, 0x74, 0xcd) },
- { "DodgerBlue4", RGB(0x10, 0x4e, 0x8b) },
- { "Firebrick", RGB(0xb2, 0x22, 0x22) },
- { "Firebrick1", RGB(0xff, 0x30, 0x30) },
- { "Firebrick2", RGB(0xee, 0x2c, 0x2c) },
- { "Firebrick3", RGB(0xcd, 0x26, 0x26) },
- { "Firebrick4", RGB(0x8b, 0x1a, 0x1a) },
- { "FloralWhite", RGB(0xff, 0xfa, 0xf0) },
- { "ForestGreen", RGB(0x22, 0x8b, 0x22) },
- { "Fuchsia", RGB(0xff, 0x00, 0xff) },
- { "Gainsboro", RGB(0xdc, 0xdc, 0xdc) },
- { "GhostWhite", RGB(0xf8, 0xf8, 0xff) },
- { "Gold", RGB(0xff, 0xd7, 0x00) },
- { "Gold1", RGB(0xff, 0xd7, 0x0) },
- { "Gold2", RGB(0xee, 0xc9, 0x0) },
- { "Gold3", RGB(0xcd, 0xad, 0x0) },
- { "Gold4", RGB(0x8b, 0x75, 0x0) },
- { "GoldenRod", RGB(0xda, 0xa5, 0x20) },
- { "Goldenrod1", RGB(0xff, 0xc1, 0x25) },
- { "Goldenrod2", RGB(0xee, 0xb4, 0x22) },
- { "Goldenrod3", RGB(0xcd, 0x9b, 0x1d) },
- { "Goldenrod4", RGB(0x8b, 0x69, 0x14) },
- { "Gray", RGB(0x80, 0x80, 0x80) },
- { "Gray0", RGB(0x0, 0x0, 0x0) },
- { "Gray1", RGB(0x3, 0x3, 0x3) },
- { "Gray10", RGB(0x1a, 0x1a, 0x1a) },
- { "Gray100", RGB(0xff, 0xff, 0xff) },
- { "Gray11", RGB(0x1c, 0x1c, 0x1c) },
- { "Gray12", RGB(0x1f, 0x1f, 0x1f) },
- { "Gray13", RGB(0x21, 0x21, 0x21) },
- { "Gray14", RGB(0x24, 0x24, 0x24) },
- { "Gray15", RGB(0x26, 0x26, 0x26) },
- { "Gray16", RGB(0x29, 0x29, 0x29) },
- { "Gray17", RGB(0x2b, 0x2b, 0x2b) },
- { "Gray18", RGB(0x2e, 0x2e, 0x2e) },
- { "Gray19", RGB(0x30, 0x30, 0x30) },
- { "Gray2", RGB(0x5, 0x5, 0x5) },
- { "Gray20", RGB(0x33, 0x33, 0x33) },
- { "Gray21", RGB(0x36, 0x36, 0x36) },
- { "Gray22", RGB(0x38, 0x38, 0x38) },
- { "Gray23", RGB(0x3b, 0x3b, 0x3b) },
- { "Gray24", RGB(0x3d, 0x3d, 0x3d) },
- { "Gray25", RGB(0x40, 0x40, 0x40) },
- { "Gray26", RGB(0x42, 0x42, 0x42) },
- { "Gray27", RGB(0x45, 0x45, 0x45) },
- { "Gray28", RGB(0x47, 0x47, 0x47) },
- { "Gray29", RGB(0x4a, 0x4a, 0x4a) },
- { "Gray3", RGB(0x8, 0x8, 0x8) },
- { "Gray30", RGB(0x4d, 0x4d, 0x4d) },
- { "Gray31", RGB(0x4f, 0x4f, 0x4f) },
- { "Gray32", RGB(0x52, 0x52, 0x52) },
- { "Gray33", RGB(0x54, 0x54, 0x54) },
- { "Gray34", RGB(0x57, 0x57, 0x57) },
- { "Gray35", RGB(0x59, 0x59, 0x59) },
- { "Gray36", RGB(0x5c, 0x5c, 0x5c) },
- { "Gray37", RGB(0x5e, 0x5e, 0x5e) },
- { "Gray38", RGB(0x61, 0x61, 0x61) },
- { "Gray39", RGB(0x63, 0x63, 0x63) },
- { "Gray4", RGB(0xa, 0xa, 0xa) },
- { "Gray40", RGB(0x66, 0x66, 0x66) },
- { "Gray41", RGB(0x69, 0x69, 0x69) },
- { "Gray42", RGB(0x6b, 0x6b, 0x6b) },
- { "Gray43", RGB(0x6e, 0x6e, 0x6e) },
- { "Gray44", RGB(0x70, 0x70, 0x70) },
- { "Gray45", RGB(0x73, 0x73, 0x73) },
- { "Gray46", RGB(0x75, 0x75, 0x75) },
- { "Gray47", RGB(0x78, 0x78, 0x78) },
- { "Gray48", RGB(0x7a, 0x7a, 0x7a) },
- { "Gray49", RGB(0x7d, 0x7d, 0x7d) },
- { "Gray5", RGB(0xd, 0xd, 0xd) },
- { "Gray50", RGB(0x7f, 0x7f, 0x7f) },
- { "Gray51", RGB(0x82, 0x82, 0x82) },
- { "Gray52", RGB(0x85, 0x85, 0x85) },
- { "Gray53", RGB(0x87, 0x87, 0x87) },
- { "Gray54", RGB(0x8a, 0x8a, 0x8a) },
- { "Gray55", RGB(0x8c, 0x8c, 0x8c) },
- { "Gray56", RGB(0x8f, 0x8f, 0x8f) },
- { "Gray57", RGB(0x91, 0x91, 0x91) },
- { "Gray58", RGB(0x94, 0x94, 0x94) },
- { "Gray59", RGB(0x96, 0x96, 0x96) },
- { "Gray6", RGB(0xf, 0xf, 0xf) },
- { "Gray60", RGB(0x99, 0x99, 0x99) },
- { "Gray61", RGB(0x9c, 0x9c, 0x9c) },
- { "Gray62", RGB(0x9e, 0x9e, 0x9e) },
- { "Gray63", RGB(0xa1, 0xa1, 0xa1) },
- { "Gray64", RGB(0xa3, 0xa3, 0xa3) },
- { "Gray65", RGB(0xa6, 0xa6, 0xa6) },
- { "Gray66", RGB(0xa8, 0xa8, 0xa8) },
- { "Gray67", RGB(0xab, 0xab, 0xab) },
- { "Gray68", RGB(0xad, 0xad, 0xad) },
- { "Gray69", RGB(0xb0, 0xb0, 0xb0) },
- { "Gray7", RGB(0x12, 0x12, 0x12) },
- { "Gray70", RGB(0xb3, 0xb3, 0xb3) },
- { "Gray71", RGB(0xb5, 0xb5, 0xb5) },
- { "Gray72", RGB(0xb8, 0xb8, 0xb8) },
- { "Gray73", RGB(0xba, 0xba, 0xba) },
- { "Gray74", RGB(0xbd, 0xbd, 0xbd) },
- { "Gray75", RGB(0xbf, 0xbf, 0xbf) },
- { "Gray76", RGB(0xc2, 0xc2, 0xc2) },
- { "Gray77", RGB(0xc4, 0xc4, 0xc4) },
- { "Gray78", RGB(0xc7, 0xc7, 0xc7) },
- { "Gray79", RGB(0xc9, 0xc9, 0xc9) },
- { "Gray8", RGB(0x14, 0x14, 0x14) },
- { "Gray80", RGB(0xcc, 0xcc, 0xcc) },
- { "Gray81", RGB(0xcf, 0xcf, 0xcf) },
- { "Gray82", RGB(0xd1, 0xd1, 0xd1) },
- { "Gray83", RGB(0xd4, 0xd4, 0xd4) },
- { "Gray84", RGB(0xd6, 0xd6, 0xd6) },
- { "Gray85", RGB(0xd9, 0xd9, 0xd9) },
- { "Gray86", RGB(0xdb, 0xdb, 0xdb) },
- { "Gray87", RGB(0xde, 0xde, 0xde) },
- { "Gray88", RGB(0xe0, 0xe0, 0xe0) },
- { "Gray89", RGB(0xe3, 0xe3, 0xe3) },
- { "Gray9", RGB(0x17, 0x17, 0x17) },
- { "Gray90", RGB(0xe5, 0xe5, 0xe5) },
- { "Gray91", RGB(0xe8, 0xe8, 0xe8) },
- { "Gray92", RGB(0xeb, 0xeb, 0xeb) },
- { "Gray93", RGB(0xed, 0xed, 0xed) },
- { "Gray94", RGB(0xf0, 0xf0, 0xf0) },
- { "Gray95", RGB(0xf2, 0xf2, 0xf2) },
- { "Gray96", RGB(0xf5, 0xf5, 0xf5) },
- { "Gray97", RGB(0xf7, 0xf7, 0xf7) },
- { "Gray98", RGB(0xfa, 0xfa, 0xfa) },
- { "Gray99", RGB(0xfc, 0xfc, 0xfc) },
- { "Green", RGB(0x00, 0x80, 0x00) },
- { "Green1", RGB(0x0, 0xff, 0x0) },
- { "Green2", RGB(0x0, 0xee, 0x0) },
- { "Green3", RGB(0x0, 0xcd, 0x0) },
- { "Green4", RGB(0x0, 0x8b, 0x0) },
- { "GreenYellow", RGB(0xad, 0xff, 0x2f) },
- { "Grey", RGB(0x80, 0x80, 0x80) },
- { "Grey0", RGB(0x0, 0x0, 0x0) },
- { "Grey1", RGB(0x3, 0x3, 0x3) },
- { "Grey10", RGB(0x1a, 0x1a, 0x1a) },
- { "Grey100", RGB(0xff, 0xff, 0xff) },
- { "Grey11", RGB(0x1c, 0x1c, 0x1c) },
- { "Grey12", RGB(0x1f, 0x1f, 0x1f) },
- { "Grey13", RGB(0x21, 0x21, 0x21) },
- { "Grey14", RGB(0x24, 0x24, 0x24) },
- { "Grey15", RGB(0x26, 0x26, 0x26) },
- { "Grey16", RGB(0x29, 0x29, 0x29) },
- { "Grey17", RGB(0x2b, 0x2b, 0x2b) },
- { "Grey18", RGB(0x2e, 0x2e, 0x2e) },
- { "Grey19", RGB(0x30, 0x30, 0x30) },
- { "Grey2", RGB(0x5, 0x5, 0x5) },
- { "Grey20", RGB(0x33, 0x33, 0x33) },
- { "Grey21", RGB(0x36, 0x36, 0x36) },
- { "Grey22", RGB(0x38, 0x38, 0x38) },
- { "Grey23", RGB(0x3b, 0x3b, 0x3b) },
- { "Grey24", RGB(0x3d, 0x3d, 0x3d) },
- { "Grey25", RGB(0x40, 0x40, 0x40) },
- { "Grey26", RGB(0x42, 0x42, 0x42) },
- { "Grey27", RGB(0x45, 0x45, 0x45) },
- { "Grey28", RGB(0x47, 0x47, 0x47) },
- { "Grey29", RGB(0x4a, 0x4a, 0x4a) },
- { "Grey3", RGB(0x8, 0x8, 0x8) },
- { "Grey30", RGB(0x4d, 0x4d, 0x4d) },
- { "Grey31", RGB(0x4f, 0x4f, 0x4f) },
- { "Grey32", RGB(0x52, 0x52, 0x52) },
- { "Grey33", RGB(0x54, 0x54, 0x54) },
- { "Grey34", RGB(0x57, 0x57, 0x57) },
- { "Grey35", RGB(0x59, 0x59, 0x59) },
- { "Grey36", RGB(0x5c, 0x5c, 0x5c) },
- { "Grey37", RGB(0x5e, 0x5e, 0x5e) },
- { "Grey38", RGB(0x61, 0x61, 0x61) },
- { "Grey39", RGB(0x63, 0x63, 0x63) },
- { "Grey4", RGB(0xa, 0xa, 0xa) },
- { "Grey40", RGB(0x66, 0x66, 0x66) },
- { "Grey41", RGB(0x69, 0x69, 0x69) },
- { "Grey42", RGB(0x6b, 0x6b, 0x6b) },
- { "Grey43", RGB(0x6e, 0x6e, 0x6e) },
- { "Grey44", RGB(0x70, 0x70, 0x70) },
- { "Grey45", RGB(0x73, 0x73, 0x73) },
- { "Grey46", RGB(0x75, 0x75, 0x75) },
- { "Grey47", RGB(0x78, 0x78, 0x78) },
- { "Grey48", RGB(0x7a, 0x7a, 0x7a) },
- { "Grey49", RGB(0x7d, 0x7d, 0x7d) },
- { "Grey5", RGB(0xd, 0xd, 0xd) },
- { "Grey50", RGB(0x7f, 0x7f, 0x7f) },
- { "Grey51", RGB(0x82, 0x82, 0x82) },
- { "Grey52", RGB(0x85, 0x85, 0x85) },
- { "Grey53", RGB(0x87, 0x87, 0x87) },
- { "Grey54", RGB(0x8a, 0x8a, 0x8a) },
- { "Grey55", RGB(0x8c, 0x8c, 0x8c) },
- { "Grey56", RGB(0x8f, 0x8f, 0x8f) },
- { "Grey57", RGB(0x91, 0x91, 0x91) },
- { "Grey58", RGB(0x94, 0x94, 0x94) },
- { "Grey59", RGB(0x96, 0x96, 0x96) },
- { "Grey6", RGB(0xf, 0xf, 0xf) },
- { "Grey60", RGB(0x99, 0x99, 0x99) },
- { "Grey61", RGB(0x9c, 0x9c, 0x9c) },
- { "Grey62", RGB(0x9e, 0x9e, 0x9e) },
- { "Grey63", RGB(0xa1, 0xa1, 0xa1) },
- { "Grey64", RGB(0xa3, 0xa3, 0xa3) },
- { "Grey65", RGB(0xa6, 0xa6, 0xa6) },
- { "Grey66", RGB(0xa8, 0xa8, 0xa8) },
- { "Grey67", RGB(0xab, 0xab, 0xab) },
- { "Grey68", RGB(0xad, 0xad, 0xad) },
- { "Grey69", RGB(0xb0, 0xb0, 0xb0) },
- { "Grey7", RGB(0x12, 0x12, 0x12) },
- { "Grey70", RGB(0xb3, 0xb3, 0xb3) },
- { "Grey71", RGB(0xb5, 0xb5, 0xb5) },
- { "Grey72", RGB(0xb8, 0xb8, 0xb8) },
- { "Grey73", RGB(0xba, 0xba, 0xba) },
- { "Grey74", RGB(0xbd, 0xbd, 0xbd) },
- { "Grey75", RGB(0xbf, 0xbf, 0xbf) },
- { "Grey76", RGB(0xc2, 0xc2, 0xc2) },
- { "Grey77", RGB(0xc4, 0xc4, 0xc4) },
- { "Grey78", RGB(0xc7, 0xc7, 0xc7) },
- { "Grey79", RGB(0xc9, 0xc9, 0xc9) },
- { "Grey8", RGB(0x14, 0x14, 0x14) },
- { "Grey80", RGB(0xcc, 0xcc, 0xcc) },
- { "Grey81", RGB(0xcf, 0xcf, 0xcf) },
- { "Grey82", RGB(0xd1, 0xd1, 0xd1) },
- { "Grey83", RGB(0xd4, 0xd4, 0xd4) },
- { "Grey84", RGB(0xd6, 0xd6, 0xd6) },
- { "Grey85", RGB(0xd9, 0xd9, 0xd9) },
- { "Grey86", RGB(0xdb, 0xdb, 0xdb) },
- { "Grey87", RGB(0xde, 0xde, 0xde) },
- { "Grey88", RGB(0xe0, 0xe0, 0xe0) },
- { "Grey89", RGB(0xe3, 0xe3, 0xe3) },
- { "Grey9", RGB(0x17, 0x17, 0x17) },
- { "Grey90", RGB(0xe5, 0xe5, 0xe5) },
- { "Grey91", RGB(0xe8, 0xe8, 0xe8) },
- { "Grey92", RGB(0xeb, 0xeb, 0xeb) },
- { "Grey93", RGB(0xed, 0xed, 0xed) },
- { "Grey94", RGB(0xf0, 0xf0, 0xf0) },
- { "Grey95", RGB(0xf2, 0xf2, 0xf2) },
- { "Grey96", RGB(0xf5, 0xf5, 0xf5) },
- { "Grey97", RGB(0xf7, 0xf7, 0xf7) },
- { "Grey98", RGB(0xfa, 0xfa, 0xfa) },
- { "Grey99", RGB(0xfc, 0xfc, 0xfc) },
- { "Honeydew", RGB(0xf0, 0xff, 0xf0) },
- { "Honeydew1", RGB(0xf0, 0xff, 0xf0) },
- { "Honeydew2", RGB(0xe0, 0xee, 0xe0) },
- { "Honeydew3", RGB(0xc1, 0xcd, 0xc1) },
- { "Honeydew4", RGB(0x83, 0x8b, 0x83) },
- { "HotPink", RGB(0xff, 0x69, 0xb4) },
- { "HotPink1", RGB(0xff, 0x6e, 0xb4) },
- { "HotPink2", RGB(0xee, 0x6a, 0xa7) },
- { "HotPink3", RGB(0xcd, 0x60, 0x90) },
- { "HotPink4", RGB(0x8b, 0x3a, 0x62) },
- { "IndianRed", RGB(0xcd, 0x5c, 0x5c) },
- { "IndianRed1", RGB(0xff, 0x6a, 0x6a) },
- { "IndianRed2", RGB(0xee, 0x63, 0x63) },
- { "IndianRed3", RGB(0xcd, 0x55, 0x55) },
- { "IndianRed4", RGB(0x8b, 0x3a, 0x3a) },
- { "Indigo", RGB(0x4b, 0x00, 0x82) },
- { "Ivory", RGB(0xff, 0xff, 0xf0) },
- { "Ivory1", RGB(0xff, 0xff, 0xf0) },
- { "Ivory2", RGB(0xee, 0xee, 0xe0) },
- { "Ivory3", RGB(0xcd, 0xcd, 0xc1) },
- { "Ivory4", RGB(0x8b, 0x8b, 0x83) },
- { "Khaki", RGB(0xf0, 0xe6, 0x8c) },
- { "Khaki1", RGB(0xff, 0xf6, 0x8f) },
- { "Khaki2", RGB(0xee, 0xe6, 0x85) },
- { "Khaki3", RGB(0xcd, 0xc6, 0x73) },
- { "Khaki4", RGB(0x8b, 0x86, 0x4e) },
- { "Lavender", RGB(0xe6, 0xe6, 0xfa) },
- { "LavenderBlush", RGB(0xff, 0xf0, 0xf5) },
- { "LavenderBlush1", RGB(0xff, 0xf0, 0xf5) },
- { "LavenderBlush2", RGB(0xee, 0xe0, 0xe5) },
- { "LavenderBlush3", RGB(0xcd, 0xc1, 0xc5) },
- { "LavenderBlush4", RGB(0x8b, 0x83, 0x86) },
- { "LawnGreen", RGB(0x7c, 0xfc, 0x00) },
- { "LemonChiffon", RGB(0xff, 0xfa, 0xcd) },
- { "LemonChiffon1", RGB(0xff, 0xfa, 0xcd) },
- { "LemonChiffon2", RGB(0xee, 0xe9, 0xbf) },
- { "LemonChiffon3", RGB(0xcd, 0xc9, 0xa5) },
- { "LemonChiffon4", RGB(0x8b, 0x89, 0x70) },
- { "LightBlue", RGB(0xad, 0xd8, 0xe6) },
- { "LightBlue1", RGB(0xbf, 0xef, 0xff) },
- { "LightBlue2", RGB(0xb2, 0xdf, 0xee) },
- { "LightBlue3", RGB(0x9a, 0xc0, 0xcd) },
- { "LightBlue4", RGB(0x68, 0x83, 0x8b) },
- { "LightCoral", RGB(0xf0, 0x80, 0x80) },
- { "LightCyan", RGB(0xe0, 0xff, 0xff) },
- { "LightCyan1", RGB(0xe0, 0xff, 0xff) },
- { "LightCyan2", RGB(0xd1, 0xee, 0xee) },
- { "LightCyan3", RGB(0xb4, 0xcd, 0xcd) },
- { "LightCyan4", RGB(0x7a, 0x8b, 0x8b) },
- { "LightGoldenrod", RGB(0xee, 0xdd, 0x82) },
- { "LightGoldenrod1", RGB(0xff, 0xec, 0x8b) },
- { "LightGoldenrod2", RGB(0xee, 0xdc, 0x82) },
- { "LightGoldenrod3", RGB(0xcd, 0xbe, 0x70) },
- { "LightGoldenrod4", RGB(0x8b, 0x81, 0x4c) },
- { "LightGoldenRodYellow", RGB(0xfa, 0xfa, 0xd2) },
- { "LightGray", RGB(0xd3, 0xd3, 0xd3) },
- { "LightGreen", RGB(0x90, 0xee, 0x90) },
- { "LightGrey", RGB(0xd3, 0xd3, 0xd3) },
- { "LightMagenta", RGB(0xff, 0xbb, 0xff) },
- { "LightPink", RGB(0xff, 0xb6, 0xc1) },
- { "LightPink1", RGB(0xff, 0xae, 0xb9) },
- { "LightPink2", RGB(0xee, 0xa2, 0xad) },
- { "LightPink3", RGB(0xcd, 0x8c, 0x95) },
- { "LightPink4", RGB(0x8b, 0x5f, 0x65) },
- { "LightRed", RGB(0xff, 0xbb, 0xbb) },
- { "LightSalmon", RGB(0xff, 0xa0, 0x7a) },
- { "LightSalmon1", RGB(0xff, 0xa0, 0x7a) },
- { "LightSalmon2", RGB(0xee, 0x95, 0x72) },
- { "LightSalmon3", RGB(0xcd, 0x81, 0x62) },
- { "LightSalmon4", RGB(0x8b, 0x57, 0x42) },
- { "LightSeaGreen", RGB(0x20, 0xb2, 0xaa) },
- { "LightSkyBlue", RGB(0x87, 0xce, 0xfa) },
- { "LightSkyBlue1", RGB(0xb0, 0xe2, 0xff) },
- { "LightSkyBlue2", RGB(0xa4, 0xd3, 0xee) },
- { "LightSkyBlue3", RGB(0x8d, 0xb6, 0xcd) },
- { "LightSkyBlue4", RGB(0x60, 0x7b, 0x8b) },
- { "LightSlateBlue", RGB(0x84, 0x70, 0xff) },
- { "LightSlateGray", RGB(0x77, 0x88, 0x99) },
- { "LightSlateGrey", RGB(0x77, 0x88, 0x99) },
- { "LightSteelBlue", RGB(0xb0, 0xc4, 0xde) },
- { "LightSteelBlue1", RGB(0xca, 0xe1, 0xff) },
- { "LightSteelBlue2", RGB(0xbc, 0xd2, 0xee) },
- { "LightSteelBlue3", RGB(0xa2, 0xb5, 0xcd) },
- { "LightSteelBlue4", RGB(0x6e, 0x7b, 0x8b) },
- { "LightYellow", RGB(0xff, 0xff, 0xe0) },
- { "LightYellow1", RGB(0xff, 0xff, 0xe0) },
- { "LightYellow2", RGB(0xee, 0xee, 0xd1) },
- { "LightYellow3", RGB(0xcd, 0xcd, 0xb4) },
- { "LightYellow4", RGB(0x8b, 0x8b, 0x7a) },
- { "Lime", RGB(0x00, 0xff, 0x00) },
- { "LimeGreen", RGB(0x32, 0xcd, 0x32) },
- { "Linen", RGB(0xfa, 0xf0, 0xe6) },
- { "Magenta", RGB(0xff, 0x00, 0xff) },
- { "Magenta1", RGB(0xff, 0x0, 0xff) },
- { "Magenta2", RGB(0xee, 0x0, 0xee) },
- { "Magenta3", RGB(0xcd, 0x0, 0xcd) },
- { "Magenta4", RGB(0x8b, 0x0, 0x8b) },
- { "Maroon", RGB(0x80, 0x00, 0x00) },
- { "Maroon1", RGB(0xff, 0x34, 0xb3) },
- { "Maroon2", RGB(0xee, 0x30, 0xa7) },
- { "Maroon3", RGB(0xcd, 0x29, 0x90) },
- { "Maroon4", RGB(0x8b, 0x1c, 0x62) },
- { "MediumAquamarine", RGB(0x66, 0xcd, 0xaa) },
- { "MediumBlue", RGB(0x00, 0x00, 0xcd) },
- { "MediumOrchid", RGB(0xba, 0x55, 0xd3) },
- { "MediumOrchid1", RGB(0xe0, 0x66, 0xff) },
- { "MediumOrchid2", RGB(0xd1, 0x5f, 0xee) },
- { "MediumOrchid3", RGB(0xb4, 0x52, 0xcd) },
- { "MediumOrchid4", RGB(0x7a, 0x37, 0x8b) },
- { "MediumPurple", RGB(0x93, 0x70, 0xdb) },
- { "MediumPurple1", RGB(0xab, 0x82, 0xff) },
- { "MediumPurple2", RGB(0x9f, 0x79, 0xee) },
- { "MediumPurple3", RGB(0x89, 0x68, 0xcd) },
- { "MediumPurple4", RGB(0x5d, 0x47, 0x8b) },
- { "MediumSeaGreen", RGB(0x3c, 0xb3, 0x71) },
- { "MediumSlateBlue", RGB(0x7b, 0x68, 0xee) },
- { "MediumSpringGreen", RGB(0x00, 0xfa, 0x9a) },
- { "MediumTurquoise", RGB(0x48, 0xd1, 0xcc) },
- { "MediumVioletRed", RGB(0xc7, 0x15, 0x85) },
- { "MidnightBlue", RGB(0x19, 0x19, 0x70) },
- { "MintCream", RGB(0xf5, 0xff, 0xfa) },
- { "MistyRose", RGB(0xff, 0xe4, 0xe1) },
- { "MistyRose1", RGB(0xff, 0xe4, 0xe1) },
- { "MistyRose2", RGB(0xee, 0xd5, 0xd2) },
- { "MistyRose3", RGB(0xcd, 0xb7, 0xb5) },
- { "MistyRose4", RGB(0x8b, 0x7d, 0x7b) },
- { "Moccasin", RGB(0xff, 0xe4, 0xb5) },
- { "NavajoWhite", RGB(0xff, 0xde, 0xad) },
- { "NavajoWhite1", RGB(0xff, 0xde, 0xad) },
- { "NavajoWhite2", RGB(0xee, 0xcf, 0xa1) },
- { "NavajoWhite3", RGB(0xcd, 0xb3, 0x8b) },
- { "NavajoWhite4", RGB(0x8b, 0x79, 0x5e) },
- { "Navy", RGB(0x00, 0x00, 0x80) },
- { "NavyBlue", RGB(0x0, 0x0, 0x80) },
- { "OldLace", RGB(0xfd, 0xf5, 0xe6) },
- { "Olive", RGB(0x80, 0x80, 0x00) },
- { "OliveDrab", RGB(0x6b, 0x8e, 0x23) },
- { "OliveDrab1", RGB(0xc0, 0xff, 0x3e) },
- { "OliveDrab2", RGB(0xb3, 0xee, 0x3a) },
- { "OliveDrab3", RGB(0x9a, 0xcd, 0x32) },
- { "OliveDrab4", RGB(0x69, 0x8b, 0x22) },
- { "Orange", RGB(0xff, 0xa5, 0x00) },
- { "Orange1", RGB(0xff, 0xa5, 0x0) },
- { "Orange2", RGB(0xee, 0x9a, 0x0) },
- { "Orange3", RGB(0xcd, 0x85, 0x0) },
- { "Orange4", RGB(0x8b, 0x5a, 0x0) },
- { "OrangeRed", RGB(0xff, 0x45, 0x00) },
- { "OrangeRed1", RGB(0xff, 0x45, 0x0) },
- { "OrangeRed2", RGB(0xee, 0x40, 0x0) },
- { "OrangeRed3", RGB(0xcd, 0x37, 0x0) },
- { "OrangeRed4", RGB(0x8b, 0x25, 0x0) },
- { "Orchid", RGB(0xda, 0x70, 0xd6) },
- { "Orchid1", RGB(0xff, 0x83, 0xfa) },
- { "Orchid2", RGB(0xee, 0x7a, 0xe9) },
- { "Orchid3", RGB(0xcd, 0x69, 0xc9) },
- { "Orchid4", RGB(0x8b, 0x47, 0x89) },
- { "PaleGoldenRod", RGB(0xee, 0xe8, 0xaa) },
- { "PaleGreen", RGB(0x98, 0xfb, 0x98) },
- { "PaleGreen1", RGB(0x9a, 0xff, 0x9a) },
- { "PaleGreen2", RGB(0x90, 0xee, 0x90) },
- { "PaleGreen3", RGB(0x7c, 0xcd, 0x7c) },
- { "PaleGreen4", RGB(0x54, 0x8b, 0x54) },
- { "PaleTurquoise", RGB(0xaf, 0xee, 0xee) },
- { "PaleTurquoise1", RGB(0xbb, 0xff, 0xff) },
- { "PaleTurquoise2", RGB(0xae, 0xee, 0xee) },
- { "PaleTurquoise3", RGB(0x96, 0xcd, 0xcd) },
- { "PaleTurquoise4", RGB(0x66, 0x8b, 0x8b) },
- { "PaleVioletRed", RGB(0xdb, 0x70, 0x93) },
- { "PaleVioletRed1", RGB(0xff, 0x82, 0xab) },
- { "PaleVioletRed2", RGB(0xee, 0x79, 0x9f) },
- { "PaleVioletRed3", RGB(0xcd, 0x68, 0x89) },
- { "PaleVioletRed4", RGB(0x8b, 0x47, 0x5d) },
- { "PapayaWhip", RGB(0xff, 0xef, 0xd5) },
- { "PeachPuff", RGB(0xff, 0xda, 0xb9) },
- { "PeachPuff1", RGB(0xff, 0xda, 0xb9) },
- { "PeachPuff2", RGB(0xee, 0xcb, 0xad) },
- { "PeachPuff3", RGB(0xcd, 0xaf, 0x95) },
- { "PeachPuff4", RGB(0x8b, 0x77, 0x65) },
- { "Peru", RGB(0xcd, 0x85, 0x3f) },
- { "Pink", RGB(0xff, 0xc0, 0xcb) },
- { "Pink1", RGB(0xff, 0xb5, 0xc5) },
- { "Pink2", RGB(0xee, 0xa9, 0xb8) },
- { "Pink3", RGB(0xcd, 0x91, 0x9e) },
- { "Pink4", RGB(0x8b, 0x63, 0x6c) },
- { "Plum", RGB(0xdd, 0xa0, 0xdd) },
- { "Plum1", RGB(0xff, 0xbb, 0xff) },
- { "Plum2", RGB(0xee, 0xae, 0xee) },
- { "Plum3", RGB(0xcd, 0x96, 0xcd) },
- { "Plum4", RGB(0x8b, 0x66, 0x8b) },
- { "PowderBlue", RGB(0xb0, 0xe0, 0xe6) },
- { "Purple", RGB(0x80, 0x00, 0x80) },
- { "Purple1", RGB(0x9b, 0x30, 0xff) },
- { "Purple2", RGB(0x91, 0x2c, 0xee) },
- { "Purple3", RGB(0x7d, 0x26, 0xcd) },
- { "Purple4", RGB(0x55, 0x1a, 0x8b) },
- { "RebeccaPurple", RGB(0x66, 0x33, 0x99) },
- { "Red", RGB(0xff, 0x00, 0x00) },
- { "Red1", RGB(0xff, 0x0, 0x0) },
- { "Red2", RGB(0xee, 0x0, 0x0) },
- { "Red3", RGB(0xcd, 0x0, 0x0) },
- { "Red4", RGB(0x8b, 0x0, 0x0) },
- { "RosyBrown", RGB(0xbc, 0x8f, 0x8f) },
- { "RosyBrown1", RGB(0xff, 0xc1, 0xc1) },
- { "RosyBrown2", RGB(0xee, 0xb4, 0xb4) },
- { "RosyBrown3", RGB(0xcd, 0x9b, 0x9b) },
- { "RosyBrown4", RGB(0x8b, 0x69, 0x69) },
- { "RoyalBlue", RGB(0x41, 0x69, 0xe1) },
- { "RoyalBlue1", RGB(0x48, 0x76, 0xff) },
- { "RoyalBlue2", RGB(0x43, 0x6e, 0xee) },
- { "RoyalBlue3", RGB(0x3a, 0x5f, 0xcd) },
- { "RoyalBlue4", RGB(0x27, 0x40, 0x8b) },
- { "SaddleBrown", RGB(0x8b, 0x45, 0x13) },
- { "Salmon", RGB(0xfa, 0x80, 0x72) },
- { "Salmon1", RGB(0xff, 0x8c, 0x69) },
- { "Salmon2", RGB(0xee, 0x82, 0x62) },
- { "Salmon3", RGB(0xcd, 0x70, 0x54) },
- { "Salmon4", RGB(0x8b, 0x4c, 0x39) },
- { "SandyBrown", RGB(0xf4, 0xa4, 0x60) },
- { "SeaGreen", RGB(0x2e, 0x8b, 0x57) },
- { "SeaGreen1", RGB(0x54, 0xff, 0x9f) },
- { "SeaGreen2", RGB(0x4e, 0xee, 0x94) },
- { "SeaGreen3", RGB(0x43, 0xcd, 0x80) },
- { "SeaGreen4", RGB(0x2e, 0x8b, 0x57) },
- { "SeaShell", RGB(0xff, 0xf5, 0xee) },
- { "Seashell1", RGB(0xff, 0xf5, 0xee) },
- { "Seashell2", RGB(0xee, 0xe5, 0xde) },
- { "Seashell3", RGB(0xcd, 0xc5, 0xbf) },
- { "Seashell4", RGB(0x8b, 0x86, 0x82) },
- { "Sienna", RGB(0xa0, 0x52, 0x2d) },
- { "Sienna1", RGB(0xff, 0x82, 0x47) },
- { "Sienna2", RGB(0xee, 0x79, 0x42) },
- { "Sienna3", RGB(0xcd, 0x68, 0x39) },
- { "Sienna4", RGB(0x8b, 0x47, 0x26) },
- { "Silver", RGB(0xc0, 0xc0, 0xc0) },
- { "SkyBlue", RGB(0x87, 0xce, 0xeb) },
- { "SkyBlue1", RGB(0x87, 0xce, 0xff) },
- { "SkyBlue2", RGB(0x7e, 0xc0, 0xee) },
- { "SkyBlue3", RGB(0x6c, 0xa6, 0xcd) },
- { "SkyBlue4", RGB(0x4a, 0x70, 0x8b) },
- { "SlateBlue", RGB(0x6a, 0x5a, 0xcd) },
- { "SlateBlue1", RGB(0x83, 0x6f, 0xff) },
- { "SlateBlue2", RGB(0x7a, 0x67, 0xee) },
- { "SlateBlue3", RGB(0x69, 0x59, 0xcd) },
- { "SlateBlue4", RGB(0x47, 0x3c, 0x8b) },
- { "SlateGray", RGB(0x70, 0x80, 0x90) },
- { "SlateGray1", RGB(0xc6, 0xe2, 0xff) },
- { "SlateGray2", RGB(0xb9, 0xd3, 0xee) },
- { "SlateGray3", RGB(0x9f, 0xb6, 0xcd) },
- { "SlateGray4", RGB(0x6c, 0x7b, 0x8b) },
- { "SlateGrey", RGB(0x70, 0x80, 0x90) },
- { "Snow", RGB(0xff, 0xfa, 0xfa) },
- { "Snow1", RGB(0xff, 0xfa, 0xfa) },
- { "Snow2", RGB(0xee, 0xe9, 0xe9) },
- { "Snow3", RGB(0xcd, 0xc9, 0xc9) },
- { "Snow4", RGB(0x8b, 0x89, 0x89) },
- { "SpringGreen", RGB(0x00, 0xff, 0x7f) },
- { "SpringGreen1", RGB(0x0, 0xff, 0x7f) },
- { "SpringGreen2", RGB(0x0, 0xee, 0x76) },
- { "SpringGreen3", RGB(0x0, 0xcd, 0x66) },
- { "SpringGreen4", RGB(0x0, 0x8b, 0x45) },
- { "SteelBlue", RGB(0x46, 0x82, 0xb4) },
- { "SteelBlue1", RGB(0x63, 0xb8, 0xff) },
- { "SteelBlue2", RGB(0x5c, 0xac, 0xee) },
- { "SteelBlue3", RGB(0x4f, 0x94, 0xcd) },
- { "SteelBlue4", RGB(0x36, 0x64, 0x8b) },
- { "Tan", RGB(0xd2, 0xb4, 0x8c) },
- { "Tan1", RGB(0xff, 0xa5, 0x4f) },
- { "Tan2", RGB(0xee, 0x9a, 0x49) },
- { "Tan3", RGB(0xcd, 0x85, 0x3f) },
- { "Tan4", RGB(0x8b, 0x5a, 0x2b) },
- { "Teal", RGB(0x00, 0x80, 0x80) },
- { "Thistle", RGB(0xd8, 0xbf, 0xd8) },
- { "Thistle1", RGB(0xff, 0xe1, 0xff) },
- { "Thistle2", RGB(0xee, 0xd2, 0xee) },
- { "Thistle3", RGB(0xcd, 0xb5, 0xcd) },
- { "Thistle4", RGB(0x8b, 0x7b, 0x8b) },
- { "Tomato", RGB(0xff, 0x63, 0x47) },
- { "Tomato1", RGB(0xff, 0x63, 0x47) },
- { "Tomato2", RGB(0xee, 0x5c, 0x42) },
- { "Tomato3", RGB(0xcd, 0x4f, 0x39) },
- { "Tomato4", RGB(0x8b, 0x36, 0x26) },
- { "Turquoise", RGB(0x40, 0xe0, 0xd0) },
- { "Turquoise1", RGB(0x0, 0xf5, 0xff) },
- { "Turquoise2", RGB(0x0, 0xe5, 0xee) },
- { "Turquoise3", RGB(0x0, 0xc5, 0xcd) },
- { "Turquoise4", RGB(0x0, 0x86, 0x8b) },
- { "Violet", RGB(0xee, 0x82, 0xee) },
- { "VioletRed", RGB(0xd0, 0x20, 0x90) },
- { "VioletRed1", RGB(0xff, 0x3e, 0x96) },
- { "VioletRed2", RGB(0xee, 0x3a, 0x8c) },
- { "VioletRed3", RGB(0xcd, 0x32, 0x78) },
- { "VioletRed4", RGB(0x8b, 0x22, 0x52) },
- { "WebGray", RGB(0x80, 0x80, 0x80) },
- { "WebGreen", RGB(0x0, 0x80, 0x0) },
- { "WebGrey", RGB(0x80, 0x80, 0x80) },
- { "WebMaroon", RGB(0x80, 0x0, 0x0) },
- { "WebPurple", RGB(0x80, 0x0, 0x80) },
- { "Wheat", RGB(0xf5, 0xde, 0xb3) },
- { "Wheat1", RGB(0xff, 0xe7, 0xba) },
- { "Wheat2", RGB(0xee, 0xd8, 0xae) },
- { "Wheat3", RGB(0xcd, 0xba, 0x96) },
- { "Wheat4", RGB(0x8b, 0x7e, 0x66) },
- { "White", RGB(0xff, 0xff, 0xff) },
- { "WhiteSmoke", RGB(0xf5, 0xf5, 0xf5) },
- { "X11Gray", RGB(0xbe, 0xbe, 0xbe) },
- { "X11Green", RGB(0x0, 0xff, 0x0) },
- { "X11Grey", RGB(0xbe, 0xbe, 0xbe) },
- { "X11Maroon", RGB(0xb0, 0x30, 0x60) },
- { "X11Purple", RGB(0xa0, 0x20, 0xf0) },
- { "Yellow", RGB(0xff, 0xff, 0x00) },
- { "Yellow1", RGB(0xff, 0xff, 0x0) },
- { "Yellow2", RGB(0xee, 0xee, 0x0) },
- { "Yellow3", RGB(0xcd, 0xcd, 0x0) },
- { "Yellow4", RGB(0x8b, 0x8b, 0x0) },
- { "YellowGreen", RGB(0x9a, 0xcd, 0x32) },
+ { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) },
+ { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) },
+ { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) },
+ { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) },
+ { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) },
+ { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) },
+ { "Aqua", RGB_(0x00, 0xff, 0xff) },
+ { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) },
+ { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) },
+ { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) },
+ { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) },
+ { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) },
+ { "Azure", RGB_(0xf0, 0xff, 0xff) },
+ { "Azure1", RGB_(0xf0, 0xff, 0xff) },
+ { "Azure2", RGB_(0xe0, 0xee, 0xee) },
+ { "Azure3", RGB_(0xc1, 0xcd, 0xcd) },
+ { "Azure4", RGB_(0x83, 0x8b, 0x8b) },
+ { "Beige", RGB_(0xf5, 0xf5, 0xdc) },
+ { "Bisque", RGB_(0xff, 0xe4, 0xc4) },
+ { "Bisque1", RGB_(0xff, 0xe4, 0xc4) },
+ { "Bisque2", RGB_(0xee, 0xd5, 0xb7) },
+ { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) },
+ { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) },
+ { "Black", RGB_(0x00, 0x00, 0x00) },
+ { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) },
+ { "Blue", RGB_(0x00, 0x00, 0xff) },
+ { "Blue1", RGB_(0x0, 0x0, 0xff) },
+ { "Blue2", RGB_(0x0, 0x0, 0xee) },
+ { "Blue3", RGB_(0x0, 0x0, 0xcd) },
+ { "Blue4", RGB_(0x0, 0x0, 0x8b) },
+ { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) },
+ { "Brown", RGB_(0xa5, 0x2a, 0x2a) },
+ { "Brown1", RGB_(0xff, 0x40, 0x40) },
+ { "Brown2", RGB_(0xee, 0x3b, 0x3b) },
+ { "Brown3", RGB_(0xcd, 0x33, 0x33) },
+ { "Brown4", RGB_(0x8b, 0x23, 0x23) },
+ { "BurlyWood", RGB_(0xde, 0xb8, 0x87) },
+ { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) },
+ { "Burlywood2", RGB_(0xee, 0xc5, 0x91) },
+ { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) },
+ { "Burlywood4", RGB_(0x8b, 0x73, 0x55) },
+ { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) },
+ { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) },
+ { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) },
+ { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) },
+ { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) },
+ { "ChartReuse", RGB_(0x7f, 0xff, 0x00) },
+ { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) },
+ { "Chartreuse2", RGB_(0x76, 0xee, 0x0) },
+ { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) },
+ { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) },
+ { "Chocolate", RGB_(0xd2, 0x69, 0x1e) },
+ { "Chocolate1", RGB_(0xff, 0x7f, 0x24) },
+ { "Chocolate2", RGB_(0xee, 0x76, 0x21) },
+ { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) },
+ { "Chocolate4", RGB_(0x8b, 0x45, 0x13) },
+ { "Coral", RGB_(0xff, 0x7f, 0x50) },
+ { "Coral1", RGB_(0xff, 0x72, 0x56) },
+ { "Coral2", RGB_(0xee, 0x6a, 0x50) },
+ { "Coral3", RGB_(0xcd, 0x5b, 0x45) },
+ { "Coral4", RGB_(0x8b, 0x3e, 0x2f) },
+ { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) },
+ { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) },
+ { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) },
+ { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) },
+ { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) },
+ { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) },
+ { "Crimson", RGB_(0xdc, 0x14, 0x3c) },
+ { "Cyan", RGB_(0x00, 0xff, 0xff) },
+ { "Cyan1", RGB_(0x0, 0xff, 0xff) },
+ { "Cyan2", RGB_(0x0, 0xee, 0xee) },
+ { "Cyan3", RGB_(0x0, 0xcd, 0xcd) },
+ { "Cyan4", RGB_(0x0, 0x8b, 0x8b) },
+ { "DarkBlue", RGB_(0x00, 0x00, 0x8b) },
+ { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) },
+ { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) },
+ { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) },
+ { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) },
+ { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) },
+ { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) },
+ { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) },
+ { "DarkGreen", RGB_(0x00, 0x64, 0x00) },
+ { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) },
+ { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) },
+ { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) },
+ { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) },
+ { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) },
+ { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) },
+ { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) },
+ { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) },
+ { "DarkOrange", RGB_(0xff, 0x8c, 0x00) },
+ { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) },
+ { "DarkOrange2", RGB_(0xee, 0x76, 0x0) },
+ { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) },
+ { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) },
+ { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) },
+ { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) },
+ { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) },
+ { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) },
+ { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) },
+ { "DarkRed", RGB_(0x8b, 0x00, 0x00) },
+ { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) },
+ { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) },
+ { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) },
+ { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) },
+ { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) },
+ { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) },
+ { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) },
+ { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) },
+ { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) },
+ { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) },
+ { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) },
+ { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) },
+ { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) },
+ { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) },
+ { "DarkViolet", RGB_(0x94, 0x00, 0xd3) },
+ { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) },
+ { "DeepPink", RGB_(0xff, 0x14, 0x93) },
+ { "DeepPink1", RGB_(0xff, 0x14, 0x93) },
+ { "DeepPink2", RGB_(0xee, 0x12, 0x89) },
+ { "DeepPink3", RGB_(0xcd, 0x10, 0x76) },
+ { "DeepPink4", RGB_(0x8b, 0xa, 0x50) },
+ { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) },
+ { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) },
+ { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) },
+ { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) },
+ { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) },
+ { "DimGray", RGB_(0x69, 0x69, 0x69) },
+ { "DimGrey", RGB_(0x69, 0x69, 0x69) },
+ { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) },
+ { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) },
+ { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) },
+ { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) },
+ { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) },
+ { "Firebrick", RGB_(0xb2, 0x22, 0x22) },
+ { "Firebrick1", RGB_(0xff, 0x30, 0x30) },
+ { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) },
+ { "Firebrick3", RGB_(0xcd, 0x26, 0x26) },
+ { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) },
+ { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) },
+ { "ForestGreen", RGB_(0x22, 0x8b, 0x22) },
+ { "Fuchsia", RGB_(0xff, 0x00, 0xff) },
+ { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) },
+ { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) },
+ { "Gold", RGB_(0xff, 0xd7, 0x00) },
+ { "Gold1", RGB_(0xff, 0xd7, 0x0) },
+ { "Gold2", RGB_(0xee, 0xc9, 0x0) },
+ { "Gold3", RGB_(0xcd, 0xad, 0x0) },
+ { "Gold4", RGB_(0x8b, 0x75, 0x0) },
+ { "GoldenRod", RGB_(0xda, 0xa5, 0x20) },
+ { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) },
+ { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) },
+ { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) },
+ { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) },
+ { "Gray", RGB_(0x80, 0x80, 0x80) },
+ { "Gray0", RGB_(0x0, 0x0, 0x0) },
+ { "Gray1", RGB_(0x3, 0x3, 0x3) },
+ { "Gray10", RGB_(0x1a, 0x1a, 0x1a) },
+ { "Gray100", RGB_(0xff, 0xff, 0xff) },
+ { "Gray11", RGB_(0x1c, 0x1c, 0x1c) },
+ { "Gray12", RGB_(0x1f, 0x1f, 0x1f) },
+ { "Gray13", RGB_(0x21, 0x21, 0x21) },
+ { "Gray14", RGB_(0x24, 0x24, 0x24) },
+ { "Gray15", RGB_(0x26, 0x26, 0x26) },
+ { "Gray16", RGB_(0x29, 0x29, 0x29) },
+ { "Gray17", RGB_(0x2b, 0x2b, 0x2b) },
+ { "Gray18", RGB_(0x2e, 0x2e, 0x2e) },
+ { "Gray19", RGB_(0x30, 0x30, 0x30) },
+ { "Gray2", RGB_(0x5, 0x5, 0x5) },
+ { "Gray20", RGB_(0x33, 0x33, 0x33) },
+ { "Gray21", RGB_(0x36, 0x36, 0x36) },
+ { "Gray22", RGB_(0x38, 0x38, 0x38) },
+ { "Gray23", RGB_(0x3b, 0x3b, 0x3b) },
+ { "Gray24", RGB_(0x3d, 0x3d, 0x3d) },
+ { "Gray25", RGB_(0x40, 0x40, 0x40) },
+ { "Gray26", RGB_(0x42, 0x42, 0x42) },
+ { "Gray27", RGB_(0x45, 0x45, 0x45) },
+ { "Gray28", RGB_(0x47, 0x47, 0x47) },
+ { "Gray29", RGB_(0x4a, 0x4a, 0x4a) },
+ { "Gray3", RGB_(0x8, 0x8, 0x8) },
+ { "Gray30", RGB_(0x4d, 0x4d, 0x4d) },
+ { "Gray31", RGB_(0x4f, 0x4f, 0x4f) },
+ { "Gray32", RGB_(0x52, 0x52, 0x52) },
+ { "Gray33", RGB_(0x54, 0x54, 0x54) },
+ { "Gray34", RGB_(0x57, 0x57, 0x57) },
+ { "Gray35", RGB_(0x59, 0x59, 0x59) },
+ { "Gray36", RGB_(0x5c, 0x5c, 0x5c) },
+ { "Gray37", RGB_(0x5e, 0x5e, 0x5e) },
+ { "Gray38", RGB_(0x61, 0x61, 0x61) },
+ { "Gray39", RGB_(0x63, 0x63, 0x63) },
+ { "Gray4", RGB_(0xa, 0xa, 0xa) },
+ { "Gray40", RGB_(0x66, 0x66, 0x66) },
+ { "Gray41", RGB_(0x69, 0x69, 0x69) },
+ { "Gray42", RGB_(0x6b, 0x6b, 0x6b) },
+ { "Gray43", RGB_(0x6e, 0x6e, 0x6e) },
+ { "Gray44", RGB_(0x70, 0x70, 0x70) },
+ { "Gray45", RGB_(0x73, 0x73, 0x73) },
+ { "Gray46", RGB_(0x75, 0x75, 0x75) },
+ { "Gray47", RGB_(0x78, 0x78, 0x78) },
+ { "Gray48", RGB_(0x7a, 0x7a, 0x7a) },
+ { "Gray49", RGB_(0x7d, 0x7d, 0x7d) },
+ { "Gray5", RGB_(0xd, 0xd, 0xd) },
+ { "Gray50", RGB_(0x7f, 0x7f, 0x7f) },
+ { "Gray51", RGB_(0x82, 0x82, 0x82) },
+ { "Gray52", RGB_(0x85, 0x85, 0x85) },
+ { "Gray53", RGB_(0x87, 0x87, 0x87) },
+ { "Gray54", RGB_(0x8a, 0x8a, 0x8a) },
+ { "Gray55", RGB_(0x8c, 0x8c, 0x8c) },
+ { "Gray56", RGB_(0x8f, 0x8f, 0x8f) },
+ { "Gray57", RGB_(0x91, 0x91, 0x91) },
+ { "Gray58", RGB_(0x94, 0x94, 0x94) },
+ { "Gray59", RGB_(0x96, 0x96, 0x96) },
+ { "Gray6", RGB_(0xf, 0xf, 0xf) },
+ { "Gray60", RGB_(0x99, 0x99, 0x99) },
+ { "Gray61", RGB_(0x9c, 0x9c, 0x9c) },
+ { "Gray62", RGB_(0x9e, 0x9e, 0x9e) },
+ { "Gray63", RGB_(0xa1, 0xa1, 0xa1) },
+ { "Gray64", RGB_(0xa3, 0xa3, 0xa3) },
+ { "Gray65", RGB_(0xa6, 0xa6, 0xa6) },
+ { "Gray66", RGB_(0xa8, 0xa8, 0xa8) },
+ { "Gray67", RGB_(0xab, 0xab, 0xab) },
+ { "Gray68", RGB_(0xad, 0xad, 0xad) },
+ { "Gray69", RGB_(0xb0, 0xb0, 0xb0) },
+ { "Gray7", RGB_(0x12, 0x12, 0x12) },
+ { "Gray70", RGB_(0xb3, 0xb3, 0xb3) },
+ { "Gray71", RGB_(0xb5, 0xb5, 0xb5) },
+ { "Gray72", RGB_(0xb8, 0xb8, 0xb8) },
+ { "Gray73", RGB_(0xba, 0xba, 0xba) },
+ { "Gray74", RGB_(0xbd, 0xbd, 0xbd) },
+ { "Gray75", RGB_(0xbf, 0xbf, 0xbf) },
+ { "Gray76", RGB_(0xc2, 0xc2, 0xc2) },
+ { "Gray77", RGB_(0xc4, 0xc4, 0xc4) },
+ { "Gray78", RGB_(0xc7, 0xc7, 0xc7) },
+ { "Gray79", RGB_(0xc9, 0xc9, 0xc9) },
+ { "Gray8", RGB_(0x14, 0x14, 0x14) },
+ { "Gray80", RGB_(0xcc, 0xcc, 0xcc) },
+ { "Gray81", RGB_(0xcf, 0xcf, 0xcf) },
+ { "Gray82", RGB_(0xd1, 0xd1, 0xd1) },
+ { "Gray83", RGB_(0xd4, 0xd4, 0xd4) },
+ { "Gray84", RGB_(0xd6, 0xd6, 0xd6) },
+ { "Gray85", RGB_(0xd9, 0xd9, 0xd9) },
+ { "Gray86", RGB_(0xdb, 0xdb, 0xdb) },
+ { "Gray87", RGB_(0xde, 0xde, 0xde) },
+ { "Gray88", RGB_(0xe0, 0xe0, 0xe0) },
+ { "Gray89", RGB_(0xe3, 0xe3, 0xe3) },
+ { "Gray9", RGB_(0x17, 0x17, 0x17) },
+ { "Gray90", RGB_(0xe5, 0xe5, 0xe5) },
+ { "Gray91", RGB_(0xe8, 0xe8, 0xe8) },
+ { "Gray92", RGB_(0xeb, 0xeb, 0xeb) },
+ { "Gray93", RGB_(0xed, 0xed, 0xed) },
+ { "Gray94", RGB_(0xf0, 0xf0, 0xf0) },
+ { "Gray95", RGB_(0xf2, 0xf2, 0xf2) },
+ { "Gray96", RGB_(0xf5, 0xf5, 0xf5) },
+ { "Gray97", RGB_(0xf7, 0xf7, 0xf7) },
+ { "Gray98", RGB_(0xfa, 0xfa, 0xfa) },
+ { "Gray99", RGB_(0xfc, 0xfc, 0xfc) },
+ { "Green", RGB_(0x00, 0x80, 0x00) },
+ { "Green1", RGB_(0x0, 0xff, 0x0) },
+ { "Green2", RGB_(0x0, 0xee, 0x0) },
+ { "Green3", RGB_(0x0, 0xcd, 0x0) },
+ { "Green4", RGB_(0x0, 0x8b, 0x0) },
+ { "GreenYellow", RGB_(0xad, 0xff, 0x2f) },
+ { "Grey", RGB_(0x80, 0x80, 0x80) },
+ { "Grey0", RGB_(0x0, 0x0, 0x0) },
+ { "Grey1", RGB_(0x3, 0x3, 0x3) },
+ { "Grey10", RGB_(0x1a, 0x1a, 0x1a) },
+ { "Grey100", RGB_(0xff, 0xff, 0xff) },
+ { "Grey11", RGB_(0x1c, 0x1c, 0x1c) },
+ { "Grey12", RGB_(0x1f, 0x1f, 0x1f) },
+ { "Grey13", RGB_(0x21, 0x21, 0x21) },
+ { "Grey14", RGB_(0x24, 0x24, 0x24) },
+ { "Grey15", RGB_(0x26, 0x26, 0x26) },
+ { "Grey16", RGB_(0x29, 0x29, 0x29) },
+ { "Grey17", RGB_(0x2b, 0x2b, 0x2b) },
+ { "Grey18", RGB_(0x2e, 0x2e, 0x2e) },
+ { "Grey19", RGB_(0x30, 0x30, 0x30) },
+ { "Grey2", RGB_(0x5, 0x5, 0x5) },
+ { "Grey20", RGB_(0x33, 0x33, 0x33) },
+ { "Grey21", RGB_(0x36, 0x36, 0x36) },
+ { "Grey22", RGB_(0x38, 0x38, 0x38) },
+ { "Grey23", RGB_(0x3b, 0x3b, 0x3b) },
+ { "Grey24", RGB_(0x3d, 0x3d, 0x3d) },
+ { "Grey25", RGB_(0x40, 0x40, 0x40) },
+ { "Grey26", RGB_(0x42, 0x42, 0x42) },
+ { "Grey27", RGB_(0x45, 0x45, 0x45) },
+ { "Grey28", RGB_(0x47, 0x47, 0x47) },
+ { "Grey29", RGB_(0x4a, 0x4a, 0x4a) },
+ { "Grey3", RGB_(0x8, 0x8, 0x8) },
+ { "Grey30", RGB_(0x4d, 0x4d, 0x4d) },
+ { "Grey31", RGB_(0x4f, 0x4f, 0x4f) },
+ { "Grey32", RGB_(0x52, 0x52, 0x52) },
+ { "Grey33", RGB_(0x54, 0x54, 0x54) },
+ { "Grey34", RGB_(0x57, 0x57, 0x57) },
+ { "Grey35", RGB_(0x59, 0x59, 0x59) },
+ { "Grey36", RGB_(0x5c, 0x5c, 0x5c) },
+ { "Grey37", RGB_(0x5e, 0x5e, 0x5e) },
+ { "Grey38", RGB_(0x61, 0x61, 0x61) },
+ { "Grey39", RGB_(0x63, 0x63, 0x63) },
+ { "Grey4", RGB_(0xa, 0xa, 0xa) },
+ { "Grey40", RGB_(0x66, 0x66, 0x66) },
+ { "Grey41", RGB_(0x69, 0x69, 0x69) },
+ { "Grey42", RGB_(0x6b, 0x6b, 0x6b) },
+ { "Grey43", RGB_(0x6e, 0x6e, 0x6e) },
+ { "Grey44", RGB_(0x70, 0x70, 0x70) },
+ { "Grey45", RGB_(0x73, 0x73, 0x73) },
+ { "Grey46", RGB_(0x75, 0x75, 0x75) },
+ { "Grey47", RGB_(0x78, 0x78, 0x78) },
+ { "Grey48", RGB_(0x7a, 0x7a, 0x7a) },
+ { "Grey49", RGB_(0x7d, 0x7d, 0x7d) },
+ { "Grey5", RGB_(0xd, 0xd, 0xd) },
+ { "Grey50", RGB_(0x7f, 0x7f, 0x7f) },
+ { "Grey51", RGB_(0x82, 0x82, 0x82) },
+ { "Grey52", RGB_(0x85, 0x85, 0x85) },
+ { "Grey53", RGB_(0x87, 0x87, 0x87) },
+ { "Grey54", RGB_(0x8a, 0x8a, 0x8a) },
+ { "Grey55", RGB_(0x8c, 0x8c, 0x8c) },
+ { "Grey56", RGB_(0x8f, 0x8f, 0x8f) },
+ { "Grey57", RGB_(0x91, 0x91, 0x91) },
+ { "Grey58", RGB_(0x94, 0x94, 0x94) },
+ { "Grey59", RGB_(0x96, 0x96, 0x96) },
+ { "Grey6", RGB_(0xf, 0xf, 0xf) },
+ { "Grey60", RGB_(0x99, 0x99, 0x99) },
+ { "Grey61", RGB_(0x9c, 0x9c, 0x9c) },
+ { "Grey62", RGB_(0x9e, 0x9e, 0x9e) },
+ { "Grey63", RGB_(0xa1, 0xa1, 0xa1) },
+ { "Grey64", RGB_(0xa3, 0xa3, 0xa3) },
+ { "Grey65", RGB_(0xa6, 0xa6, 0xa6) },
+ { "Grey66", RGB_(0xa8, 0xa8, 0xa8) },
+ { "Grey67", RGB_(0xab, 0xab, 0xab) },
+ { "Grey68", RGB_(0xad, 0xad, 0xad) },
+ { "Grey69", RGB_(0xb0, 0xb0, 0xb0) },
+ { "Grey7", RGB_(0x12, 0x12, 0x12) },
+ { "Grey70", RGB_(0xb3, 0xb3, 0xb3) },
+ { "Grey71", RGB_(0xb5, 0xb5, 0xb5) },
+ { "Grey72", RGB_(0xb8, 0xb8, 0xb8) },
+ { "Grey73", RGB_(0xba, 0xba, 0xba) },
+ { "Grey74", RGB_(0xbd, 0xbd, 0xbd) },
+ { "Grey75", RGB_(0xbf, 0xbf, 0xbf) },
+ { "Grey76", RGB_(0xc2, 0xc2, 0xc2) },
+ { "Grey77", RGB_(0xc4, 0xc4, 0xc4) },
+ { "Grey78", RGB_(0xc7, 0xc7, 0xc7) },
+ { "Grey79", RGB_(0xc9, 0xc9, 0xc9) },
+ { "Grey8", RGB_(0x14, 0x14, 0x14) },
+ { "Grey80", RGB_(0xcc, 0xcc, 0xcc) },
+ { "Grey81", RGB_(0xcf, 0xcf, 0xcf) },
+ { "Grey82", RGB_(0xd1, 0xd1, 0xd1) },
+ { "Grey83", RGB_(0xd4, 0xd4, 0xd4) },
+ { "Grey84", RGB_(0xd6, 0xd6, 0xd6) },
+ { "Grey85", RGB_(0xd9, 0xd9, 0xd9) },
+ { "Grey86", RGB_(0xdb, 0xdb, 0xdb) },
+ { "Grey87", RGB_(0xde, 0xde, 0xde) },
+ { "Grey88", RGB_(0xe0, 0xe0, 0xe0) },
+ { "Grey89", RGB_(0xe3, 0xe3, 0xe3) },
+ { "Grey9", RGB_(0x17, 0x17, 0x17) },
+ { "Grey90", RGB_(0xe5, 0xe5, 0xe5) },
+ { "Grey91", RGB_(0xe8, 0xe8, 0xe8) },
+ { "Grey92", RGB_(0xeb, 0xeb, 0xeb) },
+ { "Grey93", RGB_(0xed, 0xed, 0xed) },
+ { "Grey94", RGB_(0xf0, 0xf0, 0xf0) },
+ { "Grey95", RGB_(0xf2, 0xf2, 0xf2) },
+ { "Grey96", RGB_(0xf5, 0xf5, 0xf5) },
+ { "Grey97", RGB_(0xf7, 0xf7, 0xf7) },
+ { "Grey98", RGB_(0xfa, 0xfa, 0xfa) },
+ { "Grey99", RGB_(0xfc, 0xfc, 0xfc) },
+ { "Honeydew", RGB_(0xf0, 0xff, 0xf0) },
+ { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) },
+ { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) },
+ { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) },
+ { "Honeydew4", RGB_(0x83, 0x8b, 0x83) },
+ { "HotPink", RGB_(0xff, 0x69, 0xb4) },
+ { "HotPink1", RGB_(0xff, 0x6e, 0xb4) },
+ { "HotPink2", RGB_(0xee, 0x6a, 0xa7) },
+ { "HotPink3", RGB_(0xcd, 0x60, 0x90) },
+ { "HotPink4", RGB_(0x8b, 0x3a, 0x62) },
+ { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) },
+ { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) },
+ { "IndianRed2", RGB_(0xee, 0x63, 0x63) },
+ { "IndianRed3", RGB_(0xcd, 0x55, 0x55) },
+ { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) },
+ { "Indigo", RGB_(0x4b, 0x00, 0x82) },
+ { "Ivory", RGB_(0xff, 0xff, 0xf0) },
+ { "Ivory1", RGB_(0xff, 0xff, 0xf0) },
+ { "Ivory2", RGB_(0xee, 0xee, 0xe0) },
+ { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) },
+ { "Ivory4", RGB_(0x8b, 0x8b, 0x83) },
+ { "Khaki", RGB_(0xf0, 0xe6, 0x8c) },
+ { "Khaki1", RGB_(0xff, 0xf6, 0x8f) },
+ { "Khaki2", RGB_(0xee, 0xe6, 0x85) },
+ { "Khaki3", RGB_(0xcd, 0xc6, 0x73) },
+ { "Khaki4", RGB_(0x8b, 0x86, 0x4e) },
+ { "Lavender", RGB_(0xe6, 0xe6, 0xfa) },
+ { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) },
+ { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) },
+ { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) },
+ { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) },
+ { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) },
+ { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) },
+ { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) },
+ { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) },
+ { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) },
+ { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) },
+ { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) },
+ { "LightBlue", RGB_(0xad, 0xd8, 0xe6) },
+ { "LightBlue1", RGB_(0xbf, 0xef, 0xff) },
+ { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) },
+ { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) },
+ { "LightBlue4", RGB_(0x68, 0x83, 0x8b) },
+ { "LightCoral", RGB_(0xf0, 0x80, 0x80) },
+ { "LightCyan", RGB_(0xe0, 0xff, 0xff) },
+ { "LightCyan1", RGB_(0xe0, 0xff, 0xff) },
+ { "LightCyan2", RGB_(0xd1, 0xee, 0xee) },
+ { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) },
+ { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) },
+ { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) },
+ { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) },
+ { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) },
+ { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) },
+ { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) },
+ { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) },
+ { "LightGray", RGB_(0xd3, 0xd3, 0xd3) },
+ { "LightGreen", RGB_(0x90, 0xee, 0x90) },
+ { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) },
+ { "LightMagenta", RGB_(0xff, 0xbb, 0xff) },
+ { "LightPink", RGB_(0xff, 0xb6, 0xc1) },
+ { "LightPink1", RGB_(0xff, 0xae, 0xb9) },
+ { "LightPink2", RGB_(0xee, 0xa2, 0xad) },
+ { "LightPink3", RGB_(0xcd, 0x8c, 0x95) },
+ { "LightPink4", RGB_(0x8b, 0x5f, 0x65) },
+ { "LightRed", RGB_(0xff, 0xbb, 0xbb) },
+ { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) },
+ { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) },
+ { "LightSalmon2", RGB_(0xee, 0x95, 0x72) },
+ { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) },
+ { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) },
+ { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) },
+ { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) },
+ { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) },
+ { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) },
+ { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) },
+ { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) },
+ { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) },
+ { "LightSlateGray", RGB_(0x77, 0x88, 0x99) },
+ { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) },
+ { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) },
+ { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) },
+ { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) },
+ { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) },
+ { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) },
+ { "LightYellow", RGB_(0xff, 0xff, 0xe0) },
+ { "LightYellow1", RGB_(0xff, 0xff, 0xe0) },
+ { "LightYellow2", RGB_(0xee, 0xee, 0xd1) },
+ { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) },
+ { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) },
+ { "Lime", RGB_(0x00, 0xff, 0x00) },
+ { "LimeGreen", RGB_(0x32, 0xcd, 0x32) },
+ { "Linen", RGB_(0xfa, 0xf0, 0xe6) },
+ { "Magenta", RGB_(0xff, 0x00, 0xff) },
+ { "Magenta1", RGB_(0xff, 0x0, 0xff) },
+ { "Magenta2", RGB_(0xee, 0x0, 0xee) },
+ { "Magenta3", RGB_(0xcd, 0x0, 0xcd) },
+ { "Magenta4", RGB_(0x8b, 0x0, 0x8b) },
+ { "Maroon", RGB_(0x80, 0x00, 0x00) },
+ { "Maroon1", RGB_(0xff, 0x34, 0xb3) },
+ { "Maroon2", RGB_(0xee, 0x30, 0xa7) },
+ { "Maroon3", RGB_(0xcd, 0x29, 0x90) },
+ { "Maroon4", RGB_(0x8b, 0x1c, 0x62) },
+ { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) },
+ { "MediumBlue", RGB_(0x00, 0x00, 0xcd) },
+ { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) },
+ { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) },
+ { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) },
+ { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) },
+ { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) },
+ { "MediumPurple", RGB_(0x93, 0x70, 0xdb) },
+ { "MediumPurple1", RGB_(0xab, 0x82, 0xff) },
+ { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) },
+ { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) },
+ { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) },
+ { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) },
+ { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) },
+ { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) },
+ { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) },
+ { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) },
+ { "MidnightBlue", RGB_(0x19, 0x19, 0x70) },
+ { "MintCream", RGB_(0xf5, 0xff, 0xfa) },
+ { "MistyRose", RGB_(0xff, 0xe4, 0xe1) },
+ { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) },
+ { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) },
+ { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) },
+ { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) },
+ { "Moccasin", RGB_(0xff, 0xe4, 0xb5) },
+ { "NavajoWhite", RGB_(0xff, 0xde, 0xad) },
+ { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) },
+ { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) },
+ { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) },
+ { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) },
+ { "Navy", RGB_(0x00, 0x00, 0x80) },
+ { "NavyBlue", RGB_(0x0, 0x0, 0x80) },
+ { "OldLace", RGB_(0xfd, 0xf5, 0xe6) },
+ { "Olive", RGB_(0x80, 0x80, 0x00) },
+ { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) },
+ { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) },
+ { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) },
+ { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) },
+ { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) },
+ { "Orange", RGB_(0xff, 0xa5, 0x00) },
+ { "Orange1", RGB_(0xff, 0xa5, 0x0) },
+ { "Orange2", RGB_(0xee, 0x9a, 0x0) },
+ { "Orange3", RGB_(0xcd, 0x85, 0x0) },
+ { "Orange4", RGB_(0x8b, 0x5a, 0x0) },
+ { "OrangeRed", RGB_(0xff, 0x45, 0x00) },
+ { "OrangeRed1", RGB_(0xff, 0x45, 0x0) },
+ { "OrangeRed2", RGB_(0xee, 0x40, 0x0) },
+ { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) },
+ { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) },
+ { "Orchid", RGB_(0xda, 0x70, 0xd6) },
+ { "Orchid1", RGB_(0xff, 0x83, 0xfa) },
+ { "Orchid2", RGB_(0xee, 0x7a, 0xe9) },
+ { "Orchid3", RGB_(0xcd, 0x69, 0xc9) },
+ { "Orchid4", RGB_(0x8b, 0x47, 0x89) },
+ { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) },
+ { "PaleGreen", RGB_(0x98, 0xfb, 0x98) },
+ { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) },
+ { "PaleGreen2", RGB_(0x90, 0xee, 0x90) },
+ { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) },
+ { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) },
+ { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) },
+ { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) },
+ { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) },
+ { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) },
+ { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) },
+ { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) },
+ { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) },
+ { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) },
+ { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) },
+ { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) },
+ { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) },
+ { "PeachPuff", RGB_(0xff, 0xda, 0xb9) },
+ { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) },
+ { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) },
+ { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) },
+ { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) },
+ { "Peru", RGB_(0xcd, 0x85, 0x3f) },
+ { "Pink", RGB_(0xff, 0xc0, 0xcb) },
+ { "Pink1", RGB_(0xff, 0xb5, 0xc5) },
+ { "Pink2", RGB_(0xee, 0xa9, 0xb8) },
+ { "Pink3", RGB_(0xcd, 0x91, 0x9e) },
+ { "Pink4", RGB_(0x8b, 0x63, 0x6c) },
+ { "Plum", RGB_(0xdd, 0xa0, 0xdd) },
+ { "Plum1", RGB_(0xff, 0xbb, 0xff) },
+ { "Plum2", RGB_(0xee, 0xae, 0xee) },
+ { "Plum3", RGB_(0xcd, 0x96, 0xcd) },
+ { "Plum4", RGB_(0x8b, 0x66, 0x8b) },
+ { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) },
+ { "Purple", RGB_(0x80, 0x00, 0x80) },
+ { "Purple1", RGB_(0x9b, 0x30, 0xff) },
+ { "Purple2", RGB_(0x91, 0x2c, 0xee) },
+ { "Purple3", RGB_(0x7d, 0x26, 0xcd) },
+ { "Purple4", RGB_(0x55, 0x1a, 0x8b) },
+ { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) },
+ { "Red", RGB_(0xff, 0x00, 0x00) },
+ { "Red1", RGB_(0xff, 0x0, 0x0) },
+ { "Red2", RGB_(0xee, 0x0, 0x0) },
+ { "Red3", RGB_(0xcd, 0x0, 0x0) },
+ { "Red4", RGB_(0x8b, 0x0, 0x0) },
+ { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) },
+ { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) },
+ { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) },
+ { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) },
+ { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) },
+ { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) },
+ { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) },
+ { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) },
+ { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) },
+ { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) },
+ { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) },
+ { "Salmon", RGB_(0xfa, 0x80, 0x72) },
+ { "Salmon1", RGB_(0xff, 0x8c, 0x69) },
+ { "Salmon2", RGB_(0xee, 0x82, 0x62) },
+ { "Salmon3", RGB_(0xcd, 0x70, 0x54) },
+ { "Salmon4", RGB_(0x8b, 0x4c, 0x39) },
+ { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) },
+ { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) },
+ { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) },
+ { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) },
+ { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) },
+ { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) },
+ { "SeaShell", RGB_(0xff, 0xf5, 0xee) },
+ { "Seashell1", RGB_(0xff, 0xf5, 0xee) },
+ { "Seashell2", RGB_(0xee, 0xe5, 0xde) },
+ { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) },
+ { "Seashell4", RGB_(0x8b, 0x86, 0x82) },
+ { "Sienna", RGB_(0xa0, 0x52, 0x2d) },
+ { "Sienna1", RGB_(0xff, 0x82, 0x47) },
+ { "Sienna2", RGB_(0xee, 0x79, 0x42) },
+ { "Sienna3", RGB_(0xcd, 0x68, 0x39) },
+ { "Sienna4", RGB_(0x8b, 0x47, 0x26) },
+ { "Silver", RGB_(0xc0, 0xc0, 0xc0) },
+ { "SkyBlue", RGB_(0x87, 0xce, 0xeb) },
+ { "SkyBlue1", RGB_(0x87, 0xce, 0xff) },
+ { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) },
+ { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) },
+ { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) },
+ { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) },
+ { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) },
+ { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) },
+ { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) },
+ { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) },
+ { "SlateGray", RGB_(0x70, 0x80, 0x90) },
+ { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) },
+ { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) },
+ { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) },
+ { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) },
+ { "SlateGrey", RGB_(0x70, 0x80, 0x90) },
+ { "Snow", RGB_(0xff, 0xfa, 0xfa) },
+ { "Snow1", RGB_(0xff, 0xfa, 0xfa) },
+ { "Snow2", RGB_(0xee, 0xe9, 0xe9) },
+ { "Snow3", RGB_(0xcd, 0xc9, 0xc9) },
+ { "Snow4", RGB_(0x8b, 0x89, 0x89) },
+ { "SpringGreen", RGB_(0x00, 0xff, 0x7f) },
+ { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) },
+ { "SpringGreen2", RGB_(0x0, 0xee, 0x76) },
+ { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) },
+ { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) },
+ { "SteelBlue", RGB_(0x46, 0x82, 0xb4) },
+ { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) },
+ { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) },
+ { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) },
+ { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) },
+ { "Tan", RGB_(0xd2, 0xb4, 0x8c) },
+ { "Tan1", RGB_(0xff, 0xa5, 0x4f) },
+ { "Tan2", RGB_(0xee, 0x9a, 0x49) },
+ { "Tan3", RGB_(0xcd, 0x85, 0x3f) },
+ { "Tan4", RGB_(0x8b, 0x5a, 0x2b) },
+ { "Teal", RGB_(0x00, 0x80, 0x80) },
+ { "Thistle", RGB_(0xd8, 0xbf, 0xd8) },
+ { "Thistle1", RGB_(0xff, 0xe1, 0xff) },
+ { "Thistle2", RGB_(0xee, 0xd2, 0xee) },
+ { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) },
+ { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) },
+ { "Tomato", RGB_(0xff, 0x63, 0x47) },
+ { "Tomato1", RGB_(0xff, 0x63, 0x47) },
+ { "Tomato2", RGB_(0xee, 0x5c, 0x42) },
+ { "Tomato3", RGB_(0xcd, 0x4f, 0x39) },
+ { "Tomato4", RGB_(0x8b, 0x36, 0x26) },
+ { "Turquoise", RGB_(0x40, 0xe0, 0xd0) },
+ { "Turquoise1", RGB_(0x0, 0xf5, 0xff) },
+ { "Turquoise2", RGB_(0x0, 0xe5, 0xee) },
+ { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) },
+ { "Turquoise4", RGB_(0x0, 0x86, 0x8b) },
+ { "Violet", RGB_(0xee, 0x82, 0xee) },
+ { "VioletRed", RGB_(0xd0, 0x20, 0x90) },
+ { "VioletRed1", RGB_(0xff, 0x3e, 0x96) },
+ { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) },
+ { "VioletRed3", RGB_(0xcd, 0x32, 0x78) },
+ { "VioletRed4", RGB_(0x8b, 0x22, 0x52) },
+ { "WebGray", RGB_(0x80, 0x80, 0x80) },
+ { "WebGreen", RGB_(0x0, 0x80, 0x0) },
+ { "WebGrey", RGB_(0x80, 0x80, 0x80) },
+ { "WebMaroon", RGB_(0x80, 0x0, 0x0) },
+ { "WebPurple", RGB_(0x80, 0x0, 0x80) },
+ { "Wheat", RGB_(0xf5, 0xde, 0xb3) },
+ { "Wheat1", RGB_(0xff, 0xe7, 0xba) },
+ { "Wheat2", RGB_(0xee, 0xd8, 0xae) },
+ { "Wheat3", RGB_(0xcd, 0xba, 0x96) },
+ { "Wheat4", RGB_(0x8b, 0x7e, 0x66) },
+ { "White", RGB_(0xff, 0xff, 0xff) },
+ { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) },
+ { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) },
+ { "X11Green", RGB_(0x0, 0xff, 0x0) },
+ { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) },
+ { "X11Maroon", RGB_(0xb0, 0x30, 0x60) },
+ { "X11Purple", RGB_(0xa0, 0x20, 0xf0) },
+ { "Yellow", RGB_(0xff, 0xff, 0x00) },
+ { "Yellow1", RGB_(0xff, 0xff, 0x0) },
+ { "Yellow2", RGB_(0xee, 0xee, 0x0) },
+ { "Yellow3", RGB_(0xcd, 0xcd, 0x0) },
+ { "Yellow4", RGB_(0x8b, 0x8b, 0x0) },
+ { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) },
{ NULL, 0 },
};
@@ -8182,7 +8488,7 @@ color_name_table_T color_name_table[] = {
///
/// @param[in] name string value to convert to RGB
/// return the hex value or -1 if could not find a correct value
-RgbValue name_to_color(const uint8_t *name)
+RgbValue name_to_color(const char_u *name)
{
if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2])
@@ -8205,6 +8511,27 @@ RgbValue name_to_color(const uint8_t *name)
return -1;
}
+/// Gets highlight description for id `attr_id` as a map.
+Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
+{
+ HlAttrs *aep = NULL;
+ Dictionary dic = ARRAY_DICT_INIT;
+
+ if (attr_id == 0) {
+ return dic;
+ }
+
+ aep = syn_cterm_attr2entry((int)attr_id);
+ if (!aep) {
+ api_set_error(err, kErrorTypeException,
+ "Invalid attribute id: %" PRId64, attr_id);
+ return dic;
+ }
+
+ return hlattrs2dict(aep, rgb);
+}
+
+
/**************************************
* End of Highlighting stuff *
**************************************/
diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h
index bb733ead30..f8282955a6 100644
--- a/src/nvim/syntax.h
+++ b/src/nvim/syntax.h
@@ -6,19 +6,6 @@
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
-/// Terminal highlighting attribute bits.
-/// Attributes above HL_ALL are used for syntax highlighting.
-/// \addtogroup HL_ATTRIBUTES
-/// @{
-#define HL_NORMAL 0x00
-#define HL_INVERSE 0x01
-#define HL_BOLD 0x02
-#define HL_ITALIC 0x04
-#define HL_UNDERLINE 0x08
-#define HL_UNDERCURL 0x10
-#define HL_STANDOUT 0x20
-/// @}
-
#define HL_CONTAINED 0x01 /* not used on toplevel */
#define HL_TRANSP 0x02 /* has no highlighting */
#define HL_ONELINE 0x04 /* match within one line only */
@@ -45,6 +32,9 @@ typedef struct {
} color_name_table_T;
extern color_name_table_T color_name_table[];
+/// Array of highlight definitions, used for unit testing
+extern const char *const highlight_init_cmdline[];
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "syntax.h.generated.h"
#endif
diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h
index 9f309451b0..63089a62af 100644
--- a/src/nvim/syntax_defs.h
+++ b/src/nvim/syntax_defs.h
@@ -2,7 +2,6 @@
#define NVIM_SYNTAX_DEFS_H
#include "nvim/highlight_defs.h"
-#include "nvim/regexp_defs.h"
# define SST_MIN_ENTRIES 150 /* minimal size for state stack array */
# define SST_MAX_ENTRIES 1000 /* maximal size for state stack array */
@@ -10,6 +9,11 @@
# define SST_DIST 16 /* normal distance between entries */
# define SST_INVALID (synstate_T *)-1 /* invalid syn_state pointer */
+typedef struct syn_state synstate_T;
+
+#include "nvim/buffer_defs.h"
+#include "nvim/regexp_defs.h"
+
typedef unsigned short disptick_T; /* display tick type */
/* struct passed to in_id_list() */
@@ -48,8 +52,6 @@ typedef struct buf_state {
* syn_state contains the syntax state stack for the start of one line.
* Used by b_sst_array[].
*/
-typedef struct syn_state synstate_T;
-
struct syn_state {
synstate_T *sst_next; /* next entry in used or free list */
linenr_T sst_lnum; /* line number for this state */
@@ -66,11 +68,4 @@ struct syn_state {
* may have made the state invalid */
};
-// Structure shared between syntax.c, screen.c
-typedef struct attr_entry {
- int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc.
- RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color;
- int cterm_fg_color, cterm_bg_color;
-} attrentry_T;
-
#endif // NVIM_SYNTAX_DEFS_H
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index be9d621c7d..f23465e501 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -667,7 +667,7 @@ do_tag (
fname = xmalloc(MAXPATHL + 1);
cmd = xmalloc(CMDBUFFSIZE + 1);
- list = tv_list_alloc();
+ list = tv_list_alloc(num_matches);
for (i = 0; i < num_matches; ++i) {
int len, cmd_len;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 1882f263db..276b47536f 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -43,6 +43,7 @@
#include <vterm.h>
+#include "nvim/log.h"
#include "nvim/vim.h"
#include "nvim/terminal.h"
#include "nvim/message.h"
@@ -60,7 +61,6 @@
#include "nvim/edit.h"
#include "nvim/mouse.h"
#include "nvim/memline.h"
-#include "nvim/mark.h"
#include "nvim/map.h"
#include "nvim/misc1.h"
#include "nvim/move.h"
@@ -180,13 +180,14 @@ void terminal_init(void)
vterm_state_get_palette_color(state, color_index, &color);
}
map_put(int, int)(color_indexes,
- RGB(color.red, color.green, color.blue), color_index + 1);
+ RGB_(color.red, color.green, color.blue),
+ color_index + 1);
}
VTermColor fg, bg;
vterm_state_get_default_colors(state, &fg, &bg);
- default_vt_fg = RGB(fg.red, fg.green, fg.blue);
- default_vt_bg = RGB(bg.red, bg.green, bg.blue);
+ default_vt_fg = RGB_(fg.red, fg.green, fg.blue);
+ default_vt_bg = RGB_(bg.red, bg.green, bg.blue);
default_vt_bg_rgb = bg;
vterm_free(vt);
}
@@ -229,7 +230,7 @@ Terminal *terminal_open(TerminalOptions opts)
rv->invalid_start = 0;
rv->invalid_end = opts.height;
refresh_screen(rv, curbuf);
- set_option_value("buftype", 0, "terminal", OPT_LOCAL);
+ set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666
// Default settings for terminal buffers
curbuf->b_p_ma = false; // 'nomodifiable'
@@ -375,8 +376,6 @@ void terminal_enter(void)
// Ensure the terminal is properly sized.
terminal_resize(s->term, 0, 0);
- checkpcmark();
- setpcmark();
int save_state = State;
s->save_rd = RedrawingDisabled;
State = TERM_FOCUS;
@@ -431,14 +430,6 @@ static int terminal_execute(VimState *state, int key)
TerminalState *s = (TerminalState *)state;
switch (key) {
- case K_FOCUSGAINED: // nvim has been given focus
- apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
- break;
-
- case K_FOCUSLOST: // nvim has lost focus
- apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
- break;
-
// Temporary fix until paste events gets implemented
case K_PASTE:
break;
@@ -470,6 +461,10 @@ static int terminal_execute(VimState *state, int key)
}
break;
+ case K_COMMAND:
+ do_cmdline(NULL, getcmdkeycmd, NULL, 0);
+ break;
+
case Ctrl_N:
if (s->got_bsl) {
return 0;
@@ -529,6 +524,12 @@ void terminal_send(Terminal *term, char *data, size_t size)
void terminal_send_key(Terminal *term, int c)
{
VTermModifier mod = VTERM_MOD_NONE;
+
+ // Convert K_ZERO back to ASCII
+ if (c == K_ZERO) {
+ c = Ctrl_AT;
+ }
+
VTermKey key = convert_key(c, &mod);
if (key) {
@@ -553,7 +554,7 @@ void terminal_receive(Terminal *term, char *data, size_t len)
}
void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
- int *term_attrs)
+ int *term_attrs)
{
int height, width;
vterm_get_size(term->vt, &height, &width);
@@ -569,8 +570,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
VTermScreenCell cell;
fetch_cell(term, row, col, &cell);
// Get the rgb value set by libvterm.
- int vt_fg = RGB(cell.fg.red, cell.fg.green, cell.fg.blue);
- int vt_bg = RGB(cell.bg.red, cell.bg.green, cell.bg.blue);
+ int vt_fg = RGB_(cell.fg.red, cell.fg.green, cell.fg.blue);
+ int vt_bg = RGB_(cell.bg.red, cell.bg.green, cell.bg.blue);
vt_fg = vt_fg != default_vt_fg ? vt_fg : - 1;
vt_bg = vt_bg != default_vt_bg ? vt_bg : - 1;
// Since libvterm does not expose the color index used by the program, we
@@ -589,7 +590,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
int attr_id = 0;
if (hl_attrs || vt_fg != -1 || vt_bg != -1) {
- attr_id = get_attr_entry(&(attrentry_T) {
+ attr_id = get_attr_entry(&(HlAttrs) {
.cterm_ae_attr = (int16_t)hl_attrs,
.cterm_fg_color = vt_fg_idx,
.cterm_bg_color = vt_bg_idx,
@@ -782,26 +783,60 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data)
// }}}
// input handling {{{
-static void convert_modifiers(VTermModifier *statep)
+static void convert_modifiers(int key, VTermModifier *statep)
{
if (mod_mask & MOD_MASK_SHIFT) { *statep |= VTERM_MOD_SHIFT; }
if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; }
if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; }
+
+ switch (key) {
+ case K_S_TAB:
+ case K_S_UP:
+ case K_S_DOWN:
+ case K_S_LEFT:
+ case K_S_RIGHT:
+ case K_S_F1:
+ case K_S_F2:
+ case K_S_F3:
+ case K_S_F4:
+ case K_S_F5:
+ case K_S_F6:
+ case K_S_F7:
+ case K_S_F8:
+ case K_S_F9:
+ case K_S_F10:
+ case K_S_F11:
+ case K_S_F12:
+ *statep |= VTERM_MOD_SHIFT;
+ break;
+
+ case K_C_LEFT:
+ case K_C_RIGHT:
+ *statep |= VTERM_MOD_CTRL;
+ break;
+ }
}
static VTermKey convert_key(int key, VTermModifier *statep)
{
- convert_modifiers(statep);
+ convert_modifiers(key, statep);
switch (key) {
case K_BS: return VTERM_KEY_BACKSPACE;
+ case K_S_TAB: // FALLTHROUGH
case TAB: return VTERM_KEY_TAB;
case Ctrl_M: return VTERM_KEY_ENTER;
case ESC: return VTERM_KEY_ESCAPE;
+ case K_S_UP: // FALLTHROUGH
case K_UP: return VTERM_KEY_UP;
+ case K_S_DOWN: // FALLTHROUGH
case K_DOWN: return VTERM_KEY_DOWN;
+ case K_S_LEFT: // FALLTHROUGH
+ case K_C_LEFT: // FALLTHROUGH
case K_LEFT: return VTERM_KEY_LEFT;
+ case K_S_RIGHT: // FALLTHROUGH
+ case K_C_RIGHT: // FALLTHROUGH
case K_RIGHT: return VTERM_KEY_RIGHT;
case K_INS: return VTERM_KEY_INS;
@@ -811,22 +846,22 @@ static VTermKey convert_key(int key, VTermModifier *statep)
case K_PAGEUP: return VTERM_KEY_PAGEUP;
case K_PAGEDOWN: return VTERM_KEY_PAGEDOWN;
- case K_K0:
+ case K_K0: // FALLTHROUGH
case K_KINS: return VTERM_KEY_KP_0;
- case K_K1:
+ case K_K1: // FALLTHROUGH
case K_KEND: return VTERM_KEY_KP_1;
case K_K2: return VTERM_KEY_KP_2;
- case K_K3:
+ case K_K3: // FALLTHROUGH
case K_KPAGEDOWN: return VTERM_KEY_KP_3;
case K_K4: return VTERM_KEY_KP_4;
case K_K5: return VTERM_KEY_KP_5;
case K_K6: return VTERM_KEY_KP_6;
- case K_K7:
+ case K_K7: // FALLTHROUGH
case K_KHOME: return VTERM_KEY_KP_7;
case K_K8: return VTERM_KEY_KP_8;
- case K_K9:
+ case K_K9: // FALLTHROUGH
case K_KPAGEUP: return VTERM_KEY_KP_9;
- case K_KDEL:
+ case K_KDEL: // FALLTHROUGH
case K_KPOINT: return VTERM_KEY_KP_PERIOD;
case K_KENTER: return VTERM_KEY_KP_ENTER;
case K_KPLUS: return VTERM_KEY_KP_PLUS;
@@ -834,6 +869,57 @@ static VTermKey convert_key(int key, VTermModifier *statep)
case K_KMULTIPLY: return VTERM_KEY_KP_MULT;
case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE;
+ case K_S_F1: // FALLTHROUGH
+ case K_F1: return VTERM_KEY_FUNCTION(1);
+ case K_S_F2: // FALLTHROUGH
+ case K_F2: return VTERM_KEY_FUNCTION(2);
+ case K_S_F3: // FALLTHROUGH
+ case K_F3: return VTERM_KEY_FUNCTION(3);
+ case K_S_F4: // FALLTHROUGH
+ case K_F4: return VTERM_KEY_FUNCTION(4);
+ case K_S_F5: // FALLTHROUGH
+ case K_F5: return VTERM_KEY_FUNCTION(5);
+ case K_S_F6: // FALLTHROUGH
+ case K_F6: return VTERM_KEY_FUNCTION(6);
+ case K_S_F7: // FALLTHROUGH
+ case K_F7: return VTERM_KEY_FUNCTION(7);
+ case K_S_F8: // FALLTHROUGH
+ case K_F8: return VTERM_KEY_FUNCTION(8);
+ case K_S_F9: // FALLTHROUGH
+ case K_F9: return VTERM_KEY_FUNCTION(9);
+ case K_S_F10: // FALLTHROUGH
+ case K_F10: return VTERM_KEY_FUNCTION(10);
+ case K_S_F11: // FALLTHROUGH
+ case K_F11: return VTERM_KEY_FUNCTION(11);
+ case K_S_F12: // FALLTHROUGH
+ case K_F12: return VTERM_KEY_FUNCTION(12);
+
+ case K_F13: return VTERM_KEY_FUNCTION(13);
+ case K_F14: return VTERM_KEY_FUNCTION(14);
+ case K_F15: return VTERM_KEY_FUNCTION(15);
+ case K_F16: return VTERM_KEY_FUNCTION(16);
+ case K_F17: return VTERM_KEY_FUNCTION(17);
+ case K_F18: return VTERM_KEY_FUNCTION(18);
+ case K_F19: return VTERM_KEY_FUNCTION(19);
+ case K_F20: return VTERM_KEY_FUNCTION(20);
+ case K_F21: return VTERM_KEY_FUNCTION(21);
+ case K_F22: return VTERM_KEY_FUNCTION(22);
+ case K_F23: return VTERM_KEY_FUNCTION(23);
+ case K_F24: return VTERM_KEY_FUNCTION(24);
+ case K_F25: return VTERM_KEY_FUNCTION(25);
+ case K_F26: return VTERM_KEY_FUNCTION(26);
+ case K_F27: return VTERM_KEY_FUNCTION(27);
+ case K_F28: return VTERM_KEY_FUNCTION(28);
+ case K_F29: return VTERM_KEY_FUNCTION(29);
+ case K_F30: return VTERM_KEY_FUNCTION(30);
+ case K_F31: return VTERM_KEY_FUNCTION(31);
+ case K_F32: return VTERM_KEY_FUNCTION(32);
+ case K_F33: return VTERM_KEY_FUNCTION(33);
+ case K_F34: return VTERM_KEY_FUNCTION(34);
+ case K_F35: return VTERM_KEY_FUNCTION(35);
+ case K_F36: return VTERM_KEY_FUNCTION(36);
+ case K_F37: return VTERM_KEY_FUNCTION(37);
+
default: return VTERM_KEY_NONE;
}
}
@@ -1010,8 +1096,12 @@ static void refresh_terminal(Terminal *term)
// Calls refresh_terminal() on all invalidated_terminals.
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
- if (exiting) { // Cannot redraw (requires event loop) during teardown/exit.
- goto end;
+ refresh_pending = false;
+ if (exiting // Cannot redraw (requires event loop) during teardown/exit.
+ // WM_LIST (^D) is not redrawn, unlike the normal wildmenu. So we must
+ // skip redraws to keep it visible.
+ || wild_menu_showing == WM_LIST) {
+ return;
}
Terminal *term;
void *stub; (void)(stub);
@@ -1026,8 +1116,6 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
if (any_visible) {
redraw(true);
}
-end:
- refresh_pending = false;
}
static void refresh_size(Terminal *term, buf_T *buf)
@@ -1172,6 +1260,10 @@ static void redraw(bool restore_cursor)
update_screen(0);
}
+ if (need_maketitle) { // Update title in terminal-mode. #7248
+ maketitle();
+ }
+
if (restore_cursor) {
ui_cursor_goto(save_row, save_col);
} else if (term) {
diff --git a/src/nvim/terminal.h b/src/nvim/terminal.h
index 25e609fb68..f2b0e232c3 100644
--- a/src/nvim/terminal.h
+++ b/src/nvim/terminal.h
@@ -10,6 +10,8 @@ typedef void (*terminal_write_cb)(char *buffer, size_t size, void *data);
typedef void (*terminal_resize_cb)(uint16_t width, uint16_t height, void *data);
typedef void (*terminal_close_cb)(void *data);
+#include "nvim/buffer_defs.h"
+
typedef struct {
void *data;
uint16_t width, height;
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 7e55fffa06..a31e1843fc 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -2,68 +2,106 @@
# Makefile to run all tests for Vim
#
-NVIM_PRG ?= ../../../build/bin/nvim
-SCRIPTSOURCE := ../../../runtime
+ifeq ($(OS),Windows_NT)
+ NVIM_PRG ?= ../../../build/bin/nvim.exe
+else
+ NVIM_PRG ?= ../../../build/bin/nvim
+endif
+ROOT := ../../..
export SHELL := sh
export NVIM_PRG := $(NVIM_PRG)
+export TMPDIR := $(abspath ../../../Xtest-tmpdir)
-SCRIPTS ?= \
- test13.out \
+SCRIPTS_DEFAULT = \
test14.out \
- test17.out \
test24.out \
- test32.out \
test37.out \
test40.out \
test42.out \
test48.out \
test49.out \
test52.out \
- test53.out \
test64.out \
- test73.out \
- test79.out \
+
+ifneq ($(OS),Windows_NT)
+ SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
+ test17.out \
+
+endif
+
+SCRIPTS ?= $(SCRIPTS_DEFAULT)
# Tests using runtest.vim.
# Keep test_alot*.res as the last one, sort the others.
NEW_TESTS ?= \
+ test_arabic.res \
test_autocmd.res \
test_bufwintabinfo.res \
+ test_changedtick.res \
test_charsearch.res \
+ test_cindent.res \
+ test_close_count.res \
test_cmdline.res \
test_command_count.res \
test_cscope.res \
+ test_curswant.res \
test_digraph.res \
+ test_edit.res \
+ test_erasebackword.res \
+ test_exists.res \
test_diffmode.res \
test_farsi.res \
+ test_file_size.res \
test_filter_map.res \
+ test_find_complete.res \
+ test_fixeol.res \
+ test_findfile.res \
test_fnameescape.res \
test_fold.res \
+ test_ga.res \
test_glob2regpat.res \
test_gf.res \
test_gn.res \
test_hardcopy.res \
test_help_tagjump.res \
+ test_hide.res \
test_history.res \
test_hlsearch.res \
test_increment.res \
test_increment_dbcs.res \
+ test_ins_complete.res \
test_lambda.res \
test_langmap.res \
+ test_let.res \
+ test_lineending.res \
+ test_listdict.res \
+ test_listchars.res \
+ test_makeencoding.res \
test_marks.res \
test_match.res \
test_matchadd_conceal.res \
- test_matchadd_conceal_utf8.res \
+ test_mksession.res \
test_nested_function.res \
test_normal.res \
+ test_number.res \
+ test_options.res \
+ test_profile.res \
+ test_put.res \
test_quickfix.res \
+ test_recover.res \
+ test_retab.res \
+ test_scrollbind.res \
test_search.res \
test_signs.res \
test_smartindent.res \
+ test_spell.res \
test_stat.res \
+ test_startup.res \
test_substitute.res \
test_syntax.res \
+ test_system.res \
+ test_tab.res \
test_tabpage.res \
test_textobjects.res \
test_timers.res \
@@ -71,8 +109,11 @@ NEW_TESTS ?= \
test_usercommands.res \
test_vimscript.res \
test_visual.res \
+ test_winbuf_close.res \
test_window_id.res \
test_writefile.res \
+ test_alot_latin.res \
+ test_alot_utf8.res \
test_alot.res
SCRIPTS_GUI := test16.out
@@ -93,7 +134,7 @@ ifdef USE_VALGRIND
$(VALGRIND_TOOL) \
--suppressions=../../.valgrind.supp \
--error-exitcode=123 \
- --log-file=valgrind.\%p.$* \
+ --log-file=valgrind-\%p.$* \
$(VGDB) \
--trace-children=yes
else
@@ -111,7 +152,8 @@ nongui: nolog $(SCRIPTS) newtests report
gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report
.gdbinit:
- echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit
+ @echo "[OLDTEST-PREP] Setting up .gdbinit"
+ @echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit
report:
@echo
@@ -130,7 +172,7 @@ $(SCRIPTS) $(SCRIPTS_GUI): $(NVIM_PRG) test1.out
RM_ON_RUN := test.out X* viminfo
RM_ON_START := test.ok
-RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in
+RUN_VIM := $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --headless --noplugin -s dotest.in
clean:
-rm -rf *.out \
@@ -138,6 +180,7 @@ clean:
*.res \
*.rej \
*.orig \
+ *.tlog \
test.log \
messages \
$(RM_ON_RUN) \
@@ -146,60 +189,36 @@ clean:
.*.swp \
.*.swo \
.gdbinit \
+ $(TMPDIR) \
del
test1.out: .gdbinit test1.in
- -rm -rf $*.failed $(RM_ON_RUN) $(RM_ON_START) wrongtermsize
- $(RUN_VIM) $*.in
- @/bin/sh -c "if test -e wrongtermsize; then \
- echo; \
- echo test1 FAILED - terminal size must be 80x24 or larger; \
- echo; exit 1; \
- elif diff test.out $*.ok; then \
- mv -f test.out $*.out; \
- else \
- echo; \
- echo test1 FAILED - Something basic is wrong; \
- echo; \
- exit 1; \
- fi"
- -rm -rf X* viminfo
+ @echo "[OLDTEST-PREP] Running test1"
+ @rm -rf $*.failed $(RM_ON_RUN) $(RM_ON_START) wrongtermsize
+ @mkdir -p $(TMPDIR)
+ @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in
+ @rm -f wrongtermsize
+ @rm -rf X* viminfo
%.out: %.in .gdbinit
- -rm -rf $*.failed test.ok $(RM_ON_RUN)
- cp $*.ok test.ok
- # Sleep a moment to avoid that the xterm title is messed up.
- # 200 msec is sufficient, but only modern sleep supports a fraction of
- # a second, fall back to a second if it fails.
- @-/bin/sh -c "sleep .2 > /dev/null 2>&1 || sleep 1"
- $(RUN_VIM) $*.in
-
- # Check if the test.out file matches test.ok.
- @/bin/sh -c "if test -f test.out; then \
- if diff -u test.out $*.ok; then \
- mv -f test.out $*.out; \
- else \
- echo $* FAILED >> test.log; \
- mv -f test.out $*.failed; \
- fi; \
- else \
- echo $* NO OUTPUT >>test.log; \
- fi"
- @/bin/sh -c "if test -f valgrind; then \
- mv -f valgrind valgrind.$*; \
- fi"
- -rm -rf X* test.ok viminfo
+ @echo "[OLDESTTEST] Running" $*
+ @rm -rf $*.failed test.ok $(RM_ON_RUN)
+ @mkdir -p $(TMPDIR)
+ @cp $*.ok test.ok
+ @/bin/sh runnvim.sh --oldesttest $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in
+ @rm -rf X* test.ok viminfo
test49.out: test49.vim
nolog:
- -rm -f test.log messages
+ @echo "[OLDTEST-PREP] Removing test.log and messages"
+ @rm -f test.log messages
# New style of tests uses Vim script with assert calls. These are easier
# to write and a lot easier to read and debug.
# Limitation: Only works with the +eval feature.
-RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(NVIM_PRG) -u unix.vim -U NONE --noplugin
+RUN_VIMTEST = $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE --headless --noplugin
newtests: newtestssilent
@/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \
@@ -209,4 +228,6 @@ newtests: newtestssilent
newtestssilent: $(NEW_TESTS)
%.res: %.vim .gdbinit
- $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim
+ @echo "[OLDTEST] Running" $*
+ @mkdir -p $(TMPDIR)
+ @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim
diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh
new file mode 100755
index 0000000000..43556f3ad3
--- /dev/null
+++ b/src/nvim/testdir/runnvim.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+main() {(
+ local separator="================================================================================"
+ local oldesttest=
+ if test "$1" = "--oldesttest" ; then
+ shift
+ oldesttest=1
+ fi
+ local root="$1" ; shift
+ local nvim_prg="$1" ; shift
+ local test_name="$1" ; shift
+
+ local tlog="$test_name.tlog"
+
+ export NVIM_TEST_ARGC=$#
+ local arg
+ local i=0
+ for arg ; do
+ eval "export NVIM_TEST_ARG$i=\"\$arg\""
+ i=$(( i+1 ))
+ done
+
+ export CI_DIR="$root/ci"
+ export BUILD_DIR="$(dirname "$nvim_prg")/.."
+ export FAILED=0
+
+ . "$CI_DIR/common/suite.sh"
+ . "$CI_DIR/common/test.sh"
+
+ export VIMRUNTIME="$root/runtime"
+ if ! "$nvim_prg" \
+ -u NONE -i NONE \
+ --headless \
+ --cmd 'set shortmess+=I noswapfile noundofile nomore' \
+ -S runnvim.vim \
+ "$tlog" > "out-$tlog" 2> "err-$tlog"
+ then
+ fail "$test_name" F "Nvim exited with non-zero code"
+ fi
+ echo "Stdout of :terminal runner" >> "$tlog"
+ echo "$separator" >> "$tlog"
+ cat "out-$tlog" >> "$tlog"
+ echo "$separator" >> "$tlog"
+ echo "Stderr of :terminal runner" >> "$tlog"
+ echo "$separator" >> "$tlog"
+ cat "err-$tlog" >> "$tlog"
+ echo "$separator" >> "$tlog"
+ if test "$oldesttest" = 1 ; then
+ if ! diff -q test.out "$test_name.ok" > /dev/null 2>&1 ; then
+ if test -f test.out ; then
+ fail "$test_name" F "Oldest test .out file differs from .ok file"
+ echo "Diff between test.out and $test_name.ok" >> "$tlog"
+ echo "$separator" >> "$tlog"
+ diff -a test.out "$test_name.ok" >> "$tlog"
+ echo "$separator" >> "$tlog"
+ else
+ echo "No output in test.out" >> "$tlog"
+ fi
+ fi
+ fi
+ if test "$FAILED" = 1 ; then
+ travis_fold start "$NVIM_TEST_CURRENT_SUITE/$test_name"
+ fi
+ valgrind_check .
+ if test -n "$LOG_DIR" ; then
+ asan_check "$LOG_DIR"
+ fi
+ check_core_dumps
+ if test "$FAILED" = 1 ; then
+ cat "$tlog"
+ fi
+ rm -f "$tlog"
+ if test "$FAILED" = 1 ; then
+ travis_fold end "$NVIM_TEST_CURRENT_SUITE/$test_name"
+ fi
+ if test "$FAILED" = 1 ; then
+ echo "Test $test_name failed, see output above and summary for more details" >> test.log
+ fi
+)}
+
+main "$@"
diff --git a/src/nvim/testdir/runnvim.vim b/src/nvim/testdir/runnvim.vim
new file mode 100644
index 0000000000..396a3a6477
--- /dev/null
+++ b/src/nvim/testdir/runnvim.vim
@@ -0,0 +1,55 @@
+let s:logger = {'d_events': []}
+function s:logger.on_stdout(id, data, event)
+ call add(self.d_events, [a:event, a:data])
+endfunction
+let s:logger.on_stderr = s:logger.on_stdout
+function s:logger.on_exit(id, data, event)
+ call add(self.d_events, [a:event, ['']])
+endfunction
+
+function Main()
+ let argc = +$NVIM_TEST_ARGC
+ let args = []
+ for i in range(argc)
+ call add(args, eval("$NVIM_TEST_ARG" . i))
+ endfor
+ set lines=25
+ set columns=80
+ enew
+ let job = termopen(args, s:logger)
+ let results = jobwait([job], 5 * 60 * 1000)
+ " TODO(ZyX-I): Get colors
+ let screen = getline(1, '$')
+ bwipeout!
+ let stringified_events = map(s:logger.d_events,
+ \'v:val[0] . ": " . ' .
+ \'join(map(v:val[1], '.
+ \ '''substitute(v:val, '.
+ \ '"\\v\\C(\\p@!.|\\<)", '.
+ \ '"\\=printf(\"<%x>\", '.
+ \ 'char2nr(submatch(0)))", '.
+ \ '"")''), '.
+ \ '''\n'')')
+ call setline(1, [
+ \ 'Job exited with code ' . results[0],
+ \ printf('Screen (%u lines)', len(screen)),
+ \ repeat('=', 80),
+ \] + screen + [
+ \ repeat('=', 80),
+ \ printf('Events (%u lines):', len(stringified_events)),
+ \ repeat('=', 80),
+ \] + stringified_events + [
+ \ repeat('=', 80),
+ \])
+ write
+ if results[0] != 0
+ if results[0] != -1
+ call jobstop(job)
+ endif
+ cquit
+ else
+ qall
+ endif
+endfunction
+
+call Main()
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 117ba52eb6..7090be7726 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -4,7 +4,7 @@
"
" To execute only specific test functions, add a second argument. It will be
" matched against the names of the Test_ function. E.g.:
-" ../vim -u NONE -S runtest.vim test_channel.vim open_delay
+" ../vim -u NONE -S runtest.vim test_channel.vim open_delay
" The output can be found in the "messages" file.
"
" The test script may contain anything, only functions that start with
@@ -42,10 +42,11 @@ endif
" Common with all tests on all systems.
source setup.vim
+" For consistency run all tests with 'nocompatible' set.
" This also enables use of line continuation.
-set viminfo+=nviminfo
+set nocp viminfo+=nviminfo
-" Use utf-8 or latin1 be default, instead of whatever the system default
+" Use utf-8 or latin1 by default, instead of whatever the system default
" happens to be. Individual tests can overrule this at the top of the file.
if has('multi_byte')
set encoding=utf-8
@@ -62,42 +63,84 @@ lang mess C
" Always use forward slashes.
set shellslash
-" Make sure $HOME does not get read or written.
-let $HOME = '/does/not/exist'
-
-" Prepare for calling garbagecollect_for_testing().
+" Prepare for calling test_garbagecollect_now().
let v:testing = 1
-" Align Nvim defaults to Vim.
-set directory^=.
-set backspace=
-set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd
-set listchars=eol:$
-" Prevent Nvim log from writing to stderr.
-let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
+" Support function: get the alloc ID by name.
+function GetAllocId(name)
+ exe 'split ' . s:srcdir . '/alloc.h'
+ let top = search('typedef enum')
+ if top == 0
+ call add(v:errors, 'typedef not found in alloc.h')
+ endif
+ let lnum = search('aid_' . a:name . ',')
+ if lnum == 0
+ call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
+ endif
+ close
+ return lnum - top - 1
+endfunc
func RunTheTest(test)
echo 'Executing ' . a:test
+
+ " Avoid stopping at the "hit enter" prompt
+ set nomore
+
+ " Avoid a three second wait when a message is about to be overwritten by the
+ " mode message.
+ set noshowmode
+
+ " Some tests wipe out buffers. To be consistent, always wipe out all
+ " buffers.
+ %bwipe!
+
+ " The test may change the current directory. Save and restore the
+ " directory after executing the test.
+ let save_cwd = getcwd()
+
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)
let s:done += 1
- try
+
+ if a:test =~ 'Test_nocatch_'
+ " Function handles errors itself. This avoids skipping commands after the
+ " error.
exe 'call ' . a:test
- catch /^\cskipped/
- call add(s:messages, ' Skipped')
- call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
- catch
- call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
- endtry
+ else
+ try
+ exe 'call ' . a:test
+ catch /^\cskipped/
+ call add(s:messages, ' Skipped')
+ call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
+ catch
+ call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
+ endif
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.
+ " Clear any autocommands
+ au!
+
+ " Close any extra tab pages and windows and make the current one not modified.
+ while tabpagenr('$') > 1
+ quit!
+ endwhile
+
while 1
let wincount = winnr('$')
if wincount == 1
@@ -110,7 +153,8 @@ func RunTheTest(test)
break
endif
endwhile
- set nomodified
+
+ exe 'cd ' . save_cwd
endfunc
func AfterTheTest()
@@ -189,13 +233,20 @@ endif
" Names of flaky tests.
let s:flaky = [
- \ 'Test_with_partial_callback()',
+ \ 'Test_exit_callback_interval()',
\ 'Test_oneshot()',
+ \ 'Test_out_cb()',
+ \ 'Test_paused()',
+ \ 'Test_quoteplus()',
+ \ 'Test_reltime()',
+ \ 'Test_terminal_composing_unicode()',
+ \ 'Test_terminal_redir_file()',
+ \ 'Test_terminal_tmap()',
+ \ 'Test_with_partial_callback()',
\ 'Test_lambda_with_timer()',
\ ]
" Locate Test_ functions and execute them.
-set nomore
redir @q
silent function /^Test_
redir END
@@ -208,12 +259,30 @@ endif
" Execute the tests in alphabetical order.
for s:test in sort(s:tests)
+ " Silence, please!
+ set belloff=all
+
call RunTheTest(s:test)
if len(v:errors) > 0 && index(s:flaky, s:test) >= 0
+ call add(s:messages, 'Found errors in ' . s:test . ':')
+ call extend(s:messages, v:errors)
call add(s:messages, 'Flaky test failed, running it again')
+ let first_run = v:errors
+
+ " Flakiness is often caused by the system being very busy. Sleep a couple
+ " of seconds to have a higher chance of succeeding the second time.
+ sleep 2
+
let v:errors = []
call RunTheTest(s:test)
+ if len(v:errors) > 0
+ let second_run = v:errors
+ let v:errors = ['First run:']
+ call extend(v:errors, first_run)
+ call add(v:errors, 'Second run:')
+ call extend(v:errors, second_run)
+ endif
endif
call AfterTheTest()
diff --git a/src/nvim/testdir/sautest/autoload/footest.vim b/src/nvim/testdir/sautest/autoload/footest.vim
index f467bc376d..1e78963a10 100644
--- a/src/nvim/testdir/sautest/autoload/footest.vim
+++ b/src/nvim/testdir/sautest/autoload/footest.vim
@@ -1,4 +1,4 @@
-" Autoload script used by test55 and test60
+" Autoload script used by test_listdict.vim, test_exists.vim and test_let.vim
let footest#x = 1
func footest#F()
return 0
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index 06f2199214..7d6dd0c7ce 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -1,8 +1,15 @@
" Common preparations for running tests.
-set noruler
-set noshowcmd
-set belloff=
+" Align Nvim defaults to Vim.
+set sidescroll=0
+set directory^=.
+set backspace=
+set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd
+set listchars=eol:$
+set fillchars=vert:\|,fold:-
+" Prevent Nvim log from writing to stderr.
+let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
+
" Make sure 'runtimepath' and 'packpath' does not include $HOME.
set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index 72cfea96c6..4925b04a82 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -1,5 +1,25 @@
" Functions shared by several tests.
+" {Nvim}
+" Filepath captured from output may be truncated, like this:
+" /home/va...estdir/Xtest-tmpdir/nvimxbXN4i/10
+" Get last 2 segments, then combine with $TMPDIR.
+func! Fix_truncated_tmpfile(fname)
+ " sanity check
+ if $TMPDIR ==# ''
+ throw '$TMPDIR is empty'
+ endif
+ let tmpdir_tail = fnamemodify(substitute($TMPDIR, '[\/]\+$', '', 'g'), ':t')
+ if tmpdir_tail ==# ''
+ throw 'empty tmpdir_tail'
+ endif
+ if a:fname !~# tmpdir_tail
+ throw printf('$TMPDIR (%s) not in fname: %s', tmpdir_tail, a:fname)
+ endif
+ let last2segments = matchstr(a:fname, '[\/][^\/]\+[\/][^\/]\+$')
+ return $TMPDIR.last2segments
+endfunc
+
" Get the name of the Python executable.
" Also keeps it in s:python.
func PythonProg()
diff --git a/src/nvim/testdir/test32.in b/src/nvim/testdir/test32.in
deleted file mode 100644
index 76bd9be889..0000000000
--- a/src/nvim/testdir/test32.in
+++ /dev/null
@@ -1,60 +0,0 @@
-Test for insert expansion
-
-:se cpt=.,w
-* add-expands (word from next line) from other window
-* add-expands (current buffer first)
-* Local expansion, ends in an empty line (unless it becomes a global expansion)
-* starts Local and switches to global add-expansion
-:se cpt=.,w,i
-* i-add-expands and switches to local
-* add-expands lines (it would end in an empty line if it didn't ignored it self)
-:se cpt=kXtestfile
-* checks k-expansion, and file expansion (use Xtest11 instead of test11,
-* because TEST11.OUT may match first on DOS)
-:se cpt=w
-* checks make_cyclic in other window
-:se cpt=u nohid
-* checks unloaded buffer expansion
-* checks adding mode abortion
-:se cpt=t,d
-* tag expansion, define add-expansion interrupted
-* t-expansion
-
-STARTTEST
-:se backspace=""
-:se cpt=.,w ff=unix | $-2,$w!Xtestfile | set ff&
-:se cot=
-nO#include "Xtestfile"
-ru
-O
-
-
-:se cpt=.,w,i
-kOM
-  
-:se cpt=kXtestfile
-:w Xtest11.one
-:w Xtest11.two
-OIXA
-:" use CTRL-X CTRL-F to complete Xtest11.one, remove it and then use
-:" CTRL-X CTRL-F again to verify this doesn't cause trouble.
-OXddk
-:se cpt=w
-OST
-:se cpt=u nohid
-oOEN
-unl
-:se cpt=t,d def=^\\k* tags=Xtestfile notagbsearch
-O
-a
-:wq! test.out
-ENDTEST
-
-start of testfile
-run1
-run2
-end of testfile
-
-test11 36Gepeto /Tag/
-asd test11file 36G
-Makefile to run
diff --git a/src/nvim/testdir/test32.ok b/src/nvim/testdir/test32.ok
deleted file mode 100644
index afc4463fac..0000000000
--- a/src/nvim/testdir/test32.ok
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "Xtestfile"
-run1 run3
-run3 run3
-
-Makefile to run3
-Makefile to run3
-Makefile to run3
-Xtest11.two
-STARTTEST
-ENDTEST
-unless
-test11file 36Gepeto /Tag/ asd
-asd
-run1 run2
-
diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in
deleted file mode 100644
index 20b5d019af..0000000000
--- a/src/nvim/testdir/test53.in
+++ /dev/null
@@ -1,74 +0,0 @@
-Tests for string and html text objects. vim: set ft=vim :
-
-Note that the end-of-line moves the cursor to the next test line.
-
-Also test match() and matchstr()
-
-STARTTEST
-/^start:/
-da"
-0va'a'rx
-02f`da`
-0fXdi"
-03f'vi'ry
-:set quoteescape=+*-
-di`
-$F"va"oha"i"rz
-:"
-/^<begin
-jfXdit
-0fXdit
-fXdat
-0fXdat
-dit
-:"
-:put =matchstr(\"abcd\", \".\", 0, 2) " b
-:put =matchstr(\"abcd\", \"..\", 0, 2) " bc
-:put =matchstr(\"abcd\", \".\", 2, 0) " c (zero and negative -> first match)
-:put =matchstr(\"abcd\", \".\", 0, -1) " a
-:put =match(\"abcd\", \".\", 0, 5) " -1
-:put =match(\"abcd\", \".\", 0, -1) " 0
-:put =match('abc', '.', 0, 1) " 0
-:put =match('abc', '.', 0, 2) " 1
-:put =match('abc', '.', 0, 3) " 2
-:put =match('abc', '.', 0, 4) " -1
-:put =match('abc', '.', 1, 1) " 1
-:put =match('abc', '.', 2, 1) " 2
-:put =match('abc', '.', 3, 1) " -1
-:put =match('abc', '$', 0, 1) " 3
-:put =match('abc', '$', 0, 2) " -1
-:put =match('abc', '$', 1, 1) " 3
-:put =match('abc', '$', 2, 1) " 3
-:put =match('abc', '$', 3, 1) " 3
-:put =match('abc', '$', 4, 1) " -1
-:put =match('abc', '\zs', 0, 1) " 0
-:put =match('abc', '\zs', 0, 2) " 1
-:put =match('abc', '\zs', 0, 3) " 2
-:put =match('abc', '\zs', 0, 4) " 3
-:put =match('abc', '\zs', 0, 5) " -1
-:put =match('abc', '\zs', 1, 1) " 1
-:put =match('abc', '\zs', 2, 1) " 2
-:put =match('abc', '\zs', 3, 1) " 3
-:put =match('abc', '\zs', 4, 1) " -1
-:/^start:/,/^end:/wq! test.out
-ENDTEST
-
-start: "wo\"rd\\" foo
-'foo' 'bar' 'piep'
-bla bla `quote` blah
-out " in "noXno"
-"'" 'blah' rep 'buh'
-bla `s*`d-`+++`l**` b`la
-voo "nah" sdf " asdf" sdf " sdf" sd
-
-<begin>
--<b>asdf<i>Xasdf</i>asdf</b>-
--<b>asdX<i>a<i />sdf</i>asdf</b>-
--<b>asdf<i>Xasdf</i>asdf</b>-
--<b>asdX<i>as<b />df</i>asdf</b>-
--<b>
-innertext object
-</b>
-</begin>
-SEARCH:
-end:
diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok
deleted file mode 100644
index d57d86bbb0..0000000000
--- a/src/nvim/testdir/test53.ok
+++ /dev/null
@@ -1,45 +0,0 @@
-start: foo
-xxxxxxxxxxxx'piep'
-bla bla blah
-out " in ""
-"'" 'blah'yyyyy'buh'
-bla `` b`la
-voo "zzzzzzzzzzzzzzzzzzzzzzzzzzzzsd
-
-<begin>
--<b>asdf<i></i>asdf</b>-
--<b></b>-
--<b>asdfasdf</b>-
---
--<b></b>
-</begin>
-b
-bc
-c
-a
--1
-0
-0
-1
-2
--1
-1
-2
--1
-3
--1
-3
-3
-3
--1
-0
-1
-2
-3
--1
-1
-2
-3
--1
-SEARCH:
-end:
diff --git a/src/nvim/testdir/test73.in b/src/nvim/testdir/test73.in
deleted file mode 100644
index 9d50f7a789..0000000000
--- a/src/nvim/testdir/test73.in
+++ /dev/null
@@ -1,168 +0,0 @@
-Tests for find completion.
-
-STARTTEST
-:set wildmode=full
-:" Do all test in a separate window to avoid E211 when we recursively
-:" delete the Xfind directory during cleanup
-:"
-:" This will cause a few errors, do it silently.
-:set visualbell
-:"
-:" On windows a stale "Xfind" directory may exist, remove it so that
-:" we start from a clean state.
-:call delete("Xfind", "rf")
-:new
-:let cwd=getcwd()
-:let test_out = cwd . '/test.out'
-:call mkdir('Xfind')
-:cd Xfind
-:set path=
-:find
-:exec "w! " . test_out
-:close
-:new
-:set path=.
-:find
-:exec "w >>" . test_out
-:close
-:new
-:set path=.,,
-:find
-:exec "w >>" . test_out
-:close
-:new
-:set path=./**
-:find
-:exec "w >>" . test_out
-:close
-:new
-:" We shouldn't find any file at this point, test.out must be empty.
-:call mkdir('in')
-:cd in
-:call mkdir('path')
-:exec "cd " . cwd
-:e Xfind/file.txt
-SHoly Grail:w
-:e Xfind/in/file.txt
-SJimmy Hoffa:w
-:e Xfind/in/stuff.txt
-SAnother Holy Grail:w
-:e Xfind/in/path/file.txt
-SE.T.:w
-:set path=Xfind/**
-:find file
-:exec "w >>" . test_out
-:find file
-:exec "w >>" . test_out
-:find file
-:exec "w >>" . test_out
-:" Rerun the previous three find completions, using fullpath in 'path'
-:exec "set path=" . cwd . "/Xfind/**"
-:find file
-:exec "w >>" . test_out
-:find file
-:exec "w >>" . test_out
-:find file
-:exec "w >>" . test_out
-:" Same steps again, using relative and fullpath items that point to the same
-:" recursive location.
-:" This is to test that there are no duplicates in the completion list.
-:exec "set path+=Xfind/**"
-:find file
-:exec "w >>" . test_out
-:find file
-:exec "w >>" . test_out
-:find file
-:exec "w >>" . test_out
-:find file
-:" Test find completion for directory of current buffer, which at this point
-:" is Xfind/in/file.txt.
-:set path=.
-:find st
-:exec "w >>" . test_out
-:" Test find completion for empty path item ",," which is the current directory
-:cd Xfind
-:set path=,,
-:find f
-:exec "w >>" . test_out
-:" Test shortening of
-:"
-:" foo/x/bar/voyager.txt
-:" foo/y/bar/voyager.txt
-:"
-:" When current directory is above foo/ they should be shortened to (in order
-:" of appearance):
-:"
-:" x/bar/voyager.txt
-:" y/bar/voyager.txt
-:call mkdir('foo')
-:cd foo
-:call mkdir('x')
-:call mkdir('y')
-:cd x
-:call mkdir('bar')
-:cd ..
-:cd y
-:call mkdir('bar')
-:cd ..
-:cd ..
-:" We should now be in the Xfind directory
-:e foo/x/bar/voyager.txt
-SVoyager 1:w
-:e foo/y/bar/voyager.txt
-SVoyager 2:w
-:exec "set path=" . cwd . "/Xfind/**"
-:find voyager
-:exec "w >>" . test_out
-:find voyager
-:exec "w >>" . test_out
-:"
-:" When current directory is .../foo/y/bar they should be shortened to (in
-:" order of appearance):
-:"
-:" ./voyager.txt
-:" x/bar/voyager.txt
-:cd foo
-:cd y
-:cd bar
-:find voyager
-:exec "w >> " . test_out
-:find voyager
-:exec "w >> " . test_out
-:" Check the opposite too:
-:cd ..
-:cd ..
-:cd x
-:cd bar
-:find voyager
-:exec "w >> " . test_out
-:find voyager
-:exec "w >> " . test_out
-:" Check for correct handling of shorten_fname()'s behavior on windows
-:exec "cd " . cwd . "/Xfind/in"
-:find file
-:exec "w >>" . test_out
-:" Test for relative to current buffer 'path' item
-:exec "cd " . cwd . "/Xfind/"
-:set path=./path
-:" Open the file where Jimmy Hoffa is found
-:e in/file.txt
-:" Find the file containing 'E.T.' in the Xfind/in/path directory
-:find file
-:exec "w >>" . test_out
-:"
-:" Test that completion works when path=.,,
-:"
-:set path=.,,
-:" Open Jimmy Hoffa file
-:e in/file.txt
-:exec "w >>" . test_out
-:" Search for the file containing Holy Grail in same directory as in/path.txt
-:find stu
-:exec "w >>" . test_out
-:q
-:exec "cd " . cwd
-:call delete("Xfind", "rf")
-:qa!
-ENDTEST
-
diff --git a/src/nvim/testdir/test73.ok b/src/nvim/testdir/test73.ok
deleted file mode 100644
index 90efab756f..0000000000
--- a/src/nvim/testdir/test73.ok
+++ /dev/null
@@ -1,21 +0,0 @@
-Holy Grail
-Jimmy Hoffa
-E.T.
-Holy Grail
-Jimmy Hoffa
-E.T.
-Holy Grail
-Jimmy Hoffa
-E.T.
-Another Holy Grail
-Holy Grail
-Voyager 1
-Voyager 2
-Voyager 2
-Voyager 1
-Voyager 1
-Voyager 2
-Jimmy Hoffa
-E.T.
-Jimmy Hoffa
-Another Holy Grail
diff --git a/src/nvim/testdir/test79.in b/src/nvim/testdir/test79.in
deleted file mode 100644
index afbf2083d2..0000000000
--- a/src/nvim/testdir/test79.in
+++ /dev/null
Binary files differ
diff --git a/src/nvim/testdir/test79.ok b/src/nvim/testdir/test79.ok
deleted file mode 100644
index d4e0ae8819..0000000000
--- a/src/nvim/testdir/test79.ok
+++ /dev/null
Binary files differ
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 1103778107..b4baf7a8d7 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -2,27 +2,33 @@
" This makes testing go faster, since Vim doesn't need to restart.
source test_assign.vim
+source test_changedtick.vim
source test_cursor_func.vim
-source test_execute_func.vim
source test_ex_undo.vim
+source test_execute_func.vim
source test_expr.vim
-source test_expr_utf8.vim
source test_feedkeys.vim
source test_filter_cmd.vim
source test_filter_map.vim
+source test_findfile.vim
source test_float_func.vim
source test_functions.vim
+source test_ga.vim
source test_goto.vim
source test_jumps.vim
+source test_fileformat.vim
+source test_filetype.vim
source test_lambda.vim
source test_menu.vim
source test_mapping.vim
source test_messages.vim
-source test_options.vim
source test_partial.vim
source test_popup.vim
+source test_put.vim
+source test_recover.vim
source test_regexp_utf8.vim
source test_source_utf8.vim
+source test_sha256.vim
source test_statusline.vim
source test_syn_attr.vim
source test_tabline.vim
@@ -33,4 +39,5 @@ source test_taglist.vim
source test_true_false.vim
source test_unlet.vim
source test_utf8.vim
+source test_virtualedit.vim
source test_window_cmd.vim
diff --git a/src/nvim/testdir/test_alot_latin.vim b/src/nvim/testdir/test_alot_latin.vim
new file mode 100644
index 0000000000..ebb3bde4ce
--- /dev/null
+++ b/src/nvim/testdir/test_alot_latin.vim
@@ -0,0 +1,10 @@
+" A series of tests that can run in one Vim invocation.
+" This makes testing go faster, since Vim doesn't need to restart.
+
+" These tests use latin1 'encoding'. Setting 'encoding' is in the individual
+" files, so that they can be run by themselves.
+
+" Nvim does not allow setting 'encoding', so skip this test group.
+finish
+
+source test_regexp_latin.vim
diff --git a/src/nvim/testdir/test_alot_utf8.vim b/src/nvim/testdir/test_alot_utf8.vim
new file mode 100644
index 0000000000..648d806a94
--- /dev/null
+++ b/src/nvim/testdir/test_alot_utf8.vim
@@ -0,0 +1,17 @@
+" A series of tests that can run in one Vim invocation.
+" This makes testing go faster, since Vim doesn't need to restart.
+
+" These tests use utf8 'encoding'. Setting 'encoding' is already done in
+" runtest.vim. Checking for the multi_byte feature is in the individual
+" files, so that they can be run by themselves.
+
+source test_charsearch_utf8.vim
+source test_expr_utf8.vim
+source test_listlbr_utf8.vim
+source test_matchadd_conceal_utf8.vim
+source test_mksession_utf8.vim
+source test_regexp_utf8.vim
+source test_source_utf8.vim
+source test_startup_utf8.vim
+source test_utf8.vim
+source test_utf8_comparisons.vim
diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim
new file mode 100644
index 0000000000..9a16833a4c
--- /dev/null
+++ b/src/nvim/testdir/test_arabic.vim
@@ -0,0 +1,472 @@
+" Simplistic testing of Arabic mode.
+
+if !has('arabic') || !has('multi_byte')
+ finish
+endif
+
+source view_util.vim
+
+" Return list of Unicode characters at line lnum.
+" Combining characters are treated as a single item.
+func s:get_chars(lnum)
+ call cursor(a:lnum, 1)
+ let chars = []
+ let numchars = strchars(getline('.'), 1)
+ for i in range(1, numchars)
+ exe 'norm ' i . '|'
+ let c=execute('ascii')
+ let c=substitute(c, '\n\?<.\{-}Hex\s*', 'U+', 'g')
+ let c=substitute(c, ',\s*Octal\s*\d*', '', 'g')
+ call add(chars, c)
+ endfor
+ return chars
+endfunc
+
+func Test_arabic_toggle()
+ set arabic
+ call assert_equal(1, &rightleft)
+ call assert_equal(1, &arabicshape)
+ call assert_equal('arabic', &keymap)
+ call assert_equal(1, &delcombine)
+
+ set iminsert=1 imsearch=1
+ set arabic&
+ call assert_equal(0, &rightleft)
+ call assert_equal(1, &arabicshape)
+ call assert_equal('arabic', &keymap)
+ call assert_equal(1, &delcombine)
+ call assert_equal(0, &iminsert)
+ call assert_equal(-1, &imsearch)
+
+ set arabicshape& keymap= delcombine&
+endfunc
+
+func Test_arabic_input()
+ new
+ set arabic
+ " Typing sghl in Arabic insert mode should show the
+ " Arabic word 'Salaam' i.e. 'peace', spelled:
+ " SEEN, LAM, ALEF, MEEM.
+ " See: https://www.mediawiki.org/wiki/VisualEditor/Typing/Right-to-left
+ call feedkeys('isghl!', 'tx')
+ call assert_match("^ *!\uFEE1\uFEFC\uFEB3$", ScreenLines(1, &columns)[0])
+ call assert_equal([
+ \ 'U+0633',
+ \ 'U+0644 U+0627',
+ \ 'U+0645',
+ \ 'U+21'], s:get_chars(1))
+
+ " Without shaping, it should give individual Arabic letters.
+ set noarabicshape
+ call assert_match("^ *!\u0645\u0627\u0644\u0633$", ScreenLines(1, &columns)[0])
+ call assert_equal([
+ \ 'U+0633',
+ \ 'U+0644',
+ \ 'U+0627',
+ \ 'U+0645',
+ \ 'U+21'], s:get_chars(1))
+
+ set arabic& arabicshape&
+ bwipe!
+endfunc
+
+func Test_arabic_toggle_keymap()
+ new
+ set arabic
+ call feedkeys("i12\<C-^>12\<C-^>12", 'tx')
+ call assert_match("^ *٢١21٢١$", ScreenLines(1, &columns)[0])
+ call assert_equal('١٢12١٢', getline('.'))
+ set arabic&
+ bwipe!
+endfunc
+
+func Test_delcombine()
+ new
+ set arabic
+ call feedkeys("isghl\<BS>\<BS>", 'tx')
+ call assert_match("^ *\uFEDE\uFEB3$", ScreenLines(1, &columns)[0])
+ call assert_equal(['U+0633', 'U+0644'], s:get_chars(1))
+
+ " Now the same with 'nodelcombine'
+ set nodelcombine
+ %d
+ call feedkeys("isghl\<BS>\<BS>", 'tx')
+ call assert_match("^ *\uFEB1$", ScreenLines(1, &columns)[0])
+ call assert_equal(['U+0633'], s:get_chars(1))
+ set arabic&
+ bwipe!
+endfunc
+
+" Values from src/arabic.h (not all used yet)
+let s:a_COMMA = "\u060C"
+let s:a_SEMICOLON = "\u061B"
+let s:a_QUESTION = "\u061F"
+let s:a_HAMZA = "\u0621"
+let s:a_ALEF_MADDA = "\u0622"
+let s:a_ALEF_HAMZA_ABOVE = "\u0623"
+let s:a_WAW_HAMZA = "\u0624"
+let s:a_ALEF_HAMZA_BELOW = "\u0625"
+let s:a_YEH_HAMZA = "\u0626"
+let s:a_ALEF = "\u0627"
+let s:a_BEH = "\u0628"
+let s:a_TEH_MARBUTA = "\u0629"
+let s:a_TEH = "\u062a"
+let s:a_THEH = "\u062b"
+let s:a_JEEM = "\u062c"
+let s:a_HAH = "\u062d"
+let s:a_KHAH = "\u062e"
+let s:a_DAL = "\u062f"
+let s:a_THAL = "\u0630"
+let s:a_REH = "\u0631"
+let s:a_ZAIN = "\u0632"
+let s:a_SEEN = "\u0633"
+let s:a_SHEEN = "\u0634"
+let s:a_SAD = "\u0635"
+let s:a_DAD = "\u0636"
+let s:a_TAH = "\u0637"
+let s:a_ZAH = "\u0638"
+let s:a_AIN = "\u0639"
+let s:a_GHAIN = "\u063a"
+let s:a_TATWEEL = "\u0640"
+let s:a_FEH = "\u0641"
+let s:a_QAF = "\u0642"
+let s:a_KAF = "\u0643"
+let s:a_LAM = "\u0644"
+let s:a_MEEM = "\u0645"
+let s:a_NOON = "\u0646"
+let s:a_HEH = "\u0647"
+let s:a_WAW = "\u0648"
+let s:a_ALEF_MAKSURA = "\u0649"
+let s:a_YEH = "\u064a"
+
+let s:a_FATHATAN = "\u064b"
+let s:a_DAMMATAN = "\u064c"
+let s:a_KASRATAN = "\u064d"
+let s:a_FATHA = "\u064e"
+let s:a_DAMMA = "\u064f"
+let s:a_KASRA = "\u0650"
+let s:a_SHADDA = "\u0651"
+let s:a_SUKUN = "\u0652"
+
+let s:a_MADDA_ABOVE = "\u0653"
+let s:a_HAMZA_ABOVE = "\u0654"
+let s:a_HAMZA_BELOW = "\u0655"
+
+let s:a_ZERO = "\u0660"
+let s:a_ONE = "\u0661"
+let s:a_TWO = "\u0662"
+let s:a_THREE = "\u0663"
+let s:a_FOUR = "\u0664"
+let s:a_FIVE = "\u0665"
+let s:a_SIX = "\u0666"
+let s:a_SEVEN = "\u0667"
+let s:a_EIGHT = "\u0668"
+let s:a_NINE = "\u0669"
+let s:a_PERCENT = "\u066a"
+let s:a_DECIMAL = "\u066b"
+let s:a_THOUSANDS = "\u066c"
+let s:a_STAR = "\u066d"
+let s:a_MINI_ALEF = "\u0670"
+
+let s:a_s_FATHATAN = "\ufe70"
+let s:a_m_TATWEEL_FATHATAN = "\ufe71"
+let s:a_s_DAMMATAN = "\ufe72"
+
+let s:a_s_KASRATAN = "\ufe74"
+
+let s:a_s_FATHA = "\ufe76"
+let s:a_m_FATHA = "\ufe77"
+let s:a_s_DAMMA = "\ufe78"
+let s:a_m_DAMMA = "\ufe79"
+let s:a_s_KASRA = "\ufe7a"
+let s:a_m_KASRA = "\ufe7b"
+let s:a_s_SHADDA = "\ufe7c"
+let s:a_m_SHADDA = "\ufe7d"
+let s:a_s_SUKUN = "\ufe7e"
+let s:a_m_SUKUN = "\ufe7f"
+
+let s:a_s_HAMZA = "\ufe80"
+let s:a_s_ALEF_MADDA = "\ufe81"
+let s:a_f_ALEF_MADDA = "\ufe82"
+let s:a_s_ALEF_HAMZA_ABOVE = "\ufe83"
+let s:a_f_ALEF_HAMZA_ABOVE = "\ufe84"
+let s:a_s_WAW_HAMZA = "\ufe85"
+let s:a_f_WAW_HAMZA = "\ufe86"
+let s:a_s_ALEF_HAMZA_BELOW = "\ufe87"
+let s:a_f_ALEF_HAMZA_BELOW = "\ufe88"
+let s:a_s_YEH_HAMZA = "\ufe89"
+let s:a_f_YEH_HAMZA = "\ufe8a"
+let s:a_i_YEH_HAMZA = "\ufe8b"
+let s:a_m_YEH_HAMZA = "\ufe8c"
+let s:a_s_ALEF = "\ufe8d"
+let s:a_f_ALEF = "\ufe8e"
+let s:a_s_BEH = "\ufe8f"
+let s:a_f_BEH = "\ufe90"
+let s:a_i_BEH = "\ufe91"
+let s:a_m_BEH = "\ufe92"
+let s:a_s_TEH_MARBUTA = "\ufe93"
+let s:a_f_TEH_MARBUTA = "\ufe94"
+let s:a_s_TEH = "\ufe95"
+let s:a_f_TEH = "\ufe96"
+let s:a_i_TEH = "\ufe97"
+let s:a_m_TEH = "\ufe98"
+let s:a_s_THEH = "\ufe99"
+let s:a_f_THEH = "\ufe9a"
+let s:a_i_THEH = "\ufe9b"
+let s:a_m_THEH = "\ufe9c"
+let s:a_s_JEEM = "\ufe9d"
+let s:a_f_JEEM = "\ufe9e"
+let s:a_i_JEEM = "\ufe9f"
+let s:a_m_JEEM = "\ufea0"
+let s:a_s_HAH = "\ufea1"
+let s:a_f_HAH = "\ufea2"
+let s:a_i_HAH = "\ufea3"
+let s:a_m_HAH = "\ufea4"
+let s:a_s_KHAH = "\ufea5"
+let s:a_f_KHAH = "\ufea6"
+let s:a_i_KHAH = "\ufea7"
+let s:a_m_KHAH = "\ufea8"
+let s:a_s_DAL = "\ufea9"
+let s:a_f_DAL = "\ufeaa"
+let s:a_s_THAL = "\ufeab"
+let s:a_f_THAL = "\ufeac"
+let s:a_s_REH = "\ufead"
+let s:a_f_REH = "\ufeae"
+let s:a_s_ZAIN = "\ufeaf"
+let s:a_f_ZAIN = "\ufeb0"
+let s:a_s_SEEN = "\ufeb1"
+let s:a_f_SEEN = "\ufeb2"
+let s:a_i_SEEN = "\ufeb3"
+let s:a_m_SEEN = "\ufeb4"
+let s:a_s_SHEEN = "\ufeb5"
+let s:a_f_SHEEN = "\ufeb6"
+let s:a_i_SHEEN = "\ufeb7"
+let s:a_m_SHEEN = "\ufeb8"
+let s:a_s_SAD = "\ufeb9"
+let s:a_f_SAD = "\ufeba"
+let s:a_i_SAD = "\ufebb"
+let s:a_m_SAD = "\ufebc"
+let s:a_s_DAD = "\ufebd"
+let s:a_f_DAD = "\ufebe"
+let s:a_i_DAD = "\ufebf"
+let s:a_m_DAD = "\ufec0"
+let s:a_s_TAH = "\ufec1"
+let s:a_f_TAH = "\ufec2"
+let s:a_i_TAH = "\ufec3"
+let s:a_m_TAH = "\ufec4"
+let s:a_s_ZAH = "\ufec5"
+let s:a_f_ZAH = "\ufec6"
+let s:a_i_ZAH = "\ufec7"
+let s:a_m_ZAH = "\ufec8"
+let s:a_s_AIN = "\ufec9"
+let s:a_f_AIN = "\ufeca"
+let s:a_i_AIN = "\ufecb"
+let s:a_m_AIN = "\ufecc"
+let s:a_s_GHAIN = "\ufecd"
+let s:a_f_GHAIN = "\ufece"
+let s:a_i_GHAIN = "\ufecf"
+let s:a_m_GHAIN = "\ufed0"
+let s:a_s_FEH = "\ufed1"
+let s:a_f_FEH = "\ufed2"
+let s:a_i_FEH = "\ufed3"
+let s:a_m_FEH = "\ufed4"
+let s:a_s_QAF = "\ufed5"
+let s:a_f_QAF = "\ufed6"
+let s:a_i_QAF = "\ufed7"
+let s:a_m_QAF = "\ufed8"
+let s:a_s_KAF = "\ufed9"
+let s:a_f_KAF = "\ufeda"
+let s:a_i_KAF = "\ufedb"
+let s:a_m_KAF = "\ufedc"
+let s:a_s_LAM = "\ufedd"
+let s:a_f_LAM = "\ufede"
+let s:a_i_LAM = "\ufedf"
+let s:a_m_LAM = "\ufee0"
+let s:a_s_MEEM = "\ufee1"
+let s:a_f_MEEM = "\ufee2"
+let s:a_i_MEEM = "\ufee3"
+let s:a_m_MEEM = "\ufee4"
+let s:a_s_NOON = "\ufee5"
+let s:a_f_NOON = "\ufee6"
+let s:a_i_NOON = "\ufee7"
+let s:a_m_NOON = "\ufee8"
+let s:a_s_HEH = "\ufee9"
+let s:a_f_HEH = "\ufeea"
+let s:a_i_HEH = "\ufeeb"
+let s:a_m_HEH = "\ufeec"
+let s:a_s_WAW = "\ufeed"
+let s:a_f_WAW = "\ufeee"
+let s:a_s_ALEF_MAKSURA = "\ufeef"
+let s:a_f_ALEF_MAKSURA = "\ufef0"
+let s:a_s_YEH = "\ufef1"
+let s:a_f_YEH = "\ufef2"
+let s:a_i_YEH = "\ufef3"
+let s:a_m_YEH = "\ufef4"
+let s:a_s_LAM_ALEF_MADDA_ABOVE = "\ufef5"
+let s:a_f_LAM_ALEF_MADDA_ABOVE = "\ufef6"
+let s:a_s_LAM_ALEF_HAMZA_ABOVE = "\ufef7"
+let s:a_f_LAM_ALEF_HAMZA_ABOVE = "\ufef8"
+let s:a_s_LAM_ALEF_HAMZA_BELOW = "\ufef9"
+let s:a_f_LAM_ALEF_HAMZA_BELOW = "\ufefa"
+let s:a_s_LAM_ALEF = "\ufefb"
+let s:a_f_LAM_ALEF = "\ufefc"
+
+let s:a_BYTE_ORDER_MARK = "\ufeff"
+
+func Test_shape_initial()
+ new
+ set arabicshape
+
+ " Shaping arabic {testchar} non-arabic Tests chg_c_a2i().
+ " pair[0] = testchar, pair[1] = next-result, pair[2] = current-result
+ for pair in [[s:a_YEH_HAMZA, s:a_f_GHAIN, s:a_i_YEH_HAMZA],
+ \ [s:a_HAMZA, s:a_s_GHAIN, s:a_s_HAMZA],
+ \ [s:a_ALEF_MADDA, s:a_s_GHAIN, s:a_s_ALEF_MADDA],
+ \ [s:a_ALEF_HAMZA_ABOVE, s:a_s_GHAIN, s:a_s_ALEF_HAMZA_ABOVE],
+ \ [s:a_WAW_HAMZA, s:a_s_GHAIN, s:a_s_WAW_HAMZA],
+ \ [s:a_ALEF_HAMZA_BELOW, s:a_s_GHAIN, s:a_s_ALEF_HAMZA_BELOW],
+ \ [s:a_ALEF, s:a_s_GHAIN, s:a_s_ALEF],
+ \ [s:a_TEH_MARBUTA, s:a_s_GHAIN, s:a_s_TEH_MARBUTA],
+ \ [s:a_DAL, s:a_s_GHAIN, s:a_s_DAL],
+ \ [s:a_THAL, s:a_s_GHAIN, s:a_s_THAL],
+ \ [s:a_REH, s:a_s_GHAIN, s:a_s_REH],
+ \ [s:a_ZAIN, s:a_s_GHAIN, s:a_s_ZAIN],
+ \ [s:a_TATWEEL, s:a_f_GHAIN, s:a_TATWEEL],
+ \ [s:a_WAW, s:a_s_GHAIN, s:a_s_WAW],
+ \ [s:a_ALEF_MAKSURA, s:a_s_GHAIN, s:a_s_ALEF_MAKSURA],
+ \ [s:a_BEH, s:a_f_GHAIN, s:a_i_BEH],
+ \ [s:a_TEH, s:a_f_GHAIN, s:a_i_TEH],
+ \ [s:a_THEH, s:a_f_GHAIN, s:a_i_THEH],
+ \ [s:a_JEEM, s:a_f_GHAIN, s:a_i_JEEM],
+ \ [s:a_HAH, s:a_f_GHAIN, s:a_i_HAH],
+ \ [s:a_KHAH, s:a_f_GHAIN, s:a_i_KHAH],
+ \ [s:a_SEEN, s:a_f_GHAIN, s:a_i_SEEN],
+ \ [s:a_SHEEN, s:a_f_GHAIN, s:a_i_SHEEN],
+ \ [s:a_SAD, s:a_f_GHAIN, s:a_i_SAD],
+ \ [s:a_DAD, s:a_f_GHAIN, s:a_i_DAD],
+ \ [s:a_TAH, s:a_f_GHAIN, s:a_i_TAH],
+ \ [s:a_ZAH, s:a_f_GHAIN, s:a_i_ZAH],
+ \ [s:a_AIN, s:a_f_GHAIN, s:a_i_AIN],
+ \ [s:a_GHAIN, s:a_f_GHAIN, s:a_i_GHAIN],
+ \ [s:a_FEH, s:a_f_GHAIN, s:a_i_FEH],
+ \ [s:a_QAF, s:a_f_GHAIN, s:a_i_QAF],
+ \ [s:a_KAF, s:a_f_GHAIN, s:a_i_KAF],
+ \ [s:a_LAM, s:a_f_GHAIN, s:a_i_LAM],
+ \ [s:a_MEEM, s:a_f_GHAIN, s:a_i_MEEM],
+ \ [s:a_NOON, s:a_f_GHAIN, s:a_i_NOON],
+ \ [s:a_HEH, s:a_f_GHAIN, s:a_i_HEH],
+ \ [s:a_YEH, s:a_f_GHAIN, s:a_i_YEH],
+ \ ]
+ call setline(1, s:a_GHAIN . pair[0] . ' ')
+ call assert_equal([pair[1] . pair[2] . ' '], ScreenLines(1, 3))
+ endfor
+
+ set arabicshape&
+ bwipe!
+endfunc
+
+func Test_shape_isolated()
+ new
+ set arabicshape
+
+ " Shaping non-arabic {testchar} non-arabic Tests chg_c_a2s().
+ " pair[0] = testchar, pair[1] = current-result
+ for pair in [[s:a_HAMZA, s:a_s_HAMZA],
+ \ [s:a_ALEF_MADDA, s:a_s_ALEF_MADDA],
+ \ [s:a_ALEF_HAMZA_ABOVE, s:a_s_ALEF_HAMZA_ABOVE],
+ \ [s:a_WAW_HAMZA, s:a_s_WAW_HAMZA],
+ \ [s:a_ALEF_HAMZA_BELOW, s:a_s_ALEF_HAMZA_BELOW],
+ \ [s:a_YEH_HAMZA, s:a_s_YEH_HAMZA],
+ \ [s:a_ALEF, s:a_s_ALEF],
+ \ [s:a_TEH_MARBUTA, s:a_s_TEH_MARBUTA],
+ \ [s:a_DAL, s:a_s_DAL],
+ \ [s:a_THAL, s:a_s_THAL],
+ \ [s:a_REH, s:a_s_REH],
+ \ [s:a_ZAIN, s:a_s_ZAIN],
+ \ [s:a_TATWEEL, s:a_TATWEEL],
+ \ [s:a_WAW, s:a_s_WAW],
+ \ [s:a_ALEF_MAKSURA, s:a_s_ALEF_MAKSURA],
+ \ [s:a_BEH, s:a_s_BEH],
+ \ [s:a_TEH, s:a_s_TEH],
+ \ [s:a_THEH, s:a_s_THEH],
+ \ [s:a_JEEM, s:a_s_JEEM],
+ \ [s:a_HAH, s:a_s_HAH],
+ \ [s:a_KHAH, s:a_s_KHAH],
+ \ [s:a_SEEN, s:a_s_SEEN],
+ \ [s:a_SHEEN, s:a_s_SHEEN],
+ \ [s:a_SAD, s:a_s_SAD],
+ \ [s:a_DAD, s:a_s_DAD],
+ \ [s:a_TAH, s:a_s_TAH],
+ \ [s:a_ZAH, s:a_s_ZAH],
+ \ [s:a_AIN, s:a_s_AIN],
+ \ [s:a_GHAIN, s:a_s_GHAIN],
+ \ [s:a_FEH, s:a_s_FEH],
+ \ [s:a_QAF, s:a_s_QAF],
+ \ [s:a_KAF, s:a_s_KAF],
+ \ [s:a_LAM, s:a_s_LAM],
+ \ [s:a_MEEM, s:a_s_MEEM],
+ \ [s:a_NOON, s:a_s_NOON],
+ \ [s:a_HEH, s:a_s_HEH],
+ \ [s:a_YEH, s:a_s_YEH],
+ \ ]
+ call setline(1, ' ' . pair[0] . ' ')
+ call assert_equal([' ' . pair[1] . ' '], ScreenLines(1, 3))
+ endfor
+
+ set arabicshape&
+ bwipe!
+endfunc
+
+func Test_shape_medial()
+ new
+ set arabicshape
+
+ " Shaping arabic {testchar} arabic Tests chg_c_a2m().
+ " pair[0] = testchar, pair[1] = next-result, pair[2] = current-result,
+ " pair[3] = previous-result
+ for pair in [[s:a_HAMZA, s:a_s_GHAIN, s:a_s_HAMZA, s:a_s_BEH],
+ \[s:a_ALEF_MADDA, s:a_s_GHAIN, s:a_f_ALEF_MADDA, s:a_i_BEH],
+ \[s:a_ALEF_HAMZA_ABOVE, s:a_s_GHAIN, s:a_f_ALEF_HAMZA_ABOVE, s:a_i_BEH],
+ \[s:a_WAW_HAMZA, s:a_s_GHAIN, s:a_f_WAW_HAMZA, s:a_i_BEH],
+ \[s:a_ALEF_HAMZA_BELOW, s:a_s_GHAIN, s:a_f_ALEF_HAMZA_BELOW, s:a_i_BEH],
+ \[s:a_YEH_HAMZA, s:a_f_GHAIN, s:a_m_YEH_HAMZA, s:a_i_BEH],
+ \[s:a_ALEF, s:a_s_GHAIN, s:a_f_ALEF, s:a_i_BEH],
+ \[s:a_BEH, s:a_f_GHAIN, s:a_m_BEH, s:a_i_BEH],
+ \[s:a_TEH_MARBUTA, s:a_s_GHAIN, s:a_f_TEH_MARBUTA, s:a_i_BEH],
+ \[s:a_TEH, s:a_f_GHAIN, s:a_m_TEH, s:a_i_BEH],
+ \[s:a_THEH, s:a_f_GHAIN, s:a_m_THEH, s:a_i_BEH],
+ \[s:a_JEEM, s:a_f_GHAIN, s:a_m_JEEM, s:a_i_BEH],
+ \[s:a_HAH, s:a_f_GHAIN, s:a_m_HAH, s:a_i_BEH],
+ \[s:a_KHAH, s:a_f_GHAIN, s:a_m_KHAH, s:a_i_BEH],
+ \[s:a_DAL, s:a_s_GHAIN, s:a_f_DAL, s:a_i_BEH],
+ \[s:a_THAL, s:a_s_GHAIN, s:a_f_THAL, s:a_i_BEH],
+ \[s:a_REH, s:a_s_GHAIN, s:a_f_REH, s:a_i_BEH],
+ \[s:a_ZAIN, s:a_s_GHAIN, s:a_f_ZAIN, s:a_i_BEH],
+ \[s:a_SEEN, s:a_f_GHAIN, s:a_m_SEEN, s:a_i_BEH],
+ \[s:a_SHEEN, s:a_f_GHAIN, s:a_m_SHEEN, s:a_i_BEH],
+ \[s:a_SAD, s:a_f_GHAIN, s:a_m_SAD, s:a_i_BEH],
+ \[s:a_DAD, s:a_f_GHAIN, s:a_m_DAD, s:a_i_BEH],
+ \[s:a_TAH, s:a_f_GHAIN, s:a_m_TAH, s:a_i_BEH],
+ \[s:a_ZAH, s:a_f_GHAIN, s:a_m_ZAH, s:a_i_BEH],
+ \[s:a_AIN, s:a_f_GHAIN, s:a_m_AIN, s:a_i_BEH],
+ \[s:a_GHAIN, s:a_f_GHAIN, s:a_m_GHAIN, s:a_i_BEH],
+ \[s:a_TATWEEL, s:a_f_GHAIN, s:a_TATWEEL, s:a_i_BEH],
+ \[s:a_FEH, s:a_f_GHAIN, s:a_m_FEH, s:a_i_BEH],
+ \[s:a_QAF, s:a_f_GHAIN, s:a_m_QAF, s:a_i_BEH],
+ \[s:a_KAF, s:a_f_GHAIN, s:a_m_KAF, s:a_i_BEH],
+ \[s:a_LAM, s:a_f_GHAIN, s:a_m_LAM, s:a_i_BEH],
+ \[s:a_MEEM, s:a_f_GHAIN, s:a_m_MEEM, s:a_i_BEH],
+ \[s:a_NOON, s:a_f_GHAIN, s:a_m_NOON, s:a_i_BEH],
+ \[s:a_HEH, s:a_f_GHAIN, s:a_m_HEH, s:a_i_BEH],
+ \[s:a_WAW, s:a_s_GHAIN, s:a_f_WAW, s:a_i_BEH],
+ \[s:a_ALEF_MAKSURA, s:a_s_GHAIN, s:a_f_ALEF_MAKSURA, s:a_i_BEH],
+ \[s:a_YEH, s:a_f_GHAIN, s:a_m_YEH, s:a_i_BEH],
+ \ ]
+ call setline(1, s:a_GHAIN . pair[0] . s:a_BEH)
+ call assert_equal([pair[1] . pair[2] . pair[3]], ScreenLines(1, 3))
+ endfor
+
+ set arabicshape&
+ bwipe!
+endfunc
+
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
new file mode 100644
index 0000000000..19d0cee47a
--- /dev/null
+++ b/src/nvim/testdir/test_arglist.vim
@@ -0,0 +1,355 @@
+" Test argument list commands
+
+func Test_argidx()
+ args a b c
+ last
+ call assert_equal(2, argidx())
+ %argdelete
+ call assert_equal(0, argidx())
+ " doing it again doesn't result in an error
+ %argdelete
+ call assert_equal(0, argidx())
+ call assert_fails('2argdelete', 'E16:')
+
+ args a b c
+ call assert_equal(0, argidx())
+ next
+ call assert_equal(1, argidx())
+ next
+ call assert_equal(2, argidx())
+ 1argdelete
+ call assert_equal(1, argidx())
+ 1argdelete
+ call assert_equal(0, argidx())
+ 1argdelete
+ call assert_equal(0, argidx())
+endfunc
+
+func Test_argadd()
+ %argdelete
+ argadd a b c
+ call assert_equal(0, argidx())
+
+ %argdelete
+ argadd a
+ call assert_equal(0, argidx())
+ argadd b c d
+ call assert_equal(0, argidx())
+
+ call Init_abc()
+ argadd x
+ call Assert_argc(['a', 'b', 'x', 'c'])
+ call assert_equal(1, argidx())
+
+ call Init_abc()
+ 0argadd x
+ call Assert_argc(['x', 'a', 'b', 'c'])
+ call assert_equal(2, argidx())
+
+ call Init_abc()
+ 1argadd x
+ call Assert_argc(['a', 'x', 'b', 'c'])
+ call assert_equal(2, argidx())
+
+ call Init_abc()
+ $argadd x
+ call Assert_argc(['a', 'b', 'c', 'x'])
+ call assert_equal(1, argidx())
+
+ call Init_abc()
+ $argadd x
+ +2argadd y
+ call Assert_argc(['a', 'b', 'c', 'x', 'y'])
+ call assert_equal(1, argidx())
+
+ %argd
+ edit d
+ arga
+ call assert_equal(1, len(argv()))
+ call assert_equal('d', get(argv(), 0, ''))
+
+ %argd
+ edit some\ file
+ arga
+ call assert_equal(1, len(argv()))
+ call assert_equal('some file', get(argv(), 0, ''))
+
+ %argd
+ new
+ arga
+ call assert_equal(0, len(argv()))
+endfunc
+
+func Init_abc()
+ args a b c
+ next
+endfunc
+
+func Assert_argc(l)
+ call assert_equal(len(a:l), argc())
+ let i = 0
+ while i < len(a:l) && i < argc()
+ call assert_equal(a:l[i], argv(i))
+ let i += 1
+ endwhile
+endfunc
+
+" Test for [count]argument and [count]argdelete commands
+" Ported from the test_argument_count.in test script
+func Test_argument()
+ " Clean the argument list
+ arga a | %argd
+
+ let save_hidden = &hidden
+ set hidden
+
+ let g:buffers = []
+ augroup TEST
+ au BufEnter * call add(buffers, expand('%:t'))
+ augroup END
+
+ argadd a b c d
+ $argu
+ $-argu
+ -argu
+ 1argu
+ +2argu
+
+ augroup TEST
+ au!
+ augroup END
+
+ call assert_equal(['d', 'c', 'b', 'a', 'c'], g:buffers)
+
+ redir => result
+ ar
+ redir END
+ call assert_true(result =~# 'a b \[c] d')
+
+ .argd
+ call assert_equal(['a', 'b', 'd'], argv())
+
+ -argd
+ call assert_equal(['a', 'd'], argv())
+
+ $argd
+ call assert_equal(['a'], argv())
+
+ 1arga c
+ 1arga b
+ $argu
+ $arga x
+ call assert_equal(['a', 'b', 'c', 'x'], argv())
+
+ 0arga y
+ call assert_equal(['y', 'a', 'b', 'c', 'x'], argv())
+
+ %argd
+ call assert_equal([], argv())
+
+ arga a b c d e f
+ 2,$-argd
+ call assert_equal(['a', 'f'], argv())
+
+ let &hidden = save_hidden
+
+ " Setting argument list should fail when the current buffer has unsaved
+ " changes
+ %argd
+ enew!
+ set modified
+ call assert_fails('args x y z', 'E37:')
+ args! x y z
+ call assert_equal(['x', 'y', 'z'], argv())
+ call assert_equal('x', expand('%:t'))
+
+ last | enew | argu
+ call assert_equal('z', expand('%:t'))
+
+ %argdelete
+ call assert_fails('argument', 'E163:')
+endfunc
+
+" Test for 0argadd and 0argedit
+" Ported from the test_argument_0count.in test script
+func Test_zero_argadd()
+ " Clean the argument list
+ arga a | %argd
+
+ arga a b c d
+ 2argu
+ 0arga added
+ call assert_equal(['added', 'a', 'b', 'c', 'd'], argv())
+
+ 2argu
+ arga third
+ call assert_equal(['added', 'a', 'third', 'b', 'c', 'd'], argv())
+
+ %argd
+ arga a b c d
+ 2argu
+ 0arge edited
+ call assert_equal(['edited', 'a', 'b', 'c', 'd'], argv())
+
+ 2argu
+ arga third
+ call assert_equal(['edited', 'a', 'third', 'b', 'c', 'd'], argv())
+
+ 2argu
+ argedit file\ with\ spaces another file
+ call assert_equal(['edited', 'a', 'file with spaces', 'another', 'file', 'third', 'b', 'c', 'd'], argv())
+ call assert_equal('file with spaces', expand('%'))
+endfunc
+
+func Reset_arglist()
+ args a | %argd
+endfunc
+
+" Test for argc()
+func Test_argc()
+ call Reset_arglist()
+ call assert_equal(0, argc())
+ argadd a b
+ call assert_equal(2, argc())
+endfunc
+
+" Test for arglistid()
+func Test_arglistid()
+ call Reset_arglist()
+ arga a b
+ call assert_equal(0, arglistid())
+ split
+ arglocal
+ call assert_equal(1, arglistid())
+ tabnew | tabfirst
+ call assert_equal(0, arglistid(2))
+ call assert_equal(1, arglistid(1, 1))
+ call assert_equal(0, arglistid(2, 1))
+ call assert_equal(1, arglistid(1, 2))
+ tabonly | only | enew!
+ argglobal
+ call assert_equal(0, arglistid())
+endfunc
+
+" Test for argv()
+func Test_argv()
+ call Reset_arglist()
+ call assert_equal([], argv())
+ call assert_equal("", argv(2))
+ argadd a b c d
+ call assert_equal('c', argv(2))
+endfunc
+
+" Test for the :argedit command
+func Test_argedit()
+ call Reset_arglist()
+ argedit a
+ call assert_equal(['a'], argv())
+ call assert_equal('a', expand('%:t'))
+ argedit b
+ call assert_equal(['a', 'b'], argv())
+ call assert_equal('b', expand('%:t'))
+ argedit a
+ call assert_equal(['a', 'b', 'a'], argv())
+ call assert_equal('a', expand('%:t'))
+ " When file name case is ignored, an existing buffer with only case
+ " difference is re-used.
+ argedit C D
+ call assert_equal('C', expand('%:t'))
+ call assert_equal(['a', 'b', 'a', 'C', 'D'], argv())
+ argedit c
+ if has('fname_case')
+ call assert_equal(['a', 'b', 'a', 'C', 'c', 'D'], argv())
+ else
+ call assert_equal(['a', 'b', 'a', 'C', 'C', 'D'], argv())
+ endif
+ 0argedit x
+ if has('fname_case')
+ call assert_equal(['x', 'a', 'b', 'a', 'C', 'c', 'D'], argv())
+ else
+ call assert_equal(['x', 'a', 'b', 'a', 'C', 'C', 'D'], argv())
+ endif
+ enew! | set modified
+ call assert_fails('argedit y', 'E37:')
+ argedit! y
+ if has('fname_case')
+ call assert_equal(['x', 'y', 'y', 'a', 'b', 'a', 'C', 'c', 'D'], argv())
+ else
+ call assert_equal(['x', 'y', 'y', 'a', 'b', 'a', 'C', 'C', 'D'], argv())
+ endif
+ %argd
+ bwipe! C
+ bwipe! D
+endfunc
+
+" Test for the :argdelete command
+func Test_argdelete()
+ call Reset_arglist()
+ args aa a aaa b bb
+ argdelete a*
+ call assert_equal(['b', 'bb'], argv())
+ call assert_equal('aa', expand('%:t'))
+ last
+ argdelete %
+ call assert_equal(['b'], argv())
+ call assert_fails('argdelete', 'E471:')
+ call assert_fails('1,100argdelete', 'E16:')
+ %argd
+endfunc
+
+" Tests for the :next, :prev, :first, :last, :rewind commands
+func Test_argpos()
+ call Reset_arglist()
+ args a b c d
+ last
+ call assert_equal(3, argidx())
+ call assert_fails('next', 'E165:')
+ prev
+ call assert_equal(2, argidx())
+ Next
+ call assert_equal(1, argidx())
+ first
+ call assert_equal(0, argidx())
+ call assert_fails('prev', 'E164:')
+ 3next
+ call assert_equal(3, argidx())
+ rewind
+ call assert_equal(0, argidx())
+ %argd
+endfunc
+
+" Test for autocommand that redefines the argument list, when doing ":all".
+func Test_arglist_autocmd()
+ autocmd BufReadPost Xxx2 next Xxx2 Xxx1
+ call writefile(['test file Xxx1'], 'Xxx1')
+ call writefile(['test file Xxx2'], 'Xxx2')
+ call writefile(['test file Xxx3'], 'Xxx3')
+
+ new
+ " redefine arglist; go to Xxx1
+ next! Xxx1 Xxx2 Xxx3
+ " open window for all args
+ all
+ call assert_equal('test file Xxx1', getline(1))
+ wincmd w
+ wincmd w
+ call assert_equal('test file Xxx1', getline(1))
+ " should now be in Xxx2
+ rewind
+ call assert_equal('test file Xxx2', getline(1))
+
+ autocmd! BufReadPost Xxx2
+ enew! | only
+ call delete('Xxx1')
+ call delete('Xxx2')
+ call delete('Xxx3')
+ argdelete Xxx*
+ bwipe! Xxx1 Xxx2 Xxx3
+endfunc
+
+func Test_arg_all_expand()
+ call writefile(['test file Xxx1'], 'Xx x')
+ next notexist Xx\ x runtest.vim
+ call assert_equal('notexist Xx\ x runtest.vim', expand('##'))
+ call delete('Xx x')
+endfunc
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
new file mode 100644
index 0000000000..05d69631c4
--- /dev/null
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -0,0 +1,19 @@
+" Test 'autochdir' behavior
+
+if !exists("+autochdir")
+ finish
+endif
+
+func Test_set_filename()
+ let cwd = getcwd()
+ call test_autochdir()
+ set acd
+ new
+ w samples/Xtest
+ call assert_equal("Xtest", expand('%'))
+ call assert_equal("samples", substitute(getcwd(), '.*/\(\k*\)', '\1', ''))
+ bwipe!
+ set noacd
+ exe 'cd ' . cwd
+ call delete('samples/Xtest')
+endfunc
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index c4110eba94..24651b75e1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1,14 +1,13 @@
" Tests for autocommands
-set belloff=all
-function! s:cleanup_buffers() abort
+func! s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
if bufloaded(bnr) && bufnr('%') != bnr
execute 'bd! ' . bnr
endif
endfor
-endfunction
+endfunc
func Test_vim_did_enter()
call assert_false(v:vim_did_enter)
@@ -49,7 +48,7 @@ if has('timers')
endfunc
endif
-function Test_bufunload()
+func Test_bufunload()
augroup test_bufunload_group
autocmd!
autocmd BufUnload * call add(s:li, "bufunload")
@@ -80,7 +79,7 @@ function Test_bufunload()
endfunc
" SEGV occurs in older versions. (At least 7.4.2005 or older)
-function Test_autocmd_bufunload_with_tabnext()
+func Test_autocmd_bufunload_with_tabnext()
tabedit
tabfirst
@@ -98,7 +97,7 @@ function Test_autocmd_bufunload_with_tabnext()
quit
endfunc
-function Test_autocmd_bufwinleave_with_tabfirst()
+func Test_autocmd_bufwinleave_with_tabfirst()
tabedit
augroup sample
autocmd!
@@ -110,7 +109,7 @@ function Test_autocmd_bufwinleave_with_tabfirst()
endfunc
" SEGV occurs in older versions. (At least 7.4.2321 or older)
-function Test_autocmd_bufunload_avoiding_SEGV_01()
+func Test_autocmd_bufunload_avoiding_SEGV_01()
split aa.txt
let lastbuf = bufnr('$')
@@ -128,7 +127,7 @@ function Test_autocmd_bufunload_avoiding_SEGV_01()
endfunc
" SEGV occurs in older versions. (At least 7.4.2321 or older)
-function Test_autocmd_bufunload_avoiding_SEGV_02()
+func Test_autocmd_bufunload_avoiding_SEGV_02()
setlocal buftype=nowrite
let lastbuf = bufnr('$')
@@ -342,7 +341,10 @@ func Test_BufEnter()
call mkdir('Xdir')
split Xdir
call assert_equal('+++', g:val)
- bwipe!
+
+ " On MS-Windows we can't edit the directory, make sure we wipe the right
+ " buffer.
+ bwipe! Xdir
call delete('Xdir', 'd')
au! BufEnter
@@ -350,41 +352,40 @@ endfunc
" Closing a window might cause an endless loop
" E814 for older Vims
-function Test_autocmd_bufwipe_in_SessLoadPost()
- if has('win32')
- throw 'Skipped: test hangs on MS-Windows'
- endif
+func Test_autocmd_bufwipe_in_SessLoadPost()
+ edit Xtest
tabnew
+ file Xsomething
set noswapfile
- let g:bufnr=bufnr('%')
mksession!
- let content=['set nocp noswapfile',
+ let content = ['set nocp noswapfile',
\ 'let v:swapchoice="e"',
\ 'augroup test_autocmd_sessionload',
\ 'autocmd!',
- \ 'autocmd SessionLoadPost * 4bw!|qall!',
+ \ 'autocmd SessionLoadPost * exe bufnr("Xsomething") . "bw!"',
\ 'augroup END',
+ \ '',
+ \ 'func WriteErrors()',
+ \ ' call writefile([execute("messages")], "Xerrors")',
+ \ 'endfunc',
+ \ 'au VimLeave * call WriteErrors()',
\ ]
call writefile(content, 'Xvimrc')
- let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim')
- call assert_match('E814', a)
+ call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq')
+ let errors = join(readfile('Xerrors'))
+ call assert_match('E814', errors)
- unlet! g:bufnr
set swapfile
- for file in ['Session.vim', 'Xvimrc']
+ for file in ['Session.vim', 'Xvimrc', 'Xerrors']
call delete(file)
endfor
endfunc
" SEGV occurs in older versions.
-function Test_autocmd_bufwipe_in_SessLoadPost2()
- if has('win32')
- throw 'Skipped: test hangs on MS-Windows'
- endif
+func Test_autocmd_bufwipe_in_SessLoadPost2()
tabnew
set noswapfile
- let g:bufnr=bufnr('%')
mksession!
let content = ['set nocp noswapfile',
@@ -399,22 +400,768 @@ function Test_autocmd_bufwipe_in_SessLoadPost2()
\ ' exec ''bwipeout '' . b',
\ ' endif',
\ ' endfor',
- \ 'redraw!',
- \ 'echon "SessionLoadPost DONE"',
- \ 'qall!',
+ \ ' echomsg "SessionLoadPost DONE"',
\ 'endfunction',
- \ 'au SessionLoadPost * call DeleteInactiveBufs()']
+ \ 'au SessionLoadPost * call DeleteInactiveBufs()',
+ \ '',
+ \ 'func WriteErrors()',
+ \ ' call writefile([execute("messages")], "Xerrors")',
+ \ 'endfunc',
+ \ 'au VimLeave * call WriteErrors()',
+ \ ]
call writefile(content, 'Xvimrc')
- let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim')
- " this probably only matches on unix
- if has("unix")
- call assert_notmatch('Caught deadly signal SEGV', a)
- endif
- call assert_match('SessionLoadPost DONE', a)
+ call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq')
+ let errors = join(readfile('Xerrors'))
+ " This probably only ever matches on unix.
+ call assert_notmatch('Caught deadly signal SEGV', errors)
+ call assert_match('SessionLoadPost DONE', errors)
- unlet! g:bufnr
set swapfile
- for file in ['Session.vim', 'Xvimrc']
+ for file in ['Session.vim', 'Xvimrc', 'Xerrors']
call delete(file)
endfor
endfunc
+
+func Test_empty_doau()
+ doau \|
+endfunc
+
+func s:AutoCommandOptionSet(match)
+ let item = remove(g:options, 0)
+ let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3])
+ let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type)
+ let g:opt = [expected, actual]
+ "call assert_equal(expected, actual)
+endfunc
+
+func Test_OptionSet()
+ throw 'skipped: Nvim does not support test_override()'
+ if !has("eval") || !has("autocmd") || !exists("+autochdir")
+ return
+ endif
+
+ call test_override('starting', 1)
+ set nocp
+ au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>"))
+
+ " 1: Setting number option"
+ let g:options=[['number', 0, 1, 'global']]
+ set nu
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 2: Setting local number option"
+ let g:options=[['number', 1, 0, 'local']]
+ setlocal nonu
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 3: Setting global number option"
+ let g:options=[['number', 1, 0, 'global']]
+ setglobal nonu
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 4: Setting local autoindent option"
+ let g:options=[['autoindent', 0, 1, 'local']]
+ setlocal ai
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 5: Setting global autoindent option"
+ let g:options=[['autoindent', 0, 1, 'global']]
+ setglobal ai
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 6: Setting global autoindent option"
+ let g:options=[['autoindent', 1, 0, 'global']]
+ set ai!
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " Should not print anything, use :noa
+ " 7: don't trigger OptionSet"
+ let g:options=[['invalid', 1, 1, 'invalid']]
+ noa set nonu
+ call assert_equal([['invalid', 1, 1, 'invalid']], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 8: Setting several global list and number option"
+ let g:options=[['list', 0, 1, 'global'], ['number', 0, 1, 'global']]
+ set list nu
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 9: don't trigger OptionSet"
+ let g:options=[['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']]
+ noa set nolist nonu
+ call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 10: Setting global acd"
+ let g:options=[['autochdir', 0, 1, 'local']]
+ setlocal acd
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 11: Setting global autoread (also sets local value)"
+ let g:options=[['autoread', 0, 1, 'global']]
+ set ar
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 12: Setting local autoread"
+ let g:options=[['autoread', 1, 1, 'local']]
+ setlocal ar
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 13: Setting global autoread"
+ let g:options=[['autoread', 1, 0, 'global']]
+ setglobal invar
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 14: Setting option backspace through :let"
+ let g:options=[['backspace', '', 'eol,indent,start', 'global']]
+ let &bs="eol,indent,start"
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 15: Setting option backspace through setbufvar()"
+ let g:options=[['backup', 0, 1, 'local']]
+ " try twice, first time, shouldn't trigger because option name is invalid,
+ " second time, it should trigger
+ call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355")
+ " should trigger, use correct option name
+ call setbufvar(1, '&backup', 1)
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 16: Setting number option using setwinvar"
+ let g:options=[['number', 0, 1, 'local']]
+ call setwinvar(0, '&number', 1)
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 17: Setting key option, shouldn't trigger"
+ let g:options=[['key', 'invalid', 'invalid1', 'invalid']]
+ setlocal key=blah
+ setlocal key=
+ call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 18: Setting string option"
+ let oldval = &tags
+ let g:options=[['tags', oldval, 'tagpath', 'global']]
+ set tags=tagpath
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 1l: Resetting string option"
+ let g:options=[['tags', 'tagpath', oldval, 'global']]
+ set tags&
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " Cleanup
+ au! OptionSet
+ for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp']
+ exe printf(":set %s&vi", opt)
+ endfor
+ call test_override('starting', 0)
+ delfunc! AutoCommandOptionSet
+endfunc
+
+func Test_OptionSet_diffmode()
+ throw 'skipped: Nvim does not support test_override()'
+ call test_override('starting', 1)
+ " 18: Changing an option when enetering diff mode
+ new
+ au OptionSet diff :let &l:cul=v:option_new
+
+ call setline(1, ['buffer 1', 'line2', 'line3', 'line4'])
+ call assert_equal(0, &l:cul)
+ diffthis
+ call assert_equal(1, &l:cul)
+
+ vnew
+ call setline(1, ['buffer 2', 'line 2', 'line 3', 'line4'])
+ call assert_equal(0, &l:cul)
+ diffthis
+ call assert_equal(1, &l:cul)
+
+ diffoff
+ call assert_equal(0, &l:cul)
+ call assert_equal(1, getwinvar(2, '&l:cul'))
+ bw!
+
+ call assert_equal(1, &l:cul)
+ diffoff!
+ call assert_equal(0, &l:cul)
+ call assert_equal(0, getwinvar(1, '&l:cul'))
+ bw!
+
+ " Cleanup
+ au! OptionSet
+ call test_override('starting', 0)
+endfunc
+
+func Test_OptionSet_diffmode_close()
+ throw 'skipped: Nvim does not support test_override()'
+ call test_override('starting', 1)
+ " 19: Try to close the current window when entering diff mode
+ " should not segfault
+ new
+ au OptionSet diff close
+
+ call setline(1, ['buffer 1', 'line2', 'line3', 'line4'])
+ call assert_fails(':diffthis', 'E788')
+ call assert_equal(1, &diff)
+ vnew
+ call setline(1, ['buffer 2', 'line 2', 'line 3', 'line4'])
+ call assert_fails(':diffthis', 'E788')
+ call assert_equal(1, &diff)
+ bw!
+ call assert_fails(':diffoff!', 'E788')
+ bw!
+
+ " Cleanup
+ au! OptionSet
+ call test_override('starting', 0)
+ "delfunc! AutoCommandOptionSet
+endfunc
+
+" Test for Bufleave autocommand that deletes the buffer we are about to edit.
+func Test_BufleaveWithDelete()
+ new | edit Xfile1
+
+ augroup test_bufleavewithdelete
+ autocmd!
+ autocmd BufLeave Xfile1 bwipe Xfile2
+ augroup END
+
+ call assert_fails('edit Xfile2', 'E143:')
+ call assert_equal('Xfile1', bufname('%'))
+
+ autocmd! test_bufleavewithdelete BufLeave Xfile1
+ augroup! test_bufleavewithdelete
+
+ new
+ bwipe! Xfile1
+endfunc
+
+" Test for autocommand that changes the buffer list, when doing ":ball".
+func Test_Acmd_BufAll()
+ enew!
+ %bwipe!
+ call writefile(['Test file Xxx1'], 'Xxx1')
+ call writefile(['Test file Xxx2'], 'Xxx2')
+ call writefile(['Test file Xxx3'], 'Xxx3')
+
+ " Add three files to the buffer list
+ split Xxx1
+ close
+ split Xxx2
+ close
+ split Xxx3
+ close
+
+ " Wipe the buffer when the buffer is opened
+ au BufReadPost Xxx2 bwipe
+
+ call append(0, 'Test file Xxx4')
+ ball
+
+ call assert_equal(2, winnr('$'))
+ call assert_equal('Xxx1', bufname(winbufnr(winnr('$'))))
+ wincmd t
+
+ au! BufReadPost
+ %bwipe!
+ call delete('Xxx1')
+ call delete('Xxx2')
+ call delete('Xxx3')
+ enew! | only
+endfunc
+
+" Test for autocommand that changes current buffer on BufEnter event.
+" Check if modelines are interpreted for the correct buffer.
+func Test_Acmd_BufEnter()
+ %bwipe!
+ call writefile(['start of test file Xxx1',
+ \ "\<Tab>this is a test",
+ \ 'end of test file Xxx1'], 'Xxx1')
+ call writefile(['start of test file Xxx2',
+ \ 'vim: set noai :',
+ \ "\<Tab>this is a test",
+ \ 'end of test file Xxx2'], 'Xxx2')
+
+ au BufEnter Xxx2 brew
+ set ai modeline modelines=3
+ edit Xxx1
+ " edit Xxx2, autocmd will do :brew
+ edit Xxx2
+ exe "normal G?this is a\<CR>"
+ " Append text with autoindent to this file
+ normal othis should be auto-indented
+ call assert_equal("\<Tab>this should be auto-indented", getline('.'))
+ call assert_equal(3, line('.'))
+ " Remove autocmd and edit Xxx2 again
+ au! BufEnter Xxx2
+ buf! Xxx2
+ exe "normal G?this is a\<CR>"
+ " append text without autoindent to Xxx
+ normal othis should be in column 1
+ call assert_equal("this should be in column 1", getline('.'))
+ call assert_equal(4, line('.'))
+
+ %bwipe!
+ call delete('Xxx1')
+ call delete('Xxx2')
+ set ai&vim modeline&vim modelines&vim
+endfunc
+
+" Test for issue #57
+" do not move cursor on <c-o> when autoindent is set
+func Test_ai_CTRL_O()
+ enew!
+ set ai
+ let save_fo = &fo
+ set fo+=r
+ exe "normal o# abcdef\<Esc>2hi\<CR>\<C-O>d0\<Esc>"
+ exe "normal o# abcdef\<Esc>2hi\<C-O>d0\<Esc>"
+ call assert_equal(['# abc', 'def', 'def'], getline(2, 4))
+
+ set ai&vim
+ let &fo = save_fo
+ enew!
+endfunc
+
+" Test for autocommand that deletes the current buffer on BufLeave event.
+" Also test deleting the last buffer, should give a new, empty buffer.
+func Test_BufLeave_Wipe()
+ throw 'skipped: TODO: '
+ %bwipe!
+ let content = ['start of test file Xxx',
+ \ 'this is a test',
+ \ 'end of test file Xxx']
+ call writefile(content, 'Xxx1')
+ call writefile(content, 'Xxx2')
+
+ au BufLeave Xxx2 bwipe
+ edit Xxx1
+ split Xxx2
+ " delete buffer Xxx2, we should be back to Xxx1
+ bwipe
+ call assert_equal('Xxx1', bufname('%'))
+ call assert_equal(1, winnr('$'))
+
+ " Create an alternate buffer
+ %write! test.out
+ call assert_equal('test.out', bufname('#'))
+ " delete alternate buffer
+ bwipe test.out
+ call assert_equal('Xxx1', bufname('%'))
+ call assert_equal('', bufname('#'))
+
+ au BufLeave Xxx1 bwipe
+ " delete current buffer, get an empty one
+ bwipe!
+ call assert_equal(1, line('$'))
+ call assert_equal('', bufname('%'))
+ let g:bufinfo = getbufinfo()
+ call assert_equal(1, len(g:bufinfo))
+
+ call delete('Xxx1')
+ call delete('Xxx2')
+ call delete('test.out')
+ %bwipe
+ au! BufLeave
+
+ " check that bufinfo doesn't contain a pointer to freed memory
+ call test_garbagecollect_now()
+endfunc
+
+func Test_QuitPre()
+ edit Xfoo
+ let winid = win_getid(winnr())
+ split Xbar
+ au! QuitPre * let g:afile = expand('<afile>')
+ " Close the other window, <afile> should be correct.
+ exe win_id2win(winid) . 'q'
+ call assert_equal('Xfoo', g:afile)
+
+ unlet g:afile
+ bwipe Xfoo
+ bwipe Xbar
+endfunc
+
+func Test_Cmdline()
+ au! CmdlineEnter : let g:entered = expand('<afile>')
+ au! CmdlineLeave : let g:left = expand('<afile>')
+ let g:entered = 0
+ let g:left = 0
+ call feedkeys(":echo 'hello'\<CR>", 'xt')
+ call assert_equal(':', g:entered)
+ call assert_equal(':', g:left)
+ au! CmdlineEnter
+ au! CmdlineLeave
+
+ au! CmdlineEnter / let g:entered = expand('<afile>')
+ au! CmdlineLeave / let g:left = expand('<afile>')
+ let g:entered = 0
+ let g:left = 0
+ new
+ call setline(1, 'hello')
+ call feedkeys("/hello\<CR>", 'xt')
+ call assert_equal('/', g:entered)
+ call assert_equal('/', g:left)
+ bwipe!
+ au! CmdlineEnter
+ au! CmdlineLeave
+endfunc
+
+" Test for BufWritePre autocommand that deletes or unloads the buffer.
+func Test_BufWritePre()
+ %bwipe
+ au BufWritePre Xxx1 bunload
+ au BufWritePre Xxx2 bwipe
+
+ call writefile(['start of Xxx1', 'test', 'end of Xxx1'], 'Xxx1')
+ call writefile(['start of Xxx2', 'test', 'end of Xxx2'], 'Xxx2')
+
+ edit Xtest
+ e! Xxx2
+ bdel Xtest
+ e Xxx1
+ " write it, will unload it and give an error msg
+ call assert_fails('w', 'E203')
+ call assert_equal('Xxx2', bufname('%'))
+ edit Xtest
+ e! Xxx2
+ bwipe Xtest
+ " write it, will delete the buffer and give an error msg
+ call assert_fails('w', 'E203')
+ call assert_equal('Xxx1', bufname('%'))
+ au! BufWritePre
+ call delete('Xxx1')
+ call delete('Xxx2')
+endfunc
+
+" Test for BufUnload autocommand that unloads all the other buffers
+func Test_bufunload_all()
+ call writefile(['Test file Xxx1'], 'Xxx1')"
+ call writefile(['Test file Xxx2'], 'Xxx2')"
+
+ let content = [
+ \ "func UnloadAllBufs()",
+ \ " let i = 1",
+ \ " while i <= bufnr('$')",
+ \ " if i != bufnr('%') && bufloaded(i)",
+ \ " exe i . 'bunload'",
+ \ " endif",
+ \ " let i += 1",
+ \ " endwhile",
+ \ "endfunc",
+ \ "au BufUnload * call UnloadAllBufs()",
+ \ "au VimLeave * call writefile(['Test Finished'], 'Xout')",
+ \ "edit Xxx1",
+ \ "split Xxx2",
+ \ "q"]
+ call writefile(content, 'Xtest')
+
+ call delete('Xout')
+ call system(v:progpath. ' -u NORC -i NONE -N -S Xtest')
+ call assert_true(filereadable('Xout'))
+
+ call delete('Xxx1')
+ call delete('Xxx2')
+ call delete('Xtest')
+ call delete('Xout')
+endfunc
+
+" Some tests for buffer-local autocommands
+func Test_buflocal_autocmd()
+ let g:bname = ''
+ edit xx
+ au BufLeave <buffer> let g:bname = expand("%")
+ " here, autocommand for xx should trigger.
+ " but autocommand shall not apply to buffer named <buffer>.
+ edit somefile
+ call assert_equal('xx', g:bname)
+ let g:bname = ''
+ " here, autocommand shall be auto-deleted
+ bwipe xx
+ " autocmd should not trigger
+ edit xx
+ call assert_equal('', g:bname)
+ " autocmd should not trigger
+ edit somefile
+ call assert_equal('', g:bname)
+ enew
+ unlet g:bname
+endfunc
+
+" Test for "*Cmd" autocommands
+func Test_Cmd_Autocmds()
+ call writefile(['start of Xxx', "\tabc2", 'end of Xxx'], 'Xxx')
+
+ enew!
+ au BufReadCmd XtestA 0r Xxx|$del
+ edit XtestA " will read text of Xxd instead
+ call assert_equal('start of Xxx', getline(1))
+
+ au BufWriteCmd XtestA call append(line("$"), "write")
+ write " will append a line to the file
+ call assert_equal('write', getline('$'))
+ call assert_fails('read XtestA', 'E484') " should not read anything
+ call assert_equal('write', getline(4))
+
+ " now we have:
+ " 1 start of Xxx
+ " 2 abc2
+ " 3 end of Xxx
+ " 4 write
+
+ au FileReadCmd XtestB '[r Xxx
+ 2r XtestB " will read Xxx below line 2 instead
+ call assert_equal('start of Xxx', getline(3))
+
+ " now we have:
+ " 1 start of Xxx
+ " 2 abc2
+ " 3 start of Xxx
+ " 4 abc2
+ " 5 end of Xxx
+ " 6 end of Xxx
+ " 7 write
+
+ au FileWriteCmd XtestC '[,']copy $
+ normal 4GA1
+ 4,5w XtestC " will copy lines 4 and 5 to the end
+ call assert_equal("\tabc21", getline(8))
+ call assert_fails('r XtestC', 'E484') " should not read anything
+ call assert_equal("end of Xxx", getline(9))
+
+ " now we have:
+ " 1 start of Xxx
+ " 2 abc2
+ " 3 start of Xxx
+ " 4 abc21
+ " 5 end of Xxx
+ " 6 end of Xxx
+ " 7 write
+ " 8 abc21
+ " 9 end of Xxx
+
+ let g:lines = []
+ au FileAppendCmd XtestD call extend(g:lines, getline(line("'["), line("']")))
+ w >>XtestD " will add lines to 'lines'
+ call assert_equal(9, len(g:lines))
+ call assert_fails('$r XtestD', 'E484') " should not read anything
+ call assert_equal(9, line('$'))
+ call assert_equal('end of Xxx', getline('$'))
+
+ au BufReadCmd XtestE 0r Xxx|$del
+ sp XtestE " split window with test.out
+ call assert_equal('end of Xxx', getline(3))
+
+ let g:lines = []
+ exe "normal 2Goasdf\<Esc>\<C-W>\<C-W>"
+ au BufWriteCmd XtestE call extend(g:lines, getline(0, '$'))
+ wall " will write other window to 'lines'
+ call assert_equal(4, len(g:lines), g:lines)
+ call assert_equal("\tasdf", g:lines[2])
+
+ au! BufReadCmd
+ au! BufWriteCmd
+ au! FileReadCmd
+ au! FileWriteCmd
+ au! FileAppendCmd
+ %bwipe!
+ call delete('Xxx')
+ enew!
+endfunc
+
+func SetChangeMarks(start, end)
+ exe a:start. 'mark ['
+ exe a:end. 'mark ]'
+endfunc
+
+" Verify the effects of autocmds on '[ and ']
+func Test_change_mark_in_autocmds()
+ edit! Xtest
+ call feedkeys("ia\<CR>b\<CR>c\<CR>d\<C-g>u", 'xtn')
+
+ call SetChangeMarks(2, 3)
+ write
+ call assert_equal([1, 4], [line("'["), line("']")])
+
+ call SetChangeMarks(2, 3)
+ au BufWritePre * call assert_equal([1, 4], [line("'["), line("']")])
+ write
+ au! BufWritePre
+
+ if executable('cat')
+ write XtestFilter
+ write >> XtestFilter
+
+ call SetChangeMarks(2, 3)
+ " Marks are set to the entire range of the write
+ au FilterWritePre * call assert_equal([1, 4], [line("'["), line("']")])
+ " '[ is adjusted to just before the line that will receive the filtered
+ " data
+ au FilterReadPre * call assert_equal([4, 4], [line("'["), line("']")])
+ " The filtered data is read into the buffer, and the source lines are
+ " still present, so the range is after the source lines
+ au FilterReadPost * call assert_equal([5, 12], [line("'["), line("']")])
+ %!cat XtestFilter
+ " After the filtered data is read, the original lines are deleted
+ call assert_equal([1, 8], [line("'["), line("']")])
+ au! FilterWritePre,FilterReadPre,FilterReadPost
+ undo
+
+ call SetChangeMarks(1, 4)
+ au FilterWritePre * call assert_equal([2, 3], [line("'["), line("']")])
+ au FilterReadPre * call assert_equal([3, 3], [line("'["), line("']")])
+ au FilterReadPost * call assert_equal([4, 11], [line("'["), line("']")])
+ 2,3!cat XtestFilter
+ call assert_equal([2, 9], [line("'["), line("']")])
+ au! FilterWritePre,FilterReadPre,FilterReadPost
+ undo
+
+ call delete('XtestFilter')
+ endif
+
+ call SetChangeMarks(1, 4)
+ au FileWritePre * call assert_equal([2, 3], [line("'["), line("']")])
+ 2,3write Xtest2
+ au! FileWritePre
+
+ call SetChangeMarks(2, 3)
+ au FileAppendPre * call assert_equal([1, 4], [line("'["), line("']")])
+ write >> Xtest2
+ au! FileAppendPre
+
+ call SetChangeMarks(1, 4)
+ au FileAppendPre * call assert_equal([2, 3], [line("'["), line("']")])
+ 2,3write >> Xtest2
+ au! FileAppendPre
+
+ call SetChangeMarks(1, 1)
+ au FileReadPre * call assert_equal([3, 1], [line("'["), line("']")])
+ au FileReadPost * call assert_equal([4, 11], [line("'["), line("']")])
+ 3read Xtest2
+ au! FileReadPre,FileReadPost
+ undo
+
+ call SetChangeMarks(4, 4)
+ " When the line is 0, it's adjusted to 1
+ au FileReadPre * call assert_equal([1, 4], [line("'["), line("']")])
+ au FileReadPost * call assert_equal([1, 8], [line("'["), line("']")])
+ 0read Xtest2
+ au! FileReadPre,FileReadPost
+ undo
+
+ call SetChangeMarks(4, 4)
+ " When the line is 0, it's adjusted to 1
+ au FileReadPre * call assert_equal([1, 4], [line("'["), line("']")])
+ au FileReadPost * call assert_equal([2, 9], [line("'["), line("']")])
+ 1read Xtest2
+ au! FileReadPre,FileReadPost
+ undo
+
+ bwipe!
+ call delete('Xtest')
+ call delete('Xtest2')
+endfunc
+
+func Test_Filter_noshelltemp()
+ if !executable('cat')
+ return
+ endif
+
+ enew!
+ call setline(1, ['a', 'b', 'c', 'd'])
+
+ let shelltemp = &shelltemp
+ set shelltemp
+
+ let g:filter_au = 0
+ au FilterWritePre * let g:filter_au += 1
+ au FilterReadPre * let g:filter_au += 1
+ au FilterReadPost * let g:filter_au += 1
+ %!cat
+ call assert_equal(3, g:filter_au)
+
+ if has('filterpipe')
+ set noshelltemp
+
+ let g:filter_au = 0
+ au FilterWritePre * let g:filter_au += 1
+ au FilterReadPre * let g:filter_au += 1
+ au FilterReadPost * let g:filter_au += 1
+ %!cat
+ call assert_equal(0, g:filter_au)
+ endif
+
+ au! FilterWritePre,FilterReadPre,FilterReadPost
+ let &shelltemp = shelltemp
+ bwipe!
+endfunc
+
+func Test_TextYankPost()
+ enew!
+ call setline(1, ['foo'])
+
+ let g:event = []
+ au TextYankPost * let g:event = copy(v:event)
+
+ call assert_equal({}, v:event)
+ call assert_fails('let v:event = {}', 'E46:')
+ call assert_fails('let v:event.mykey = 0', 'E742:')
+
+ norm "ayiw
+ call assert_equal(
+ \{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
+ \g:event)
+ norm y_
+ call assert_equal(
+ \{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'},
+ \g:event)
+ call feedkeys("\<C-V>y", 'x')
+ call assert_equal(
+ \{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"},
+ \g:event)
+ norm "xciwbar
+ call assert_equal(
+ \{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
+ \g:event)
+ norm "bdiw
+ call assert_equal(
+ \{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
+ \g:event)
+
+ call assert_equal({}, v:event)
+
+ au! TextYankPost
+ unlet g:event
+ bwipe!
+endfunc
+
+func Test_nocatch_wipe_all_buffers()
+ " Real nasty autocommand: wipe all buffers on any event.
+ au * * bwipe *
+ call assert_fails('next x', 'E93')
+ bwipe
+ au!
+endfunc
+
+func Test_nocatch_wipe_dummy_buffer()
+ " Nasty autocommand: wipe buffer on any event.
+ au * x bwipe
+ call assert_fails('lv½ /x', 'E480')
+ au!
+endfunc
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
new file mode 100644
index 0000000000..7deffbe452
--- /dev/null
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -0,0 +1,298 @@
+" Test for breakindent
+"
+" Note: if you get strange failures when adding new tests, it might be that
+" while the test is run, the breakindent cacheing gets in its way.
+" It helps to change the tabstop setting and force a redraw (e.g. see
+" Test_breakindent08())
+if !exists('+breakindent')
+ finish
+endif
+
+source view_util.vim
+
+let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+
+function s:screen_lines(lnum, width) abort
+ return ScreenLines([a:lnum, a:lnum + 2], a:width)
+endfunction
+
+function! s:compare_lines(expect, actual)
+ call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
+endfunction
+
+function s:test_windows(...)
+ call NewWindow(10, 20)
+ setl ts=4 sw=4 sts=4 breakindent
+ put =s:input
+ exe get(a:000, 0, '')
+endfunction
+
+function s:close_windows(...)
+ call CloseWindow()
+ exe get(a:000, 0, '')
+endfunction
+
+function Test_breakindent01()
+ " simple breakindent test
+ call s:test_windows('setl briopt=min:0')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qrst",
+\ " GHIJ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent02()
+ " simple breakindent test with showbreak set
+ call s:test_windows('setl briopt=min:0 sbr=>>')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " >>qr",
+\ " >>EF",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent03()
+ " simple breakindent test with showbreak set and briopt including sbr
+ call s:test_windows('setl briopt=sbr,min:0 sbr=++')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ "++ qrst",
+\ "++ GHIJ",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent04()
+ " breakindent set with min width 18
+ call s:test_windows('setl sbr= briopt=min:18')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qrstuv",
+\ " IJKLMN",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent05()
+ " breakindent set and shift by 2
+ call s:test_windows('setl briopt=shift:2,min:0')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qr",
+\ " EF",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent06()
+ " breakindent set and shift by -1
+ call s:test_windows('setl briopt=shift:-1,min:0')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qrstu",
+\ " HIJKL",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent07()
+ " breakindent set and shift by 1, Number set sbr=? and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ab",
+\ "? m",
+\ "? x",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr= cpo-=n')
+endfunction
+
+function Test_breakindent07a()
+ " breakindent set and shift by 1, Number set sbr=? and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ab",
+\ " ? m",
+\ " ? x",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent08()
+ " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4')
+ " make sure, cache is invalidated!
+ set ts=8
+ redraw!
+ set ts=4
+ redraw!
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ^Iabcd",
+\ "# opq",
+\ "# BCD",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= cpo-=n')
+endfunction
+
+function Test_breakindent08a()
+ " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ^Iabcd",
+\ " # opq",
+\ " # BCD",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent09()
+ " breakindent set and shift by 1, Number and list set sbr=#
+ call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ^Iabcd",
+\ " #op",
+\ " #AB",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent10()
+ " breakindent set, Number set sbr=~
+ call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0')
+ " make sure, cache is invalidated!
+ set ts=8
+ redraw!
+ set ts=4
+ redraw!
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ab",
+\ "~ mn",
+\ "~ yz",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= cpo-=n')
+endfunction
+
+function Test_breakindent11()
+ " test strdisplaywidth()
+ call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4')
+ let text=getline(2)
+ let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
+ call assert_equal(width, strdisplaywidth(text))
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent12()
+ " test breakindent with long indent
+ let s:input="\t\t\t\t\t{"
+ call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>-')
+ let lines=s:screen_lines(2,16)
+ let expect=[
+\ " 2 >--->--->--->",
+\ " ---{ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set nuw=4 listchars=')
+endfunction
+
+function Test_breakindent13()
+ let s:input=""
+ call s:test_windows('setl breakindent briopt=min:10 ts=8')
+ vert resize 20
+ call setline(1, [" a\tb\tc\td\te", " z y x w v"])
+ 1
+ norm! fbgj"ayl
+ 2
+ norm! fygj"byl
+ call assert_equal('d', @a)
+ call assert_equal('w', @b)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent14()
+ let s:input=""
+ call s:test_windows('setl breakindent briopt= ts=8')
+ vert resize 30
+ norm! 3a1234567890
+ norm! a abcde
+ exec "norm! 0\<C-V>tex"
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ "e ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent15()
+ let s:input=""
+ call s:test_windows('setl breakindent briopt= ts=8 sw=8')
+ vert resize 30
+ norm! 4a1234567890
+ exe "normal! >>\<C-V>3f0x"
+ let lines=s:screen_lines(line('.'),20)
+ let expect=[
+\ " 1234567890 ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent16()
+ " Check that overlong lines are indented correctly.
+ let s:input=""
+ call s:test_windows('setl breakindent briopt=min:0 ts=4')
+ call setline(1, "\t".repeat("1234567890", 10))
+ resize 6
+ norm! 1gg$
+ redraw!
+ let lines=s:screen_lines(1,10)
+ let expect=[
+\ " 789012",
+\ " 345678",
+\ " 901234",
+\ ]
+ call s:compare_lines(expect, lines)
+ let lines=s:screen_lines(4,10)
+ let expect=[
+\ " 567890",
+\ " 123456",
+\ " 7890 ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim
index 5c916e2dd7..a592cd7b11 100644
--- a/src/nvim/testdir/test_bufwintabinfo.vim
+++ b/src/nvim/testdir/test_bufwintabinfo.vim
@@ -1,7 +1,6 @@
" Tests for the getbufinfo(), getwininfo() and gettabinfo() functions
function Test_getbufwintabinfo()
- 1,$bwipeout
edit Xtestfile1
edit Xtestfile2
let buflist = getbufinfo()
@@ -87,9 +86,17 @@ function Test_get_buf_options()
endfunc
function Test_get_win_options()
+ if has('folding')
+ set foldlevel=999
+ endif
+ set list
let opts = getwinvar(1, '&')
call assert_equal(v:t_dict, type(opts))
call assert_equal(0, opts.linebreak)
+ call assert_equal(1, opts.list)
+ if has('folding')
+ call assert_equal(999, opts.foldlevel)
+ endif
if has('signs')
call assert_equal('auto', opts.signcolumn)
endif
@@ -97,7 +104,12 @@ function Test_get_win_options()
let opts = gettabwinvar(1, 1, '&')
call assert_equal(v:t_dict, type(opts))
call assert_equal(0, opts.linebreak)
+ call assert_equal(1, opts.list)
if has('signs')
call assert_equal('auto', opts.signcolumn)
endif
+ set list&
+ if has('folding')
+ set foldlevel=0
+ endif
endfunc
diff --git a/src/nvim/testdir/test_changedtick.vim b/src/nvim/testdir/test_changedtick.vim
new file mode 100644
index 0000000000..3a91bb54aa
--- /dev/null
+++ b/src/nvim/testdir/test_changedtick.vim
@@ -0,0 +1,57 @@
+" Tests for b:changedtick
+
+func Test_changedtick_increments()
+ new
+ " New buffer has an empty line, tick starts at 2.
+ let expected = 2
+ call assert_equal(expected, b:changedtick)
+ call assert_equal(expected, b:['changedtick'])
+ call setline(1, 'hello')
+ let expected += 1
+ call assert_equal(expected, b:changedtick)
+ call assert_equal(expected, b:['changedtick'])
+ undo
+ " Somehow undo counts as two changes.
+ let expected += 2
+ call assert_equal(expected, b:changedtick)
+ call assert_equal(expected, b:['changedtick'])
+ bwipe!
+endfunc
+
+func Test_changedtick_dict_entry()
+ let d = b:
+ call assert_equal(b:changedtick, d['changedtick'])
+endfunc
+
+func Test_changedtick_bdel()
+ new
+ let bnr = bufnr('%')
+ let v = b:changedtick
+ bdel
+ " Delete counts as a change too.
+ call assert_equal(v + 1, getbufvar(bnr, 'changedtick'))
+endfunc
+
+func Test_changedtick_islocked()
+ call assert_equal(0, islocked('b:changedtick'))
+ let d = b:
+ call assert_equal(0, islocked('d.changedtick'))
+endfunc
+
+func Test_changedtick_fixed()
+ call assert_fails('let b:changedtick = 4', 'E46:')
+ call assert_fails('let b:["changedtick"] = 4', 'E46:')
+
+ call assert_fails('lockvar b:changedtick', 'E940:')
+ call assert_fails('lockvar b:["changedtick"]', 'E46:')
+ call assert_fails('unlockvar b:changedtick', 'E940:')
+ call assert_fails('unlockvar b:["changedtick"]', 'E46:')
+ call assert_fails('unlet b:changedtick', 'E795:')
+ call assert_fails('unlet b:["changedtick"]', 'E46:')
+
+ let d = b:
+ call assert_fails('lockvar d["changedtick"]', 'E46:')
+ call assert_fails('unlockvar d["changedtick"]', 'E46:')
+ call assert_fails('unlet d["changedtick"]', 'E46:')
+
+endfunc
diff --git a/src/nvim/testdir/test_charsearch_utf8.vim b/src/nvim/testdir/test_charsearch_utf8.vim
new file mode 100644
index 0000000000..ade7dd408c
--- /dev/null
+++ b/src/nvim/testdir/test_charsearch_utf8.vim
@@ -0,0 +1,22 @@
+" Tests for related f{char} and t{char} using utf-8.
+if !has('multi_byte')
+ finish
+endif
+
+" Test for t,f,F,T movement commands
+function! Test_search_cmds()
+ new!
+ call setline(1, "・最初から最後まで最強のVimは最高")
+ 1
+ normal! f最
+ call assert_equal([0, 1, 4, 0], getpos('.'))
+ normal! ;
+ call assert_equal([0, 1, 16, 0], getpos('.'))
+ normal! 2;
+ call assert_equal([0, 1, 43, 0], getpos('.'))
+ normal! ,
+ call assert_equal([0, 1, 28, 0], getpos('.'))
+ bw!
+endfunction
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim
new file mode 100644
index 0000000000..444c4c4109
--- /dev/null
+++ b/src/nvim/testdir/test_cindent.vim
@@ -0,0 +1,76 @@
+" Test for cinoptions and cindent
+"
+" TODO: rewrite test3.in into this new style test
+
+func Test_cino_hash()
+ " Test that curbuf->b_ind_hash_comment is correctly reset
+ new
+ setlocal cindent cinoptions=#1
+ setlocal cinoptions=
+ call setline(1, ["#include <iostream>"])
+ call cursor(1, 1)
+ norm! o#include
+ "call feedkeys("o#include\<esc>", 't')
+ call assert_equal(["#include <iostream>", "#include"], getline(1,2))
+ bwipe!
+endfunc
+
+func Test_cino_extern_c()
+ " Test for cino-E
+
+ let without_ind = [
+ \ '#ifdef __cplusplus',
+ \ 'extern "C" {',
+ \ '#endif',
+ \ 'int func_a(void);',
+ \ '#ifdef __cplusplus',
+ \ '}',
+ \ '#endif'
+ \ ]
+
+ let with_ind = [
+ \ '#ifdef __cplusplus',
+ \ 'extern "C" {',
+ \ '#endif',
+ \ "\tint func_a(void);",
+ \ '#ifdef __cplusplus',
+ \ '}',
+ \ '#endif'
+ \ ]
+ new
+ setlocal cindent cinoptions=E0
+ call setline(1, without_ind)
+ call feedkeys("gg=G", 'tx')
+ call assert_equal(with_ind, getline(1, '$'))
+
+ setlocal cinoptions=E-s
+ call setline(1, with_ind)
+ call feedkeys("gg=G", 'tx')
+ call assert_equal(without_ind, getline(1, '$'))
+
+ setlocal cinoptions=Es
+ let tests = [
+ \ ['recognized', ['extern "C" {'], "\t\t;"],
+ \ ['recognized', ['extern "C++" {'], "\t\t;"],
+ \ ['recognized', ['extern /* com */ "C"{'], "\t\t;"],
+ \ ['recognized', ['extern"C"{'], "\t\t;"],
+ \ ['recognized', ['extern "C"', '{'], "\t\t;"],
+ \ ['not recognized', ['extern {'], "\t;"],
+ \ ['not recognized', ['extern /*"C"*/{'], "\t;"],
+ \ ['not recognized', ['extern "C" //{'], ";"],
+ \ ['not recognized', ['extern "C" /*{*/'], ";"],
+ \ ]
+
+ for pair in tests
+ let lines = pair[1]
+ call setline(1, lines)
+ call feedkeys(len(lines) . "Go;", 'tx')
+ call assert_equal(pair[2], getline(len(lines) + 1), 'Failed for "' . string(lines) . '"')
+ endfor
+
+
+
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_close_count.vim b/src/nvim/testdir/test_close_count.vim
new file mode 100644
index 0000000000..1f9adba32d
--- /dev/null
+++ b/src/nvim/testdir/test_close_count.vim
@@ -0,0 +1,174 @@
+
+" Tests for :[count]close! command
+func Test_close_count()
+ enew! | only
+
+ let wids = [win_getid()]
+ for i in range(5)
+ new
+ call add(wids, win_getid())
+ endfor
+
+ 4wincmd w
+ close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[5], wids[4], wids[3], wids[1], wids[0]], ids)
+
+ 1close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[3], wids[1], wids[0]], ids)
+
+ $close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[3], wids[1]], ids)
+
+ 1wincmd w
+ 2close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[1]], ids)
+
+ 1wincmd w
+ new
+ call add(wids, win_getid())
+ new
+ call add(wids, win_getid())
+ 2wincmd w
+ -1close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[6], wids[4], wids[1]], ids)
+
+ 2wincmd w
+ +1close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[6], wids[4]], ids)
+
+ only!
+endfunc
+
+" Tests for :[count]hide command
+func Test_hide_count()
+ enew! | only
+
+ let wids = [win_getid()]
+ for i in range(5)
+ new
+ call add(wids, win_getid())
+ endfor
+
+ 4wincmd w
+ .hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[5], wids[4], wids[3], wids[1], wids[0]], ids)
+
+ 1hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[3], wids[1], wids[0]], ids)
+
+ $hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[3], wids[1]], ids)
+
+ 1wincmd w
+ 2hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[1]], ids)
+
+ 1wincmd w
+ new
+ call add(wids, win_getid())
+ new
+ call add(wids, win_getid())
+ 3wincmd w
+ -hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[7], wids[4], wids[1]], ids)
+
+ 2wincmd w
+ +hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[7], wids[4]], ids)
+
+ only!
+endfunc
+
+" Tests for :[count]close! command with 'hidden'
+func Test_hidden_close_count()
+ enew! | only
+
+ let wids = [win_getid()]
+ for i in range(5)
+ new
+ call add(wids, win_getid())
+ endfor
+
+ set hidden
+
+ $ hide
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[5], wids[4], wids[3], wids[2], wids[1]], ids)
+
+ $-1 close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[5], wids[4], wids[3], wids[1]], ids)
+
+ 1wincmd w
+ .+close!
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[5], wids[3], wids[1]], ids)
+
+ set nohidden
+ only!
+endfunc
+
+" Tests for 'CTRL-W c' command to close windows.
+func Test_winclose_command()
+ enew! | only
+
+ let wids = [win_getid()]
+ for i in range(5)
+ new
+ call add(wids, win_getid())
+ endfor
+
+ set hidden
+
+ 4wincmd w
+ exe "normal \<C-W>c"
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[5], wids[4], wids[3], wids[1], wids[0]], ids)
+
+ exe "normal 1\<C-W>c"
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[3], wids[1], wids[0]], ids)
+
+ exe "normal 9\<C-W>c"
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[3], wids[1]], ids)
+
+ 1wincmd w
+ exe "normal 2\<C-W>c"
+ let ids = []
+ windo call add(ids, win_getid())
+ call assert_equal([wids[4], wids[1]], ids)
+
+ set nohidden
+ only!
+endfunc
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 0c9d1297d6..673246e1fb 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -1,8 +1,9 @@
" Tests for editing the command line.
+
func Test_complete_tab()
call writefile(['testfile'], 'Xtestfile')
- call feedkeys(":e Xtest\t\r", "tx")
+ call feedkeys(":e Xtestf\t\r", "tx")
call assert_equal('testfile', getline(1))
call delete('Xtestfile')
endfunc
@@ -17,7 +18,7 @@ func Test_complete_wildmenu()
call writefile(['testfile1'], 'Xtestfile1')
call writefile(['testfile2'], 'Xtestfile2')
set wildmenu
- call feedkeys(":e Xtest\t\t\r", "tx")
+ call feedkeys(":e Xtestf\t\t\r", "tx")
call assert_equal('testfile2', getline(1))
call delete('Xtestfile1')
@@ -25,6 +26,88 @@ func Test_complete_wildmenu()
set nowildmenu
endfunc
+func Test_map_completion()
+ if !has('cmdline_compl')
+ return
+ endif
+ call feedkeys(":map <unique> <si\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <unique> <silent>', getreg(':'))
+ call feedkeys(":map <script> <un\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <script> <unique>', getreg(':'))
+ call feedkeys(":map <expr> <sc\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <expr> <script>', getreg(':'))
+ call feedkeys(":map <buffer> <e\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <buffer> <expr>', getreg(':'))
+ call feedkeys(":map <nowait> <b\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <nowait> <buffer>', getreg(':'))
+ call feedkeys(":map <special> <no\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <special> <nowait>', getreg(':'))
+ call feedkeys(":map <silent> <sp\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <silent> <special>', getreg(':'))
+endfunc
+
+func Test_match_completion()
+ if !has('cmdline_compl')
+ return
+ endif
+ hi Aardig ctermfg=green
+ call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"match Aardig', getreg(':'))
+ call feedkeys(":match \<S-Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"match none', getreg(':'))
+endfunc
+
+func Test_highlight_completion()
+ if !has('cmdline_compl')
+ return
+ endif
+ hi Aardig ctermfg=green
+ call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"hi Aardig', getreg(':'))
+ call feedkeys(":hi li\<S-Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"hi link', getreg(':'))
+ call feedkeys(":hi d\<S-Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"hi default', getreg(':'))
+ call feedkeys(":hi c\<S-Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"hi clear', getreg(':'))
+
+ " A cleared group does not show up in completions.
+ hi Anders ctermfg=green
+ call assert_equal(['Aardig', 'Anders'], getcompletion('A', 'highlight'))
+ hi clear Aardig
+ call assert_equal(['Anders'], getcompletion('A', 'highlight'))
+ hi clear Anders
+ call assert_equal([], getcompletion('A', 'highlight'))
+endfunc
+
+func Test_expr_completion()
+ if !has('cmdline_compl')
+ return
+ endif
+ for cmd in [
+ \ 'let a = ',
+ \ 'if',
+ \ 'elseif',
+ \ 'while',
+ \ 'for',
+ \ 'echo',
+ \ 'echon',
+ \ 'execute',
+ \ 'echomsg',
+ \ 'echoerr',
+ \ 'call',
+ \ 'return',
+ \ 'cexpr',
+ \ 'caddexpr',
+ \ 'cgetexpr',
+ \ 'lexpr',
+ \ 'laddexpr',
+ \ 'lgetexpr']
+ call feedkeys(":" . cmd . " getl\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"' . cmd . ' getline(', getreg(':'))
+ endfor
+endfunc
+
func Test_getcompletion()
if !has('cmdline_compl')
return
@@ -130,6 +213,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']
@@ -204,3 +292,129 @@ func Test_expand_star_star()
bwipe!
call delete('a', 'rf')
endfunc
+
+func Test_paste_in_cmdline()
+ let @a = "def"
+ call feedkeys(":abc \<C-R>a ghi\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abc def ghi', @:)
+
+ new
+ call setline(1, 'asdf.x /tmp/some verylongword a;b-c*d ')
+
+ call feedkeys(":aaa \<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa asdf bbb', @:)
+
+ call feedkeys("ft:aaa \<C-R>\<C-F> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa /tmp/some bbb', @:)
+
+ set incsearch
+ call feedkeys("fy:aaa veryl\<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa verylongword bbb', @:)
+
+ call feedkeys("f;:aaa \<C-R>\<C-A> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa a;b-c*d bbb', @:)
+
+ call feedkeys(":\<C-\>etoupper(getline(1))\<CR>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"ASDF.X /TMP/SOME VERYLONGWORD A;B-C*D ', @:)
+ bwipe!
+endfunc
+
+func Test_remove_char_in_cmdline()
+ call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abc ef', @:)
+
+ call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abcdef', @:)
+
+ call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abc ghi', @:)
+
+ call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"def', @:)
+endfunc
+
+func Test_illegal_address1()
+ new
+ 2;'(
+ 2;')
+ quit
+endfunc
+
+func Test_illegal_address2()
+ call writefile(['c', 'x', ' x', '.', '1;y'], 'Xtest.vim')
+ new
+ source Xtest.vim
+ " Trigger calling validate_cursor()
+ diffsp Xtest.vim
+ quit!
+ bwipe!
+ call delete('Xtest.vim')
+endfunc
+
+func Test_cmdline_complete_wildoptions()
+ help
+ call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx')
+ let a = join(sort(split(@:)),' ')
+ set wildoptions=tagfile
+ call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx')
+ let b = join(sort(split(@:)),' ')
+ call assert_equal(a, b)
+ bw!
+endfunc
+
+" using a leading backslash here
+set cpo+=C
+
+func Test_cmdline_search_range()
+ new
+ call setline(1, ['a', 'b', 'c', 'd'])
+ /d
+ 1,\/s/b/B/
+ call assert_equal('B', getline(2))
+
+ /a
+ $
+ \?,4s/c/C/
+ call assert_equal('C', getline(3))
+
+ call setline(1, ['a', 'b', 'c', 'd'])
+ %s/c/c/
+ 1,\&s/b/B/
+ call assert_equal('B', getline(2))
+
+ bwipe!
+endfunc
+
+" Tests for getcmdline(), getcmdpos() and getcmdtype()
+func Check_cmdline(cmdtype)
+ call assert_equal('MyCmd a', getcmdline())
+ call assert_equal(8, getcmdpos())
+ call assert_equal(a:cmdtype, getcmdtype())
+ return ''
+endfunc
+
+func Test_getcmdtype()
+ call feedkeys(":MyCmd a\<C-R>=Check_cmdline(':')\<CR>\<Esc>", "xt")
+
+ let cmdtype = ''
+ debuggreedy
+ call feedkeys(":debug echo 'test'\<CR>", "t")
+ call feedkeys("let cmdtype = \<C-R>=string(getcmdtype())\<CR>\<CR>", "t")
+ call feedkeys("cont\<CR>", "xt")
+ 0debuggreedy
+ call assert_equal('>', cmdtype)
+
+ call feedkeys("/MyCmd a\<C-R>=Check_cmdline('/')\<CR>\<Esc>", "xt")
+ call feedkeys("?MyCmd a\<C-R>=Check_cmdline('?')\<CR>\<Esc>", "xt")
+
+ call feedkeys(":call input('Answer?')\<CR>", "t")
+ call feedkeys("MyCmd a\<C-R>=Check_cmdline('@')\<CR>\<C-C>", "xt")
+
+ call feedkeys(":insert\<CR>MyCmd a\<C-R>=Check_cmdline('-')\<CR>\<Esc>", "xt")
+
+ cnoremap <expr> <F6> Check_cmdline('=')
+ call feedkeys("a\<C-R>=MyCmd a\<F6>\<Esc>\<Esc>", "xt")
+ cunmap <F6>
+endfunc
+
+set cpo&
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index e438a8b077..2d793ed88f 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -1,6 +1,7 @@
" Test for user command counts.
func Test_command_count_0()
+ let bufnr = bufnr('%')
set hidden
set noswapfile
@@ -15,17 +16,17 @@ func Test_command_count_0()
command! -range=% -addr=buffers RangeBuffersAll :let lines = [<line1>, <line2>]
.,$RangeLoadedBuffers
- call assert_equal([1, 1], lines)
+ call assert_equal([bufnr, bufnr], lines)
%RangeLoadedBuffers
- call assert_equal([1, 1], lines)
+ call assert_equal([bufnr, bufnr], lines)
RangeLoadedBuffersAll
- call assert_equal([1, 1], lines)
+ call assert_equal([bufnr, bufnr], lines)
.,$RangeBuffers
- call assert_equal([1, lastbuf], lines)
+ call assert_equal([bufnr, lastbuf], lines)
%RangeBuffers
- call assert_equal([1, lastbuf], lines)
+ call assert_equal([bufnr, lastbuf], lines)
RangeBuffersAll
- call assert_equal([1, lastbuf], lines)
+ call assert_equal([bufnr, lastbuf], lines)
delcommand RangeLoadedBuffers
delcommand RangeLoadedBuffersAll
@@ -138,6 +139,7 @@ func Test_command_count_2()
endfunc
func Test_command_count_3()
+ let bufnr = bufnr('%')
se nohidden
e aaa
let buf_aaa = bufnr('%')
@@ -145,7 +147,7 @@ func Test_command_count_3()
let buf_bbb = bufnr('%')
e ccc
let buf_ccc = bufnr('%')
- buf 1
+ exe bufnr . 'buf'
call assert_equal([1, 1, 1], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)])
exe buf_bbb . "," . buf_ccc . "bdelete"
call assert_equal([1, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)])
@@ -155,7 +157,7 @@ endfunc
func Test_command_count_4()
%argd
- let bufnr = bufnr('$') + 1
+ let bufnr = bufnr('$')
arga aa bb cc dd ee ff
3argu
let args = []
@@ -171,6 +173,8 @@ func Test_command_count_4()
only!
exe bufnr . 'buf'
+ bnext
+ let bufnr = bufnr('%')
let buffers = []
.,$-bufdo call add(buffers, bufnr('%'))
call assert_equal([bufnr, bufnr + 1, bufnr + 2, bufnr + 3, bufnr + 4], buffers)
diff --git a/src/nvim/testdir/test_curswant.vim b/src/nvim/testdir/test_curswant.vim
new file mode 100644
index 0000000000..e54cd4b280
--- /dev/null
+++ b/src/nvim/testdir/test_curswant.vim
@@ -0,0 +1,23 @@
+" Tests for curswant not changing when setting an option
+
+func Test_curswant()
+ new
+ call append(0, ['1234567890', '12345'])
+
+ normal! ggf8j
+ call assert_equal(7, winsaveview().curswant)
+ let &tabstop=&tabstop
+ call assert_equal(4, winsaveview().curswant)
+
+ normal! ggf8j
+ call assert_equal(7, winsaveview().curswant)
+ let &timeoutlen=&timeoutlen
+ call assert_equal(7, winsaveview().curswant)
+
+ normal! ggf8j
+ call assert_equal(7, winsaveview().curswant)
+ let &ttimeoutlen=&ttimeoutlen
+ call assert_equal(7, winsaveview().curswant)
+
+ enew!
+endfunc
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 5de394de8e..d95b29759e 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -198,23 +198,366 @@ func Test_diffget_diffput()
call assert_fails('diffget', 'E101:')
windo diffoff
- bwipe!
- bwipe!
- enew!
+ %bwipe!
+endfunc
+
+func Test_dp_do_buffer()
+ e! one
+ let bn1=bufnr('%')
+ let l = range(60)
+ call setline(1, l)
+ diffthis
+
+ new two
+ let l[10] = 'one'
+ let l[20] = 'two'
+ let l[30] = 'three'
+ let l[40] = 'four'
+ let l[50] = 'five'
+ call setline(1, l)
+ diffthis
+
+ " dp and do with invalid buffer number.
+ 11
+ call assert_fails('norm 99999dp', 'E102:')
+ call assert_fails('norm 99999do', 'E102:')
+ call assert_fails('diffput non_existing_buffer', 'E94:')
+ call assert_fails('diffget non_existing_buffer', 'E94:')
+
+ " dp and do with valid buffer number.
+ call assert_equal('one', getline('.'))
+ exe 'norm ' . bn1 . 'do'
+ call assert_equal('10', getline('.'))
+ 21
+ call assert_equal('two', getline('.'))
+ diffget one
+ call assert_equal('20', getline('.'))
+
+ 31
+ exe 'norm ' . bn1 . 'dp'
+ 41
+ diffput one
+ wincmd w
+ 31
+ call assert_equal('three', getline('.'))
+ 41
+ call assert_equal('four', getline('.'))
+
+ " dp and do with buffer number which is not in diff mode.
+ new not_in_diff_mode
+ let bn3=bufnr('%')
+ wincmd w
+ 51
+ call assert_fails('exe "norm" . bn3 . "dp"', 'E103:')
+ call assert_fails('exe "norm" . bn3 . "do"', 'E103:')
+ call assert_fails('diffput not_in_diff_mode', 'E94:')
+ call assert_fails('diffget not_in_diff_mode', 'E94:')
+
+ windo diffoff
+ %bwipe!
endfunc
func Test_diffoff()
enew!
call setline(1, ['Two', 'Three'])
+ redraw
let normattr = screenattr(1, 1)
diffthis
botright vert new
call setline(1, ['One', '', 'Two', 'Three'])
diffthis
redraw
+ call assert_notequal(normattr, screenattr(1, 1))
+ diffoff!
+ redraw
+ call assert_equal(normattr, screenattr(1, 1))
+ bwipe!
+ bwipe!
+endfunc
+
+func Test_diffopt_icase()
+ set diffopt=icase,foldcolumn:0
+
+ e one
+ call setline(1, ['One', 'Two', 'Three', 'Four'])
+ redraw
+ let normattr = screenattr(1, 1)
+ diffthis
+
+ botright vert new two
+ call setline(1, ['one', 'TWO', 'Three ', 'Four'])
+ diffthis
+
+ redraw
+ call assert_equal(normattr, screenattr(1, 1))
+ call assert_equal(normattr, screenattr(2, 1))
+ call assert_notequal(normattr, screenattr(3, 1))
+ call assert_equal(normattr, screenattr(4, 1))
+
diffoff!
+ %bwipe!
+ set diffopt&
+endfunc
+
+func Test_diffopt_iwhite()
+ set diffopt=iwhite,foldcolumn:0
+
+ e one
+ " Difference in trailing spaces should be ignored,
+ " but not other space differences.
+ call setline(1, ["One \t", 'Two', 'Three', 'Four'])
+ redraw
+ let normattr = screenattr(1, 1)
+ diffthis
+
+ botright vert new two
+ call setline(1, ["One\t ", "Two\t ", 'Three', ' Four'])
+ diffthis
+
redraw
call assert_equal(normattr, screenattr(1, 1))
+ call assert_equal(normattr, screenattr(2, 1))
+ call assert_equal(normattr, screenattr(3, 1))
+ call assert_notequal(normattr, screenattr(4, 1))
+
+ diffoff!
+ %bwipe!
+ set diffopt&
+endfunc
+
+func Test_diffopt_context()
+ enew!
+ call setline(1, ['1', '2', '3', '4', '5', '6', '7'])
+ diffthis
+ new
+ call setline(1, ['1', '2', '3', '4', '5x', '6', '7'])
+ diffthis
+
+ set diffopt=context:2
+ call assert_equal('+-- 2 lines: 1', foldtextresult(1))
+ set diffopt=context:1
+ call assert_equal('+-- 3 lines: 1', foldtextresult(1))
+
+ diffoff!
+ %bwipe!
+ set diffopt&
+endfunc
+
+func Test_diffopt_horizontal()
+ set diffopt=horizontal
+ diffsplit
+
+ call assert_equal(&columns, winwidth(1))
+ call assert_equal(&columns, winwidth(2))
+ call assert_equal(&lines, winheight(1) + winheight(2) + 3)
+ call assert_inrange(0, 1, winheight(1) - winheight(2))
+
+ set diffopt&
+ diffoff!
+ %bwipe
+endfunc
+
+func Test_diffopt_vertical()
+ set diffopt=vertical
+ diffsplit
+
+ call assert_equal(&lines - 2, winheight(1))
+ call assert_equal(&lines - 2, winheight(2))
+ call assert_equal(&columns, winwidth(1) + winwidth(2) + 1)
+ call assert_inrange(0, 1, winwidth(1) - winwidth(2))
+
+ set diffopt&
+ diffoff!
+ %bwipe
+endfunc
+
+func Test_diffoff_hidden()
+ set diffopt=filler,foldcolumn:0
+ e! one
+ call setline(1, ['Two', 'Three'])
+ redraw
+ let normattr = screenattr(1, 1)
+ diffthis
+ botright vert new two
+ call setline(1, ['One', 'Four'])
+ diffthis
+ redraw
+ call assert_notequal(normattr, screenattr(1, 1))
+ set hidden
+ close
+ redraw
+ " diffing with hidden buffer two
+ call assert_notequal(normattr, screenattr(1, 1))
+ diffoff
+ redraw
+ call assert_equal(normattr, screenattr(1, 1))
+ diffthis
+ redraw
+ " still diffing with hidden buffer two
+ call assert_notequal(normattr, screenattr(1, 1))
+ diffoff!
+ redraw
+ call assert_equal(normattr, screenattr(1, 1))
+ diffthis
+ redraw
+ " no longer diffing with hidden buffer two
+ call assert_equal(normattr, screenattr(1, 1))
+
+ bwipe!
+ bwipe!
+ set hidden& diffopt&
+endfunc
+
+func Test_setting_cursor()
+ new Xtest1
+ put =range(1,90)
+ wq
+ new Xtest2
+ put =range(1,100)
+ wq
+
+ tabe Xtest2
+ $
+ diffsp Xtest1
+ tabclose
+
+ call delete('Xtest1')
+ call delete('Xtest2')
+endfunc
+
+func Test_diff_move_to()
+ new
+ call setline(1, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+ diffthis
+ vnew
+ call setline(1, [1, '2x', 3, 4, 4, 5, '6x', 7, '8x', 9, '10x'])
+ diffthis
+ norm ]c
+ call assert_equal(2, line('.'))
+ norm 3]c
+ call assert_equal(9, line('.'))
+ norm 10]c
+ call assert_equal(11, line('.'))
+ norm [c
+ call assert_equal(9, line('.'))
+ norm 2[c
+ call assert_equal(5, line('.'))
+ norm 10[c
+ call assert_equal(2, line('.'))
+ %bwipe!
+endfunc
+
+func Test_diffexpr()
+ if !executable('diff')
+ return
+ endif
+
+ func DiffExpr()
+ silent exe '!diff ' . v:fname_in . ' ' . v:fname_new . '>' . v:fname_out
+ endfunc
+ set diffexpr=DiffExpr()
+ set diffopt=foldcolumn:0
+
+ enew!
+ call setline(1, ['one', 'two', 'three'])
+ redraw
+ let normattr = screenattr(1, 1)
+ diffthis
+
+ botright vert new
+ call setline(1, ['one', 'two', 'three.'])
+ diffthis
+
+ redraw
+ call assert_equal(normattr, screenattr(1, 1))
+ call assert_equal(normattr, screenattr(2, 1))
+ call assert_notequal(normattr, screenattr(3, 1))
+
+ diffoff!
+ %bwipe!
+ set diffexpr& diffopt&
+endfunc
+
+func Test_diffpatch()
+ " The patch program on MS-Windows may fail or hang.
+ if !executable('patch') || !has('unix')
+ return
+ endif
+ new
+ insert
+***************
+*** 1,3 ****
+ 1
+! 2
+ 3
+--- 1,4 ----
+ 1
+! 2x
+ 3
++ 4
+.
+ saveas Xpatch
+ bwipe!
+ new
+ call assert_fails('diffpatch Xpatch', 'E816:')
+
+ for name in ['Xpatch', 'Xpatch$HOME', 'Xpa''tch']
+ call setline(1, ['1', '2', '3'])
+ if name != 'Xpatch'
+ call rename('Xpatch', name)
+ endif
+ exe 'diffpatch ' . escape(name, '$')
+ call assert_equal(['1', '2x', '3', '4'], getline(1, '$'))
+ if name != 'Xpatch'
+ call rename(name, 'Xpatch')
+ endif
+ bwipe!
+ endfor
+
+ call delete('Xpatch')
+ bwipe!
+endfunc
+
+func Test_diff_too_many_buffers()
+ for i in range(1, 8)
+ exe "new Xtest" . i
+ diffthis
+ endfor
+ new Xtest9
+ call assert_fails('diffthis', 'E96:')
+ %bwipe!
+endfunc
+
+func Test_diff_nomodifiable()
+ new
+ call setline(1, [1, 2, 3, 4])
+ setl nomodifiable
+ diffthis
+ vnew
+ call setline(1, ['1x', 2, 3, 3, 4])
+ diffthis
+ call assert_fails('norm dp', 'E793:')
+ setl nomodifiable
+ call assert_fails('norm do', 'E21:')
+ %bwipe!
+endfunc
+
+func Test_diff_lastline()
+ enew!
+ only!
+ call setline(1, ['This is a ', 'line with five ', 'rows'])
+ diffthis
+ botright vert new
+ call setline(1, ['This is', 'a line with ', 'four rows'])
+ diffthis
+ 1
+ call feedkeys("Je a\<CR>", 'tx')
+ call feedkeys("Je a\<CR>", 'tx')
+ let w1lines = winline()
+ wincmd w
+ $
+ let w2lines = winline()
+ call assert_equal(w2lines, w1lines)
bwipe!
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
new file mode 100644
index 0000000000..0ed672d577
--- /dev/null
+++ b/src/nvim/testdir/test_display.vim
@@ -0,0 +1,71 @@
+" Test for displaying stuff
+
+" Nvim: `:set term` is not supported.
+" if !has('gui_running') && has('unix')
+" set term=ansi
+" endif
+
+source view_util.vim
+
+func! Test_display_foldcolumn()
+ if !has("folding")
+ return
+ endif
+ new
+ vnew
+ vert resize 25
+ call assert_equal(25, winwidth(winnr()))
+ set isprint=@
+
+ 1put='e more noise blah blah‚ more stuff here'
+
+ let expect = [
+ \ "e more noise blah blah<82",
+ \ "> more stuff here "
+ \ ]
+
+ call cursor(2, 1)
+ norm! zt
+ let lines=ScreenLines([1,2], winwidth(0))
+ call assert_equal(expect, lines)
+ set fdc=2
+ let lines=ScreenLines([1,2], winwidth(0))
+ let expect = [
+ \ " e more noise blah blah<",
+ \ " 82> more stuff here "
+ \ ]
+ call assert_equal(expect, lines)
+
+ quit!
+ quit!
+endfunc
+
+func! Test_display_foldtext_mbyte()
+ if !has("folding") || !has("multi_byte")
+ return
+ endif
+ call NewWindow(10, 40)
+ call append(0, range(1,20))
+ exe "set foldmethod=manual foldtext=foldtext() fillchars=fold:\u2500,vert:\u2502 fdc=2"
+ call cursor(2, 1)
+ norm! zf13G
+ let lines=ScreenLines([1,3], winwidth(0)+1)
+ let expect=[
+ \ " 1 \u2502",
+ \ "+ +-- 12 lines: 2". repeat("\u2500", 23). "\u2502",
+ \ " 14 \u2502",
+ \ ]
+ call assert_equal(expect, lines)
+
+ set fillchars=fold:-,vert:\|
+ let lines=ScreenLines([1,3], winwidth(0)+1)
+ let expect=[
+ \ " 1 |",
+ \ "+ +-- 12 lines: 2". repeat("-", 23). "|",
+ \ " 14 |",
+ \ ]
+ call assert_equal(expect, lines)
+
+ set foldtext& fillchars& foldmethod& fdc&
+ bw!
+endfunc
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
new file mode 100644
index 0000000000..8f815478c2
--- /dev/null
+++ b/src/nvim/testdir/test_edit.vim
@@ -0,0 +1,1325 @@
+" Test for edit functions
+"
+if exists("+t_kD")
+ let &t_kD="[3;*~"
+endif
+
+" Needed for testing basic rightleft: Test_edit_rightleft
+source view_util.vim
+
+" Needs to come first until the bug in getchar() is
+" fixed: https://groups.google.com/d/msg/vim_dev/fXL9yme4H4c/bOR-U6_bAQAJ
+func! Test_edit_00b()
+ new
+ call setline(1, ['abc '])
+ inoreabbr <buffer> h here some more
+ call cursor(1, 4)
+ " <c-l> expands the abbreviation and ends insertmode
+ call feedkeys(":set im\<cr> h\<c-l>:set noim\<cr>", 'tix')
+ call assert_equal(['abc here some more '], getline(1,'$'))
+ iunabbr <buffer> h
+ bw!
+endfunc
+
+func! Test_edit_01()
+ " set for Travis CI?
+ " set nocp noesckeys
+ new
+ " 1) empty buffer
+ call assert_equal([''], getline(1,'$'))
+ " 2) delete in an empty line
+ call feedkeys("i\<del>\<esc>", 'tnix')
+ call assert_equal([''], getline(1,'$'))
+ %d
+ " 3) delete one character
+ call setline(1, 'a')
+ call feedkeys("i\<del>\<esc>", 'tnix')
+ call assert_equal([''], getline(1,'$'))
+ %d
+ " 4) delete a multibyte character
+ if has("multi_byte")
+ call setline(1, "\u0401")
+ call feedkeys("i\<del>\<esc>", 'tnix')
+ call assert_equal([''], getline(1,'$'))
+ %d
+ endif
+ " 5.1) delete linebreak with 'bs' option containing eol
+ let _bs=&bs
+ set bs=eol
+ call setline(1, ["abc def", "ghi jkl"])
+ call cursor(1, 1)
+ call feedkeys("A\<del>\<esc>", 'tnix')
+ call assert_equal(['abc defghi jkl'], getline(1, 2))
+ %d
+ " 5.2) delete linebreak with backspace option w/out eol
+ set bs=
+ call setline(1, ["abc def", "ghi jkl"])
+ call cursor(1, 1)
+ call feedkeys("A\<del>\<esc>", 'tnix')
+ call assert_equal(["abc def", "ghi jkl"], getline(1, 2))
+ let &bs=_bs
+ bw!
+endfunc
+
+func! Test_edit_02()
+ " Change cursor position in InsertCharPre command
+ new
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ fu! DoIt(...)
+ call cursor(1, 4)
+ if len(a:000)
+ let v:char=a:1
+ endif
+ endfu
+ au InsertCharPre <buffer> :call DoIt('y')
+ call feedkeys("ix\<esc>", 'tnix')
+ call assert_equal(['abcy'], getline(1, '$'))
+ " Setting <Enter> in InsertCharPre
+ au! InsertCharPre <buffer> :call DoIt("\n")
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ call feedkeys("ix\<esc>", 'tnix')
+ call assert_equal(['abc', ''], getline(1, '$'))
+ %d
+ au! InsertCharPre
+ " Change cursor position in InsertEnter command
+ " 1) when setting v:char, keeps changed cursor position
+ au! InsertEnter <buffer> :call DoIt('y')
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ call feedkeys("ix\<esc>", 'tnix')
+ call assert_equal(['abxc'], getline(1, '$'))
+ " 2) when not setting v:char, restores changed cursor position
+ au! InsertEnter <buffer> :call DoIt()
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ call feedkeys("ix\<esc>", 'tnix')
+ call assert_equal(['xabc'], getline(1, '$'))
+ au! InsertEnter
+ delfu DoIt
+ bw!
+endfunc
+
+func! Test_edit_03()
+ " Change cursor after <c-o> command to end of line
+ new
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ call feedkeys("i\<c-o>$y\<esc>", 'tnix')
+ call assert_equal(['abcy'], getline(1, '$'))
+ %d
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ call feedkeys("i\<c-o>80|y\<esc>", 'tnix')
+ call assert_equal(['abcy'], getline(1, '$'))
+ %d
+ call setline(1, 'abc')
+ call feedkeys("Ad\<c-o>:s/$/efg/\<cr>hij", 'tnix')
+ call assert_equal(['hijabcdefg'], getline(1, '$'))
+ bw!
+endfunc
+
+func! Test_edit_04()
+ " test for :stopinsert
+ new
+ call setline(1, 'abc')
+ call cursor(1, 1)
+ call feedkeys("i\<c-o>:stopinsert\<cr>$", 'tnix')
+ call feedkeys("aX\<esc>", 'tnix')
+ call assert_equal(['abcX'], getline(1, '$'))
+ %d
+ bw!
+endfunc
+
+func! Test_edit_05()
+ " test for folds being opened
+ new
+ call setline(1, ['abcX', 'abcX', 'zzzZ'])
+ call cursor(1, 1)
+ set foldmethod=manual foldopen+=insert
+ " create fold for those two lines
+ norm! Vjzf
+ call feedkeys("$ay\<esc>", 'tnix')
+ call assert_equal(['abcXy', 'abcX', 'zzzZ'], getline(1, '$'))
+ %d
+ call setline(1, ['abcX', 'abcX', 'zzzZ'])
+ call cursor(1, 1)
+ set foldmethod=manual foldopen-=insert
+ " create fold for those two lines
+ norm! Vjzf
+ call feedkeys("$ay\<esc>", 'tnix')
+ call assert_equal(['abcXy', 'abcX', 'zzzZ'], getline(1, '$'))
+ %d
+ bw!
+endfunc
+
+func! Test_edit_06()
+ " Test in diff mode
+ if !has("diff") || !executable("diff")
+ return
+ endif
+ new
+ call setline(1, ['abc', 'xxx', 'yyy'])
+ vnew
+ call setline(1, ['abc', 'zzz', 'xxx', 'yyy'])
+ wincmd p
+ diffthis
+ wincmd p
+ diffthis
+ wincmd p
+ call cursor(2, 1)
+ norm! zt
+ call feedkeys("Ozzz\<esc>", 'tnix')
+ call assert_equal(['abc', 'zzz', 'xxx', 'yyy'], getline(1,'$'))
+ bw!
+ bw!
+endfunc
+
+func! Test_edit_07()
+ " 1) Test with completion <c-l> when popupmenu is visible
+ new
+ call setline(1, 'J')
+
+ func! ListMonths()
+ call complete(col('.')-1, ['January', 'February', 'March',
+ \ 'April', 'May', 'June', 'July', 'August', 'September',
+ \ 'October', 'November', 'December'])
+ return ''
+ endfunc
+ inoremap <buffer> <F5> <C-R>=ListMonths()<CR>
+
+ call feedkeys("A\<f5>\<c-p>". repeat("\<down>", 6)."\<c-l>\<down>\<c-l>\<cr>", 'tx')
+ call assert_equal(['July'], getline(1,'$'))
+ " 1) Test completion when InsertCharPre kicks in
+ %d
+ call setline(1, 'J')
+ fu! DoIt()
+ if v:char=='u'
+ let v:char='an'
+ endif
+ endfu
+ au InsertCharPre <buffer> :call DoIt()
+ call feedkeys("A\<f5>\<c-p>u\<cr>\<c-l>\<cr>", 'tx')
+ call assert_equal(["Jan\<c-l>",''], getline(1,'$'))
+ %d
+ call setline(1, 'J')
+ call feedkeys("A\<f5>\<c-p>u\<down>\<c-l>\<cr>", 'tx')
+ call assert_equal(["January"], getline(1,'$'))
+
+ delfu ListMonths
+ delfu DoIt
+ iunmap <buffer> <f5>
+ bw!
+endfunc
+
+func! Test_edit_08()
+ throw 'skipped: moved to test/functional/legacy/edit_spec.lua'
+ " reset insertmode from i_ctrl-r_=
+ let g:bufnr = bufnr('%')
+ new
+ call setline(1, ['abc'])
+ call cursor(1, 4)
+ call feedkeys(":set im\<cr>ZZZ\<c-r>=setbufvar(g:bufnr,'&im', 0)\<cr>",'tnix')
+ call assert_equal(['abZZZc'], getline(1,'$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call assert_false(0, '&im')
+ bw!
+ unlet g:bufnr
+endfunc
+
+func! Test_edit_09()
+ " test i_CTRL-\ combinations
+ new
+ call setline(1, ['abc', 'def', 'ghi'])
+ call cursor(1, 1)
+ " 1) CTRL-\ CTLR-N
+ call feedkeys(":set im\<cr>\<c-\>\<c-n>ccABC\<c-l>", 'txin')
+ call assert_equal(['ABC', 'def', 'ghi'], getline(1,'$'))
+ call setline(1, ['ABC', 'def', 'ghi'])
+ " 2) CTRL-\ CTLR-G
+ call feedkeys("j0\<c-\>\<c-g>ZZZ\<cr>\<c-l>", 'txin')
+ call assert_equal(['ABC', 'ZZZ', 'def', 'ghi'], getline(1,'$'))
+ call feedkeys("I\<c-\>\<c-g>YYY\<c-l>", 'txin')
+ call assert_equal(['ABC', 'ZZZ', 'YYYdef', 'ghi'], getline(1,'$'))
+ set noinsertmode
+ " 3) CTRL-\ CTRL-O
+ call setline(1, ['ABC', 'ZZZ', 'def', 'ghi'])
+ call cursor(1, 1)
+ call feedkeys("A\<c-o>ix", 'txin')
+ call assert_equal(['ABxC', 'ZZZ', 'def', 'ghi'], getline(1,'$'))
+ call feedkeys("A\<c-\>\<c-o>ix", 'txin')
+ call assert_equal(['ABxCx', 'ZZZ', 'def', 'ghi'], getline(1,'$'))
+ " 4) CTRL-\ a (should be inserted literally, not special after <c-\>
+ call setline(1, ['ABC', 'ZZZ', 'def', 'ghi'])
+ call cursor(1, 1)
+ call feedkeys("A\<c-\>a", 'txin')
+ call assert_equal(["ABC\<c-\>a", 'ZZZ', 'def', 'ghi'], getline(1, '$'))
+ bw!
+endfunc
+
+func! Test_edit_10()
+ " Test for starting selectmode
+ new
+ set selectmode=key keymodel=startsel
+ call setline(1, ['abc', 'def', 'ghi'])
+ call cursor(1, 4)
+ call feedkeys("A\<s-home>start\<esc>", 'txin')
+ call assert_equal(['startdef', 'ghi'], getline(1, '$'))
+ set selectmode= keymodel=
+ bw!
+endfunc
+
+func! Test_edit_11()
+ " Test that indenting kicks in
+ new
+ set cindent
+ call setline(1, ['{', '', ''])
+ call cursor(2, 1)
+ call feedkeys("i\<c-f>int c;\<esc>", 'tnix')
+ call cursor(3, 1)
+ call feedkeys("i/* comment */", 'tnix')
+ call assert_equal(['{', "\<tab>int c;", "/* comment */"], getline(1, '$'))
+ " added changed cindentkeys slightly
+ set cindent cinkeys+=*/
+ call setline(1, ['{', '', ''])
+ call cursor(2, 1)
+ call feedkeys("i\<c-f>int c;\<esc>", 'tnix')
+ call cursor(3, 1)
+ call feedkeys("i/* comment */", 'tnix')
+ call assert_equal(['{', "\<tab>int c;", "\<tab>/* comment */"], getline(1, '$'))
+ set cindent cinkeys+==end
+ call feedkeys("oend\<cr>\<esc>", 'tnix')
+ call assert_equal(['{', "\<tab>int c;", "\<tab>/* comment */", "\tend", ''], getline(1, '$'))
+ set cinkeys-==end
+ %d
+ " Use indentexpr instead of cindenting
+ func! Do_Indent()
+ if v:lnum == 3
+ return 3*shiftwidth()
+ else
+ return 2*shiftwidth()
+ endif
+ endfunc
+ setl indentexpr=Do_Indent() indentkeys+=*/
+ call setline(1, ['{', '', ''])
+ call cursor(2, 1)
+ call feedkeys("i\<c-f>int c;\<esc>", 'tnix')
+ call cursor(3, 1)
+ call feedkeys("i/* comment */", 'tnix')
+ call assert_equal(['{', "\<tab>\<tab>int c;", "\<tab>\<tab>\<tab>/* comment */"], getline(1, '$'))
+ set cinkeys&vim indentkeys&vim
+ set nocindent indentexpr=
+ delfu Do_Indent
+ bw!
+endfunc
+
+func! Test_edit_12()
+ " Test changing indent in replace mode
+ new
+ call setline(1, ["\tabc", "\tdef"])
+ call cursor(2, 4)
+ call feedkeys("R^\<c-d>", 'tnix')
+ call assert_equal(["\tabc", "def"], getline(1, '$'))
+ call assert_equal([0, 2, 2, 0], getpos('.'))
+ %d
+ call setline(1, ["\tabc", "\t\tdef"])
+ call cursor(2, 2)
+ call feedkeys("R^\<c-d>", 'tnix')
+ call assert_equal(["\tabc", "def"], getline(1, '$'))
+ call assert_equal([0, 2, 1, 0], getpos('.'))
+ %d
+ call setline(1, ["\tabc", "\t\tdef"])
+ call cursor(2, 2)
+ call feedkeys("R\<c-t>", 'tnix')
+ call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$'))
+ call assert_equal([0, 2, 2, 0], getpos('.'))
+ bw!
+ 10vnew
+ call setline(1, ["\tabc", "\t\tdef"])
+ call cursor(2, 2)
+ call feedkeys("R\<c-t>", 'tnix')
+ call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$'))
+ call assert_equal([0, 2, 2, 0], getpos('.'))
+ %d
+ set sw=4
+ call setline(1, ["\tabc", "\t\tdef"])
+ call cursor(2, 2)
+ call feedkeys("R\<c-t>\<c-t>", 'tnix')
+ call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$'))
+ call assert_equal([0, 2, 2, 0], getpos('.'))
+ %d
+ call setline(1, ["\tabc", "\t\tdef"])
+ call cursor(2, 2)
+ call feedkeys("R\<c-t>\<c-t>", 'tnix')
+ call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$'))
+ call assert_equal([0, 2, 2, 0], getpos('.'))
+ set et
+ set sw& et&
+ %d
+ call setline(1, ["\t/*"])
+ set formatoptions=croql
+ call cursor(1, 3)
+ call feedkeys("A\<cr>\<cr>/", 'tnix')
+ call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
+ set formatoptions&
+ bw!
+endfunc
+
+func! Test_edit_13()
+ " Test smartindenting
+ if exists("+smartindent")
+ new
+ set smartindent autoindent
+ call setline(1, ["\tabc"])
+ call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix')
+ call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$'))
+ set smartindent& autoindent&
+ bw!
+ endif
+endfunc
+
+func! Test_edit_CR()
+ " Test for <CR> in insert mode
+ " basically only in quickfix mode ist tested, the rest
+ " has been taken care of by other tests
+ if !has("quickfix")
+ return
+ endif
+ botright new
+ call writefile(range(1, 10), 'Xqflist.txt')
+ call setqflist([{'filename': 'Xqflist.txt', 'lnum': 2}])
+ copen
+ set modifiable
+ call feedkeys("A\<cr>", 'tnix')
+ call assert_equal('Xqflist.txt', bufname(''))
+ call assert_equal(2, line('.'))
+ cclose
+ botright new
+ call setloclist(0, [{'filename': 'Xqflist.txt', 'lnum': 10}])
+ lopen
+ set modifiable
+ call feedkeys("A\<cr>", 'tnix')
+ call assert_equal('Xqflist.txt', bufname(''))
+ call assert_equal(10, line('.'))
+ call feedkeys("A\<Enter>", 'tnix')
+ call feedkeys("A\<kEnter>", 'tnix')
+ call feedkeys("A\n", 'tnix')
+ call feedkeys("A\r", 'tnix')
+ call assert_equal(map(range(1, 10), 'string(v:val)') + ['', '', '', ''], getline(1, '$'))
+ bw!
+ lclose
+ call delete('Xqflist.txt')
+endfunc
+
+func! Test_edit_CTRL_()
+ " disabled for Windows builds, why?
+ if !has("multi_byte") || !has("rightleft") || has("win32")
+ return
+ endif
+ let _encoding=&encoding
+ set encoding=utf-8
+ " Test for CTRL-_
+ new
+ call setline(1, ['abc'])
+ call cursor(1, 1)
+ call feedkeys("i\<c-_>xyz\<esc>", 'tnix')
+ call assert_equal(["\<C-_>xyzabc"], getline(1, '$'))
+ call assert_false(&revins)
+ set ari
+ call setline(1, ['abc'])
+ call cursor(1, 1)
+ call feedkeys("i\<c-_>xyz\<esc>", 'tnix')
+ call assert_equal(["æèñabc"], getline(1, '$'))
+ call assert_true(&revins)
+ call setline(1, ['abc'])
+ call cursor(1, 1)
+ call feedkeys("i\<c-_>xyz\<esc>", 'tnix')
+ call assert_equal(["xyzabc"], getline(1, '$'))
+ call assert_false(&revins)
+ set noari
+ let &encoding=_encoding
+ bw!
+endfunc
+
+" needs to come first, to have the @. register empty
+func! Test_edit_00a_CTRL_A()
+ " Test pressing CTRL-A
+ new
+ call setline(1, repeat([''], 5))
+ call cursor(1, 1)
+ try
+ call feedkeys("A\<NUL>", 'tnix')
+ catch /^Vim\%((\a\+)\)\=:E29/
+ call assert_true(1, 'E29 error caught')
+ endtry
+ call cursor(1, 1)
+ call feedkeys("Afoobar \<esc>", 'tnix')
+ call cursor(2, 1)
+ call feedkeys("A\<c-a>more\<esc>", 'tnix')
+ call cursor(3, 1)
+ call feedkeys("A\<NUL>and more\<esc>", 'tnix')
+ call assert_equal(['foobar ', 'foobar more', 'foobar morend more', '', ''], getline(1, '$'))
+ bw!
+endfunc
+
+func! Test_edit_CTRL_EY()
+ " Ctrl-E/ Ctrl-Y in insert mode completion to scroll
+ 10new
+ call setline(1, range(1, 100))
+ call cursor(30, 1)
+ norm! z.
+ call feedkeys("A\<c-x>\<c-e>\<c-e>\<c-e>\<c-e>\<c-e>", 'tnix')
+ call assert_equal(30, winsaveview()['topline'])
+ call assert_equal([0, 30, 2, 0], getpos('.'))
+ call feedkeys("A\<c-x>\<c-e>\<c-e>\<c-e>\<c-e>\<c-e>", 'tnix')
+ call feedkeys("A\<c-x>".repeat("\<c-y>", 10), 'tnix')
+ call assert_equal(21, winsaveview()['topline'])
+ call assert_equal([0, 30, 2, 0], getpos('.'))
+ bw!
+endfunc
+
+func! Test_edit_CTRL_G()
+ new
+ call setline(1, ['foobar', 'foobar', 'foobar'])
+ call cursor(2, 4)
+ call feedkeys("ioooooooo\<c-g>k\<c-r>.\<esc>", 'tnix')
+ call assert_equal(['foooooooooobar', 'foooooooooobar', 'foobar'], getline(1, '$'))
+ call assert_equal([0, 1, 11, 0], getpos('.'))
+ call feedkeys("i\<c-g>k\<esc>", 'tnix')
+ call assert_equal([0, 1, 10, 0], getpos('.'))
+ call cursor(2, 4)
+ call feedkeys("i\<c-g>jzzzz\<esc>", 'tnix')
+ call assert_equal(['foooooooooobar', 'foooooooooobar', 'foozzzzbar'], getline(1, '$'))
+ call assert_equal([0, 3, 7, 0], getpos('.'))
+ call feedkeys("i\<c-g>j\<esc>", 'tnix')
+ call assert_equal([0, 3, 6, 0], getpos('.'))
+ bw!
+endfunc
+
+func! Test_edit_CTRL_I()
+ " Tab in completion mode
+ let path=expand("%:p:h")
+ new
+ call setline(1, [path."/", ''])
+ call feedkeys("Arunt\<c-x>\<c-f>\<tab>\<cr>\<esc>", 'tnix')
+ call assert_match('runtest\.vim', getline(1))
+ %d
+ call writefile(['one', 'two', 'three'], 'Xinclude.txt')
+ let include='#include Xinclude.txt'
+ call setline(1, [include, ''])
+ call cursor(2, 1)
+ call feedkeys("A\<c-x>\<tab>\<cr>\<esc>", 'tnix')
+ call assert_equal([include, 'one', ''], getline(1, '$'))
+ call feedkeys("2ggC\<c-x>\<tab>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal([include, 'two', ''], getline(1, '$'))
+ call feedkeys("2ggC\<c-x>\<tab>\<down>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal([include, 'three', ''], getline(1, '$'))
+ call feedkeys("2ggC\<c-x>\<tab>\<down>\<down>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal([include, '', ''], getline(1, '$'))
+ call delete("Xinclude.txt")
+ bw!
+endfunc
+
+func! Test_edit_CTRL_K()
+ " Test pressing CTRL-K (basically only dictionary completion and digraphs
+ " the rest is already covered
+ call writefile(['A', 'AA', 'AAA', 'AAAA'], 'Xdictionary.txt')
+ set dictionary=Xdictionary.txt
+ new
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<cr>\<esc>", 'tnix')
+ call assert_equal(['AA', ''], getline(1, '$'))
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal(['AAA'], getline(1, '$'))
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal(['AAAA'], getline(1, '$'))
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal(['A'], getline(1, '$'))
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<down>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal(['AA'], getline(1, '$'))
+
+ " press an unexecpted key after dictionary completion
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<c-]>\<cr>\<esc>", 'tnix')
+ call assert_equal(['AA', ''], getline(1, '$'))
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<c-s>\<cr>\<esc>", 'tnix')
+ call assert_equal(["AA\<c-s>", ''], getline(1, '$'))
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-k>\<c-f>\<cr>\<esc>", 'tnix')
+ call assert_equal(["AA\<c-f>", ''], getline(1, '$'))
+
+ set dictionary=
+ %d
+ call setline(1, 'A')
+ call cursor(1, 1)
+ let v:testing = 1
+ try
+ call feedkeys("A\<c-x>\<c-k>\<esc>", 'tnix')
+ catch
+ " error sleeps 2 seconds, when v:testing is not set
+ let v:testing = 0
+ endtry
+ call delete('Xdictionary.txt')
+
+ if has("multi_byte") && !has("nvim")
+ call test_override("char_avail", 1)
+ set showcmd
+ %d
+ call feedkeys("A\<c-k>a:\<esc>", 'tnix')
+ call assert_equal(['ä'], getline(1, '$'))
+ call test_override("char_avail", 0)
+ set noshowcmd
+ endif
+ bw!
+endfunc
+
+func! Test_edit_CTRL_L()
+ " Test Ctrl-X Ctrl-L (line completion)
+ new
+ set complete=.
+ call setline(1, ['one', 'two', 'three', '', '', '', ''])
+ call cursor(4, 1)
+ call feedkeys("A\<c-x>\<c-l>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 'three', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-n>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 't', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-n>\<c-n>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 't', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-n>\<c-n>\<c-n>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 'two', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-n>\<c-n>\<c-n>\<c-n>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 'three', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-p>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 'two', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-p>\<c-p>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 't', '', '', ''], getline(1, '$'))
+ call feedkeys("cct\<c-x>\<c-l>\<c-p>\<c-p>\<c-p>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 'three', '', '', ''], getline(1, '$'))
+ set complete=
+ call cursor(5, 1)
+ call feedkeys("A\<c-x>\<c-l>\<c-p>\<c-n>\<esc>", 'tnix')
+ call assert_equal(['one', 'two', 'three', 'three', "\<c-l>\<c-p>\<c-n>", '', ''], getline(1, '$'))
+ set complete&
+ %d
+ if has("conceal") && has("syntax") && !has("nvim")
+ call setline(1, ['foo', 'bar', 'foobar'])
+ call test_override("char_avail", 1)
+ set conceallevel=2 concealcursor=n
+ syn on
+ syn match ErrorMsg "^bar"
+ call matchadd("Conceal", 'oo', 10, -1, {'conceal': 'X'})
+ func! DoIt()
+ let g:change=1
+ endfunc
+ au! TextChangedI <buffer> :call DoIt()
+
+ call cursor(2, 1)
+ call assert_false(exists("g:change"))
+ call feedkeys("A \<esc>", 'tnix')
+ call assert_equal(['foo', 'bar ', 'foobar'], getline(1, '$'))
+ call assert_equal(1, g:change)
+
+ call test_override("char_avail", 0)
+ call clearmatches()
+ syn off
+ au! TextChangedI
+ delfu DoIt
+ unlet! g:change
+ endif
+ bw!
+endfunc
+
+func! Test_edit_CTRL_N()
+ " Check keyword completion
+ new
+ set complete=.
+ call setline(1, ['INFER', 'loWER', '', '', ])
+ call cursor(3, 1)
+ call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix")
+ call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$'))
+ %d
+ call setline(1, ['INFER', 'loWER', '', '', ])
+ call cursor(3, 1)
+ set ignorecase infercase
+ call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix")
+ call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$'))
+
+ set noignorecase noinfercase complete&
+ bw!
+endfunc
+
+func! Test_edit_CTRL_O()
+ " Check for CTRL-O in insert mode
+ new
+ inoreabbr <buffer> h here some more
+ call setline(1, ['abc', 'def'])
+ call cursor(1, 1)
+ " Ctrl-O after an abbreviation
+ exe "norm A h\<c-o>:set nu\<cr> text"
+ call assert_equal(['abc here some more text', 'def'], getline(1, '$'))
+ call assert_true(&nu)
+ set nonu
+ iunabbr <buffer> h
+ " Ctrl-O at end of line with 've'=onemore
+ call cursor(1, 1)
+ call feedkeys("A\<c-o>:let g:a=getpos('.')\<cr>\<esc>", 'tnix')
+ call assert_equal([0, 1, 23, 0], g:a)
+ call cursor(1, 1)
+ set ve=onemore
+ call feedkeys("A\<c-o>:let g:a=getpos('.')\<cr>\<esc>", 'tnix')
+ call assert_equal([0, 1, 24, 0], g:a)
+ set ve=
+ unlet! g:a
+ bw!
+endfunc
+
+func! Test_edit_CTRL_R()
+ throw 'skipped: Nvim does not support test_override()'
+ " Insert Register
+ new
+ call test_override("ALL", 1)
+ set showcmd
+ call feedkeys("AFOOBAR eins zwei\<esc>", 'tnix')
+ call feedkeys("O\<c-r>.", 'tnix')
+ call feedkeys("O\<c-r>=10*500\<cr>\<esc>", 'tnix')
+ call feedkeys("O\<c-r>=getreg('=', 1)\<cr>\<esc>", 'tnix')
+ call assert_equal(["getreg('=', 1)", '5000', "FOOBAR eins zwei", "FOOBAR eins zwei"], getline(1, '$'))
+ call test_override("ALL", 0)
+ set noshowcmd
+ bw!
+endfunc
+
+func! Test_edit_CTRL_S()
+ " Test pressing CTRL-S (basically only spellfile completion)
+ " the rest is already covered
+ new
+ if !has("spell")
+ call setline(1, 'vim')
+ call feedkeys("A\<c-x>ss\<cr>\<esc>", 'tnix')
+ call assert_equal(['vims', ''], getline(1, '$'))
+ bw!
+ return
+ endif
+ call setline(1, 'vim')
+ " spell option not yet set
+ try
+ call feedkeys("A\<c-x>\<c-s>\<cr>\<esc>", 'tnix')
+ catch /^Vim\%((\a\+)\)\=:E756/
+ call assert_true(1, 'error caught')
+ endtry
+ call assert_equal(['vim', ''], getline(1, '$'))
+ %d
+ setl spell spelllang=en
+ call setline(1, 'vim')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-s>\<cr>\<esc>", 'tnix')
+ call assert_equal(['Vim', ''], getline(1, '$'))
+ %d
+ call setline(1, 'vim')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-s>\<down>\<cr>\<esc>", 'tnix')
+ call assert_equal(['Aim'], getline(1, '$'))
+ %d
+ call setline(1, 'vim')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-s>\<c-p>\<cr>\<esc>", 'tnix')
+ call assert_equal(['vim', ''], getline(1, '$'))
+ %d
+ " empty buffer
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-s>\<c-p>\<cr>\<esc>", 'tnix')
+ call assert_equal(['', ''], getline(1, '$'))
+ setl nospell
+ bw!
+endfunc
+
+func! Test_edit_CTRL_T()
+ " Check for CTRL-T and CTRL-X CTRL-T in insert mode
+ " 1) increase indent
+ new
+ call setline(1, "abc")
+ call cursor(1, 1)
+ call feedkeys("A\<c-t>xyz", 'tnix')
+ call assert_equal(["\<tab>abcxyz"], getline(1, '$'))
+ " 2) also when paste option is set
+ set paste
+ call setline(1, "abc")
+ call cursor(1, 1)
+ call feedkeys("A\<c-t>xyz", 'tnix')
+ call assert_equal(["\<tab>abcxyz"], getline(1, '$'))
+ set nopaste
+ " CTRL-X CTRL-T (thesaurus complete)
+ call writefile(['angry furious mad enraged'], 'Xthesaurus')
+ set thesaurus=Xthesaurus
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<cr>\<esc>", 'tnix')
+ call assert_equal(['mad', ''], getline(1, '$'))
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['angry', ''], getline(1, '$'))
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['furious', ''], getline(1, '$'))
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['enraged', ''], getline(1, '$'))
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<c-n>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['mad', ''], getline(1, '$'))
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<c-n>\<c-n>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['mad', ''], getline(1, '$'))
+ " Using <c-p> <c-n> when 'complete' is empty
+ set complete=
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['angry', ''], getline(1, '$'))
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<c-p>\<cr>\<esc>", 'tnix')
+ call assert_equal(['mad', ''], getline(1, '$'))
+ set complete&
+
+ set thesaurus=
+ %d
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ let v:testing = 1
+ try
+ call feedkeys("A\<c-x>\<c-t>\<esc>", 'tnix')
+ catch
+ " error sleeps 2 seconds, when v:testing is not set
+ let v:testing = 0
+ endtry
+ call assert_equal(['mad'], getline(1, '$'))
+ call delete('Xthesaurus')
+ bw!
+endfunc
+
+func! Test_edit_CTRL_U()
+ " Test 'completefunc'
+ new
+ " -1, -2 and -3 are special return values
+ let g:special=0
+ fun! CompleteMonths(findstart, base)
+ if a:findstart
+ " locate the start of the word
+ return g:special
+ else
+ " find months matching with "a:base"
+ let res = []
+ for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
+ if m =~ '^\c'.a:base
+ call add(res, {'word': m, 'abbr': m.' Month', 'icase': 0})
+ endif
+ endfor
+ return {'words': res, 'refresh': 'always'}
+ endif
+ endfun
+ set completefunc=CompleteMonths
+ call setline(1, ['', ''])
+ call cursor(1, 1)
+ call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix')
+ call assert_equal(['X', '', ''], getline(1, '$'))
+ %d
+ let g:special=-1
+ call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix')
+ call assert_equal(['XJan', ''], getline(1, '$'))
+ %d
+ let g:special=-2
+ call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix')
+ call assert_equal(['X', ''], getline(1, '$'))
+ %d
+ let g:special=-3
+ call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix')
+ call assert_equal(['X', ''], getline(1, '$'))
+ %d
+ let g:special=0
+ call feedkeys("AM\<c-x>\<c-u>\<cr>\<esc>", 'tnix')
+ call assert_equal(['Mar', ''], getline(1, '$'))
+ %d
+ call feedkeys("AM\<c-x>\<c-u>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['May', ''], getline(1, '$'))
+ %d
+ call feedkeys("AM\<c-x>\<c-u>\<c-n>\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['M', ''], getline(1, '$'))
+ delfu CompleteMonths
+ %d
+ try
+ call feedkeys("A\<c-x>\<c-u>", 'tnix')
+ call assert_fails(1, 'unknown completion function')
+ catch /^Vim\%((\a\+)\)\=:E117/
+ call assert_true(1, 'E117 error caught')
+ endtry
+ set completefunc=
+ bw!
+endfunc
+
+func! Test_edit_CTRL_Z()
+ " Ctrl-Z when insertmode is not set inserts it literally
+ new
+ call setline(1, 'abc')
+ call feedkeys("A\<c-z>\<esc>", 'tnix')
+ call assert_equal(["abc\<c-z>"], getline(1,'$'))
+ bw!
+ " TODO: How to Test Ctrl-Z in insert mode, e.g. suspend?
+endfunc
+
+func! Test_edit_DROP()
+ if !has("dnd")
+ return
+ endif
+ new
+ call setline(1, ['abc def ghi'])
+ call cursor(1, 1)
+ try
+ call feedkeys("i\<Drop>\<Esc>", 'tnix')
+ call assert_fails(1, 'Invalid register name')
+ catch /^Vim\%((\a\+)\)\=:E353/
+ call assert_true(1, 'error caught')
+ endtry
+ bw!
+endfunc
+
+func! Test_edit_CTRL_V()
+ throw 'skipped: Nvim does not support test_override()'
+ if has("ebcdic")
+ return
+ endif
+ new
+ call setline(1, ['abc'])
+ call cursor(2, 1)
+ " force some redraws
+ set showmode showcmd
+ "call test_override_char_avail(1)
+ call test_override('ALL', 1)
+ call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
+ call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
+
+ if has("rightleft") && exists("+rl")
+ set rl
+ call setline(1, ['abc'])
+ call cursor(2, 1)
+ call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
+ call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
+ set norl
+ endif
+
+ call test_override('ALL', 0)
+ set noshowmode showcmd
+ bw!
+endfunc
+
+func! Test_edit_F1()
+ " Pressing <f1>
+ new
+ call feedkeys(":set im\<cr>\<f1>\<c-l>", 'tnix')
+ set noinsertmode
+ call assert_equal('help', &buftype)
+ bw
+ bw
+endfunc
+
+func! Test_edit_F21()
+ " Pressing <f21>
+ " sends a netbeans command
+ if has("netbeans_intg")
+ new
+ " I have no idea what this is supposed to do :)
+ call feedkeys("A\<F21>\<F1>\<esc>", 'tnix')
+ bw
+ endif
+endfunc
+
+func! Test_edit_HOME_END()
+ " Test Home/End Keys
+ new
+ set foldopen+=hor
+ call setline(1, ['abc', 'def'])
+ call cursor(1, 1)
+ call feedkeys("AX\<Home>Y\<esc>", 'tnix')
+ call cursor(2, 1)
+ call feedkeys("iZ\<End>Y\<esc>", 'tnix')
+ call assert_equal(['YabcX', 'ZdefY'], getline(1, '$'))
+
+ set foldopen-=hor
+ bw!
+endfunc
+
+func! Test_edit_INS()
+ " Test for Pressing <Insert>
+ new
+ call setline(1, ['abc', 'def'])
+ call cursor(1, 1)
+ call feedkeys("i\<Insert>ZYX>", 'tnix')
+ call assert_equal(['ZYX>', 'def'], getline(1, '$'))
+ call setline(1, ['abc', 'def'])
+ call cursor(1, 1)
+ call feedkeys("i\<Insert>Z\<Insert>YX>", 'tnix')
+ call assert_equal(['ZYX>bc', 'def'], getline(1, '$'))
+ bw!
+endfunc
+
+func! Test_edit_LEFT_RIGHT()
+ " Left, Shift-Left, Right, Shift-Right
+ new
+ call setline(1, ['abc def ghi', 'ABC DEF GHI', 'ZZZ YYY XXX'])
+ let _ww=&ww
+ set ww=
+ call cursor(2, 1)
+ call feedkeys("i\<left>\<esc>", 'tnix')
+ call assert_equal([0, 2, 1, 0], getpos('.'))
+ " Is this a bug, <s-left> does not respect whichwrap option
+ call feedkeys("i\<s-left>\<esc>", 'tnix')
+ call assert_equal([0, 1, 8, 0], getpos('.'))
+ call feedkeys("i". repeat("\<s-left>", 3). "\<esc>", 'tnix')
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call feedkeys("i\<right>\<esc>", 'tnix')
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call feedkeys("i\<right>\<right>\<esc>", 'tnix')
+ call assert_equal([0, 1, 2, 0], getpos('.'))
+ call feedkeys("A\<right>\<esc>", 'tnix')
+ call assert_equal([0, 1, 11, 0], getpos('.'))
+ call feedkeys("A\<s-right>\<esc>", 'tnix')
+ call assert_equal([0, 2, 1, 0], getpos('.'))
+ call feedkeys("i\<s-right>\<esc>", 'tnix')
+ call assert_equal([0, 2, 4, 0], getpos('.'))
+ call cursor(3, 11)
+ call feedkeys("A\<right>\<esc>", 'tnix')
+ call feedkeys("A\<s-right>\<esc>", 'tnix')
+ call assert_equal([0, 3, 11, 0], getpos('.'))
+ call cursor(2, 11)
+ " <S-Right> does not respect 'whichwrap' option
+ call feedkeys("A\<s-right>\<esc>", 'tnix')
+ call assert_equal([0, 3, 1, 0], getpos('.'))
+ " Check motion when 'whichwrap' contains cursor keys for insert mode
+ set ww+=[,]
+ call cursor(2, 1)
+ call feedkeys("i\<left>\<esc>", 'tnix')
+ call assert_equal([0, 1, 11, 0], getpos('.'))
+ call cursor(2, 11)
+ call feedkeys("A\<right>\<esc>", 'tnix')
+ call assert_equal([0, 3, 1, 0], getpos('.'))
+ call cursor(2, 11)
+ call feedkeys("A\<s-right>\<esc>", 'tnix')
+ call assert_equal([0, 3, 1, 0], getpos('.'))
+ let &ww = _ww
+ bw!
+endfunc
+
+func! Test_edit_MOUSE()
+ " This is a simple test, since we not really using the mouse here
+ if !has("mouse")
+ return
+ endif
+ 10new
+ call setline(1, range(1, 100))
+ call cursor(1, 1)
+ set mouse=a
+ call feedkeys("A\<ScrollWheelDown>\<esc>", 'tnix')
+ call assert_equal([0, 4, 1, 0], getpos('.'))
+ " This should move by one pageDown, but only moves
+ " by one line when the test is run...
+ call feedkeys("A\<S-ScrollWheelDown>\<esc>", 'tnix')
+ call assert_equal([0, 5, 1, 0], getpos('.'))
+ set nostartofline
+ call feedkeys("A\<C-ScrollWheelDown>\<esc>", 'tnix')
+ call assert_equal([0, 6, 1, 0], getpos('.'))
+ call feedkeys("A\<LeftMouse>\<esc>", 'tnix')
+ call assert_equal([0, 6, 1, 0], getpos('.'))
+ call feedkeys("A\<RightMouse>\<esc>", 'tnix')
+ call assert_equal([0, 6, 1, 0], getpos('.'))
+ call cursor(1, 100)
+ norm! zt
+ " this should move by a screen up, but when the test
+ " is run, it moves up to the top of the buffer...
+ call feedkeys("A\<ScrollWheelUp>\<esc>", 'tnix')
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call cursor(1, 30)
+ norm! zt
+ call feedkeys("A\<S-ScrollWheelUp>\<esc>", 'tnix')
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call cursor(1, 30)
+ norm! zt
+ call feedkeys("A\<C-ScrollWheelUp>\<esc>", 'tnix')
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ %d
+ call setline(1, repeat(["12345678901234567890"], 100))
+ call cursor(2, 1)
+ call feedkeys("A\<ScrollWheelRight>\<esc>", 'tnix')
+ call assert_equal([0, 2, 20, 0], getpos('.'))
+ call feedkeys("A\<ScrollWheelLeft>\<esc>", 'tnix')
+ call assert_equal([0, 2, 20, 0], getpos('.'))
+ call feedkeys("A\<S-ScrollWheelRight>\<esc>", 'tnix')
+ call assert_equal([0, 2, 20, 0], getpos('.'))
+ call feedkeys("A\<S-ScrollWheelLeft>\<esc>", 'tnix')
+ call assert_equal([0, 2, 20, 0], getpos('.'))
+ call feedkeys("A\<C-ScrollWheelRight>\<esc>", 'tnix')
+ call assert_equal([0, 2, 20, 0], getpos('.'))
+ call feedkeys("A\<C-ScrollWheelLeft>\<esc>", 'tnix')
+ call assert_equal([0, 2, 20, 0], getpos('.'))
+ set mouse& startofline
+ bw!
+endfunc
+
+func! Test_edit_PAGEUP_PAGEDOWN()
+ 10new
+ call setline(1, repeat(['abc def ghi'], 30))
+ call cursor(1, 1)
+ call feedkeys("i\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 9, 1, 0], getpos('.'))
+ call feedkeys("i\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 17, 1, 0], getpos('.'))
+ call feedkeys("i\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 25, 1, 0], getpos('.'))
+ call feedkeys("i\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 30, 1, 0], getpos('.'))
+ call feedkeys("i\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 30, 1, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 29, 1, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 21, 1, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 13, 1, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 5, 1, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 5, 11, 0], getpos('.'))
+ " <S-Up> is the same as <PageUp>
+ " <S-Down> is the same as <PageDown>
+ call cursor(1, 1)
+ call feedkeys("i\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 9, 1, 0], getpos('.'))
+ call feedkeys("i\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 17, 1, 0], getpos('.'))
+ call feedkeys("i\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 25, 1, 0], getpos('.'))
+ call feedkeys("i\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 30, 1, 0], getpos('.'))
+ call feedkeys("i\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 30, 1, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 29, 1, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 21, 1, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 13, 1, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 5, 1, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 5, 11, 0], getpos('.'))
+ set nostartofline
+ call cursor(30, 11)
+ norm! zt
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 29, 11, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 21, 11, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 13, 11, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 5, 11, 0], getpos('.'))
+ call feedkeys("A\<PageUp>\<esc>", 'tnix')
+ call assert_equal([0, 5, 11, 0], getpos('.'))
+ call cursor(1, 1)
+ call feedkeys("A\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 9, 11, 0], getpos('.'))
+ call feedkeys("A\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 17, 11, 0], getpos('.'))
+ call feedkeys("A\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 25, 11, 0], getpos('.'))
+ call feedkeys("A\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 30, 11, 0], getpos('.'))
+ call feedkeys("A\<PageDown>\<esc>", 'tnix')
+ call assert_equal([0, 30, 11, 0], getpos('.'))
+ " <S-Up> is the same as <PageUp>
+ " <S-Down> is the same as <PageDown>
+ call cursor(30, 11)
+ norm! zt
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 29, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 21, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 13, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 5, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Up>\<esc>", 'tnix')
+ call assert_equal([0, 5, 11, 0], getpos('.'))
+ call cursor(1, 1)
+ call feedkeys("A\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 9, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 17, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 25, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 30, 11, 0], getpos('.'))
+ call feedkeys("A\<S-Down>\<esc>", 'tnix')
+ call assert_equal([0, 30, 11, 0], getpos('.'))
+ bw!
+endfunc
+
+func! Test_edit_forbidden()
+ new
+ " 1) edit in the sandbox is not allowed
+ call setline(1, 'a')
+ com! Sandbox :sandbox call feedkeys("i\<del>\<esc>", 'tnix')
+ call assert_fails(':Sandbox', 'E48:')
+ com! Sandbox :sandbox exe "norm! i\<del>"
+ call assert_fails(':Sandbox', 'E48:')
+ delcom Sandbox
+ call assert_equal(['a'], getline(1,'$'))
+ " 2) edit with textlock set
+ fu! DoIt()
+ call feedkeys("i\<del>\<esc>", 'tnix')
+ endfu
+ au InsertCharPre <buffer> :call DoIt()
+ try
+ call feedkeys("ix\<esc>", 'tnix')
+ call assert_fails(1, 'textlock')
+ catch /^Vim\%((\a\+)\)\=:E523/ " catch E523: not allowed here
+ endtry
+ " TODO: Might be a bug: should x really be inserted here
+ call assert_equal(['xa'], getline(1, '$'))
+ delfu DoIt
+ try
+ call feedkeys("ix\<esc>", 'tnix')
+ call assert_fails(1, 'unknown function')
+ catch /^Vim\%((\a\+)\)\=:E117/ " catch E117: unknown function
+ endtry
+ au! InsertCharPre
+ " 3) edit when completion is shown
+ fun! Complete(findstart, base)
+ if a:findstart
+ return col('.')
+ else
+ call feedkeys("i\<del>\<esc>", 'tnix')
+ return []
+ endif
+ endfun
+ set completefunc=Complete
+ try
+ call feedkeys("i\<c-x>\<c-u>\<esc>", 'tnix')
+ call assert_fails(1, 'change in complete function')
+ catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
+ endtry
+ delfu Complete
+ set completefunc=
+ if has("rightleft") && exists("+fkmap")
+ " 4) 'R' when 'fkmap' and 'revins' is set.
+ set revins fkmap
+ try
+ normal Ri
+ call assert_fails(1, "R with 'fkmap' and 'ri' set")
+ catch
+ finally
+ set norevins nofkmap
+ endtry
+ endif
+ bw!
+endfunc
+
+func! Test_edit_rightleft()
+ " Cursor in rightleft mode moves differently
+ if !exists("+rightleft")
+ return
+ endif
+ call NewWindow(10, 20)
+ call setline(1, ['abc', 'def', 'ghi'])
+ call cursor(1, 2)
+ set rightleft
+ " Screen looks as expected
+ let lines = ScreenLines([1, 4], winwidth(0))
+ let expect = [
+ \" cba",
+ \" fed",
+ \" ihg",
+ \" ~"]
+ call assert_equal(join(expect, "\n"), join(lines, "\n"))
+ " 2) right moves to the left
+ call feedkeys("i\<right>\<esc>x", 'txin')
+ call assert_equal(['bc', 'def', 'ghi'], getline(1,'$'))
+ call cursor(1, 2)
+ call feedkeys("i\<s-right>\<esc>", 'txin')
+ call cursor(1, 2)
+ call feedkeys("i\<c-right>\<esc>", 'txin')
+ " Screen looks as expected
+ let lines = ScreenLines([1, 4], winwidth(0))
+ let expect = [
+ \" cb",
+ \" fed",
+ \" ihg",
+ \" ~"]
+ call assert_equal(join(expect, "\n"), join(lines, "\n"))
+ " 2) left moves to the right
+ call setline(1, ['abc', 'def', 'ghi'])
+ call cursor(1, 2)
+ call feedkeys("i\<left>\<esc>x", 'txin')
+ call assert_equal(['ac', 'def', 'ghi'], getline(1,'$'))
+ call cursor(1, 2)
+ call feedkeys("i\<s-left>\<esc>", 'txin')
+ call cursor(1, 2)
+ call feedkeys("i\<c-left>\<esc>", 'txin')
+ " Screen looks as expected
+ let lines = ScreenLines([1, 4], winwidth(0))
+ let expect = [
+ \" ca",
+ \" fed",
+ \" ihg",
+ \" ~"]
+ call assert_equal(join(expect, "\n"), join(lines, "\n"))
+ set norightleft
+ bw!
+endfunc
+
+func Test_edit_quit()
+ edit foo.txt
+ split
+ new
+ call setline(1, 'hello')
+ 3wincmd w
+ redraw!
+ call assert_fails('1q', 'E37:')
+ bwipe! foo.txt
+ only
+endfunc
+
diff --git a/src/nvim/testdir/test_erasebackword.vim b/src/nvim/testdir/test_erasebackword.vim
new file mode 100644
index 0000000000..098d6edfcb
--- /dev/null
+++ b/src/nvim/testdir/test_erasebackword.vim
@@ -0,0 +1,25 @@
+
+func Test_erasebackword()
+ if !has('multi_byte')
+ return
+ endif
+
+ set encoding=utf-8
+ enew
+
+ exe "normal o wwwこんにちわ世界ワールドvim \<C-W>"
+ call assert_equal(' wwwこんにちわ世界ワールド', getline('.'))
+ exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>"
+ call assert_equal(' wwwこんにちわ世界', getline('.'))
+ exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>"
+ call assert_equal(' wwwこんにちわ', getline('.'))
+ exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>\<C-W>"
+ call assert_equal(' www', getline('.'))
+ exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>\<C-W>\<C-W>"
+ call assert_equal(' ', getline('.'))
+ exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>\<C-W>\<C-W>\<C-W>"
+ call assert_equal('', getline('.'))
+
+ enew!
+ set encoding&
+endfunc
diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim
new file mode 100644
index 0000000000..fd34be83b0
--- /dev/null
+++ b/src/nvim/testdir/test_exists.vim
@@ -0,0 +1,321 @@
+" Tests for the exists() function
+func Test_exists()
+ augroup myagroup
+ autocmd! BufEnter *.my echo "myfile edited"
+ autocmd! FuncUndefined UndefFun exec "fu UndefFun()\nendfu"
+ augroup END
+ set rtp+=./sautest
+
+ " valid autocmd group
+ call assert_equal(1, exists('#myagroup'))
+ " valid autocmd group with garbage
+ call assert_equal(0, exists('#myagroup+b'))
+ " Valid autocmd group and event
+ call assert_equal(1, exists('#myagroup#BufEnter'))
+ " Valid autocmd group, event and pattern
+ call assert_equal(1, exists('#myagroup#BufEnter#*.my'))
+ " Valid autocmd event
+ call assert_equal(1, exists('#BufEnter'))
+ " Valid autocmd event and pattern
+ call assert_equal(1, exists('#BufEnter#*.my'))
+ " Non-existing autocmd group or event
+ call assert_equal(0, exists('#xyzagroup'))
+ " Non-existing autocmd group and valid autocmd event
+ call assert_equal(0, exists('#xyzagroup#BufEnter'))
+ " Valid autocmd group and event with no matching pattern
+ call assert_equal(0, exists('#myagroup#CmdwinEnter'))
+ " Valid autocmd group and non-existing autocmd event
+ call assert_equal(0, exists('#myagroup#xyzacmd'))
+ " Valid autocmd group and event and non-matching pattern
+ call assert_equal(0, exists('#myagroup#BufEnter#xyzpat'))
+ " Valid autocmd event and non-matching pattern
+ call assert_equal(0, exists('#BufEnter#xyzpat'))
+ " Empty autocmd group, event and pattern
+ call assert_equal(0, exists('###'))
+ " Empty autocmd group and event or empty event and pattern
+ call assert_equal(0, exists('##'))
+ " Valid autocmd event
+ call assert_equal(1, exists('##FileReadCmd'))
+ " Non-existing autocmd event
+ call assert_equal(0, exists('##MySpecialCmd'))
+
+ " Existing and working option (long form)
+ call assert_equal(1, exists('&textwidth'))
+ " Existing and working option (short form)
+ call assert_equal(1, exists('&tw'))
+ " Existing and working option with garbage
+ call assert_equal(0, exists('&tw-'))
+ " Global option
+ call assert_equal(1, exists('&g:errorformat'))
+ " Local option
+ call assert_equal(1, exists('&l:errorformat'))
+ " Negative form of existing and working option (long form)
+ call assert_equal(0, exists('&nojoinspaces'))
+ " Negative form of existing and working option (short form)
+ call assert_equal(0, exists('&nojs'))
+ " Non-existing option
+ call assert_equal(0, exists('&myxyzoption'))
+
+ " Existing and working option (long form)
+ call assert_equal(1, exists('+incsearch'))
+ " Existing and working option with garbage
+ call assert_equal(0, exists('+incsearch!1'))
+ " Existing and working option (short form)
+ call assert_equal(1, exists('+is'))
+ " Existing option that is hidden.
+ call assert_equal(0, exists('+autoprint'))
+
+ " Existing environment variable
+ let $EDITOR_NAME = 'Vim Editor'
+ call assert_equal(1, exists('$EDITOR_NAME'))
+ " Non-existing environment variable
+ call assert_equal(0, exists('$NON_ENV_VAR'))
+
+ " Valid internal function
+ call assert_equal(1, exists('*bufnr'))
+ " Valid internal function with ()
+ call assert_equal(1, exists('*bufnr()'))
+ " Non-existing internal function
+ call assert_equal(0, exists('*myxyzfunc'))
+ " Valid internal function with garbage
+ call assert_equal(0, exists('*bufnr&6'))
+ " Valid user defined function
+ call assert_equal(1, exists('*Test_exists'))
+ " Non-existing user defined function
+ call assert_equal(0, exists('*MyxyzFunc'))
+ " Function that may be created by FuncUndefined event
+ call assert_equal(0, exists('*UndefFun'))
+ " Function that may be created by script autoloading
+ call assert_equal(0, exists('*footest#F'))
+
+ " Valid internal command (full match)
+ call assert_equal(2, exists(':edit'))
+ " Valid internal command (full match) with garbage
+ call assert_equal(0, exists(':edit/a'))
+ " Valid internal command (partial match)
+ call assert_equal(1, exists(':q'))
+ " Non-existing internal command
+ call assert_equal(0, exists(':invalidcmd'))
+
+ " User defined command (full match)
+ command! MyCmd :echo 'My command'
+ call assert_equal(2, exists(':MyCmd'))
+ " User defined command (partial match)
+ command! MyOtherCmd :echo 'Another command'
+ call assert_equal(3, exists(':My'))
+
+ " Command modifier
+ call assert_equal(2, exists(':rightbelow'))
+
+ " Non-existing user defined command (full match)
+ delcommand MyCmd
+ call assert_equal(0, exists(':MyCmd'))
+
+ " Non-existing user defined command (partial match)
+ delcommand MyOtherCmd
+ call assert_equal(0, exists(':My'))
+
+ " Valid local variable
+ let local_var = 1
+ call assert_equal(1, exists('local_var'))
+ " Valid local variable with garbage
+ call assert_equal(0, exists('local_var%n'))
+ " Non-existing local variable
+ unlet local_var
+ call assert_equal(0, exists('local_var'))
+
+ " Non-existing autoload variable that may be autoloaded
+ call assert_equal(0, exists('footest#x'))
+
+ " Valid local list
+ let local_list = ["blue", "orange"]
+ call assert_equal(1, exists('local_list'))
+ " Valid local list item
+ call assert_equal(1, exists('local_list[1]'))
+ " Valid local list item with garbage
+ call assert_equal(0, exists('local_list[1]+5'))
+ " Invalid local list item
+ call assert_equal(0, exists('local_list[2]'))
+ " Non-existing local list
+ unlet local_list
+ call assert_equal(0, exists('local_list'))
+ " Valid local dictionary
+ let local_dict = {"xcord":100, "ycord":2}
+ call assert_equal(1, exists('local_dict'))
+ " Non-existing local dictionary
+ unlet local_dict
+ call assert_equal(0, exists('local_dict'))
+ " Existing local curly-brace variable
+ let str = "local"
+ let curly_{str}_var = 1
+ call assert_equal(1, exists('curly_{str}_var'))
+ " Non-existing local curly-brace variable
+ unlet curly_{str}_var
+ call assert_equal(0, exists('curly_{str}_var'))
+
+ " Existing global variable
+ let g:global_var = 1
+ call assert_equal(1, exists('g:global_var'))
+ " Existing global variable with garbage
+ call assert_equal(0, exists('g:global_var-n'))
+ " Non-existing global variable
+ unlet g:global_var
+ call assert_equal(0, exists('g:global_var'))
+ " Existing global list
+ let g:global_list = ["blue", "orange"]
+ call assert_equal(1, exists('g:global_list'))
+ " Non-existing global list
+ unlet g:global_list
+ call assert_equal(0, exists('g:global_list'))
+ " Existing global dictionary
+ let g:global_dict = {"xcord":100, "ycord":2}
+ call assert_equal(1, exists('g:global_dict'))
+ " Non-existing global dictionary
+ unlet g:global_dict
+ call assert_equal(0, exists('g:global_dict'))
+ " Existing global curly-brace variable
+ let str = "global"
+ let g:curly_{str}_var = 1
+ call assert_equal(1, exists('g:curly_{str}_var'))
+ " Non-existing global curly-brace variable
+ unlet g:curly_{str}_var
+ call assert_equal(0, exists('g:curly_{str}_var'))
+
+ " Existing window variable
+ let w:window_var = 1
+ call assert_equal(1, exists('w:window_var'))
+ " Non-existing window variable
+ unlet w:window_var
+ call assert_equal(0, exists('w:window_var'))
+ " Existing window list
+ let w:window_list = ["blue", "orange"]
+ call assert_equal(1, exists('w:window_list'))
+ " Non-existing window list
+ unlet w:window_list
+ call assert_equal(0, exists('w:window_list'))
+ " Existing window dictionary
+ let w:window_dict = {"xcord":100, "ycord":2}
+ call assert_equal(1, exists('w:window_dict'))
+ " Non-existing window dictionary
+ unlet w:window_dict
+ call assert_equal(0, exists('w:window_dict'))
+ " Existing window curly-brace variable
+ let str = "window"
+ let w:curly_{str}_var = 1
+ call assert_equal(1, exists('w:curly_{str}_var'))
+ " Non-existing window curly-brace variable
+ unlet w:curly_{str}_var
+ call assert_equal(0, exists('w:curly_{str}_var'))
+
+ " Existing tab variable
+ let t:tab_var = 1
+ call assert_equal(1, exists('t:tab_var'))
+ " Non-existing tab variable
+ unlet t:tab_var
+ call assert_equal(0, exists('t:tab_var'))
+ " Existing tab list
+ let t:tab_list = ["blue", "orange"]
+ call assert_equal(1, exists('t:tab_list'))
+ " Non-existing tab list
+ unlet t:tab_list
+ call assert_equal(0, exists('t:tab_list'))
+ " Existing tab dictionary
+ let t:tab_dict = {"xcord":100, "ycord":2}
+ call assert_equal(1, exists('t:tab_dict'))
+ " Non-existing tab dictionary
+ unlet t:tab_dict
+ call assert_equal(0, exists('t:tab_dict'))
+ " Existing tab curly-brace variable
+ let str = "tab"
+ let t:curly_{str}_var = 1
+ call assert_equal(1, exists('t:curly_{str}_var'))
+ " Non-existing tab curly-brace variable
+ unlet t:curly_{str}_var
+ call assert_equal(0, exists('t:curly_{str}_var'))
+
+ " Existing buffer variable
+ let b:buffer_var = 1
+ call assert_equal(1, exists('b:buffer_var'))
+ " Non-existing buffer variable
+ unlet b:buffer_var
+ call assert_equal(0, exists('b:buffer_var'))
+ " Existing buffer list
+ let b:buffer_list = ["blue", "orange"]
+ call assert_equal(1, exists('b:buffer_list'))
+ " Non-existing buffer list
+ unlet b:buffer_list
+ call assert_equal(0, exists('b:buffer_list'))
+ " Existing buffer dictionary
+ let b:buffer_dict = {"xcord":100, "ycord":2}
+ call assert_equal(1, exists('b:buffer_dict'))
+ " Non-existing buffer dictionary
+ unlet b:buffer_dict
+ call assert_equal(0, exists('b:buffer_dict'))
+ " Existing buffer curly-brace variable
+ let str = "buffer"
+ let b:curly_{str}_var = 1
+ call assert_equal(1, exists('b:curly_{str}_var'))
+ " Non-existing buffer curly-brace variable
+ unlet b:curly_{str}_var
+ call assert_equal(0, exists('b:curly_{str}_var'))
+
+ " Existing Vim internal variable
+ call assert_equal(1, exists('v:version'))
+ " Non-existing Vim internal variable
+ call assert_equal(0, exists('v:non_exists_var'))
+
+ " Existing script-local variable
+ let s:script_var = 1
+ call assert_equal(1, exists('s:script_var'))
+ " Non-existing script-local variable
+ unlet s:script_var
+ call assert_equal(0, exists('s:script_var'))
+ " Existing script-local list
+ let s:script_list = ["blue", "orange"]
+ call assert_equal(1, exists('s:script_list'))
+ " Non-existing script-local list
+ unlet s:script_list
+ call assert_equal(0, exists('s:script_list'))
+ " Existing script-local dictionary
+ let s:script_dict = {"xcord":100, "ycord":2}
+ call assert_equal(1, exists('s:script_dict'))
+ " Non-existing script-local dictionary
+ unlet s:script_dict
+ call assert_equal(0, exists('s:script_dict'))
+ " Existing script curly-brace variable
+ let str = "script"
+ let s:curly_{str}_var = 1
+ call assert_equal(1, exists('s:curly_{str}_var'))
+ " Non-existing script-local curly-brace variable
+ unlet s:curly_{str}_var
+ call assert_equal(0, exists('s:curly_{str}_var'))
+
+ " Existing script-local function
+ function! s:my_script_func()
+ endfunction
+
+ echo '*s:my_script_func: 1'
+ call assert_equal(1, exists('*s:my_script_func'))
+
+ " Non-existing script-local function
+ delfunction s:my_script_func
+
+ call assert_equal(0, exists('*s:my_script_func'))
+ unlet str
+
+ call assert_equal(1, g:footest#x)
+ call assert_equal(0, footest#F())
+ call assert_equal(0, UndefFun())
+endfunc
+
+" exists() test for Function arguments
+func FuncArg_Tests(func_arg, ...)
+ call assert_equal(1, exists('a:func_arg'))
+ call assert_equal(0, exists('a:non_exists_arg'))
+ call assert_equal(1, exists('a:1'))
+ call assert_equal(0, exists('a:2'))
+endfunc
+
+func Test_exists_funcarg()
+ call FuncArg_Tests("arg1", "arg2")
+endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 82c5d21bd0..ad967c528c 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -384,9 +384,10 @@ func Test_substitute_expr()
\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
func Recurse()
- return substitute('yyy', 'y*', {-> g:val}, '')
+ return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
endfunc
- call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
+ " recursive call works
+ call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
endfunc
func Test_invalid_submatch()
@@ -418,6 +419,9 @@ func Test_function_with_funcref()
let s:fref = function(s:f)
call assert_equal(v:t_string, s:fref('x'))
call assert_fails("call function('s:f')", 'E700:')
+
+ call assert_fails("call function('foo()')", 'E475:')
+ call assert_fails("call function('foo()')", 'foo()')
endfunc
func Test_funcref()
@@ -435,3 +439,8 @@ func Test_funcref()
call assert_equal(2, OneByRef())
call assert_fails('echo funcref("{")', 'E475:')
endfunc
+
+func Test_empty_concatenate()
+ call assert_equal('b', 'a'[4:0] . 'b')
+ call assert_equal('b', 'b' . 'a'[4:0])
+endfunc
diff --git a/src/nvim/testdir/test_expr_utf8.vim b/src/nvim/testdir/test_expr_utf8.vim
index 9ea6d8872b..1737a9f745 100644
--- a/src/nvim/testdir/test_expr_utf8.vim
+++ b/src/nvim/testdir/test_expr_utf8.vim
@@ -3,7 +3,7 @@ if !has('multi_byte')
finish
endif
-func Test_strgetchar_utf8()
+func Test_strgetchar()
call assert_equal(char2nr('á'), strgetchar('áxb', 0))
call assert_equal(char2nr('x'), strgetchar('áxb', 1))
@@ -16,7 +16,7 @@ func Test_strgetchar_utf8()
call assert_equal(char2nr('い'), strgetchar('あaい', 2))
endfunc
-func Test_strcharpart_utf8()
+func Test_strcharpart()
call assert_equal('áxb', strcharpart('áxb', 0))
call assert_equal('á', strcharpart('áxb', 0, 1))
call assert_equal('x', strcharpart('áxb', 1, 1))
diff --git a/src/nvim/testdir/test_farsi.vim b/src/nvim/testdir/test_farsi.vim
index e094191599..9ff2653af4 100644
--- a/src/nvim/testdir/test_farsi.vim
+++ b/src/nvim/testdir/test_farsi.vim
@@ -1,4 +1,5 @@
" Simplistic testing of Farsi mode.
+" Note: must be edited with latin1 encoding.
if !has('farsi') || has('nvim') " Not supported in Nvim. #6192
finish
@@ -82,3 +83,51 @@ func Test_farsi_map()
set noaltkeymap
bwipe!
endfunc
+
+func Test_input_farsi()
+ new
+ setlocal rightleft fkmap
+ " numbers switch input direction
+ call feedkeys("aabc0123456789.+-^%#=xyz\<Esc>", 'tx')
+ call assert_equal("\x8cν\x93", getline('.'))
+
+ " all non-number special chars with spaces
+ call feedkeys("oB E F H I K L M O P Q R T U W Y ` ! @ # $ % ^ & * () - _ = + \\ | : \" . / < > ? \<Esc>", 'tx')
+ call assert_equal(" []񠢠蠨頽꠺", getline('.'))
+
+ " all non-number special chars without spaces
+ call feedkeys("oBEFHIKLMOPQRTUWY`!@#$%^&*()-_=+\\|:\"./<>?\<Esc>",'tx')
+ call assert_equal("[]񢣧訩齫꺻", getline('.'))
+
+ " all letter chars with spaces
+ call feedkeys("oa A b c C d D e f g G h i j J k l m n N o p q r s S t u v V w x X y z Z ; \ , [ ] \<Esc>", 'tx')
+ call assert_equal("Ѡ̠ΠϠƠàܠŠޠݠĠˠˠʠɠӠ٠Рؠ֠͠͠ҠԠԠנՠڠߠǠȠ", getline('.'))
+
+ " all letter chars without spaces
+ call feedkeys("oaAbcCdDefgGhijJklmnNopqrsStuvVwxXyzZ;\,[]\<Esc>", 'tx')
+ call assert_equal("\x8c\x9f\x86\x83\x9d\x85\x80\x9c\x9b\x84\x8a\x89\x8e\x96\x8b\x95\x90\x8d\x93\x97\x87\x88", getline('.'))
+
+ bwipe!
+endfunc
+
+func Test_command_line_farsi()
+ set allowrevins altkeymap
+
+ " letter characters with spaces
+ call feedkeys(":\"\<C-_>a A b c C d D e f g G h i j J k l m n N o p q r s S t u v V w x X y z Z ; \\ , [ ]\<CR>", 'tx')
+ call assert_equal("\"\x88ǠߠڠՠՠנԠԠҠ֠͠͠ؠР٠ӠɠʠˠˠĠݠޠŠܠàƠϠΠ̠", getreg(':'))
+
+ " letter characters without spaces
+ call feedkeys(":\"\<C-_>aAbcCdDefgGhijJklmnNopqrsStuvVwxXyzZ;\\,[]\<CR>", 'tx')
+ call assert_equal("\"\x88\x87\x93\x8d\x90\x95\x8b\x96\x8e\x89\x8a\x84\x9b\x9c\x80\x85\x9d\x83\x86\x9f\x8c", getreg(':'))
+
+ " other characters with spaces
+ call feedkeys(":\"\<C-_>0 1 2 3 4 5 6 7 8 9 ` . ! \" $ % ^ & / () = \\ ? + - _ * : # ~ @ < > { } | B E F H I K L M O P Q R T U W Y\<CR>", 'tx')
+ call assert_equal("\"][ }{~頭렽", getreg(':'))
+
+ " other characters without spaces
+ call feedkeys(":\"\<C-_>0123456789`.!\"$%^&/()=\\?+-_*:#~@<>{}|BEFHIKLMOPQRTUWY\<CR>", 'tx')
+ call assert_equal("\"][}{~魫뽩", getreg(':'))
+
+ set noallowrevins noaltkeymap
+endfunc
diff --git a/src/nvim/testdir/test_file_size.vim b/src/nvim/testdir/test_file_size.vim
new file mode 100644
index 0000000000..3e78a7b23c
--- /dev/null
+++ b/src/nvim/testdir/test_file_size.vim
@@ -0,0 +1,58 @@
+" Inserts 2 million lines with consecutive integers starting from 1
+" (essentially, the output of GNU's seq 1 2000000), writes them to Xtest
+" and writes its cksum to test.out.
+"
+" We need 2 million lines to trigger a call to mf_hash_grow(). If it would mess
+" up the lines the checksum would differ.
+"
+" cksum is part of POSIX and so should be available on most Unixes.
+" If it isn't available then the test will be skipped.
+func Test_File_Size()
+ if !executable('cksum')
+ return
+ endif
+
+ new
+ set fileformat=unix undolevels=-1
+ for i in range(1, 2000000, 100)
+ call append(i, range(i, i + 99))
+ endfor
+
+ 1delete
+ w! Xtest
+ let res = systemlist('cksum Xtest')[0]
+ let res = substitute(res, "\r", "", "")
+ call assert_equal('3678979763 14888896 Xtest', res)
+
+ enew!
+ call delete('Xtest')
+ set fileformat& undolevels&
+endfunc
+
+" Test for writing and reading a file of over 100 Kbyte
+func Test_File_Read_Write()
+ enew!
+
+ " Create a file with the following contents
+ " 1 line: "This is the start"
+ " 3001 lines: "This is the leader"
+ " 1 line: "This is the middle"
+ " 3001 lines: "This is the trailer"
+ " 1 line: "This is the end"
+ call append(0, "This is the start")
+ call append(1, repeat(["This is the leader"], 3001))
+ call append(3002, "This is the middle")
+ call append(3003, repeat(["This is the trailer"], 3001))
+ call append(6004, "This is the end")
+
+ write! Xtest
+ enew!
+ edit! Xtest
+
+ call assert_equal("This is the start", getline(1))
+ call assert_equal("This is the middle", getline(3003))
+ call assert_equal("This is the end", getline(6005))
+
+ enew!
+ call delete("Xtest")
+endfunc
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
new file mode 100644
index 0000000000..8dc25f62b1
--- /dev/null
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -0,0 +1,33 @@
+" Test behavior of fileformat after bwipeout of last buffer
+
+func Test_fileformat_after_bw()
+ bwipeout
+ set fileformat&
+ if &fileformat == 'dos'
+ let test_fileformats = 'unix'
+ elseif &fileformat == 'unix'
+ let test_fileformats = 'mac'
+ else " must be mac
+ let test_fileformats = 'dos'
+ endif
+ exec 'set fileformats='.test_fileformats
+ bwipeout!
+ call assert_equal(test_fileformats, &fileformat)
+ set fileformats&
+endfunc
+
+func Test_fileformat_autocommand()
+ let filecnt = ["", "foobar\<CR>", "eins\<CR>", "\<CR>", "zwei\<CR>", "drei", "vier", "fünf", ""]
+ let ffs = &ffs
+ call writefile(filecnt, 'Xfile', 'b')
+ au BufReadPre Xfile set ffs=dos ff=dos
+ new Xfile
+ call assert_equal('dos', &l:ff)
+ call assert_equal('dos', &ffs)
+
+ " cleanup
+ call delete('Xfile')
+ let &ffs = ffs
+ au! BufReadPre Xfile
+ bw!
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
new file mode 100644
index 0000000000..43dc383f3d
--- /dev/null
+++ b/src/nvim/testdir/test_filetype.vim
@@ -0,0 +1,557 @@
+" Test :setfiletype
+
+func Test_detection()
+ filetype on
+ augroup filetypedetect
+ au BufNewFile,BufRead * call assert_equal(1, did_filetype())
+ augroup END
+ new something.vim
+ call assert_equal('vim', &filetype)
+
+ bwipe!
+ filetype off
+endfunc
+
+func Test_conf_type()
+ filetype on
+ call writefile(['# some comment', 'must be conf'], 'Xfile')
+ augroup filetypedetect
+ au BufNewFile,BufRead * call assert_equal(0, did_filetype())
+ augroup END
+ split Xfile
+ call assert_equal('conf', &filetype)
+
+ bwipe!
+ call delete('Xfile')
+ filetype off
+endfunc
+
+func Test_other_type()
+ filetype on
+ augroup filetypedetect
+ au BufNewFile,BufRead * call assert_equal(0, did_filetype())
+ au BufNewFile,BufRead Xfile setf testfile
+ au BufNewFile,BufRead * call assert_equal(1, did_filetype())
+ augroup END
+ call writefile(['# some comment', 'must be conf'], 'Xfile')
+ split Xfile
+ call assert_equal('testfile', &filetype)
+
+ bwipe!
+ call delete('Xfile')
+ filetype off
+endfunc
+
+" Filetypes detected just from matching the file name.
+let s:filename_checks = {
+ \ 'a2ps': ['/etc/a2ps.cfg', '/etc/a2ps/file.cfg', 'a2psrc', '.a2psrc'],
+ \ 'a65': ['file.a65'],
+ \ 'abap': ['file.abap'],
+ \ 'abc': ['file.abc'],
+ \ 'abel': ['file.abl'],
+ \ 'acedb': ['file.wrm'],
+ \ 'ada': ['file.adb', 'file.ads', 'file.ada', 'file.gpr'],
+ \ 'ahdl': ['file.tdf'],
+ \ 'alsaconf': ['.asoundrc', '/usr/share/alsa/alsa.conf', '/etc/asound.conf'],
+ \ 'aml': ['file.aml'],
+ \ 'ampl': ['file.run'],
+ \ 'ant': ['build.xml'],
+ \ 'apache': ['.htaccess', '/etc/httpd/file.conf'],
+ \ 'applescript': ['file.scpt'],
+ \ 'aptconf': ['apt.conf', '/.aptitude/config'],
+ \ 'arch': ['.arch-inventory'],
+ \ 'arduino': ['file.ino', 'file.pde'],
+ \ 'art': ['file.art'],
+ \ 'asciidoc': ['file.asciidoc', 'file.adoc'],
+ \ 'asn': ['file.asn', 'file.asn1'],
+ \ 'atlas': ['file.atl', 'file.as'],
+ \ 'autohotkey': ['file.ahk'],
+ \ 'autoit': ['file.au3'],
+ \ 'automake': ['GNUmakefile.am'],
+ \ 'ave': ['file.ave'],
+ \ 'awk': ['file.awk'],
+ \ 'b': ['file.mch', 'file.ref', 'file.imp'],
+ \ 'bc': ['file.bc'],
+ \ 'bdf': ['file.bdf'],
+ \ 'bib': ['file.bib'],
+ \ 'bindzone': ['named.root'],
+ \ 'blank': ['file.bl'],
+ \ 'bst': ['file.bst'],
+ \ 'bzr': ['bzr_log.any'],
+ \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c'],
+ \ 'cabal': ['file.cabal'],
+ \ 'calendar': ['calendar'],
+ \ 'catalog': ['catalog'],
+ \ 'cdl': ['file.cdl'],
+ \ 'cdrdaoconf': ['/etc/cdrdao.conf', '/etc/defaults/cdrdao', '/etc/default/cdrdao', '.cdrdao'],
+ \ 'cdrtoc': ['file.toc'],
+ \ 'cf': ['file.cfm', 'file.cfi', 'file.cfc'],
+ \ 'cfengine': ['cfengine.conf'],
+ \ 'cfg': ['file.cfg', 'file.hgrc', 'filehgrc'],
+ \ 'ch': ['file.chf'],
+ \ 'chaiscript': ['file.chai'],
+ \ 'chaskell': ['file.chs'],
+ \ 'chill': ['file..ch'],
+ \ 'chordpro': ['file.chopro', 'file.crd', 'file.cho', 'file.crdpro', 'file.chordpro'],
+ \ 'cl': ['file.eni'],
+ \ 'clean': ['file.dcl', 'file.icl'],
+ \ 'clojure': ['file.clj', 'file.cljs', 'file.cljx', 'file.cljc'],
+ \ 'cmake': ['CMakeLists.txt', 'file.cmake', 'file.cmake.in'],
+ \ 'cmusrc': ['any/.cmus/autosave', 'any/.cmus/rc', 'any/.cmus/command-history', 'any/.cmus/file.theme', 'any/cmus/rc', 'any/cmus/file.theme'],
+ \ 'cobol': ['file.cbl', 'file.cob', 'file.lib'],
+ \ 'coco': ['file.atg'],
+ \ 'conaryrecipe': ['file.recipe'],
+ \ 'conf': ['auto.master'],
+ \ 'config': ['configure.in', 'configure.ac'],
+ \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi'],
+ \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'],
+ \ 'crm': ['file.crm'],
+ \ 'cs': ['file.cs'],
+ \ 'csc': ['file.csc'],
+ \ 'csdl': ['file.csdl'],
+ \ 'csp': ['file.csp', 'file.fdr'],
+ \ 'css': ['file.css'],
+ \ 'cterm': ['file.con'],
+ \ 'cucumber': ['file.feature'],
+ \ 'cuda': ['file.cu'],
+ \ 'cupl': ['file.pld'],
+ \ 'cuplsim': ['file.si'],
+ \ 'cvs': ['cvs123'],
+ \ 'cvsrc': ['.cvsrc'],
+ \ 'cynpp': ['file.cyn'],
+ \ 'datascript': ['file.ds'],
+ \ 'dcd': ['file.dcd'],
+ \ 'debcontrol': ['/debian/control'],
+ \ 'debsources': ['/etc/apt/sources.list', '/etc/apt/sources.list.d/file.list'],
+ \ 'def': ['file.def'],
+ \ 'denyhosts': ['denyhosts.conf'],
+ \ 'desc': ['file.desc'],
+ \ 'desktop': ['file.desktop', '.directory'],
+ \ 'dictconf': ['dict.conf', '.dictrc'],
+ \ 'dictdconf': ['dictd.conf'],
+ \ 'diff': ['file.diff', 'file.rej'],
+ \ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS'],
+ \ 'dnsmasq': ['/etc/dnsmasq.conf'],
+ \ 'dockerfile': ['Dockerfile', 'file.Dockerfile'],
+ \ 'dosbatch': ['file.bat', 'file.sys'],
+ \ 'dosini': ['.editorconfig', '/etc/yum.conf', 'file.ini'],
+ \ 'dot': ['file.dot'],
+ \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe'],
+ \ 'dsl': ['file.dsl'],
+ \ 'dtd': ['file.dtd'],
+ \ 'dts': ['file.dts', 'file.dtsi'],
+ \ 'dylan': ['file.dylan'],
+ \ 'dylanintr': ['file.intr'],
+ \ 'dylanlid': ['file.lid'],
+ \ 'ecd': ['file.ecd'],
+ \ 'edif': ['file.edf', 'file.edif', 'file.edo'],
+ \ 'elinks': ['/etc/elinks.conf', '/.elinks/elinks.conf'],
+ \ 'elmfilt': ['filter-rules'],
+ \ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'],
+ \ 'eruby': ['file.erb', 'file.rhtml'],
+ \ 'esmtprc': ['anyesmtprc'],
+ \ 'esqlc': ['file.ec', 'file.EC'],
+ \ 'esterel': ['file.strl'],
+ \ 'eterm': ['anyEterm/file.cfg'],
+ \ 'exim': ['exim.conf'],
+ \ 'expect': ['file.exp'],
+ \ 'exports': ['exports'],
+ \ 'factor': ['file.factor'],
+ \ 'falcon': ['file.fal'],
+ \ 'fan': ['file.fan', 'file.fwt'],
+ \ 'fetchmail': ['.fetchmailrc'],
+ \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'],
+ \ 'focexec': ['file.fex', 'file.focexec'],
+ \ 'forth': ['file.fs', 'file.ft'],
+ \ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'],
+ \ 'framescript': ['file.fsl'],
+ \ 'freebasic': ['file.fb', 'file.bi'],
+ \ 'fstab': ['fstab', 'mtab'],
+ \ 'gdb': ['.gdbinit'],
+ \ 'gdmo': ['file.mo', 'file.gdmo'],
+ \ 'gedcom': ['file.ged', 'lltxxxxx.txt'],
+ \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'],
+ \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config'],
+ \ 'gitolite': ['gitolite.conf'],
+ \ 'gitrebase': ['git-rebase-todo'],
+ \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'],
+ \ 'gkrellmrc': ['gkrellmrc', 'gkrellmrc_x'],
+ \ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
+ \ 'gnuplot': ['file.gpi'],
+ \ 'go': ['file.go'],
+ \ 'gp': ['file.gp', '.gprc'],
+ \ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel'],
+ \ 'grads': ['file.gs'],
+ \ 'gretl': ['file.gretl'],
+ \ 'groovy': ['file.gradle', 'file.groovy'],
+ \ 'group': ['any/etc/group', 'any/etc/group-', 'any/etc/group.edit', 'any/etc/gshadow', 'any/etc/gshadow-', 'any/etc/gshadow.edit', 'any/var/backups/group.bak', 'any/var/backups/gshadow.bak'],
+ \ 'grub': ['/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf'],
+ \ 'gsp': ['file.gsp'],
+ \ 'gtkrc': ['.gtkrc', 'gtkrc'],
+ \ 'haml': ['file.haml'],
+ \ 'hamster': ['file.hsc', 'file.hsm'],
+ \ 'haskell': ['file.hs', 'file.hs-boot'],
+ \ 'haste': ['file.ht'],
+ \ 'hastepreproc': ['file.htpp'],
+ \ 'hb': ['file.hb'],
+ \ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'],
+ \ 'hex': ['file.hex', 'file.h32'],
+ \ 'hgcommit': ['hg-editor-file.txt'],
+ \ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
+ \ 'hostconf': ['/etc/host.conf'],
+ \ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny'],
+ \ 'htmlcheetah': ['file.tmpl'],
+ \ 'htmlm4': ['file.html.m4'],
+ \ 'httest': ['file.htt', 'file.htb'],
+ \ 'ibasic': ['file.iba', 'file.ibi'],
+ \ 'icemenu': ['/.icewm/menu'],
+ \ 'icon': ['file.icn'],
+ \ 'indent': ['.indent.pro', 'indentrc'],
+ \ 'inform': ['file.inf', 'file.INF'],
+ \ 'initng': ['/etc/initng/any/file.i', 'file.ii'],
+ \ 'inittab': ['inittab'],
+ \ 'ipfilter': ['ipf.conf', 'ipf6.conf', 'ipf.rules'],
+ \ 'iss': ['file.iss'],
+ \ 'ist': ['file.ist', 'file.mst'],
+ \ 'j': ['file.ijs'],
+ \ 'jal': ['file.jal', 'file.JAL'],
+ \ 'jam': ['file.jpl', 'file.jpr'],
+ \ 'java': ['file.java', 'file.jav'],
+ \ 'javacc': ['file.jj', 'file.jjt'],
+ \ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.jsx', 'file.mjs'],
+ \ 'jess': ['file.clp'],
+ \ 'jgraph': ['file.jgr'],
+ \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
+ \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.webmanifest'],
+ \ 'jsp': ['file.jsp'],
+ \ 'kconfig': ['Kconfig', 'Kconfig.debug'],
+ \ 'kivy': ['file.kv'],
+ \ 'kix': ['file.kix'],
+ \ 'kscript': ['file.ks'],
+ \ 'kwt': ['file.k'],
+ \ 'lace': ['file.ace', 'file.ACE'],
+ \ 'latte': ['file.latte', 'file.lte'],
+ \ 'ld': ['file.ld'],
+ \ 'ldif': ['file.ldif'],
+ \ 'less': ['file.less'],
+ \ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'],
+ \ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc'],
+ \ 'lhaskell': ['file.lhs'],
+ \ 'libao': ['/etc/libao.conf', '/.libao'],
+ \ 'lifelines': ['file.ll'],
+ \ 'lilo': ['lilo.conf'],
+ \ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf'],
+ \ 'liquid': ['file.liquid'],
+ \ 'lisp': ['sbclrc', '.sbclrc'],
+ \ 'lite': ['file.lite', 'file.lt'],
+ \ 'litestep': ['/LiteStep/any/file.rc'],
+ \ 'loginaccess': ['/etc/login.access'],
+ \ 'logindefs': ['/etc/login.defs'],
+ \ 'logtalk': ['file.lgt'],
+ \ 'lotos': ['file.lot', 'file.lotos'],
+ \ 'lout': ['file.lou', 'file.lout'],
+ \ 'lprolog': ['file.sig'],
+ \ 'lsl': ['file.lsl'],
+ \ 'lss': ['file.lss'],
+ \ 'lua': ['file.lua', 'file.rockspec', 'file.nse'],
+ \ 'lynx': ['lynx.cfg'],
+ \ 'm4': ['file.at'],
+ \ 'mail': ['snd.123', '.letter', '.letter.123', '.followup', '.article', '.article.123', 'pico.123', 'mutt-xx-xxx', 'muttng-xx-xxx', 'ae123.txt', 'file.eml'],
+ \ 'mailaliases': ['/etc/mail/aliases', '/etc/aliases'],
+ \ 'mailcap': ['.mailcap', 'mailcap'],
+ \ 'make': ['file.mk', 'file.mak', 'file.dsp'],
+ \ 'mallard': ['file.page'],
+ \ 'manconf': ['/etc/man.conf', 'man.config'],
+ \ 'map': ['file.map'],
+ \ 'maple': ['file.mv', 'file.mpl', 'file.mws'],
+ \ 'markdown': ['file.markdown', 'file.mdown', 'file.mkd', 'file.mkdn', 'file.mdwn', 'file.md'],
+ \ 'mason': ['file.mason', 'file.mhtml', 'file.comp'],
+ \ 'master': ['file.mas', 'file.master'],
+ \ 'mel': ['file.mel'],
+ \ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user',
+ \ '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log',
+ \ '/log/auth.err', '/log/cron.err', '/log/daemon.err', '/log/debug.err', '/log/kern.err', '/log/lpr.err', '/log/mail.err', '/log/messages.err', '/log/news/news.err', '/log/syslog.err', '/log/user.err',
+ \ '/log/auth.info', '/log/cron.info', '/log/daemon.info', '/log/debug.info', '/log/kern.info', '/log/lpr.info', '/log/mail.info', '/log/messages.info', '/log/news/news.info', '/log/syslog.info', '/log/user.info',
+ \ '/log/auth.warn', '/log/cron.warn', '/log/daemon.warn', '/log/debug.warn', '/log/kern.warn', '/log/lpr.warn', '/log/mail.warn', '/log/messages.warn', '/log/news/news.warn', '/log/syslog.warn', '/log/user.warn',
+ \ '/log/auth.crit', '/log/cron.crit', '/log/daemon.crit', '/log/debug.crit', '/log/kern.crit', '/log/lpr.crit', '/log/mail.crit', '/log/messages.crit', '/log/news/news.crit', '/log/syslog.crit', '/log/user.crit',
+ \ '/log/auth.notice', '/log/cron.notice', '/log/daemon.notice', '/log/debug.notice', '/log/kern.notice', '/log/lpr.notice', '/log/mail.notice', '/log/messages.notice', '/log/news/news.notice', '/log/syslog.notice', '/log/user.notice'],
+ \ 'mf': ['file.mf'],
+ \ 'mgl': ['file.mgl'],
+ \ 'mgp': ['file.mgp'],
+ \ 'mib': ['file.mib', 'file.my'],
+ \ 'mix': ['file.mix', 'file.mixal'],
+ \ 'mma': ['file.nb'],
+ \ 'mmp': ['file.mmp'],
+ \ 'modconf': ['/etc/modules.conf', '/etc/modules', '/etc/conf.modules'],
+ \ 'modula2': ['file.m2', 'file.mi'],
+ \ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'],
+ \ 'moo': ['file.moo'],
+ \ 'mp': ['file.mp'],
+ \ 'mplayerconf': ['mplayer.conf', '/.mplayer/config'],
+ \ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'],
+ \ 'msidl': ['file.odl', 'file.mof'],
+ \ 'msql': ['file.msql'],
+ \ 'mupad': ['file.mu'],
+ \ 'mush': ['file.mush'],
+ \ 'muttrc': ['Muttngrc', 'Muttrc'],
+ \ 'mysql': ['file.mysql'],
+ \ 'n1ql': ['file.n1ql', 'file.nql'],
+ \ 'named': ['namedfile.conf', 'rndcfile.conf'],
+ \ 'nanorc': ['/etc/nanorc', 'file.nanorc'],
+ \ 'ncf': ['file.ncf'],
+ \ 'netrc': ['.netrc'],
+ \ 'ninja': ['file.ninja'],
+ \ 'nqc': ['file.nqc'],
+ \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom'],
+ \ 'nsis': ['file.nsi', 'file.nsh'],
+ \ 'obj': ['file.obj'],
+ \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit'],
+ \ 'occam': ['file.occ'],
+ \ 'omnimark': ['file.xom', 'file.xin'],
+ \ 'openroad': ['file.or'],
+ \ 'ora': ['file.ora'],
+ \ 'pamconf': ['/etc/pam.conf'],
+ \ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'],
+ \ 'pascal': ['file.pas', 'file.dpr'],
+ \ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'],
+ \ 'pccts': ['file.g'],
+ \ 'pdf': ['file.pdf'],
+ \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'],
+ \ 'perl6': ['file.p6', 'file.pm6', 'file.pl6'],
+ \ 'pf': ['pf.conf'],
+ \ 'pfmain': ['main.cf'],
+ \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp'],
+ \ 'pike': ['file.pike', 'file.lpc', 'file.ulpc', 'file.pmod'],
+ \ 'pilrc': ['file.rcp'],
+ \ 'pine': ['.pinerc', 'pinerc', '.pinercex', 'pinercex'],
+ \ 'pinfo': ['/etc/pinforc', '/.pinforc'],
+ \ 'pli': ['file.pli', 'file.pl1'],
+ \ 'plm': ['file.plm', 'file.p36', 'file.pac'],
+ \ 'plp': ['file.plp'],
+ \ 'plsql': ['file.pls', 'file.plsql'],
+ \ 'po': ['file.po', 'file.pot'],
+ \ 'pod': ['file.pod'],
+ \ 'pod6': ['file.pod6'],
+ \ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'],
+ \ 'pov': ['file.pov'],
+ \ 'povini': ['.povrayrc'],
+ \ 'ppd': ['file.ppd'],
+ \ 'ppwiz': ['file.it', 'file.ih'],
+ \ 'privoxy': ['file.action'],
+ \ 'proc': ['file.pc'],
+ \ 'procmail': ['.procmail', '.procmailrc'],
+ \ 'prolog': ['file.pdb'],
+ \ 'promela': ['file.pml'],
+ \ 'proto': ['file.proto'],
+ \ 'protocols': ['/etc/protocols'],
+ \ 'psf': ['file.psf'],
+ \ 'pyrex': ['file.pyx', 'file.pxd'],
+ \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl'],
+ \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg'],
+ \ 'radiance': ['file.rad', 'file.mat'],
+ \ 'ratpoison': ['.ratpoisonrc', 'ratpoisonrc'],
+ \ 'rc': ['file.rc', 'file.rch'],
+ \ 'rcs': ['file,v'],
+ \ 'readline': ['.inputrc', 'inputrc'],
+ \ 'remind': ['.reminders', 'file.remind', 'file.rem'],
+ \ 'resolv': ['resolv.conf'],
+ \ 'reva': ['file.frt'],
+ \ 'rexx': ['file.rex', 'file.orx', 'file.rxo', 'file.rxj', 'file.jrexx', 'file.rexxj', 'file.rexx', 'file.testGroup', 'file.testUnit'],
+ \ 'rib': ['file.rib'],
+ \ 'rnc': ['file.rnc'],
+ \ 'rng': ['file.rng'],
+ \ 'robots': ['robots.txt'],
+ \ 'rpcgen': ['file.x'],
+ \ 'rpl': ['file.rpl'],
+ \ 'rst': ['file.rst'],
+ \ 'rtf': ['file.rtf'],
+ \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake'],
+ \ 'rust': ['file.rs'],
+ \ 'samba': ['smb.conf'],
+ \ 'sas': ['file.sas'],
+ \ 'sass': ['file.sass'],
+ \ 'sather': ['file.sa'],
+ \ 'sbt': ['file.sbt'],
+ \ 'scala': ['file.scala'],
+ \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'],
+ \ 'scilab': ['file.sci', 'file.sce'],
+ \ 'screen': ['.screenrc', 'screenrc'],
+ \ 'scss': ['file.scss'],
+ \ 'sd': ['file.sd'],
+ \ 'sdc': ['file.sdc'],
+ \ 'sdl': ['file.sdl', 'file.pr'],
+ \ 'sed': ['file.sed'],
+ \ 'sensors': ['/etc/sensors.conf', '/etc/sensors3.conf'],
+ \ 'services': ['/etc/services'],
+ \ 'setserial': ['/etc/serial.conf'],
+ \ 'sh': ['/etc/udev/cdsymlinks.conf'],
+ \ 'sieve': ['file.siv'],
+ \ 'simula': ['file.sim'],
+ \ 'sinda': ['file.sin', 'file.s85'],
+ \ 'sisu': ['file.sst', 'file.ssm', 'file.ssi', 'file.-sst', 'file._sst', 'file.sst.meta', 'file.-sst.meta', 'file._sst.meta'],
+ \ 'skill': ['file.il', 'file.ils', 'file.cdf'],
+ \ 'slang': ['file.sl'],
+ \ 'slice': ['file.ice'],
+ \ 'slpconf': ['/etc/slp.conf'],
+ \ 'slpreg': ['/etc/slp.reg'],
+ \ 'slpspi': ['/etc/slp.spi'],
+ \ 'slrnrc': ['.slrnrc'],
+ \ 'slrnsc': ['file.score'],
+ \ 'sm': ['sendmail.cf'],
+ \ 'smarty': ['file.tpl'],
+ \ 'smcl': ['file.hlp', 'file.ihlp', 'file.smcl'],
+ \ 'smith': ['file.smt', 'file.smith'],
+ \ 'sml': ['file.sml'],
+ \ 'snobol4': ['file.sno', 'file.spt'],
+ \ 'spec': ['file.spec'],
+ \ 'spice': ['file.sp', 'file.spice'],
+ \ 'spup': ['file.speedup', 'file.spdata', 'file.spd'],
+ \ 'spyce': ['file.spy', 'file.spi'],
+ \ 'sql': ['file.tyb', 'file.typ', 'file.tyc', 'file.pkb', 'file.pks'],
+ \ 'sqlj': ['file.sqlj'],
+ \ 'sqr': ['file.sqr', 'file.sqi'],
+ \ 'squid': ['squid.conf'],
+ \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'],
+ \ 'sshconfig': ['ssh_config', '/.ssh/config'],
+ \ 'sshdconfig': ['sshd_config'],
+ \ 'st': ['file.st'],
+ \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'],
+ \ 'stp': ['file.stp'],
+ \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp'],
+ \ 'svg': ['file.svg'],
+ \ 'svn': ['svn-commitfile.tmp'],
+ \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf'],
+ \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.mount', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer'],
+ \ 'systemverilog': ['file.sv', 'file.svh'],
+ \ 'tags': ['tags'],
+ \ 'tak': ['file.tak'],
+ \ 'taskdata': ['pending.data', 'completed.data', 'undo.data'],
+ \ 'taskedit': ['file.task'],
+ \ 'tcl': ['file.tcl', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl'],
+ \ 'teraterm': ['file.ttl'],
+ \ 'terminfo': ['file.ti'],
+ \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'],
+ \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'],
+ \ 'texmf': ['texmf.cnf'],
+ \ 'text': ['file.text', 'README'],
+ \ 'tf': ['file.tf', '.tfrc', 'tfrc'],
+ \ 'tidy': ['.tidyrc', 'tidyrc'],
+ \ 'tilde': ['file.t.html'],
+ \ 'tli': ['file.tli'],
+ \ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'],
+ \ 'tpp': ['file.tpp'],
+ \ 'treetop': ['file.treetop'],
+ \ 'trustees': ['trustees.conf'],
+ \ 'tsalt': ['file.slt'],
+ \ 'tsscl': ['file.tsscl'],
+ \ 'tssgm': ['file.tssgm'],
+ \ 'tssop': ['file.tssop'],
+ \ 'twig': ['file.twig'],
+ \ 'uc': ['file.uc'],
+ \ 'udevconf': ['/etc/udev/udev.conf'],
+ \ 'udevperm': ['/etc/udev/permissions.d/file.permissions'],
+ \ 'uil': ['file.uit', 'file.uil'],
+ \ 'updatedb': ['/etc/updatedb.conf'],
+ \ 'upstart': ['/usr/share/upstart/file.conf', '/usr/share/upstart/file.override', '/etc/init/file.conf', '/etc/init/file.override', '/.init/file.conf', '/.init/file.override', '/.config/upstart/file.conf', '/.config/upstart/file.override'],
+ \ 'upstreamdat': ['upstream.dat', 'UPSTREAM.DAT', 'upstream.file.dat', 'UPSTREAM.FILE.DAT', 'file.upstream.dat', 'FILE.UPSTREAM.DAT'],
+ \ 'upstreaminstalllog': ['upstreaminstall.log', 'UPSTREAMINSTALL.LOG', 'upstreaminstall.file.log', 'UPSTREAMINSTALL.FILE.LOG', 'file.upstreaminstall.log', 'FILE.UPSTREAMINSTALL.LOG'],
+ \ 'upstreamlog': ['fdrupstream.log', 'upstream.log', 'UPSTREAM.LOG', 'upstream.file.log', 'UPSTREAM.FILE.LOG', 'file.upstream.log', 'FILE.UPSTREAM.LOG', 'UPSTREAM-file.log', 'UPSTREAM-FILE.LOG'],
+ \ 'usserverlog': ['usserver.log', 'USSERVER.LOG', 'usserver.file.log', 'USSERVER.FILE.LOG', 'file.usserver.log', 'FILE.USSERVER.LOG'],
+ \ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'],
+ \ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'],
+ \ 'vera': ['file.vr', 'file.vri', 'file.vrh'],
+ \ 'verilog': ['file.v'],
+ \ 'verilogams': ['file.va', 'file.vams'],
+ \ 'vgrindefs': ['vgrindefs'],
+ \ 'vhdl': ['file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst'],
+ \ 'vim': ['file.vim', 'file.vba', '.exrc', '_exrc'],
+ \ 'viminfo': ['.viminfo', '_viminfo'],
+ \ 'vmasm': ['file.mar'],
+ \ 'voscm': ['file.cm'],
+ \ 'vrml': ['file.wrl'],
+ \ 'vroom': ['file.vroom'],
+ \ 'webmacro': ['file.wm'],
+ \ 'wget': ['.wgetrc', 'wgetrc'],
+ \ 'winbatch': ['file.wbt'],
+ \ 'wml': ['file.wml'],
+ \ 'wsml': ['file.wsml'],
+ \ 'wvdial': ['wvdial.conf', '.wvdialrc'],
+ \ 'xdefaults': ['.Xdefaults', '.Xpdefaults', '.Xresources', 'xdm-config', 'file.ad'],
+ \ 'xhtml': ['file.xhtml', 'file.xht'],
+ \ 'xinetd': ['/etc/xinetd.conf'],
+ \ 'xmath': ['file.msc', 'file.msf'],
+ \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ts', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul'],
+ \ 'xmodmap': ['anyXmodmap'],
+ \ 'xf86conf': ['xorg.conf', 'xorg.conf-4'],
+ \ 'xpm2': ['file.xpm2'],
+ \ 'xquery': ['file.xq', 'file.xql', 'file.xqm', 'file.xquery', 'file.xqy'],
+ \ 'xs': ['file.xs'],
+ \ 'xsd': ['file.xsd'],
+ \ 'xslt': ['file.xsl', 'file.xslt'],
+ \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
+ \ 'yaml': ['file.yaml', 'file.yml'],
+ \ 'z8a': ['file.z8a'],
+ \ 'zimbu': ['file.zu'],
+ \ 'zimbutempl': ['file.zut'],
+ \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh'],
+ \
+ \ 'aap': ['file.aap'],
+ \ 'help': [$VIMRUNTIME . '/doc/help.txt'],
+ \ 'xpm': ['file.xpm'],
+ \ }
+
+let s:filename_case_checks = {
+ \ 'modula2': ['file.DEF', 'file.MOD'],
+ \ }
+
+func CheckItems(checks)
+ for [ft, names] in items(a:checks)
+ for i in range(0, len(names) - 1)
+ new
+ try
+ exe 'edit ' . names[i]
+ catch
+ call assert_report('cannot edit "' . names[i] . '": ' . v:errmsg)
+ endtry
+ call assert_equal(ft, &filetype, 'with file name: ' . names[i])
+ bwipe!
+ endfor
+ endfor
+endfunc
+
+func Test_filetype_detection()
+ filetype on
+ call CheckItems(s:filename_checks)
+ if has('fname_case')
+ call CheckItems(s:filename_case_checks)
+ endif
+ filetype off
+endfunc
+
+" Filetypes detected from the file contents by scripts.vim
+let s:script_checks = {
+ \ 'virata': [['% Virata'],
+ \ ['', '% Virata'],
+ \ ['', '', '% Virata'],
+ \ ['', '', '', '% Virata'],
+ \ ['', '', '', '', '% Virata']],
+ \ 'strace': [['execve("/usr/bin/pstree", ["pstree"], 0x7ff0 /* 63 vars */) = 0'],
+ \ ['15:17:47 execve("/usr/bin/pstree", ["pstree"], ... "_=/usr/bin/strace"]) = 0'],
+ \ ['__libc_start_main and something']],
+ \ }
+
+func Test_script_detection()
+ filetype on
+ for [ft, files] in items(s:script_checks)
+ for file in files
+ call writefile(file, 'Xtest')
+ split Xtest
+ call assert_equal(ft, &filetype, 'for text: ' . string(file))
+ bwipe!
+ endfor
+ endfor
+ call delete('Xtest')
+ filetype off
+endfunc
+
diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim
new file mode 100644
index 0000000000..4732109ed0
--- /dev/null
+++ b/src/nvim/testdir/test_find_complete.vim
@@ -0,0 +1,157 @@
+" Tests for the 'find' command completion.
+
+" Do all the tests in a separate window to avoid E211 when we recursively
+" delete the Xfind directory during cleanup
+func Test_find_complete()
+ set belloff=all
+
+ " On windows a stale "Xfind" directory may exist, remove it so that
+ " we start from a clean state.
+ call delete("Xfind", "rf")
+ let cwd = getcwd()
+ let test_out = cwd . '/test.out'
+ call mkdir('Xfind')
+ cd Xfind
+
+ new
+ set path=
+ call assert_fails('call feedkeys(":find\t\n", "xt")', 'E345:')
+ close
+
+ new
+ set path=.
+ call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:')
+ close
+
+ new
+ set path=.,,
+ call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:')
+ close
+
+ new
+ set path=./**
+ call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:')
+ close
+
+ " We shouldn't find any file till this point
+
+ call mkdir('in/path', 'p')
+ exe 'cd ' . cwd
+ call writefile(['Holy Grail'], 'Xfind/file.txt')
+ call writefile(['Jimmy Hoffa'], 'Xfind/in/file.txt')
+ call writefile(['Another Holy Grail'], 'Xfind/in/stuff.txt')
+ call writefile(['E.T.'], 'Xfind/in/path/file.txt')
+
+ new
+ set path=Xfind/**
+ call feedkeys(":find file\t\n", "xt")
+ call assert_equal('Holy Grail', getline(1))
+ call feedkeys(":find file\t\t\n", "xt")
+ call assert_equal('Jimmy Hoffa', getline(1))
+ call feedkeys(":find file\t\t\t\n", "xt")
+ call assert_equal('E.T.', getline(1))
+
+ " Rerun the previous three find completions, using fullpath in 'path'
+ exec "set path=" . cwd . "/Xfind/**"
+
+ call feedkeys(":find file\t\n", "xt")
+ call assert_equal('Holy Grail', getline(1))
+ call feedkeys(":find file\t\t\n", "xt")
+ call assert_equal('Jimmy Hoffa', getline(1))
+ call feedkeys(":find file\t\t\t\n", "xt")
+ call assert_equal('E.T.', getline(1))
+
+ " Same steps again, using relative and fullpath items that point to the same
+ " recursive location.
+ " This is to test that there are no duplicates in the completion list.
+ set path+=Xfind/**
+ call feedkeys(":find file\t\n", "xt")
+ call assert_equal('Holy Grail', getline(1))
+ call feedkeys(":find file\t\t\n", "xt")
+ call assert_equal('Jimmy Hoffa', getline(1))
+ call feedkeys(":find file\t\t\t\n", "xt")
+ call assert_equal('E.T.', getline(1))
+ call feedkeys(":find file\t\t\n", "xt")
+
+ " Test find completion for directory of current buffer, which at this point
+ " is Xfind/in/file.txt.
+ set path=.
+ call feedkeys(":find st\t\n", "xt")
+ call assert_equal('Another Holy Grail', getline(1))
+
+ " Test find completion for empty path item ",," which is the current
+ " directory
+ cd Xfind
+ set path=,,
+ call feedkeys(":find f\t\n", "xt")
+ call assert_equal('Holy Grail', getline(1))
+
+ " Test shortening of
+ "
+ " foo/x/bar/voyager.txt
+ " foo/y/bar/voyager.txt
+ "
+ " When current directory is above foo/ they should be shortened to (in order
+ " of appearance):
+ "
+ " x/bar/voyager.txt
+ " y/bar/voyager.txt
+ call mkdir('foo/x/bar', 'p')
+ call mkdir('foo/y/bar', 'p')
+ call writefile(['Voyager 1'], 'foo/x/bar/voyager.txt')
+ call writefile(['Voyager 2'], 'foo/y/bar/voyager.txt')
+
+ exec "set path=" . cwd . "/Xfind/**"
+ call feedkeys(":find voyager\t\n", "xt")
+ call assert_equal('Voyager 1', getline(1))
+ call feedkeys(":find voyager\t\t\n", "xt")
+ call assert_equal('Voyager 2', getline(1))
+
+ "
+ " When current directory is .../foo/y/bar they should be shortened to (in
+ " order of appearance):
+ "
+ " ./voyager.txt
+ " x/bar/voyager.txt
+ cd foo/y/bar
+ call feedkeys(":find voyager\t\n", "xt")
+ call assert_equal('Voyager 2', getline(1))
+ call feedkeys(":find voyager\t\t\n", "xt")
+ call assert_equal('Voyager 1', getline(1))
+
+ " Check the opposite too:
+ cd ../../x/bar
+ call feedkeys(":find voyager\t\n", "xt")
+ call assert_equal('Voyager 1', getline(1))
+ call feedkeys(":find voyager\t\t\n", "xt")
+ call assert_equal('Voyager 2', getline(1))
+
+ " Check for correct handling of shorten_fname()'s behavior on windows
+ exec "cd " . cwd . "/Xfind/in"
+ call feedkeys(":find file\t\n", "xt")
+ call assert_equal('Jimmy Hoffa', getline(1))
+
+ " Test for relative to current buffer 'path' item
+ exec "cd " . cwd . "/Xfind/"
+ set path=./path
+ " Open the file where Jimmy Hoffa is found
+ e in/file.txt
+ " Find the file containing 'E.T.' in the Xfind/in/path directory
+ call feedkeys(":find file\t\n", "xt")
+ call assert_equal('E.T.', getline(1))
+
+ " Test that completion works when path=.,,
+ set path=.,,
+ " Open Jimmy Hoffa file
+ e in/file.txt
+ call assert_equal('Jimmy Hoffa', getline(1))
+
+ " Search for the file containing Holy Grail in same directory as in/path.txt
+ call feedkeys(":find stu\t\n", "xt")
+ call assert_equal('Another Holy Grail', getline(1))
+
+ enew | only
+ exe 'cd ' . cwd
+ call delete('Xfind', 'rf')
+ set path&
+endfunc
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
new file mode 100644
index 0000000000..d9a89801ea
--- /dev/null
+++ b/src/nvim/testdir/test_findfile.vim
@@ -0,0 +1,25 @@
+" Test for findfile()
+"
+func Test_findfile()
+ new
+ let cwd=getcwd()
+ cd ..
+
+ " Tests may be run from a shadow directory, so an extra cd needs to be done to
+ " get above src/
+ if fnamemodify(getcwd(), ':t') != 'src'
+ cd ../..
+ else
+ cd ..
+ endif
+ set ssl
+
+ call assert_equal('src/nvim/testdir/test_findfile.vim', findfile('test_findfile.vim','src/nvim/test*'))
+ exe "cd" cwd
+ cd ..
+ call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','test*'))
+ call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','testdir'))
+
+ exe "cd" cwd
+ q!
+endfunc
diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim
new file mode 100644
index 0000000000..32cb059e26
--- /dev/null
+++ b/src/nvim/testdir/test_fixeol.vim
@@ -0,0 +1,48 @@
+" Tests for 'fixeol' and 'eol'
+func Test_fixeol()
+ " first write two test files – with and without trailing EOL
+ " use Unix fileformat for consistency
+ set ff=unix
+ enew!
+ call setline('.', 'with eol')
+ w! XXEol
+ enew!
+ set noeol nofixeol
+ call setline('.', 'without eol')
+ w! XXNoEol
+ set eol fixeol
+ bwipe XXEol XXNoEol
+
+ " try editing files with 'fixeol' disabled
+ e! XXEol
+ normal ostays eol
+ set nofixeol
+ w! XXTestEol
+ e! XXNoEol
+ normal ostays without
+ set nofixeol
+ w! XXTestNoEol
+ bwipe! XXEol XXNoEol XXTestEol XXTestNoEol
+ set fixeol
+
+ " Append "END" to each file so that we can see what the last written char
+ " was.
+ normal ggdGaEND
+ w >>XXEol
+ w >>XXNoEol
+ w >>XXTestEol
+ w >>XXTestNoEol
+
+ call assert_equal(['with eol', 'END'], readfile('XXEol'))
+ call assert_equal(['without eolEND'], readfile('XXNoEol'))
+ call assert_equal(['with eol', 'stays eol', 'END'], readfile('XXTestEol'))
+ call assert_equal(['without eol', 'stays withoutEND'],
+ \ readfile('XXTestNoEol'))
+
+ call delete('XXEol')
+ call delete('XXNoEol')
+ call delete('XXTestEol')
+ call delete('XXTestNoEol')
+ set ff& fixeol& eol&
+ enew!
+endfunc
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 46c54e8614..7c6d38d7ec 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -360,3 +360,24 @@ func! Test_move_folds_around_indent()
call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 1], map(range(1, line('$')), 'foldlevel(v:val)'))
bw!
endfunc
+
+" test for patch 7.3.637
+" Cannot catch the error caused by a foldopen when there is no fold.
+func Test_foldopen_exception()
+ enew!
+ let a = 'No error caught'
+ try
+ foldopen
+ catch
+ let a = matchstr(v:exception,'^[^ ]*')
+ endtry
+ call assert_equal('Vim(foldopen):E490:', a)
+
+ let a = 'No error caught'
+ try
+ foobar
+ catch
+ let a = matchstr(v:exception,'^[^ ]*')
+ endtry
+ call assert_match('E492:', a)
+endfunc
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 237a2dc820..398e9ab331 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -191,4 +191,165 @@ func Test_toupper()
call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ"))
endfunc
+" Tests for the mode() function
+let current_modes = ''
+func! Save_mode()
+ let g:current_modes = mode(0) . '-' . mode(1)
+ return ''
+endfunc
+
+func! Test_mode()
+ new
+ call append(0, ["Blue Ball Black", "Brown Band Bowl", ""])
+
+ inoremap <F2> <C-R>=Save_mode()<CR>
+
+ normal! 3G
+ exe "normal i\<F2>\<Esc>"
+ call assert_equal('i-i', g:current_modes)
+ " i_CTRL-P: Multiple matches
+ exe "normal i\<C-G>uBa\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-P: Single match
+ exe "normal iBro\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X
+ exe "normal iBa\<C-X>\<F2>\<Esc>u"
+ call assert_equal('i-ix', g:current_modes)
+ " i_CTRL-X CTRL-P: Multiple matches
+ exe "normal iBa\<C-X>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X CTRL-P: Single match
+ exe "normal iBro\<C-X>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X CTRL-P + CTRL-P: Single match
+ exe "normal iBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X CTRL-L: Multiple matches
+ exe "normal i\<C-X>\<C-L>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X CTRL-L: Single match
+ exe "normal iBlu\<C-X>\<C-L>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-P: No match
+ exe "normal iCom\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X CTRL-P: No match
+ exe "normal iCom\<C-X>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+ " i_CTRL-X CTRL-L: No match
+ exe "normal iabc\<C-X>\<C-L>\<F2>\<Esc>u"
+ call assert_equal('i-ic', g:current_modes)
+
+ " R_CTRL-P: Multiple matches
+ exe "normal RBa\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-P: Single match
+ exe "normal RBro\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X
+ exe "normal RBa\<C-X>\<F2>\<Esc>u"
+ call assert_equal('R-Rx', g:current_modes)
+ " R_CTRL-X CTRL-P: Multiple matches
+ exe "normal RBa\<C-X>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X CTRL-P: Single match
+ exe "normal RBro\<C-X>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X CTRL-P + CTRL-P: Single match
+ exe "normal RBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X CTRL-L: Multiple matches
+ exe "normal R\<C-X>\<C-L>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X CTRL-L: Single match
+ exe "normal RBlu\<C-X>\<C-L>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-P: No match
+ exe "normal RCom\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X CTRL-P: No match
+ exe "normal RCom\<C-X>\<C-P>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+ " R_CTRL-X CTRL-L: No match
+ exe "normal Rabc\<C-X>\<C-L>\<F2>\<Esc>u"
+ call assert_equal('R-Rc', g:current_modes)
+
+ call assert_equal('n', mode(0))
+ call assert_equal('n', mode(1))
+
+ " How to test operator-pending mode?
+
+ call feedkeys("v", 'xt')
+ call assert_equal('v', mode())
+ call assert_equal('v', mode(1))
+ call feedkeys("\<Esc>V", 'xt')
+ call assert_equal('V', mode())
+ call assert_equal('V', mode(1))
+ call feedkeys("\<Esc>\<C-V>", 'xt')
+ call assert_equal("\<C-V>", mode())
+ call assert_equal("\<C-V>", mode(1))
+ call feedkeys("\<Esc>", 'xt')
+
+ call feedkeys("gh", 'xt')
+ call assert_equal('s', mode())
+ call assert_equal('s', mode(1))
+ call feedkeys("\<Esc>gH", 'xt')
+ call assert_equal('S', mode())
+ call assert_equal('S', mode(1))
+ call feedkeys("\<Esc>g\<C-H>", 'xt')
+ call assert_equal("\<C-S>", mode())
+ call assert_equal("\<C-S>", mode(1))
+ call feedkeys("\<Esc>", 'xt')
+
+ call feedkeys(":echo \<C-R>=Save_mode()\<C-U>\<CR>", 'xt')
+ call assert_equal('c-c', g:current_modes)
+ call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt')
+ call assert_equal('c-cv', g:current_modes)
+ " How to test Ex mode?
+
+ bwipe!
+ iunmap <F2>
+endfunc
+
+func Test_getbufvar()
+ let bnr = bufnr('%')
+ let b:var_num = '1234'
+ let def_num = '5678'
+ call assert_equal('1234', getbufvar(bnr, 'var_num'))
+ call assert_equal('1234', getbufvar(bnr, 'var_num', def_num))
+
+ let bd = getbufvar(bnr, '')
+ call assert_equal('1234', bd['var_num'])
+ call assert_true(exists("bd['changedtick']"))
+ call assert_equal(2, len(bd))
+
+ let bd2 = getbufvar(bnr, '', def_num)
+ call assert_equal(bd, bd2)
+
+ unlet b:var_num
+ call assert_equal(def_num, getbufvar(bnr, 'var_num', def_num))
+ call assert_equal('', getbufvar(bnr, 'var_num'))
+ let bd = getbufvar(bnr, '')
+ call assert_equal(1, len(bd))
+ let bd = getbufvar(bnr, '',def_num)
+ call assert_equal(1, len(bd))
+
+ call assert_equal('', getbufvar(9999, ''))
+ call assert_equal(def_num, getbufvar(9999, '', def_num))
+ unlet def_num
+
+ call assert_equal(0, getbufvar(bnr, '&autoindent'))
+ call assert_equal(0, getbufvar(bnr, '&autoindent', 1))
+
+ " Open new window with forced option values
+ set fileformats=unix,dos
+ new ++ff=dos ++bin ++enc=iso-8859-2
+ call assert_equal('dos', getbufvar(bufnr('%'), '&fileformat'))
+ call assert_equal(1, getbufvar(bufnr('%'), '&bin'))
+ call assert_equal('iso-8859-2', getbufvar(bufnr('%'), '&fenc'))
+ close
+
+ set fileformats&
+endfunc
diff --git a/src/nvim/testdir/test_ga.vim b/src/nvim/testdir/test_ga.vim
new file mode 100644
index 0000000000..f9357ddc87
--- /dev/null
+++ b/src/nvim/testdir/test_ga.vim
@@ -0,0 +1,37 @@
+" Test ga normal command, and :ascii Ex command.
+func Do_ga(c)
+ call setline(1, a:c)
+ let l:a = execute("norm 1goga")
+ let l:b = execute("ascii")
+ call assert_equal(l:a, l:b)
+ return l:a
+endfunc
+
+func Test_ga_command()
+ new
+ set display=uhex
+ call assert_equal("\nNUL", Do_ga(''))
+ call assert_equal("\n<<01>> 1, Hex 01, Octal 001", Do_ga("\x01"))
+ call assert_equal("\n<<09>> 9, Hex 09, Octal 011", Do_ga("\t"))
+
+ set display=
+ call assert_equal("\nNUL", Do_ga(''))
+ call assert_equal("\n<^A> 1, Hex 01, Octal 001", Do_ga("\x01"))
+ call assert_equal("\n<^I> 9, Hex 09, Octal 011", Do_ga("\t"))
+
+ call assert_equal("\n<e> 101, Hex 65, Octal 145", Do_ga('e'))
+
+ if !has('multi_byte')
+ return
+ endif
+
+ " Test a few multi-bytes characters.
+ call assert_equal("\n<é> 233, Hex 00e9, Octal 351", Do_ga('é'))
+ call assert_equal("\n<ẻ> 7867, Hex 1ebb, Octal 17273", Do_ga('ẻ'))
+
+ " Test with combining characters.
+ call assert_equal("\n<e> 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401", Do_ga("e\u0301"))
+ call assert_equal("\n<e> 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401 < ̱> 817, Hex 0331, Octal 1461", Do_ga("e\u0301\u0331"))
+ call assert_equal("\n<e> 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401 < ̱> 817, Hex 0331, Octal 1461 < ̸> 824, Hex 0338, Octal 1470", Do_ga("e\u0301\u0331\u0338"))
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index b6ac5720c3..2573401707 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -1,22 +1,277 @@
" Test commands that jump somewhere.
-func Test_geeDEE()
+" Create a new buffer using "lines" and place the cursor on the word after the
+" first occurrence of return and invoke "cmd". The cursor should now be
+" positioned at the given line and col.
+func XTest_goto_decl(cmd, lines, line, col)
new
- call setline(1, ["Filename x;", "", "int Filename", "int func() {", "Filename y;"])
- /y;/
- normal gD
- call assert_equal(1, line('.'))
+ call setline(1, a:lines)
+ /return/
+ normal! W
+ execute 'norm! ' . a:cmd
+ call assert_equal(a:line, line('.'))
+ call assert_equal(a:col, col('.'))
quit!
endfunc
-func Test_gee_dee()
- new
- call setline(1, ["int x;", "", "int func(int x)", "{", " return x;", "}"])
- /return/
- normal $hgd
- call assert_equal(3, line('.'))
- call assert_equal(14, col('.'))
- quit!
+func Test_gD()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 5)
+endfunc
+
+func Test_gD_too()
+ let lines = [
+ \ 'Filename x;',
+ \ '',
+ \ 'int Filename',
+ \ 'int func() {',
+ \ ' Filename x;',
+ \ ' return x;',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 10)
+endfunc
+
+func Test_gD_comment()
+ let lines = [
+ \ '/* int x; */',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_inline_comment()
+ let lines = [
+ \ 'int y /* , x */;',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_string()
+ let lines = [
+ \ 'char *s[] = "x";',
+ \ 'int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_string_same_line()
+ let lines = [
+ \ 'char *s[] = "x", int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 22)
+endfunc
+
+func Test_gD_char()
+ let lines = [
+ \ "char c = 'x';",
+ \ 'int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gd()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 14)
+endfunc
+
+func Test_gd_not_local()
+ let lines = [
+ \ 'int func1(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ '',
+ \ 'int func2(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gd_kr_style()
+ let lines = [
+ \ 'int func(x)',
+ \ ' int x;',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 2, 7)
+endfunc
+
+func Test_gd_missing_braces()
+ let lines = [
+ \ 'def func1(a)',
+ \ ' a + 1',
+ \ 'end',
+ \ '',
+ \ 'a = 1',
+ \ '',
+ \ 'def func2()',
+ \ ' return a',
+ \ 'end',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 11)
+endfunc
+
+func Test_gd_comment()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* int x; */',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gd_comment_in_string()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s ="//"; int x;',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 3, 22)
+endfunc
+
+func Test_gd_string_in_comment()
+ set comments=
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* " */ int x;',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 3, 15)
+ set comments&
+endfunc
+
+func Test_gd_inline_comment()
+ let lines = [
+ \ 'int func(/* x is an int */ int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 32)
+endfunc
+
+func Test_gd_inline_comment_only()
+ let lines = [
+ \ 'int func(void) /* one lonely x */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gd_inline_comment_body()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' int y /* , x */;',
+ \ '',
+ \ ' for (/* int x = 0 */; y < 2; y++);',
+ \ '',
+ \ ' int x = 0;',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 7, 7)
+endfunc
+
+func Test_gd_trailing_multiline_comment()
+ let lines = [
+ \ 'int func(int x) /* x is an int */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
+endfunc
+
+func Test_gd_trailing_comment()
+ let lines = [
+ \ 'int func(int x) // x is an int',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
+endfunc
+
+func Test_gd_string()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s = "x";',
+ \ ' int x = 1;',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gd_string_only()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s = "x";',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 5, 10)
endfunc
" Check that setting 'cursorline' does not change curswant
diff --git a/src/nvim/testdir/test_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim
index ea9790d134..7aea704e86 100644
--- a/src/nvim/testdir/test_hardcopy.vim
+++ b/src/nvim/testdir/test_hardcopy.vim
@@ -50,6 +50,7 @@ endfunc
" We don't check much of the contents.
func Test_with_syntax()
if has('postscript')
+ edit test_hardcopy.vim
set printoptions=syntax:y
syn on
hardcopy > Xhardcopy
diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index 1ca0f722cf..06c48d8e76 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -18,6 +18,52 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*help.txt\*')
helpclose
+ help |
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*bar\*')
+ helpclose
+
+ help "*
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*quotestar\*')
+ helpclose
+
+ help sp?it
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*:split\*')
+ helpclose
+
+ help :?
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*:?\*')
+ helpclose
+
+ help FileW*Post
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*FileWritePost\*')
+ helpclose
+
+ help `ls`
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*:ls\*')
+ helpclose
+
+ help ^X
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*CTRL-X\*')
+ helpclose
+
+ help i_^_CTRL-D
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*i_^_CTRL-D\*')
+ helpclose
+
+ exec "help \<C-V>"
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*CTRL-V\*')
+ helpclose
+
+
exec "help! ('textwidth'"
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ "\\*'textwidth'\\*")
@@ -47,6 +93,16 @@ func Test_help_tagjump()
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ '\*{address}\*')
helpclose
+
+ exusage
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*:index\*')
+ helpclose
+
+ viusage
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*normal-index\*')
+ helpclose
endfunc
let s:langs = ['en', 'ab', 'ja']
@@ -89,17 +145,8 @@ func s:doc_config_teardown()
endif
endfunc
-func s:get_cmd_compl_list(cmd)
- let list = []
- let str = ''
- for cnt in range(1, 999)
- call feedkeys(a:cmd . repeat("\<Tab>", cnt) . "'\<C-B>let str='\<CR>", 'tx')
- if str ==# a:cmd[1:]
- break
- endif
- call add(list, str)
- endfor
- return list
+func s:get_help_compl_list(cmd)
+ return getcompletion(a:cmd, 'help')
endfunc
func Test_help_complete()
@@ -111,49 +158,49 @@ func Test_help_complete()
if has('multi_lang')
set helplang=
endif
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(['h test-col', 'h test-char'], list)
+ let list = s:get_help_compl_list("test")
+ call assert_equal(['test-col', 'test-char'], list)
if has('multi_lang')
" 'helplang=ab' and help file lang is 'en'
set helplang=ab
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(['h test-col', 'h test-char'], list)
+ let list = s:get_help_compl_list("test")
+ call assert_equal(['test-col', 'test-char'], list)
" 'helplang=' and help file lang is 'en' and 'ab'
set rtp+=Xdir1/doc-ab
set helplang=
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(sort(['h test-col@en', 'h test-col@ab',
- \ 'h test-char@en', 'h test-char@ab']), sort(list))
+ let list = s:get_help_compl_list("test")
+ call assert_equal(sort(['test-col@en', 'test-col@ab',
+ \ 'test-char@en', 'test-char@ab']), sort(list))
" 'helplang=ab' and help file lang is 'en' and 'ab'
set helplang=ab
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(sort(['h test-col', 'h test-col@en',
- \ 'h test-char', 'h test-char@en']), sort(list))
+ let list = s:get_help_compl_list("test")
+ call assert_equal(sort(['test-col', 'test-col@en',
+ \ 'test-char', 'test-char@en']), sort(list))
" 'helplang=' and help file lang is 'en', 'ab' and 'ja'
set rtp+=Xdir1/doc-ja
set helplang=
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(sort(['h test-col@en', 'h test-col@ab',
- \ 'h test-col@ja', 'h test-char@en',
- \ 'h test-char@ab', 'h test-char@ja']), sort(list))
+ let list = s:get_help_compl_list("test")
+ call assert_equal(sort(['test-col@en', 'test-col@ab',
+ \ 'test-col@ja', 'test-char@en',
+ \ 'test-char@ab', 'test-char@ja']), sort(list))
" 'helplang=ab' and help file lang is 'en', 'ab' and 'ja'
set helplang=ab
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(sort(['h test-col', 'h test-col@en',
- \ 'h test-col@ja', 'h test-char',
- \ 'h test-char@en', 'h test-char@ja']), sort(list))
+ let list = s:get_help_compl_list("test")
+ call assert_equal(sort(['test-col', 'test-col@en',
+ \ 'test-col@ja', 'test-char',
+ \ 'test-char@en', 'test-char@ja']), sort(list))
" 'helplang=ab,ja' and help file lang is 'en', 'ab' and 'ja'
set helplang=ab,ja
- let list = s:get_cmd_compl_list(":h test")
- call assert_equal(sort(['h test-col', 'h test-col@ja',
- \ 'h test-col@en', 'h test-char',
- \ 'h test-char@ja', 'h test-char@en']), sort(list))
+ let list = s:get_help_compl_list("test")
+ call assert_equal(sort(['test-col', 'test-col@ja',
+ \ 'test-col@en', 'test-char',
+ \ 'test-char@ja', 'test-char@en']), sort(list))
endif
catch
call assert_exception('X')
diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim
new file mode 100644
index 0000000000..128b8ff945
--- /dev/null
+++ b/src/nvim/testdir/test_hide.vim
@@ -0,0 +1,97 @@
+" Tests for :hide command/modifier and 'hidden' option
+
+function SetUp()
+ let s:save_hidden = &hidden
+ let s:save_bufhidden = &bufhidden
+ let s:save_autowrite = &autowrite
+ set nohidden
+ set bufhidden=
+ set noautowrite
+endfunc
+
+function TearDown()
+ let &hidden = s:save_hidden
+ let &bufhidden = s:save_bufhidden
+ let &autowrite = s:save_autowrite
+endfunc
+
+function Test_hide()
+ let orig_bname = bufname('')
+ let orig_winnr = winnr('$')
+
+ new Xf1
+ set modified
+ call assert_fails('edit Xf2')
+ bwipeout! Xf1
+
+ new Xf1
+ set modified
+ edit! Xf2
+ call assert_equal(['Xf2', 2], [bufname(''), winnr('$')])
+ call assert_equal([1, 0], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+ bwipeout! Xf2
+
+ new Xf1
+ set modified
+ " :hide as a command
+ hide
+ call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')])
+ call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+
+ new Xf1
+ set modified
+ " :hide as a command with trailing comment
+ hide " comment
+ call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')])
+ call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+
+ new Xf1
+ set modified
+ " :hide as a command with bar
+ hide | new Xf2 " comment
+ call assert_equal(['Xf2', 2], [bufname(''), winnr('$')])
+ call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+ bwipeout! Xf2
+
+ new Xf1
+ set modified
+ " :hide as a modifier with trailing comment
+ hide edit Xf2 " comment
+ call assert_equal(['Xf2', 2], [bufname(''), winnr('$')])
+ call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+ bwipeout! Xf2
+
+ new Xf1
+ set modified
+ " To check that the bar is not recognized to separate commands
+ hide echo "one|two"
+ call assert_equal(['Xf1', 2], [bufname(''), winnr('$')])
+ call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+
+ " set hidden
+ new Xf1
+ set hidden
+ set modified
+ edit Xf2 " comment
+ call assert_equal(['Xf2', 2], [bufname(''), winnr('$')])
+ call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf1
+ bwipeout! Xf2
+
+ " set hidden bufhidden=wipe
+ new Xf1
+ set bufhidden=wipe
+ set modified
+ hide edit! Xf2 " comment
+ call assert_equal(['Xf2', 2], [bufname(''), winnr('$')])
+ call assert_equal([0, 0], [buflisted('Xf1'), bufloaded('Xf1')])
+ bwipeout! Xf2
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim
index 3163b344d3..ca31e3f06c 100644
--- a/src/nvim/testdir/test_history.vim
+++ b/src/nvim/testdir/test_history.vim
@@ -31,6 +31,30 @@ function History_Tests(hist)
call assert_equal('ls', histget(a:hist, -1))
call assert_equal(4, histnr(a:hist))
+ let a=execute('history ' . a:hist)
+ call assert_match("^\n # \\S* history\n 3 buffers\n> 4 ls$", a)
+ let a=execute('history all')
+ call assert_match("^\n # .* history\n 3 buffers\n> 4 ls", a)
+
+ if len(a:hist) > 0
+ let a=execute('history ' . a:hist . ' 2')
+ call assert_match("^\n # \\S* history$", a)
+ let a=execute('history ' . a:hist . ' 3')
+ call assert_match("^\n # \\S* history\n 3 buffers$", a)
+ let a=execute('history ' . a:hist . ' 4')
+ call assert_match("^\n # \\S* history\n> 4 ls$", a)
+ let a=execute('history ' . a:hist . ' 3,4')
+ call assert_match("^\n # \\S* history\n 3 buffers\n> 4 ls$", a)
+ let a=execute('history ' . a:hist . ' -1')
+ call assert_match("^\n # \\S* history\n> 4 ls$", a)
+ let a=execute('history ' . a:hist . ' -2')
+ call assert_match("^\n # \\S* history\n 3 buffers$", a)
+ let a=execute('history ' . a:hist . ' -2,')
+ call assert_match("^\n # \\S* history\n 3 buffers\n> 4 ls$", a)
+ let a=execute('history ' . a:hist . ' -3')
+ call assert_match("^\n # \\S* history$", a)
+ endif
+
" Test for removing entries matching a pattern
for i in range(1, 3)
call histadd(a:hist, 'text_' . i)
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
new file mode 100644
index 0000000000..c307e33cbf
--- /dev/null
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -0,0 +1,219 @@
+
+" Test for insert expansion
+func Test_ins_complete()
+ edit test_ins_complete.vim
+ " The files in the current directory interferes with the files
+ " used by this test. So use a separate directory for the test.
+ call mkdir('Xdir')
+ cd Xdir
+
+ set ff=unix
+ call writefile(["test11\t36Gepeto\t/Tag/",
+ \ "asd\ttest11file\t36G",
+ \ "Makefile\tto\trun"], 'Xtestfile')
+ call writefile(['', 'start of testfile',
+ \ 'ru',
+ \ 'run1',
+ \ 'run2',
+ \ 'STARTTEST',
+ \ 'ENDTEST',
+ \ 'end of testfile'], 'Xtestdata')
+ set ff&
+
+ enew!
+ edit Xtestdata
+ new
+ call append(0, ['#include "Xtestfile"', ''])
+ call cursor(2, 1)
+
+ set cot=
+ set cpt=.,w
+ " add-expands (word from next line) from other window
+ exe "normal iru\<C-N>\<C-N>\<C-X>\<C-N>\<Esc>\<C-A>"
+ call assert_equal('run1 run3', getline('.'))
+ " add-expands (current buffer first)
+ exe "normal o\<C-P>\<C-X>\<C-N>"
+ call assert_equal('run3 run3', getline('.'))
+ " Local expansion, ends in an empty line (unless it becomes a global
+ " expansion)
+ exe "normal o\<C-X>\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>"
+ call assert_equal('', getline('.'))
+ " starts Local and switches to global add-expansion
+ exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>"
+ call assert_equal('run1 run2', getline('.'))
+
+ set cpt=.,w,i
+ " i-add-expands and switches to local
+ exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>"
+ call assert_equal("Makefile\tto\trun3", getline('.'))
+ " add-expands lines (it would end in an empty line if it didn't ignored
+ " itself)
+ exe "normal o\<C-X>\<C-L>\<C-X>\<C-L>\<C-P>\<C-P>"
+ call assert_equal("Makefile\tto\trun3", getline('.'))
+ call assert_equal("Makefile\tto\trun3", getline(line('.') - 1))
+
+ set cpt=kXtestfile
+ " checks k-expansion, and file expansion (use Xtest11 instead of test11,
+ " because TEST11.OUT may match first on DOS)
+ write Xtest11.one
+ write Xtest11.two
+ exe "normal o\<C-N>\<Esc>IX\<Esc>A\<C-X>\<C-F>\<C-N>"
+ call assert_equal('Xtest11.two', getline('.'))
+
+ " use CTRL-X CTRL-F to complete Xtest11.one, remove it and then use CTRL-X
+ " CTRL-F again to verify this doesn't cause trouble.
+ exe "normal oXt\<C-X>\<C-F>\<BS>\<BS>\<BS>\<BS>\<BS>\<BS>\<BS>\<BS>\<C-X>\<C-F>"
+ call assert_equal('Xtest11.one', getline('.'))
+ normal ddk
+
+ set cpt=w
+ " checks make_cyclic in other window
+ exe "normal oST\<C-N>\<C-P>\<C-P>\<C-P>\<C-P>"
+ call assert_equal('STARTTEST', getline('.'))
+
+ set cpt=u nohid
+ " checks unloaded buffer expansion
+ only
+ exe "normal oEN\<C-N>"
+ call assert_equal('ENDTEST', getline('.'))
+ " checks adding mode abortion
+ exe "normal ounl\<C-N>\<C-X>\<C-X>\<C-P>"
+ call assert_equal('unless', getline('.'))
+
+ set cpt=t,d def=^\\k* tags=Xtestfile notagbsearch
+ " tag expansion, define add-expansion interrupted
+ exe "normal o\<C-X>\<C-]>\<C-X>\<C-D>\<C-X>\<C-D>\<C-X>\<C-X>\<C-D>\<C-X>\<C-D>\<C-X>\<C-D>\<C-X>\<C-D>"
+ call assert_equal('test11file 36Gepeto /Tag/ asd', getline('.'))
+ " t-expansion
+ exe "normal oa\<C-N>\<Esc>"
+ call assert_equal('asd', getline('.'))
+
+ %bw!
+ call delete('Xtestfile')
+ call delete('Xtest11.one')
+ call delete('Xtest11.two')
+ call delete('Xtestdata')
+ set cpt& cot& def& tags& tagbsearch& hidden&
+ cd ..
+ call delete('Xdir', 'rf')
+endfunc
+
+function! s:CompleteDone_CompleteFuncDict( findstart, base )
+ if a:findstart
+ return 0
+ endif
+
+ return {
+ \ 'words': [
+ \ {
+ \ 'word': 'aword',
+ \ 'abbr': 'wrd',
+ \ 'menu': 'extra text',
+ \ 'info': 'words are cool',
+ \ 'kind': 'W',
+ \ 'user_data': 'test'
+ \ }
+ \ ]
+ \ }
+endfunction
+
+function! s:CompleteDone_CheckCompletedItemDict()
+ call assert_equal( 'aword', v:completed_item[ 'word' ] )
+ call assert_equal( 'wrd', v:completed_item[ 'abbr' ] )
+ call assert_equal( 'extra text', v:completed_item[ 'menu' ] )
+ call assert_equal( 'words are cool', v:completed_item[ 'info' ] )
+ call assert_equal( 'W', v:completed_item[ 'kind' ] )
+ call assert_equal( 'test', v:completed_item[ 'user_data' ] )
+
+ let s:called_completedone = 1
+endfunction
+
+function Test_CompleteDoneDict()
+ au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDict()
+
+ set completefunc=<SID>CompleteDone_CompleteFuncDict
+ execute "normal a\<C-X>\<C-U>\<C-Y>"
+ set completefunc&
+
+ call assert_equal( 'test', v:completed_item[ 'user_data' ] )
+ call assert_true( s:called_completedone )
+
+ let s:called_completedone = 0
+ au! CompleteDone
+endfunc
+
+function! s:CompleteDone_CompleteFuncDictNoUserData( findstart, base )
+ if a:findstart
+ return 0
+ endif
+
+ return {
+ \ 'words': [
+ \ {
+ \ 'word': 'aword',
+ \ 'abbr': 'wrd',
+ \ 'menu': 'extra text',
+ \ 'info': 'words are cool',
+ \ 'kind': 'W'
+ \ }
+ \ ]
+ \ }
+endfunction
+
+function! s:CompleteDone_CheckCompletedItemDictNoUserData()
+ call assert_equal( 'aword', v:completed_item[ 'word' ] )
+ call assert_equal( 'wrd', v:completed_item[ 'abbr' ] )
+ call assert_equal( 'extra text', v:completed_item[ 'menu' ] )
+ call assert_equal( 'words are cool', v:completed_item[ 'info' ] )
+ call assert_equal( 'W', v:completed_item[ 'kind' ] )
+ call assert_equal( '', v:completed_item[ 'user_data' ] )
+
+ let s:called_completedone = 1
+endfunction
+
+function Test_CompleteDoneDictNoUserData()
+ au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDictNoUserData()
+
+ set completefunc=<SID>CompleteDone_CompleteFuncDictNoUserData
+ execute "normal a\<C-X>\<C-U>\<C-Y>"
+ set completefunc&
+
+ call assert_equal( '', v:completed_item[ 'user_data' ] )
+ call assert_true( s:called_completedone )
+
+ let s:called_completedone = 0
+ au! CompleteDone
+endfunc
+
+function! s:CompleteDone_CompleteFuncList( findstart, base )
+ if a:findstart
+ return 0
+ endif
+
+ return [ 'aword' ]
+endfunction
+
+function! s:CompleteDone_CheckCompletedItemList()
+ call assert_equal( 'aword', v:completed_item[ 'word' ] )
+ call assert_equal( '', v:completed_item[ 'abbr' ] )
+ call assert_equal( '', v:completed_item[ 'menu' ] )
+ call assert_equal( '', v:completed_item[ 'info' ] )
+ call assert_equal( '', v:completed_item[ 'kind' ] )
+ call assert_equal( '', v:completed_item[ 'user_data' ] )
+
+ let s:called_completedone = 1
+endfunction
+
+function Test_CompleteDoneList()
+ au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemList()
+
+ set completefunc=<SID>CompleteDone_CompleteFuncList
+ execute "normal a\<C-X>\<C-U>\<C-Y>"
+ set completefunc&
+
+ call assert_equal( '', v:completed_item[ 'user_data' ] )
+ call assert_true( s:called_completedone )
+
+ let s:called_completedone = 0
+ au! CompleteDone
+endfunc
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
new file mode 100644
index 0000000000..24c6ef5e01
--- /dev/null
+++ b/src/nvim/testdir/test_let.vim
@@ -0,0 +1,27 @@
+" Tests for the :let command.
+
+func Test_let()
+ " Test to not autoload when assigning. It causes internal error.
+ set runtimepath+=./sautest
+ let Test104#numvar = function('tr')
+ call assert_equal("function('tr')", string(Test104#numvar))
+
+ let a = 1
+ let b = 2
+
+ let out = execute('let a b')
+ let s = "\na #1\nb #2"
+ call assert_equal(s, out)
+
+ let out = execute('let {0 == 1 ? "a" : "b"}')
+ let s = "\nb #2"
+ call assert_equal(s, out)
+
+ let out = execute('let {0 == 1 ? "a" : "b"} a')
+ let s = "\nb #2\na #1"
+ call assert_equal(s, out)
+
+ let out = execute('let a {0 == 1 ? "a" : "b"}')
+ let s = "\na #1\nb #2"
+ call assert_equal(s, out)
+endfunc
diff --git a/src/nvim/testdir/test_lineending.vim b/src/nvim/testdir/test_lineending.vim
new file mode 100644
index 0000000000..5be3be8db3
--- /dev/null
+++ b/src/nvim/testdir/test_lineending.vim
@@ -0,0 +1,19 @@
+" Tests for saving/loading a file with some lines ending in
+" CTRL-M, some not
+func Test_lineending()
+ let l = ["this line ends in a\<CR>",
+ \ "this one doesn't",
+ \ "this one does\<CR>",
+ \ "and the last one doesn't"]
+ set fileformat=dos
+ enew!
+ call append(0, l)
+ $delete
+ write Xfile1
+ bwipe Xfile1
+ edit Xfile1
+ let t = getline(1, '$')
+ call assert_equal(l, t)
+ new | only
+ call delete('Xfile1')
+endfunc
diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim
new file mode 100644
index 0000000000..4c05504cf1
--- /dev/null
+++ b/src/nvim/testdir/test_lispwords.vim
@@ -0,0 +1,82 @@
+" Tests for 'lispwords' settings being global-local
+
+set nocompatible viminfo+=nviminfo
+
+func Test_global_local_lispwords()
+ setglobal lispwords=foo,bar,baz
+ setlocal lispwords-=foo | setlocal lispwords+=quux
+ call assert_equal('foo,bar,baz', &g:lispwords)
+ call assert_equal('bar,baz,quux', &l:lispwords)
+ call assert_equal('bar,baz,quux', &lispwords)
+
+ setlocal lispwords<
+ call assert_equal('foo,bar,baz', &g:lispwords)
+ call assert_equal('foo,bar,baz', &l:lispwords)
+ call assert_equal('foo,bar,baz', &lispwords)
+endfunc
+
+func Test_lisp_indent()
+ enew!
+
+ call append(0, [
+ \ '(defun html-file (base)',
+ \ '(format nil "~(~A~).html" base))',
+ \ '',
+ \ '(defmacro page (name title &rest body)',
+ \ '(let ((ti (gensym)))',
+ \ '`(with-open-file (*standard-output*',
+ \ '(html-file ,name)',
+ \ ':direction :output',
+ \ ':if-exists :supersede)',
+ \ '(let ((,ti ,title))',
+ \ '(as title ,ti)',
+ \ '(with center ',
+ \ '(as h2 (string-upcase ,ti)))',
+ \ '(brs 3)',
+ \ ',@body))))',
+ \ '',
+ \ ';;; Utilities for generating links',
+ \ '',
+ \ '(defmacro with-link (dest &rest body)',
+ \ '`(progn',
+ \ '(format t "<a href=\"~A\">" (html-file ,dest))',
+ \ ',@body',
+ \ '(princ "</a>")))'
+ \ ])
+ set lisp
+ set lispwords&
+ let save_copt = &cpoptions
+ set cpoptions+=p
+ normal 1G=G
+
+ call assert_equal([
+ \ '(defun html-file (base)',
+ \ ' (format nil "~(~A~).html" base))',
+ \ '',
+ \ '(defmacro page (name title &rest body)',
+ \ ' (let ((ti (gensym)))',
+ \ ' `(with-open-file (*standard-output*',
+ \ ' (html-file ,name)',
+ \ ' :direction :output',
+ \ ' :if-exists :supersede)',
+ \ ' (let ((,ti ,title))',
+ \ ' (as title ,ti)',
+ \ ' (with center ',
+ \ ' (as h2 (string-upcase ,ti)))',
+ \ ' (brs 3)',
+ \ ' ,@body))))',
+ \ '',
+ \ ';;; Utilities for generating links',
+ \ '',
+ \ '(defmacro with-link (dest &rest body)',
+ \ ' `(progn',
+ \ ' (format t "<a href=\"~A\">" (html-file ,dest))',
+ \ ' ,@body',
+ \ ' (princ "</a>")))',
+ \ ''
+ \ ], getline(1, "$"))
+
+ enew!
+ let &cpoptions=save_copt
+ set nolisp
+endfunc
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
new file mode 100644
index 0000000000..57ea7ca5a9
--- /dev/null
+++ b/src/nvim/testdir/test_listchars.vim
@@ -0,0 +1,63 @@
+" Tests for 'listchars' display with 'list' and :list
+
+source view_util.vim
+
+func Test_listchars()
+ enew!
+ set ff=unix
+ set list
+
+ set listchars+=tab:>-,space:.,trail:<
+ call append(0, [
+ \ ' aa ',
+ \ ' bb ',
+ \ ' cccc ',
+ \ 'dd ee ',
+ \ ' '
+ \ ])
+ let expected = [
+ \ '>-------aa>-----$',
+ \ '..bb>---<<$',
+ \ '...cccc><$',
+ \ 'dd........ee<<>-$',
+ \ '<$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ set listchars-=trail:<
+ let expected = [
+ \ '>-------aa>-----$',
+ \ '..bb>---..$',
+ \ '...cccc>.$',
+ \ 'dd........ee..>-$',
+ \ '.$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ set listchars+=trail:<
+ set nolist
+ normal ggdG
+ call append(0, [
+ \ ' fff ',
+ \ ' gg ',
+ \ ' h ',
+ \ 'iii ',
+ \ ])
+ let l = split(execute("%list"), "\n")
+ call assert_equal([
+ \ '..fff>--<<$',
+ \ '>-------gg>-----$',
+ \ '.....h>-$',
+ \ 'iii<<<<><<$', '$'], l)
+
+ enew!
+ set listchars& ff&
+endfunc
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
new file mode 100644
index 0000000000..023332c90a
--- /dev/null
+++ b/src/nvim/testdir/test_listdict.vim
@@ -0,0 +1,603 @@
+" Tests for the List and Dict types
+
+func TearDown()
+ " Run garbage collection after every test
+ call test_garbagecollect_now()
+endfunc
+
+" Tests for List type
+
+" List creation
+func Test_list_create()
+ " Creating List directly with different types
+ let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},]
+ call assert_equal("[1, 'as''d', [1, 2, function('strlen')], {'a': 1}]", string(l))
+ call assert_equal({'a' : 1}, l[-1])
+ call assert_equal(1, l[-4])
+ let x = 10
+ try
+ let x = l[-5]
+ catch
+ call assert_match('E684:', v:exception)
+ endtry
+ call assert_equal(10, x)
+endfunc
+
+" List slices
+func Test_list_slice()
+ let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},]
+ call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[:])
+ call assert_equal(['as''d', [1, 2, function('strlen')], {'a': 1}], l[1:])
+ call assert_equal([1, 'as''d', [1, 2, function('strlen')]], l[:-2])
+ call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8])
+ call assert_equal([], l[8:-1])
+endfunc
+
+" List identity
+func Test_list_identity()
+ let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},]
+ let ll = l
+ let lx = copy(l)
+ call assert_true(l == ll)
+ call assert_false(l isnot ll)
+ call assert_true(l is ll)
+ call assert_true(l == lx)
+ call assert_false(l is lx)
+ call assert_true(l isnot lx)
+endfunc
+
+" removing items with :unlet
+func Test_list_unlet()
+ let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},]
+ unlet l[2]
+ call assert_equal([1, 'as''d', {'a': 1}], l)
+ let l = range(8)
+ unlet l[:3]
+ unlet l[1:]
+ call assert_equal([4], l)
+
+ " removing items out of range: silently skip items that don't exist
+ let l = [0, 1, 2, 3]
+ call assert_fails('unlet l[2:1]', 'E684')
+ let l = [0, 1, 2, 3]
+ unlet l[2:2]
+ call assert_equal([0, 1, 3], l)
+ let l = [0, 1, 2, 3]
+ unlet l[2:3]
+ call assert_equal([0, 1], l)
+ let l = [0, 1, 2, 3]
+ unlet l[2:4]
+ call assert_equal([0, 1], l)
+ let l = [0, 1, 2, 3]
+ unlet l[2:5]
+ call assert_equal([0, 1], l)
+ let l = [0, 1, 2, 3]
+ call assert_fails('unlet l[-1:2]', 'E684')
+ let l = [0, 1, 2, 3]
+ unlet l[-2:2]
+ call assert_equal([0, 1, 3], l)
+ let l = [0, 1, 2, 3]
+ unlet l[-3:2]
+ call assert_equal([0, 3], l)
+ let l = [0, 1, 2, 3]
+ unlet l[-4:2]
+ call assert_equal([3], l)
+ let l = [0, 1, 2, 3]
+ unlet l[-5:2]
+ call assert_equal([3], l)
+ let l = [0, 1, 2, 3]
+ unlet l[-6:2]
+ call assert_equal([3], l)
+endfunc
+
+" assignment to a list
+func Test_list_assign()
+ let l = [0, 1, 2, 3]
+ let [va, vb] = l[2:3]
+ call assert_equal([2, 3], [va, vb])
+ call assert_fails('let [va, vb] = l', 'E687')
+ call assert_fails('let [va, vb] = l[1:1]', 'E688')
+endfunc
+
+" test for range assign
+func Test_list_range_assign()
+ let l = [0]
+ let l[:] = [1, 2]
+ call assert_equal([1, 2], l)
+endfunc
+
+" Tests for Dictionary type
+
+func Test_dict()
+ " Creating Dictionary directly with different types
+ let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},}
+ call assert_equal("{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}", string(d))
+ call assert_equal('asd', d.1)
+ call assert_equal(['-1', '1', 'b'], sort(keys(d)))
+ call assert_equal(['asd', [1, 2, function('strlen')], {'a': 1}], values(d))
+ let v = []
+ for [key, val] in items(d)
+ call extend(v, [key, val])
+ unlet key val
+ endfor
+ call assert_equal(['1','asd','b',[1, 2, function('strlen')],'-1',{'a': 1}], v)
+
+ call extend(d, {3:33, 1:99})
+ call extend(d, {'b':'bbb', 'c':'ccc'}, "keep")
+ call assert_fails("call extend(d, {3:333,4:444}, 'error')", 'E737')
+ call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d)
+ call filter(d, 'v:key =~ ''[ac391]''')
+ call assert_equal({'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}}, d)
+endfunc
+
+" Dictionary identity
+func Test_dict_identity()
+ let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},}
+ let dd = d
+ let dx = copy(d)
+ call assert_true(d == dd)
+ call assert_false(d isnot dd)
+ call assert_true(d is dd)
+ call assert_true(d == dx)
+ call assert_false(d is dx)
+ call assert_true(d isnot dx)
+endfunc
+
+" removing items with :unlet
+func Test_dict_unlet()
+ let d = {'b':'bbb', '1': 99, '3': 33, '-1': {'a': 1}}
+ unlet d.b
+ unlet d[-1]
+ call assert_equal({'1': 99, '3': 33}, d)
+endfunc
+
+" manipulating a big Dictionary (hashtable.c has a border of 1000 entries)
+func Test_dict_big()
+ let d = {}
+ for i in range(1500)
+ let d[i] = 3000 - i
+ endfor
+ call assert_equal([3000, 2900, 2001, 1600, 1501], [d[0], d[100], d[999], d[1400], d[1499]])
+ let str = ''
+ try
+ let n = d[1500]
+ catch
+ let str=substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '')
+ endtry
+ call assert_equal('Vim(let):E716: 1500', str)
+
+ " lookup each items
+ for i in range(1500)
+ call assert_equal(3000 - i, d[i])
+ endfor
+ let i += 1
+
+ " delete even items
+ while i >= 2
+ let i -= 2
+ unlet d[i]
+ endwhile
+ call assert_equal('NONE', get(d, 1500 - 100, 'NONE'))
+ call assert_equal(2999, d[1])
+
+ " delete odd items, checking value, one intentionally wrong
+ let d[33] = 999
+ let i = 1
+ while i < 1500
+ if i != 33
+ call assert_equal(3000 - i, d[i])
+ else
+ call assert_equal(999, d[i])
+ endif
+ unlet d[i]
+ let i += 2
+ endwhile
+ call assert_equal({}, d)
+ unlet d
+endfunc
+
+" Dictionary function
+func Test_dict_func()
+ let d = {}
+ func d.func(a) dict
+ return a:a . len(self.data)
+ endfunc
+ let d.data = [1,2,3]
+ call assert_equal('len: 3', d.func('len: '))
+ let x = d.func('again: ')
+ call assert_equal('again: 3', x)
+ let Fn = d.func
+ call assert_equal('xxx3', Fn('xxx'))
+endfunc
+
+" Function in script-local List or Dict
+func Test_script_local_dict_func()
+ let g:dict = {}
+ function g:dict.func() dict
+ return 'g:dict.func' . self.foo[1] . self.foo[0]('asdf')
+ endfunc
+ let g:dict.foo = ['-', 2, 3]
+ call insert(g:dict.foo, function('strlen'))
+ call assert_equal('g:dict.func-4', g:dict.func())
+ unlet g:dict
+endfunc
+
+" Nasty: remove func from Dict that's being called (works)
+func Test_dict_func_remove_in_use()
+ let d = {1:1}
+ func d.func(a)
+ return "a:" . a:a
+ endfunc
+ let expected = 'a:' . string(get(d, 'func'))
+ call assert_equal(expected, d.func(string(remove(d, 'func'))))
+endfunc
+
+" Nasty: deepcopy() dict that refers to itself (fails when noref used)
+func Test_dict_deepcopy()
+ let d = {1:1, 2:2}
+ let l = [4, d, 6]
+ let d[3] = l
+ let dc = deepcopy(d)
+ call assert_fails('call deepcopy(d, 1)', 'E698')
+ let l2 = [0, l, l, 3]
+ let l[1] = l2
+ let l3 = deepcopy(l2)
+ call assert_true(l3[1] is l3[2])
+endfunc
+
+" Locked variables
+func Test_list_locked_var()
+ let expected = [
+ \ [['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1000-000', 'ppppppF'],
+ \ ['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1100-100', 'ppFppFF'],
+ \ ['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1110-110', 'pFFpFFF'],
+ \ ['0010-010', 'pFppFpp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1111-111', 'FFFFFFF'],
+ \ ['0011-011', 'FFpFFpp'],
+ \ ['0000-000', 'ppppppp']]
+ \ ]
+ for depth in range(5)
+ for u in range(3)
+ unlet! l
+ let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}]
+ exe "lockvar " . depth . " l"
+ if u == 1
+ exe "unlockvar l"
+ elseif u == 2
+ exe "unlockvar " . depth . " l"
+ endif
+ let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
+ call assert_equal(expected[depth][u][0], ps)
+ let ps = ''
+ try
+ let l[1][1][0] = 99
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ let l[1][1] = [99]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ let l[1] = [99]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ let l[2]['6'][7] = 99
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ let l[2][6] = {99: 99}
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ let l[2] = {99: 99}
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ let l = [99]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ call assert_equal(expected[depth][u][1], ps)
+ endfor
+ endfor
+endfunc
+
+" Unletting locked variables
+func Test_list_locked_var_unlet()
+ let expected = [
+ \ [['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1000-000', 'ppFppFp'],
+ \ ['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1100-100', 'pFFpFFp'],
+ \ ['0000-000', 'ppppppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1110-110', 'FFFFFFp'],
+ \ ['0010-010', 'FppFppp'],
+ \ ['0000-000', 'ppppppp']],
+ \ [['1111-111', 'FFFFFFp'],
+ \ ['0011-011', 'FppFppp'],
+ \ ['0000-000', 'ppppppp']]
+ \ ]
+
+ for depth in range(5)
+ for u in range(3)
+ unlet! l
+ let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}]
+ exe "lockvar " . depth . " l"
+ if u == 1
+ exe "unlockvar l"
+ elseif u == 2
+ exe "unlockvar " . depth . " l"
+ endif
+ let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
+ call assert_equal(expected[depth][u][0], ps)
+ let ps = ''
+ try
+ unlet l[2]['6'][7]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ unlet l[2][6]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ unlet l[2]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ unlet l[1][1][0]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ unlet l[1][1]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ unlet l[1]
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ try
+ unlet l
+ let ps .= 'p'
+ catch
+ let ps .= 'F'
+ endtry
+ call assert_equal(expected[depth][u][1], ps)
+ endfor
+ endfor
+endfunc
+
+" Locked variables and :unlet or list / dict functions
+
+" No :unlet after lock on dict:
+func Test_dict_lock_unlet()
+ unlet! d
+ let d = {'a': 99, 'b': 100}
+ lockvar 1 d
+ call assert_fails('unlet d.a', 'E741')
+endfunc
+
+" unlet after lock on dict item
+func Test_dict_item_lock_unlet()
+ unlet! d
+ let d = {'a': 99, 'b': 100}
+ lockvar d.a
+ unlet d.a
+ call assert_equal({'b' : 100}, d)
+endfunc
+
+" filter() after lock on dict item
+func Test_dict_lock_filter()
+ unlet! d
+ let d = {'a': 99, 'b': 100}
+ lockvar d.a
+ call filter(d, 'v:key != "a"')
+ call assert_equal({'b' : 100}, d)
+endfunc
+
+" map() after lock on dict
+func Test_dict_lock_map()
+ unlet! d
+ let d = {'a': 99, 'b': 100}
+ lockvar 1 d
+ call map(d, 'v:val + 200')
+ call assert_equal({'a' : 299, 'b' : 300}, d)
+endfunc
+
+" No extend() after lock on dict item
+func Test_dict_lock_extend()
+ unlet! d
+ let d = {'a': 99, 'b': 100}
+ lockvar d.a
+ call assert_fails("call extend(d, {'a' : 123})", 'E741')
+ call assert_equal({'a': 99, 'b': 100}, d)
+endfunc
+
+" No remove() of write-protected scope-level variable
+func! Tfunc(this_is_a_long_parameter_name)
+ call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E795')
+endfun
+func Test_dict_scope_var_remove()
+ call Tfunc('testval')
+endfunc
+
+" No extend() of write-protected scope-level variable
+func! Tfunc(this_is_a_long_parameter_name)
+ call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742')
+endfunc
+func Test_dict_scope_var_extend()
+ call Tfunc('testval')
+endfunc
+
+" No :unlet of variable in locked scope
+func Test_lock_var_unlet()
+ let b:testvar = 123
+ lockvar 1 b:
+ call assert_fails('unlet b:testvar', 'E741:')
+ unlockvar 1 b:
+ unlet! b:testvar
+endfunc
+
+" No :let += of locked list variable
+func Test_let_lock_list()
+ let l = ['a', 'b', 3]
+ lockvar 1 l
+ call assert_fails("let l += ['x']", 'E741:')
+ call assert_equal(['a', 'b', 3], l)
+
+ unlet l
+ let l = [1, 2, 3, 4]
+ lockvar! l
+ call assert_equal([1, 2, 3, 4], l)
+ unlockvar l[1]
+ call assert_fails('unlet l[0:1]', 'E741:')
+ call assert_equal([1, 2, 3, 4], l)
+ call assert_fails('unlet l[1:2]', 'E741:')
+ call assert_equal([1, 2, 3, 4], l)
+ unlockvar l[1]
+ call assert_fails('let l[0:1] = [0, 1]', 'E741:')
+ call assert_equal([1, 2, 3, 4], l)
+ call assert_fails('let l[1:2] = [0, 1]', 'E741:')
+ call assert_equal([1, 2, 3, 4], l)
+ unlet l
+endfunc
+
+" lockvar/islocked() triggering script autoloading
+func Test_lockvar_script_autoload()
+ let old_rtp = &rtp
+ set rtp+=./sautest
+ lockvar g:footest#x
+ unlockvar g:footest#x
+ call assert_equal(-1, islocked('g:footest#x'))
+ call assert_equal(0, exists('g:footest#x'))
+ call assert_equal(1, g:footest#x)
+ let &rtp = old_rtp
+endfunc
+
+" a:000 function argument test
+func s:arg_list_test(...)
+ call assert_fails('let a:000 = [1, 2]', 'E46:')
+ call assert_fails('let a:000[0] = 9', 'E742:')
+ call assert_fails('let a:000[2] = [9, 10]', 'E742:')
+ call assert_fails('let a:000[3] = {9 : 10}', 'E742:')
+
+ " now the tests that should pass
+ let a:000[2][1] = 9
+ call extend(a:000[2], [5, 6])
+ let a:000[3][5] = 8
+ let a:000[3]['a'] = 12
+ call assert_equal([1, 2, [3, 9, 5, 6], {'a': 12, '5': 8}], a:000)
+endfunc
+
+func Test_func_arg_list()
+ call s:arg_list_test(1, 2, [3, 4], {5: 6})
+endfunc
+
+" Tests for reverse(), sort(), uniq()
+func Test_reverse_sort_uniq()
+ let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5]
+ call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l)))
+ call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l))
+ call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l)))
+ call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l))
+ call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l)))
+ call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l))))
+ call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l)))
+
+ let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four']
+ call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n'))
+
+ let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []]
+ call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
+ call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
+ call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
+endfunc
+
+" splitting a string to a List
+func Test_str_split()
+ call assert_equal(['aa', 'bb'], split(' aa bb '))
+ call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0))
+ call assert_equal(['', 'aa', 'bb', ''], split(' aa bb ', '\W\+', 1))
+ call assert_equal(['', '', 'aa', '', 'bb', '', ''], split(' aa bb ', '\W', 1))
+ call assert_equal(['aa', '', 'bb'], split(':aa::bb:', ':', 0))
+ call assert_equal(['', 'aa', '', 'bb', ''], split(':aa::bb:', ':', 1))
+ call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1))
+ call assert_equal(['a', 'b', 'c'], split('abc', '\zs'))
+ call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1))
+endfunc
+
+" compare recursively linked list and dict
+func Test_listdict_compare()
+ let l = [1, 2, 3, 4]
+ let d = {'1': 1, '2': l, '3': 3}
+ let l[1] = d
+ call assert_true(l == l)
+ call assert_true(d == d)
+ call assert_false(l != deepcopy(l))
+ call assert_false(d != deepcopy(d))
+endfunc
+
+ " compare complex recursively linked list and dict
+func Test_listdict_compare_complex()
+ let l = []
+ call add(l, l)
+ let dict4 = {"l": l}
+ call add(dict4.l, dict4)
+ let lcopy = deepcopy(l)
+ let dict4copy = deepcopy(dict4)
+ call assert_true(l == lcopy)
+ call assert_true(dict4 == dict4copy)
+endfunc
+
+func Test_listdict_extend()
+ " Pass the same List to extend()
+ let l = [1, 2, 3, 4, 5]
+ call extend(l, l)
+ call assert_equal([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], l)
+
+ " Pass the same Dict to extend()
+ let d = { 'a': {'b': 'B'}}
+ call extend(d, d)
+ call assert_equal({'a': {'b': 'B'}}, d)
+
+ " Pass the same Dict to extend() with "error"
+ call assert_fails("call extend(d, d, 'error')", 'E737:')
+ call assert_equal({'a': {'b': 'B'}}, d)
+endfunc
diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim
new file mode 100644
index 0000000000..d28dbc444c
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr.vim
@@ -0,0 +1,238 @@
+" Test for linebreak and list option (non-utf8)
+
+" Nvim does not allow setting 'encoding', so skip this test.
+finish
+
+set encoding=latin1
+scriptencoding latin1
+
+if !exists("+linebreak") || !has("conceal")
+ finish
+endif
+
+source view_util.vim
+
+function s:screen_lines(lnum, width) abort
+ return ScreenLines(a:lnum, a:width)
+endfunction
+
+function! s:compare_lines(expect, actual)
+ call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
+endfunction
+
+function s:test_windows(...)
+ call NewWindow(10, 20)
+ setl ts=8 sw=4 sts=4 linebreak sbr= wrap
+ exe get(a:000, 0, '')
+endfunction
+
+function s:close_windows(...)
+ call CloseWindow()
+ exe get(a:000, 0, '')
+endfunction
+
+func Test_set_linebreak()
+ call s:test_windows('setl ts=4 sbr=+')
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ " abcdef ",
+\ "+hijklmn ",
+\ "+pqrstuvwxyz_1060ABC",
+\ "+DEFGHIJKLMNOP ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_list()
+ call s:test_windows('setl ts=4 sbr=+ list listchars=')
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "^Iabcdef hijklmn^I ",
+\ "+pqrstuvwxyz_1060ABC",
+\ "+DEFGHIJKLMNOP ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_nolist()
+ call s:test_windows('setl ts=4 sbr=+ nolist')
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ " abcdef ",
+\ "+hijklmn ",
+\ "+pqrstuvwxyz_1060ABC",
+\ "+DEFGHIJKLMNOP ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_should_break()
+ call s:test_windows('setl sbr=+ nolist')
+ call setline(1, "1\t" . repeat('a', winwidth(0)-2))
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "1 ",
+\ "+aaaaaaaaaaaaaaaaaa ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_conceal()
+ call s:test_windows('setl cpo&vim sbr=+ list conceallevel=2 concealcursor=nv listchars=tab:ab')
+ call setline(1, "_S_\t bla")
+ syn match ConcealVar contained /_/ conceal
+ syn match All /.*/ contains=ConcealVar
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "Sabbbbbb bla ",
+\ "~ ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_virtual_block()
+ call s:test_windows('setl sbr=+')
+ call setline(1, [
+\ "REMOVE: this not",
+\ "REMOVE: aaaaaaaaaaaaa",
+\ ])
+ exe "norm! 1/^REMOVE:"
+ exe "norm! 0\<C-V>jf x"
+ $put
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "this not ",
+\ "aaaaaaaaaaaaa ",
+\ "REMOVE: ",
+\ "REMOVE: ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_virtual_block_and_vbA()
+ call s:test_windows()
+ call setline(1, "long line: " . repeat("foobar ", 40) . "TARGET at end")
+ exe "norm! $3B\<C-v>eAx\<Esc>"
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ let expect = [
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar TARGETx at ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_virtual_char_and_block()
+ call s:test_windows()
+ call setline(1, "1111-1111-1111-11-1111-1111-1111")
+ exe "norm! 0f-lv3lc2222\<Esc>bgj."
+ let lines = s:screen_lines([1, 2], winwidth(0))
+ let expect = [
+\ "1111-2222-1111-11- ",
+\ "1111-2222-1111 ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_undo_after_block_visual()
+ call s:test_windows()
+ call setline(1, ["aaa", "aaa", "a"])
+ exe "norm! gg\<C-V>2j~e."
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ "AaA ",
+\ "AaA ",
+\ "A ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_norm_after_block_visual()
+ call s:test_windows()
+ call setline(1, ["abcd{ef", "ghijklm", "no}pgrs"])
+ exe "norm! ggf{\<C-V>\<C-V>c%"
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ "abcdpgrs ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_block_replace_after_wrapping()
+ call s:test_windows()
+ call setline(1, repeat("a", 150))
+ exe "norm! 0yypk147|\<C-V>jr0"
+ call assert_equal(repeat("a", 146) . "0aaa", getline(1))
+ call assert_equal(repeat("a", 146) . "0aaa", getline(2))
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ let expect = [
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaa0aaa ",
+\ "@ ",
+\ "@ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_with_listchars()
+ call s:test_windows('setl list listchars=space:_,trail:-,tab:>-,eol:$')
+ call setline(1, "a aaaaaaaaaaaaaaaaaaaaaa\ta ")
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ "a_ ",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aa>-----a-$ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_with_tab_and_skipping_first_chars()
+ call s:test_windows('setl list listchars=tab:>- ts=70 nowrap')
+ call setline(1, ["iiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa"])
+ call cursor(4,64)
+ norm! 2zl
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "---------------aaaaa",
+\ "---------------aaaaa",
+\ "---------------aaaaa",
+\ "iiiiiiiii>-----aaaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfu
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
new file mode 100644
index 0000000000..56a4cc9b31
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -0,0 +1,256 @@
+" Test for linebreak and list option in utf-8 mode
+
+set encoding=utf-8
+scriptencoding utf-8
+
+if !exists("+linebreak") || !has("conceal") || !has("signs")
+ finish
+endif
+
+source view_util.vim
+
+function s:screen_lines(lnum, width) abort
+ return ScreenLines(a:lnum, a:width)
+endfunction
+
+function! s:compare_lines(expect, actual)
+ call assert_equal(a:expect, a:actual)
+endfunction
+
+function s:screen_attr(lnum, chars, ...) abort
+ let line = getline(a:lnum)
+ let attr = []
+ let prefix = get(a:000, 0, 0)
+ for i in range(a:chars[0], a:chars[1])
+ let scol = strdisplaywidth(strcharpart(line, 0, i-1)) + 1
+ let attr += [screenattr(a:lnum, scol + prefix)]
+ endfor
+ return attr
+endfunction
+
+function s:test_windows(...)
+ call NewWindow(10, 20)
+ setl ts=4 sw=4 sts=4 linebreak sbr=+ wrap
+ exe get(a:000, 0, '')
+endfunction
+
+function s:close_windows(...)
+ call CloseWindow()
+ exe get(a:000, 0, '')
+endfunction
+
+func Test_linebreak_with_fancy_listchars()
+ call s:test_windows("setl list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP ")
+ redraw!
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "▕———abcdef ",
+\ "+hijklmn▕——— ",
+\ "+pqrstuvwxyz␣1060ABC",
+\ "+DEFGHIJKLMNOPˑ¶ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_nolinebreak_with_list()
+ call s:test_windows("setl nolinebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP ")
+ redraw!
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "▕———abcdef hijklmn▕—",
+\ "+pqrstuvwxyz␣1060ABC",
+\ "+DEFGHIJKLMNOPˑ¶ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_nolist()
+ call s:test_windows('setl nolist')
+ call setline(1, "\t*mask = nil;")
+ redraw!
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ " *mask = nil; ",
+\ "~ ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_and_concealing1()
+ call s:test_windows('setl list listchars=tab:>- cole=1')
+ call setline(1, [
+\ "#define ABCDE\t\t1",
+\ "#define ABCDEF\t\t1",
+\ "#define ABCDEFG\t\t1",
+\ "#define ABCDEFGH\t1",
+\ "#define MSG_MODE_FILE\t\t\t1",
+\ "#define MSG_MODE_CONSOLE\t\t2",
+\ "#define MSG_MODE_FILE_AND_CONSOLE\t3",
+\ "#define MSG_MODE_FILE_THEN_CONSOLE\t4",
+\ ])
+ vert resize 40
+ syn match Conceal conceal cchar=>'AB\|MSG_MODE'
+ redraw!
+ let lines = s:screen_lines([1, 7], winwidth(0))
+ let expect = [
+\ "#define ABCDE>-->---1 ",
+\ "#define >CDEF>-->---1 ",
+\ "#define >CDEFG>->---1 ",
+\ "#define >CDEFGH>----1 ",
+\ "#define >_FILE>--------->--->---1 ",
+\ "#define >_CONSOLE>---------->---2 ",
+\ "#define >_FILE_AND_CONSOLE>---------3 ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_and_concealing2()
+ call s:test_windows('setl nowrap ts=2 list listchars=tab:>- cole=2 concealcursor=n')
+ call setline(1, "bbeeeeee\t\t;\tsome text")
+ vert resize 40
+ syn clear
+ syn match meaning /;\s*\zs.*/
+ syn match hasword /^\x\{8}/ contains=word
+ syn match word /\<\x\{8}\>/ contains=beginword,endword contained
+ syn match beginword /\<\x\x/ contained conceal
+ syn match endword /\x\{6}\>/ contained
+ hi meaning guibg=blue
+ hi beginword guibg=green
+ hi endword guibg=red
+ redraw!
+ let lines = s:screen_lines([1, 1], winwidth(0))
+ let expect = [
+\ "eeeeee>--->-;>some text ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_screenattr_for_comment()
+ call s:test_windows("setl ft=c ts=7 list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, " /*\t\t and some more */")
+ norm! gg0
+ syntax on
+ hi SpecialKey term=underline ctermfg=red guifg=red
+ redraw!
+ let line = getline(1)
+ let attr = s:screen_attr(1, [1, 6])
+ call assert_notequal(attr[0], attr[1])
+ call assert_notequal(attr[1], attr[3])
+ call assert_notequal(attr[3], attr[5])
+ call s:close_windows()
+endfunc
+
+func Test_visual_block_and_selection_exclusive()
+ call s:test_windows('setl selection=exclusive')
+ call setline(1, "long line: " . repeat("foobar ", 40) . "TARGETÃ' at end")
+ exe "norm! $3B\<C-v>eAx\<Esc>"
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ let expect = [
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar TARGETÃx' ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_multibyte_sign_and_colorcolumn()
+ call s:test_windows("setl nolinebreak cc=3 list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, ["", "a b c", "a b c"])
+ exe "sign define foo text=\uff0b"
+ exe "sign place 1 name=foo line=2 buffer=" . bufnr('%')
+ redraw!
+ norm! ggj0
+ let signwidth = strdisplaywidth("\uff0b")
+ let attr1 = s:screen_attr(2, [1, 3], signwidth)
+ let attr2 = s:screen_attr(3, [1, 3], signwidth)
+ call assert_equal(attr1[0], attr2[0])
+ call assert_equal(attr1[1], attr2[1])
+ call assert_equal(attr1[2], attr2[2])
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ " ¶ ",
+\ "+a b c¶ ",
+\ " a b c¶ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_illegal_byte_and_breakat()
+ call s:test_windows("setl sbr= brk+=<")
+ vert resize 18
+ call setline(1, repeat("\x80", 6))
+ redraw!
+ let lines = s:screen_lines([1, 2], winwidth(0))
+ let expect = [
+\ "<80><80><80><80><8",
+\ "0><80> ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('setl brk&vim')
+endfunc
+
+func Test_multibyte_wrap_and_breakat()
+ call s:test_windows("setl sbr= brk+=>")
+ call setline(1, repeat('a', 17) . repeat('あ', 2))
+ redraw!
+ let lines = s:screen_lines([1, 2], winwidth(0))
+ let expect = [
+\ "aaaaaaaaaaaaaaaaaあ>",
+\ "あ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('setl brk&vim')
+endfunc
+
+func Test_chinese_char_on_wrap_column()
+ call s:test_windows("setl nolbr wrap sbr=")
+ syntax off
+ call setline(1, [
+\ 'aaaaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'hello'])
+ call cursor(1,1)
+ norm! $
+ redraw!
+ let expect=[
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中hello ']
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfu
diff --git a/src/nvim/testdir/test_makeencoding.py b/src/nvim/testdir/test_makeencoding.py
new file mode 100644
index 0000000000..041edadc0a
--- /dev/null
+++ b/src/nvim/testdir/test_makeencoding.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Test program for :make, :grep and :cgetfile.
+
+from __future__ import print_function, unicode_literals
+import locale
+import io
+import sys
+
+def set_output_encoding(enc=None):
+ """Set the encoding of stdout and stderr
+
+ arguments:
+ enc -- Encoding name.
+ If omitted, locale.getpreferredencoding() is used.
+ """
+ if enc is None:
+ enc = locale.getpreferredencoding()
+
+ def get_text_writer(fo, **kwargs):
+ kw = dict(kwargs)
+ kw.setdefault('errors', 'backslashreplace') # use \uXXXX style
+ kw.setdefault('closefd', False)
+
+ if sys.version_info[0] < 3:
+ # Work around for Python 2.x
+ # New line conversion isn't needed here. Done in somewhere else.
+ writer = io.open(fo.fileno(), mode='w', newline='', **kw)
+ write = writer.write # save the original write() function
+ enc = locale.getpreferredencoding()
+ def convwrite(s):
+ if isinstance(s, bytes):
+ write(s.decode(enc)) # convert to unistr
+ else:
+ write(s)
+ try:
+ writer.flush() # needed on Windows
+ except IOError:
+ pass
+ writer.write = convwrite
+ else:
+ writer = io.open(fo.fileno(), mode='w', **kw)
+ return writer
+
+ sys.stdout = get_text_writer(sys.stdout, encoding=enc)
+ sys.stderr = get_text_writer(sys.stderr, encoding=enc)
+
+
+def main():
+ enc = 'utf-8'
+ if len(sys.argv) > 1:
+ enc = sys.argv[1]
+ set_output_encoding(enc)
+
+ message_tbl = {
+ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好',
+ 'latin1': 'ÀÈÌÒÙ',
+ 'cp932': 'こんにちは',
+ 'cp936': '你好',
+ }
+
+ print('Xfoobar.c(10) : %s (%s)' % (message_tbl[enc], enc))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim
new file mode 100644
index 0000000000..a3d5538a47
--- /dev/null
+++ b/src/nvim/testdir/test_makeencoding.vim
@@ -0,0 +1,106 @@
+" Tests for 'makeencoding'.
+if !has('multi_byte')
+ finish
+endif
+
+source shared.vim
+
+let s:python = PythonProg()
+if s:python == ''
+ " Can't run this test.
+ finish
+endif
+
+let s:script = 'test_makeencoding.py'
+
+let s:message_tbl = {
+ \ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好',
+ \ 'latin1': 'ÀÈÌÒÙ',
+ \ 'cp932': 'こんにちは',
+ \ 'cp936': '你好',
+ \}
+
+
+" Tests for :cgetfile and :lgetfile.
+func Test_getfile()
+ set errorfile=Xerror.txt
+ set errorformat=%f(%l)\ :\ %m
+
+ " :cgetfile
+ for enc in keys(s:message_tbl)
+ let &makeencoding = enc
+ exec "silent !" . s:python . " " . s:script . " " . enc . " > " . &errorfile
+ cgetfile
+ copen
+ call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")",
+ \ getline('.'))
+ cclose
+ endfor
+
+ " :lgetfile
+ for enc in keys(s:message_tbl)
+ let &makeencoding = enc
+ exec "silent !" . s:python . " " . s:script . " " . enc . " > " . &errorfile
+ lgetfile
+ lopen
+ call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")",
+ \ getline('.'))
+ lclose
+ endfor
+
+ call delete(&errorfile)
+endfunc
+
+
+" Tests for :grep and :lgrep.
+func Test_grep()
+ let &grepprg = s:python
+ set grepformat=%f(%l)\ :\ %m
+
+ " :grep
+ for enc in keys(s:message_tbl)
+ let &makeencoding = enc
+ exec "silent grep! " . s:script . " " . enc
+ copen
+ call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")",
+ \ getline('.'))
+ cclose
+ endfor
+
+ " :lgrep
+ for enc in keys(s:message_tbl)
+ let &makeencoding = enc
+ exec "silent lgrep! " . s:script . " " . enc
+ lopen
+ call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")",
+ \ getline('.'))
+ lclose
+ endfor
+endfunc
+
+
+" Tests for :make and :lmake.
+func Test_make()
+ let &makeprg = s:python
+ set errorformat=%f(%l)\ :\ %m
+
+ " :make
+ for enc in keys(s:message_tbl)
+ let &makeencoding = enc
+ exec "silent make! " . s:script . " " . enc
+ copen
+ call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")",
+ \ getline('.'))
+ cclose
+ endfor
+
+ " :lmake
+ for enc in keys(s:message_tbl)
+ let &makeencoding = enc
+ exec "silent lmake! " . s:script . " " . enc
+ lopen
+ call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")",
+ \ getline('.'))
+ lclose
+ endfor
+endfunc
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 7f93ddd56e..f5e4c4b90c 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -110,6 +110,8 @@ func Test_map_langmap()
call feedkeys(":call append(line('$'), '+')\<CR>", "xt")
call assert_equal('+', getline('$'))
+ iunmap a
+ iunmap c
set nomodified
endfunc
@@ -120,7 +122,7 @@ func Test_map_feedkeys()
$-1
call feedkeys("0qqdw.ifoo\<Esc>qj0@q\<Esc>", "xt")
call assert_equal(['fooc d', 'fooc d'], getline(line('$') - 1, line('$')))
- unmap .
+ nunmap .
set nomodified
endfunc
diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim
index c788689e33..c11f1a84a9 100644
--- a/src/nvim/testdir/test_matchadd_conceal.vim
+++ b/src/nvim/testdir/test_matchadd_conceal.vim
@@ -277,6 +277,7 @@ function! Test_matchadd_and_syn_conceal()
call assert_notequal(screenattr(1, 11) , screenattr(1, 12))
call assert_equal(screenattr(1, 11) , screenattr(1, 32))
call matchadd('CheckedByCoq', '\%<2l\%>9c\%<16c')
+ redraw!
call assert_equal(expect, s:screenline(1))
call assert_notequal(screenattr(1, 10) , screenattr(1, 11))
call assert_notequal(screenattr(1, 11) , screenattr(1, 12))
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
new file mode 100644
index 0000000000..4774cf4af5
--- /dev/null
+++ b/src/nvim/testdir/test_mksession.vim
@@ -0,0 +1,155 @@
+" Test for :mksession, :mkview and :loadview in latin1 encoding
+
+scriptencoding latin1
+
+if !has('multi_byte') || !has('mksession')
+ finish
+endif
+
+func Test_mksession()
+ tabnew
+ let wrap_save = &wrap
+ set sessionoptions=buffers splitbelow fileencoding=latin1
+ call setline(1, [
+ \ 'start:',
+ \ 'no multibyte chAracter',
+ \ ' one leaDing tab',
+ \ ' four leadinG spaces',
+ \ 'two consecutive tabs',
+ \ 'two tabs in one line',
+ \ 'one multibyteCharacter',
+ \ 'a two multiByte characters',
+ \ 'A three mulTibyte characters'
+ \ ])
+ let tmpfile = 'Xtemp'
+ exec 'w! ' . tmpfile
+ /^start:
+ set wrap
+ vsplit
+ norm! j16|
+ split
+ norm! j16|
+ split
+ norm! j16|
+ split
+ norm! j8|
+ split
+ norm! j8|
+ split
+ norm! j16|
+ split
+ norm! j16|
+ split
+ norm! j16|
+ wincmd l
+
+ set nowrap
+ /^start:
+ norm! j16|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j08|3zl
+ split
+ norm! j08|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j016|3zl
+ split
+ call wincol()
+ mksession! Xtest_mks.out
+ let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"')
+ let expected = [
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ 'normal! 08|',
+ \ 'normal! 08|',
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
+ \ " normal! 08|",
+ \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
+ \ " normal! 08|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|"
+ \ ]
+ call assert_equal(expected, li)
+ tabclose!
+
+ call delete('Xtest_mks.out')
+ call delete(tmpfile)
+ let &wrap = wrap_save
+endfunc
+
+func Test_mksession_winheight()
+ new
+ set winheight=10 winminheight=2
+ mksession! Xtest_mks.out
+ source Xtest_mks.out
+
+ call delete('Xtest_mks.out')
+endfunc
+
+" Verify that arglist is stored correctly to the session file.
+func Test_mksession_arglist()
+ argdel *
+ next file1 file2 file3 file4
+ mksession! Xtest_mks.out
+ source Xtest_mks.out
+ call assert_equal(['file1', 'file2', 'file3', 'file4'], argv())
+
+ call delete('Xtest_mks.out')
+ argdel *
+endfunc
+
+
+func Test_mksession_one_buffer_two_windows()
+ edit Xtest1
+ new Xtest2
+ split
+ mksession! Xtest_mks.out
+ let lines = readfile('Xtest_mks.out')
+ let count1 = 0
+ let count2 = 0
+ let count2buf = 0
+ for line in lines
+ if line =~ 'edit \f*Xtest1$'
+ let count1 += 1
+ endif
+ if line =~ 'edit \f\{-}Xtest2'
+ let count2 += 1
+ endif
+ if line =~ 'buffer \f\{-}Xtest2'
+ let count2buf += 1
+ endif
+ endfor
+ call assert_equal(1, count1, 'Xtest1 count')
+ call assert_equal(2, count2, 'Xtest2 count')
+ call assert_equal(2, count2buf, 'Xtest2 buffer count')
+
+ close
+ bwipe!
+ call delete('Xtest_mks.out')
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim
new file mode 100644
index 0000000000..8ffbba2a1c
--- /dev/null
+++ b/src/nvim/testdir/test_mksession_utf8.vim
@@ -0,0 +1,105 @@
+" Test for :mksession, :mkview and :loadview in utf-8 encoding
+
+set encoding=utf-8
+scriptencoding utf-8
+
+if !has('multi_byte') || !has('mksession')
+ finish
+endif
+
+func Test_mksession_utf8()
+ tabnew
+ let wrap_save = &wrap
+ set sessionoptions=buffers splitbelow fileencoding=utf-8
+ call setline(1, [
+ \ 'start:',
+ \ 'no multibyte chAracter',
+ \ ' one leaDing tab',
+ \ ' four leadinG spaces',
+ \ 'two consecutive tabs',
+ \ 'two tabs in one line',
+ \ 'one … multibyteCharacter',
+ \ 'a “b” two multiByte characters',
+ \ '“c”1€ three mulTibyte characters'
+ \ ])
+ let tmpfile = tempname()
+ exec 'w! ' . tmpfile
+ /^start:
+ set wrap
+ vsplit
+ norm! j16|
+ split
+ norm! j16|
+ split
+ norm! j16|
+ split
+ norm! j8|
+ split
+ norm! j8|
+ split
+ norm! j16|
+ split
+ norm! j16|
+ split
+ norm! j16|
+ wincmd l
+
+ set nowrap
+ /^start:
+ norm! j16|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j08|3zl
+ split
+ norm! j08|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j016|3zl
+ split
+ norm! j016|3zl
+ split
+ call wincol()
+ mksession! test_mks.out
+ let li = filter(readfile('test_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"')
+ let expected = [
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ 'normal! 08|',
+ \ 'normal! 08|',
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ 'normal! 016|',
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
+ \ " normal! 08|",
+ \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
+ \ " normal! 08|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
+ \ " normal! 016|"
+ \ ]
+ call assert_equal(expected, li)
+ tabclose!
+
+ call delete('test_mks.out')
+ call delete(tmpfile)
+ let &wrap = wrap_save
+ set sessionoptions& splitbelow& fileencoding&
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_nested_function.vim b/src/nvim/testdir/test_nested_function.vim
index f881730529..afaaea6ceb 100644
--- a/src/nvim/testdir/test_nested_function.vim
+++ b/src/nvim/testdir/test_nested_function.vim
@@ -1,32 +1,63 @@
"Tests for nested functions
"
-function! NestedFunc()
- fu! Func1()
+func NestedFunc()
+ func! Func1()
let g:text .= 'Func1 '
- endfunction
+ endfunc
call Func1()
- fu! s:func2()
+ func! s:func2()
let g:text .= 's:func2 '
- endfunction
+ endfunc
call s:func2()
- fu! s:_func3()
+ func! s:_func3()
let g:text .= 's:_func3 '
- endfunction
+ endfunc
call s:_func3()
let fn = 'Func4'
- fu! {fn}()
+ func! {fn}()
let g:text .= 'Func4 '
- endfunction
+ endfunc
call {fn}()
let fn = 'func5'
- fu! s:{fn}()
+ func! s:{fn}()
let g:text .= 's:func5'
- endfunction
+ endfunc
call s:{fn}()
-endfunction
+endfunc
-function! Test_nested_functions()
+func Test_nested_functions()
let g:text = ''
call NestedFunc()
call assert_equal('Func1 s:func2 s:_func3 Func4 s:func5', g:text)
endfunction
+
+func Test_nested_argument()
+ func g:X()
+ let g:Y = function('sort')
+ endfunc
+ let g:Y = function('sort')
+ echo g:Y([], g:X())
+ delfunc g:X
+ unlet g:Y
+endfunc
+
+func Recurse(count)
+ if a:count > 0
+ call Recurse(a:count - 1)
+ endif
+endfunc
+
+func Test_max_nesting()
+ let call_depth_here = 2
+ let ex_depth_here = 5
+ set mfd&
+
+ call Recurse(99 - call_depth_here)
+ call assert_fails('call Recurse(' . (100 - call_depth_here) . ')', 'E132:')
+
+ set mfd=210
+ call Recurse(209 - ex_depth_here)
+ call assert_fails('call Recurse(' . (210 - ex_depth_here) . ')', 'E169:')
+
+ set mfd&
+endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 6261625801..1d15c7f83d 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1,5 +1,7 @@
" Test for various Normal mode commands
+source shared.vim
+
func! Setup_NewWindow()
10new
call setline(1, range(1,100))
@@ -1069,7 +1071,6 @@ func! Test_normal18_z_fold()
endfunc
func! Test_normal19_z_spell()
- throw "skipped: Nvim 'spell' requires download"
if !has("spell") || !has('syntax')
return
endif
@@ -1120,6 +1121,7 @@ func! Test_normal19_z_spell()
let a=execute('unsilent norm! V$zG')
call assert_match("Word '2 goood' added to .*", a)
let fname=matchstr(a, 'to\s\+\zs\f\+$')
+ let fname=Fix_truncated_tmpfile(fname)
let cnt=readfile(fname)
call assert_equal('2 goood', cnt[0])
@@ -1255,21 +1257,27 @@ func! Test_normal22_zet()
" Test for ZZ
" let shell = &shell
" let &shell = 'sh'
- call writefile(['1', '2'], 'Xfile')
+
+ " Remove any stale test files from previous run.
+ for file in ['Xfile_Test_normal22_zet']
+ call delete(file)
+ endfor
+
+ call writefile(['1', '2'], 'Xfile_Test_normal22_zet')
let args = ' --headless -u NONE -N -U NONE -i NONE --noplugins'
- call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile')
- let a = readfile('Xfile')
+ call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet')
+ let a = readfile('Xfile_Test_normal22_zet')
call assert_equal([], a)
" Test for ZQ
- call writefile(['1', '2'], 'Xfile')
- call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile')
- let a = readfile('Xfile')
+ call writefile(['1', '2'], 'Xfile_Test_normal22_zet')
+ call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet')
+ let a = readfile('Xfile_Test_normal22_zet')
call assert_equal(['1', '2'], a)
- " clean up
- for file in ['Xfile']
- call delete(file)
- endfor
+ " Nvim: This sometimes hangs the TSAN build.
+ " for file in ['Xfile_Test_normal22_zet']
+ " call delete(file)
+ " endfor
" let &shell = shell
endfunc
@@ -2329,3 +2337,45 @@ func! Test_normal54_Ctrl_bsl()
" clean up
bw!
endfunc
+
+" Test for the gr (virtual replace) command
+" Test for the bug fixed by 7.4.387
+func Test_gr_command()
+ enew!
+ let save_cpo = &cpo
+ call append(0, ['First line', 'Second line', 'Third line'])
+ exe "normal i\<C-G>u"
+ call cursor(2, 1)
+ set cpo-=X
+ normal 4gro
+ call assert_equal('oooond line', getline(2))
+ undo
+ set cpo+=X
+ normal 4gro
+ call assert_equal('ooooecond line', getline(2))
+ let &cpo = save_cpo
+ enew!
+endfunc
+
+" When splitting a window the changelist position is wrong.
+" Test the changelist position after splitting a window.
+" Test for the bug fixed by 7.4.386
+func Test_changelist()
+ let save_ul = &ul
+ enew!
+ call append('$', ['1', '2'])
+ exe "normal i\<C-G>u"
+ exe "normal Gkylpa\<C-G>u"
+ set ul=100
+ exe "normal Gylpa\<C-G>u"
+ set ul=100
+ normal gg
+ vsplit
+ normal g;
+ call assert_equal([3, 2], [line('.'), col('.')])
+ normal g;
+ call assert_equal([2, 2], [line('.'), col('.')])
+ call assert_fails('normal g;', 'E662:')
+ %bwipe!
+ let &ul = save_ul
+endfunc
diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim
new file mode 100644
index 0000000000..59debcea0d
--- /dev/null
+++ b/src/nvim/testdir/test_number.vim
@@ -0,0 +1,254 @@
+" Test for 'number' and 'relativenumber'
+
+source view_util.vim
+
+func! s:screen_lines(start, end) abort
+ return ScreenLines([a:start, a:end], 8)
+endfunc
+
+func! s:compare_lines(expect, actual)
+ call assert_equal(a:expect, a:actual)
+endfunc
+
+func! s:test_windows(h, w) abort
+ call NewWindow(a:h, a:w)
+endfunc
+
+func! s:close_windows() abort
+ call CloseWindow()
+endfunc
+
+func! s:validate_cursor() abort
+ " update skipcol.
+ " wincol():
+ " f_wincol
+ " -> validate_cursor
+ " -> curs_columns
+ call wincol()
+endfunc
+
+func Test_set_options()
+ set nu rnu
+ call assert_equal(1, &nu)
+ call assert_equal(1, &rnu)
+
+ call s:test_windows(10, 20)
+ call assert_equal(1, &nu)
+ call assert_equal(1, &rnu)
+ call s:close_windows()
+
+ set nu& rnu&
+endfunc
+
+func Test_set_global_and_local()
+ " setlocal must NOT reset the other global value
+ set nonu nornu
+ setglobal nu
+ setlocal rnu
+ call assert_equal(1, &g:nu)
+
+ set nonu nornu
+ setglobal rnu
+ setlocal nu
+ call assert_equal(1, &g:rnu)
+
+ " setglobal MUST reset the other global value
+ set nonu nornu
+ setglobal nu
+ setglobal rnu
+ call assert_equal(1, &g:nu)
+
+ set nonu nornu
+ setglobal rnu
+ setglobal nu
+ call assert_equal(1, &g:rnu)
+
+ " set MUST reset the other global value
+ set nonu nornu
+ set nu
+ set rnu
+ call assert_equal(1, &g:nu)
+
+ set nonu nornu
+ set rnu
+ set nu
+ call assert_equal(1, &g:rnu)
+
+ set nu& rnu&
+endfunc
+
+func Test_number()
+ call s:test_windows(10, 20)
+ call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"])
+ setl number
+ let lines = s:screen_lines(1, 4)
+ let expect = [
+\ " 1 abcd",
+\ " 2 klmn",
+\ " 3 uvwx",
+\ " 4 EFGH",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_relativenumber()
+ call s:test_windows(10, 20)
+ call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"])
+ 3
+ setl relativenumber
+ let lines = s:screen_lines(1, 6)
+ let expect = [
+\ " 2 abcd",
+\ " 1 klmn",
+\ " 0 uvwx",
+\ " 1 EFGH",
+\ " 2 OPQR",
+\ " 3 YZ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_number_with_relativenumber()
+ call s:test_windows(10, 20)
+ call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"])
+ 4
+ setl number relativenumber
+ let lines = s:screen_lines(1, 6)
+ let expect = [
+\ " 3 abcd",
+\ " 2 klmn",
+\ " 1 uvwx",
+\ "4 EFGH",
+\ " 1 OPQR",
+\ " 2 YZ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_number_with_linewrap1()
+ call s:test_windows(3, 20)
+ normal! 61ia
+ setl number wrap
+ call s:validate_cursor()
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ "--1 aaaa",
+\ " aaaa",
+\ " aaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI
+func XTest_number_with_linewrap2()
+ call s:test_windows(3, 20)
+ normal! 61ia
+ setl number wrap
+ call s:validate_cursor()
+ 0
+ call s:validate_cursor()
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 1 aaaa",
+\ " aaaa",
+\ " aaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI
+func XTest_number_with_linewrap3()
+ call s:test_windows(4, 20)
+ normal! 81ia
+ setl number wrap
+ call s:validate_cursor()
+ setl nonumber
+ call s:validate_cursor()
+ let lines = s:screen_lines(1, 4)
+ let expect = [
+\ "aaaaaaaa",
+\ "aaaaaaaa",
+\ "aaaaaaaa",
+\ "a ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_numberwidth()
+ call s:test_windows(10, 20)
+ call setline(1, repeat(['aaaa'], 10))
+ setl number numberwidth=6
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 1 aa",
+\ " 2 aa",
+\ " 3 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ set relativenumber
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ "1 aa",
+\ " 1 aa",
+\ " 2 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ set nonumber
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 0 aa",
+\ " 1 aa",
+\ " 2 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_numberwidth_adjusted()
+ call s:test_windows(10, 20)
+ call setline(1, repeat(['aaaa'], 10000))
+ setl number numberwidth=4
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 1 aa",
+\ " 2 aa",
+\ " 3 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ $
+ let lines = s:screen_lines(8, 10)
+ let expect = [
+\ " 9998 aa",
+\ " 9999 aa",
+\ "10000 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ setl relativenumber
+ let lines = s:screen_lines(8, 10)
+ let expect = [
+\ " 2 aa",
+\ " 1 aa",
+\ "10000 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ setl nonumber
+ let lines = s:screen_lines(8, 10)
+ let expect = [
+\ " 2 aaaa",
+\ " 1 aaaa",
+\ " 0 aaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 5ee0919e18..a15d15213a 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -13,6 +13,12 @@ function! Test_whichwrap()
set whichwrap+=h,l
call assert_equal('b,s,h,l', &whichwrap)
+ set whichwrap=h,h
+ call assert_equal('h', &whichwrap)
+
+ set whichwrap=h,h,h
+ call assert_equal('h', &whichwrap)
+
set whichwrap&
endfunction
@@ -97,3 +103,133 @@ func Test_keymap_valid()
call assert_fails(":set kmp=trunc\x00name", "E544:")
call assert_fails(":set kmp=trunc\x00name", "trunc")
endfunc
+
+func Check_dir_option(name)
+ " Check that it's possible to set the option.
+ exe 'set ' . a:name . '=/usr/share/dict/words'
+ call assert_equal('/usr/share/dict/words', eval('&' . a:name))
+ exe 'set ' . a:name . '=/usr/share/dict/words,/and/there'
+ call assert_equal('/usr/share/dict/words,/and/there', eval('&' . a:name))
+ exe 'set ' . a:name . '=/usr/share/dict\ words'
+ call assert_equal('/usr/share/dict words', eval('&' . a:name))
+
+ " Check rejecting weird characters.
+ call assert_fails("set " . a:name . "=/not&there", "E474:")
+ call assert_fails("set " . a:name . "=/not>there", "E474:")
+ call assert_fails("set " . a:name . "=/not.*there", "E474:")
+endfunc
+
+func Test_cinkeys()
+ " This used to cause invalid memory access
+ set cindent cinkeys=0
+ norm a
+ set cindent& cinkeys&
+endfunc
+
+func Test_dictionary()
+ call Check_dir_option('dictionary')
+endfunc
+
+func Test_thesaurus()
+ call Check_dir_option('thesaurus')
+endfun
+
+func Test_set_completion()
+ call feedkeys(":set di\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set dictionary diff diffexpr diffopt digraph directory display', @:)
+
+ " Expand boolan options. When doing :set no<Tab>
+ " vim displays the options names without "no" but completion uses "no...".
+ call feedkeys(":set nodi\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set nodiff digraph', @:)
+
+ call feedkeys(":set invdi\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set invdiff digraph', @:)
+
+ " Expand abbreviation of options.
+ call feedkeys(":set ts\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set tabstop thesaurus', @:)
+
+ " Expand current value
+ call feedkeys(":set fileencodings=\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set fileencodings=ucs-bom,utf-8,default,latin1', @:)
+
+ call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:)
+
+ " Expand directories.
+ call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('./samples/ ', @:)
+ call assert_notmatch('./small.vim ', @:)
+
+ " Expand files and directories.
+ call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('./samples/ ./sautest/ ./setup.vim ./shared.vim', @:)
+
+ call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)
+endfunc
+
+func Test_set_errors()
+ call assert_fails('set scroll=-1', 'E49:')
+ call assert_fails('set backupcopy=', 'E474:')
+ call assert_fails('set regexpengine=3', 'E474:')
+ call assert_fails('set history=10001', 'E474:')
+ call assert_fails('set numberwidth=11', 'E474:')
+ call assert_fails('set colorcolumn=-a')
+ call assert_fails('set colorcolumn=a')
+ call assert_fails('set colorcolumn=1,')
+ call assert_fails('set cmdheight=-1', 'E487:')
+ call assert_fails('set cmdwinheight=-1', 'E487:')
+ if has('conceal')
+ call assert_fails('set conceallevel=-1', 'E487:')
+ call assert_fails('set conceallevel=4', 'E474:')
+ endif
+ call assert_fails('set helpheight=-1', 'E487:')
+ call assert_fails('set history=-1', 'E487:')
+ call assert_fails('set report=-1', 'E487:')
+ call assert_fails('set shiftwidth=-1', 'E487:')
+ call assert_fails('set sidescroll=-1', 'E487:')
+ call assert_fails('set tabstop=-1', 'E487:')
+ call assert_fails('set textwidth=-1', 'E487:')
+ call assert_fails('set timeoutlen=-1', 'E487:')
+ call assert_fails('set updatecount=-1', 'E487:')
+ call assert_fails('set updatetime=-1', 'E487:')
+ call assert_fails('set winheight=-1', 'E487:')
+ call assert_fails('set tabstop!', 'E488:')
+ call assert_fails('set xxx', 'E518:')
+ call assert_fails('set beautify?', 'E518:')
+ call assert_fails('set undolevels=x', 'E521:')
+ call assert_fails('set tabstop=', 'E521:')
+ call assert_fails('set comments=-', 'E524:')
+ call assert_fails('set comments=a', 'E525:')
+ call assert_fails('set foldmarker=x', 'E536:')
+ call assert_fails('set commentstring=x', 'E537:')
+ call assert_fails('set complete=x', 'E539:')
+ call assert_fails('set statusline=%{', 'E540:')
+ call assert_fails('set statusline=' . repeat("%p", 81), 'E541:')
+ call assert_fails('set statusline=%(', 'E542:')
+ if has('cursorshape')
+ " This invalid value for 'guicursor' used to cause Vim to crash.
+ call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:')
+ call assert_fails('set guicursor=i-ci', 'E545:')
+ call assert_fails('set guicursor=x', 'E545:')
+ call assert_fails('set guicursor=r-cr:horx', 'E548:')
+ call assert_fails('set guicursor=r-cr:hor0', 'E549:')
+ endif
+ call assert_fails('set backupext=~ patchmode=~', 'E589:')
+ call assert_fails('set winminheight=10 winheight=9', 'E591:')
+ call assert_fails('set winminwidth=10 winwidth=9', 'E592:')
+ call assert_fails("set showbreak=\x01", 'E595:')
+ call assert_fails('set t_foo=', 'E846:')
+endfunc
+
+func Test_complete()
+ " Trailing single backslash used to cause invalid memory access.
+ set complete=s\
+ new
+ call feedkeys("i\<C-N>\<Esc>", 'xt')
+ bwipe!
+ set complete&
+endfun
+
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 519d855cd8..f5bdd717dd 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -7,10 +7,10 @@ func! ListMonths()
if g:setting != ''
exe ":set" g:setting
endif
- let mth=copy(g:months)
+ let mth = copy(g:months)
let entered = strcharpart(getline('.'),0,col('.'))
if !empty(entered)
- let mth=filter(mth, 'v:val=~"^".entered')
+ let mth = filter(mth, 'v:val=~"^".entered')
endif
call complete(1, mth)
return ''
@@ -468,7 +468,7 @@ endfunc
" auto-wrap text.
func Test_completion_ctrl_e_without_autowrap()
new
- let tw_save=&tw
+ let tw_save = &tw
set tw=78
let li = [
\ '" zzz',
@@ -478,7 +478,7 @@ func Test_completion_ctrl_e_without_autowrap()
call feedkeys("A\<C-X>\<C-N>\<C-E>\<Esc>", "tx")
call assert_equal(li, getline(1, '$'))
- let &tw=tw_save
+ let &tw = tw_save
q!
endfunc
@@ -541,4 +541,71 @@ func Test_completion_comment_formatting()
bwipe!
endfunc
+function! DummyCompleteSix()
+ call complete(1, ['Hello', 'World'])
+ return ''
+endfunction
+
+" complete() correctly clears the list of autocomplete candidates
+func Test_completion_clear_candidate_list()
+ new
+ %d
+ " select first entry from the completion popup
+ call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>", "tx")
+ call assert_equal('Hello', getline(1))
+ %d
+ " select second entry from the completion popup
+ call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>", "tx")
+ call assert_equal('World', getline(1))
+ %d
+ " select original text
+ call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>", "tx")
+ call assert_equal(' xxx', getline(1))
+ %d
+ " back at first entry from completion list
+ call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>\<C-N>", "tx")
+ call assert_equal('Hello', getline(1))
+
+ bw!
+endfunc
+
+
+func Test_popup_and_preview_autocommand()
+ " This used to crash Vim
+ if !has('python')
+ return
+ endif
+ let h = winheight(0)
+ if h < 15
+ return
+ endif
+ new
+ augroup MyBufAdd
+ au!
+ au BufAdd * nested tab sball
+ augroup END
+ set omnifunc=pythoncomplete#Complete
+ call setline(1, 'import os')
+ " make the line long
+ call setline(2, ' os.')
+ $
+ call feedkeys("A\<C-X>\<C-O>\<C-N>\<C-N>\<C-N>\<enter>\<esc>", 'tx')
+ call assert_equal("import os", getline(1))
+ call assert_match(' os.\(EX_IOERR\|O_CREAT\)$', getline(2))
+ call assert_equal(1, winnr('$'))
+ " previewwindow option is not set
+ call assert_equal(0, &previewwindow)
+ norm! gt
+ call assert_equal(0, &previewwindow)
+ norm! gT
+ call assert_equal(12, tabpagenr('$'))
+ tabonly
+ pclose
+ augroup MyBufAdd
+ au!
+ augroup END
+ augroup! MyBufAdd
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim
new file mode 100644
index 0000000000..4cbd800da5
--- /dev/null
+++ b/src/nvim/testdir/test_profile.vim
@@ -0,0 +1,183 @@
+" Test Vim profiler
+if !has('profile')
+ finish
+endif
+
+func Test_profile_func()
+ let lines = [
+ \ "func! Foo1()",
+ \ "endfunc",
+ \ "func! Foo2()",
+ \ " let l:count = 100",
+ \ " while l:count > 0",
+ \ " let l:count = l:count - 1",
+ \ " endwhile",
+ \ "endfunc",
+ \ "func! Foo3()",
+ \ "endfunc",
+ \ "func! Bar()",
+ \ "endfunc",
+ \ "call Foo1()",
+ \ "call Foo1()",
+ \ "profile pause",
+ \ "call Foo1()",
+ \ "profile continue",
+ \ "call Foo2()",
+ \ "call Foo3()",
+ \ "call Bar()",
+ \ "if !v:profiling",
+ \ " delfunc Foo2",
+ \ "endif",
+ \ "delfunc Foo3",
+ \ ]
+
+ call writefile(lines, 'Xprofile_func.vim')
+ call system(v:progpath
+ \ . ' -es -u NONE -U NONE -i NONE --noplugin'
+ \ . ' -c "profile start Xprofile_func.log"'
+ \ . ' -c "profile func Foo*"'
+ \ . ' -c "so Xprofile_func.vim"'
+ \ . ' -c "qall!"')
+ call assert_equal(0, v:shell_error)
+
+ let lines = readfile('Xprofile_func.log')
+
+ " - Foo1() is called 3 times but should be reported as called twice
+ " since one call is in between "profile pause" .. "profile continue".
+ " - Foo2() should come before Foo1() since Foo1() does much more work.
+ " - Foo3() is not reported because function is deleted.
+ " - Unlike Foo3(), Foo2() should not be deleted since there is a check
+ " for v:profiling.
+ " - Bar() is not reported since it does not match "profile func Foo*".
+ call assert_equal(28, len(lines))
+
+ call assert_equal('FUNCTION Foo1()', lines[0])
+ call assert_equal('Called 2 times', lines[1])
+ call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2])
+ call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3])
+ call assert_equal('', lines[4])
+ call assert_equal('count total (s) self (s)', lines[5])
+ call assert_equal('', lines[6])
+ call assert_equal('FUNCTION Foo2()', lines[7])
+ call assert_equal('Called 1 time', lines[8])
+ call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[9])
+ call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[10])
+ call assert_equal('', lines[11])
+ call assert_equal('count total (s) self (s)', lines[12])
+ call assert_match('^\s*1\s\+.*\slet l:count = 100$', lines[13])
+ call assert_match('^\s*101\s\+.*\swhile l:count > 0$', lines[14])
+ call assert_match('^\s*100\s\+.*\s let l:count = l:count - 1$', lines[15])
+ call assert_match('^\s*100\s\+.*\sendwhile$', lines[16])
+ call assert_equal('', lines[17])
+ call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[18])
+ call assert_equal('count total (s) self (s) function', lines[19])
+ call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[20])
+ call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[21])
+ call assert_equal('', lines[22])
+ call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[23])
+ call assert_equal('count total (s) self (s) function', lines[24])
+ call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[25])
+ call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[26])
+ call assert_equal('', lines[27])
+
+ call delete('Xprofile_func.vim')
+ call delete('Xprofile_func.log')
+endfunc
+
+func Test_profile_file()
+ let lines = [
+ \ 'func! Foo()',
+ \ 'endfunc',
+ \ 'for i in range(10)',
+ \ ' " a comment',
+ \ ' call Foo()',
+ \ 'endfor',
+ \ 'call Foo()',
+ \ ]
+
+ call writefile(lines, 'Xprofile_file.vim')
+ call system(v:progpath
+ \ . ' -es -u NONE -U NONE -i NONE --noplugin'
+ \ . ' -c "profile start Xprofile_file.log"'
+ \ . ' -c "profile file Xprofile_file.vim"'
+ \ . ' -c "so Xprofile_file.vim"'
+ \ . ' -c "so Xprofile_file.vim"'
+ \ . ' -c "qall!"')
+ call assert_equal(0, v:shell_error)
+
+ let lines = readfile('Xprofile_file.log')
+
+ call assert_equal(14, len(lines))
+
+ call assert_match('^SCRIPT .*Xprofile_file.vim$', lines[0])
+ call assert_equal('Sourced 2 times', lines[1])
+ call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2])
+ call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3])
+ call assert_equal('', lines[4])
+ call assert_equal('count total (s) self (s)', lines[5])
+ call assert_match(' 2 0.\d\+ func! Foo()', lines[6])
+ call assert_equal(' endfunc', lines[7])
+ " Loop iterates 10 times. Since script runs twice, body executes 20 times.
+ " First line of loop executes one more time than body to detect end of loop.
+ call assert_match('^\s*22\s\+\d\+\.\d\+\s\+for i in range(10)$', lines[8])
+ call assert_equal(' " a comment', lines[9])
+ " if self and total are equal we only get one number
+ call assert_match('^\s*20\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[10])
+ call assert_match('^\s*20\s\+\d\+\.\d\+\s\+endfor$', lines[11])
+ " if self and total are equal we only get one number
+ call assert_match('^\s*2\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[12])
+ call assert_equal('', lines[13])
+
+ call delete('Xprofile_file.vim')
+ call delete('Xprofile_file.log')
+endfunc
+
+func Test_profile_file_with_cont()
+ let lines = [
+ \ 'echo "hello',
+ \ ' \ world"',
+ \ 'echo "foo ',
+ \ ' \bar"',
+ \ ]
+
+ call writefile(lines, 'Xprofile_file.vim')
+ call system(v:progpath
+ \ . ' -es -u NONE -U NONE -i NONE --noplugin'
+ \ . ' -c "profile start Xprofile_file.log"'
+ \ . ' -c "profile file Xprofile_file.vim"'
+ \ . ' -c "so Xprofile_file.vim"'
+ \ . ' -c "qall!"')
+ call assert_equal(0, v:shell_error)
+
+ let lines = readfile('Xprofile_file.log')
+ call assert_equal(11, len(lines))
+
+ call assert_match('^SCRIPT .*Xprofile_file.vim$', lines[0])
+ call assert_equal('Sourced 1 time', lines[1])
+ call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2])
+ call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3])
+ call assert_equal('', lines[4])
+ call assert_equal('count total (s) self (s)', lines[5])
+ call assert_match(' 1 0.\d\+ echo "hello', lines[6])
+ call assert_equal(' \ world"', lines[7])
+ call assert_match(' 1 0.\d\+ echo "foo ', lines[8])
+ call assert_equal(' \bar"', lines[9])
+ call assert_equal('', lines[10])
+
+ call delete('Xprofile_file.vim')
+ call delete('Xprofile_file.log')
+endfunc
+
+func Test_profile_completion()
+ call feedkeys(":profile \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profile continue dump file func pause start stop', @:)
+
+ call feedkeys(":profile start test_prof\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^"profile start.* test_profile\.vim', @:)
+endfunc
+
+func Test_profile_errors()
+ call assert_fails("profile func Foo", 'E750:')
+ call assert_fails("profile pause", 'E750:')
+ call assert_fails("profile continue", 'E750:')
+endfunc
diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim
new file mode 100644
index 0000000000..38c812bc9c
--- /dev/null
+++ b/src/nvim/testdir/test_put.vim
@@ -0,0 +1,36 @@
+
+func Test_put_block()
+ if !has('multi_byte')
+ return
+ endif
+ new
+ call feedkeys("i\<C-V>u2500\<CR>x\<ESC>", 'x')
+ call feedkeys("\<C-V>y", 'x')
+ call feedkeys("gg0p", 'x')
+ call assert_equal("\u2500x", getline(1))
+ bwipe!
+endfunc
+
+func Test_put_char_block()
+ new
+ call setline(1, ['Line 1', 'Line 2'])
+ f Xfile_put
+ " visually select both lines and put the cursor at the top of the visual
+ " selection and then put the buffer name over it
+ exe "norm! G0\<c-v>ke\"%p"
+ call assert_equal(['Xfile_put 1', 'Xfile_put 2'], getline(1,2))
+ bw!
+endfunc
+
+func Test_put_char_block2()
+ new
+ let a = [ getreg('a'), getregtype('a') ]
+ call setreg('a', ' one ', 'v')
+ call setline(1, ['Line 1', '', 'Line 3', ''])
+ " visually select the first 3 lines and put register a over it
+ exe "norm! ggl\<c-v>2j2l\"ap"
+ call assert_equal(['L one 1', '', 'L one 3', ''], getline(1,4))
+ " clean up
+ bw!
+ call setreg('a', a[0], a[1])
+endfunc
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 75ab01f013..dd177fd633 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -6,7 +6,7 @@ endif
set encoding=utf-8
-function! s:setup_commands(cchar)
+func s:setup_commands(cchar)
if a:cchar == 'c'
command! -nargs=* -bang Xlist <mods>clist<bang> <args>
command! -nargs=* Xgetexpr <mods>cgetexpr <args>
@@ -24,19 +24,21 @@ function! s:setup_commands(cchar)
command! -nargs=* Xgetbuffer <mods>cgetbuffer <args>
command! -nargs=* Xaddbuffer <mods>caddbuffer <args>
command! -nargs=* Xrewind <mods>crewind <args>
- command! -nargs=* -bang Xnext <mods>cnext<bang> <args>
- command! -nargs=* -bang Xprev <mods>cprev<bang> <args>
+ command! -count -nargs=* -bang Xnext <mods><count>cnext<bang> <args>
+ command! -count -nargs=* -bang Xprev <mods><count>cprev<bang> <args>
command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args>
command! -nargs=* -bang Xlast <mods>clast<bang> <args>
command! -nargs=* -bang Xnfile <mods>cnfile<bang> <args>
command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args>
command! -nargs=* Xexpr <mods>cexpr <args>
- command! -nargs=* Xvimgrep <mods>vimgrep <args>
+ command! -range -nargs=* Xvimgrep <mods><count>vimgrep <args>
+ command! -nargs=* Xvimgrepadd <mods>vimgrepadd <args>
command! -nargs=* Xgrep <mods> grep <args>
command! -nargs=* Xgrepadd <mods> grepadd <args>
command! -nargs=* Xhelpgrep helpgrep <args>
let g:Xgetlist = function('getqflist')
let g:Xsetlist = function('setqflist')
+ call setqflist([], 'f')
else
command! -nargs=* -bang Xlist <mods>llist<bang> <args>
command! -nargs=* Xgetexpr <mods>lgetexpr <args>
@@ -54,26 +56,31 @@ function! s:setup_commands(cchar)
command! -nargs=* Xgetbuffer <mods>lgetbuffer <args>
command! -nargs=* Xaddbuffer <mods>laddbuffer <args>
command! -nargs=* Xrewind <mods>lrewind <args>
- command! -nargs=* -bang Xnext <mods>lnext<bang> <args>
- command! -nargs=* -bang Xprev <mods>lprev<bang> <args>
+ command! -count -nargs=* -bang Xnext <mods><count>lnext<bang> <args>
+ command! -count -nargs=* -bang Xprev <mods><count>lprev<bang> <args>
command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args>
command! -nargs=* -bang Xlast <mods>llast<bang> <args>
command! -nargs=* -bang Xnfile <mods>lnfile<bang> <args>
command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args>
command! -nargs=* Xexpr <mods>lexpr <args>
- command! -nargs=* Xvimgrep <mods>lvimgrep <args>
+ command! -range -nargs=* Xvimgrep <mods><count>lvimgrep <args>
+ command! -nargs=* Xvimgrepadd <mods>lvimgrepadd <args>
command! -nargs=* Xgrep <mods> lgrep <args>
command! -nargs=* Xgrepadd <mods> lgrepadd <args>
command! -nargs=* Xhelpgrep lhelpgrep <args>
let g:Xgetlist = function('getloclist', [0])
let g:Xsetlist = function('setloclist', [0])
+ call setloclist(0, [], 'f')
endif
-endfunction
+endfunc
" Tests for the :clist and :llist commands
-function XlistTests(cchar)
+func XlistTests(cchar)
call s:setup_commands(a:cchar)
+ if a:cchar == 'l'
+ call assert_fails('llist', 'E776:')
+ endif
" With an empty list, command should return error
Xgetexpr []
silent! Xlist
@@ -85,62 +92,68 @@ function XlistTests(cchar)
\ 'non-error 3', 'Xtestfile3:3:1:Line3']
" List only valid entries
- redir => result
- Xlist
- redir END
- let l = split(result, "\n")
+ let l = split(execute('Xlist', ''), "\n")
call assert_equal([' 2 Xtestfile1:1 col 3: Line1',
\ ' 4 Xtestfile2:2 col 2: Line2',
\ ' 6 Xtestfile3:3 col 1: Line3'], l)
" List all the entries
- redir => result
- Xlist!
- redir END
- let l = split(result, "\n")
+ let l = split(execute('Xlist!', ''), "\n")
call assert_equal([' 1: non-error 1', ' 2 Xtestfile1:1 col 3: Line1',
\ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2',
\ ' 5: non-error 3', ' 6 Xtestfile3:3 col 1: Line3'], l)
" List a range of errors
- redir => result
- Xlist 3,6
- redir END
- let l = split(result, "\n")
+ let l = split(execute('Xlist 3,6', ''), "\n")
call assert_equal([' 4 Xtestfile2:2 col 2: Line2',
\ ' 6 Xtestfile3:3 col 1: Line3'], l)
- redir => result
- Xlist! 3,4
- redir END
- let l = split(result, "\n")
+ let l = split(execute('Xlist! 3,4', ''), "\n")
call assert_equal([' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l)
- redir => result
- Xlist -6,-4
- redir END
- let l = split(result, "\n")
+ let l = split(execute('Xlist -6,-4', ''), "\n")
call assert_equal([' 2 Xtestfile1:1 col 3: Line1'], l)
- redir => result
- Xlist! -5,-3
- redir END
- let l = split(result, "\n")
+ let l = split(execute('Xlist! -5,-3', ''), "\n")
+ call assert_equal([' 2 Xtestfile1:1 col 3: Line1',
+ \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l)
+
+ " Test for '+'
+ let l = split(execute('Xlist! +2', ''), "\n")
call assert_equal([' 2 Xtestfile1:1 col 3: Line1',
\ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l)
-endfunction
-function Test_clist()
+ " Different types of errors
+ call g:Xsetlist([{'lnum':10,'col':5,'type':'W', 'text':'Warning','nr':11},
+ \ {'lnum':20,'col':10,'type':'e','text':'Error','nr':22},
+ \ {'lnum':30,'col':15,'type':'i','text':'Info','nr':33},
+ \ {'lnum':40,'col':20,'type':'x', 'text':'Other','nr':44},
+ \ {'lnum':50,'col':25,'type':"\<C-A>",'text':'one','nr':55}])
+ let l = split(execute('Xlist', ""), "\n")
+ call assert_equal([' 1:10 col 5 warning 11: Warning',
+ \ ' 2:20 col 10 error 22: Error',
+ \ ' 3:30 col 15 info 33: Info',
+ \ ' 4:40 col 20 x 44: Other',
+ \ ' 5:50 col 25 55: one'], l)
+
+ " Error cases
+ call assert_fails('Xlist abc', 'E488:')
+endfunc
+
+func Test_clist()
call XlistTests('c')
call XlistTests('l')
-endfunction
+endfunc
" Tests for the :colder, :cnewer, :lolder and :lnewer commands
" Note that this test assumes that a quickfix/location list is
" already set by the caller.
-function XageTests(cchar)
+func XageTests(cchar)
call s:setup_commands(a:cchar)
+ let list = [{'bufnr': bufnr('%'), 'lnum': 1}]
+ call g:Xsetlist(list)
+
" Jumping to a non existent list should return error
silent! Xolder 99
call assert_true(v:errmsg ==# 'E380: At bottom of quickfix stack')
@@ -171,22 +184,23 @@ function XageTests(cchar)
Xnewer 2
let l = g:Xgetlist()
call assert_equal('Line3', l[0].text)
-endfunction
+endfunc
-function Test_cage()
- let list = [{'bufnr': 1, 'lnum': 1}]
- call setqflist(list)
+func Test_cage()
call XageTests('c')
-
- call setloclist(0, list)
call XageTests('l')
-endfunction
+endfunc
" Tests for the :cwindow, :lwindow :cclose, :lclose, :copen and :lopen
" commands
-function XwindowTests(cchar)
+func XwindowTests(cchar)
call s:setup_commands(a:cchar)
+ " Opening the location list window without any errors should fail
+ if a:cchar == 'l'
+ call assert_fails('lopen', 'E776:')
+ endif
+
" Create a list with no valid entries
Xgetexpr ['non-error 1', 'non-error 2', 'non-error 3']
@@ -227,16 +241,29 @@ function XwindowTests(cchar)
" Calling cwindow should close the quickfix window with no valid errors
Xwindow
call assert_true(winnr('$') == 1)
-endfunction
-function Test_cwindow()
+ if a:cchar == 'c'
+ " Opening the quickfix window in multiple tab pages should reuse the
+ " quickfix buffer
+ Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2',
+ \ 'Xtestfile3:3:1:Line3']
+ Xopen
+ let qfbufnum = bufnr('%')
+ tabnew
+ Xopen
+ call assert_equal(qfbufnum, bufnr('%'))
+ new | only | tabonly
+ endif
+endfunc
+
+func Test_cwindow()
call XwindowTests('c')
call XwindowTests('l')
-endfunction
+endfunc
" Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile
" commands.
-function XfileTests(cchar)
+func XfileTests(cchar)
call s:setup_commands(a:cchar)
call writefile(['Xtestfile1:700:10:Line 700',
@@ -275,16 +302,16 @@ function XfileTests(cchar)
\ l[1].lnum == 333 && l[1].col == 88 && l[1].text ==# 'Line 333')
call delete('Xqftestfile1')
-endfunction
+endfunc
-function Test_cfile()
+func Test_cfile()
call XfileTests('c')
call XfileTests('l')
-endfunction
+endfunc
" Tests for the :cbuffer, :lbuffer, :caddbuffer, :laddbuffer, :cgetbuffer and
" :lgetbuffer commands.
-function XbufferTests(cchar)
+func XbufferTests(cchar)
call s:setup_commands(a:cchar)
enew!
@@ -316,35 +343,61 @@ function XbufferTests(cchar)
\ l[3].lnum == 750 && l[3].col == 25 && l[3].text ==# 'Line 750')
enew!
-endfunction
+ " Check for invalid buffer
+ call assert_fails('Xbuffer 199', 'E474:')
-function Test_cbuffer()
+ " Check for unloaded buffer
+ edit Xtestfile1
+ let bnr = bufnr('%')
+ enew!
+ call assert_fails('Xbuffer ' . bnr, 'E681:')
+
+ " Check for invalid range
+ " Using Xbuffer will not run the range check in the cbuffer/lbuffer
+ " commands. So directly call the commands.
+ if (a:cchar == 'c')
+ call assert_fails('900,999cbuffer', 'E16:')
+ else
+ call assert_fails('900,999lbuffer', 'E16:')
+ endif
+endfunc
+
+func Test_cbuffer()
call XbufferTests('c')
call XbufferTests('l')
-endfunction
+endfunc
-function XexprTests(cchar)
+func XexprTests(cchar)
call s:setup_commands(a:cchar)
call assert_fails('Xexpr 10', 'E777:')
-endfunction
+endfunc
-function Test_cexpr()
+func Test_cexpr()
call XexprTests('c')
call XexprTests('l')
-endfunction
+endfunc
" Tests for :cnext, :cprev, :cfirst, :clast commands
-function Xtest_browse(cchar)
+func Xtest_browse(cchar)
call s:setup_commands(a:cchar)
+ " Jumping to first or next location list entry without any error should
+ " result in failure
+ if a:cchar == 'l'
+ call assert_fails('lfirst', 'E776:')
+ call assert_fails('lnext', 'E776:')
+ endif
+
call s:create_test_file('Xqftestfile1')
call s:create_test_file('Xqftestfile2')
Xgetexpr ['Xqftestfile1:5:Line5',
\ 'Xqftestfile1:6:Line6',
\ 'Xqftestfile2:10:Line10',
- \ 'Xqftestfile2:11:Line11']
+ \ 'Xqftestfile2:11:Line11',
+ \ 'RegularLine1',
+ \ 'RegularLine2']
Xfirst
call assert_fails('Xprev', 'E553')
@@ -356,6 +409,7 @@ function Xtest_browse(cchar)
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal(6, line('.'))
Xlast
+ Xprev
call assert_equal('Xqftestfile2', bufname('%'))
call assert_equal(11, line('.'))
call assert_fails('Xnext', 'E553')
@@ -364,16 +418,26 @@ function Xtest_browse(cchar)
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal(5, line('.'))
+ 10Xnext
+ call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal(11, line('.'))
+ 10Xprev
+ call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal(5, line('.'))
+
+ Xexpr ""
+ call assert_fails('Xnext', 'E42:')
+
call delete('Xqftestfile1')
call delete('Xqftestfile2')
-endfunction
+endfunc
-function Test_browse()
+func Test_browse()
call Xtest_browse('c')
call Xtest_browse('l')
-endfunction
+endfunc
-function! s:test_xhelpgrep(cchar)
+func s:test_xhelpgrep(cchar)
call s:setup_commands(a:cchar)
Xhelpgrep quickfix
Xopen
@@ -383,11 +447,35 @@ function! s:test_xhelpgrep(cchar)
let title_text = ':lhelpgrep quickfix'
endif
call assert_true(w:quickfix_title =~ title_text, w:quickfix_title)
+
+ " Jumping to a help topic should open the help window
+ only
+ Xnext
+ call assert_true(&buftype == 'help')
+ call assert_true(winnr('$') == 2)
+ " Jumping to the next match should reuse the help window
+ Xnext
+ call assert_true(&buftype == 'help')
+ call assert_true(winnr() == 1)
+ call assert_true(winnr('$') == 2)
+ " Jumping to the next match from the quickfix window should reuse the help
+ " window
+ Xopen
+ Xnext
+ call assert_true(&buftype == 'help')
+ call assert_true(winnr() == 1)
+ call assert_true(winnr('$') == 2)
+
" This wipes out the buffer, make sure that doesn't cause trouble.
Xclose
-endfunction
-function Test_helpgrep()
+ new | only
+
+ " Search for non existing help string
+ call assert_fails('Xhelpgrep a1b2c3', 'E480:')
+endfunc
+
+func Test_helpgrep()
call s:test_xhelpgrep('c')
helpclose
call s:test_xhelpgrep('l')
@@ -425,7 +513,7 @@ func Test_vimgreptitle()
augroup! QfBufWinEnter
endfunc
-function XqfTitleTests(cchar)
+func XqfTitleTests(cchar)
call s:setup_commands(a:cchar)
Xgetexpr ['file:1:1:message']
@@ -444,16 +532,16 @@ function XqfTitleTests(cchar)
endif
call assert_equal(title, w:quickfix_title)
Xclose
-endfunction
+endfunc
" Tests for quickfix window's title
-function Test_qf_title()
+func Test_qf_title()
call XqfTitleTests('c')
call XqfTitleTests('l')
-endfunction
+endfunc
" Tests for 'errorformat'
-function Test_efm()
+func Test_efm()
let save_efm = &efm
set efm=%EEEE%m,%WWWW%m,%+CCCC%.%#,%-GGGG%.%#
cgetexpr ['WWWW', 'EEEE', 'CCCC']
@@ -466,7 +554,7 @@ function Test_efm()
let l = strtrans(string(map(getqflist(), '[v:val.text, v:val.valid]')))
call assert_equal("[['W', 1], ['ZZZZ', 0], ['E^@CCCC', 1], ['YYYY', 0]]", l)
let &efm = save_efm
-endfunction
+endfunc
" This will test for problems in quickfix:
" A. incorrectly copying location lists which caused the location list to show
@@ -477,7 +565,7 @@ endfunction
" window it belongs to.
"
" Set up the test environment:
-function! ReadTestProtocol(name)
+func ReadTestProtocol(name)
let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '')
let word = substitute(base, '\v(.*)\..*', '\1', '')
@@ -496,9 +584,9 @@ function! ReadTestProtocol(name)
setl nomodifiable
setl readonly
exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '')
-endfunction
+endfunc
-function Test_locationlist()
+func Test_locationlist()
enew
augroup testgroup
@@ -521,10 +609,7 @@ function Test_locationlist()
lrewind
enew
lopen
- lnext
- lnext
- lnext
- lnext
+ 4lnext
vert split
wincmd L
lopen
@@ -578,15 +663,15 @@ function Test_locationlist()
wincmd n | only
augroup! testgroup
-endfunction
+endfunc
-function Test_locationlist_curwin_was_closed()
+func Test_locationlist_curwin_was_closed()
augroup testgroup
au!
autocmd BufReadCmd test_curwin.txt call R(expand("<amatch>"))
augroup END
- function! R(n)
+ func! R(n)
quit
endfunc
@@ -597,9 +682,9 @@ function Test_locationlist_curwin_was_closed()
call assert_fails('lrewind', 'E924:')
augroup! testgroup
-endfunction
+endfunc
-function Test_locationlist_cross_tab_jump()
+func Test_locationlist_cross_tab_jump()
call writefile(['loclistfoo'], 'loclistfoo')
call writefile(['loclistbar'], 'loclistbar')
set switchbuf=usetab
@@ -613,10 +698,10 @@ function Test_locationlist_cross_tab_jump()
set switchbuf&vim
call delete('loclistfoo')
call delete('loclistbar')
-endfunction
+endfunc
" More tests for 'errorformat'
-function! Test_efm1()
+func Test_efm1()
if !has('unix')
" The 'errorformat' setting is different on non-Unix systems.
" This test works only on Unix-like systems.
@@ -734,10 +819,10 @@ function! Test_efm1()
call delete('Xerrorfile1')
call delete('Xerrorfile2')
call delete('Xtestfile')
-endfunction
+endfunc
" Test for quickfix directory stack support
-function! s:dir_stack_tests(cchar)
+func s:dir_stack_tests(cchar)
call s:setup_commands(a:cchar)
let save_efm=&efm
@@ -779,10 +864,10 @@ function! s:dir_stack_tests(cchar)
call assert_equal(5, qf[11].lnum)
let &efm=save_efm
-endfunction
+endfunc
" Tests for %D and %X errorformat options
-function! Test_efm_dirstack()
+func Test_efm_dirstack()
" Create the directory stack and files
call mkdir('dir1')
call mkdir('dir1/a')
@@ -814,10 +899,33 @@ function! Test_efm_dirstack()
call delete('dir1', 'rf')
call delete('dir2', 'rf')
call delete('habits1.txt')
-endfunction
+endfunc
+
+" Test for resync after continuing an ignored message
+func Xefm_ignore_continuations(cchar)
+ call s:setup_commands(a:cchar)
+
+ let save_efm = &efm
+
+ let &efm =
+ \ '%Eerror %m %l,' .
+ \ '%-Wignored %m %l,' .
+ \ '%+Cmore ignored %m %l,' .
+ \ '%Zignored end'
+ Xgetexpr ['ignored warning 1', 'more ignored continuation 2', 'ignored end', 'error resync 4']
+ let l = map(g:Xgetlist(), '[v:val.text, v:val.valid, v:val.lnum, v:val.type]')
+ call assert_equal([['resync', 1, 4, 'E']], l)
+
+ let &efm = save_efm
+endfunc
+
+func Test_efm_ignore_continuations()
+ call Xefm_ignore_continuations('c')
+ call Xefm_ignore_continuations('l')
+endfunc
" Tests for invalid error format specifies
-function Xinvalid_efm_Tests(cchar)
+func Xinvalid_efm_Tests(cchar)
call s:setup_commands(a:cchar)
let save_efm = &efm
@@ -850,17 +958,17 @@ function Xinvalid_efm_Tests(cchar)
call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:')
let &efm = save_efm
-endfunction
+endfunc
-function Test_invalid_efm()
+func Test_invalid_efm()
call Xinvalid_efm_Tests('c')
call Xinvalid_efm_Tests('l')
-endfunction
+endfunc
" TODO:
" Add tests for the following formats in 'errorformat'
" %r %O
-function! Test_efm2()
+func Test_efm2()
let save_efm = &efm
" Test for %s format in efm
@@ -870,30 +978,44 @@ function! Test_efm2()
call assert_equal(l[0].pattern, '^\VLine search text\$')
call assert_equal(l[0].lnum, 0)
+ let l = split(execute('clist', ''), "\n")
+ call assert_equal([' 1 Xtestfile:^\VLine search text\$: '], l)
+
" Test for %P, %Q and %t format specifiers
let lines=["[Xtestfile1]",
\ "(1,17) error: ';' missing",
\ "(21,2) warning: variable 'z' not defined",
\ "(67,3) error: end of file found before string ended",
+ \ "--",
\ "",
\ "[Xtestfile2]",
+ \ "--",
\ "",
\ "[Xtestfile3]",
\ "NEW compiler v1.1",
\ "(2,2) warning: variable 'x' not defined",
- \ "(67,3) warning: 's' already defined"
+ \ "(67,3) warning: 's' already defined",
+ \ "--"
\]
- set efm=%+P[%f],(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%-Q
+ set efm=%+P[%f]%r,(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%+Q--%r
+ " To exercise the push/pop file functionality in quickfix, the test files
+ " need to be created.
+ call writefile(['Line1'], 'Xtestfile1')
+ call writefile(['Line2'], 'Xtestfile2')
+ call writefile(['Line3'], 'Xtestfile3')
cexpr ""
for l in lines
caddexpr l
endfor
let l = getqflist()
- call assert_equal(9, len(l))
+ call assert_equal(12, len(l))
call assert_equal(21, l[2].lnum)
call assert_equal(2, l[2].col)
call assert_equal('w', l[2].type)
call assert_equal('e', l[3].type)
+ call delete('Xtestfile1')
+ call delete('Xtestfile2')
+ call delete('Xtestfile3')
" Tests for %E, %C and %Z format specifiers
let lines = ["Error 275",
@@ -945,20 +1067,39 @@ function! Test_efm2()
call assert_equal(1, l[4].valid)
call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr))
+ " The following sequence of commands used to crash Vim
+ set efm=%W%m
+ cgetexpr ['msg1']
+ let l = getqflist()
+ call assert_equal(1, len(l), string(l))
+ call assert_equal('msg1', l[0].text)
+ set efm=%C%m
+ lexpr 'msg2'
+ let l = getloclist(0)
+ call assert_equal(1, len(l), string(l))
+ call assert_equal('msg2', l[0].text)
+ lopen
+ call setqflist([], 'r')
+ caddbuf
+ let l = getqflist()
+ call assert_equal(1, len(l), string(l))
+ call assert_equal('|| msg2', l[0].text)
+
+ new | only
let &efm = save_efm
-endfunction
+endfunc
-function XquickfixChangedByAutocmd(cchar)
+func XquickfixChangedByAutocmd(cchar)
call s:setup_commands(a:cchar)
if a:cchar == 'c'
let ErrorNr = 'E925'
- function! ReadFunc()
+ func! ReadFunc()
colder
cgetexpr []
endfunc
else
let ErrorNr = 'E926'
- function! ReadFunc()
+ func! ReadFunc()
lolder
lgetexpr []
endfunc
@@ -981,10 +1122,10 @@ function XquickfixChangedByAutocmd(cchar)
augroup! testgroup
endfunc
-function Test_quickfix_was_changed_by_autocmd()
+func Test_quickfix_was_changed_by_autocmd()
call XquickfixChangedByAutocmd('c')
call XquickfixChangedByAutocmd('l')
-endfunction
+endfunc
func Test_caddbuffer_to_empty()
helpgr quickfix
@@ -1006,7 +1147,7 @@ func Test_cgetexpr_works()
endfunc
" Tests for the setqflist() and setloclist() functions
-function SetXlistTests(cchar, bnum)
+func SetXlistTests(cchar, bnum)
call s:setup_commands(a:cchar)
call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1},
@@ -1041,9 +1182,35 @@ function SetXlistTests(cchar, bnum)
call g:Xsetlist([])
let l = g:Xgetlist()
call assert_equal(0, len(l))
-endfunction
-function Test_setqflist()
+ " Tests for setting the 'valid' flag
+ call g:Xsetlist([{'bufnr':a:bnum, 'lnum':4, 'valid':0}])
+ Xwindow
+ call assert_equal(1, winnr('$'))
+ let l = g:Xgetlist()
+ call g:Xsetlist(l)
+ call assert_equal(0, g:Xgetlist()[0].valid)
+ call g:Xsetlist([{'text':'Text1', 'valid':1}])
+ Xwindow
+ call assert_equal(2, winnr('$'))
+ Xclose
+ let save_efm = &efm
+ set efm=%m
+ Xgetexpr 'TestMessage'
+ let l = g:Xgetlist()
+ call g:Xsetlist(l)
+ call assert_equal(1, g:Xgetlist()[0].valid)
+ let &efm = save_efm
+
+ " Error cases:
+ " Refer to a non-existing buffer and pass a non-dictionary type
+ call assert_fails("call g:Xsetlist([{'bufnr':998, 'lnum':4}," .
+ \ " {'bufnr':999, 'lnum':5}])", 'E92:')
+ call g:Xsetlist([[1, 2,3]])
+ call assert_equal(0, len(g:Xgetlist()))
+endfunc
+
+func Test_setqflist()
new Xtestfile | only
let bnum = bufnr('%')
call setline(1, range(1,5))
@@ -1053,13 +1220,14 @@ function Test_setqflist()
enew!
call delete('Xtestfile')
-endfunction
+endfunc
-function Xlist_empty_middle(cchar)
+func Xlist_empty_middle(cchar)
call s:setup_commands(a:cchar)
" create three quickfix lists
- Xvimgrep Test_ test_quickfix.vim
+ let @/ = 'Test_'
+ Xvimgrep // test_quickfix.vim
let testlen = len(g:Xgetlist())
call assert_true(testlen > 0)
Xvimgrep empty test_quickfix.vim
@@ -1078,12 +1246,12 @@ function Xlist_empty_middle(cchar)
call assert_equal(matchlen, len(g:Xgetlist()))
endfunc
-function Test_setqflist_empty_middle()
+func Test_setqflist_empty_middle()
call Xlist_empty_middle('c')
call Xlist_empty_middle('l')
-endfunction
+endfunc
-function Xlist_empty_older(cchar)
+func Xlist_empty_older(cchar)
call s:setup_commands(a:cchar)
" create three quickfix lists
@@ -1104,14 +1272,14 @@ function Xlist_empty_older(cchar)
call assert_equal(twolen, len(g:Xgetlist()))
Xnewer
call assert_equal(threelen, len(g:Xgetlist()))
-endfunction
+endfunc
-function Test_setqflist_empty_older()
+func Test_setqflist_empty_older()
call Xlist_empty_older('c')
call Xlist_empty_older('l')
-endfunction
+endfunc
-function! XquickfixSetListWithAct(cchar)
+func XquickfixSetListWithAct(cchar)
call s:setup_commands(a:cchar)
let list1 = [{'filename': 'fnameA', 'text': 'A'},
@@ -1185,12 +1353,12 @@ function! XquickfixSetListWithAct(cchar)
call assert_fails("call g:Xsetlist(list1, 0)", 'E928:')
endfunc
-function Test_quickfix_set_list_with_act()
+func Test_quickfix_set_list_with_act()
call XquickfixSetListWithAct('c')
call XquickfixSetListWithAct('l')
-endfunction
+endfunc
-function XLongLinesTests(cchar)
+func XLongLinesTests(cchar)
let l = g:Xgetlist()
call assert_equal(4, len(l))
@@ -1208,9 +1376,9 @@ function XLongLinesTests(cchar)
call assert_equal(10, len(l[3].text))
call g:Xsetlist([], 'r')
-endfunction
+endfunc
-function s:long_lines_tests(cchar)
+func s:long_lines_tests(cchar)
call s:setup_commands(a:cchar)
let testfile = 'samples/quickfix.txt'
@@ -1231,22 +1399,22 @@ function s:long_lines_tests(cchar)
exe 'edit' testfile
exe 'Xbuffer' bufnr('%')
call XLongLinesTests(a:cchar)
-endfunction
+endfunc
-function Test_long_lines()
+func Test_long_lines()
call s:long_lines_tests('c')
call s:long_lines_tests('l')
-endfunction
+endfunc
-function! s:create_test_file(filename)
+func s:create_test_file(filename)
let l = []
for i in range(1, 20)
call add(l, 'Line' . i)
endfor
call writefile(l, a:filename)
-endfunction
+endfunc
-function! Test_switchbuf()
+func Test_switchbuf()
call s:create_test_file('Xqftestfile1')
call s:create_test_file('Xqftestfile2')
call s:create_test_file('Xqftestfile3')
@@ -1267,18 +1435,18 @@ function! Test_switchbuf()
let winid = win_getid()
cfirst | cnext
call assert_equal(winid, win_getid())
- cnext | cnext
+ 2cnext
call assert_equal(winid, win_getid())
- cnext | cnext
+ 2cnext
call assert_equal(winid, win_getid())
enew
set switchbuf=useopen
cfirst | cnext
call assert_equal(file1_winid, win_getid())
- cnext | cnext
+ 2cnext
call assert_equal(file2_winid, win_getid())
- cnext | cnext
+ 2cnext
call assert_equal(file2_winid, win_getid())
enew | only
@@ -1288,9 +1456,9 @@ function! Test_switchbuf()
tabfirst
cfirst | cnext
call assert_equal(2, tabpagenr())
- cnext | cnext
+ 2cnext
call assert_equal(3, tabpagenr())
- cnext | cnext
+ 2cnext
call assert_equal(3, tabpagenr())
tabfirst | tabonly | enew
@@ -1328,14 +1496,28 @@ function! Test_switchbuf()
call assert_equal(2, winnr('$'))
call assert_equal(1, bufwinnr('Xqftestfile3'))
+ " If only quickfix window is open in the current tabpage, jumping to an
+ " entry with 'switchubf' set to 'usetab' should search in other tabpages.
enew | only
+ set switchbuf=usetab
+ tabedit Xqftestfile1
+ tabedit Xqftestfile2
+ tabedit Xqftestfile3
+ tabfirst
+ copen | only
+ clast
+ call assert_equal(4, tabpagenr())
+ tabfirst | tabonly | enew | only
call delete('Xqftestfile1')
call delete('Xqftestfile2')
call delete('Xqftestfile3')
-endfunction
+ set switchbuf&vim
-function! Xadjust_qflnum(cchar)
+ enew | only
+endfunc
+
+func Xadjust_qflnum(cchar)
call s:setup_commands(a:cchar)
enew | only
@@ -1360,17 +1542,17 @@ function! Xadjust_qflnum(cchar)
enew!
call delete(fname)
-endfunction
+endfunc
-function! Test_adjust_lnum()
+func Test_adjust_lnum()
call setloclist(0, [])
call Xadjust_qflnum('c')
call setqflist([])
call Xadjust_qflnum('l')
-endfunction
+endfunc
" Tests for the :grep/:lgrep and :grepadd/:lgrepadd commands
-function! s:test_xgrep(cchar)
+func s:test_xgrep(cchar)
call s:setup_commands(a:cchar)
" The following lines are used for the grep test. Don't remove.
@@ -1389,9 +1571,9 @@ function! s:test_xgrep(cchar)
set makeef=Temp_File_##
silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim
call assert_true(len(g:Xgetlist()) == 6)
-endfunction
+endfunc
-function! Test_grep()
+func Test_grep()
if !has('unix')
" The grepprg may not be set on non-Unix systems
return
@@ -1399,9 +1581,9 @@ function! Test_grep()
call s:test_xgrep('c')
call s:test_xgrep('l')
-endfunction
+endfunc
-function! Test_two_windows()
+func Test_two_windows()
" Use one 'errorformat' for two windows. Add an expression to each of them,
" make sure they each keep their own state.
set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f'
@@ -1427,12 +1609,10 @@ function! Test_two_windows()
laddexpr 'one.txt:3:one one one'
let loc_one = getloclist(one_id)
-echo string(loc_one)
call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr))
call assert_equal(3, loc_one[1].lnum)
let loc_two = getloclist(two_id)
-echo string(loc_two)
call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr))
call assert_equal(5, loc_two[1].lnum)
@@ -1444,15 +1624,20 @@ echo string(loc_two)
call delete('Xtwo', 'rf')
endfunc
-function XbottomTests(cchar)
+func XbottomTests(cchar)
call s:setup_commands(a:cchar)
- call g:Xsetlist([{'filename': 'foo', 'lnum': 42}])
+ " Calling lbottom without any errors should fail
+ if a:cchar == 'l'
+ call assert_fails('lbottom', 'E776:')
+ endif
+
+ call g:Xsetlist([{'filename': 'foo', 'lnum': 42}])
Xopen
let wid = win_getid()
call assert_equal(1, line('.'))
wincmd w
- call g:Xsetlist([{'filename': 'var', 'lnum': 24}], 'a')
+ call g:Xsetlist([{'filename': 'var', 'lnum': 24}], 'a')
Xbottom
call win_gotoid(wid)
call assert_equal(2, line('.'))
@@ -1460,18 +1645,17 @@ function XbottomTests(cchar)
endfunc
" Tests for the :cbottom and :lbottom commands
-function Test_cbottom()
+func Test_cbottom()
call XbottomTests('c')
call XbottomTests('l')
-endfunction
+endfunc
-function HistoryTest(cchar)
+func HistoryTest(cchar)
call s:setup_commands(a:cchar)
- call assert_fails(a:cchar . 'older 99', 'E380:')
" clear all lists after the first one, then replace the first one.
call g:Xsetlist([])
- Xolder
+ call assert_fails('Xolder 99', 'E380:')
let entry = {'filename': 'foo', 'lnum': 42}
call g:Xsetlist([entry], 'r')
call g:Xsetlist([entry, entry])
@@ -1505,7 +1689,7 @@ func Test_duplicate_buf()
endfunc
" Quickfix/Location list set/get properties tests
-function Xproperty_tests(cchar)
+func Xproperty_tests(cchar)
call s:setup_commands(a:cchar)
" Error cases
@@ -1514,6 +1698,7 @@ function Xproperty_tests(cchar)
call assert_fails('call g:Xsetlist([], "a", [])', 'E715:')
" Set and get the title
+ call g:Xsetlist([])
Xopen
wincmd p
call g:Xsetlist([{'filename':'foo', 'lnum':27}])
@@ -1532,9 +1717,30 @@ function Xproperty_tests(cchar)
call assert_equal('N1', g:Xgetlist({'all':1}).title)
call g:Xsetlist([], ' ', {'title' : 'N2'})
call assert_equal(qfnr + 1, g:Xgetlist({'all':1}).nr)
+
+ let res = g:Xgetlist({'nr': 0})
+ call assert_equal(qfnr + 1, res.nr)
+ call assert_equal(['nr'], keys(res))
+
call g:Xsetlist([], ' ', {'title' : 'N3'})
call assert_equal('N2', g:Xgetlist({'nr':2, 'title':1}).title)
+ " Changing the title of an earlier quickfix list
+ call g:Xsetlist([], ' ', {'title' : 'NewTitle', 'nr' : 2})
+ call assert_equal('NewTitle', g:Xgetlist({'nr':2, 'title':1}).title)
+
+ " Changing the title of an invalid quickfix list
+ call assert_equal(-1, g:Xsetlist([], ' ',
+ \ {'title' : 'SomeTitle', 'nr' : 99}))
+ call assert_equal(-1, g:Xsetlist([], ' ',
+ \ {'title' : 'SomeTitle', 'nr' : 'abc'}))
+
+ if a:cchar == 'c'
+ copen
+ call assert_equal({'winid':win_getid()}, getqflist({'winid':1}))
+ cclose
+ endif
+
" Invalid arguments
call assert_fails('call g:Xgetlist([])', 'E715')
call assert_fails('call g:Xsetlist([], "a", [])', 'E715')
@@ -1542,23 +1748,155 @@ function Xproperty_tests(cchar)
call assert_equal(-1, s)
call assert_equal({}, g:Xgetlist({'abc':1}))
+ call assert_equal({}, g:Xgetlist({'nr':99, 'title':1}))
+ call assert_equal({}, g:Xgetlist({'nr':[], 'title':1}))
if a:cchar == 'l'
- call assert_equal({}, getloclist(99, ['title']))
+ call assert_equal({}, getloclist(99, {'title': 1}))
endif
-endfunction
-function Test_qf_property()
+ " Context related tests
+ call g:Xsetlist([], 'a', {'context':[1,2,3]})
+ call test_garbagecollect_now()
+ let d = g:Xgetlist({'context':1})
+ call assert_equal([1,2,3], d.context)
+ call g:Xsetlist([], 'a', {'context':{'color':'green'}})
+ let d = g:Xgetlist({'context':1})
+ call assert_equal({'color':'green'}, d.context)
+ call g:Xsetlist([], 'a', {'context':"Context info"})
+ let d = g:Xgetlist({'context':1})
+ call assert_equal("Context info", d.context)
+ call g:Xsetlist([], 'a', {'context':246})
+ let d = g:Xgetlist({'context':1})
+ call assert_equal(246, d.context)
+ if a:cchar == 'l'
+ " Test for copying context across two different location lists
+ new | only
+ let w1_id = win_getid()
+ let l = [1]
+ call setloclist(0, [], 'a', {'context':l})
+ new
+ let w2_id = win_getid()
+ call add(l, 2)
+ call assert_equal([1, 2], getloclist(w1_id, {'context':1}).context)
+ call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context)
+ unlet! l
+ call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context)
+ only
+ call setloclist(0, [], 'f')
+ call assert_equal({}, getloclist(0, {'context':1}))
+ endif
+
+ " Test for changing the context of previous quickfix lists
+ call g:Xsetlist([], 'f')
+ Xexpr "One"
+ Xexpr "Two"
+ Xexpr "Three"
+ call g:Xsetlist([], ' ', {'context' : [1], 'nr' : 1})
+ call g:Xsetlist([], ' ', {'context' : [2], 'nr' : 2})
+ " Also, check for setting the context using quickfix list number zero.
+ call g:Xsetlist([], ' ', {'context' : [3], 'nr' : 0})
+ call test_garbagecollect_now()
+ let l = g:Xgetlist({'nr' : 1, 'context' : 1})
+ call assert_equal([1], l.context)
+ let l = g:Xgetlist({'nr' : 2, 'context' : 1})
+ call assert_equal([2], l.context)
+ let l = g:Xgetlist({'nr' : 3, 'context' : 1})
+ call assert_equal([3], l.context)
+
+ " Test for changing the context through reference and for garbage
+ " collection of quickfix context
+ let l = ["red"]
+ call g:Xsetlist([], ' ', {'context' : l})
+ call add(l, "blue")
+ let x = g:Xgetlist({'context' : 1})
+ call add(x.context, "green")
+ call assert_equal(["red", "blue", "green"], l)
+ call assert_equal(["red", "blue", "green"], x.context)
+ unlet l
+ call test_garbagecollect_now()
+ let m = g:Xgetlist({'context' : 1})
+ call assert_equal(["red", "blue", "green"], m.context)
+
+ " Test for setting/getting items
+ Xexpr ""
+ let qfprev = g:Xgetlist({'nr':0})
+ call g:Xsetlist([], ' ', {'title':'Green',
+ \ 'items' : [{'filename':'F1', 'lnum':10}]})
+ let qfcur = g:Xgetlist({'nr':0})
+ call assert_true(qfcur.nr == qfprev.nr + 1)
+ let l = g:Xgetlist({'items':1})
+ call assert_equal('F1', bufname(l.items[0].bufnr))
+ call assert_equal(10, l.items[0].lnum)
+ call g:Xsetlist([], 'a', {'items' : [{'filename':'F2', 'lnum':20},
+ \ {'filename':'F2', 'lnum':30}]})
+ let l = g:Xgetlist({'items':1})
+ call assert_equal('F2', bufname(l.items[2].bufnr))
+ call assert_equal(30, l.items[2].lnum)
+ call g:Xsetlist([], 'r', {'items' : [{'filename':'F3', 'lnum':40}]})
+ let l = g:Xgetlist({'items':1})
+ call assert_equal('F3', bufname(l.items[0].bufnr))
+ call assert_equal(40, l.items[0].lnum)
+ call g:Xsetlist([], 'r', {'items' : []})
+ let l = g:Xgetlist({'items':1})
+ call assert_equal(0, len(l.items))
+
+ " Save and restore the quickfix stack
+ call g:Xsetlist([], 'f')
+ call assert_equal(0, g:Xgetlist({'nr':'$'}).nr)
+ Xexpr "File1:10:Line1"
+ Xexpr "File2:20:Line2"
+ Xexpr "File3:30:Line3"
+ let last_qf = g:Xgetlist({'nr':'$'}).nr
+ call assert_equal(3, last_qf)
+ let qstack = []
+ for i in range(1, last_qf)
+ let qstack = add(qstack, g:Xgetlist({'nr':i, 'all':1}))
+ endfor
+ call g:Xsetlist([], 'f')
+ for i in range(len(qstack))
+ call g:Xsetlist([], ' ', qstack[i])
+ endfor
+ call assert_equal(3, g:Xgetlist({'nr':'$'}).nr)
+ call assert_equal(10, g:Xgetlist({'nr':1, 'items':1}).items[0].lnum)
+ call assert_equal(20, g:Xgetlist({'nr':2, 'items':1}).items[0].lnum)
+ call assert_equal(30, g:Xgetlist({'nr':3, 'items':1}).items[0].lnum)
+ call g:Xsetlist([], 'f')
+
+ " Swap two quickfix lists
+ Xexpr "File1:10:Line10"
+ Xexpr "File2:20:Line20"
+ Xexpr "File3:30:Line30"
+ call g:Xsetlist([], 'r', {'nr':1,'title':'Colors','context':['Colors']})
+ call g:Xsetlist([], 'r', {'nr':2,'title':'Fruits','context':['Fruits']})
+ let l1=g:Xgetlist({'nr':1,'all':1})
+ let l2=g:Xgetlist({'nr':2,'all':1})
+ let l1.nr=2
+ let l2.nr=1
+ call g:Xsetlist([], 'r', l1)
+ call g:Xsetlist([], 'r', l2)
+ let newl1=g:Xgetlist({'nr':1,'all':1})
+ let newl2=g:Xgetlist({'nr':2,'all':1})
+ call assert_equal(':Fruits', newl1.title)
+ call assert_equal(['Fruits'], newl1.context)
+ call assert_equal('Line20', newl1.items[0].text)
+ call assert_equal(':Colors', newl2.title)
+ call assert_equal(['Colors'], newl2.context)
+ call assert_equal('Line10', newl2.items[0].text)
+ call g:Xsetlist([], 'f')
+endfunc
+
+func Test_qf_property()
call Xproperty_tests('c')
call Xproperty_tests('l')
-endfunction
+endfunc
" Tests for the QuickFixCmdPre/QuickFixCmdPost autocommands
-function QfAutoCmdHandler(loc, cmd)
+func QfAutoCmdHandler(loc, cmd)
call add(g:acmds, a:loc . a:cmd)
-endfunction
+endfunc
-function Test_Autocmd()
+func Test_Autocmd()
autocmd QuickFixCmdPre * call QfAutoCmdHandler('pre', expand('<amatch>'))
autocmd QuickFixCmdPost * call QfAutoCmdHandler('post', expand('<amatch>'))
@@ -1586,9 +1924,9 @@ function Test_Autocmd()
\ 'precaddbuffer',
\ 'postcaddbuffer']
call assert_equal(l, g:acmds)
-endfunction
+endfunc
-function! Test_Autocmd_Exception()
+func Test_Autocmd_Exception()
set efm=%m
lgetexpr '?'
@@ -1603,4 +1941,271 @@ function! Test_Autocmd_Exception()
call assert_equal('1', getloclist(0)[0].text)
set efm&vim
-endfunction
+endfunc
+
+func Test_caddbuffer_wrong()
+ " This used to cause a memory access in freed memory.
+ let save_efm = &efm
+ set efm=%EEEE%m,%WWWW,%+CCCC%>%#,%GGGG%.#
+ cgetexpr ['WWWW', 'EEEE', 'CCCC']
+ let &efm = save_efm
+ caddbuffer
+ bwipe!
+endfunc
+
+func Test_caddexpr_wrong()
+ " This used to cause a memory access in freed memory.
+ cbuffer
+ cbuffer
+ copen
+ let save_efm = &efm
+ set efm=%
+ call assert_fails('caddexpr ""', 'E376:')
+ let &efm = save_efm
+endfunc
+
+func Test_dirstack_cleanup()
+ " This used to cause a memory access in freed memory.
+ let save_efm = &efm
+ lexpr '0'
+ lopen
+ fun X(c)
+ let save_efm=&efm
+ set efm=%D%f
+ if a:c == 'c'
+ caddexpr '::'
+ else
+ laddexpr ':0:0'
+ endif
+ let &efm=save_efm
+ endfun
+ call X('c')
+ call X('l')
+ call setqflist([], 'r')
+ caddbuffer
+ let &efm = save_efm
+endfunc
+
+" Tests for jumping to entries from the location list window and quickfix
+" window
+func Test_cwindow_jump()
+ set efm=%f%%%l%%%m
+ lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"]
+ lopen | only
+ lfirst
+ call assert_true(winnr('$') == 2)
+ call assert_true(winnr() == 1)
+ " Location list for the new window should be set
+ call assert_true(getloclist(0)[2].text == 'Line 30')
+
+ " Open a scratch buffer
+ " Open a new window and create a location list
+ " Open the location list window and close the other window
+ " Jump to an entry.
+ " Should create a new window and jump to the entry. The scrtach buffer
+ " should not be used.
+ enew | only
+ set buftype=nofile
+ below new
+ lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"]
+ lopen
+ 2wincmd c
+ lnext
+ call assert_true(winnr('$') == 3)
+ call assert_true(winnr() == 2)
+
+ " Open two windows with two different location lists
+ " Open the location list window and close the previous window
+ " Jump to an entry in the location list window
+ " Should open the file in the first window and not set the location list.
+ enew | only
+ lgetexpr ["F1%5%Line 5"]
+ below new
+ lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"]
+ lopen
+ 2wincmd c
+ lnext
+ call assert_true(winnr() == 1)
+ call assert_true(getloclist(0)[0].text == 'Line 5')
+
+ enew | only
+ cgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"]
+ copen
+ cnext
+ call assert_true(winnr('$') == 2)
+ call assert_true(winnr() == 1)
+
+ enew | only
+ set efm&vim
+endfunc
+
+func XvimgrepTests(cchar)
+ call s:setup_commands(a:cchar)
+
+ call writefile(['Editor:VIM vim',
+ \ 'Editor:Emacs EmAcS',
+ \ 'Editor:Notepad NOTEPAD'], 'Xtestfile1')
+ call writefile(['Linux', 'MacOS', 'MS-Windows'], 'Xtestfile2')
+
+ " Error cases
+ call assert_fails('Xvimgrep /abc *', 'E682:')
+
+ let @/=''
+ call assert_fails('Xvimgrep // *', 'E35:')
+
+ call assert_fails('Xvimgrep abc', 'E683:')
+ call assert_fails('Xvimgrep a1b2c3 Xtestfile1', 'E480:')
+ call assert_fails('Xvimgrep pat Xa1b2c3', 'E480:')
+
+ Xexpr ""
+ Xvimgrepadd Notepad Xtestfile1
+ Xvimgrepadd MacOS Xtestfile2
+ let l = g:Xgetlist()
+ call assert_equal(2, len(l))
+ call assert_equal('Editor:Notepad NOTEPAD', l[0].text)
+
+ Xvimgrep #\cvim#g Xtestfile?
+ let l = g:Xgetlist()
+ call assert_equal(2, len(l))
+ call assert_equal(8, l[0].col)
+ call assert_equal(12, l[1].col)
+
+ 1Xvimgrep ?Editor? Xtestfile*
+ let l = g:Xgetlist()
+ call assert_equal(1, len(l))
+ call assert_equal('Editor:VIM vim', l[0].text)
+
+ edit +3 Xtestfile2
+ Xvimgrep +\cemacs+j Xtestfile1
+ let l = g:Xgetlist()
+ call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Editor:Emacs EmAcS', l[0].text)
+
+ call delete('Xtestfile1')
+ call delete('Xtestfile2')
+endfunc
+
+" Tests for the :vimgrep command
+func Test_vimgrep()
+ call XvimgrepTests('c')
+ call XvimgrepTests('l')
+endfunc
+
+func XfreeTests(cchar)
+ call s:setup_commands(a:cchar)
+
+ enew | only
+
+ " Deleting the quickfix stack should work even When the current list is
+ " somewhere in the middle of the stack
+ Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15']
+ Xexpr ['Xfile2:20:20:Line 20', 'Xfile2:25:25:Line 25']
+ Xexpr ['Xfile3:30:30:Line 30', 'Xfile3:35:35:Line 35']
+ Xolder
+ call g:Xsetlist([], 'f')
+ call assert_equal(0, len(g:Xgetlist()))
+
+ " After deleting the stack, adding a new list should create a stack with a
+ " single list.
+ Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15']
+ call assert_equal(1, g:Xgetlist({'all':1}).nr)
+
+ " Deleting the stack from a quickfix window should update/clear the
+ " quickfix/location list window.
+ Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15']
+ Xexpr ['Xfile2:20:20:Line 20', 'Xfile2:25:25:Line 25']
+ Xexpr ['Xfile3:30:30:Line 30', 'Xfile3:35:35:Line 35']
+ Xolder
+ Xwindow
+ call g:Xsetlist([], 'f')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, line('$'))
+ Xclose
+
+ " Deleting the stack from a non-quickfix window should update/clear the
+ " quickfix/location list window.
+ Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15']
+ Xexpr ['Xfile2:20:20:Line 20', 'Xfile2:25:25:Line 25']
+ Xexpr ['Xfile3:30:30:Line 30', 'Xfile3:35:35:Line 35']
+ Xolder
+ Xwindow
+ wincmd p
+ call g:Xsetlist([], 'f')
+ call assert_equal(0, len(g:Xgetlist()))
+ wincmd p
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, line('$'))
+
+ " After deleting the location list stack, if the location list window is
+ " opened, then a new location list should be created. So opening the
+ " location list window again should not create a new window.
+ if a:cchar == 'l'
+ lexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15']
+ wincmd p
+ lopen
+ call assert_equal(2, winnr('$'))
+ endif
+ Xclose
+endfunc
+
+" Tests for the quickifx free functionality
+func Test_qf_free()
+ call XfreeTests('c')
+ call XfreeTests('l')
+endfunc
+
+" Test for buffer overflow when parsing lines and adding new entries to
+" the quickfix list.
+func Test_bufoverflow()
+ set efm=%f:%l:%m
+ cgetexpr ['File1:100:' . repeat('x', 1025)]
+
+ set efm=%+GCompiler:\ %.%#,%f:%l:%m
+ cgetexpr ['Compiler: ' . repeat('a', 1015), 'File1:10:Hello World']
+
+ set efm=%DEntering\ directory\ %f,%f:%l:%m
+ cgetexpr ['Entering directory ' . repeat('a', 1006),
+ \ 'File1:10:Hello World']
+ set efm&vim
+endfunc
+
+func Test_cclose_from_copen()
+ augroup QF_Test
+ au!
+ au FileType qf :cclose
+ augroup END
+ copen
+ augroup QF_Test
+ au!
+ augroup END
+ augroup! QF_Test
+endfunc
+
+" Tests for getting the quickfix stack size
+func XsizeTests(cchar)
+ call s:setup_commands(a:cchar)
+
+ call g:Xsetlist([], 'f')
+ call assert_equal(0, g:Xgetlist({'nr':'$'}).nr)
+ call assert_equal(1, len(g:Xgetlist({'nr':'$', 'all':1})))
+ call assert_equal(0, len(g:Xgetlist({'nr':0})))
+
+ Xexpr "File1:10:Line1"
+ Xexpr "File2:20:Line2"
+ Xexpr "File3:30:Line3"
+ Xolder | Xolder
+ call assert_equal(3, g:Xgetlist({'nr':'$'}).nr)
+ call g:Xsetlist([], 'f')
+
+ Xexpr "File1:10:Line1"
+ Xexpr "File2:20:Line2"
+ Xexpr "File3:30:Line3"
+ Xolder | Xolder
+ call g:Xsetlist([], 'a', {'nr':'$', 'title':'Compiler'})
+ call assert_equal('Compiler', g:Xgetlist({'nr':3, 'all':1}).title)
+endfunc
+
+func Test_Qf_Size()
+ call XsizeTests('c')
+ call XsizeTests('l')
+endfunc
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
new file mode 100644
index 0000000000..46d884a97c
--- /dev/null
+++ b/src/nvim/testdir/test_recover.vim
@@ -0,0 +1,63 @@
+" Test :recover
+
+func Test_recover_root_dir()
+ " This used to access invalid memory.
+ split Xtest
+ set dir=/
+ call assert_fails('recover', 'E305:')
+ close!
+
+ if has('win32') || filewritable('/') == 2
+ " can write in / directory on MS-Windows
+ set dir=/notexist/
+ endif
+ call assert_fails('split Xtest', 'E303:')
+ set dir&
+endfunc
+
+" Inserts 10000 lines with text to fill the swap file with two levels of pointer
+" blocks. Then recovers from the swap file and checks all text is restored.
+"
+" We need about 10000 lines of 100 characters to get two levels of pointer
+" blocks.
+func Test_swap_file()
+ set directory=.
+ set fileformat=unix undolevels=-1
+ edit! Xtest
+ let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789"
+ let i = 1
+ let linecount = 10000
+ while i <= linecount
+ call append(i - 1, i . text)
+ let i += 1
+ endwhile
+ $delete
+ preserve
+ " get the name of the swap file
+ let swname = split(execute("swapname"))[0]
+ let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
+ " make a copy of the swap file in Xswap
+ set binary
+ exe 'sp ' . swname
+ w! Xswap
+ set nobinary
+ new
+ only!
+ bwipe! Xtest
+ call rename('Xswap', swname)
+ recover Xtest
+ call delete(swname)
+ let linedollar = line('$')
+ call assert_equal(linecount, linedollar)
+ if linedollar < linecount
+ let linecount = linedollar
+ endif
+ let i = 1
+ while i <= linecount
+ call assert_equal(i . text, getline(i))
+ let i += 1
+ endwhile
+
+ set undolevels&
+ enew! | only
+endfunc
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
new file mode 100644
index 0000000000..8528412806
--- /dev/null
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -0,0 +1,32 @@
+" Tests for regexp in latin1 encoding
+set encoding=latin1
+scriptencoding latin1
+
+func s:equivalence_test()
+ let str = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z"
+ let groups = split(str)
+ for group1 in groups
+ for c in split(group1, '\zs')
+ " next statement confirms that equivalence class matches every
+ " character in group
+ call assert_match('^[[=' . c . '=]]*$', group1)
+ for group2 in groups
+ if group2 != group1
+ " next statement converts that equivalence class doesn't match
+ " a character in any other group
+ call assert_equal(-1, match(group2, '[[=' . c . '=]]'))
+ endif
+ endfor
+ endfor
+ endfor
+endfunc
+
+func Test_equivalence_re1()
+ set re=1
+ call s:equivalence_test()
+endfunc
+
+func Test_equivalence_re2()
+ set re=2
+ call s:equivalence_test()
+endfunc
diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim
index 7f3b31575d..a2f4286d4f 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -98,6 +98,21 @@ func Test_recursive_substitute()
bwipe!
endfunc
+func Test_nested_backrefs()
+ " Check example in change.txt.
+ new
+ for re in range(0, 2)
+ exe 'set re=' . re
+ call setline(1, 'aa ab x')
+ 1s/\(\(a[a-d] \)*\)\(x\)/-\1- -\2- -\3-/
+ call assert_equal('-aa ab - -ab - -x-', getline(1))
+
+ call assert_equal('-aa ab - -ab - -x-', substitute('aa ab x', '\(\(a[a-d] \)*\)\(x\)', '-\1- -\2- -\3-', ''))
+ endfor
+ bwipe!
+ set re=0
+endfunc
+
func Test_eow_with_optional()
let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', '']
for re in range(0, 2)
diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim
new file mode 100644
index 0000000000..f11a32bade
--- /dev/null
+++ b/src/nvim/testdir/test_retab.vim
@@ -0,0 +1,77 @@
+" Test :retab
+func SetUp()
+ new
+ call setline(1, "\ta \t b c ")
+endfunc
+
+func TearDown()
+ bwipe!
+endfunc
+
+func Retab(bang, n)
+ let l:old_tabstop = &tabstop
+ let l:old_line = getline(1)
+ exe "retab" . a:bang . a:n
+ let l:line = getline(1)
+ call setline(1, l:old_line)
+ if a:n > 0
+ " :retab changes 'tabstop' to n with argument n > 0.
+ call assert_equal(a:n, &tabstop)
+ exe 'set tabstop=' . l:old_tabstop
+ else
+ " :retab does not change 'tabstop' with empty or n <= 0.
+ call assert_equal(l:old_tabstop, &tabstop)
+ endif
+ return l:line
+endfunc
+
+func Test_retab()
+ set tabstop=8 noexpandtab
+ call assert_equal("\ta\t b c ", Retab('', ''))
+ call assert_equal("\ta\t b c ", Retab('', 0))
+ call assert_equal("\ta\t b c ", Retab('', 8))
+ call assert_equal("\ta\t b\t c\t ", Retab('!', ''))
+ call assert_equal("\ta\t b\t c\t ", Retab('!', 0))
+ call assert_equal("\ta\t b\t c\t ", Retab('!', 8))
+
+ call assert_equal("\t\ta\t\t\tb c ", Retab('', 4))
+ call assert_equal("\t\ta\t\t\tb\t\t c\t ", Retab('!', 4))
+
+ call assert_equal(" a\t\tb c ", Retab('', 10))
+ call assert_equal(" a\t\tb c ", Retab('!', 10))
+
+ set tabstop=8 expandtab
+ call assert_equal(" a b c ", Retab('', ''))
+ call assert_equal(" a b c ", Retab('', 0))
+ call assert_equal(" a b c ", Retab('', 8))
+ call assert_equal(" a b c ", Retab('!', ''))
+ call assert_equal(" a b c ", Retab('!', 0))
+ call assert_equal(" a b c ", Retab('!', 8))
+
+ call assert_equal(" a b c ", Retab(' ', 4))
+ call assert_equal(" a b c ", Retab('!', 4))
+
+ call assert_equal(" a b c ", Retab(' ', 10))
+ call assert_equal(" a b c ", Retab('!', 10))
+
+ set tabstop=4 noexpandtab
+ call assert_equal("\ta\t\tb c ", Retab('', ''))
+ call assert_equal("\ta\t\tb\t\t c\t ", Retab('!', ''))
+ call assert_equal("\t a\t\t\tb c ", Retab('', 3))
+ call assert_equal("\t a\t\t\tb\t\t\tc\t ", Retab('!', 3))
+ call assert_equal(" a\t b c ", Retab('', 5))
+ call assert_equal(" a\t b\t\t c\t ", Retab('!', 5))
+
+ set tabstop=4 expandtab
+ call assert_equal(" a b c ", Retab('', ''))
+ call assert_equal(" a b c ", Retab('!', ''))
+ call assert_equal(" a b c ", Retab('', 3))
+ call assert_equal(" a b c ", Retab('!', 3))
+ call assert_equal(" a b c ", Retab('', 5))
+ call assert_equal(" a b c ", Retab('!', 5))
+endfunc
+
+func Test_retab_error()
+ call assert_fails('retab -1', 'E487:')
+ call assert_fails('retab! -1', 'E487:')
+endfunc
diff --git a/src/nvim/testdir/test_scrollbind.vim b/src/nvim/testdir/test_scrollbind.vim
new file mode 100644
index 0000000000..baa24f1979
--- /dev/null
+++ b/src/nvim/testdir/test_scrollbind.vim
@@ -0,0 +1,32 @@
+" Test for 'scrollbind' causing an unexpected scroll of one of the windows.
+func Test_scrollbind()
+ " We don't want the status line to cause problems:
+ set laststatus=0
+ let totalLines = &lines * 20
+ let middle = totalLines / 2
+ new | only
+ for i in range(1, totalLines)
+ call setline(i, 'LINE ' . i)
+ endfor
+ exe string(middle)
+ normal zt
+ normal M
+ aboveleft vert new
+ for i in range(1, totalLines)
+ call setline(i, 'line ' . i)
+ endfor
+ exe string(middle)
+ normal zt
+ normal M
+ " Execute the following two commands at once to reproduce the problem.
+ setl scb | wincmd p
+ setl scb
+ wincmd w
+ let topLineLeft = line('w0')
+ wincmd p
+ let topLineRight = line('w0')
+ setl noscrollbind
+ wincmd p
+ setl noscrollbind
+ call assert_equal(0, topLineLeft - topLineRight)
+endfunc
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 2106fc2dec..5da9397be5 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -2,13 +2,13 @@
func Test_search_cmdline()
" See test/functional/legacy/search_spec.lua
- throw 'skipped: Nvim does not support test_disable_char_avail()'
+ throw 'skipped: Nvim does not support test_override()'
if !exists('+incsearch')
return
endif
" need to disable char_avail,
" so that expansion of commandline works
- call test_disable_char_avail(1)
+ call test_override("char_avail", 1)
new
call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'])
" Test 1
@@ -195,19 +195,19 @@ func Test_search_cmdline()
call assert_equal(' 3 the', getline('.'))
" clean up
- call test_disable_char_avail(0)
+ call test_override("char_avail", 0)
bw!
endfunc
func Test_search_cmdline2()
" See test/functional/legacy/search_spec.lua
- throw 'skipped: Nvim does not support test_disable_char_avail()'
+ throw 'skipped: Nvim does not support test_override()'
if !exists('+incsearch')
return
endif
" need to disable char_avail,
" so that expansion of commandline works
- call test_disable_char_avail(1)
+ call test_override("char_avail", 1)
new
call setline(1, [' 1', ' 2 these', ' 3 the theother'])
" Test 1
@@ -269,7 +269,7 @@ func Test_search_cmdline2()
" clean up
set noincsearch
- call test_disable_char_avail(0)
+ call test_override("char_avail", 0)
bw!
endfunc
@@ -283,3 +283,173 @@ func Test_use_sub_pat()
call X()
bwipe!
endfunc
+
+func Test_searchpair()
+ new
+ call setline(1, ['other code here', '', '[', '" cursor here', ']'])
+ 4
+ let a=searchpair('\[','',']','bW')
+ call assert_equal(3, a)
+ set nomagic
+ 4
+ let a=searchpair('\[','',']','bW')
+ call assert_equal(3, a)
+ set magic
+ q!
+endfunc
+
+func Test_searchc()
+ " These commands used to cause memory overflow in searchc().
+ new
+ norm ixx
+ exe "norm 0t\u93cf"
+ bw!
+endfunc
+
+func Test_search_cmdline3()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ " need to disable char_avail,
+ " so that expansion of commandline works
+ call test_override("char_avail", 1)
+ new
+ call setline(1, [' 1', ' 2 the~e', ' 3 the theother'])
+ set incsearch
+ 1
+ " first match
+ call feedkeys("/the\<c-l>\<cr>", 'tx')
+ call assert_equal(' 2 the~e', getline('.'))
+ " clean up
+ set noincsearch
+ call test_override("char_avail", 0)
+ bw!
+endfunc
+
+func Test_search_cmdline4()
+ " See test/functional/legacy/search_spec.lua
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ " need to disable char_avail,
+ " so that expansion of commandline works
+ call test_override("char_avail", 1)
+ new
+ call setline(1, [' 1 the first', ' 2 the second', ' 3 the third'])
+ set incsearch
+ $
+ call feedkeys("?the\<c-g>\<cr>", 'tx')
+ call assert_equal(' 3 the third', getline('.'))
+ $
+ call feedkeys("?the\<c-g>\<c-g>\<cr>", 'tx')
+ call assert_equal(' 1 the first', getline('.'))
+ $
+ call feedkeys("?the\<c-g>\<c-g>\<c-g>\<cr>", 'tx')
+ call assert_equal(' 2 the second', getline('.'))
+ $
+ call feedkeys("?the\<c-t>\<cr>", 'tx')
+ call assert_equal(' 1 the first', getline('.'))
+ $
+ call feedkeys("?the\<c-t>\<c-t>\<cr>", 'tx')
+ call assert_equal(' 3 the third', getline('.'))
+ $
+ call feedkeys("?the\<c-t>\<c-t>\<c-t>\<cr>", 'tx')
+ call assert_equal(' 2 the second', getline('.'))
+ " clean up
+ set noincsearch
+ call test_override("char_avail", 0)
+ bw!
+endfunc
+
+func Test_search_cmdline5()
+ if !exists('+incsearch')
+ return
+ endif
+ " Do not call test_override("char_avail", 1) so that <C-g> and <C-t> work
+ " regardless char_avail.
+ new
+ call setline(1, [' 1 the first', ' 2 the second', ' 3 the third'])
+ set incsearch
+ 1
+ call feedkeys("/the\<c-g>\<c-g>\<cr>", 'tx')
+ call assert_equal(' 3 the third', getline('.'))
+ $
+ call feedkeys("?the\<c-t>\<c-t>\<c-t>\<cr>", 'tx')
+ call assert_equal(' 2 the second', getline('.'))
+ " clean up
+ set noincsearch
+ bw!
+endfunc
+
+" Tests for regexp with various magic settings
+func Test_search_regexp()
+ enew!
+
+ put ='1 a aa abb abbccc'
+ exe 'normal! /a*b\{2}c\+/e' . "\<CR>"
+ call assert_equal([0, 2, 17, 0], getpos('.'))
+
+ put ='2 d dd dee deefff'
+ exe 'normal! /\Md\*e\{2}f\+/e' . "\<CR>"
+ call assert_equal([0, 3, 17, 0], getpos('.'))
+
+ set nomagic
+ put ='3 g gg ghh ghhiii'
+ exe 'normal! /g\*h\{2}i\+/e' . "\<CR>"
+ call assert_equal([0, 4, 17, 0], getpos('.'))
+
+ put ='4 j jj jkk jkklll'
+ exe 'normal! /\mj*k\{2}l\+/e' . "\<CR>"
+ call assert_equal([0, 5, 17, 0], getpos('.'))
+
+ put ='5 m mm mnn mnnooo'
+ exe 'normal! /\vm*n{2}o+/e' . "\<CR>"
+ call assert_equal([0, 6, 17, 0], getpos('.'))
+
+ put ='6 x ^aa$ x'
+ exe 'normal! /\V^aa$' . "\<CR>"
+ call assert_equal([0, 7, 5, 0], getpos('.'))
+
+ set magic
+ put ='7 (a)(b) abbaa'
+ exe 'normal! /\v(a)(b)\2\1\1/e' . "\<CR>"
+ call assert_equal([0, 8, 14, 0], getpos('.'))
+
+ put ='8 axx [ab]xx'
+ exe 'normal! /\V[ab]\(\[xy]\)\1' . "\<CR>"
+ call assert_equal([0, 9, 7, 0], getpos('.'))
+
+ set undolevels=100
+ put ='9 foobar'
+ put =''
+ exe "normal! a\<C-G>u\<Esc>"
+ normal G
+ exe 'normal! dv?bar?' . "\<CR>"
+ call assert_equal('9 foo', getline('.'))
+ call assert_equal([0, 10, 5, 0], getpos('.'))
+ call assert_equal(10, line('$'))
+ normal u
+ call assert_equal('9 foobar', getline('.'))
+ call assert_equal([0, 10, 6, 0], getpos('.'))
+ call assert_equal(11, line('$'))
+
+ set undolevels&
+ enew!
+endfunc
+
+" Test for search('multi-byte char', 'bce')
+func Test_search_multibyte()
+ if !has('multi_byte')
+ return
+ endif
+ let save_enc = &encoding
+ set encoding=utf8
+ enew!
+ call append('$', 'A')
+ call cursor(2, 1)
+ call assert_equal(2, search('A', 'bce', line('.')))
+ enew!
+ let &encoding = save_enc
+endfunc
diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim
new file mode 100644
index 0000000000..dd4707977e
--- /dev/null
+++ b/src/nvim/testdir/test_sha256.vim
@@ -0,0 +1,22 @@
+" Tests for the sha256() function.
+
+if !has('cryptv') || !exists('*sha256')
+ finish
+endif
+
+function Test_sha256()
+ " test for empty string:
+ call assert_equal(sha256(""), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
+
+ "'test for 1 char:
+ call assert_equal(sha256("a"), 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb')
+ "
+ "test for 3 chars:
+ call assert_equal(sha256("abc"), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')
+
+ " test for contains meta char:
+ call assert_equal(sha256("foo\nbar"), '807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776')
+
+ " test for contains non-ascii char:
+ call assert_equal(sha256("\xde\xad\xbe\xef"), '5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953')
+endfunction
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index 75dbd74b34..a967435346 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -182,7 +182,7 @@ func Test_sign_invalid_commands()
call assert_fails('sign define Sign1 xxx', 'E475:')
call assert_fails('sign undefine', 'E156:')
call assert_fails('sign list xxx', 'E155:')
- call assert_fails('sign place 1 buffer=', 'E158:')
+ call assert_fails('sign place 1 buffer=999', 'E158:')
call assert_fails('sign define Sign2 text=', 'E239:')
endfunc
diff --git a/src/nvim/testdir/test_source_utf8.vim b/src/nvim/testdir/test_source_utf8.vim
index edb76fc43d..c29c2ec1f3 100644
--- a/src/nvim/testdir/test_source_utf8.vim
+++ b/src/nvim/testdir/test_source_utf8.vim
@@ -31,3 +31,33 @@ func Test_source_latin()
bwipe!
call delete('Xscript')
endfunc
+
+" Test for sourcing a file with CTRL-V's at the end of the line
+func Test_source_ctrl_v()
+ call writefile(['map __1 afirst',
+ \ 'map __2 asecond',
+ \ 'map __3 athird',
+ \ 'map __4 afourth',
+ \ 'map __5 afifth',
+ \ "map __1 asd\<C-V>",
+ \ "map __2 asd\<C-V>\<C-V>",
+ \ "map __3 asd\<C-V>\<C-V>",
+ \ "map __4 asd\<C-V>\<C-V>\<C-V>",
+ \ "map __5 asd\<C-V>\<C-V>\<C-V>",
+ \ ], 'Xtestfile')
+ source Xtestfile
+ enew!
+ exe "normal __1\<Esc>\<Esc>__2\<Esc>__3\<Esc>\<Esc>__4\<Esc>__5\<Esc>"
+ exe "%s/\<C-J>/0/g"
+ call assert_equal(['sd',
+ \ "map __2 asd\<Esc>secondsd\<Esc>sd0map __5 asd0fifth"],
+ \ getline(1, 2))
+
+ enew!
+ call delete('Xtestfile')
+ unmap __1
+ unmap __2
+ unmap __3
+ unmap __4
+ unmap __5
+endfunc
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
new file mode 100644
index 0000000000..21f2363731
--- /dev/null
+++ b/src/nvim/testdir/test_spell.vim
@@ -0,0 +1,821 @@
+" Test spell checking
+
+if !has('spell')
+ finish
+endif
+
+func TearDown()
+ set nospell
+ call delete('Xtest.aff')
+ call delete('Xtest.dic')
+ call delete('Xtest.latin1.add')
+ call delete('Xtest.latin1.add.spl')
+ call delete('Xtest.latin1.spl')
+ call delete('Xtest.latin1.sug')
+endfunc
+
+func Test_wrap_search()
+ new
+ call setline(1, ['The', '', 'A plong line with two zpelling mistakes', '', 'End'])
+ set spell wrapscan
+ normal ]s
+ call assert_equal('plong', expand('<cword>'))
+ normal ]s
+ call assert_equal('zpelling', expand('<cword>'))
+ normal ]s
+ call assert_equal('plong', expand('<cword>'))
+ bwipe!
+ set nospell
+endfunc
+
+func Test_curswant()
+ new
+ call setline(1, ['Another plong line', 'abcdefghijklmnopq'])
+ set spell wrapscan
+ normal 0]s
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+ normal 0[s
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+
+ normal 0]S
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+ normal 0[S
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+
+ normal 1G0
+ call assert_equal('plong', spellbadword()[0])
+ normal j
+ call assert_equal(9, getcurpos()[2])
+
+ bwipe!
+ set nospell
+endfunc
+
+func Test_z_equal_on_invalid_utf8_word()
+ split
+ set spell
+ call setline(1, "\xff")
+ norm z=
+ set nospell
+ bwipe!
+endfunc
+
+func Test_spellreall()
+ new
+ set spell
+ call assert_fails('spellrepall', 'E752:')
+ call setline(1, ['A speling mistake. The same speling mistake.',
+ \ 'Another speling mistake.'])
+ call feedkeys(']s1z=', 'tx')
+ call assert_equal('A spelling mistake. The same speling mistake.', getline(1))
+ call assert_equal('Another speling mistake.', getline(2))
+ spellrepall
+ call assert_equal('A spelling mistake. The same spelling mistake.', getline(1))
+ call assert_equal('Another spelling mistake.', getline(2))
+ call assert_fails('spellrepall', 'E753:')
+ set spell&
+ bwipe!
+endfunc
+
+func Test_zz_basic()
+ call LoadAffAndDic(g:test_data_aff1, g:test_data_dic1)
+ call RunGoodBad("wrong OK puts. Test the end",
+ \ "bad: inputs comment ok Ok. test d\xE9\xF4l end the",
+ \["Comment", "deol", "d\xE9\xF4r", "input", "OK", "output", "outputs", "outtest", "put", "puts",
+ \ "test", "testen", "testn", "the end", "uk", "wrong"],
+ \[
+ \ ["bad", ["put", "uk", "OK"]],
+ \ ["inputs", ["input", "puts", "outputs"]],
+ \ ["comment", ["Comment", "outtest", "the end"]],
+ \ ["ok", ["OK", "uk", "put"]],
+ \ ["Ok", ["OK", "Uk", "Put"]],
+ \ ["test", ["Test", "testn", "testen"]],
+ \ ["d\xE9\xF4l", ["deol", "d\xE9\xF4r", "test"]],
+ \ ["end", ["put", "uk", "test"]],
+ \ ["the", ["put", "uk", "test"]],
+ \ ]
+ \ )
+
+ call assert_equal("gebletegek", soundfold('goobledygoook'))
+ call assert_equal("kepereneven", soundfold('koprnven'))
+ call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale'))
+endfunc
+
+" Postponed prefixes
+func Test_zz_prefixes()
+ call LoadAffAndDic(g:test_data_aff2, g:test_data_dic1)
+ call RunGoodBad("puts",
+ \ "bad: inputs comment ok Ok end the. test d\xE9\xF4l",
+ \ ["Comment", "deol", "d\xE9\xF4r", "OK", "put", "input", "output", "puts", "outputs", "test", "outtest", "testen", "testn", "the end", "uk", "wrong"],
+ \ [
+ \ ["bad", ["put", "uk", "OK"]],
+ \ ["inputs", ["input", "puts", "outputs"]],
+ \ ["comment", ["Comment"]],
+ \ ["ok", ["OK", "uk", "put"]],
+ \ ["Ok", ["OK", "Uk", "Put"]],
+ \ ["end", ["put", "uk", "deol"]],
+ \ ["the", ["put", "uk", "test"]],
+ \ ["test", ["Test", "testn", "testen"]],
+ \ ["d\xE9\xF4l", ["deol", "d\xE9\xF4r", "test"]],
+ \ ])
+endfunc
+
+"Compound words
+func Test_zz_compound()
+ call LoadAffAndDic(g:test_data_aff3, g:test_data_dic3)
+ call RunGoodBad("foo m\xEF foobar foofoobar barfoo barbarfoo",
+ \ "bad: bar la foom\xEF barm\xEF m\xEFfoo m\xEFbar m\xEFm\xEF lala m\xEFla lam\xEF foola labar",
+ \ ["foo", "m\xEF"],
+ \ [
+ \ ["bad", ["foo", "m\xEF"]],
+ \ ["bar", ["barfoo", "foobar", "foo"]],
+ \ ["la", ["m\xEF", "foo"]],
+ \ ["foom\xEF", ["foo m\xEF", "foo", "foofoo"]],
+ \ ["barm\xEF", ["barfoo", "m\xEF", "barbar"]],
+ \ ["m\xEFfoo", ["m\xEF foo", "foo", "foofoo"]],
+ \ ["m\xEFbar", ["foobar", "barbar", "m\xEF"]],
+ \ ["m\xEFm\xEF", ["m\xEF m\xEF", "m\xEF"]],
+ \ ["lala", []],
+ \ ["m\xEFla", ["m\xEF", "m\xEF m\xEF"]],
+ \ ["lam\xEF", ["m\xEF", "m\xEF m\xEF"]],
+ \ ["foola", ["foo", "foobar", "foofoo"]],
+ \ ["labar", ["barbar", "foobar"]],
+ \ ])
+
+ call LoadAffAndDic(g:test_data_aff4, g:test_data_dic4)
+ call RunGoodBad("word util bork prebork start end wordutil wordutils pro-ok bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork tomato tomatotomato startend startword startwordword startwordend startwordwordend startwordwordwordend prebork preborkbork preborkborkbork nouword",
+ \ "bad: wordutilize pro borkborkborkborkborkbork tomatotomatotomato endstart endend startstart wordend wordstart preborkprebork preborkpreborkbork startwordwordwordwordend borkpreborkpreborkbork utilsbork startnouword",
+ \ ["bork", "prebork", "end", "pro-ok", "start", "tomato", "util", "utilize", "utils", "word", "nouword"],
+ \ [
+ \ ["bad", ["end", "bork", "word"]],
+ \ ["wordutilize", ["word utilize", "wordutils", "wordutil"]],
+ \ ["pro", ["bork", "word", "end"]],
+ \ ["borkborkborkborkborkbork", ["bork borkborkborkborkbork", "borkbork borkborkborkbork", "borkborkbork borkborkbork"]],
+ \ ["tomatotomatotomato", ["tomato tomatotomato", "tomatotomato tomato", "tomato tomato tomato"]],
+ \ ["endstart", ["end start", "start"]],
+ \ ["endend", ["end end", "end"]],
+ \ ["startstart", ["start start"]],
+ \ ["wordend", ["word end", "word", "wordword"]],
+ \ ["wordstart", ["word start", "bork start"]],
+ \ ["preborkprebork", ["prebork prebork", "preborkbork", "preborkborkbork"]],
+ \ ["preborkpreborkbork", ["prebork preborkbork", "preborkborkbork", "preborkborkborkbork"]],
+ \ ["startwordwordwordwordend", ["startwordwordwordword end", "startwordwordwordword", "start wordwordwordword end"]],
+ \ ["borkpreborkpreborkbork", ["bork preborkpreborkbork", "bork prebork preborkbork", "bork preborkprebork bork"]],
+ \ ["utilsbork", ["utilbork", "utils bork", "util bork"]],
+ \ ["startnouword", ["start nouword", "startword", "startborkword"]],
+ \ ])
+
+endfunc
+
+"Test affix flags with two characters
+func Test_zz_affix()
+ call LoadAffAndDic(g:test_data_aff5, g:test_data_dic5)
+ call RunGoodBad("fooa1 fooa\xE9 bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend",
+ \ "bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart startprobar startnouend",
+ \ ["bar", "barbork", "end", "fooa1", "fooa\xE9", "nouend", "prebar", "prebarbork", "start"],
+ \ [
+ \ ["bad", ["bar", "end", "fooa1"]],
+ \ ["foo", ["fooa1", "fooa\xE9", "bar"]],
+ \ ["fooa2", ["fooa1", "fooa\xE9", "bar"]],
+ \ ["prabar", ["prebar", "bar", "bar bar"]],
+ \ ["probarbirk", ["prebarbork"]],
+ \ ["middle", []],
+ \ ["startmiddle", ["startmiddleend", "startmiddlebar"]],
+ \ ["middleend", []],
+ \ ["endstart", ["end start", "start"]],
+ \ ["startprobar", ["startprebar", "start prebar", "startbar"]],
+ \ ["startnouend", ["start nouend", "startend"]],
+ \ ])
+
+ call LoadAffAndDic(g:test_data_aff6, g:test_data_dic6)
+ call RunGoodBad("meea1 meea\xE9 bar prebar barbork prebarbork leadprebar lead end leadend leadmiddleend",
+ \ "bad: mee meea2 prabar probarbirk middle leadmiddle middleend endlead leadprobar",
+ \ ["bar", "barbork", "end", "lead", "meea1", "meea\xE9", "prebar", "prebarbork"],
+ \ [
+ \ ["bad", ["bar", "end", "lead"]],
+ \ ["mee", ["meea1", "meea\xE9", "bar"]],
+ \ ["meea2", ["meea1", "meea\xE9", "lead"]],
+ \ ["prabar", ["prebar", "bar", "leadbar"]],
+ \ ["probarbirk", ["prebarbork"]],
+ \ ["middle", []],
+ \ ["leadmiddle", ["leadmiddleend", "leadmiddlebar"]],
+ \ ["middleend", []],
+ \ ["endlead", ["end lead", "lead", "end end"]],
+ \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]],
+ \ ])
+
+ call LoadAffAndDic(g:test_data_aff7, g:test_data_dic7)
+ call RunGoodBad("meea1 meea\xE9 bar prebar barmeat prebarmeat leadprebar lead tail leadtail leadmiddletail",
+ \ "bad: mee meea2 prabar probarmaat middle leadmiddle middletail taillead leadprobar",
+ \ ["bar", "barmeat", "lead", "meea1", "meea\xE9", "prebar", "prebarmeat", "tail"],
+ \ [
+ \ ["bad", ["bar", "lead", "tail"]],
+ \ ["mee", ["meea1", "meea\xE9", "bar"]],
+ \ ["meea2", ["meea1", "meea\xE9", "lead"]],
+ \ ["prabar", ["prebar", "bar", "leadbar"]],
+ \ ["probarmaat", ["prebarmeat"]],
+ \ ["middle", []],
+ \ ["leadmiddle", ["leadmiddlebar"]],
+ \ ["middletail", []],
+ \ ["taillead", ["tail lead", "tail"]],
+ \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]],
+ \ ])
+endfunc
+
+func Test_zz_NOSLITSUGS()
+ call LoadAffAndDic(g:test_data_aff8, g:test_data_dic8)
+ call RunGoodBad("foo bar faabar", "bad: foobar barfoo",
+ \ ["bar", "faabar", "foo"],
+ \ [
+ \ ["bad", ["bar", "foo"]],
+ \ ["foobar", ["faabar", "foo bar", "bar"]],
+ \ ["barfoo", ["bar foo", "bar", "foo"]],
+ \ ])
+endfunc
+
+" Numbers
+func Test_zz_Numbers()
+ call LoadAffAndDic(g:test_data_aff9, g:test_data_dic9)
+ call RunGoodBad("0b1011 0777 1234 0x01ff", "",
+ \ ["bar", "foo"],
+ \ [
+ \ ])
+endfunc
+
+function FirstSpellWord()
+ call feedkeys("/^start:\n", 'tx')
+ normal ]smm
+ let [str, a] = spellbadword()
+ return str
+endfunc
+
+function SecondSpellWord()
+ normal `m]s
+ let [str, a] = spellbadword()
+ return str
+endfunc
+
+"Test with SAL instead of SOFO items; test automatic reloading
+func Test_zz_sal_and_addition()
+ throw 'skipped: Nvim does not support enc=latin1'
+ set enc=latin1
+ set spellfile=
+ call writefile(g:test_data_dic1, "Xtest.dic")
+ call writefile(g:test_data_aff_sal, "Xtest.aff")
+ mkspell! Xtest Xtest
+ set spl=Xtest.latin1.spl spell
+ call assert_equal('kbltykk', soundfold('goobledygoook'))
+ call assert_equal('kprnfn', soundfold('koprnven'))
+ call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale'))
+
+ "also use an addition file
+ call writefile(["/regions=usgbnz", "elequint/2", "elekwint/3"], "Xtest.latin1.add")
+ mkspell! Xtest.latin1.add.spl Xtest.latin1.add
+
+ bwipe!
+ call setline(1, ["start: elequint test elekwint test elekwent asdf"])
+
+ set spellfile=Xtest.latin1.add
+ call assert_equal("elekwent", FirstSpellWord())
+
+ set spl=Xtest_us.latin1.spl
+ call assert_equal("elequint", FirstSpellWord())
+ call assert_equal("elekwint", SecondSpellWord())
+
+ set spl=Xtest_gb.latin1.spl
+ call assert_equal("elekwint", FirstSpellWord())
+ call assert_equal("elekwent", SecondSpellWord())
+
+ set spl=Xtest_nz.latin1.spl
+ call assert_equal("elequint", FirstSpellWord())
+ call assert_equal("elekwent", SecondSpellWord())
+
+ set spl=Xtest_ca.latin1.spl
+ call assert_equal("elequint", FirstSpellWord())
+ call assert_equal("elekwint", SecondSpellWord())
+endfunc
+
+func Test_region_error()
+ messages clear
+ call writefile(["/regions=usgbnz", "elequint/0"], "Xtest.latin1.add")
+ mkspell! Xtest.latin1.add.spl Xtest.latin1.add
+ call assert_match('Invalid region nr in Xtest.latin1.add line 2: 0', execute('messages'))
+ call delete('Xtest.latin1.add')
+ call delete('Xtest.latin1.add.spl')
+endfunc
+
+" Check using z= in new buffer (crash fixed by patch 7.4a.028).
+func Test_zeq_crash()
+ new
+ set maxmem=512 spell
+ call feedkeys('iasdz=:\"', 'tx')
+
+ bwipe!
+endfunc
+
+func LoadAffAndDic(aff_contents, dic_contents)
+ throw 'skipped: Nvim does not support enc=latin1'
+ set enc=latin1
+ set spellfile=
+ call writefile(a:aff_contents, "Xtest.aff")
+ call writefile(a:dic_contents, "Xtest.dic")
+ " Generate a .spl file from a .dic and .aff file.
+ mkspell! Xtest Xtest
+ " use that spell file
+ set spl=Xtest.latin1.spl spell
+endfunc
+
+func ListWords()
+ spelldump
+ %yank
+ quit
+ return split(@", "\n")
+endfunc
+
+func TestGoodBadBase()
+ exe '1;/^good:'
+ normal 0f:]s
+ let prevbad = ''
+ let result = []
+ while 1
+ let [bad, a] = spellbadword()
+ if bad == '' || bad == prevbad || bad == 'badend'
+ break
+ endif
+ let prevbad = bad
+ let lst = spellsuggest(bad, 3)
+ normal mm
+
+ call add(result, [bad, lst])
+ normal `m]s
+ endwhile
+ return result
+endfunc
+
+func RunGoodBad(good, bad, expected_words, expected_bad_words)
+ bwipe!
+ call setline(1, ["good: ", a:good, a:bad, " badend "])
+ let words = ListWords()
+ call assert_equal(a:expected_words, words[1:-1])
+ let bad_words = TestGoodBadBase()
+ call assert_equal(a:expected_bad_words, bad_words)
+ bwipe!
+endfunc
+
+let g:test_data_aff1 = [
+ \"SET ISO8859-1",
+ \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xBF",
+ \"SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep?",
+ \"",
+ \"MIDWORD\t'-",
+ \"",
+ \"KEP =",
+ \"RAR ?",
+ \"BAD !",
+ \"",
+ \"PFX I N 1",
+ \"PFX I 0 in .",
+ \"",
+ \"PFX O Y 1",
+ \"PFX O 0 out .",
+ \"",
+ \"SFX S Y 2",
+ \"SFX S 0 s [^s]",
+ \"SFX S 0 es s",
+ \"",
+ \"SFX N N 3",
+ \"SFX N 0 en [^n]",
+ \"SFX N 0 nen n",
+ \"SFX N 0 n .",
+ \"",
+ \"REP 3",
+ \"REP g ch",
+ \"REP ch g",
+ \"REP svp s.v.p.",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \ ]
+let g:test_data_dic1 = [
+ \"123456",
+ \"test/NO",
+ \"# comment",
+ \"wrong",
+ \"Comment",
+ \"OK",
+ \"uk",
+ \"put/ISO",
+ \"the end",
+ \"deol",
+ \"d\xE9\xF4r",
+ \ ]
+let g:test_data_aff2 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"PFXPOSTPONE",
+ \"",
+ \"MIDWORD\t'-",
+ \"",
+ \"KEP =",
+ \"RAR ?",
+ \"BAD !",
+ \"",
+ \"PFX I N 1",
+ \"PFX I 0 in .",
+ \"",
+ \"PFX O Y 1",
+ \"PFX O 0 out [a-z]",
+ \"",
+ \"SFX S Y 2",
+ \"SFX S 0 s [^s]",
+ \"SFX S 0 es s",
+ \"",
+ \"SFX N N 3",
+ \"SFX N 0 en [^n]",
+ \"SFX N 0 nen n",
+ \"SFX N 0 n .",
+ \"",
+ \"REP 3",
+ \"REP g ch",
+ \"REP ch g",
+ \"REP svp s.v.p.",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \ ]
+let g:test_data_aff3 = [
+ \"SET ISO8859-1",
+ \"",
+ \"COMPOUNDMIN 3",
+ \"COMPOUNDRULE m*",
+ \"NEEDCOMPOUND x",
+ \ ]
+let g:test_data_dic3 = [
+ \"1234",
+ \"foo/m",
+ \"bar/mx",
+ \"m\xEF/m",
+ \"la/mx",
+ \ ]
+let g:test_data_aff4 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"COMPOUNDRULE m+",
+ \"COMPOUNDRULE sm*e",
+ \"COMPOUNDRULE sm+",
+ \"COMPOUNDMIN 3",
+ \"COMPOUNDWORDMAX 3",
+ \"COMPOUNDFORBIDFLAG t",
+ \"",
+ \"COMPOUNDSYLMAX 5",
+ \"SYLLABLE a\xE1e\xE9i\xEDo\xF3\xF6\xF5u\xFA\xFC\xFBy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \"",
+ \"NEEDAFFIX x",
+ \"",
+ \"PFXPOSTPONE",
+ \"",
+ \"MIDWORD '-",
+ \"",
+ \"SFX q N 1",
+ \"SFX q 0 -ok .",
+ \"",
+ \"SFX a Y 2",
+ \"SFX a 0 s .",
+ \"SFX a 0 ize/t .",
+ \"",
+ \"PFX p N 1",
+ \"PFX p 0 pre .",
+ \"",
+ \"PFX P N 1",
+ \"PFX P 0 nou .",
+ \ ]
+let g:test_data_dic4 = [
+ \"1234",
+ \"word/mP",
+ \"util/am",
+ \"pro/xq",
+ \"tomato/m",
+ \"bork/mp",
+ \"start/s",
+ \"end/e",
+ \ ]
+let g:test_data_aff5 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FLAG long",
+ \"",
+ \"NEEDAFFIX !!",
+ \"",
+ \"COMPOUNDRULE ssmm*ee",
+ \"",
+ \"NEEDCOMPOUND xx",
+ \"COMPOUNDPERMITFLAG pp",
+ \"",
+ \"SFX 13 Y 1",
+ \"SFX 13 0 bork .",
+ \"",
+ \"SFX a1 Y 1",
+ \"SFX a1 0 a1 .",
+ \"",
+ \"SFX a\xE9 Y 1",
+ \"SFX a\xE9 0 a\xE9 .",
+ \"",
+ \"PFX zz Y 1",
+ \"PFX zz 0 pre/pp .",
+ \"",
+ \"PFX yy Y 1",
+ \"PFX yy 0 nou .",
+ \ ]
+let g:test_data_dic5 = [
+ \"1234",
+ \"foo/a1a\xE9!!",
+ \"bar/zz13ee",
+ \"start/ss",
+ \"end/eeyy",
+ \"middle/mmxx",
+ \ ]
+let g:test_data_aff6 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FLAG caplong",
+ \"",
+ \"NEEDAFFIX A!",
+ \"",
+ \"COMPOUNDRULE sMm*Ee",
+ \"",
+ \"NEEDCOMPOUND Xx",
+ \"",
+ \"COMPOUNDPERMITFLAG p",
+ \"",
+ \"SFX N3 Y 1",
+ \"SFX N3 0 bork .",
+ \"",
+ \"SFX A1 Y 1",
+ \"SFX A1 0 a1 .",
+ \"",
+ \"SFX A\xE9 Y 1",
+ \"SFX A\xE9 0 a\xE9 .",
+ \"",
+ \"PFX Zz Y 1",
+ \"PFX Zz 0 pre/p .",
+ \ ]
+let g:test_data_dic6 = [
+ \"1234",
+ \"mee/A1A\xE9A!",
+ \"bar/ZzN3Ee",
+ \"lead/s",
+ \"end/Ee",
+ \"middle/MmXx",
+ \ ]
+let g:test_data_aff7 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FLAG num",
+ \"",
+ \"NEEDAFFIX 9999",
+ \"",
+ \"COMPOUNDRULE 2,77*123",
+ \"",
+ \"NEEDCOMPOUND 1",
+ \"COMPOUNDPERMITFLAG 432",
+ \"",
+ \"SFX 61003 Y 1",
+ \"SFX 61003 0 meat .",
+ \"",
+ \"SFX 391 Y 1",
+ \"SFX 391 0 a1 .",
+ \"",
+ \"SFX 111 Y 1",
+ \"SFX 111 0 a\xE9 .",
+ \"",
+ \"PFX 17 Y 1",
+ \"PFX 17 0 pre/432 .",
+ \ ]
+let g:test_data_dic7 = [
+ \"1234",
+ \"mee/391,111,9999",
+ \"bar/17,61003,123",
+ \"lead/2",
+ \"tail/123",
+ \"middle/77,1",
+ \ ]
+let g:test_data_aff8 = [
+ \"SET ISO8859-1",
+ \"",
+ \"NOSPLITSUGS",
+ \ ]
+let g:test_data_dic8 = [
+ \"1234",
+ \"foo",
+ \"bar",
+ \"faabar",
+ \ ]
+let g:test_data_aff9 = [
+ \ ]
+let g:test_data_dic9 = [
+ \"1234",
+ \"foo",
+ \"bar",
+ \ ]
+let g:test_data_aff_sal = [
+ \"SET ISO8859-1",
+ \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"MIDWORD\t'-",
+ \"",
+ \"KEP =",
+ \"RAR ?",
+ \"BAD !",
+ \"",
+ \"PFX I N 1",
+ \"PFX I 0 in .",
+ \"",
+ \"PFX O Y 1",
+ \"PFX O 0 out .",
+ \"",
+ \"SFX S Y 2",
+ \"SFX S 0 s [^s]",
+ \"SFX S 0 es s",
+ \"",
+ \"SFX N N 3",
+ \"SFX N 0 en [^n]",
+ \"SFX N 0 nen n",
+ \"SFX N 0 n .",
+ \"",
+ \"REP 3",
+ \"REP g ch",
+ \"REP ch g",
+ \"REP svp s.v.p.",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \"",
+ \"SAL AH(AEIOUY)-^ *H",
+ \"SAL AR(AEIOUY)-^ *R",
+ \"SAL A(HR)^ *",
+ \"SAL A^ *",
+ \"SAL AH(AEIOUY)- H",
+ \"SAL AR(AEIOUY)- R",
+ \"SAL A(HR) _",
+ \"SAL \xC0^ *",
+ \"SAL \xC5^ *",
+ \"SAL BB- _",
+ \"SAL B B",
+ \"SAL CQ- _",
+ \"SAL CIA X",
+ \"SAL CH X",
+ \"SAL C(EIY)- S",
+ \"SAL CK K",
+ \"SAL COUGH^ KF",
+ \"SAL CC< C",
+ \"SAL C K",
+ \"SAL DG(EIY) K",
+ \"SAL DD- _",
+ \"SAL D T",
+ \"SAL \xC9< E",
+ \"SAL EH(AEIOUY)-^ *H",
+ \"SAL ER(AEIOUY)-^ *R",
+ \"SAL E(HR)^ *",
+ \"SAL ENOUGH^$ *NF",
+ \"SAL E^ *",
+ \"SAL EH(AEIOUY)- H",
+ \"SAL ER(AEIOUY)- R",
+ \"SAL E(HR) _",
+ \"SAL FF- _",
+ \"SAL F F",
+ \"SAL GN^ N",
+ \"SAL GN$ N",
+ \"SAL GNS$ NS",
+ \"SAL GNED$ N",
+ \"SAL GH(AEIOUY)- K",
+ \"SAL GH _",
+ \"SAL GG9 K",
+ \"SAL G K",
+ \"SAL H H",
+ \"SAL IH(AEIOUY)-^ *H",
+ \"SAL IR(AEIOUY)-^ *R",
+ \"SAL I(HR)^ *",
+ \"SAL I^ *",
+ \"SAL ING6 N",
+ \"SAL IH(AEIOUY)- H",
+ \"SAL IR(AEIOUY)- R",
+ \"SAL I(HR) _",
+ \"SAL J K",
+ \"SAL KN^ N",
+ \"SAL KK- _",
+ \"SAL K K",
+ \"SAL LAUGH^ LF",
+ \"SAL LL- _",
+ \"SAL L L",
+ \"SAL MB$ M",
+ \"SAL MM M",
+ \"SAL M M",
+ \"SAL NN- _",
+ \"SAL N N",
+ \"SAL OH(AEIOUY)-^ *H",
+ \"SAL OR(AEIOUY)-^ *R",
+ \"SAL O(HR)^ *",
+ \"SAL O^ *",
+ \"SAL OH(AEIOUY)- H",
+ \"SAL OR(AEIOUY)- R",
+ \"SAL O(HR) _",
+ \"SAL PH F",
+ \"SAL PN^ N",
+ \"SAL PP- _",
+ \"SAL P P",
+ \"SAL Q K",
+ \"SAL RH^ R",
+ \"SAL ROUGH^ RF",
+ \"SAL RR- _",
+ \"SAL R R",
+ \"SAL SCH(EOU)- SK",
+ \"SAL SC(IEY)- S",
+ \"SAL SH X",
+ \"SAL SI(AO)- X",
+ \"SAL SS- _",
+ \"SAL S S",
+ \"SAL TI(AO)- X",
+ \"SAL TH @",
+ \"SAL TCH-- _",
+ \"SAL TOUGH^ TF",
+ \"SAL TT- _",
+ \"SAL T T",
+ \"SAL UH(AEIOUY)-^ *H",
+ \"SAL UR(AEIOUY)-^ *R",
+ \"SAL U(HR)^ *",
+ \"SAL U^ *",
+ \"SAL UH(AEIOUY)- H",
+ \"SAL UR(AEIOUY)- R",
+ \"SAL U(HR) _",
+ \"SAL V^ W",
+ \"SAL V F",
+ \"SAL WR^ R",
+ \"SAL WH^ W",
+ \"SAL W(AEIOU)- W",
+ \"SAL X^ S",
+ \"SAL X KS",
+ \"SAL Y(AEIOU)- Y",
+ \"SAL ZZ- _",
+ \"SAL Z S",
+ \ ]
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 5996b2cd4a..11e26d03aa 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -24,28 +24,34 @@ func Test_after_comes_later()
\ 'set guioptions+=M',
\ 'let $HOME = "/does/not/exist"',
\ 'set loadplugins',
- \ 'set rtp=Xhere,Xafter',
+ \ 'set rtp=Xhere,Xafter,Xanother',
\ 'set packpath=Xhere,Xafter',
\ 'set nomore',
+ \ 'let g:sequence = ""',
\ ]
let after = [
\ 'redir! > Xtestout',
\ 'scriptnames',
\ 'redir END',
+ \ 'redir! > Xsequence',
+ \ 'echo g:sequence',
+ \ 'redir END',
\ 'quit',
\ ]
call mkdir('Xhere/plugin', 'p')
- call writefile(['let done = 1'], 'Xhere/plugin/here.vim')
+ call writefile(['let g:sequence .= "here "'], 'Xhere/plugin/here.vim')
+ call mkdir('Xanother/plugin', 'p')
+ call writefile(['let g:sequence .= "another "'], 'Xanother/plugin/another.vim')
call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p')
- call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim')
+ call writefile(['let g:sequence .= "pack "'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim')
call mkdir('Xafter/plugin', 'p')
- call writefile(['let done = 1'], 'Xafter/plugin/later.vim')
+ call writefile(['let g:sequence .= "after "'], 'Xafter/plugin/later.vim')
if RunVim(before, after, '')
let lines = readfile('Xtestout')
- let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim']
+ let expected = ['Xbefore.vim', 'here.vim', 'another.vim', 'foo.vim', 'later.vim', 'Xafter.vim']
let found = []
for line in lines
for one in expected
@@ -57,11 +63,47 @@ func Test_after_comes_later()
call assert_equal(expected, found)
endif
+ call assert_equal('here another pack after', substitute(join(readfile('Xsequence', 1), ''), '\s\+$', '', ''))
+
call delete('Xtestout')
+ call delete('Xsequence')
call delete('Xhere', 'rf')
+ call delete('Xanother', 'rf')
call delete('Xafter', 'rf')
endfunc
+func Test_pack_in_rtp_when_plugins_run()
+ if !has('packages')
+ return
+ endif
+ let before = [
+ \ 'set nocp viminfo+=nviminfo',
+ \ 'set guioptions+=M',
+ \ 'let $HOME = "/does/not/exist"',
+ \ 'set loadplugins',
+ \ 'set rtp=Xhere',
+ \ 'set packpath=Xhere',
+ \ 'set nomore',
+ \ ]
+ let after = [
+ \ 'quit',
+ \ ]
+ call mkdir('Xhere/plugin', 'p')
+ call writefile(['redir! > Xtestout', 'silent set runtimepath?', 'silent! call foo#Trigger()', 'redir END'], 'Xhere/plugin/here.vim')
+ call mkdir('Xhere/pack/foo/start/foobar/autoload', 'p')
+ call writefile(['function! foo#Trigger()', 'echo "autoloaded foo"', 'endfunction'], 'Xhere/pack/foo/start/foobar/autoload/foo.vim')
+
+ if RunVim(before, after, '')
+
+ let lines = filter(readfile('Xtestout'), '!empty(v:val)')
+ call assert_match('Xhere[/\\]pack[/\\]foo[/\\]start[/\\]foobar', get(lines, 0))
+ call assert_match('autoloaded foo', get(lines, 1))
+ endif
+
+ call delete('Xtestout')
+ call delete('Xhere', 'rf')
+endfunc
+
func Test_help_arg()
if !has('unix') && has('gui')
" this doesn't work with gvim on MS-Windows
@@ -75,12 +117,12 @@ func Test_help_arg()
" check if couple of lines are there
let found = []
for line in lines
- if line =~ '-R.*Readonly mode'
- call add(found, 'Readonly mode')
+ if line =~ '-R.*Read-only mode'
+ call add(found, 'Readonly mode')
endif
" Watch out for a second --version line in the Gnome version.
- if line =~ '--version.*Print version information and exit'
- call add(found, "--version")
+ if line =~ '--version.*Print version information'
+ call add(found, "--version")
endif
endfor
call assert_equal(['Readonly mode', '--version'], found)
diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim
new file mode 100644
index 0000000000..d179a4cc79
--- /dev/null
+++ b/src/nvim/testdir/test_startup_utf8.vim
@@ -0,0 +1,64 @@
+" Tests for startup using utf-8.
+if !has('multi_byte')
+ finish
+endif
+
+source shared.vim
+
+func Test_read_stdin_utf8()
+ let linesin = ['テスト', '€ÀÈÌÒÙ']
+ call writefile(linesin, 'Xtestin')
+ let before = [
+ \ 'set enc=utf-8',
+ \ 'set fencs=cp932,utf-8',
+ \ ]
+ let after = [
+ \ 'write ++enc=utf-8 Xtestout',
+ \ 'quit!',
+ \ ]
+ if has('win32')
+ let pipecmd = 'type Xtestin | '
+ else
+ let pipecmd = 'cat Xtestin | '
+ endif
+ if RunVimPiped(before, after, '-', pipecmd)
+ let lines = readfile('Xtestout')
+ call assert_equal(linesin, lines)
+ else
+ call assert_equal('', 'RunVimPiped failed.')
+ endif
+ call delete('Xtestout')
+ call delete('Xtestin')
+endfunc
+
+func Test_read_fifo_utf8()
+ if !has('unix')
+ return
+ endif
+ " Using bash/zsh's process substitution.
+ if executable('bash')
+ set shell=bash
+ elseif executable('zsh')
+ set shell=zsh
+ else
+ return
+ endif
+ let linesin = ['テスト', '€ÀÈÌÒÙ']
+ call writefile(linesin, 'Xtestin')
+ let before = [
+ \ 'set enc=utf-8',
+ \ 'set fencs=cp932,utf-8',
+ \ ]
+ let after = [
+ \ 'write ++enc=utf-8 Xtestout',
+ \ 'quit!',
+ \ ]
+ if RunVim(before, after, '<(cat Xtestin)')
+ let lines = readfile('Xtestout')
+ call assert_equal(linesin, lines)
+ else
+ call assert_equal('', 'RunVim failed.')
+ endif
+ call delete('Xtestout')
+ call delete('Xtestin')
+endfunc
diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim
index 89ca9ef379..1239fe9427 100644
--- a/src/nvim/testdir/test_stat.vim
+++ b/src/nvim/testdir/test_stat.vim
@@ -1,24 +1,45 @@
" Tests for stat functions and checktime
-func Test_existent_file()
- let fname='Xtest.tmp'
+func CheckFileTime(doSleep)
+ let fname = 'Xtest.tmp'
+ let result = 0
- let ts=localtime()
- sleep 1
- let fl=['Hello World!']
+ let ts = localtime()
+ if a:doSleep
+ sleep 1
+ endif
+ let fl = ['Hello World!']
call writefile(fl, fname)
- let tf=getftime(fname)
- sleep 1
- let te=localtime()
+ let tf = getftime(fname)
+ if a:doSleep
+ sleep 1
+ endif
+ let te = localtime()
+
+ let time_correct = (ts <= tf && tf <= te)
+ if a:doSleep || time_correct
+ call assert_true(time_correct)
+ call assert_equal(strlen(fl[0] . "\n"), getfsize(fname))
+ call assert_equal('file', getftype(fname))
+ call assert_equal('rw-', getfperm(fname)[0:2])
+ let result = 1
+ endif
+
+ call delete(fname)
+ return result
+endfunc
- call assert_true(ts <= tf && tf <= te)
- call assert_equal(strlen(fl[0] . "\n"), getfsize(fname))
- call assert_equal('file', getftype(fname))
- call assert_equal('rw-', getfperm(fname)[0:2])
+func Test_existent_file()
+ " On some systems the file timestamp is rounded to a multiple of 2 seconds.
+ " We need to sleep to handle that, but that makes the test slow. First try
+ " without the sleep, and if it fails try again with the sleep.
+ if CheckFileTime(0) == 0
+ call CheckFileTime(1)
+ endif
endfunc
func Test_existent_directory()
- let dname='.'
+ let dname = '.'
call assert_equal(0, getfsize(dname))
call assert_equal('dir', getftype(dname))
@@ -26,22 +47,29 @@ func Test_existent_directory()
endfunc
func Test_checktime()
- let fname='Xtest.tmp'
+ let fname = 'Xtest.tmp'
- let fl=['Hello World!']
+ let fl = ['Hello World!']
call writefile(fl, fname)
set autoread
exec 'e' fname
- sleep 2
- let fl=readfile(fname)
+ " FAT has a granularity of 2 seconds, otherwise it's usually 1 second
+ if has('win32')
+ sleep 2
+ else
+ sleep 2
+ endif
+ let fl = readfile(fname)
let fl[0] .= ' - checktime'
call writefile(fl, fname)
checktime
call assert_equal(fl[0], getline(1))
+
+ call delete(fname)
endfunc
func Test_nonexistent_file()
- let fname='Xtest.tmp'
+ let fname = 'Xtest.tmp'
call delete(fname)
call assert_equal(-1, getftime(fname))
@@ -55,7 +83,7 @@ func Test_win32_symlink_dir()
" So we use an existing symlink for this test.
if has('win32')
" Check if 'C:\Users\All Users' is a symlink to a directory.
- let res=system('dir C:\Users /a')
+ let res = system('dir C:\Users /a')
if match(res, '\C<SYMLINKD> *All Users') >= 0
" Get the filetype of the symlink.
call assert_equal('dir', getftype('C:\Users\All Users'))
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index 82898df92d..cf85bd58ac 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -1,19 +1,39 @@
-function! StatuslineWithCaughtError()
+" Test 'statusline'
+"
+" Not tested yet:
+" %a
+" %N
+" %T
+" %X
+" %*
+
+source view_util.vim
+
+func s:get_statusline()
+ return ScreenLines(&lines - 1, &columns)[0]
+endfunc
+
+func StatuslineWithCaughtError()
let s:func_in_statusline_called = 1
try
call eval('unknown expression')
catch
endtry
return ''
-endfunction
+endfunc
-function! StatuslineWithError()
+func StatuslineWithError()
let s:func_in_statusline_called = 1
call eval('unknown expression')
return ''
-endfunction
+endfunc
-function! Test_caught_error_in_statusline()
+" Function used to display syntax group.
+func SyntaxItem()
+ return synIDattr(synID(line("."),col("."),1),"name")
+endfunc
+
+func Test_caught_error_in_statusline()
let s:func_in_statusline_called = 0
set laststatus=2
let statusline = '%{StatuslineWithCaughtError()}'
@@ -22,9 +42,9 @@ function! Test_caught_error_in_statusline()
call assert_true(s:func_in_statusline_called)
call assert_equal(statusline, &statusline)
set statusline=
-endfunction
+endfunc
-function! Test_statusline_will_be_disabled_with_error()
+func Test_statusline_will_be_disabled_with_error()
let s:func_in_statusline_called = 0
set laststatus=2
let statusline = '%{StatuslineWithError()}'
@@ -36,4 +56,219 @@ function! Test_statusline_will_be_disabled_with_error()
call assert_true(s:func_in_statusline_called)
call assert_equal('', &statusline)
set statusline=
-endfunction
+endfunc
+
+func Test_statusline()
+ new Xstatusline
+ only
+ set laststatus=2
+ set splitbelow
+ call setline(1, range(1, 200))
+
+ " %b: Value of character under cursor.
+ " %B: As above, in hexadecimal.
+ call cursor(180, 2)
+ set statusline=%b,%B
+ call assert_match('^56,38\s*$', s:get_statusline())
+
+ " %o: Byte number in file of byte under cursor, first byte is 1.
+ " %O: As above, in hexadecimal.
+ set statusline=%o,%O
+ set fileformat=dos
+ call assert_match('^789,315\s*$', s:get_statusline())
+ set fileformat=mac
+ call assert_match('^610,262\s*$', s:get_statusline())
+ set fileformat=unix
+ call assert_match('^610,262\s*$', s:get_statusline())
+ set fileformat&
+
+ " %f: Path to the file in the buffer, as typed or relative to current dir.
+ set statusline=%f
+ call assert_match('^Xstatusline\s*$', s:get_statusline())
+
+ " %F: Full path to the file in the buffer.
+ set statusline=%F
+ call assert_match('/testdir/Xstatusline\s*$', s:get_statusline())
+
+ " %h: Help buffer flag, text is "[help]".
+ " %H: Help buffer flag, text is ",HLP".
+ set statusline=%h,%H
+ call assert_match('^,\s*$', s:get_statusline())
+ help
+ call assert_match('^\[Help\],HLP\s*$', s:get_statusline())
+ helpclose
+
+ " %k: Value of "b:keymap_name" or 'keymap'
+ " when :lmap mappings are being used: <keymap>"
+ set statusline=%k
+ if has('keymap')
+ set keymap=esperanto
+ call assert_match('^<Eo>\s*$', s:get_statusline())
+ set keymap&
+ else
+ call assert_match('^\s*$', s:get_statusline())
+ endif
+
+ " %l: Line number.
+ " %L: Number of line in buffer.
+ " %c: Column number.
+ set statusline=%l/%L,%c
+ call assert_match('^180/200,2\s*$', s:get_statusline())
+
+ " %m: Modified flag, text is "[+]", "[-]" if 'modifiable' is off.
+ " %M: Modified flag, text is ",+" or ",-".
+ set statusline=%m%M
+ call assert_match('^\[+\],+\s*$', s:get_statusline())
+ set nomodifiable
+ call assert_match('^\[+-\],+-\s*$', s:get_statusline())
+ write
+ call assert_match('^\[-\],-\s*$', s:get_statusline())
+ set modifiable&
+ call assert_match('^\s*$', s:get_statusline())
+
+ " %n: Buffer number.
+ set statusline=%n
+ call assert_match('^'.bufnr('%').'\s*$', s:get_statusline())
+
+ " %p: Percentage through file in lines as in CTRL-G.
+ " %P: Percentage through file of displayed window.
+ set statusline=%p,%P
+ 0
+ call assert_match('^0,Top\s*$', s:get_statusline())
+ norm G
+ call assert_match('^100,Bot\s*$', s:get_statusline())
+ 180
+ " Don't check the exact percentage as it depends on the window size
+ call assert_match('^90,\(Top\|Bot\|\d\+%\)\s*$', s:get_statusline())
+
+ " %q: "[Quickfix List]", "[Location List]" or empty.
+ set statusline=%q
+ call assert_match('^\s*$', s:get_statusline())
+ copen
+ call assert_match('^\[Quickfix List\]\s*$', s:get_statusline())
+ cclose
+ lexpr getline(1, 2)
+ lopen
+ call assert_match('^\[Location List\]\s*$', s:get_statusline())
+ lclose
+
+ " %r: Readonly flag, text is "[RO]".
+ " %R: Readonly flag, text is ",RO".
+ set statusline=%r,%R
+ call assert_match('^,\s*$', s:get_statusline())
+ help
+ call assert_match('^\[RO\],RO\s*$', s:get_statusline())
+ helpclose
+
+ " %t: File name (tail) of file in the buffer.
+ set statusline=%t
+ call assert_match('^Xstatusline\s*$', s:get_statusline())
+
+ " %v: Virtual column number.
+ " %V: Virtual column number as -{num}. Not displayed if equal to 'c'.
+ call cursor(180, 2)
+ set statusline=%v,%V
+ call assert_match('^2,\s*$', s:get_statusline())
+ set virtualedit=all
+ norm 10|
+ call assert_match('^10,-10\s*$', s:get_statusline())
+ set virtualedit&
+
+ " %w: Preview window flag, text is "[Preview]".
+ " %W: Preview window flag, text is ",PRV".
+ set statusline=%w%W
+ call assert_match('^\s*$', s:get_statusline())
+ pedit
+ wincmd j
+ call assert_match('^\[Preview\],PRV\s*$', s:get_statusline())
+ pclose
+
+ " %y: Type of file in the buffer, e.g., "[vim]". See 'filetype'.
+ " %Y: Type of file in the buffer, e.g., ",VIM". See 'filetype'.
+ set statusline=%y\ %Y
+ call assert_match('^\s*$', s:get_statusline())
+ setfiletype vim
+ call assert_match('^\[vim\] VIM\s*$', s:get_statusline())
+
+ " %=: Separation point between left and right aligned items.
+ set statusline=foo%=bar
+ call assert_match('^foo\s\+bar\s*$', s:get_statusline())
+
+ " Test min/max width, leading zeroes, left/right justify.
+ set statusline=%04B
+ call cursor(180, 2)
+ call assert_match('^0038\s*$', s:get_statusline())
+ set statusline=#%4B#
+ call assert_match('^# 38#\s*$', s:get_statusline())
+ set statusline=#%-4B#
+ call assert_match('^#38 #\s*$', s:get_statusline())
+ set statusline=%.6f
+ call assert_match('^<sline\s*$', s:get_statusline())
+
+ " %<: Where to truncate.
+ exe 'set statusline=a%<b' . repeat('c', 1000) . 'd'
+ call assert_match('^a<c*d$', s:get_statusline())
+ exe 'set statusline=a' . repeat('b', 1000) . '%<c'
+ call assert_match('^ab*>$', s:get_statusline())
+
+ "%{: Evaluate expression between '%{' and '}' and substitute result.
+ syntax on
+ set statusline=%{SyntaxItem()}
+ call assert_match('^vimNumber\s*$', s:get_statusline())
+ s/^/"/
+ call assert_match('^vimLineComment\s*$', s:get_statusline())
+ syntax off
+
+ "%(: Start of item group.
+ set statusline=ab%(cd%q%)de
+ call assert_match('^abde\s*$', s:get_statusline())
+ copen
+ call assert_match('^abcd\[Quickfix List\1]de\s*$', s:get_statusline())
+ cclose
+
+ " %#: Set highlight group. The name must follow and then a # again.
+ set statusline=ab%#Todo#cd%#Error#ef
+ call assert_match('^abcdef\s*$', s:get_statusline())
+ let sa1=screenattr(&lines - 1, 1)
+ let sa2=screenattr(&lines - 1, 3)
+ let sa3=screenattr(&lines - 1, 5)
+ call assert_notequal(sa1, sa2)
+ call assert_notequal(sa1, sa3)
+ call assert_notequal(sa2, sa3)
+ call assert_equal(sa1, screenattr(&lines - 1, 2))
+ call assert_equal(sa2, screenattr(&lines - 1, 4))
+ call assert_equal(sa3, screenattr(&lines - 1, 6))
+ call assert_equal(sa3, screenattr(&lines - 1, 7))
+
+ " %*: Set highlight group to User{N}
+ set statusline=a%1*b%0*c
+ call assert_match('^abc\s*$', s:get_statusline())
+ let sa1=screenattr(&lines - 1, 1)
+ let sa2=screenattr(&lines - 1, 2)
+ let sa3=screenattr(&lines - 1, 3)
+ call assert_equal(sa1, sa3)
+ call assert_notequal(sa1, sa2)
+
+ " %%: a percent sign.
+ set statusline=10%%
+ call assert_match('^10%\s*$', s:get_statusline())
+
+ " %!: evaluated expression is used as the option value
+ set statusline=%!2*3+1
+ call assert_match('7\s*$', s:get_statusline())
+
+ " Check statusline in current and non-current window
+ " with the 'fillchars' option.
+ set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:-
+ vsplit
+ set statusline=x%=y
+ call assert_match('^x^\+y^x=\+y$', s:get_statusline())
+ set fillchars&
+ close
+
+ %bw!
+ call delete('Xstatusline')
+ set statusline&
+ set laststatus&
+ set splitbelow&
+endfunc
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index e2b6de03c3..75ce24de46 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -39,3 +39,285 @@ function! Test_multiline_subst()
call assert_equal('xxxxx', getline(13))
enew!
endfunction
+
+function! Test_substitute_variants()
+ " Validate that all the 2-/3-letter variants which embed the flags into the
+ " command name actually work.
+ enew!
+ let ln = 'Testing string'
+ let variants = [
+ \ { 'cmd': ':s/Test/test/c', 'exp': 'testing string', 'prompt': 'y' },
+ \ { 'cmd': ':s/foo/bar/ce', 'exp': ln },
+ \ { 'cmd': ':s/t/r/cg', 'exp': 'Tesring srring', 'prompt': 'a' },
+ \ { 'cmd': ':s/t/r/ci', 'exp': 'resting string', 'prompt': 'y' },
+ \ { 'cmd': ':s/t/r/cI', 'exp': 'Tesring string', 'prompt': 'y' },
+ \ { 'cmd': ':s/t/r/cn', 'exp': ln },
+ \ { 'cmd': ':s/t/r/cp', 'exp': 'Tesring string', 'prompt': 'y' },
+ \ { 'cmd': ':s/t/r/cl', 'exp': 'Tesring string', 'prompt': 'y' },
+ \ { 'cmd': ':s/t/r/gc', 'exp': 'Tesring srring', 'prompt': 'a' },
+ \ { 'cmd': ':s/foo/bar/ge', 'exp': ln },
+ \ { 'cmd': ':s/t/r/g', 'exp': 'Tesring srring' },
+ \ { 'cmd': ':s/t/r/gi', 'exp': 'resring srring' },
+ \ { 'cmd': ':s/t/r/gI', 'exp': 'Tesring srring' },
+ \ { 'cmd': ':s/t/r/gn', 'exp': ln },
+ \ { 'cmd': ':s/t/r/gp', 'exp': 'Tesring srring' },
+ \ { 'cmd': ':s/t/r/gl', 'exp': 'Tesring srring' },
+ \ { 'cmd': ':s//r/gr', 'exp': 'Testr strr' },
+ \ { 'cmd': ':s/t/r/ic', 'exp': 'resting string', 'prompt': 'y' },
+ \ { 'cmd': ':s/foo/bar/ie', 'exp': ln },
+ \ { 'cmd': ':s/t/r/i', 'exp': 'resting string' },
+ \ { 'cmd': ':s/t/r/iI', 'exp': 'Tesring string' },
+ \ { 'cmd': ':s/t/r/in', 'exp': ln },
+ \ { 'cmd': ':s/t/r/ip', 'exp': 'resting string' },
+ \ { 'cmd': ':s//r/ir', 'exp': 'Testr string' },
+ \ { 'cmd': ':s/t/r/Ic', 'exp': 'Tesring string', 'prompt': 'y' },
+ \ { 'cmd': ':s/foo/bar/Ie', 'exp': ln },
+ \ { 'cmd': ':s/t/r/Ig', 'exp': 'Tesring srring' },
+ \ { 'cmd': ':s/t/r/Ii', 'exp': 'resting string' },
+ \ { 'cmd': ':s/t/r/I', 'exp': 'Tesring string' },
+ \ { 'cmd': ':s/t/r/Ip', 'exp': 'Tesring string' },
+ \ { 'cmd': ':s/t/r/Il', 'exp': 'Tesring string' },
+ \ { 'cmd': ':s//r/Ir', 'exp': 'Testr string' },
+ \ { 'cmd': ':s//r/rc', 'exp': 'Testr string', 'prompt': 'y' },
+ \ { 'cmd': ':s//r/rg', 'exp': 'Testr strr' },
+ \ { 'cmd': ':s//r/ri', 'exp': 'Testr string' },
+ \ { 'cmd': ':s//r/rI', 'exp': 'Testr string' },
+ \ { 'cmd': ':s//r/rn', 'exp': 'Testing string' },
+ \ { 'cmd': ':s//r/rp', 'exp': 'Testr string' },
+ \ { 'cmd': ':s//r/rl', 'exp': 'Testr string' },
+ \ { 'cmd': ':s//r/r', 'exp': 'Testr string' },
+ \]
+
+ for var in variants
+ for run in [1, 2]
+ let cmd = var.cmd
+ if run == 2 && cmd =~ "/.*/.*/."
+ " Change :s/from/to/{flags} to :s{flags}
+ let cmd = substitute(cmd, '/.*/', '', '')
+ endif
+ call setline(1, [ln])
+ let msg = printf('using "%s"', cmd)
+ let @/='ing'
+ let v:errmsg = ''
+ call feedkeys(cmd . "\<CR>" . get(var, 'prompt', ''), 'ntx')
+ " No error should exist (matters for testing e flag)
+ call assert_equal('', v:errmsg, msg)
+ call assert_equal(var.exp, getline('.'), msg)
+ endfor
+ endfor
+endfunction
+
+func Test_substitute_repeat()
+ " This caused an invalid memory access.
+ split Xfile
+ s/^/x
+ call feedkeys("Qsc\<CR>y", 'tx')
+ bwipe!
+endfunc
+
+" Tests for *sub-replace-special* and *sub-replace-expression* on :substitute.
+
+" Execute a list of :substitute command tests
+func Run_SubCmd_Tests(tests)
+ enew!
+ for t in a:tests
+ let start = line('.') + 1
+ let end = start + len(t[2]) - 1
+ exe "normal o" . t[0]
+ call cursor(start, 1)
+ exe t[1]
+ call assert_equal(t[2], getline(start, end), t[1])
+ endfor
+ enew!
+endfunc
+
+func Test_sub_cmd_1()
+ set magic
+ set cpo&
+
+ " List entry format: [input, cmd, output]
+ let tests = [['A', 's/A/&&/', ['AA']],
+ \ ['B', 's/B/\&/', ['&']],
+ \ ['C123456789', 's/C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']],
+ \ ['D', 's/D/d/', ['d']],
+ \ ['E', 's/E/~/', ['d']],
+ \ ['F', 's/F/\~/', ['~']],
+ \ ['G', 's/G/\ugg/', ['Gg']],
+ \ ['H', 's/H/\Uh\Eh/', ['Hh']],
+ \ ['I', 's/I/\lII/', ['iI']],
+ \ ['J', 's/J/\LJ\EJ/', ['jJ']],
+ \ ['K', 's/K/\Uk\ek/', ['Kk']],
+ \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']],
+ \ ['mMm', 's/M/\r/', ['m', 'm']],
+ \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']],
+ \ ['oOo', 's/O/\n/', ["o\no"]],
+ \ ['pPp', 's/P/\b/', ["p\<C-H>p"]],
+ \ ['qQq', 's/Q/\t/', ["q\tq"]],
+ \ ['rRr', 's/R/\\/', ['r\r']],
+ \ ['sSs', 's/S/\c/', ['scs']],
+ \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
+ \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
+ \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']]
+ \ ]
+ call Run_SubCmd_Tests(tests)
+endfunc
+
+func Test_sub_cmd_2()
+ set nomagic
+ set cpo&
+
+ " List entry format: [input, cmd, output]
+ let tests = [['A', 's/A/&&/', ['&&']],
+ \ ['B', 's/B/\&/', ['B']],
+ \ ['C123456789', 's/\mC\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']],
+ \ ['D', 's/D/d/', ['d']],
+ \ ['E', 's/E/~/', ['~']],
+ \ ['F', 's/F/\~/', ['~']],
+ \ ['G', 's/G/\ugg/', ['Gg']],
+ \ ['H', 's/H/\Uh\Eh/', ['Hh']],
+ \ ['I', 's/I/\lII/', ['iI']],
+ \ ['J', 's/J/\LJ\EJ/', ['jJ']],
+ \ ['K', 's/K/\Uk\ek/', ['Kk']],
+ \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']],
+ \ ['mMm', 's/M/\r/', ['m', 'm']],
+ \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']],
+ \ ['oOo', 's/O/\n/', ["o\no"]],
+ \ ['pPp', 's/P/\b/', ["p\<C-H>p"]],
+ \ ['qQq', 's/Q/\t/', ["q\tq"]],
+ \ ['rRr', 's/R/\\/', ['r\r']],
+ \ ['sSs', 's/S/\c/', ['scs']],
+ \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
+ \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
+ \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']]
+ \ ]
+ call Run_SubCmd_Tests(tests)
+endfunc
+
+func Test_sub_cmd_3()
+ set nomagic
+ set cpo&
+
+ " List entry format: [input, cmd, output]
+ let tests = [['aAa', "s/A/\\='\\'/", ['a\a']],
+ \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']],
+ \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']],
+ \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']],
+ \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']],
+ \ ['fFf', "s/F/\\='\r'/", ['f', 'f']],
+ \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']],
+ \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']],
+ \ ['iIi', "s/I/\\='\\\\\<C-V>\<C-J>'/", ["i\\\\\<C-V>", 'i']],
+ \ ['jJj', "s/J/\\='\n'/", ['j', 'j']],
+ \ ['kKk', 's/K/\="\r"/', ['k', 'k']],
+ \ ['lLl', 's/L/\="\n"/', ['l', 'l']]
+ \ ]
+ call Run_SubCmd_Tests(tests)
+endfunc
+
+" Test for submatch() on :substitue.
+func Test_sub_cmd_4()
+ set magic&
+ set cpo&
+
+ " List entry format: [input, cmd, output]
+ let tests = [ ['aAa', "s/A/\\=substitute(submatch(0), '.', '\\', '')/",
+ \ ['a\a']],
+ \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/",
+ \ ['b\b']],
+ \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/",
+ \ ["c\<C-V>", 'c']],
+ \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/",
+ \ ["d\<C-V>", 'd']],
+ \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/",
+ \ ["e\\\<C-V>", 'e']],
+ \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/",
+ \ ['f', 'f']],
+ \ ['gGg', 's/G/\=substitute(submatch(0), ".", "\<C-V>\<C-J>", "")/',
+ \ ["g\<C-V>", 'g']],
+ \ ['hHh', 's/H/\=substitute(submatch(0), ".", "\\\<C-V>\<C-J>", "")/',
+ \ ["h\<C-V>", 'h']],
+ \ ['iIi', 's/I/\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-J>", "")/',
+ \ ["i\\\<C-V>", 'i']],
+ \ ['jJj', "s/J/\\=substitute(submatch(0), '.', '\\n', '')/",
+ \ ['j', 'j']],
+ \ ['kKk', "s/K/\\=substitute(submatch(0), '.', '\\r', '')/",
+ \ ['k', 'k']],
+ \ ['lLl', "s/L/\\=substitute(submatch(0), '.', '\\n', '')/",
+ \ ['l', 'l']],
+ \ ]
+ call Run_SubCmd_Tests(tests)
+endfunc
+
+func Test_sub_cmd_5()
+ set magic&
+ set cpo&
+
+ " List entry format: [input, cmd, output]
+ let tests = [ ['A123456789', 's/A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)/', ['A123456789987654321']],
+ \ ['B123456789', 's/B\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=string([submatch(0, 1), submatch(9, 1), submatch(8, 1), submatch(7, 1), submatch(6, 1), submatch(5, 1), submatch(4, 1), submatch(3, 1), submatch(2, 1), submatch(1, 1)])/', ["[['B123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']]"]],
+ \ ]
+ call Run_SubCmd_Tests(tests)
+endfunc
+
+" Test for *:s%* on :substitute.
+func Test_sub_cmd_6()
+ throw "skipped: Nvim removed POSIX-related 'cpoptions' flags"
+ set magic&
+ set cpo+=/
+
+ " List entry format: [input, cmd, output]
+ let tests = [ ['A', 's/A/a/', ['a']],
+ \ ['B', 's/B/%/', ['a']],
+ \ ]
+ call Run_SubCmd_Tests(tests)
+
+ set cpo-=/
+ let tests = [ ['C', 's/C/c/', ['c']],
+ \ ['D', 's/D/%/', ['%']],
+ \ ]
+ call Run_SubCmd_Tests(tests)
+
+ set cpo&
+endfunc
+
+" Test for :s replacing \n with line break.
+func Test_sub_cmd_7()
+ set magic&
+ set cpo&
+
+ " List entry format: [input, cmd, output]
+ let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']],
+ \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']],
+ \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]],
+ \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]],
+ \ ["E\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>E", 's/E\_.\{-}E/\=strtrans(string(submatch(0, 1)))/', [strtrans("['E\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>E']")]],
+ \ ]
+ call Run_SubCmd_Tests(tests)
+
+ exe "normal oQ\nQ\<Esc>k"
+ call assert_fails('s/Q[^\n]Q/\=submatch(0)."foobar"/', 'E486')
+ enew!
+endfunc
+
+func TitleString()
+ let check = 'foo' =~ 'bar'
+ return ""
+endfunc
+
+func Test_sub_cmd_8()
+ set titlestring=%{TitleString()}
+
+ enew!
+ call append(0, ['', 'test_one', 'test_two'])
+ call cursor(1,1)
+ /^test_one/s/.*/\="foo\nbar"/
+ call assert_equal('foo', getline(2))
+ call assert_equal('bar', getline(3))
+ call feedkeys(':/^test_two/s/.*/\="foo\nbar"/c', "t")
+ call feedkeys("\<CR>y", "xt")
+ call assert_equal('foo', getline(4))
+ call assert_equal('bar', getline(5))
+
+ enew!
+ set titlestring&
+endfunc
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index af2cbbfe8e..8465fe7d45 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -1,5 +1,7 @@
" Test for syntax and syntax iskeyword option
+source view_util.vim
+
func GetSyntaxItem(pat)
let c = ''
let a = ['a', getreg('a'), getregtype('a')]
@@ -50,7 +52,7 @@ func Test_syn_iskeyword()
setlocal isk-=_
call assert_equal('DLTD_BY', GetSyntaxItem('DLTD'))
/\<D\k\+\>/:norm! ygn
- let b2=@0
+ let b2 = @0
call assert_equal('DLTD', @0)
syn iskeyword clear
@@ -76,3 +78,307 @@ func Test_syntax_after_reload()
call assert_true(exists('g:gotit'))
call delete('Xsomefile')
endfunc
+
+func Test_syntime()
+ if !has('profile')
+ return
+ endif
+
+ syntax on
+ syntime on
+ let a = execute('syntime report')
+ call assert_equal("\nNo Syntax items defined for this buffer", a)
+
+ view ../memfile_test.c
+ setfiletype cpp
+ redraw
+ let a = execute('syntime report')
+ call assert_match('^ TOTAL *COUNT *MATCH *SLOWEST *AVERAGE *NAME *PATTERN', a)
+ call assert_match(' \d*\.\d* \+[^0]\d* .* cppRawString ', a)
+ call assert_match(' \d*\.\d* \+[^0]\d* .* cppNumber ', a)
+
+ syntime off
+ syntime clear
+ let a = execute('syntime report')
+ call assert_match('^ TOTAL *COUNT *MATCH *SLOWEST *AVERAGE *NAME *PATTERN', a)
+ call assert_notmatch('.* cppRawString *', a)
+ call assert_notmatch('.* cppNumber*', a)
+ call assert_notmatch('[1-9]', a)
+
+ call assert_fails('syntime abc', 'E475')
+
+ syntax clear
+ let a = execute('syntime report')
+ call assert_equal("\nNo Syntax items defined for this buffer", a)
+
+ bd
+endfunc
+
+func Test_syntax_list()
+ syntax on
+ let a = execute('syntax list')
+ call assert_equal("\nNo Syntax items defined for this buffer", a)
+
+ view ../memfile_test.c
+ setfiletype c
+
+ let a = execute('syntax list')
+ call assert_match('cInclude*', a)
+ call assert_match('cDefine', a)
+
+ let a = execute('syntax list cDefine')
+ call assert_notmatch('cInclude*', a)
+ call assert_match('cDefine', a)
+ call assert_match(' links to Macro$', a)
+
+ call assert_fails('syntax list ABCD', 'E28:')
+ call assert_fails('syntax list @ABCD', 'E392:')
+
+ syntax clear
+ let a = execute('syntax list')
+ call assert_equal("\nNo Syntax items defined for this buffer", a)
+
+ bd
+endfunc
+
+func Test_syntax_completion()
+ call feedkeys(":syn \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syn case clear cluster conceal enable include iskeyword keyword list manual match off on region reset spell sync', @:)
+
+ call feedkeys(":syn case \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syn case ignore match', @:)
+
+ call feedkeys(":syn spell \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syn spell default notoplevel toplevel', @:)
+
+ call feedkeys(":syn sync \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syn sync ccomment clear fromstart linebreaks= linecont lines= match maxlines= minlines= region', @:)
+
+ " Check that clearing "Aap" avoids it showing up before Boolean.
+ hi Aap ctermfg=blue
+ call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^"syn list Aap Boolean Character ', @:)
+ hi clear Aap
+
+ call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^"syn list Boolean Character ', @:)
+
+ call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^"syn match Boolean Character ', @:)
+endfunc
+
+func Test_syntax_arg_skipped()
+ syn clear
+ syntax case ignore
+ if 0
+ syntax case match
+ endif
+ call assert_match('case ignore', execute('syntax case'))
+
+ syn keyword Foo foo
+ call assert_match('Foo', execute('syntax'))
+ syn clear
+ call assert_match('case match', execute('syntax case'))
+ call assert_notmatch('Foo', execute('syntax'))
+
+ if has('conceal')
+ syn clear
+ syntax conceal on
+ if 0
+ syntax conceal off
+ endif
+ call assert_match('conceal on', execute('syntax conceal'))
+ syn clear
+ call assert_match('conceal off', execute('syntax conceal'))
+ endif
+
+ syntax conceal on
+ syntax conceal off
+ call assert_match('conceal off', execute('syntax conceal'))
+
+ syntax region Bar start=/</ end=/>/
+ if 0
+ syntax region NotTest start=/</ end=/>/ contains=@Spell
+ endif
+ call assert_match('Bar', execute('syntax'))
+ call assert_notmatch('NotTest', execute('syntax'))
+ call assert_notmatch('Spell', execute('syntax'))
+
+ hi Foo ctermfg=blue
+ let a = execute('hi Foo')
+ if 0
+ syntax rest
+ endif
+ call assert_equal(a, execute('hi Foo'))
+ hi clear Bar
+ hi clear Foo
+
+ set ft=tags
+ syn off
+ if 0
+ syntax enable
+ endif
+ call assert_match('No Syntax items defined', execute('syntax'))
+ syntax enable
+ call assert_match('tagComment', execute('syntax'))
+ set ft=
+
+ syn clear
+ if 0
+ syntax include @Spell nothing
+ endif
+ call assert_notmatch('Spell', execute('syntax'))
+
+ syn clear
+ syn iskeyword 48-57,$,_
+ call assert_match('48-57,$,_', execute('syntax iskeyword'))
+ if 0
+ syn clear
+ syn iskeyword clear
+ endif
+ call assert_match('48-57,$,_', execute('syntax iskeyword'))
+ syn iskeyword clear
+ call assert_match('not set', execute('syntax iskeyword'))
+ syn iskeyword 48-57,$,_
+ syn clear
+ call assert_match('not set', execute('syntax iskeyword'))
+
+ syn clear
+ syn keyword Foo foo
+ if 0
+ syn keyword NotAdded bar
+ endif
+ call assert_match('Foo', execute('syntax'))
+ call assert_notmatch('NotAdded', execute('highlight'))
+
+ syn clear
+ syn keyword Foo foo
+ call assert_match('Foo', execute('syntax'))
+ call assert_match('Foo', execute('syntax list'))
+ call assert_notmatch('Foo', execute('if 0 | syntax | endif'))
+ call assert_notmatch('Foo', execute('if 0 | syntax list | endif'))
+
+ syn clear
+ syn match Fopi /asdf/
+ if 0
+ syn match Fopx /asdf/
+ endif
+ call assert_match('Fopi', execute('syntax'))
+ call assert_notmatch('Fopx', execute('syntax'))
+
+ syn clear
+ syn spell toplevel
+ call assert_match('spell toplevel', execute('syntax spell'))
+ if 0
+ syn spell notoplevel
+ endif
+ call assert_match('spell toplevel', execute('syntax spell'))
+ syn spell notoplevel
+ call assert_match('spell notoplevel', execute('syntax spell'))
+ syn spell default
+ call assert_match('spell default', execute('syntax spell'))
+
+ syn clear
+ if 0
+ syntax cluster Spell
+ endif
+ call assert_notmatch('Spell', execute('syntax'))
+
+ syn clear
+ syn keyword Foo foo
+ syn sync ccomment
+ syn sync maxlines=5
+ if 0
+ syn sync maxlines=11
+ endif
+ call assert_match('on C-style comments', execute('syntax sync'))
+ call assert_match('maximal 5 lines', execute('syntax sync'))
+ syn sync clear
+ if 0
+ syn sync ccomment
+ endif
+ call assert_notmatch('on C-style comments', execute('syntax sync'))
+
+ syn clear
+endfunc
+
+func Test_invalid_arg()
+ call assert_fails('syntax case asdf', 'E390:')
+ call assert_fails('syntax conceal asdf', 'E390:')
+ call assert_fails('syntax spell asdf', 'E390:')
+endfunc
+
+func Test_syn_sync()
+ syntax region HereGroup start=/this/ end=/that/
+ syntax sync match SyncHere grouphere HereGroup "pattern"
+ call assert_match('SyncHere', execute('syntax sync'))
+ syn sync clear
+ call assert_notmatch('SyncHere', execute('syntax sync'))
+ syn clear
+endfunc
+
+func Test_syn_clear()
+ syntax keyword Foo foo
+ syntax keyword Bar tar
+ call assert_match('Foo', execute('syntax'))
+ call assert_match('Bar', execute('syntax'))
+ call assert_equal('Foo', synIDattr(hlID("Foo"), "name"))
+ syn clear Foo
+ call assert_notmatch('Foo', execute('syntax'))
+ call assert_match('Bar', execute('syntax'))
+ call assert_equal('Foo', synIDattr(hlID("Foo"), "name"))
+ syn clear Foo Bar
+ call assert_notmatch('Foo', execute('syntax'))
+ call assert_notmatch('Bar', execute('syntax'))
+ hi clear Foo
+ call assert_equal('Foo', synIDattr(hlID("Foo"), "name"))
+ hi clear Bar
+endfunc
+
+func Test_invalid_name()
+ syn clear
+ syn keyword Nop yes
+ call assert_fails("syntax keyword Wr\x17ong bar", 'E669:')
+ syntax keyword @Wrong bar
+ call assert_match('W18:', execute('1messages'))
+ syn clear
+ hi clear Nop
+ hi clear @Wrong
+endfunc
+
+
+func Test_conceal()
+ if !has('conceal')
+ return
+ endif
+
+ new
+ call setline(1, ['', '123456'])
+ syn match test23 "23" conceal cchar=X
+ syn match test45 "45" conceal
+
+ set conceallevel=0
+ call assert_equal('123456 ', ScreenLines(2, 7)[0])
+ call assert_equal([[0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
+
+ set conceallevel=1
+ call assert_equal('1X 6 ', ScreenLines(2, 7)[0])
+ call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, ' ', 2], [1, ' ', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
+
+ set conceallevel=1
+ set listchars=conceal:Y
+ call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, 'Y', 2], [1, 'Y', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
+ call assert_equal('1XY6 ', ScreenLines(2, 7)[0])
+
+ set conceallevel=2
+ call assert_match('1X6 ', ScreenLines(2, 7)[0])
+ call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
+
+ set conceallevel=3
+ call assert_match('16 ', ScreenLines(2, 7)[0])
+ call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
+
+ syn clear
+ set conceallevel&
+ bw!
+endfunc
diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim
new file mode 100644
index 0000000000..ce9d110d82
--- /dev/null
+++ b/src/nvim/testdir/test_system.vim
@@ -0,0 +1,92 @@
+" Tests for system() and systemlist()
+
+function! Test_System()
+ if !executable('echo') || !executable('cat') || !executable('wc')
+ return
+ endif
+ let out = system('echo 123')
+ " On Windows we may get a trailing space.
+ if out != "123 \n"
+ call assert_equal("123\n", out)
+ endif
+
+ let out = systemlist('echo 123')
+ " On Windows we may get a trailing space and CR.
+ if out != ["123 \r"]
+ call assert_equal(['123'], out)
+ endif
+
+ call assert_equal('123', system('cat', '123'))
+ call assert_equal(['123'], systemlist('cat', '123'))
+ call assert_equal(["as\<NL>df"], systemlist('cat', ["as\<NL>df"]))
+
+ new Xdummy
+ call setline(1, ['asdf', "pw\<NL>er", 'xxxx'])
+ let out = system('wc -l', bufnr('%'))
+ " On OS/X we get leading spaces
+ let out = substitute(out, '^ *', '', '')
+ call assert_equal("3\n", out)
+
+ let out = systemlist('wc -l', bufnr('%'))
+ " On Windows we may get a trailing CR.
+ if out != ["3\r"]
+ " On OS/X we get leading spaces
+ if type(out) == v:t_list
+ let out[0] = substitute(out[0], '^ *', '', '')
+ endif
+ call assert_equal(['3'], out)
+ endif
+
+ let out = systemlist('cat', bufnr('%'))
+ " On Windows we may get a trailing CR.
+ if out != ["asdf\r", "pw\<NL>er\r", "xxxx\r"]
+ call assert_equal(['asdf', "pw\<NL>er", 'xxxx'], out)
+ endif
+ bwipe!
+
+ call assert_fails('call system("wc -l", 99999)', 'E86:')
+endfunction
+
+function! Test_system_exmode()
+ if has('unix') " echo $? only works on Unix
+ let cmd = ' -es --headless -u NONE -c "source Xscript" +q; echo "result=$?"'
+ " Need to put this in a script, "catch" isn't found after an unknown
+ " function.
+ call writefile(['try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript')
+ let a = system(v:progpath . cmd)
+ call assert_match('result=0', a)
+ call assert_equal(0, v:shell_error)
+ endif
+
+ " Error before try does set error flag.
+ call writefile(['call nosuchfunction()', 'try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript')
+ if has('unix') " echo $? only works on Unix
+ let a = system(v:progpath . cmd)
+ call assert_notequal('0', a[0])
+ endif
+
+ let cmd = ' -es --headless -u NONE -c "source Xscript" +q'
+ let a = system(v:progpath . cmd)
+ call assert_notequal(0, v:shell_error)
+ call delete('Xscript')
+
+ if has('unix') " echo $? only works on Unix
+ let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q; echo $?'
+ let a = system(v:progpath. cmd)
+ call assert_notequal(0, a[0])
+ endif
+
+ let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q'
+ let a = system(v:progpath. cmd)
+ call assert_notequal(0, v:shell_error)
+
+ if has('unix') " echo $? only works on Unix
+ let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q; echo $?'
+ let a = system(v:progpath. cmd)
+ call assert_notequal(0, a[0])
+ endif
+
+ let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q'
+ let a = system(v:progpath. cmd)
+ call assert_notequal(0, v:shell_error)
+endfunc
diff --git a/src/nvim/testdir/test_tab.vim b/src/nvim/testdir/test_tab.vim
new file mode 100644
index 0000000000..b847dbd962
--- /dev/null
+++ b/src/nvim/testdir/test_tab.vim
@@ -0,0 +1,45 @@
+
+" Tests for "r<Tab>" with 'smarttab' and 'expandtab' set/not set.
+" Also test that dv_ works correctly
+func Test_smarttab()
+ enew!
+ set smarttab expandtab ts=8 sw=4
+ " make sure that backspace works, no matter what termcap is used
+ exe "set t_kD=\<C-V>x7f t_kb=\<C-V>x08"
+ call append(0, ['start text',
+ \ "\t\tsome test text",
+ \ 'test text',
+ \ "\t\tother test text",
+ \ ' a cde',
+ \ ' f ghi',
+ \ 'test text',
+ \ ' Second line beginning with whitespace'
+ \ ])
+ call cursor(1, 1)
+ exe "normal /some\<CR>"
+ exe "normal r\t"
+ call assert_equal("\t\t ome test text", getline('.'))
+ set noexpandtab
+ exe "normal /other\<CR>"
+ exe "normal r\t"
+ call assert_equal("\t\t ther test text", getline('.'))
+
+ " Test replacing with Tabs and then backspacing to undo it
+ exe "normal j0wR\t\t\t\<BS>\<BS>\<BS>"
+ call assert_equal(" a cde", getline('.'))
+ " Test replacing with Tabs
+ exe "normal j0wR\t\t\t"
+ call assert_equal(" \t\thi", getline('.'))
+
+ " Test that copyindent works with expandtab set
+ set expandtab smartindent copyindent ts=8 sw=8 sts=8
+ exe "normal jo{\<CR>x"
+ call assert_equal('{', getline(line('.') - 1))
+ call assert_equal(' x', getline('.'))
+ set nosol
+ exe "normal /Second line/\<CR>"
+ exe "normal fwdv_"
+ call assert_equal(' with whitespace', getline('.'))
+ enew!
+ set expandtab& smartindent& copyindent& ts& sw& sts&
+endfunc
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 33139fcda0..180563ebbd 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -473,5 +473,24 @@ func Test_tabnext_on_buf_unload2()
endwhile
endfunc
+func Test_close_on_quitpre()
+ " This once caused a crash
+ edit Xtest
+ new
+ only
+ set bufhidden=delete
+ au QuitPre <buffer> close
+ tabnew tab1
+ tabnew tab2
+ 1tabn
+ q!
+ call assert_equal(1, tabpagenr())
+ call assert_equal(2, tabpagenr('$'))
+ " clean up
+ while tabpagenr('$') > 1
+ bwipe!
+ endwhile
+ buf Xtest
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 0d697b3f3e..d5ce193bc6 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -95,4 +95,41 @@ function Test_keyword_jump()
call delete('Xinclude')
endfunction
+" Test for jumping to a tag with 'hidden' set, with symbolic link in path of
+" tag. This only works for Unix, because of the symbolic link.
+func Test_tag_symbolic()
+ if !has('unix')
+ return
+ endif
+ set hidden
+ call delete("Xtest.dir", "rf")
+ call system("ln -s . Xtest.dir")
+ " Create a tags file with the current directory name inserted.
+ call writefile([
+ \ "SECTION_OFF " . getcwd() . "/Xtest.dir/Xtest.c /^#define SECTION_OFF 3$/",
+ \ '',
+ \ ], 'Xtags')
+ call writefile(['#define SECTION_OFF 3',
+ \ '#define NUM_SECTIONS 3'], 'Xtest.c')
+
+ " Try jumping to a tag, but with a path that contains a symbolic link. When
+ " wrong, this will give the ATTENTION message. The next space will then be
+ " eaten by hit-return, instead of moving the cursor to 'd'.
+ set tags=Xtags
+ enew!
+ call append(0, 'SECTION_OFF')
+ call cursor(1,1)
+ exe "normal \<C-]> "
+ call assert_equal('Xtest.c', expand('%:t'))
+ call assert_equal(2, col('.'))
+
+ set hidden&
+ set tags&
+ enew!
+ call delete('Xtags')
+ call delete('Xtest.c')
+ call delete("Xtest.dir", "rf")
+ %bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
new file mode 100644
index 0000000000..999566c6ac
--- /dev/null
+++ b/src/nvim/testdir/test_textformat.vim
@@ -0,0 +1,168 @@
+" Tests for the various 'formatoptions' settings
+func Test_text_format()
+ enew!
+
+ setl noai tw=2 fo=t
+ call append('$', [
+ \ '{',
+ \ ' ',
+ \ '',
+ \ '}'])
+ exe "normal /^{/+1\n0"
+ normal gRa b
+ let lnum = line('.')
+ call assert_equal([
+ \ 'a',
+ \ 'b'], getline(lnum - 1, lnum))
+
+ normal ggdG
+ setl ai tw=2 fo=tw
+ call append('$', [
+ \ '{',
+ \ 'a b ',
+ \ '',
+ \ 'a ',
+ \ '}'])
+ exe "normal /^{/+1\n0"
+ normal gqgqjjllab
+ let lnum = line('.')
+ call assert_equal([
+ \ 'a ',
+ \ 'b ',
+ \ '',
+ \ 'a ',
+ \ 'b'], getline(lnum - 4, lnum))
+
+ normal ggdG
+ setl tw=3 fo=t
+ call append('$', [
+ \ '{',
+ \ "a \<C-A>",
+ \ '}'])
+ exe "normal /^{/+1\n0"
+ exe "normal gqgqo\na \<C-V>\<C-A>"
+ let lnum = line('.')
+ call assert_equal([
+ \ 'a',
+ \ "\<C-A>",
+ \ '',
+ \ 'a',
+ \ "\<C-A>"], getline(lnum - 4, lnum))
+
+ normal ggdG
+ setl tw=2 fo=tcq1 comments=:#
+ call append('$', [
+ \ '{',
+ \ 'a b',
+ \ '#a b',
+ \ '}'])
+ exe "normal /^{/+1\n0"
+ exe "normal gqgqjgqgqo\na b\n#a b"
+ let lnum = line('.')
+ call assert_equal([
+ \ 'a b',
+ \ '#a b',
+ \ '',
+ \ 'a b',
+ \ '#a b'], getline(lnum - 4, lnum))
+
+ normal ggdG
+ setl tw=5 fo=tcn comments=:#
+ call append('$', [
+ \ '{',
+ \ ' 1 a',
+ \ '# 1 a',
+ \ '}'])
+ exe "normal /^{/+1\n0"
+ exe "normal A b\<Esc>jA b"
+ let lnum = line('.')
+ call assert_equal([
+ \ ' 1 a',
+ \ ' b',
+ \ '# 1 a',
+ \ '# b'], getline(lnum - 3, lnum))
+
+ normal ggdG
+ setl tw=5 fo=t2a si
+ call append('$', [
+ \ '{',
+ \ '',
+ \ ' x a',
+ \ ' b',
+ \ ' c',
+ \ '',
+ \ '}'])
+ exe "normal /^{/+3\n0"
+ exe "normal i \<Esc>A_"
+ let lnum = line('.')
+ call assert_equal([
+ \ '',
+ \ ' x a',
+ \ ' b_',
+ \ ' c',
+ \ ''], getline(lnum - 2, lnum + 2))
+
+ normal ggdG
+ setl tw=5 fo=qn comments=:#
+ call append('$', [
+ \ '{',
+ \ '# 1 a b',
+ \ '}'])
+ exe "normal /^{/+1\n5|"
+ normal gwap
+ call assert_equal(5, col('.'))
+ let lnum = line('.')
+ call assert_equal([
+ \ '# 1 a',
+ \ '# b'], getline(lnum, lnum + 1))
+
+ normal ggdG
+ setl tw=5 fo=q2 comments=:#
+ call append('$', [
+ \ '{',
+ \ '# x',
+ \ '# a b',
+ \ '}'])
+ exe "normal /^{/+1\n0"
+ normal gwap
+ let lnum = line('.')
+ call assert_equal([
+ \ '# x a',
+ \ '# b'], getline(lnum, lnum + 1))
+
+ normal ggdG
+ setl tw& fo=a
+ call append('$', [
+ \ '{',
+ \ ' 1aa',
+ \ ' 2bb',
+ \ '}'])
+ exe "normal /^{/+2\n0"
+ normal I^^
+ call assert_equal('{ 1aa ^^2bb }', getline('.'))
+
+ normal ggdG
+ setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/
+ call append('$', [
+ \ '/* abc def ghi jkl ',
+ \ ' * mno pqr stu',
+ \ ' */'])
+ exe "normal /mno pqr/\n"
+ normal A vwx yz
+ let lnum = line('.')
+ call assert_equal([
+ \ ' * mno pqr stu ',
+ \ ' * vwx yz',
+ \ ' */'], getline(lnum - 1, lnum + 1))
+
+ normal ggdG
+ setl tw=12 fo=tqnc comments=:#
+ call setline('.', '# 1 xxxxx')
+ normal A foobar
+ call assert_equal([
+ \ '# 1 xxxxx',
+ \ '# foobar'], getline(1, 2))
+
+ setl ai& tw& fo& si& comments&
+ enew!
+endfunc
diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim
index 630ae5d3a4..684f197f5f 100644
--- a/src/nvim/testdir/test_textobjects.vim
+++ b/src/nvim/testdir/test_textobjects.vim
@@ -4,7 +4,7 @@ if !has('textobjects')
finish
endif
-function! CpoM(line, useM, expected)
+func CpoM(line, useM, expected)
new
if a:useM
@@ -28,16 +28,127 @@ function! CpoM(line, useM, expected)
call assert_equal(getreg('"'), a:expected[2])
q!
-endfunction
+endfunc
-function! Test_inner_block_without_cpo_M()
+func Test_inner_block_without_cpo_M()
call CpoM('(red \(blue) green)', 0, ['red \(blue', 'red \(blue', ''])
-endfunction
+endfunc
-function! Test_inner_block_with_cpo_M_left_backslash()
+func Test_inner_block_with_cpo_M_left_backslash()
call CpoM('(red \(blue) green)', 1, ['red \(blue) green', 'blue', 'red \(blue) green'])
-endfunction
+endfunc
-function! Test_inner_block_with_cpo_M_right_backslash()
+func Test_inner_block_with_cpo_M_right_backslash()
call CpoM('(red (blue\) green)', 1, ['red (blue\) green', 'blue\', 'red (blue\) green'])
-endfunction
+endfunc
+
+func Test_quote_selection_selection_exclusive()
+ new
+ call setline(1, "a 'bcde' f")
+ set selection=exclusive
+ exe "norm! fdvhi'y"
+ call assert_equal('bcde', @")
+ set selection&vim
+ bw!
+endfunc
+
+" Tests for string and html text objects
+func Test_string_html_objects()
+ enew!
+
+ let t = '"wo\"rd\\" foo'
+ put =t
+ normal! da"
+ call assert_equal('foo', getline('.'))
+
+ let t = "'foo' 'bar' 'piep'"
+ put =t
+ normal! 0va'a'rx
+ call assert_equal("xxxxxxxxxxxx'piep'", getline('.'))
+
+ let t = "bla bla `quote` blah"
+ put =t
+ normal! 02f`da`
+ call assert_equal("bla bla blah", getline('.'))
+
+ let t = 'out " in "noXno"'
+ put =t
+ normal! 0fXdi"
+ call assert_equal('out " in ""', getline('.'))
+
+ let t = "\"'\" 'blah' rep 'buh'"
+ put =t
+ normal! 03f'vi'ry
+ call assert_equal("\"'\" 'blah'yyyyy'buh'", getline('.'))
+
+ set quoteescape=+*-
+ let t = "bla `s*`d-`+++`l**` b`la"
+ put =t
+ normal! di`
+ call assert_equal("bla `` b`la", getline('.'))
+
+ let t = 'voo "nah" sdf " asdf" sdf " sdf" sd'
+ put =t
+ normal! $F"va"oha"i"rz
+ call assert_equal('voo "zzzzzzzzzzzzzzzzzzzzzzzzzzzzsd', getline('.'))
+
+ let t = "-<b>asdf<i>Xasdf</i>asdf</b>-"
+ put =t
+ normal! fXdit
+ call assert_equal('-<b>asdf<i></i>asdf</b>-', getline('.'))
+
+ let t = "-<b>asdX<i>a<i />sdf</i>asdf</b>-"
+ put =t
+ normal! 0fXdit
+ call assert_equal('-<b></b>-', getline('.'))
+
+ let t = "-<b>asdf<i>Xasdf</i>asdf</b>-"
+ put =t
+ normal! fXdat
+ call assert_equal('-<b>asdfasdf</b>-', getline('.'))
+
+ let t = "-<b>asdX<i>as<b />df</i>asdf</b>-"
+ put =t
+ normal! 0fXdat
+ call assert_equal('--', getline('.'))
+
+ let t = "-<b>\ninnertext object\n</b>"
+ put =t
+ normal! dit
+ call assert_equal('-<b></b>', getline('.'))
+
+ set quoteescape&
+ enew!
+endfunc
+
+" Tests for match() and matchstr()
+func Test_match()
+ call assert_equal("b", matchstr("abcd", ".", 0, 2))
+ call assert_equal("bc", matchstr("abcd", "..", 0, 2))
+ call assert_equal("c", matchstr("abcd", ".", 2, 0))
+ call assert_equal("a", matchstr("abcd", ".", 0, -1))
+ call assert_equal(-1, match("abcd", ".", 0, 5))
+ call assert_equal(0 , match("abcd", ".", 0, -1))
+ call assert_equal(0 , match('abc', '.', 0, 1))
+ call assert_equal(1 , match('abc', '.', 0, 2))
+ call assert_equal(2 , match('abc', '.', 0, 3))
+ call assert_equal(-1, match('abc', '.', 0, 4))
+ call assert_equal(1 , match('abc', '.', 1, 1))
+ call assert_equal(2 , match('abc', '.', 2, 1))
+ call assert_equal(-1, match('abc', '.', 3, 1))
+ call assert_equal(3 , match('abc', '$', 0, 1))
+ call assert_equal(-1, match('abc', '$', 0, 2))
+ call assert_equal(3 , match('abc', '$', 1, 1))
+ call assert_equal(3 , match('abc', '$', 2, 1))
+ call assert_equal(3 , match('abc', '$', 3, 1))
+ call assert_equal(-1, match('abc', '$', 4, 1))
+ call assert_equal(0 , match('abc', '\zs', 0, 1))
+ call assert_equal(1 , match('abc', '\zs', 0, 2))
+ call assert_equal(2 , match('abc', '\zs', 0, 3))
+ call assert_equal(3 , match('abc', '\zs', 0, 4))
+ call assert_equal(-1, match('abc', '\zs', 0, 5))
+ call assert_equal(1 , match('abc', '\zs', 1, 1))
+ call assert_equal(2 , match('abc', '\zs', 2, 1))
+ call assert_equal(3 , match('abc', '\zs', 3, 1))
+ call assert_equal(-1, match('abc', '\zs', 4, 1))
+endfunc
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 9ff73fd870..2bc6073d52 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -237,3 +237,54 @@ func Test_insert_expr()
close!
endfunc
+
+func Test_undofile_earlier()
+ throw 'skipped: Nvim does not support test_settime()'
+
+ let t0 = localtime() - 43200
+ call test_settime(t0)
+ new Xfile
+ call feedkeys("ione\<Esc>", 'xt')
+ set ul=100
+ call test_settime(t0 + 1)
+ call feedkeys("otwo\<Esc>", 'xt')
+ set ul=100
+ call test_settime(t0 + 2)
+ call feedkeys("othree\<Esc>", 'xt')
+ set ul=100
+ w
+ wundo Xundofile
+ bwipe!
+ " restore normal timestamps.
+ call test_settime(0)
+ new Xfile
+ rundo Xundofile
+ earlier 1d
+ call assert_equal('', getline(1))
+ bwipe!
+ call delete('Xfile')
+ call delete('Xundofile')
+endfunc
+
+" Test for undo working properly when executing commands from a register.
+" Also test this in an empty buffer.
+func Test_cmd_in_reg_undo()
+ enew!
+ let @a="Ox\<Esc>jAy\<Esc>kdd"
+ edit +/^$ test_undo.vim
+ normal @au
+ call assert_equal(0, &modified)
+ return
+ new
+ normal @au
+ call assert_equal(0, &modified)
+ only!
+ let @a=''
+endfunc
+
+func Test_redo_empty_line()
+ new
+ exe "norm\x16r\x160"
+ exe "norm."
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim
index f6705997a9..3f06058d03 100644
--- a/src/nvim/testdir/test_unlet.vim
+++ b/src/nvim/testdir/test_unlet.vim
@@ -3,7 +3,7 @@
func Test_read_only()
try
" this caused a crash
- unlet count
+ unlet v:count
catch
call assert_true(v:exception =~ ':E795:')
endtry
@@ -24,3 +24,7 @@ func Test_not_existing()
call assert_true(v:exception =~ ':E108:')
endtry
endfunc
+
+func Test_unlet_fails()
+ call assert_fails('unlet v:["count"]', 'E46:')
+endfunc
diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim
index d0864ec64c..db603610da 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -102,3 +102,107 @@ func Test_CmdUndefined()
call assert_fails('Dothat', 'E492:')
call assert_equal('yes', g:didnot)
endfunc
+
+func Test_CmdErrors()
+ call assert_fails('com! docmd :', 'E183:')
+ call assert_fails('com! \<Tab> :', 'E182:')
+ call assert_fails('com! _ :', 'E182:')
+ call assert_fails('com! X :', 'E841:')
+ call assert_fails('com! - DoCmd :', 'E175:')
+ call assert_fails('com! -xxx DoCmd :', 'E181:')
+ call assert_fails('com! -addr DoCmd :', 'E179:')
+ call assert_fails('com! -complete DoCmd :', 'E179:')
+ call assert_fails('com! -complete=xxx DoCmd :', 'E180:')
+ call assert_fails('com! -complete=custom DoCmd :', 'E467:')
+ call assert_fails('com! -complete=customlist DoCmd :', 'E467:')
+ call assert_fails('com! -complete=behave,CustomComplete DoCmd :', 'E468:')
+ call assert_fails('com! -nargs=x DoCmd :', 'E176:')
+ call assert_fails('com! -count=1 -count=2 DoCmd :', 'E177:')
+ call assert_fails('com! -count=x DoCmd :', 'E178:')
+ call assert_fails('com! -range=x DoCmd :', 'E178:')
+
+ com! -nargs=0 DoCmd :
+ call assert_fails('DoCmd x', 'E488:')
+
+ com! -nargs=1 DoCmd :
+ call assert_fails('DoCmd', 'E471:')
+
+ com! -nargs=+ DoCmd :
+ call assert_fails('DoCmd', 'E471:')
+
+ call assert_fails('com DoCmd :', 'E174:')
+ comclear
+ call assert_fails('delcom DoCmd', 'E184:')
+endfunc
+
+func CustomComplete(A, L, P)
+ return "January\nFebruary\nMars\n"
+endfunc
+
+func CustomCompleteList(A, L, P)
+ return [ "Monday", "Tuesday", "Wednesday" ]
+endfunc
+
+func Test_CmdCompletion()
+ call feedkeys(":com -\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com -addr bang bar buffer complete count nargs range register', @:)
+
+ call feedkeys(":com -nargs=0 -\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com -nargs=0 -addr bang bar buffer complete count nargs range register', @:)
+
+ call feedkeys(":com -nargs=\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com -nargs=* + 0 1 ?', @:)
+
+ call feedkeys(":com -addr=\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com -addr=arguments buffers lines loaded_buffers quickfix tabs windows', @:)
+
+ call feedkeys(":com -complete=co\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com -complete=color command compiler', @:)
+
+ command! DoCmd1 :
+ command! DoCmd2 :
+ call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com DoCmd1 DoCmd2', @:)
+
+ call feedkeys(":DoC\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd1 DoCmd2', @:)
+
+ call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"delcom DoCmd1 DoCmd2', @:)
+
+ delcom DoCmd1
+ call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"delcom DoCmd2', @:)
+
+ call feedkeys(":com DoC\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com DoCmd2', @:)
+
+ delcom DoCmd2
+ call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"delcom DoC', @:)
+
+ call feedkeys(":com DoC\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"com DoC', @:)
+
+ com! -complete=behave DoCmd :
+ call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd mswin xterm', @:)
+
+ " This does not work. Why?
+ "call feedkeys(":DoCmd x\<C-A>\<C-B>\"\<CR>", 'tx')
+ "call assert_equal('"DoCmd xterm', @:)
+
+ com! -complete=custom,CustomComplete DoCmd :
+ call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd January February Mars', @:)
+
+ com! -complete=customlist,CustomCompleteList DoCmd :
+ call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd Monday Tuesday Wednesday', @:)
+
+ com! -complete=custom,CustomCompleteList DoCmd :
+ call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E730:')
+
+ com! -complete=customlist,CustomComp DoCmd :
+ call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E117:')
+endfunc
diff --git a/src/nvim/testdir/test_utf8_comparisons.vim b/src/nvim/testdir/test_utf8_comparisons.vim
new file mode 100644
index 0000000000..576e86142f
--- /dev/null
+++ b/src/nvim/testdir/test_utf8_comparisons.vim
@@ -0,0 +1,95 @@
+" Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c)
+" Also test "g~ap".
+
+if !has("multi_byte")
+ finish
+endif
+
+function! Ch(a, op, b, expected)
+ call assert_equal(eval(printf('"%s" %s "%s"', a:a, a:op, a:b)), a:expected,
+ \ printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected))
+endfunction
+
+function! Chk(a, b, result)
+ if a:result == 0
+ call Ch(a:a, '==?', a:b, 1)
+ call Ch(a:a, '!=?', a:b, 0)
+ call Ch(a:a, '<=?', a:b, 1)
+ call Ch(a:a, '>=?', a:b, 1)
+ call Ch(a:a, '<?', a:b, 0)
+ call Ch(a:a, '>?', a:b, 0)
+ elseif a:result > 0
+ call Ch(a:a, '==?', a:b, 0)
+ call Ch(a:a, '!=?', a:b, 1)
+ call Ch(a:a, '<=?', a:b, 0)
+ call Ch(a:a, '>=?', a:b, 1)
+ call Ch(a:a, '<?', a:b, 0)
+ call Ch(a:a, '>?', a:b, 1)
+ else
+ call Ch(a:a, '==?', a:b, 0)
+ call Ch(a:a, '!=?', a:b, 1)
+ call Ch(a:a, '<=?', a:b, 1)
+ call Ch(a:a, '>=?', a:b, 0)
+ call Ch(a:a, '<?', a:b, 1)
+ call Ch(a:a, '>?', a:b, 0)
+ endif
+endfunction
+
+function! Check(a, b, result)
+ call Chk(a:a, a:b, a:result)
+ call Chk(a:b, a:a, -a:result)
+endfunction
+
+function! LT(a, b)
+ call Check(a:a, a:b, -1)
+endfunction
+
+function! GT(a, b)
+ call Check(a:a, a:b, 1)
+endfunction
+
+function! EQ(a, b)
+ call Check(a:a, a:b, 0)
+endfunction
+
+function Test_comparisons()
+ call EQ('', '')
+ call LT('', 'a')
+ call EQ('abc', 'abc')
+ call EQ('Abc', 'abC')
+ call LT('ab', 'abc')
+ call LT('AB', 'abc')
+ call LT('ab', 'aBc')
+ call EQ('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xb9\xd0\xa6\xd0\xa3\xd0\xba\xd0\x95\xd0\xbd')
+ call LT('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xaf\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd')
+ call EQ('\xe2\x84\xaa', 'k')
+ call LT('\xe2\x84\xaa', 'kkkkkk')
+ call EQ('\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa', 'kkk')
+ call LT('kk', '\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa')
+ call EQ('\xe2\x84\xaa\xe2\x84\xa6k\xe2\x84\xaak\xcf\x89', 'k\xcf\x89\xe2\x84\xaakk\xe2\x84\xa6')
+ call EQ('Abc\x80', 'AbC\x80')
+ call LT('Abc\x80', 'AbC\x81')
+ call LT('Abc', 'AbC\x80')
+ call LT('abc\x80DEF', 'abc\x80def') " case folding stops at the first bad character
+ call LT('\xc3XYZ', '\xc3xyz')
+ call EQ('\xef\xbc\xba', '\xef\xbd\x9a') " FF3A (upper), FF5A (lower)
+ call GT('\xef\xbc\xba', '\xef\xbc\xff') " first string is ok and equals \xef\xbd\x9a after folding, second string is illegal and was left unchanged, then the strings were bytewise compared
+ call LT('\xc3', '\xc3\x83')
+ call EQ('\xc3\xa3xYz', '\xc3\x83XyZ')
+ for n in range(0x60, 0xFF)
+ call LT(printf('xYz\x%.2X', n-1), printf('XyZ\x%.2X', n))
+ endfor
+ for n in range(0x80, 0xBF)
+ call EQ(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n))
+ endfor
+ for n in range(0xC0, 0xFF)
+ call LT(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n))
+ endfor
+endfunction
+
+" test that g~ap changes one paragraph only.
+function Test_gap()
+ new
+ call feedkeys("iabcd\n\ndefggg0g~ap", "tx")
+ call assert_equal(["ABCD", "", "defg"], getline(1,3))
+endfunction
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 4e0f1bbd2f..c449fc91b0 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1215,6 +1215,62 @@ func Test_bitwise_functions()
call assert_fails("call invert({})", 'E728:')
endfunc
+" Test trailing text after :endfunction {{{1
+func Test_endfunction_trailing()
+ call assert_false(exists('*Xtest'))
+
+ exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
+ call assert_true(exists('*Xtest'))
+ call assert_equal('yes', done)
+ delfunc Xtest
+ unlet done
+
+ exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
+ call assert_true(exists('*Xtest'))
+ call assert_equal('yes', done)
+ delfunc Xtest
+ unlet done
+
+ " trailing line break
+ exe "func Xtest()\necho 'hello'\nendfunc\n"
+ call assert_true(exists('*Xtest'))
+ delfunc Xtest
+
+ set verbose=1
+ exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
+ call assert_true(exists('*Xtest'))
+ delfunc Xtest
+
+ call assert_fails("func Xtest()\necho 'hello'\nendfunc garbage", 'E946')
+ call assert_true(exists('*Xtest'))
+ delfunc Xtest
+ set verbose=0
+
+ function Foo()
+ echo 'hello'
+ endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+ delfunc Foo
+endfunc
+
+func Test_delfunction_force()
+ delfunc! Xtest
+ delfunc! Xtest
+ func Xtest()
+ echo 'nothing'
+ endfunc
+ delfunc! Xtest
+ delfunc! Xtest
+endfunc
+
+" Test using bang after user command {{{1
+func Test_user_command_with_bang()
+ command -bang Nieuw let nieuw = 1
+ Ni!
+ call assert_equal(1, nieuw)
+ unlet nieuw
+ delcommand Nieuw
+endfunc
+
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=4 tw=80 fdm=marker
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
new file mode 100644
index 0000000000..2b8849f488
--- /dev/null
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -0,0 +1,43 @@
+" Tests for 'virtualedit'.
+
+func Test_yank_move_change()
+ new
+ call setline(1, [
+ \ "func foo() error {",
+ \ "\tif n, err := bar();",
+ \ "\terr != nil {",
+ \ "\t\treturn err",
+ \ "\t}",
+ \ "\tn = n * n",
+ \ ])
+ set virtualedit=all
+ set ts=4
+ function! MoveSelectionDown(count) abort
+ normal! m`
+ silent! exe "'<,'>move'>+".a:count
+ norm! ``
+ endfunction
+
+ xmap ]e :<C-U>call MoveSelectionDown(v:count1)<CR>
+ 2
+ normal 2gg
+ normal J
+ normal jVj
+ normal ]e
+ normal ce
+ bwipe!
+ set virtualedit=
+ set ts=8
+endfunc
+
+func Test_paste_end_of_line()
+ new
+ set virtualedit=all
+ call setline(1, ['456', '123'])
+ normal! gg0"ay$
+ exe "normal! 2G$lllA\<C-O>:normal! \"agP\r"
+ call assert_equal('123456', getline(2))
+
+ bwipe!
+ set virtualedit=
+endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index cf0e535937..0f2e7e493e 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1,13 +1,14 @@
-" Tests for Visual mode
-if !has('multi_byte')
- finish
-endif
-
+" Tests for various Visual mode.
if !has('visual')
finish
endif
+
func Test_block_shift_multibyte()
+ " Uses double-wide character.
+ if !has('multi_byte')
+ return
+ endif
split
call setline(1, ['xヹxxx', 'ヹxxx'])
exe "normal 1G0l\<C-V>jl>"
@@ -15,3 +16,139 @@ func Test_block_shift_multibyte()
call assert_equal(' ヹxxx', getline(2))
q!
endfunc
+
+func Test_Visual_ctrl_o()
+ new
+ call setline(1, ['one', 'two', 'three'])
+ call cursor(1,2)
+ set noshowmode
+ set tw=0
+ call feedkeys("\<c-v>jjlIa\<c-\>\<c-o>:set tw=88\<cr>\<esc>", 'tx')
+ call assert_equal(['oane', 'tawo', 'tahree'], getline(1, 3))
+ call assert_equal(88, &tw)
+ set tw&
+ bw!
+endfu
+
+func Test_Visual_vapo()
+ new
+ normal oxx
+ normal vapo
+ bwipe!
+endfunc
+
+func Test_dotregister_paste()
+ new
+ exe "norm! ihello world\<esc>"
+ norm! 0ve".p
+ call assert_equal('hello world world', getline(1))
+ q!
+endfunc
+
+func Test_Visual_inner_quote()
+ new
+ normal oxX
+ normal vki'
+ bwipe!
+endfunc
+
+" Test for visual block shift and tab characters.
+func Test_block_shift_tab()
+ enew!
+ call append(0, repeat(['one two three'], 5))
+ call cursor(1,1)
+ exe "normal i\<C-G>u"
+ exe "normal fe\<C-V>4jR\<Esc>ugvr1"
+ call assert_equal('on1 two three', getline(1))
+ call assert_equal('on1 two three', getline(2))
+ call assert_equal('on1 two three', getline(5))
+
+ enew!
+ call append(0, repeat(['abcdefghijklmnopqrstuvwxyz'], 5))
+ call cursor(1,1)
+ exe "normal \<C-V>4jI \<Esc>j<<11|D"
+ exe "normal j7|a\<Tab>\<Tab>"
+ exe "normal j7|a\<Tab>\<Tab> "
+ exe "normal j7|a\<Tab> \<Tab>\<Esc>4k13|\<C-V>4j<"
+ call assert_equal(' abcdefghijklmnopqrstuvwxyz', getline(1))
+ call assert_equal('abcdefghij', getline(2))
+ call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(3))
+ call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(4))
+ call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(5))
+
+ %s/\s\+//g
+ call cursor(1,1)
+ exe "normal \<C-V>4jI \<Esc>j<<"
+ exe "normal j7|a\<Tab>\<Tab>"
+ exe "normal j7|a\<Tab>\<Tab>\<Tab>\<Tab>\<Tab>"
+ exe "normal j7|a\<Tab> \<Tab>\<Tab>\<Esc>4k13|\<C-V>4j3<"
+ call assert_equal(' abcdefghijklmnopqrstuvwxyz', getline(1))
+ call assert_equal('abcdefghij', getline(2))
+ call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(3))
+ call assert_equal(" abc\<Tab>\<Tab>defghijklmnopqrstuvwxyz", getline(4))
+ call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(5))
+
+ enew!
+endfunc
+
+" Tests Blockwise Visual when there are TABs before the text.
+func Test_blockwise_visual()
+ enew!
+ call append(0, ['123456',
+ \ '234567',
+ \ '345678',
+ \ '',
+ \ 'test text test tex start here',
+ \ "\t\tsome text",
+ \ "\t\ttest text",
+ \ 'test text'])
+ call cursor(1,1)
+ exe "normal /start here$\<CR>"
+ exe 'normal "by$' . "\<C-V>jjlld"
+ exe "normal /456$\<CR>"
+ exe "normal \<C-V>jj" . '"bP'
+ call assert_equal(['123start here56',
+ \ '234start here67',
+ \ '345start here78',
+ \ '',
+ \ 'test text test tex rt here',
+ \ "\t\tsomext",
+ \ "\t\ttesext"], getline(1, 7))
+
+ enew!
+endfunc
+
+" Test Virtual replace mode.
+func Test_virtual_replace()
+ throw 'skipped: TODO: '
+ exe "set t_kD=\<C-V>x7f t_kb=\<C-V>x08"
+ enew!
+ exe "normal a\nabcdefghi\njk\tlmn\n opq rst\n\<C-D>uvwxyz"
+ call cursor(1,1)
+ set ai bs=2
+ exe "normal gR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR"
+ call assert_equal([' 1',
+ \ ' A',
+ \ ' BCDEFGHIJ',
+ \ ' KL',
+ \ ' MNO',
+ \ ' PQR',
+ \ ], getline(1, 6))
+ normal G
+ mark a
+ inoremap <C-D> <Del>
+ exe "normal o0\<C-D>\nabcdefghi\njk\tlmn\n opq\trst\n\<C-D>uvwxyz\n"
+ exe "normal 'ajgR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" . repeat("\<BS>", 29)
+ call assert_equal([' 1',
+ \ 'abcdefghi',
+ \ 'jk lmn',
+ \ ' opq rst',
+ \ 'uvwxyz'], getline(7, 11))
+ normal G
+ exe "normal iab\tcdefghi\tjkl"
+ exe "normal 0gRAB......CDEFGHI.J\<Esc>o"
+ exe "normal iabcdefghijklmnopqrst\<Esc>0gRAB\tIJKLMNO\tQR"
+ call assert_equal(['AB......CDEFGHI.Jkl',
+ \ 'AB IJKLMNO QRst'], getline(12, 13))
+ enew!
+endfunc
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
new file mode 100644
index 0000000000..ed64dd79b7
--- /dev/null
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -0,0 +1,124 @@
+" Test for commands that close windows and/or buffers:
+" :quit
+" :close
+" :hide
+" :only
+" :sall
+" :all
+" :ball
+" :buf
+" :edit
+"
+func Test_winbuf_close()
+ enew | only
+
+ call writefile(['testtext 1'], 'Xtest1')
+ call writefile(['testtext 2'], 'Xtest2')
+ call writefile(['testtext 3'], 'Xtest3')
+
+ next! Xtest1 Xtest2
+ call setline(1, 'testtext 1 1')
+
+ " test for working :n when hidden set
+ set hidden
+ next
+ call assert_equal('Xtest2', bufname('%'))
+
+ " test for failing :rew when hidden not set
+ set nohidden
+ call setline(1, 'testtext 2 2')
+ call assert_fails('rewind', 'E37')
+ call assert_equal('Xtest2', bufname('%'))
+ call assert_equal('testtext 2 2', getline(1))
+
+ " test for working :rew when hidden set
+ set hidden
+ rewind
+ call assert_equal('Xtest1', bufname('%'))
+ call assert_equal('testtext 1 1', getline(1))
+
+ " test for :all keeping a buffer when it's modified
+ set nohidden
+ call setline(1, 'testtext 1 1 1')
+ split
+ next Xtest2 Xtest3
+ all
+ 1wincmd w
+ call assert_equal('Xtest1', bufname('%'))
+ call assert_equal('testtext 1 1 1', getline(1))
+
+ " test abandoning changed buffer, should be unloaded even when 'hidden' set
+ set hidden
+ call setline(1, 'testtext 1 1 1 1')
+ quit!
+ call assert_equal('Xtest2', bufname('%'))
+ call assert_equal('testtext 2 2', getline(1))
+ unhide
+ call assert_equal('Xtest2', bufname('%'))
+ call assert_equal('testtext 2 2', getline(1))
+
+ " test ":hide" hides anyway when 'hidden' not set
+ set nohidden
+ call setline(1, 'testtext 2 2 2')
+ hide
+ call assert_equal('Xtest3', bufname('%'))
+ call assert_equal('testtext 3', getline(1))
+
+ " test ":edit" failing in modified buffer when 'hidden' not set
+ call setline(1, 'testtext 3 3')
+ call assert_fails('edit Xtest1', 'E37')
+ call assert_equal('Xtest3', bufname('%'))
+ call assert_equal('testtext 3 3', getline(1))
+
+ " test ":edit" working in modified buffer when 'hidden' set
+ set hidden
+ edit Xtest1
+ call assert_equal('Xtest1', bufname('%'))
+ call assert_equal('testtext 1', getline(1))
+
+ " test ":close" not hiding when 'hidden' not set in modified buffer
+ split Xtest3
+ set nohidden
+ call setline(1, 'testtext 3 3 3')
+ call assert_fails('close', 'E37')
+ call assert_equal('Xtest3', bufname('%'))
+ call assert_equal('testtext 3 3 3', getline(1))
+
+ " test ":close!" does hide when 'hidden' not set in modified buffer;
+ call setline(1, 'testtext 3 3 3 3')
+ close!
+ call assert_equal('Xtest1', bufname('%'))
+ call assert_equal('testtext 1', getline(1))
+
+ set nohidden
+
+ " test ":all!" hides changed buffer
+ split Xtest4
+ call setline(1, 'testtext 4')
+ all!
+ 1wincmd w
+ call assert_equal('Xtest2', bufname('%'))
+ call assert_equal('testtext 2 2 2', getline(1))
+
+ " test ":q!" and hidden buffer.
+ bwipe! Xtest1 Xtest2 Xtest3 Xtest4
+ split Xtest1
+ wincmd w
+ bwipe!
+ set modified
+ bot split Xtest2
+ set modified
+ bot split Xtest3
+ set modified
+ wincmd t
+ hide
+ call assert_equal('Xtest2', bufname('%'))
+ quit!
+ call assert_equal('Xtest3', bufname('%'))
+ call assert_fails('silent! quit!', 'E162')
+ call assert_equal('Xtest1', bufname('%'))
+
+ call delete('Xtest1')
+ call delete('Xtest2')
+ call delete('Xtest3')
+endfunc
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 188a7ed0f3..139d29a48b 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -306,20 +306,14 @@ func Test_window_width()
set winfixwidth
vsplit Xc
let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)]
- " FIXME: commented out: I would expect the width of 2nd window to
- " remain 2 but it's actually 1?!
- "call assert_equal(2, winwidth(2))
+ call assert_equal(2, winwidth(2))
call assert_inrange(ww3, ww3 + 1, ww1)
3wincmd >
- " FIXME: commented out: I would expect the width of 2nd window to
- " remain 2 but it's actually 1?!
- "call assert_equal(2, winwidth(2))
+ call assert_equal(2, winwidth(2))
call assert_equal(ww1 + 3, winwidth(1))
call assert_equal(ww3 - 3, winwidth(3))
wincmd =
- " FIXME: commented out: I would expect the width of 2nd window to
- " remain 2 but it's actually 1?!
- "call assert_equal(2, winwidth(2))
+ call assert_equal(2, winwidth(2))
call assert_equal(ww1, winwidth(1))
call assert_equal(ww3, winwidth(3))
@@ -336,6 +330,50 @@ func Test_window_width()
bw Xa Xb Xc
endfunc
+func Test_equalalways_on_close()
+ set equalalways
+ vsplit
+ windo split
+ split
+ wincmd J
+ " now we have a frame top-left with two windows, a frame top-right with two
+ " windows and a frame at the bottom, full-width.
+ let height_1 = winheight(1)
+ let height_2 = winheight(2)
+ let height_3 = winheight(3)
+ let height_4 = winheight(4)
+ " closing the bottom window causes all windows to be resized.
+ close
+ call assert_notequal(height_1, winheight(1))
+ call assert_notequal(height_2, winheight(2))
+ call assert_notequal(height_3, winheight(3))
+ call assert_notequal(height_4, winheight(4))
+ call assert_equal(winheight(1), winheight(3))
+ call assert_equal(winheight(2), winheight(4))
+
+ 1wincmd w
+ split
+ 4wincmd w
+ resize + 5
+ " left column has three windows, equalized heights.
+ " right column has two windows, top one a bit higher
+ let height_1 = winheight(1)
+ let height_2 = winheight(2)
+ let height_4 = winheight(4)
+ let height_5 = winheight(5)
+ 3wincmd w
+ " closing window in left column equalizes heights in left column but not in
+ " the right column
+ close
+ call assert_notequal(height_1, winheight(1))
+ call assert_notequal(height_2, winheight(2))
+ call assert_equal(height_4, winheight(3))
+ call assert_equal(height_5, winheight(4))
+
+ only
+ set equalalways&
+endfunc
+
func Test_window_jump_tag()
help
/iccf
diff --git a/src/nvim/testdir/unix.vim b/src/nvim/testdir/unix.vim
index a7daacf8cf..ce2beff7fe 100644
--- a/src/nvim/testdir/unix.vim
+++ b/src/nvim/testdir/unix.vim
@@ -2,6 +2,12 @@
" Always use "sh", don't use the value of "$SHELL".
set shell=sh
+if has('win32')
+ set shellcmdflag=-c shellxquote= shellxescape= shellquote=
+ let &shellredir = '>%s 2>&1'
+ set shellslash
+endif
+
" Don't depend on system locale, always use utf-8
set encoding=utf-8
diff --git a/src/nvim/testdir/view_util.vim b/src/nvim/testdir/view_util.vim
new file mode 100644
index 0000000000..eb92630761
--- /dev/null
+++ b/src/nvim/testdir/view_util.vim
@@ -0,0 +1,30 @@
+" Functions about view shared by several tests
+
+" ScreenLines(lnum, width) or
+" ScreenLines([start, end], width)
+function! ScreenLines(lnum, width) abort
+ redraw!
+ if type(a:lnum) == v:t_list
+ let start = a:lnum[0]
+ let end = a:lnum[1]
+ else
+ let start = a:lnum
+ let end = a:lnum
+ endif
+ let lines = []
+ for l in range(start, end)
+ let lines += [join(map(range(1, a:width), 'nr2char(screenchar(l, v:val))'), '')]
+ endfor
+ return lines
+endfunction
+
+function! NewWindow(height, width) abort
+ exe a:height . 'new'
+ exe a:width . 'vsp'
+ redraw!
+endfunction
+
+function! CloseWindow() abort
+ bw!
+ redraw!
+endfunction
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 03587d68f0..b04a6ce4f9 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -8,13 +8,12 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/main.h"
+#include "nvim/aucmd.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/event/rstream.h"
#define PASTETOGGLE_KEY "<Paste>"
-#define FOCUSGAINED_KEY "<FocusGained>"
-#define FOCUSLOST_KEY "<FocusLost>"
#define KEY_BUFFER_SIZE 0xfff
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -48,9 +47,11 @@ void term_input_init(TermInput *input, Loop *loop)
termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
// setup input handle
#ifdef WIN32
- uv_tty_init(loop, &input->tty_in, 0, 1);
+ uv_tty_init(&loop->uv, &input->tty_in, 0, 1);
uv_tty_set_mode(&input->tty_in, UV_TTY_MODE_RAW);
- rstream_init_stream(&input->read_stream, &input->tty_in, 0xfff);
+ rstream_init_stream(&input->read_stream,
+ (uv_stream_t *)&input->tty_in,
+ 0xfff);
#else
rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff);
#endif
@@ -200,18 +201,25 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right");
}
- if (ev == TERMKEY_MOUSE_PRESS) {
- if (button == 4) {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp");
- } else if (button == 5) {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown");
- } else {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse");
- }
- } else if (ev == TERMKEY_MOUSE_DRAG) {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag");
- } else if (ev == TERMKEY_MOUSE_RELEASE) {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release");
+ switch (ev) {
+ case TERMKEY_MOUSE_PRESS:
+ if (button == 4) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp");
+ } else if (button == 5) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len,
+ "ScrollWheelDown");
+ } else {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse");
+ }
+ break;
+ case TERMKEY_MOUSE_DRAG:
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag");
+ break;
+ case TERMKEY_MOUSE_RELEASE:
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release");
+ break;
+ case TERMKEY_MOUSE_UNKNOWN:
+ assert(false);
}
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
@@ -280,9 +288,9 @@ static void timer_cb(TimeWatcher *watcher, void *data)
/// Handle focus events.
///
-/// If the upcoming sequence of bytes in the input stream matches either the
-/// escape code for focus gained `<ESC>[I` or focus lost `<ESC>[O` then consume
-/// that sequence and push the appropriate event into the input queue
+/// If the upcoming sequence of bytes in the input stream matches the termcode
+/// for "focus gained" or "focus lost", consume that sequence and schedule an
+/// event on the main loop.
///
/// @param input the input stream
/// @return true iff handle_focus_event consumed some input
@@ -294,11 +302,7 @@ static bool handle_focus_event(TermInput *input)
// Advance past the sequence
bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
rbuffer_consumed(input->read_stream.buffer, 3);
- if (focus_gained) {
- enqueue_input(input, FOCUSGAINED_KEY, sizeof(FOCUSGAINED_KEY) - 1);
- } else {
- enqueue_input(input, FOCUSLOST_KEY, sizeof(FOCUSLOST_KEY) - 1);
- }
+ aucmd_schedule_focusgained(focus_gained);
return true;
}
return false;
diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c
new file mode 100644
index 0000000000..2f07e83158
--- /dev/null
+++ b/src/nvim/tui/terminfo.c
@@ -0,0 +1,255 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// Built-in fallback terminfo entries.
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <unibilium.h>
+
+#include "nvim/log.h"
+#include "nvim/globals.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
+#include "nvim/tui/terminfo.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "tui/terminfo.c.generated.h"
+#endif
+
+// One creates the dumps from terminfo.src by using
+// od -t d1 -w | cut -c9- | sed -e 's/\>/,/g'
+// on the compiled files.
+
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+// This is a 256-colour terminfo description that lacks true-colour and
+// DECSTBM/DECSLRM/DECLRMM capabilities that xterm actually has.
+static const signed char xterm_256colour_terminfo[] = {
+ 26, 1, 37, 0, 29, 0, 15, 0, 105, 1, -42, 5, 120, 116, 101, 114, 109, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 120, 116, 101, 114, 109, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, -1, -1, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 102, 0, -1, -1, 106, 0, 110, 0, 120, 0, 124, 0, -1, -1, -1, -1,-128, 0,-124, 0,-119, 0,-114, 0, -1, -1,-105, 0,-100, 0, -95, 0, -1, -1, -90, 0, -85, 0, -80, 0, -75, 0, -66, 0, -62, 0, -55, 0, -1, -1, -46, 0, -41, 0, -35, 0, -29, 0, -1, -1, -1, -1, -1, -1, -11, 0, -1, -1, -1, -1, -1, -1, 7, 1, -1, -1, 11, 1, -1, -1, -1, -1, -1, -1, 13, 1, -1, -1, 18, 1, -1, -1, -1, -1, -1, -1, -1, -1, 22, 1, 26, 1, 32, 1, 36, 1, 40, 1, 44, 1, 50, 1, 56, 1, 62, 1, 68, 1, 74, 1, 78, 1, -1, -1, 83, 1, -1, -1, 87, 1, 92, 1, 97, 1, 101, 1, 108, 1, -1, -1, 115, 1, 119, 1, 127, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-121, 1,-112, 1, -1, -1, -1, -1,-103, 1, -94, 1, -85, 1, -76, 1, -67, 1, -58, 1, -49, 1, -40, 1, -31, 1, -22, 1, -1, -1, -1, -1, -1, -1, -13, 1, -9, 1, -4, 1, -1, -1, 1, 2, 10, 2, -1, -1, -1, -1, 28, 2, 31, 2, 42, 2, 45, 2, 47, 2, 50, 2,-113, 2, -1, -1,-110, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-108, 2, -1, -1, -1, -1, -1, -1, -1, -1,-104, 2, -1, -1, -51, 2, -1, -1, -1, -1, -47, 2, -41, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -35, 2, -31, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -27, 2, -1, -1, -1, -1, -20, 2, -1, -1, -1, -1, -1, -1, -1, -1, -13, 2, -6, 2, 1, 3, -1, -1, -1, -1, 8, 3, -1, -1, 15, 3, -1, -1, -1, -1, -1, -1, 22, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 29, 3, 35, 3, 41, 3, 48, 3, 55, 3, 62, 3, 69, 3, 77, 3, 85, 3, 93, 3, 101, 3, 109, 3, 117, 3, 125, 3,-123, 3,-116, 3,-109, 3,-102, 3, -95, 3, -87, 3, -79, 3, -71, 3, -63, 3, -55, 3, -47, 3, -39, 3, -31, 3, -24, 3, -17, 3, -10, 3, -3, 3, 5, 4, 13, 4, 21, 4, 29, 4, 37, 4, 45, 4, 53, 4, 61, 4, 68, 4, 75, 4, 82, 4, 89, 4, 97, 4, 105, 4, 113, 4, 121, 4,-127, 4,-119, 4,-111, 4,-103, 4, -96, 4, -89, 4, -82, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -77, 4, -66, 4, -61, 4, -42, 4, -38, 4, -29, 4, -22, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 83, 5, -1, -1, -1, -1, -1, -1, 87, 5,-106, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 49, 50, 108, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 49, 50, 59, 50, 53, 104, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 40, 48, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 40, 66, 0, 27, 40, 66, 27, 91, 109, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 33, 112, 27, 91, 63, 51, 59, 52, 108, 27, 91, 52, 108, 27, 62, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 63, 49, 48, 51, 52, 108, 0, 27, 91, 63, 49, 48, 51, 52, 104, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 91, 105, 0, 27, 91, 52, 105, 0, 27, 91, 53, 105, 0, 27, 99, 27, 93, 49, 48, 52, 7, 0, 27, 91, 33, 112, 27, 91, 63, 51, 59, 52, 108, 27, 91, 52, 108, 27, 62, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 37, 63, 37, 112, 57, 37, 116, 27, 40, 48, 37, 101, 27, 40, 66, 37, 59, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 109, 0, 27, 72, 0, 9, 0, 27, 79, 69, 0, 96, 96, 97, 97, 102, 102, 103, 103, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 79, 70, 0, 27, 79, 77, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 59, 50, 82, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 59, 53, 82, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 59, 54, 82, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 59, 51, 82, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 59, 52, 82, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0
+};
+// Taken from unibilium/t/static_tmux.c as of 2015-08-14.
+// This is an 256-colour terminfo description that lacks
+// status line capabilities that tmux actually has.
+static const signed char tmux_256colour_terminfo[] = {
+ 26, 1, 56, 0, 15, 0, 15, 0, 105, 1, -48, 2, 116, 109, 117, 120, 124, 86, 84, 32, 49, 48, 48, 47, 65, 78, 83, 73, 32, 88, 51, 46, 54, 52, 32, 118, 105, 114, 116, 117, 97, 108, 32, 116, 101, 114, 109, 105, 110, 97, 108, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 37, 0, 41, 0, -1, -1, -1, -1, 45, 0, 62, 0, 64, 0, 68, 0, 75, 0, -1, -1, 77, 0, 89, 0, -1, -1, 93, 0, 96, 0, 102, 0, 106, 0, -1, -1, -1, -1, 110, 0, 112, 0, 117, 0, 122, 0, -1, -1, -1, -1, 123, 0, -1, -1, -1, -1, -128, 0, -123, 0, -118, 0, -1, -1, -113, 0, -111, 0, -106, 0, -1, -1, -105, 0, -100, 0, -94, 0, -88, 0, -1, -1, -1, -1, -1, -1, -85, 0, -1, -1, -1, -1, -1, -1, -81, 0, -1, -1, -77, 0, -1, -1, -1, -1, -1, -1, -75, 0, -1, -1, -70, 0, -1, -1, -1, -1, -1, -1, -1, -1, -66, 0, -62, 0, -56, 0, -52, 0, -48, 0, -44, 0, -38, 0, -32, 0, -26, 0, -20, 0, -14, 0, -9, 0, -1, -1, -4, 0, -1, -1, 0, 1, 5, 1, 10, 1, -1, -1, -1, -1, -1, -1, 14, 1, 18, 1, 26, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 34, 1, -1, -1, 37, 1, 46, 1, 55, 1, 64, 1, -1, -1, 73, 1, 82, 1, 91, 1, -1, -1, 100, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 109, 1, -1, -1, -1, -1, 126, 1, -1, -1, -127, 1, -124, 1, -122, 1, -119, 1, -46, 1, -1, -1, -43, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -41, 1, -1, -1, 24, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 40, 2, 46, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 57, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 66, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 71, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, 2, -1, -1, -1, -1, -1, -1, 81, 2, -112, 2, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 51, 52, 104, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 77, 0, 27, 91, 51, 52, 108, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 103, 0, 27, 41, 48, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 69, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 49, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 52, 126, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0
+};
+// Taken from unibilium/t/static_screen-256color.c as of 2015-08-14.
+// This is an 256-colour terminfo description that lacks
+// status line capabilities that screen actually has.
+static const signed char screen_256colour_terminfo[] = {
+ 26, 1, 43, 0, 43, 0, 15, 0, 105, 1, -43, 2, 115, 99, 114, 101, 101, 110, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 71, 78, 85, 32, 83, 99, 114, 101, 101, 110, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 37, 0, 41, 0, -1, -1, -1, -1, 45, 0, 62, 0, 64, 0, 68, 0, 75, 0, -1, -1, 77, 0, 89, 0, -1, -1, 93, 0, 96, 0, 102, 0, 106, 0, -1, -1, -1, -1, 110, 0, 112, 0, 117, 0, 122, 0, -1, -1, -1, -1, -125, 0, -1, -1, -1, -1, -120, 0, -115, 0, -110, 0, -1, -1, -105, 0, -103, 0, -98, 0, -1, -1, -89, 0, -84, 0, -78, 0, -72, 0, -1, -1, -1, -1, -1, -1, -69, 0, -1, -1, -1, -1, -1, -1, -65, 0, -1, -1, -61, 0, -1, -1, -1, -1, -1, -1, -59, 0, -1, -1, -54, 0, -1, -1, -1, -1, -1, -1, -1, -1, -50, 0, -46, 0, -40, 0, -36, 0, -32, 0, -28, 0, -22, 0, -16, 0, -10, 0, -4, 0, 2, 1, 7, 1, -1, -1, 12, 1, -1, -1, 16, 1, 21, 1, 26, 1, -1, -1, -1, -1, -1, -1, 30, 1, 34, 1, 42, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 50, 1, -1, -1, 53, 1, 62, 1, 71, 1, 80, 1, -1, -1, 89, 1, 98, 1, 107, 1, -1, -1, 116, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 125, 1, -1, -1, -1, -1, -114, 1, -1, -1, -111, 1, -108, 1, -106, 1, -103, 1, -30, 1, -1, -1, -27, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -25, 1, -1, -1, 40, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, 2, 62, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 73, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, 2, -1, -1, -1, -1, -1, -1, 86, 2, -107, 2, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 51, 52, 104, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 77, 0, 27, 91, 51, 52, 108, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 51, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 51, 109, 0, 27, 91, 50, 52, 109, 0, 27, 103, 0, 27, 41, 48, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 69, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 49, 37, 116, 59, 51, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 52, 126, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 0, 3, 0, 1, 0, 24, 0, 52, 0, -112, 0, 1, 1, 0, 0, 1, 0, 0, 0, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 3, 0, 6, 0, 9, 0, 12, 0, 15, 0, 18, 0, 23, 0, 28, 0, 32, 0, 37, 0, 43, 0, 49, 0, 55, 0, 61, 0, 66, 0, 71, 0, 77, 0, 83, 0, 89, 0, 95, 0, 101, 0, 107, 0, 111, 0, 116, 0, 120, 0, 124, 0, -128, 0, 27, 40, 66, 0, 27, 40, 37, 112, 49, 37, 99, 0, 65, 88, 0, 71, 48, 0, 88, 84, 0, 85, 56, 0, 69, 48, 0, 83, 48, 0, 107, 68, 67, 53, 0, 107, 68, 67, 54, 0, 107, 68, 78, 0, 107, 68, 78, 53, 0, 107, 69, 78, 68, 53, 0, 107, 69, 78, 68, 54, 0, 107, 72, 79, 77, 53, 0, 107, 72, 79, 77, 54, 0, 107, 73, 67, 53, 0, 107, 73, 67, 54, 0, 107, 76, 70, 84, 53, 0, 107, 78, 88, 84, 53, 0, 107, 78, 88, 84, 54, 0, 107, 80, 82, 86, 53, 0, 107, 80, 82, 86, 54, 0, 107, 82, 73, 84, 53, 0, 107, 85, 80, 0, 107, 85, 80, 53, 0, 107, 97, 50, 0, 107, 98, 49, 0, 107, 98, 51, 0, 107, 99, 50, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+static const signed char iterm_256colour_terminfo[] = {
+ 26, 1, 57, 0, 29, 0, 15, 0, 105, 1, 73, 3, 105, 84, 101, 114, 109, 46, 97, 112, 112, 124, 105, 116, 101, 114, 109, 124, 105, 84, 101, 114, 109, 46, 97, 112, 112, 32, 116, 101, 114, 109, 105, 110, 97, 108, 32, 101, 109, 117, 108, 97, 116, 111, 114, 32, 102, 111, 114, 32, 77, 97, 99, 32, 79, 83, 32, 88, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, 50, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, -1, -1, 0, 0, 2, 0, -2, -1, 4, 0, 9, 0, 16, 0, 20, 0, 24, 0, -1, -1, 35, 0, 52, 0, 54, 0, 58, 0, 65, 0, -1, -1, 67, 0, 74, 0, -1, -1, 78, 0, -1, -1, 82, 0, 86, 0, 90, 0, -1, -1, 96, 0, 98, 0, 103, 0, 108, 0, -1, -1, -2, -1, 117, 0, 122, 0, -1, -1, 127, 0,-124, 0,-119, 0, -1, -1,-114, 0,-112, 0,-107, 0, -1, -1, -94, 0, -89, 0, -85, 0, -81, 0, -1, -1, -63, 0, -1, -1, -1, -1, -1, -1, -1, -1, -61, 0, -57, 0, -1, -1, -53, 0, -1, -1, -1, -1, -1, -1, -51, 0, -1, -1, -46, 0, -1, -1, -1, -1, -1, -1, -1, -1, -42, 0, -38, 0, -32, 0, -28, 0, -24, 0, -20, 0, -14, 0, -8, 0, -2, 0, 4, 1, 10, 1, -1, -1, -1, -1, 14, 1, -1, -1, 18, 1, 23, 1, 28, 1, -1, -1, -1, -1, -1, -1, 32, 1, 36, 1, 44, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 1, 61, 1, 70, 1, 79, 1, -1, -1, 88, 1, 97, 1, 106, 1, -1, -1, 115, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 1, -1, -1, -1, -1,-104, 1,-101, 1, -90, 1, -87, 1, -85, 1, -82, 1, -4, 1, -1, -1, -1, 1, 1, 2, -1, -1, -1, -1, -1, -1, 6, 2, 10, 2, 14, 2, 18, 2, 22, 2, -1, -1, -1, -1, 26, 2, -1, -1, -1, -1, -1, -1, -1, -1, 77, 2, 83, 2, -1, -1, -1, -1, 89, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 96, 2, 100, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 104, 2, 110, 2, 116, 2, 122, 2,-128, 2,-122, 2,-116, 2,-110, 2, 104, 2, -98, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -92, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -87, 2, -76, 2, -71, 2, -63, 2, -59, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -54, 2, 9, 3, 7, 0, 13, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 93, 50, 59, 7, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 55, 27, 91, 63, 52, 55, 104, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 27, 56, 0, 27, 91, 52, 108, 0, 27, 91, 109, 0, 27, 91, 109, 0, 27, 91, 63, 53, 104, 36, 60, 50, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 91, 64, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 62, 27, 91, 63, 51, 108, 27, 91, 63, 52, 108, 27, 91, 63, 53, 108, 27, 91, 63, 55, 104, 27, 91, 63, 56, 104, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 93, 50, 59, 0, 27, 79, 113, 0, 27, 79, 115, 0, 27, 79, 114, 0, 27, 79, 112, 0, 27, 79, 110, 0, 96, 96, 97, 97, 102, 102, 103, 103, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 40, 66, 27, 41, 48, 0, 27, 79, 70, 0, 27, 79, 77, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 50, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 49, 59, 50, 99, 0, 27, 91, 99, 0, 27, 91, 48, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+// This is a 256-colour terminfo description that lacks true-colour
+// capabilities that rxvt actually has.
+static const signed char rxvt_256colour_terminfo[] = {
+ 26, 1, 47, 0, 29, 0, 15, 0, 110, 1, -31, 4, 114, 120, 118, 116, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 114, 120, 118, 116, 32, 50, 46, 55, 46, 57, 32, 119, 105, 116, 104, 32, 120, 116, 101, 114, 109, 32, 50, 53, 54, 45, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, -1, -1, 0, 0, 2, 0, 4, 0, 21, 0, 26, 0, 34, 0, 38, 0, 42, 0, -1, -1, 53, 0, 70, 0, 72, 0, 76, 0, 83, 0, -1, -1, 85, 0, 92, 0, -1, -1, 96, 0, -1, -1, -1, -1, 100, 0, -1, -1, -1, -1, 104, 0, 106, 0, 111, 0, 116, 0, -1, -1, -1, -1, 125, 0, -1, -1, -1, -1,-126, 0,-121, 0,-116, 0, -1, -1,-111, 0,-109, 0,-104, 0, -1, -1, -91, 0, -86, 0, -80, 0, -74, 0, -1, -1, -1, -1, -56, 0, -42, 0, -1, -1, -1, -1, -8, 0, -4, 0, -1, -1, 0, 1, -1, -1, -1, -1, -1, -1, 2, 1, -1, -1, 7, 1, -1, -1, 11, 1, -1, -1, 16, 1, 22, 1, 28, 1, 34, 1, 40, 1, 46, 1, 52, 1, 58, 1, 64, 1, 70, 1, 76, 1, 82, 1, 87, 1, -1, -1, 92, 1, -1, -1, 96, 1, 101, 1, 106, 1, 110, 1, 114, 1, -1, -1, 118, 1, 122, 1, 125, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-128, 1,-119, 1,-110, 1, -1, -1,-101, 1, -92, 1, -83, 1, -1, -1, -74, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -65, 1, -32, 1, -1, -1, -1, -1, 18, 2, 21, 2, 32, 2, 35, 2, 37, 2, 40, 2, 107, 2, -1, -1, 110, 2, -1, -1, -1, -1, -1, -1, -1, -1, 112, 2, 116, 2, 120, 2, 124, 2,-128, 2, -1, -1, -1, -1,-124, 2, -1, -1, -73, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -69, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -62, 2, -57, 2, -1, -1, -53, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -48, 2, -1, -1, -43, 2, -38, 2, -1, -1, -1, -1, -1, -1, -1, -1, -33, 2, -28, 2, -23, 2, -1, -1, -1, -1, -19, 2, -1, -1, -14, 2, -1, -1, -1, -1, -1, -1, -9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -5, 2, 1, 3, 7, 3, 13, 3, 19, 3, 25, 3, 31, 3, 37, 3, 43, 3, 49, 3, 55, 3, 61, 3, 67, 3, 73, 3, 79, 3, 85, 3, 91, 3, 97, 3, 103, 3, 109, 3, 115, 3, 121, 3, 127, 3,-123, 3,-117, 3,-111, 3,-105, 3, -99, 3, -93, 3, -87, 3, -81, 3, -75, 3, -69, 3, -63, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -57, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -52, 3, -41, 3, -36, 3, -28, 3, -24, 3, -15, 3, -8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 86, 4, -1, -1, -1, -1, -1, -1, 90, 4,-103, 4, -1, -1, -1, -1, -1, -1, -39, 4, -35, 4, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 55, 27, 91, 63, 52, 55, 104, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 27, 56, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 63, 52, 55, 108, 27, 61, 27, 91, 63, 49, 108, 0, 27, 91, 114, 27, 91, 109, 27, 91, 50, 74, 27, 91, 72, 27, 91, 63, 55, 104, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 91, 52, 108, 0, 27, 91, 64, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 91, 66, 0, 27, 91, 56, 94, 0, 27, 91, 50, 49, 126, 0, 27, 91, 49, 49, 126, 0, 27, 91, 50, 49, 126, 0, 27, 91, 49, 50, 126, 0, 27, 91, 49, 51, 126, 0, 27, 91, 49, 52, 126, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 55, 126, 0, 27, 91, 50, 126, 0, 27, 91, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 91, 67, 0, 27, 91, 97, 0, 27, 91, 98, 0, 27, 91, 65, 0, 27, 62, 0, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 62, 27, 91, 49, 59, 51, 59, 52, 59, 53, 59, 54, 108, 27, 91, 63, 55, 104, 27, 91, 109, 27, 91, 114, 27, 91, 50, 74, 27, 91, 72, 0, 27, 91, 114, 27, 91, 109, 27, 91, 50, 74, 27, 91, 72, 27, 91, 63, 55, 104, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 79, 119, 0, 27, 79, 121, 0, 27, 79, 117, 0, 27, 79, 113, 0, 27, 79, 115, 0, 96, 96, 97, 97, 102, 102, 103, 103, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 56, 126, 0, 27, 79, 77, 0, 27, 91, 49, 126, 0, 27, 91, 51, 36, 0, 27, 91, 52, 126, 0, 27, 91, 56, 36, 0, 27, 91, 55, 36, 0, 27, 91, 50, 36, 0, 27, 91, 100, 0, 27, 91, 54, 36, 0, 27, 91, 53, 36, 0, 27, 91, 99, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 51, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 50, 51, 36, 0, 27, 91, 50, 52, 36, 0, 27, 91, 49, 49, 94, 0, 27, 91, 49, 50, 94, 0, 27, 91, 49, 51, 94, 0, 27, 91, 49, 52, 94, 0, 27, 91, 49, 53, 94, 0, 27, 91, 49, 55, 94, 0, 27, 91, 49, 56, 94, 0, 27, 91, 49, 57, 94, 0, 27, 91, 50, 48, 94, 0, 27, 91, 50, 49, 94, 0, 27, 91, 50, 51, 94, 0, 27, 91, 50, 52, 94, 0, 27, 91, 50, 53, 94, 0, 27, 91, 50, 54, 94, 0, 27, 91, 50, 56, 94, 0, 27, 91, 50, 57, 94, 0, 27, 91, 51, 49, 94, 0, 27, 91, 51, 50, 94, 0, 27, 91, 51, 51, 94, 0, 27, 91, 51, 52, 94, 0, 27, 91, 50, 51, 64, 0, 27, 91, 50, 52, 64, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 49, 59, 50, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 40, 66, 0, 27, 40, 48, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+// This is a 16-colour terminfo description that lacks true-colour
+// and 256-colour capabilities that linux (4.8+) actually has.
+static const signed char linux_16colour_terminfo[] = {
+ 26, 1, 43, 0, 29, 0, 16, 0, 125, 1, 125, 3, 108, 105, 110, 117, 120, 45, 49, 54, 99, 111, 108, 111, 114, 124, 108, 105, 110, 117, 120, 32, 99, 111, 110, 115, 111, 108, 101, 32, 119, 105, 116, 104, 32, 49, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, -1, -1, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 0, 0, 1, 42, 0, -1, -1, 0, 0, 2, 0, 4, 0, 21, 0, 26, 0, 33, 0, 37, 0, 41, 0, -1, -1, 52, 0, 69, 0, 71, 0, 75, 0, 87, 0, -1, -1, 89, 0, 101, 0, -1, -1, 105, 0, 109, 0, 121, 0, 125, 0, -1, -1, -1, -1,-127, 0,-125, 0,-120, 0, -1, -1, -1, -1,-115, 0,-110, 0, -1, -1, -1, -1,-105, 0,-100, 0, -95, 0, -90, 0, -81, 0, -79, 0, -1, -1, -1, -1, -74, 0, -69, 0, -63, 0, -57, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -39, 0, -35, 0, -1, -1, -31, 0, -1, -1, -1, -1, -1, -1, -29, 0, -1, -1, -24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -20, 0, -15, 0, -9, 0, -4, 0, 1, 1, 6, 1, 11, 1, 17, 1, 23, 1, 29, 1, 35, 1, 40, 1, -1, -1, 45, 1, -1, -1, 49, 1, 54, 1, 59, 1, -1, -1, -1, -1, -1, -1, 63, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 67, 1, -1, -1, 70, 1, 79, 1, 88, 1, 97, 1, -1, -1, 106, 1, 115, 1, 124, 1, -1, -1,-123, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-114, 1, -1, -1, -1, -1, -1, -1,-108, 1,-105, 1, -94, 1, -91, 1, -89, 1, -86, 1, 1, 2, -1, -1, 4, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, 10, 2, -1, -1, 77, 2, -1, -1, -1, -1, 81, 2, 87, 2, -1, -1, -1, -1, 93, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 97, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 102, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 104, 2, 110, 2, 116, 2, 122, 2,-128, 2,-122, 2,-116, 2,-110, 2,-104, 2, -98, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -92, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -87, 2, -76, 2, -71, 2, -65, 2, -61, 2, -52, 2, -48, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 3, -1, -1, -1, -1, -1, -1, 37, 3, 75, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 113, 3, 119, 3, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 27, 91, 63, 49, 99, 0, 8, 0, 27, 91, 63, 50, 53, 104, 27, 91, 63, 48, 99, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 50, 53, 104, 27, 91, 63, 56, 99, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 50, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 64, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 91, 66, 0, 27, 91, 91, 65, 0, 27, 91, 50, 49, 126, 0, 27, 91, 91, 66, 0, 27, 91, 91, 67, 0, 27, 91, 91, 68, 0, 27, 91, 91, 69, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 91, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 91, 67, 0, 27, 91, 65, 0, 13, 10, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 27, 93, 82, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 59, 49, 48, 37, 63, 37, 112, 49, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 91, 71, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 95, 95, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 99, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 41, 48, 0, 27, 91, 52, 126, 0, 26, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 51, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 54, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 82, 0, 27, 93, 80, 37, 112, 49, 37, 120, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 48, 50, 120, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 48, 50, 120, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 48, 50, 120, 0, 27, 91, 77, 0, 27, 91, 51, 37, 112, 49, 37, 123, 56, 125, 37, 109, 37, 100, 37, 63, 37, 112, 49, 37, 123, 55, 125, 37, 62, 37, 116, 59, 49, 37, 101, 59, 50, 49, 37, 59, 109, 0, 27, 91, 52, 37, 112, 49, 37, 123, 56, 125, 37, 109, 37, 100, 37, 63, 37, 112, 49, 37, 123, 55, 125, 37, 62, 37, 116, 59, 53, 37, 101, 59, 50, 53, 37, 59, 109, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 48, 109, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+static const signed char putty_256colour_terminfo[] = {
+ 26, 1, 48, 0, 29, 0, 16, 0, 125, 1,-106, 4, 112, 117, 116, 116, 121, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 80, 117, 84, 84, 89, 32, 48, 46, 53, 56, 32, 119, 105, 116, 104, 32, 120, 116, 101, 114, 109, 32, 50, 53, 54, 45, 99, 111, 108, 111, 114, 115, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, -1, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 22, 0, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 37, 0, 41, 0, 45, 0, -1, -1, 56, 0, 73, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 96, 0, -1, -1, 100, 0, -1, -1, 103, 0, 107, 0, 111, 0, -1, -1, 117, 0, 119, 0, 124, 0,-127, 0, -1, -1, -1, -1,-120, 0, -1, -1, -1, -1,-115, 0,-110, 0,-105, 0,-100, 0, -91, 0, -89, 0, -84, 0, -1, -1, -73, 0, -68, 0, -62, 0, -56, 0, -1, -1, -38, 0, -1, -1, -36, 0, -1, -1, -1, -1, -1, -1, -2, 0, -1, -1, 2, 1, -1, -1, -1, -1, -1, -1, 4, 1, -1, -1, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, 13, 1, 19, 1, 25, 1, 31, 1, 37, 1, 43, 1, 49, 1, 55, 1, 61, 1, 67, 1, 73, 1, 78, 1, -1, -1, 83, 1, -1, -1, 87, 1, 92, 1, 97, 1, 101, 1, 105, 1, -1, -1, 109, 1, 113, 1, 121, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-127, 1, -1, -1,-124, 1,-115, 1,-106, 1, -1, -1, -97, 1, -88, 1, -79, 1, -70, 1, -61, 1, -52, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -43, 1, -1, -1, -1, -1, -10, 1, -7, 1, 4, 2, 7, 2, 9, 2, 12, 2, 84, 2, -1, -1, 87, 2, 89, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 94, 2, -1, -1, -1, -1, -1, -1, -1, -1, 98, 2, -1, -1,-107, 2, -1, -1, -1, -1,-103, 2, -97, 2, -1, -1, -1, -1, -91, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -84, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -79, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -77, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -73, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -69, 2, -63, 2, -57, 2, -51, 2, -45, 2, -39, 2, -33, 2, -27, 2, -21, 2, -15, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -4, 2, 7, 3, 12, 3, 18, 3, 22, 3, 31, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, 3, -1, -1, -1, -1, -1, -1, 39, 3, 102, 3, -1, -1, -1, -1, -1, -1, -90, 3, -84, 3, -78, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -72, 3,-118, 4,-112, 4, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 27, 68, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 77, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 93, 48, 59, 7, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 52, 55, 104, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 55, 27, 91, 114, 27, 91, 109, 27, 91, 63, 55, 104, 27, 91, 63, 49, 59, 52, 59, 54, 108, 27, 91, 52, 108, 27, 56, 27, 62, 27, 93, 82, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 91, 49, 49, 126, 0, 27, 91, 50, 49, 126, 0, 27, 91, 49, 50, 126, 0, 27, 91, 49, 51, 126, 0, 27, 91, 49, 52, 126, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 66, 0, 27, 91, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 13, 10, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 60, 27, 91, 34, 112, 27, 91, 53, 48, 59, 54, 34, 112, 27, 99, 27, 91, 63, 51, 108, 27, 93, 82, 27, 91, 63, 49, 48, 48, 48, 108, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 49, 37, 112, 54, 37, 124, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 93, 48, 59, 0, 27, 91, 71, 0, 96, 96, 97, 97, 102, 102, 103, 103, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 52, 126, 0, 26, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 51, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 54, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 82, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 49, 48, 109, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 50, 109, 0, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-105,-104, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 48, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-105,-103, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 50, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-103,-128, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 51, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-103, -86, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 52, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-103, -85, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 53, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-104, -68, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 50, 55, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-122,-112, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 53, 53, 125, 37, 61, 37, 116, 27, 37, 37, 71, -32,-126, -94, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 99, 37, 59, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 48, 109, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+static const signed char interix_8colour_terminfo[] = {
+ 26, 1, 82, 0, 15, 0, 16, 0, 105, 1, 123, 2, 105, 110, 116, 101, 114, 105, 120, 124, 111, 112, 101, 110, 110, 116, 124, 111, 112, 101, 110, 110, 116, 45, 50, 53, 124, 110, 116, 99, 111, 110, 115, 111, 108, 101, 124, 110, 116, 99, 111, 110, 115, 111, 108, 101, 45, 50, 53, 124, 79, 112, 101, 110, 78, 84, 45, 116, 101, 114, 109, 32, 99, 111, 109, 112, 97, 116, 105, 98, 108, 101, 32, 119, 105, 116, 104, 32, 99, 111, 108, 111, 114, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 80, 0, -1, -1, 25, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 0, 64, 0, 3, 0, 0, 0, 4, 0, -1, -1, -1, -1, -1, -1, 6, 0, 11, 0, 15, 0, -1, -1, -1, -1, 19, 0, 36, 0, 38, 0, -1, -1, 42, 0, -1, -1, -1, -1, 46, 0, 50, 0, 54, 0, -1, -1, -1, -1, 58, 0, -1, -1, -1, -1, -1, -1, -1, -1, 62, 0, 67, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 75, 0, 80, 0, 85, 0, -1, -1, -1, -1, 90, 0, 95, 0, -1, -1, -1, -1, 107, 0, 111, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, 0, -1, -1, 119, 0, -1, -1, -1, -1, -1, -1, 121, 0, -1, -1, 125, 0, -1, -1, -1, -1, -1, -1,-127, 0,-123, 0,-119, 0,-115, 0,-111, 0,-107, 0,-103, 0, -99, 0, -95, 0, -91, 0, -87, 0, -1, -1, -83, 0, -1, -1, -79, 0, -75, 0, -71, 0, -67, 0, -63, 0, -1, -1, -1, -1, -1, -1, -59, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -55, 0, -1, -1, -1, -1, -52, 0, -43, 0, -1, -1, -34, 0, -25, 0, -16, 0, -7, 0, 2, 1, 11, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 1, -1, -1, -1, -1, -1, -1, 23, 1, -1, -1, 27, 1, 31, 1, 35, 1, -1, -1, -1, -1, -1, -1, 39, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, 1, -1, -1, 104, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 108, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 112, 1, 116, 1, 120, 1, 124, 1,-128, 1,-124, 1,-120, 1,-116, 1,-112, 1,-108, 1,-104, 1,-100, 1, -96, 1, -92, 1, -88, 1, -84, 1, -80, 1, -76, 1, -72, 1, -68, 1, -64, 1, -60, 1, -56, 1, -52, 1, -48, 1, -44, 1, -40, 1, -36, 1, -32, 1, -28, 1, -24, 1, -20, 1, -16, 1, -12, 1, -8, 1, -4, 1, 0, 2, 4, 2, 8, 2, 12, 2, 16, 2, 20, 2, 24, 2, 28, 2, 32, 2, 36, 2, 40, 2, 44, 2, 48, 2, 52, 2, 56, 2, 60, 2, 64, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68, 2, -1, -1, -1, -1, -1, -1, -1, -1, 72, 2, 88, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 103, 2, 113, 2, 27, 91, 90, 0, 7, 0, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 85, 0, 27, 91, 65, 0, 27, 91, 77, 0, 27, 91, 49, 109, 0, 27, 91, 115, 27, 91, 49, 98, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 48, 109, 0, 27, 91, 50, 98, 27, 91, 117, 13, 27, 91, 75, 0, 27, 91, 109, 0, 27, 91, 109, 0, 27, 91, 76, 0, 8, 0, 27, 91, 77, 0, 27, 91, 66, 0, 27, 70, 65, 0, 27, 70, 49, 0, 27, 70, 65, 0, 27, 70, 50, 0, 27, 70, 51, 0, 27, 70, 52, 0, 27, 70, 53, 0, 27, 70, 54, 0, 27, 70, 55, 0, 27, 70, 56, 0, 27, 70, 57, 0, 27, 91, 76, 0, 27, 91, 68, 0, 27, 91, 85, 0, 27, 91, 84, 0, 27, 91, 83, 0, 27, 91, 67, 0, 27, 91, 65, 0, 13, 10, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 0, 27, 91, 117, 0, 27, 91, 115, 0, 27, 91, 83, 0, 27, 91, 84, 0, 9, 0, 43, 16, 44, 17, 45, 24, 46, 25, 48, -37, 96, 4, 97, -79, 102, -8, 103, -15, 104, -80, 106, -39, 107, -65, 108, -38, 109, -64, 110, -59, 111, 126, 112, -60, 113, -60, 114, -60, 115, 95, 116, -61, 117, -76, 118, -63, 119, -62, 120, -77, 121, -13, 122, -14, 123, -29, 124, -40, 125,-100, 126, -2, 0, 27, 91, 90, 0, 27, 91, 85, 0, 27, 70, 66, 0, 27, 70, 67, 0, 27, 70, 68, 0, 27, 70, 69, 0, 27, 70, 70, 0, 27, 70, 71, 0, 27, 70, 72, 0, 27, 70, 73, 0, 27, 70, 74, 0, 27, 70, 75, 0, 27, 70, 76, 0, 27, 70, 77, 0, 27, 70, 78, 0, 27, 70, 79, 0, 27, 70, 80, 0, 27, 70, 81, 0, 27, 70, 82, 0, 27, 70, 83, 0, 27, 70, 84, 0, 27, 70, 85, 0, 27, 70, 86, 0, 27, 70, 87, 0, 27, 70, 88, 0, 27, 70, 89, 0, 27, 70, 90, 0, 27, 70, 97, 0, 27, 70, 98, 0, 27, 70, 99, 0, 27, 70, 100, 0, 27, 70, 101, 0, 27, 70, 102, 0, 27, 70, 103, 0, 27, 70, 104, 0, 27, 70, 105, 0, 27, 70, 106, 0, 27, 70, 107, 0, 27, 70, 109, 0, 27, 70, 110, 0, 27, 70, 111, 0, 27, 70, 112, 0, 27, 70, 113, 0, 27, 70, 114, 0, 27, 70, 115, 0, 27, 70, 116, 0, 27, 70, 117, 0, 27, 70, 118, 0, 27, 70, 119, 0, 27, 70, 120, 0, 27, 70, 121, 0, 27, 70, 122, 0, 27, 70, 43, 0, 27, 70, 45, 0, 27, 70, 12, 0, 27, 91, 109, 0, 27, 91, 37, 112, 49, 37, 123, 51, 48, 125, 37, 43, 37, 100, 109, 0, 27, 91, 37, 112, 49, 37, 39, 40, 39, 37, 43, 37, 100, 109, 0, 27, 91, 51, 37, 112, 49, 37, 100, 109, 0, 27, 91, 52, 37, 112, 49, 37, 100, 109, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+// This is a 256-colour terminfo description that lacks true-colour
+// capabilities that stterm actually has.
+static const signed char st_256colour_terminfo[] = {
+ 26, 1, 55, 0, 29, 0, 15, 0, 105, 1, 117, 5, 115, 116, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 115, 116, 116, 101, 114, 109, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 115, 105, 109, 112, 108, 101, 116, 101, 114, 109, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, -1, -1, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 102, 0, -1, -1, 106, 0, 110, 0, 117, 0, 121, 0, -1, -1, -1, -1, 125, 0,-127, 0,-122, 0,-117, 0, -1, -1, -1, -1,-108, 0,-103, 0, -1, -1, -98, 0, -93, 0, -88, 0, -83, 0, -74, 0, -70, 0, -65, 0, -1, -1, -56, 0, -51, 0, -45, 0, -39, 0, -1, -1, -21, 0, -1, -1, -19, 0, -1, -1, -1, -1, -1, -1, -4, 0, -1, -1, 0, 1, -1, -1, 2, 1, -1, -1, 9, 1, 14, 1, 21, 1, 25, 1, 32, 1, 39, 1, -1, -1, 46, 1, 50, 1, 56, 1, 60, 1, 64, 1, 68, 1, 74, 1, 80, 1, 86, 1, 92, 1, 98, 1, 103, 1, 108, 1, 115, 1, -1, -1, 119, 1, 124, 1,-127, 1,-123, 1,-116, 1, -1, -1,-109, 1,-105, 1, -97, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -89, 1, -80, 1, -71, 1, -62, 1, -53, 1, -44, 1, -35, 1, -26, 1, -1, -1, -17, 1, -1, -1, -1, -1, -1, -1, -8, 1, -4, 1, 1, 2, -1, -1, 6, 2, 9, 2, -1, -1, -1, -1, 24, 2, 27, 2, 38, 2, 41, 2, 43, 2, 46, 2,-128, 2, -1, -1,-125, 2,-123, 2, -1, -1, -1, -1, -1, -1,-118, 2,-113, 2,-108, 2,-104, 2, -99, 2, -1, -1, -1, -1, -94, 2, -1, -1, -29, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -25, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -21, 2, -16, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -12, 2, -1, -1, -1, -1, -5, 2, -1, -1, -1, -1, -1, -1, -1, -1, 2, 3, 9, 3, 16, 3, -1, -1, -1, -1, 23, 3, -1, -1, 30, 3, -1, -1, -1, -1, -1, -1, 37, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, 3, 50, 3, 56, 3, 63, 3, 70, 3, 77, 3, 84, 3, 92, 3, 100, 3, 108, 3, 116, 3, 124, 3,-124, 3,-116, 3,-108, 3,-101, 3, -94, 3, -87, 3, -80, 3, -72, 3, -64, 3, -56, 3, -48, 3, -40, 3, -32, 3, -24, 3, -16, 3, -9, 3, -2, 3, 5, 4, 12, 4, 20, 4, 28, 4, 36, 4, 44, 4, 52, 4, 60, 4, 68, 4, 76, 4, 83, 4, 90, 4, 97, 4, 104, 4, 112, 4, 120, 4,-128, 4,-120, 4,-112, 4,-104, 4, -96, 4, -88, 4, -81, 4, -74, 4, -67, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -62, 4, -51, 4, -46, 4, -38, 4, -34, 4, -2, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -25, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -20, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -14, 4, -1, -1, -1, -1, -1, -1, -10, 4, 53, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 49, 50, 108, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 40, 48, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 40, 66, 0, 27, 91, 48, 109, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 51, 52, 108, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 59, 53, 126, 0, 27, 91, 51, 126, 0, 27, 91, 51, 59, 50, 126, 0, 27, 79, 66, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 53, 70, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 91, 50, 59, 53, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 91, 105, 0, 27, 91, 52, 105, 0, 27, 91, 53, 105, 0, 27, 99, 0, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 51, 52, 108, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 37, 63, 37, 112, 57, 37, 116, 27, 40, 48, 37, 101, 27, 40, 66, 37, 59, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 109, 0, 27, 72, 0, 9, 0, 27, 93, 48, 59, 0, 27, 91, 49, 126, 0, 27, 91, 53, 126, 0, 27, 79, 117, 0, 27, 91, 52, 126, 0, 27, 91, 54, 126, 0, 43, 67, 44, 68, 45, 65, 46, 66, 48, 69, 96, 96, 97, 97, 102, 102, 103, 103, 104, 70, 105, 71, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 41, 48, 0, 27, 91, 52, 126, 0, 27, 79, 77, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 59, 50, 82, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 59, 53, 82, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 59, 54, 82, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 59, 51, 82, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 59, 52, 82, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 49, 59, 50, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+// This is a 256-colour terminfo description that lacks true-colour
+// capabilities that gnome actually has.
+static const signed char vte_256colour_terminfo[] = {
+ 26, 1, 52, 0, 29, 0, 15, 0, 105, 1, -55, 5, 103, 110, 111, 109, 101, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 71, 78, 79, 77, 69, 32, 84, 101, 114, 109, 105, 110, 97, 108, 32, 119, 105, 116, 104, 32, 120, 116, 101, 114, 109, 32, 50, 53, 54, 45, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, -1, -1, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 96, 0, -1, -1, 100, 0, -1, -1, 104, 0, 108, 0, -1, -1, -1, -1, 112, 0, -1, -1, 114, 0, 119, 0, -1, -1, -128, 0, -123, 0, -118, 0, -1, -1,-113, 0, -108, 0, -103, 0, -98, 0, -89, 0, -87, 0, -81, 0, -1, -1, -68, 0, -63, 0, -57, 0, -51, 0, -1, -1, -1, -1, -1, -1, -33, 0, -1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 4, 1, -1, -1, -1, -1, -1, -1, 6, 1, -1, -1, 11, 1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 1, 19, 1, 25, 1, 29, 1, 33, 1, 37, 1, 43, 1, 49, 1, 55, 1, 61, 1, 67, 1, 71, 1, -1, -1, 76, 1, -1, -1, 80, 1, 85, 1, 90, 1, 94, 1, 101, 1, -1, -1, 108, 1, 112, 1, 120, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -128, 1,-119, 1, -110, 1, -101, 1, -92, 1, -83, 1, -74, 1, -65, 1, -56, 1, -47, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -38, 1, -35, 1, -1, -1, -1, -1, 16, 2, 19, 2, 30, 2, 33, 2, 35, 2, 38, 2, 116, 2, -1, -1, 119, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 121, 2, -1, -1, -1, -1, -1, -1, -1, -1, 125, 2, -1, -1, -78, 2, -1, -1, -1, -1, -74, 2, -68, 2, -1, -1, -1, -1, -62, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -58, 2, -54, 2, -1, -1, -50, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -45, 2, -1, -1, -38, 2, -33, 2, -1, -1, -1, -1, -1, -1, -1, -1, -26, 2, -19, 2, -12, 2, -1, -1, -1, -1, -5, 2, -1, -1, 2, 3, -1, -1, -1, -1, -1, -1, 9, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 3, 22, 3, 28, 3, 35, 3, 42, 3, 49, 3, 56, 3, 64, 3, 72, 3, 80, 3, 88, 3, 96, 3, 104, 3, 112, 3, 120, 3, 127, 3, -122, 3, -115, 3,-108, 3, -100, 3, -92, 3, -84, 3, -76, 3, -68, 3, -60, 3, -52, 3, -44, 3, -37, 3, -30, 3, -23, 3, -16, 3, -8, 3, 0, 4, 8, 4, 16, 4, 24, 4, 32, 4, 40, 4, 48, 4, 55, 4, 62, 4, 69, 4, 76, 4, 84, 4, 92, 4, 100, 4, 108, 4, 116, 4, 124, 4, -124, 4,-116, 4, -109, 4, -102, 4, -95, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -90, 4, -79, 4, -74, 4, -55, 4, -51, 4, -42, 4, -35, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 5, -1, -1, -1, -1, -1, -1, 74, 5, -119, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 49, 109, 0, 27, 55, 27, 91, 63, 52, 55, 104, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 15, 0, 27, 91, 48, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 27, 56, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 109, 27, 91, 63, 55, 104, 27, 91, 52, 108, 27, 62, 27, 55, 27, 91, 114, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 56, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 0, 27, 55, 27, 91, 114, 27, 56, 27, 91, 109, 27, 91, 63, 55, 104, 27, 91, 33, 112, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 91, 69, 0, 96, 96, 97, 97, 102, 102, 103, 103, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 41, 48, 0, 27, 79, 70, 0, 27, 79, 77, 0, 27, 91, 49, 126, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 52, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 59, 50, 82, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 59, 53, 82, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 59, 54, 82, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 59, 51, 82, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 59, 52, 82, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0
+};
+// Taken from Dickey ncurses terminfo.src dated 2017-04-22.
+static const signed char ansi_terminfo[] = {
+ 26, 1, 40, 0, 23, 0, 16, 0, 125, 1, 68, 2, 97, 110, 115, 105, 124, 97, 110, 115, 105, 47, 112, 99, 45, 116, 101, 114, 109, 32, 99, 111, 109, 112, 97, 116, 105, 98, 108, 101, 32, 119, 105, 116, 104, 32, 99, 111, 108, 111, 114, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 0, 64, 0, 3, 0, 0, 0, 4, 0, 6, 0, -1, -1, 8, 0, 13, 0, 20, 0, 24, 0, 28, 0, -1, -1, 39, 0, 56, 0, 60, 0, -1, -1, 64, 0, -1, -1, -1, -1, 68, 0, -1, -1, 72, 0, -1, -1, 76, 0, 80, 0, -1, -1, -1, -1, 84, 0, 90, 0, 95, 0, -1, -1, -1, -1, -1, -1, -1, -1, 100, 0, -1, -1, 105, 0, 110, 0, 115, 0, 120, 0,-127, 0,-121, 0, -1, -1, -1, -1, -1, -1,-113, 0,-109, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-105, 0, -1, -1,-101, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -99, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -95, 0, -91, 0, -1, -1, -87, 0, -1, -1, -1, -1, -1, -1, -83, 0, -1, -1, -1, -1, -1, -1, -79, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -75, 0, -1, -1, -70, 0, -61, 0, -52, 0, -43, 0, -34, 0, -25, 0, -16, 0, -7, 0, 2, 1, 11, 1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 1, 25, 1, 30, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 50, 1, -1, -1, 61, 1, -1, -1, 63, 1,-107, 1, -1, -1,-104, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-100, 1, -1, -1, -37, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -33, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -28, 1, -17, 1, -12, 1, 7, 2, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 2, 30, 2, -1, -1, -1, -1, -1, -1, 40, 2, 44, 2, 48, 2, 52, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, 2, 62, 2, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 27, 91, 66, 0, 27, 91, 72, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 91, 49, 49, 109, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 91, 49, 48, 109, 0, 27, 91, 48, 59, 49, 48, 109, 0, 27, 91, 109, 0, 27, 91, 109, 0, 27, 91, 76, 0, 8, 0, 27, 91, 66, 0, 27, 91, 72, 0, 27, 91, 76, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 65, 0, 13, 27, 91, 83, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 91, 52, 105, 0, 27, 91, 53, 105, 0, 37, 112, 49, 37, 99, 27, 91, 37, 112, 50, 37, 123, 49, 125, 37, 45, 37, 100, 98, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 10, 0, 27, 91, 48, 59, 49, 48, 37, 63, 37, 112, 49, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 37, 63, 37, 112, 57, 37, 116, 59, 49, 49, 37, 59, 109, 0, 27, 72, 0, 27, 91, 73, 0, 43, 16, 44, 17, 45, 24, 46, 25, 48, -37, 96, 4, 97, -79, 102, -8, 103, -15, 104, -80, 106, -39, 107, -65, 108, -38, 109, -64, 110, -59, 111, 126, 112, -60, 113, -60, 114, -60, 115, 95, 116, -61, 117, -76, 118, -63, 119, -62, 120, -77, 121, -13, 122, -14, 123, -29, 124, -40, 125,-100, 126, -2, 0, 27, 91, 90, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 51, 37, 112, 49, 37, 100, 109, 0, 27, 91, 52, 37, 112, 49, 37, 100, 109, 0, 27, 40, 66, 0, 27, 41, 66, 0, 27, 42, 66, 0, 27, 43, 66, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 48, 109, 0
+};
+
+bool terminfo_is_term_family(const char *term, const char *family)
+{
+ if (!term) {
+ return false;
+ }
+ size_t tlen = strlen(term);
+ size_t flen = strlen(family);
+ return tlen >= flen
+ && 0 == memcmp(term, family, flen)
+ // Per commentary in terminfo, minus is the only valid suffix separator.
+ && ('\0' == term[flen] || '-' == term[flen]);
+}
+
+/// Loads a built-in terminfo db when we (unibilium) failed to load a terminfo
+/// record from the environment (termcap systems, unrecognized $TERM, …).
+/// We do not attempt to detect xterm pretenders here.
+///
+/// @param term $TERM value
+/// @param[out,allocated] termname decided builtin 'term' name
+/// @return [allocated] terminfo structure
+static unibi_term *terminfo_builtin(const char *term, char **termname)
+{
+ if (terminfo_is_term_family(term, "xterm")) {
+ *termname = xstrdup("builtin_xterm");
+ return unibi_from_mem((const char *)xterm_256colour_terminfo,
+ sizeof xterm_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "screen")) {
+ *termname = xstrdup("builtin_screen");
+ return unibi_from_mem((const char *)screen_256colour_terminfo,
+ sizeof screen_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "tmux")) {
+ *termname = xstrdup("builtin_tmux");
+ return unibi_from_mem((const char *)tmux_256colour_terminfo,
+ sizeof tmux_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "rxvt")) {
+ *termname = xstrdup("builtin_rxvt");
+ return unibi_from_mem((const char *)rxvt_256colour_terminfo,
+ sizeof rxvt_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "putty")) {
+ *termname = xstrdup("builtin_putty");
+ return unibi_from_mem((const char *)putty_256colour_terminfo,
+ sizeof putty_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "linux")) {
+ *termname = xstrdup("builtin_linux");
+ return unibi_from_mem((const char *)linux_16colour_terminfo,
+ sizeof linux_16colour_terminfo);
+ } else if (terminfo_is_term_family(term, "interix")) {
+ *termname = xstrdup("builtin_interix");
+ return unibi_from_mem((const char *)interix_8colour_terminfo,
+ sizeof interix_8colour_terminfo);
+ } else if (terminfo_is_term_family(term, "iterm")
+ || terminfo_is_term_family(term, "iterm2")
+ || terminfo_is_term_family(term, "iTerm.app")
+ || terminfo_is_term_family(term, "iTerm2.app")) {
+ *termname = xstrdup("builtin_iterm");
+ return unibi_from_mem((const char *)iterm_256colour_terminfo,
+ sizeof iterm_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "st")) {
+ *termname = xstrdup("builtin_st");
+ return unibi_from_mem((const char *)st_256colour_terminfo,
+ sizeof st_256colour_terminfo);
+ } else if (terminfo_is_term_family(term, "gnome")
+ || terminfo_is_term_family(term, "vte")) {
+ *termname = xstrdup("builtin_vte");
+ return unibi_from_mem((const char *)vte_256colour_terminfo,
+ sizeof vte_256colour_terminfo);
+ } else {
+ *termname = xstrdup("builtin_ansi");
+ return unibi_from_mem((const char *)ansi_terminfo,
+ sizeof ansi_terminfo);
+ }
+}
+
+/// @param term $TERM value
+/// @param[out,allocated] termname decided builtin 'term' name
+/// @return [allocated] terminfo structure
+unibi_term *terminfo_from_builtin(const char *term, char **termname)
+{
+ unibi_term *ut = terminfo_builtin(term, termname);
+ if (*termname == NULL) {
+ *termname = xstrdup("builtin_?");
+ }
+ // Disable BCE by default (for built-in terminfos). #7624
+ // https://github.com/kovidgoyal/kitty/issues/160#issuecomment-346470545
+ unibi_set_bool(ut, unibi_back_color_erase, false);
+ return ut;
+}
+
+/// Dumps termcap info to the messages area.
+/// Serves a similar purpose as Vim `:set termcap` (removed in Nvim).
+///
+/// @note adapted from unibilium unibi-dump.c
+void terminfo_info_msg(const unibi_term *const ut)
+{
+ if (exiting) {
+ return;
+ }
+ msg_puts_title("\n\n--- Terminal info --- {{{\n");
+
+ char *term;
+ get_tty_option("term", &term);
+ msg_printf_attr(0, "&term: %s\n", term);
+ msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut));
+ const char **a = unibi_get_aliases(ut);
+ if (*a) {
+ msg_puts("Aliases: ");
+ do {
+ msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : "");
+ a++;
+ } while (*a);
+ }
+
+ msg_puts("Boolean capabilities:\n");
+ for (enum unibi_boolean i = unibi_boolean_begin_ + 1;
+ i < unibi_boolean_end_; i++) {
+ msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i),
+ unibi_short_name_bool(i),
+ unibi_get_bool(ut, i) ? "true" : "false");
+ }
+
+ msg_puts("Numeric capabilities:\n");
+ for (enum unibi_numeric i = unibi_numeric_begin_ + 1;
+ i < unibi_numeric_end_; i++) {
+ int n = unibi_get_num(ut, i); // -1 means "empty"
+ msg_printf_attr(0, " %-25s %-10s = %hd\n", unibi_name_num(i),
+ unibi_short_name_num(i), n);
+ }
+
+ msg_puts("String capabilities:\n");
+ for (enum unibi_string i = unibi_string_begin_ + 1;
+ i < unibi_string_end_; i++) {
+ const char *s = unibi_get_str(ut, i);
+ if (s) {
+ msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i),
+ unibi_short_name_str(i));
+ // Most of these strings will contain escape sequences.
+ msg_outtrans_special((char_u *)s, false);
+ msg_putchar('\n');
+ }
+ }
+
+ if (unibi_count_ext_bool(ut)) {
+ msg_puts("Extended boolean capabilities:\n");
+ for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) {
+ msg_printf_attr(0, " %-25s = %s\n",
+ unibi_get_ext_bool_name(ut, i),
+ unibi_get_ext_bool(ut, i) ? "true" : "false");
+ }
+ }
+
+ if (unibi_count_ext_num(ut)) {
+ msg_puts("Extended numeric capabilities:\n");
+ for (size_t i = 0; i < unibi_count_ext_num(ut); i++) {
+ msg_printf_attr(0, " %-25s = %hd\n",
+ unibi_get_ext_num_name(ut, i),
+ unibi_get_ext_num(ut, i));
+ }
+ }
+
+ if (unibi_count_ext_str(ut)) {
+ msg_puts("Extended string capabilities:\n");
+ for (size_t i = 0; i < unibi_count_ext_str(ut); i++) {
+ msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i));
+ msg_outtrans_special((char_u *)unibi_get_ext_str(ut, i), false);
+ msg_putchar('\n');
+ }
+ }
+
+ msg_puts("}}}\n");
+ xfree(term);
+}
diff --git a/src/nvim/tui/terminfo.h b/src/nvim/tui/terminfo.h
new file mode 100644
index 0000000000..099df8967f
--- /dev/null
+++ b/src/nvim/tui/terminfo.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_TUI_TERMINFO_H
+#define NVIM_TUI_TERMINFO_H
+
+#include <unibilium.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "tui/terminfo.h.generated.h"
+#endif
+
+#endif // NVIM_TUI_TERMINFO_H
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 736d50ee8b..dcb1f850b7 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -23,6 +23,7 @@
#include "nvim/map.h"
#include "nvim/main.h"
#include "nvim/memory.h"
+#include "nvim/option.h"
#include "nvim/api/vim.h"
#include "nvim/api/private/helpers.h"
#include "nvim/event/loop.h"
@@ -34,29 +35,32 @@
#include "nvim/ugrid.h"
#include "nvim/tui/input.h"
#include "nvim/tui/tui.h"
+#include "nvim/tui/terminfo.h"
#include "nvim/cursor_shape.h"
#include "nvim/syntax.h"
#include "nvim/macros.h"
-// Space reserved in the output buffer to restore the cursor to normal when
-// flushing. No existing terminal will require 32 bytes to do that.
+// Space reserved in two output buffers to make the cursor normal or invisible
+// when flushing. No existing terminal will require 32 bytes to do that.
#define CNORM_COMMAND_MAX_SIZE 32
#define OUTBUF_SIZE 0xffff
#define TOO_MANY_EVENTS 1000000
-#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))
-#define TMUX_WRAP(seq) (is_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
-
-typedef enum TermType {
- kTermUnknown,
- kTermGnome,
- kTermiTerm,
- kTermKonsole,
- kTermRxvt,
- kTermDTTerm,
- kTermXTerm,
- kTermTeraTerm,
-} TermType;
+#define STARTS_WITH(str, prefix) (strlen(str) >= (sizeof(prefix) - 1) \
+ && 0 == memcmp((str), (prefix), sizeof(prefix) - 1))
+#define TMUX_WRAP(is_tmux, seq) ((is_tmux) \
+ ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
+#define LINUXSET0C "\x1b[?0c"
+#define LINUXSET1C "\x1b[?1c"
+
+#ifdef NVIM_UNIBI_HAS_VAR_FROM
+#define UNIBI_SET_NUM_VAR(var, num) \
+ do { \
+ (var) = unibi_var_from_num((num)); \
+ } while (0)
+#else
+#define UNIBI_SET_NUM_VAR(var, num) (var).i = (num);
+#endif
typedef struct {
int top, bot, left, right;
@@ -65,10 +69,12 @@ typedef struct {
typedef struct {
UIBridgeData *bridge;
Loop *loop;
- bool stop;
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
- size_t bufpos, bufsize;
+ size_t bufpos;
+ char norm[CNORM_COMMAND_MAX_SIZE];
+ char invis[CNORM_COMMAND_MAX_SIZE];
+ size_t normlen, invislen;
TermInput input;
uv_loop_t write_loop;
unibi_term *ut;
@@ -86,12 +92,14 @@ typedef struct {
bool can_change_scroll_region;
bool can_set_lr_margin;
bool can_set_left_right_margin;
+ bool immediate_wrap_after_last_column;
bool mouse_enabled;
- bool busy;
+ bool busy, is_invisible;
+ bool cork, overflow;
cursorentry_T cursor_shapes[SHAPE_IDX_COUNT];
HlAttrs print_attrs;
+ bool default_attr;
ModeShape showing_mode;
- TermType term;
struct {
int enable_mouse, disable_mouse;
int enable_bracketed_paste, disable_bracketed_paste;
@@ -101,12 +109,12 @@ typedef struct {
int enable_focus_reporting, disable_focus_reporting;
int resize_screen;
int reset_scroll_region;
+ int set_cursor_style, reset_cursor_style;
} unibi_ext;
} TUIData;
static bool volatile got_winch = false;
static bool cursor_style_enabled = false;
-static bool is_tmux = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.c.generated.h"
@@ -115,9 +123,8 @@ static bool is_tmux = false;
UI *tui_start(void)
{
- UI *ui = xcalloc(1, sizeof(UI));
+ UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop().
ui->stop = tui_stop;
- ui->rgb = p_tgc;
ui->resize = tui_resize;
ui->clear = tui_clear;
ui->eol_clear = tui_eol_clear;
@@ -135,26 +142,45 @@ UI *tui_start(void)
ui->put = tui_put;
ui->bell = tui_bell;
ui->visual_bell = tui_visual_bell;
- ui->update_fg = tui_update_fg;
- ui->update_bg = tui_update_bg;
- ui->update_sp = tui_update_sp;
+ ui->default_colors_set = tui_default_colors_set;
ui->flush = tui_flush;
ui->suspend = tui_suspend;
ui->set_title = tui_set_title;
ui->set_icon = tui_set_icon;
- ui->event = tui_event;
+ ui->option_set= tui_option_set;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
return ui_bridge_attach(ui, tui_main, tui_scheduler);
}
+static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index,
+ char * buf, size_t len)
+{
+ const char *str = unibi_get_str(data->ut, unibi_index);
+ if (!str) {
+ return 0U;
+ }
+ return unibi_run(str, data->params, buf, len);
+}
+
+static void termname_set_event(void **argv)
+{
+ char *termname = argv[0];
+ set_tty_option("term", termname);
+ // Do not free termname, it is freed by set_tty_option.
+}
+
static void terminfo_start(UI *ui)
{
TUIData *data = ui->data;
data->scroll_region_is_full_screen = true;
data->bufpos = 0;
- data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE;
+ data->default_attr = false;
+ data->is_invisible = true;
+ data->busy = false;
+ data->cork = false;
+ data->overflow = false;
data->showing_mode = SHAPE_IDX_N;
data->unibi_ext.enable_mouse = -1;
data->unibi_ext.disable_mouse = -1;
@@ -167,16 +193,36 @@ static void terminfo_start(UI *ui)
data->unibi_ext.disable_focus_reporting = -1;
data->unibi_ext.resize_screen = -1;
data->unibi_ext.reset_scroll_region = -1;
+ data->unibi_ext.set_cursor_style = -1;
+ data->unibi_ext.reset_cursor_style = -1;
data->out_fd = 1;
data->out_isatty = os_isatty(data->out_fd);
- // setup unibilium
+
+ // Set up unibilium/terminfo.
+ const char *term = os_getenv("TERM");
data->ut = unibi_from_env();
+ char *termname = NULL;
if (!data->ut) {
- // For some reason could not read terminfo file, use a dummy entry that
- // will be populated with common values by fix_terminfo below
- data->ut = unibi_dummy();
+ data->ut = terminfo_from_builtin(term, &termname);
+ } else {
+ termname = xstrdup(term);
}
- fix_terminfo(data);
+ // Update 'term' option.
+ loop_schedule_deferred(&main_loop,
+ event_create(termname_set_event, 1, termname));
+
+ // None of the following work over SSH; see :help TERM .
+ const char *colorterm = os_getenv("COLORTERM");
+ const char *termprg = os_getenv("TERM_PROGRAM");
+ const char *vte_version_env = os_getenv("VTE_VERSION");
+ long vte_version = vte_version_env ? strtol(vte_version_env, NULL, 10) : 0;
+ bool iterm_env = termprg && strstr(termprg, "iTerm.app");
+ bool konsole = terminfo_is_term_family(term, "konsole")
+ || os_getenv("KONSOLE_PROFILE_NAME")
+ || os_getenv("KONSOLE_DBUS_SESSION");
+
+ patch_terminfo_bugs(data, term, colorterm, vte_version, konsole, iterm_env);
+ augment_terminfo(data, term, colorterm, vte_version, konsole, iterm_env);
data->can_change_scroll_region =
!!unibi_get_str(data->ut, unibi_change_scroll_region);
data->can_set_lr_margin =
@@ -184,20 +230,31 @@ static void terminfo_start(UI *ui)
data->can_set_left_right_margin =
!!unibi_get_str(data->ut, unibi_set_left_margin_parm)
&& !!unibi_get_str(data->ut, unibi_set_right_margin_parm);
+ data->immediate_wrap_after_last_column =
+ terminfo_is_term_family(term, "cygwin")
+ || terminfo_is_term_family(term, "interix");
+ data->normlen = unibi_pre_fmt_str(data, unibi_cursor_normal,
+ data->norm, sizeof data->norm);
+ data->invislen = unibi_pre_fmt_str(data, unibi_cursor_invisible,
+ data->invis, sizeof data->invis);
// Set 't_Co' from the result of unibilium & fix_terminfo.
t_colors = unibi_get_num(data->ut, unibi_max_colors);
// Enter alternate screen and clear
// NOTE: Do this *before* changing terminal settings. #6433
unibi_out(ui, unibi_enter_ca_mode);
+ unibi_out(ui, unibi_keypad_xmit);
unibi_out(ui, unibi_clear_screen);
// Enable bracketed paste
- unibi_out(ui, data->unibi_ext.enable_bracketed_paste);
- // Enable focus reporting
- unibi_out(ui, data->unibi_ext.enable_focus_reporting);
+ unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
+
uv_loop_init(&data->write_loop);
if (data->out_isatty) {
uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
+#ifdef WIN32
uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
+#else
+ uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+#endif
} else {
uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
uv_pipe_open(&data->output_handle.pipe, data->out_fd);
@@ -213,12 +270,13 @@ static void terminfo_stop(UI *ui)
unibi_out(ui, unibi_exit_attribute_mode);
// cursor should be set to normal before exiting alternate screen
unibi_out(ui, unibi_cursor_normal);
+ unibi_out(ui, unibi_keypad_local);
unibi_out(ui, unibi_exit_ca_mode);
// Disable bracketed paste
- unibi_out(ui, data->unibi_ext.disable_bracketed_paste);
+ unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste);
// Disable focus reporting
- unibi_out(ui, data->unibi_ext.disable_focus_reporting);
- flush_buf(ui, true);
+ unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting);
+ flush_buf(ui);
uv_tty_reset_mode();
uv_close((uv_handle_t *)&data->output_handle, NULL);
uv_run(&data->write_loop, UV_RUN_DEFAULT);
@@ -231,7 +289,7 @@ static void terminfo_stop(UI *ui)
static void tui_terminal_start(UI *ui)
{
TUIData *data = ui->data;
- data->print_attrs = EMPTY_ATTRS;
+ data->print_attrs = HLATTRS_INIT;
ugrid_init(&data->grid);
terminfo_start(ui);
update_size(ui);
@@ -239,6 +297,16 @@ static void tui_terminal_start(UI *ui)
term_input_start(&data->input);
}
+static void tui_terminal_after_startup(UI *ui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ TUIData *data = ui->data;
+
+ // Emit this after Nvim startup, not during. This works around a tmux
+ // 2.3 bug(?) which caused slow drawing during startup. #7649
+ unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
+}
+
static void tui_terminal_stop(UI *ui)
{
TUIData *data = ui->data;
@@ -251,11 +319,17 @@ static void tui_terminal_stop(UI *ui)
static void tui_stop(UI *ui)
{
tui_terminal_stop(ui);
- TUIData *data = ui->data;
- data->stop = true;
+ // Flag UI as "stopped".
+ ui->data = NULL;
+}
+
+/// Returns true if UI `ui` is stopped.
+static bool tui_is_stopped(UI *ui)
+{
+ return ui->data == NULL;
}
-// Main function of the TUI thread
+/// Main function of the TUI thread.
static void tui_main(UIBridgeData *bridge, UI *ui)
{
Loop tui_loop;
@@ -276,14 +350,26 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
#endif
term_input_init(&data->input, &tui_loop);
tui_terminal_start(ui);
- data->stop = false;
- // allow the main thread to continue, we are ready to start handling UI
- // callbacks
+ // Allow main thread to continue, we are ready to handle UI callbacks.
CONTINUE(bridge);
- while (!data->stop) {
- loop_poll_events(&tui_loop, -1);
+ loop_schedule_deferred(&main_loop,
+ event_create(show_termcap_event, 1, data->ut));
+
+ // "Active" loop: first ~100 ms of startup.
+ for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) {
+ ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1);
+ }
+ if (!tui_is_stopped(ui)) {
+ tui_terminal_after_startup(ui);
+ // Tickle `main_loop` with a dummy event, else the initial "focus-gained"
+ // terminal response may not get processed until user hits a key.
+ loop_schedule_deferred(&main_loop, event_create(tui_dummy_event, 0));
+ }
+ // "Passive" (I/O-driven) loop: TUI thread "main loop".
+ while (!tui_is_stopped(ui)) {
+ loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed
}
ui_bridge_stopped(bridge);
@@ -294,14 +380,18 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
loop_close(&tui_loop, false);
kv_destroy(data->invalid_regions);
xfree(data);
- xfree(ui);
}
+static void tui_dummy_event(void **argv)
+{
+}
+
+/// Handoff point between the main (ui_bridge) thread and the TUI thread.
static void tui_scheduler(Event event, void *d)
{
UI *ui = d;
TUIData *data = ui->data;
- loop_schedule(data->loop, event);
+ loop_schedule(data->loop, event); // `tui_loop` local to tui_main().
}
#ifdef UNIX
@@ -319,94 +409,293 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
ui_schedule_refresh();
}
-static bool attrs_differ(HlAttrs a1, HlAttrs a2)
+static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb)
{
- return a1.foreground != a2.foreground || a1.background != a2.background
- || a1.bold != a2.bold || a1.italic != a2.italic
- || a1.undercurl != a2.undercurl || a1.underline != a2.underline
- || a1.reverse != a2.reverse;
+ if (rgb) {
+ // TODO(bfredl): when we start to support special color,
+ // rgb_sp_color must be added here
+ return a1.rgb_fg_color != a2.rgb_fg_color
+ || a1.rgb_bg_color != a2.rgb_bg_color
+ || a1.rgb_ae_attr != a2.rgb_ae_attr;
+ } else {
+ return a1.cterm_fg_color != a2.cterm_fg_color
+ || a1.cterm_bg_color != a2.cterm_bg_color
+ || a1.cterm_ae_attr != a2.cterm_ae_attr;
+ }
}
static void update_attrs(UI *ui, HlAttrs attrs)
{
TUIData *data = ui->data;
- if (!attrs_differ(attrs, data->print_attrs)) {
+ if (!attrs_differ(attrs, data->print_attrs, ui->rgb)) {
return;
}
data->print_attrs = attrs;
- unibi_out(ui, unibi_exit_attribute_mode);
UGrid *grid = &data->grid;
- int fg = attrs.foreground != -1 ? attrs.foreground : grid->fg;
- int bg = attrs.background != -1 ? attrs.background : grid->bg;
+ int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1);
+ if (fg == -1) {
+ fg = ui->rgb ? grid->clear_attrs.rgb_fg_color
+ : (grid->clear_attrs.cterm_fg_color - 1);
+ }
+
+ int bg = ui->rgb ? attrs.rgb_bg_color : (attrs.cterm_bg_color - 1);
+ if (bg == -1) {
+ bg = ui->rgb ? grid->clear_attrs.rgb_bg_color
+ : (grid->clear_attrs.cterm_bg_color - 1);
+ }
+ int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr;
+ bool bold = attr & HL_BOLD;
+ bool italic = attr & HL_ITALIC;
+ bool reverse = attr & HL_INVERSE;
+ bool standout = attr & HL_STANDOUT;
+ bool underline = attr & (HL_UNDERLINE), undercurl = attr & (HL_UNDERCURL);
+
+ if (unibi_get_str(data->ut, unibi_set_attributes)) {
+ if (bold || reverse || underline || undercurl) {
+ UNIBI_SET_NUM_VAR(data->params[0], 0); // standout
+ UNIBI_SET_NUM_VAR(data->params[1], underline || undercurl);
+ UNIBI_SET_NUM_VAR(data->params[2], reverse);
+ UNIBI_SET_NUM_VAR(data->params[3], 0); // blink
+ UNIBI_SET_NUM_VAR(data->params[4], 0); // dim
+ UNIBI_SET_NUM_VAR(data->params[5], bold);
+ UNIBI_SET_NUM_VAR(data->params[6], 0); // blank
+ UNIBI_SET_NUM_VAR(data->params[7], 0); // protect
+ UNIBI_SET_NUM_VAR(data->params[8], 0); // alternate character set
+ unibi_out(ui, unibi_set_attributes);
+ } else if (!data->default_attr) {
+ unibi_out(ui, unibi_exit_attribute_mode);
+ }
+ } else {
+ if (!data->default_attr) {
+ unibi_out(ui, unibi_exit_attribute_mode);
+ }
+ if (bold) {
+ unibi_out(ui, unibi_enter_bold_mode);
+ }
+ if (underline || undercurl) {
+ unibi_out(ui, unibi_enter_underline_mode);
+ }
+ if (standout) {
+ unibi_out(ui, unibi_enter_standout_mode);
+ }
+ if (reverse) {
+ unibi_out(ui, unibi_enter_reverse_mode);
+ }
+ }
+ if (italic) {
+ unibi_out(ui, unibi_enter_italics_mode);
+ }
if (ui->rgb) {
if (fg != -1) {
- data->params[0].i = (fg >> 16) & 0xff; // red
- data->params[1].i = (fg >> 8) & 0xff; // green
- data->params[2].i = fg & 0xff; // blue
- unibi_out(ui, data->unibi_ext.set_rgb_foreground);
+ UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red
+ UNIBI_SET_NUM_VAR(data->params[1], (fg >> 8) & 0xff); // green
+ UNIBI_SET_NUM_VAR(data->params[2], fg & 0xff); // blue
+ unibi_out_ext(ui, data->unibi_ext.set_rgb_foreground);
}
if (bg != -1) {
- data->params[0].i = (bg >> 16) & 0xff; // red
- data->params[1].i = (bg >> 8) & 0xff; // green
- data->params[2].i = bg & 0xff; // blue
- unibi_out(ui, data->unibi_ext.set_rgb_background);
+ UNIBI_SET_NUM_VAR(data->params[0], (bg >> 16) & 0xff); // red
+ UNIBI_SET_NUM_VAR(data->params[1], (bg >> 8) & 0xff); // green
+ UNIBI_SET_NUM_VAR(data->params[2], bg & 0xff); // blue
+ unibi_out_ext(ui, data->unibi_ext.set_rgb_background);
}
} else {
if (fg != -1) {
- data->params[0].i = fg;
+ UNIBI_SET_NUM_VAR(data->params[0], fg);
unibi_out(ui, unibi_set_a_foreground);
}
if (bg != -1) {
- data->params[0].i = bg;
+ UNIBI_SET_NUM_VAR(data->params[0], bg);
unibi_out(ui, unibi_set_a_background);
}
}
- if (attrs.bold) {
- unibi_out(ui, unibi_enter_bold_mode);
- }
- if (attrs.italic) {
- unibi_out(ui, unibi_enter_italics_mode);
- }
- if (attrs.underline || attrs.undercurl) {
- unibi_out(ui, unibi_enter_underline_mode);
- }
- if (attrs.reverse) {
- unibi_out(ui, unibi_enter_reverse_mode);
+ data->default_attr = fg == -1 && bg == -1
+ && !bold && !italic && !underline && !undercurl && !reverse;
+}
+
+static void final_column_wrap(UI *ui)
+{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+ if (grid->col == ui->width) {
+ grid->col = 0;
+ if (grid->row < MIN(ui->height, grid->height - 1)) {
+ grid->row++;
+ }
}
}
+/// It is undocumented, but in the majority of terminals and terminal emulators
+/// printing at the right margin does not cause an automatic wrap until the
+/// next character is printed, holding the cursor in place until then.
static void print_cell(UI *ui, UCell *ptr)
{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+ if (!data->immediate_wrap_after_last_column) {
+ // Printing the next character finally advances the cursor.
+ final_column_wrap(ui);
+ }
update_attrs(ui, ptr->attrs);
out(ui, ptr->data, strlen(ptr->data));
+ grid->col++;
+ if (data->immediate_wrap_after_last_column) {
+ // Printing at the right margin immediately advances the cursor.
+ final_column_wrap(ui);
+ }
+}
+
+static bool cheap_to_print(UI *ui, int row, int col, int next)
+{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+ UCell *cell = grid->cells[row] + col;
+ while (next) {
+ next--;
+ if (attrs_differ(cell->attrs, data->print_attrs, ui->rgb)) {
+ if (data->default_attr) {
+ return false;
+ }
+ }
+ if (strlen(cell->data) > 1) {
+ return false;
+ }
+ cell++;
+ }
+ return true;
+}
+
+/// This optimizes several cases where it is cheaper to do something other
+/// than send a full cursor positioning control sequence. However, there are
+/// some further optimizations that may seem obvious but that will not work.
+///
+/// We cannot use VT (ASCII 0/11) for moving the cursor up, because VT means
+/// move the cursor down on a DEC terminal. Similarly, on a DEC terminal FF
+/// (ASCII 0/12) means the same thing and does not mean home. VT, CVT, and
+/// TAB also stop at software-defined tabulation stops, not at a fixed set
+/// of row/column positions.
+static void cursor_goto(UI *ui, int row, int col)
+{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+ if (row == grid->row && col == grid->col) {
+ return;
+ }
+ if (0 == row && 0 == col) {
+ unibi_out(ui, unibi_cursor_home);
+ ugrid_goto(grid, row, col);
+ return;
+ }
+ if (0 == col ? col != grid->col :
+ row != grid->row ? false :
+ 1 == col ? 2 < grid->col && cheap_to_print(ui, grid->row, 0, col) :
+ 2 == col ? 5 < grid->col && cheap_to_print(ui, grid->row, 0, col) :
+ false) {
+ // Motion to left margin from anywhere else, or CR + printing chars is
+ // even less expensive than using BSes or CUB.
+ unibi_out(ui, unibi_carriage_return);
+ ugrid_goto(grid, grid->row, 0);
+ } else if (col > grid->col) {
+ int n = col - grid->col;
+ if (n <= (row == grid->row ? 4 : 2)
+ && cheap_to_print(ui, grid->row, grid->col, n)) {
+ UGRID_FOREACH_CELL(grid, grid->row, grid->row,
+ grid->col, col - 1, {
+ print_cell(ui, cell);
+ });
+ }
+ }
+ if (row == grid->row) {
+ if (col < grid->col
+ // Deferred right margin wrap terminals have inconsistent ideas about
+ // where the cursor actually is during a deferred wrap. Relative
+ // motion calculations have OBOEs that cannot be compensated for,
+ // because two terminals that claim to be the same will implement
+ // different cursor positioning rules.
+ && (data->immediate_wrap_after_last_column || grid->col < ui->width)) {
+ int n = grid->col - col;
+ if (n <= 4) { // This might be just BS, so it is considered really cheap.
+ while (n--) {
+ unibi_out(ui, unibi_cursor_left);
+ }
+ } else {
+ UNIBI_SET_NUM_VAR(data->params[0], n);
+ unibi_out(ui, unibi_parm_left_cursor);
+ }
+ ugrid_goto(grid, row, col);
+ return;
+ } else if (col > grid->col) {
+ int n = col - grid->col;
+ if (n <= 2) {
+ while (n--) {
+ unibi_out(ui, unibi_cursor_right);
+ }
+ } else {
+ UNIBI_SET_NUM_VAR(data->params[0], n);
+ unibi_out(ui, unibi_parm_right_cursor);
+ }
+ ugrid_goto(grid, row, col);
+ return;
+ }
+ }
+ if (col == grid->col) {
+ if (row > grid->row) {
+ int n = row - grid->row;
+ if (n <= 4) { // This might be just LF, so it is considered really cheap.
+ while (n--) {
+ unibi_out(ui, unibi_cursor_down);
+ }
+ } else {
+ UNIBI_SET_NUM_VAR(data->params[0], n);
+ unibi_out(ui, unibi_parm_down_cursor);
+ }
+ ugrid_goto(grid, row, col);
+ return;
+ } else if (row < grid->row) {
+ int n = grid->row - row;
+ if (n <= 2) {
+ while (n--) {
+ unibi_out(ui, unibi_cursor_up);
+ }
+ } else {
+ UNIBI_SET_NUM_VAR(data->params[0], n);
+ unibi_out(ui, unibi_parm_up_cursor);
+ }
+ ugrid_goto(grid, row, col);
+ return;
+ }
+ }
+ unibi_goto(ui, row, col);
+ ugrid_goto(grid, row, col);
}
static void clear_region(UI *ui, int top, int bot, int left, int right)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
+ int saved_row = grid->row;
+ int saved_col = grid->col;
bool cleared = false;
- if (grid->bg == -1 && right == ui->width -1) {
+ bool nobg = ui->rgb ? grid->clear_attrs.rgb_bg_color == -1
+ : grid->clear_attrs.cterm_bg_color == 0;
+ if (nobg && right == ui->width -1) {
// Background is set to the default color and the right edge matches the
// screen end, try to use terminal codes for clearing the requested area.
- HlAttrs clear_attrs = EMPTY_ATTRS;
- clear_attrs.foreground = grid->fg;
- clear_attrs.background = grid->bg;
- update_attrs(ui, clear_attrs);
+ update_attrs(ui, grid->clear_attrs);
if (left == 0) {
if (bot == ui->height - 1) {
if (top == 0) {
unibi_out(ui, unibi_clear_screen);
+ ugrid_goto(&data->grid, top, left);
} else {
- unibi_goto(ui, top, 0);
+ cursor_goto(ui, top, 0);
unibi_out(ui, unibi_clr_eos);
}
cleared = true;
@@ -415,8 +704,8 @@ static void clear_region(UI *ui, int top, int bot, int left, int right)
if (!cleared) {
// iterate through each line and clear with clr_eol
- for (int row = top; row <= bot; ++row) {
- unibi_goto(ui, row, left);
+ for (int row = top; row <= bot; row++) {
+ cursor_goto(ui, row, left);
unibi_out(ui, unibi_clr_eol);
}
cleared = true;
@@ -425,18 +714,14 @@ static void clear_region(UI *ui, int top, int bot, int left, int right)
if (!cleared) {
// could not clear using faster terminal codes, refresh the whole region
- int currow = -1;
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
- if (currow != row) {
- unibi_goto(ui, row, col);
- currow = row;
- }
+ cursor_goto(ui, row, col);
print_cell(ui, cell);
});
}
// restore cursor
- unibi_goto(ui, grid->row, grid->col);
+ cursor_goto(ui, saved_row, saved_col);
}
static bool can_use_scroll(UI * ui)
@@ -456,19 +741,19 @@ static void set_scroll_region(UI *ui)
TUIData *data = ui->data;
UGrid *grid = &data->grid;
- data->params[0].i = grid->top;
- data->params[1].i = grid->bot;
+ UNIBI_SET_NUM_VAR(data->params[0], grid->top);
+ UNIBI_SET_NUM_VAR(data->params[1], grid->bot);
unibi_out(ui, unibi_change_scroll_region);
if (grid->left != 0 || grid->right != ui->width - 1) {
- unibi_out(ui, data->unibi_ext.enable_lr_margin);
+ unibi_out_ext(ui, data->unibi_ext.enable_lr_margin);
if (data->can_set_lr_margin) {
- data->params[0].i = grid->left;
- data->params[1].i = grid->right;
+ UNIBI_SET_NUM_VAR(data->params[0], grid->left);
+ UNIBI_SET_NUM_VAR(data->params[1], grid->right);
unibi_out(ui, unibi_set_lr_margin);
} else {
- data->params[0].i = grid->left;
+ UNIBI_SET_NUM_VAR(data->params[0], grid->left);
unibi_out(ui, unibi_set_left_margin_parm);
- data->params[0].i = grid->right;
+ UNIBI_SET_NUM_VAR(data->params[0], grid->right);
unibi_out(ui, unibi_set_right_margin_parm);
}
}
@@ -481,24 +766,24 @@ static void reset_scroll_region(UI *ui)
UGrid *grid = &data->grid;
if (0 <= data->unibi_ext.reset_scroll_region) {
- unibi_out(ui, data->unibi_ext.reset_scroll_region);
+ unibi_out_ext(ui, data->unibi_ext.reset_scroll_region);
} else {
- data->params[0].i = 0;
- data->params[1].i = ui->height - 1;
+ UNIBI_SET_NUM_VAR(data->params[0], 0);
+ UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1);
unibi_out(ui, unibi_change_scroll_region);
}
if (grid->left != 0 || grid->right != ui->width - 1) {
if (data->can_set_lr_margin) {
- data->params[0].i = 0;
- data->params[1].i = ui->width - 1;
+ UNIBI_SET_NUM_VAR(data->params[0], 0);
+ UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1);
unibi_out(ui, unibi_set_lr_margin);
} else {
- data->params[0].i = 0;
+ UNIBI_SET_NUM_VAR(data->params[0], 0);
unibi_out(ui, unibi_set_left_margin_parm);
- data->params[0].i = ui->width - 1;
+ UNIBI_SET_NUM_VAR(data->params[0], ui->width - 1);
unibi_out(ui, unibi_set_right_margin_parm);
}
- unibi_out(ui, data->unibi_ext.disable_lr_margin);
+ unibi_out_ext(ui, data->unibi_ext.disable_lr_margin);
}
unibi_goto(ui, grid->row, grid->col);
}
@@ -509,9 +794,9 @@ static void tui_resize(UI *ui, Integer width, Integer height)
ugrid_resize(&data->grid, (int)width, (int)height);
if (!got_winch) { // Try to resize the terminal window.
- data->params[0].i = (int)height;
- data->params[1].i = (int)width;
- unibi_out(ui, data->unibi_ext.resize_screen);
+ UNIBI_SET_NUM_VAR(data->params[0], (int)height);
+ UNIBI_SET_NUM_VAR(data->params[1], (int)width);
+ unibi_out_ext(ui, data->unibi_ext.resize_screen);
// DECSLPP does not reset the scroll region.
if (data->scroll_region_is_full_screen) {
reset_scroll_region(ui);
@@ -526,6 +811,7 @@ static void tui_clear(UI *ui)
TUIData *data = ui->data;
UGrid *grid = &data->grid;
ugrid_clear(grid);
+ kv_size(data->invalid_regions) = 0;
clear_region(ui, grid->top, grid->bot, grid->left, grid->right);
}
@@ -539,9 +825,7 @@ static void tui_eol_clear(UI *ui)
static void tui_cursor_goto(UI *ui, Integer row, Integer col)
{
- TUIData *data = ui->data;
- ugrid_goto(&data->grid, (int)row, (int)col);
- unibi_goto(ui, (int)row, (int)col);
+ cursor_goto(ui, (int)row, (int)col);
}
CursorShape tui_cursor_decode_shape(const char *shape_str)
@@ -619,7 +903,7 @@ static void tui_mouse_on(UI *ui)
{
TUIData *data = ui->data;
if (!data->mouse_enabled) {
- unibi_out(ui, data->unibi_ext.enable_mouse);
+ unibi_out_ext(ui, data->unibi_ext.enable_mouse);
data->mouse_enabled = true;
}
}
@@ -628,7 +912,7 @@ static void tui_mouse_off(UI *ui)
{
TUIData *data = ui->data;
if (data->mouse_enabled) {
- unibi_out(ui, data->unibi_ext.disable_mouse);
+ unibi_out_ext(ui, data->unibi_ext.disable_mouse);
data->mouse_enabled = false;
}
}
@@ -641,51 +925,24 @@ static void tui_set_mode(UI *ui, ModeShape mode)
TUIData *data = ui->data;
cursorentry_T c = data->cursor_shapes[mode];
int shape = c.shape;
- unibi_var_t vars[26 + 26] = { { 0 } };
-
- // Support changing cursor shape on some popular terminals.
- const char *vte_version = os_getenv("VTE_VERSION");
if (c.id != 0 && ui->rgb) {
int attr = syn_id2attr(c.id);
if (attr > 0) {
- attrentry_T *aep = syn_cterm_attr2entry(attr);
- data->params[0].i = aep->rgb_bg_color;
- unibi_out(ui, data->unibi_ext.set_cursor_color);
+ HlAttrs *aep = syn_cterm_attr2entry(attr);
+ UNIBI_SET_NUM_VAR(data->params[0], aep->rgb_bg_color);
+ unibi_out_ext(ui, data->unibi_ext.set_cursor_color);
}
}
- if (data->term == kTermKonsole) {
- // Konsole uses a proprietary escape code to set the cursor shape
- // and does not support DECSCUSR.
- switch (shape) {
- case SHAPE_BLOCK: shape = 0; break;
- case SHAPE_VER: shape = 1; break;
- case SHAPE_HOR: shape = 2; break;
- default: WLOG("Unknown shape value %d", shape); break;
- }
- data->params[0].i = shape;
- data->params[1].i = (c.blinkon != 0);
-
- unibi_format(vars, vars + 26,
- TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"),
- data->params, out, ui, NULL, NULL);
- } else if (!vte_version || atoi(vte_version) >= 3900) {
- // Assume that the terminal supports DECSCUSR unless it is an
- // old VTE based terminal. This should not get wrapped for tmux,
- // which will handle it via its Ss/Se terminfo extension - usually
- // according to its terminal-overrides.
-
- switch (shape) {
- case SHAPE_BLOCK: shape = 1; break;
- case SHAPE_HOR: shape = 3; break;
- case SHAPE_VER: shape = 5; break;
- default: WLOG("Unknown shape value %d", shape); break;
- }
- data->params[0].i = shape + (int)(c.blinkon == 0);
- unibi_format(vars, vars + 26, "\x1b[%p1%d q",
- data->params, out, ui, NULL, NULL);
+ switch (shape) {
+ case SHAPE_BLOCK: shape = 1; break;
+ case SHAPE_HOR: shape = 3; break;
+ case SHAPE_VER: shape = 5; break;
+ default: WLOG("Unknown shape value %d", shape); break;
}
+ UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0));
+ unibi_out_ext(ui, data->unibi_ext.set_cursor_style);
}
/// @param mode editor mode
@@ -715,6 +972,8 @@ static void tui_scroll(UI *ui, Integer count)
ugrid_scroll(grid, (int)count, &clear_top, &clear_bot);
if (can_use_scroll(ui)) {
+ int saved_row = grid->row;
+ int saved_col = grid->col;
bool scroll_clears_to_current_colour =
unibi_get_bool(data->ut, unibi_back_color_erase);
@@ -722,27 +981,24 @@ static void tui_scroll(UI *ui, Integer count)
if (!data->scroll_region_is_full_screen) {
set_scroll_region(ui);
}
- unibi_goto(ui, grid->top, grid->left);
+ cursor_goto(ui, grid->top, grid->left);
// also set default color attributes or some terminals can become funny
if (scroll_clears_to_current_colour) {
- HlAttrs clear_attrs = EMPTY_ATTRS;
- clear_attrs.foreground = grid->fg;
- clear_attrs.background = grid->bg;
- update_attrs(ui, clear_attrs);
+ update_attrs(ui, grid->clear_attrs);
}
if (count > 0) {
if (count == 1) {
unibi_out(ui, unibi_delete_line);
} else {
- data->params[0].i = (int)count;
+ UNIBI_SET_NUM_VAR(data->params[0], (int)count);
unibi_out(ui, unibi_parm_delete_line);
}
} else {
if (count == -1) {
unibi_out(ui, unibi_insert_line);
} else {
- data->params[0].i = -(int)count;
+ UNIBI_SET_NUM_VAR(data->params[0], -(int)count);
unibi_out(ui, unibi_parm_insert_line);
}
}
@@ -751,11 +1007,11 @@ static void tui_scroll(UI *ui, Integer count)
if (!data->scroll_region_is_full_screen) {
reset_scroll_region(ui);
}
- unibi_goto(ui, grid->row, grid->col);
+ cursor_goto(ui, saved_row, saved_col);
if (!scroll_clears_to_current_colour) {
- // This is required because scrolling will leave wrong background in the
- // cleared area on non-bge terminals.
+ // Scrolling will leave wrong background in the cleared area on non-BCE
+ // terminals. Update the cleared area.
clear_region(ui, clear_top, clear_bot, grid->left, grid->right);
}
} else {
@@ -772,7 +1028,15 @@ static void tui_highlight_set(UI *ui, HlAttrs attrs)
static void tui_put(UI *ui, String text)
{
TUIData *data = ui->data;
- print_cell(ui, ugrid_put(&data->grid, (uint8_t *)text.data, text.size));
+ UGrid *grid = &data->grid;
+ UCell *cell;
+
+ cell = ugrid_put(&data->grid, (uint8_t *)text.data, text.size);
+ // ugrid_put does not advance the cursor correctly, as the actual terminal
+ // will when we print. Its cursor motion model is simplistic and wrong. So
+ // we have to undo what it has just done before doing it right.
+ grid->col--;
+ print_cell(ui, cell);
}
static void tui_bell(UI *ui)
@@ -785,19 +1049,16 @@ static void tui_visual_bell(UI *ui)
unibi_out(ui, unibi_flash_screen);
}
-static void tui_update_fg(UI *ui, Integer fg)
-{
- ((TUIData *)ui->data)->grid.fg = (int)fg;
-}
-
-static void tui_update_bg(UI *ui, Integer bg)
+static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg,
+ Integer rgb_sp,
+ Integer cterm_fg, Integer cterm_bg)
{
- ((TUIData *)ui->data)->grid.bg = (int)bg;
-}
-
-static void tui_update_sp(UI *ui, Integer sp)
-{
- // Do nothing; 'special' color is for GUI only
+ UGrid *grid = &((TUIData *)ui->data)->grid;
+ grid->clear_attrs.rgb_fg_color = (int)rgb_fg;
+ grid->clear_attrs.rgb_bg_color = (int)rgb_bg;
+ grid->clear_attrs.rgb_sp_color = (int)rgb_sp;
+ grid->clear_attrs.cterm_fg_color = (int)cterm_fg;
+ grid->clear_attrs.cterm_bg_color = (int)cterm_bg;
}
static void tui_flush(UI *ui)
@@ -807,7 +1068,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
@@ -817,21 +1078,39 @@ static void tui_flush(UI *ui)
tui_busy_stop(ui); // avoid hidden cursor
}
+ int saved_row = grid->row;
+ int saved_col = grid->col;
+
while (kv_size(data->invalid_regions)) {
Rect r = kv_pop(data->invalid_regions);
- int currow = -1;
+ assert(r.bot < grid->height && r.right < grid->width);
UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, {
- if (currow != row) {
- unibi_goto(ui, row, col);
- currow = row;
- }
+ cursor_goto(ui, row, col);
print_cell(ui, cell);
});
}
- unibi_goto(ui, grid->row, grid->col);
+ cursor_goto(ui, saved_row, saved_col);
+
+ flush_buf(ui);
+}
- flush_buf(ui, true);
+/// Dumps termcap info to the messages area, if 'verbose' >= 3.
+static void show_termcap_event(void **argv)
+{
+ if (p_verbose < 3) {
+ return;
+ }
+ const unibi_term *const ut = argv[0];
+ if (!ut) {
+ abort();
+ }
+ verbose_enter();
+ // XXX: (future) if unibi_term is modified (e.g. after a terminal
+ // query-response) this is a race condition.
+ terminfo_info_msg(ut);
+ verbose_leave();
+ verbose_stop(); // flush now
}
#ifdef UNIX
@@ -849,6 +1128,7 @@ static void suspend_event(void **argv)
loop_poll_events(data->loop, -1);
}
tui_terminal_start(ui);
+ tui_terminal_after_startup(ui);
if (enable_mouse) {
tui_mouse_on(ui);
}
@@ -886,10 +1166,13 @@ static void tui_set_icon(UI *ui, String icon)
{
}
-// NB: if we start to use this, the ui_bridge must be updated
-// to make a copy for the tui thread
-static void tui_event(UI *ui, char *name, Array args, bool *args_consumed)
+static void tui_option_set(UI *ui, String name, Object value)
{
+ TUIData *data = ui->data;
+ if (strequal(name.data, "termguicolors")) {
+ ui->rgb = value.data.boolean;
+ invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1);
+ }
}
static void invalidate(UI *ui, int top, int bot, int left, int right)
@@ -978,39 +1261,61 @@ end:
static void unibi_goto(UI *ui, int row, int col)
{
TUIData *data = ui->data;
- data->params[0].i = row;
- data->params[1].i = col;
+ UNIBI_SET_NUM_VAR(data->params[0], row);
+ UNIBI_SET_NUM_VAR(data->params[1], col);
unibi_out(ui, unibi_cursor_address);
}
+#define UNIBI_OUT(fn) \
+ do { \
+ TUIData *data = ui->data; \
+ const char *str = NULL; \
+ if (unibi_index >= 0) { \
+ str = fn(data->ut, (unsigned)unibi_index); \
+ } \
+ if (str) { \
+ unibi_var_t vars[26 + 26]; \
+ size_t orig_pos = data->bufpos; \
+ \
+ memset(&vars, 0, sizeof(vars)); \
+ data->cork = true; \
+retry: \
+ unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); \
+ if (data->overflow) { \
+ data->bufpos = orig_pos; \
+ flush_buf(ui); \
+ goto retry; \
+ } \
+ data->cork = false; \
+ } \
+ } while (0)
static void unibi_out(UI *ui, int unibi_index)
{
- TUIData *data = ui->data;
-
- const char *str = NULL;
-
- if (unibi_index >= 0) {
- if (unibi_index < unibi_string_begin_) {
- str = unibi_get_ext_str(data->ut, (unsigned)unibi_index);
- } else {
- str = unibi_get_str(data->ut, (unsigned)unibi_index);
- }
- }
-
- if (str) {
- unibi_var_t vars[26 + 26] = {{0}};
- unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL);
- }
+ UNIBI_OUT(unibi_get_str);
+}
+static void unibi_out_ext(UI *ui, int unibi_index)
+{
+ UNIBI_OUT(unibi_get_ext_str);
}
+#undef UNIBI_OUT
static void out(void *ctx, const char *str, size_t len)
{
UI *ui = ctx;
TUIData *data = ui->data;
- size_t available = data->bufsize - data->bufpos;
+ size_t available = sizeof(data->buf) - data->bufpos;
+
+ if (data->cork && data->overflow) {
+ return;
+ }
if (len > available) {
- flush_buf(ui, false);
+ if (data->cork) {
+ data->overflow = true;
+ return;
+ } else {
+ flush_buf(ui);
+ }
}
memcpy(data->buf + data->bufpos, str, len);
@@ -1025,188 +1330,460 @@ static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str,
}
}
-static TermType detect_term(const char *term, const char *colorterm)
+static int unibi_find_ext_str(unibi_term *ut, const char *name)
{
- if (STARTS_WITH(term, "rxvt")) {
- return kTermRxvt;
- }
- if (os_getenv("KONSOLE_PROFILE_NAME") || os_getenv("KONSOLE_DBUS_SESSION")) {
- return kTermKonsole;
- }
- const char *termprg = os_getenv("TERM_PROGRAM");
- if (termprg && strstr(termprg, "iTerm.app")) {
- return kTermiTerm;
- }
- if (colorterm && strstr(colorterm, "gnome-terminal")) {
- return kTermGnome;
- }
- if (STARTS_WITH(term, "xterm")) {
- return kTermXTerm;
- }
- if (STARTS_WITH(term, "dtterm")) {
- return kTermDTTerm;
- }
- if (STARTS_WITH(term, "teraterm")) {
- return kTermTeraTerm;
+ size_t max = unibi_count_ext_str(ut);
+ for (size_t i = 0; i < max; i++) {
+ const char * n = unibi_get_ext_str_name(ut, i);
+ if (n && 0 == strcmp(n, name)) {
+ return (int)i;
+ }
}
- return kTermUnknown;
+ return -1;
}
-static void fix_terminfo(TUIData *data)
+/// Patches the terminfo records after loading from system or built-in db.
+/// Several entries in terminfo are known to be deficient or outright wrong;
+/// and several terminal emulators falsely announce incorrect terminal types.
+static void patch_terminfo_bugs(TUIData *data, const char *term,
+ const char *colorterm, long vte_version,
+ bool konsole, bool iterm_env)
{
unibi_term *ut = data->ut;
- is_tmux = os_getenv("TMUX") != NULL;
+ const char * xterm_version = os_getenv("XTERM_VERSION");
+#if 0 // We don't need to identify this specifically, for now.
+ bool roxterm = !!os_getenv("ROXTERM_ID");
+#endif
+ bool xterm = terminfo_is_term_family(term, "xterm");
+ bool linuxvt = terminfo_is_term_family(term, "linux");
+ bool rxvt = terminfo_is_term_family(term, "rxvt");
+ bool teraterm = terminfo_is_term_family(term, "teraterm");
+ bool putty = terminfo_is_term_family(term, "putty");
+ bool screen = terminfo_is_term_family(term, "screen");
+ bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
+ bool st = terminfo_is_term_family(term, "st");
+ bool gnome = terminfo_is_term_family(term, "gnome")
+ || terminfo_is_term_family(term, "vte");
+ bool iterm = terminfo_is_term_family(term, "iterm")
+ || terminfo_is_term_family(term, "iterm2")
+ || terminfo_is_term_family(term, "iTerm.app")
+ || terminfo_is_term_family(term, "iTerm2.app");
+ // None of the following work over SSH; see :help TERM .
+ bool iterm_pretending_xterm = xterm && iterm_env;
+ bool konsole_pretending_xterm = xterm && konsole;
+ bool gnome_pretending_xterm = xterm && colorterm
+ && strstr(colorterm, "gnome-terminal");
+ bool mate_pretending_xterm = xterm && colorterm
+ && strstr(colorterm, "mate-terminal");
+ bool true_xterm = xterm && !!xterm_version;
+
+ char *fix_normal = (char *)unibi_get_str(ut, unibi_cursor_normal);
+ if (fix_normal) {
+ if (STARTS_WITH(fix_normal, "\x1b[?12l")) {
+ // terminfo typically includes DECRST 12 as part of setting up the
+ // normal cursor, which interferes with the user's control via
+ // set_cursor_style. When DECRST 12 is present, skip over it, but honor
+ // the rest of the cnorm setting.
+ fix_normal += sizeof "\x1b[?12l" - 1;
+ unibi_set_str(ut, unibi_cursor_normal, fix_normal);
+ }
+ if (linuxvt
+ && strlen(fix_normal) >= (sizeof LINUXSET0C - 1)
+ && !memcmp(strchr(fix_normal, 0) - (sizeof LINUXSET0C - 1),
+ LINUXSET0C, sizeof LINUXSET0C - 1)) {
+ // The Linux terminfo entry similarly includes a Linux-idiosyncractic
+ // cursor shape reset in cnorm, which similarly interferes with
+ // set_cursor_style.
+ fix_normal[strlen(fix_normal) - (sizeof LINUXSET0C - 1)] = 0;
+ }
+ }
+ char *fix_invisible = (char *)unibi_get_str(ut, unibi_cursor_invisible);
+ if (fix_invisible) {
+ if (linuxvt
+ && strlen(fix_invisible) >= (sizeof LINUXSET1C - 1)
+ && !memcmp(strchr(fix_invisible, 0) - (sizeof LINUXSET1C - 1),
+ LINUXSET1C, sizeof LINUXSET1C - 1)) {
+ // The Linux terminfo entry similarly includes a Linux-idiosyncractic
+ // cursor shape reset in cinvis, which similarly interferes with
+ // set_cursor_style.
+ fix_invisible[strlen(fix_invisible) - (sizeof LINUXSET1C - 1)] = 0;
+ }
+ }
- const char *term = os_getenv("TERM");
- const char *colorterm = os_getenv("COLORTERM");
- if (!term) {
- goto end;
+ if (!true_xterm) {
+ // Cannot trust terminfo; safer to disable BCE. #7624
+ unibi_set_bool(ut, unibi_back_color_erase, false);
}
- data->term = detect_term(term, colorterm);
- if (data->term == kTermRxvt) {
- unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[m\x1b(B");
- unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<20/>\x1b[?5l");
+ if (xterm) {
+ // Termit, LXTerminal, GTKTerm2, GNOME Terminal, MATE Terminal, roxterm,
+ // and EvilVTE falsely claim to be xterm and do not support important xterm
+ // control sequences that we use. In an ideal world, these would have
+ // their own terminal types and terminfo entries, like PuTTY does, and not
+ // claim to be xterm. Or they would mimic xterm properly enough to be
+ // treatable as xterm.
+
+ // 2017-04 terminfo.src lacks these. genuine Xterm has them, as have
+ // the false claimants.
+ unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
+ unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
+ unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+
+ if (true_xterm) {
+ // 2017-04 terminfo.src lacks these. genuine Xterm has them.
+ unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds");
+ unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds");
+ unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds");
+ }
+ if (true_xterm
+ || iterm_pretending_xterm
+ || gnome_pretending_xterm
+ || konsole_pretending_xterm) {
+ // Apple's outdated copy of terminfo.src for MacOS lacks these.
+ // genuine Xterm and three false claimants have them.
+ unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
+ }
+ } else if (rxvt) {
+ // 2017-04 terminfo.src lacks these. Unicode rxvt has them.
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2");
- } else if (data->term == kTermXTerm) {
- unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
- } else if (STARTS_WITH(term, "screen") || STARTS_WITH(term, "tmux")) {
+ unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
+ // 2017-04 terminfo.src has older control sequences.
+ unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h");
+ unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l");
+ } else if (screen) {
+ // per the screen manual; 2017-04 terminfo.src lacks these.
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
- }
-
- if (data->term == kTermXTerm || data->term == kTermRxvt) {
- const char *normal = unibi_get_str(ut, unibi_cursor_normal);
- if (!normal) {
- unibi_set_str(ut, unibi_cursor_normal, "\x1b[?25h");
- } else if (STARTS_WITH(normal, "\x1b[?12l")) {
- // terminfo typically includes DECRST 12 as part of setting up the normal
- // cursor, which interferes with the user's control via
- // NVIM_TUI_ENABLE_CURSOR_SHAPE. When DECRST 12 is present, skip over
- // it, but honor the rest of the TI setting.
- unibi_set_str(ut, unibi_cursor_normal, normal + strlen("\x1b[?12l"));
- }
- unibi_set_if_empty(ut, unibi_cursor_invisible, "\x1b[?25l");
- unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<100/>\x1b[?5l");
- unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m");
+ } else if (tmux) {
+ unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
+ unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
+ } else if (terminfo_is_term_family(term, "interix")) {
+ // 2017-04 terminfo.src lacks this.
+ unibi_set_if_empty(ut, unibi_carriage_return, "\x0d");
+ } else if (linuxvt) {
+ // Apple's outdated copy of terminfo.src for MacOS lacks these.
+ unibi_set_if_empty(ut, unibi_parm_up_cursor, "\x1b[%p1%dA");
+ unibi_set_if_empty(ut, unibi_parm_down_cursor, "\x1b[%p1%dB");
+ unibi_set_if_empty(ut, unibi_parm_right_cursor, "\x1b[%p1%dC");
+ unibi_set_if_empty(ut, unibi_parm_left_cursor, "\x1b[%p1%dD");
+ } else if (putty) {
+ // No bugs in the vanilla terminfo for our purposes.
+ } else if (iterm) {
+ // 2017-04 terminfo.src has older control sequences.
+ unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h");
+ unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l");
+ // 2017-04 terminfo.src lacks these.
unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
- unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds");
- unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds");
- unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds");
- unibi_set_if_empty(ut, unibi_change_scroll_region, "\x1b[%i%p1%d;%p2%dr");
- unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[2J");
- unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
- unibi_set_bool(ut, unibi_back_color_erase, true);
+ unibi_set_if_empty(ut, unibi_orig_pair, "\x1b[39;49m");
+ unibi_set_if_empty(ut, unibi_enter_dim_mode, "\x1b[2m");
+ unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
+ unibi_set_if_empty(ut, unibi_exit_underline_mode, "\x1b[24m");
+ unibi_set_if_empty(ut, unibi_exit_standout_mode, "\x1b[27m");
+ } else if (st) {
+ // No bugs in the vanilla terminfo for our purposes.
}
- data->unibi_ext.enable_lr_margin = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[?69h");
- data->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[?69l");
-
- data->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[?2004h");
- data->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[?2004l");
+// At this time (2017-07-12) it seems like all terminals that support 256
+// color codes can use semicolons in the terminal code and be fine.
+// However, this is not correct according to the spec. So to reward those
+// terminals that also support colons, we output the code that way on these
+// specific ones.
- data->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[?1004h");
- data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[?1004l");
+// using colons like ISO 8613-6:1994/ITU T.416:1993 says.
+#define XTERM_SETAF_256_COLON \
+ "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m"
+#define XTERM_SETAB_256_COLON \
+ "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m"
-#define XTERM_SETAF \
+#define XTERM_SETAF_256 \
"\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"
-#define XTERM_SETAB \
+#define XTERM_SETAB_256 \
"\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"
+#define XTERM_SETAF_16 \
+ "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e39%;m"
+#define XTERM_SETAB_16 \
+ "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m"
+
+ // Terminals with 256-colour SGR support despite what terminfo says.
+ if (unibi_get_num(ut, unibi_max_colors) < 256) {
+ // See http://fedoraproject.org/wiki/Features/256_Color_Terminals
+ if (true_xterm || iterm || iterm_pretending_xterm) {
+ unibi_set_num(ut, unibi_max_colors, 256);
+ unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256_COLON);
+ unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256_COLON);
+ } else if (konsole || xterm || gnome || rxvt || st || putty
+ || linuxvt // Linux 4.8+ supports 256-colour SGR.
+ || mate_pretending_xterm || gnome_pretending_xterm
+ || tmux
+ || (colorterm && strstr(colorterm, "256"))
+ || (term && strstr(term, "256"))) {
+ unibi_set_num(ut, unibi_max_colors, 256);
+ unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256);
+ unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256);
+ }
+ }
+ // Terminals with 16-colour SGR support despite what terminfo says.
+ if (unibi_get_num(ut, unibi_max_colors) < 16) {
+ if (colorterm) {
+ unibi_set_num(ut, unibi_max_colors, 16);
+ unibi_set_if_empty(ut, unibi_set_a_foreground, XTERM_SETAF_16);
+ unibi_set_if_empty(ut, unibi_set_a_background, XTERM_SETAB_16);
+ }
+ }
- if ((colorterm && strstr(colorterm, "256"))
- || STARTS_WITH(term, "linux")
- || strstr(term, "256")
- || strstr(term, "xterm")) {
- // Linux 4.8+ supports 256-color SGR, but terminfo has 8-color setaf/setab.
- // Assume TERM=~xterm|linux or COLORTERM=~256 supports 256 colors.
- unibi_set_num(ut, unibi_max_colors, 256);
- unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF);
- unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB);
+ // Blacklist of terminals that cannot be trusted to report DECSCUSR support.
+ if (!(st || (vte_version != 0 && vte_version < 3900) || konsole)) {
+ data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se");
+ data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss");
}
+ // Dickey ncurses terminfo includes Ss/Se capabilities since 2011-07-14. So
+ // adding them to terminal types, that have such control sequences but lack
+ // the correct terminfo entries, is a fixup, not an augmentation.
+ if (-1 == data->unibi_ext.set_cursor_style) {
+ // DECSCUSR (cursor shape) sequence is widely supported by several terminal
+ // types. https://github.com/gnachman/iTerm2/pull/92
+ // xterm extension: vertical bar
+ if (!konsole
+ && ((xterm && !vte_version) // anything claiming xterm compat
+ // per MinTTY 0.4.3-1 release notes from 2009
+ || putty
+ // per https://bugzilla.gnome.org/show_bug.cgi?id=720821
+ || (vte_version >= 3900)
+ || tmux // per tmux manual page
+ // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
+ || screen
+ || st // #7641
+ || rxvt // per command.C
+ // per analysis of VT100Terminal.m
+ || iterm || iterm_pretending_xterm
+ || teraterm // per TeraTerm "Supported Control Functions" doco
+ // Some linux-type terminals implement the xterm extension.
+ // Example: console-terminal-emulator from the nosh toolset.
+ || (linuxvt
+ && (xterm_version || (vte_version > 0) || colorterm)))) {
+ data->unibi_ext.set_cursor_style =
+ (int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q");
+ if (-1 == data->unibi_ext.reset_cursor_style) {
+ data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
+ "");
+ }
+ unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
+ "\x1b[ q");
+ } else if (linuxvt) {
+ // Linux uses an idiosyncratic escape code to set the cursor shape and
+ // does not support DECSCUSR.
+ // See http://linuxgazette.net/137/anonymous.html for more info
+ data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
+ "\x1b[?"
+ "%?"
+ // The parameter passed to Ss is the DECSCUSR parameter, so the
+ // terminal capability has to translate into the Linux idiosyncratic
+ // parameter.
+ //
+ // linuxvt only supports block and underline. It is also only
+ // possible to have a steady block (no steady underline)
+ "%p1%{2}%<" "%t%{8}" // blink block
+ "%e%p1%{2}%=" "%t%{112}" // steady block
+ "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block)
+ "%e%p1%{4}%=" "%t%{4}" // steady underline
+ "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline)
+ "%e%p1%{6}%=" "%t%{2}" // steady bar
+ "%e%{0}" // anything else
+ "%;" "%dc");
+ if (-1 == data->unibi_ext.reset_cursor_style) {
+ data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
+ "");
+ }
+ unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
+ "\x1b[?c");
+ } else if (konsole) {
+ // Konsole uses an idiosyncratic escape code to set the cursor shape and
+ // does not support DECSCUSR. This makes Konsole set up and apply a
+ // nonce profile, which has side-effects on temporary font resizing.
+ // In an ideal world, Konsole would just support DECSCUSR.
+ data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
+ TMUX_WRAP(tmux, "\x1b]50;CursorShape=%?"
+ "%p1%{3}%<" "%t%{0}" // block
+ "%e%p1%{5}%<" "%t%{2}" // underline
+ "%e%{1}" // everything else is bar
+ "%;%d;BlinkingCursorEnabled=%?"
+ "%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special,
+ "%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag.
+ "%;%d\x07"));
+ if (-1 == data->unibi_ext.reset_cursor_style) {
+ data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
+ "");
+ }
+ unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
+ "\x1b]50;\x07");
+ }
+ }
+}
+
+/// This adds stuff that is not in standard terminfo as extended unibilium
+/// capabilities.
+static void augment_terminfo(TUIData *data, const char *term,
+ const char *colorterm, long vte_version, bool konsole, bool iterm_env)
+{
+ unibi_term *ut = data->ut;
+ bool xterm = terminfo_is_term_family(term, "xterm");
+ bool dtterm = terminfo_is_term_family(term, "dtterm");
+ bool rxvt = terminfo_is_term_family(term, "rxvt");
+ bool teraterm = terminfo_is_term_family(term, "teraterm");
+ bool putty = terminfo_is_term_family(term, "putty");
+ bool screen = terminfo_is_term_family(term, "screen");
+ bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
+ bool iterm = terminfo_is_term_family(term, "iterm")
+ || terminfo_is_term_family(term, "iterm2")
+ || terminfo_is_term_family(term, "iTerm.app")
+ || terminfo_is_term_family(term, "iTerm2.app");
+ // None of the following work over SSH; see :help TERM .
+ bool iterm_pretending_xterm = xterm && iterm_env;
+
+ const char * xterm_version = os_getenv("XTERM_VERSION");
+ bool true_xterm = xterm && !!xterm_version;
+
// Only define this capability for terminal types that we know understand it.
- if (data->term == kTermDTTerm // originated this extension
- || data->term == kTermXTerm // per xterm ctlseqs doc
- || data->term == kTermKonsole // per commentary in VT102Emulation.cpp
- || data->term == kTermTeraTerm // per "Supported Control Functions" doc
- || data->term == kTermRxvt) { // per command.C
- data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut, NULL,
+ if (dtterm // originated this extension
+ || xterm // per xterm ctlseqs doco
+ || konsole // per commentary in VT102Emulation.cpp
+ || teraterm // per TeraTerm "Supported Control Functions" doco
+ || rxvt) { // per command.C
+ data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut,
+ "ext.resize_screen",
"\x1b[8;%p1%d;%p2%dt");
}
-
- if (data->term == kTermXTerm || data->term == kTermRxvt) {
- data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, NULL,
+ if (putty || xterm || rxvt) {
+ data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut,
+ "ext.reset_scroll_region",
"\x1b[r");
}
-end:
- // Fill some empty slots with common terminal strings
- if (data->term == kTermiTerm) {
+ // Dickey ncurses terminfo does not include the setrgbf and setrgbb
+ // capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15. Adding
+ // them here when terminfo lacks them is an augmentation, not a fixup.
+ // https://gist.github.com/XVilka/8346728
+
+ // At this time (2017-07-12) it seems like all terminals that support rgb
+ // color codes can use semicolons in the terminal code and be fine.
+ // However, this is not correct according to the spec. So to reward those
+ // terminals that also support colons, we output the code that way on these
+ // specific ones.
+
+ // can use colons like ISO 8613-6:1994/ITU T.416:1993 says.
+ bool has_colon_rgb = !tmux && !screen
+ && !vte_version // VTE colon-support has a big memory leak. #7573
+ && (iterm || iterm_pretending_xterm // per VT100Terminal.m
+ // per http://invisible-island.net/xterm/xterm.log.html#xterm_282
+ || true_xterm);
+
+ data->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf");
+ if (-1 == data->unibi_ext.set_rgb_foreground) {
+ if (has_colon_rgb) {
+ data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf",
+ "\x1b[38:2:%p1%d:%p2%d:%p3%dm");
+ } else {
+ data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf",
+ "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
+ }
+ }
+ data->unibi_ext.set_rgb_background = unibi_find_ext_str(ut, "setrgbb");
+ if (-1 == data->unibi_ext.set_rgb_background) {
+ if (has_colon_rgb) {
+ data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb",
+ "\x1b[48:2:%p1%d:%p2%d:%p3%dm");
+ } else {
+ data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb",
+ "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
+ }
+ }
+
+ if (iterm || iterm_pretending_xterm) {
+ // FIXME: Bypassing tmux like this affects the cursor colour globally, in
+ // all panes, which is not particularly desirable. A better approach
+ // would use a tmux control sequence and an extra if(screen) test.
data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(
- ut, NULL, TMUX_WRAP("\033]Pl%p1%06x\033\\"));
- } else {
+ ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
+ } else if (xterm || (vte_version != 0) || rxvt) {
+ // This seems to be supported for a long time in VTE
+ // urxvt also supports this
data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(
ut, NULL, "\033]12;#%p1%06x\007");
}
- data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL,
+
+ /// Terminals usually ignore unrecognized private modes, and there is no
+ /// known ambiguity with these. So we just set them unconditionally.
+ data->unibi_ext.enable_lr_margin = (int)unibi_add_ext_str(ut,
+ "ext.enable_lr_margin",
+ "\x1b[?69h");
+ data->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut,
+ "ext.disable_lr_margin",
+ "\x1b[?69l");
+ data->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut,
+ "ext.enable_bpaste",
+ "\x1b[?2004h");
+ data->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut,
+ "ext.disable_bpaste",
+ "\x1b[?2004l");
+ data->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut,
+ "ext.enable_focus",
+ rxvt ? "\x1b]777;focus;on\x7" : "\x1b[?1004h");
+ data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut,
+ "ext.disable_focus",
+ rxvt ? "\x1b]777;focus;off\x7" : "\x1b[?1004l");
+ data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut,
+ "ext.enable_mouse",
"\x1b[?1002h\x1b[?1006h");
- data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, NULL,
+ data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut,
+ "ext.disable_mouse",
"\x1b[?1002l\x1b[?1006l");
- data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
- data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, NULL,
- "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
- unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH");
- unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m");
- unibi_set_if_empty(ut, unibi_set_a_foreground, XTERM_SETAF);
- unibi_set_if_empty(ut, unibi_set_a_background, XTERM_SETAB);
- unibi_set_if_empty(ut, unibi_enter_bold_mode, "\x1b[1m");
- unibi_set_if_empty(ut, unibi_enter_underline_mode, "\x1b[4m");
- unibi_set_if_empty(ut, unibi_enter_reverse_mode, "\x1b[7m");
- unibi_set_if_empty(ut, unibi_bell, "\x07");
- unibi_set_if_empty(data->ut, unibi_enter_ca_mode, "\x1b[?1049h");
- unibi_set_if_empty(data->ut, unibi_exit_ca_mode, "\x1b[?1049l");
- unibi_set_if_empty(ut, unibi_delete_line, "\x1b[M");
- unibi_set_if_empty(ut, unibi_parm_delete_line, "\x1b[%p1%dM");
- unibi_set_if_empty(ut, unibi_insert_line, "\x1b[L");
- unibi_set_if_empty(ut, unibi_parm_insert_line, "\x1b[%p1%dL");
- unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[J");
- unibi_set_if_empty(ut, unibi_clr_eol, "\x1b[K");
- unibi_set_if_empty(ut, unibi_clr_eos, "\x1b[J");
-}
-
-static void flush_buf(UI *ui, bool toggle_cursor)
+}
+
+static void flush_buf(UI *ui)
{
uv_write_t req;
- uv_buf_t buf;
+ uv_buf_t bufs[3];
+ uv_buf_t *bufp = &bufs[0];
TUIData *data = ui->data;
- if (toggle_cursor && !data->busy) {
- // not busy and the cursor is invisible(see below). Append a "cursor
- // normal" command to the end of the buffer.
- data->bufsize += CNORM_COMMAND_MAX_SIZE;
- unibi_out(ui, unibi_cursor_normal);
- data->bufsize -= CNORM_COMMAND_MAX_SIZE;
+ if (data->bufpos <= 0 && data->busy == data->is_invisible) {
+ return;
}
- buf.base = data->buf;
- buf.len = data->bufpos;
- uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle), &buf, 1, NULL);
- uv_run(&data->write_loop, UV_RUN_DEFAULT);
- data->bufpos = 0;
+ if (!data->is_invisible) {
+ // cursor is visible. Write a "cursor invisible" command before writing the
+ // buffer.
+ bufp->base = data->invis;
+ bufp->len = UV_BUF_LEN(data->invislen);
+ bufp++;
+ data->is_invisible = true;
+ }
+
+ if (data->bufpos > 0) {
+ bufp->base = data->buf;
+ bufp->len = UV_BUF_LEN(data->bufpos);
+ bufp++;
+ }
- if (toggle_cursor && !data->busy) {
- // not busy and cursor is visible(see above), append a "cursor invisible"
- // command to the beginning of the buffer for the next flush
- unibi_out(ui, unibi_cursor_invisible);
+ if (!data->busy && data->is_invisible) {
+ // not busy and the cursor is invisible. Write a "cursor normal" command
+ // after writing the buffer.
+ bufp->base = data->norm;
+ bufp->len = UV_BUF_LEN(data->normlen);
+ bufp++;
+ data->is_invisible = data->busy;
}
+
+ uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle),
+ bufs, (unsigned)(bufp - bufs), NULL);
+ uv_run(&data->write_loop, UV_RUN_DEFAULT);
+ data->bufpos = 0;
+ data->overflow = false;
}
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
@@ -1223,7 +1800,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;
@@ -1240,16 +1817,22 @@ 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;
}
+ } else if (strequal(name, "key_mouse")) {
+ DLOG("libtermkey:kmous=%s", value);
+ // If key_mouse is found, libtermkey uses its terminfo driver (driver-ti.c)
+ // for mouse input, which by accident only supports X10 protocol.
+ // Force libtermkey to fallback to its CSI driver (driver-csi.c). #7948
+ return NULL;
}
return value;
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index 7a0a16687e..6d420ef2f8 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -16,8 +16,8 @@
void ugrid_init(UGrid *grid)
{
- grid->attrs = EMPTY_ATTRS;
- grid->fg = grid->bg = -1;
+ grid->attrs = HLATTRS_INIT;
+ grid->clear_attrs = HLATTRS_INIT;
grid->cells = NULL;
}
@@ -107,6 +107,7 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
UCell *cell = grid->cells[grid->row] + grid->col;
cell->data[size] = 0;
cell->attrs = grid->attrs;
+ assert(size <= CELLBYTES);
if (text) {
memcpy(cell->data, text, size);
@@ -118,9 +119,7 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
static void clear_region(UGrid *grid, int top, int bot, int left, int right)
{
- HlAttrs clear_attrs = EMPTY_ATTRS;
- clear_attrs.foreground = grid->fg;
- clear_attrs.background = grid->bg;
+ HlAttrs clear_attrs = grid->clear_attrs;
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
cell->data[0] = ' ';
cell->data[1] = 0;
@@ -135,6 +134,7 @@ static void destroy_cells(UGrid *grid)
xfree(grid->cells[i]);
}
xfree(grid->cells);
+ grid->cells = NULL;
}
}
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index 268362bf1b..60c9068eb1 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -7,22 +7,22 @@
typedef struct ucell UCell;
typedef struct ugrid UGrid;
+#define CELLBYTES (4 * (MAX_MCO+1))
+
struct ucell {
- char data[6 * MAX_MCO + 1];
+ char data[CELLBYTES + 1];
HlAttrs attrs;
};
struct ugrid {
int top, bot, left, right;
int row, col;
- int bg, fg;
+ HlAttrs clear_attrs;
int width, height;
HlAttrs attrs;
UCell **cells;
};
-#define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 })
-
#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \
do { \
for (int row = top; row <= bot; row++) { \
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 0a2154438f..42366fdb76 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -8,11 +8,13 @@
#include <limits.h>
#include "nvim/vim.h"
+#include "nvim/log.h"
#include "nvim/ui.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_getln.h"
#include "nvim/fold.h"
#include "nvim/main.h"
#include "nvim/ascii.h"
@@ -47,18 +49,39 @@
#define MAX_UI_COUNT 16
static UI *uis[MAX_UI_COUNT];
-static bool ui_ext[UI_WIDGETS] = { 0 };
+static bool ui_ext[kUIExtCount] = { 0 };
static size_t ui_count = 0;
static int row = 0, col = 0;
static struct {
int top, bot, left, right;
} sr;
-static int current_attr_code = 0;
+static int current_attr_code = -1;
static bool pending_cursor_update = false;
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: ", NULL, -1, true, \
+ "%s (+%zu times...)", uilog_last_event, uilog_seen); \
+ } \
+ do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, 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 +90,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,15 +100,18 @@ 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__); \
} \
} while (0)
#endif
-#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
-#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
+#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, \
+ MORE, MORE, ZERO, ignore)
+#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, ...) a7
#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)
@@ -119,6 +146,9 @@ void ui_builtin_stop(void)
bool ui_rgb_attached(void)
{
+ if (!headless_mode && p_tgc) {
+ return true;
+ }
for (size_t i = 0; i < ui_count; i++) {
if (uis[i]->rgb) {
return true;
@@ -141,6 +171,67 @@ void ui_event(char *name, Array args)
}
}
+
+/// Converts an HlAttrs into Dictionary
+///
+/// @param[in] aep data to convert
+/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
+Dictionary hlattrs2dict(const HlAttrs *aep, bool use_rgb)
+{
+ assert(aep);
+ Dictionary hl = ARRAY_DICT_INIT;
+ int mask = use_rgb ? aep->rgb_ae_attr : aep->cterm_ae_attr;
+
+ if (mask & HL_BOLD) {
+ PUT(hl, "bold", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_STANDOUT) {
+ PUT(hl, "standout", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_UNDERLINE) {
+ PUT(hl, "underline", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_UNDERCURL) {
+ PUT(hl, "undercurl", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_ITALIC) {
+ PUT(hl, "italic", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_INVERSE) {
+ PUT(hl, "reverse", BOOLEAN_OBJ(true));
+ }
+
+
+ if (use_rgb) {
+ if (aep->rgb_fg_color != -1) {
+ PUT(hl, "foreground", INTEGER_OBJ(aep->rgb_fg_color));
+ }
+
+ if (aep->rgb_bg_color != -1) {
+ PUT(hl, "background", INTEGER_OBJ(aep->rgb_bg_color));
+ }
+
+ if (aep->rgb_sp_color != -1) {
+ PUT(hl, "special", INTEGER_OBJ(aep->rgb_sp_color));
+ }
+ } else {
+ if (cterm_normal_fg_color != aep->cterm_fg_color) {
+ PUT(hl, "foreground", INTEGER_OBJ(aep->cterm_fg_color - 1));
+ }
+
+ if (cterm_normal_bg_color != aep->cterm_bg_color) {
+ PUT(hl, "background", INTEGER_OBJ(aep->cterm_bg_color - 1));
+ }
+ }
+
+ return hl;
+}
+
void ui_refresh(void)
{
if (!ui_active()) {
@@ -153,8 +244,8 @@ void ui_refresh(void)
}
int width = INT_MAX, height = INT_MAX;
- bool ext_widgets[UI_WIDGETS];
- for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
+ bool ext_widgets[kUIExtCount];
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
ext_widgets[i] = true;
}
@@ -162,19 +253,27 @@ void ui_refresh(void)
UI *ui = uis[i];
width = MIN(ui->width, width);
height = MIN(ui->height, height);
- for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
ext_widgets[i] &= ui->ui_ext[i];
}
}
row = col = 0;
+
+ int save_p_lz = p_lz;
+ p_lz = false; // convince redrawing() to return true ...
screen_resize(width, height);
- for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
- ui_set_external(i, ext_widgets[i]);
+ p_lz = save_p_lz;
+
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ ui_ext[i] = ext_widgets[i];
+ ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]),
+ BOOLEAN_OBJ(ext_widgets[i]));
}
ui_mode_info_set();
old_mode_idx = -1;
ui_cursor_shape();
+ current_attr_code = -1;
}
static void ui_refresh_event(void **argv)
@@ -192,6 +291,11 @@ void ui_resize(int new_width, int new_height)
width = new_width;
height = new_height;
+ // TODO(bfredl): update default colors when they changed, NOT on resize.
+ ui_call_default_colors_set(normal_fg, normal_bg, normal_sp,
+ cterm_normal_fg_color, cterm_normal_bg_color);
+
+ // Deprecated:
UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1));
UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1));
UI_CALL(update_sp, (ui->rgb ? normal_sp : -1));
@@ -224,6 +328,7 @@ void ui_attach_impl(UI *ui)
}
uis[ui_count++] = ui;
+ ui_refresh_options();
ui_refresh();
}
@@ -284,26 +389,28 @@ void ui_reset_scroll_region(void)
ui_call_set_scroll_region(sr.top, sr.bot, sr.left, sr.right);
}
-void ui_start_highlight(int attr_code)
+void ui_set_highlight(int attr_code)
{
+ if (current_attr_code == attr_code) {
+ return;
+ }
current_attr_code = attr_code;
- if (!ui_count) {
- return;
+ HlAttrs attrs = HLATTRS_INIT;
+
+ if (attr_code != 0) {
+ HlAttrs *aep = syn_cterm_attr2entry(attr_code);
+ if (aep) {
+ attrs = *aep;
+ }
}
- set_highlight_args(current_attr_code);
+ UI_CALL(highlight_set, attrs);
}
-void ui_stop_highlight(void)
+void ui_clear_highlight(void)
{
- current_attr_code = HL_NORMAL;
-
- if (!ui_count) {
- return;
- }
-
- set_highlight_args(current_attr_code);
+ ui_set_highlight(0);
}
void ui_puts(uint8_t *str)
@@ -331,6 +438,12 @@ void ui_puts(uint8_t *str)
ui_linefeed();
}
p += clen;
+
+ if (p_wd) { // 'writedelay': flush & delay each time.
+ ui_flush();
+ uint64_t wd = (uint64_t)labs(p_wd);
+ os_delay(wd, false);
+ }
}
}
@@ -370,63 +483,10 @@ int ui_current_col(void)
void ui_flush(void)
{
+ cmdline_ui_flush();
ui_call_flush();
}
-static void set_highlight_args(int attr_code)
-{
- HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 };
- HlAttrs cterm_attrs = rgb_attrs;
-
- if (attr_code == HL_NORMAL) {
- goto end;
- }
-
- int rgb_mask = 0;
- int cterm_mask = 0;
- attrentry_T *aep = syn_cterm_attr2entry(attr_code);
-
- if (!aep) {
- goto end;
- }
-
- rgb_mask = aep->rgb_ae_attr;
- cterm_mask = aep->cterm_ae_attr;
-
- rgb_attrs.bold = rgb_mask & HL_BOLD;
- rgb_attrs.underline = rgb_mask & HL_UNDERLINE;
- rgb_attrs.undercurl = rgb_mask & HL_UNDERCURL;
- rgb_attrs.italic = rgb_mask & HL_ITALIC;
- rgb_attrs.reverse = rgb_mask & (HL_INVERSE | HL_STANDOUT);
- cterm_attrs.bold = cterm_mask & HL_BOLD;
- cterm_attrs.underline = cterm_mask & HL_UNDERLINE;
- cterm_attrs.undercurl = cterm_mask & HL_UNDERCURL;
- cterm_attrs.italic = cterm_mask & HL_ITALIC;
- cterm_attrs.reverse = cterm_mask & (HL_INVERSE | HL_STANDOUT);
-
- if (aep->rgb_fg_color != normal_fg) {
- rgb_attrs.foreground = aep->rgb_fg_color;
- }
-
- if (aep->rgb_bg_color != normal_bg) {
- rgb_attrs.background = aep->rgb_bg_color;
- }
-
- if (aep->rgb_sp_color != normal_sp) {
- rgb_attrs.special = aep->rgb_sp_color;
- }
-
- if (cterm_normal_fg_color != aep->cterm_fg_color) {
- cterm_attrs.foreground = aep->cterm_fg_color - 1;
- }
-
- if (cterm_normal_bg_color != aep->cterm_bg_color) {
- cterm_attrs.background = aep->cterm_bg_color - 1;
- }
-
-end:
- UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs));
-}
void ui_linefeed(void)
{
@@ -466,15 +526,23 @@ void ui_cursor_shape(void)
}
/// Returns true if `widget` is externalized.
-bool ui_is_external(UIWidget widget)
+bool ui_is_external(UIExtension widget)
{
return ui_ext[widget];
}
-/// Sets `widget` as "external".
-/// Such widgets are not drawn by Nvim; external UIs are expected to handle
-/// higher-level UI events and present the data.
-void ui_set_external(UIWidget widget, bool external)
+Array ui_array(void)
{
- ui_ext[widget] = external;
+ Array all_uis = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < ui_count; i++) {
+ Dictionary dic = ARRAY_DICT_INIT;
+ PUT(dic, "width", INTEGER_OBJ(uis[i]->width));
+ PUT(dic, "height", INTEGER_OBJ(uis[i]->height));
+ PUT(dic, "rgb", BOOLEAN_OBJ(uis[i]->rgb));
+ for (UIExtension j = 0; j < kUIExtCount; j++) {
+ PUT(dic, ui_ext_names[j], BOOLEAN_OBJ(uis[i]->ui_ext[j]));
+ }
+ ADD(all_uis, DICTIONARY_OBJ(dic));
+ }
+ return all_uis;
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 064f77fee1..48896a6a3f 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -13,26 +13,27 @@ typedef enum {
kUIPopupmenu,
kUITabline,
kUIWildmenu,
-} UIWidget;
-#define UI_WIDGETS (kUIWildmenu + 1)
+ kUIExtCount,
+} UIExtension;
+
+EXTERN const char *ui_ext_names[] INIT(= {
+ "ext_cmdline",
+ "ext_popupmenu",
+ "ext_tabline",
+ "ext_wildmenu"
+});
-typedef struct {
- bool bold, underline, undercurl, italic, reverse;
- int foreground, background, special;
-} HlAttrs;
typedef struct ui_t UI;
struct ui_t {
bool rgb;
- bool ui_ext[UI_WIDGETS]; ///< Externalized widgets
+ bool ui_ext[kUIExtCount]; ///< Externalized widgets
int width, height;
void *data;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events.generated.h"
#endif
-
void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
void (*stop)(UI *ui);
};
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 0165db7c0c..56e0c0c454 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -24,30 +24,10 @@
#define UI(b) (((UIBridgeData *)b)->ui)
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
-static size_t uilog_seen = 0;
-static argv_callback uilog_event = NULL;
-#define UI_CALL(ui, name, argc, ...) \
- do { \
- if (uilog_event == ui_bridge_##name##_event) { \
- uilog_seen++; \
- } else { \
- if (uilog_seen > 0) { \
- DLOG("UI bridge: ...%zu times", uilog_seen); \
- } \
- DLOG("UI bridge: " STR(name)); \
- uilog_seen = 0; \
- uilog_event = ui_bridge_##name##_event; \
- } \
- ((UIBridgeData *)ui)->scheduler( \
- event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \
- } while (0)
-#else
// Schedule a function call on the UI bridge thread.
-#define UI_CALL(ui, name, argc, ...) \
+#define UI_BRIDGE_CALL(ui, name, argc, ...) \
((UIBridgeData *)ui)->scheduler( \
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
-#endif
#define INT2PTR(i) ((void *)(intptr_t)i)
#define PTR2INT(p) ((Integer)(intptr_t)p)
@@ -79,16 +59,15 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.put = ui_bridge_put;
rv->bridge.bell = ui_bridge_bell;
rv->bridge.visual_bell = ui_bridge_visual_bell;
- rv->bridge.update_fg = ui_bridge_update_fg;
- rv->bridge.update_bg = ui_bridge_update_bg;
- rv->bridge.update_sp = ui_bridge_update_sp;
+ rv->bridge.default_colors_set = ui_bridge_default_colors_set;
rv->bridge.flush = ui_bridge_flush;
rv->bridge.suspend = ui_bridge_suspend;
rv->bridge.set_title = ui_bridge_set_title;
rv->bridge.set_icon = ui_bridge_set_icon;
+ rv->bridge.option_set = ui_bridge_option_set;
rv->scheduler = scheduler;
- for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
rv->bridge.ui_ext[i] = ui->ui_ext[i];
}
@@ -102,6 +81,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
abort();
}
+ // Suspend the main thread until CONTINUE is called by the UI thread.
while (!rv->ready) {
uv_cond_wait(&rv->cond, &rv->mutex);
}
@@ -126,9 +106,12 @@ static void ui_thread_run(void *data)
static void ui_bridge_stop(UI *b)
{
+ // Detach brigde first, so that "stop" is the last event the TUI loop
+ // receives from the main thread. #8041
+ ui_detach_impl(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;
@@ -136,12 +119,12 @@ static void ui_bridge_stop(UI *b)
if (stopped) {
break;
}
- loop_poll_events(&main_loop, 10);
+ loop_poll_events(&main_loop, 10); // Process one event.
}
uv_thread_join(&bridge->ui_thread);
uv_mutex_destroy(&bridge->mutex);
uv_cond_destroy(&bridge->cond);
- ui_detach_impl(b);
+ xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922
xfree(b);
}
static void ui_bridge_stop_event(void **argv)
@@ -154,7 +137,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,9 +150,9 @@ 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
+ // Suspend the main thread until CONTINUE is called by the UI thread.
while (!data->ready) {
uv_cond_wait(&data->cond, &data->mutex);
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 81af3bfda9..35857510fc 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -1838,11 +1838,9 @@ void undo_time(long step, int sec, int file, int absolute)
}
closest = -1;
} else {
- /* When doing computations with time_t subtract starttime, because
- * time_t converted to a long may result in a wrong number. */
- if (dosec)
- target = (long)(curbuf->b_u_time_cur - starttime) + step;
- else if (dofile) {
+ if (dosec) {
+ target = (long)(curbuf->b_u_time_cur) + step;
+ } else if (dofile) {
if (step < 0) {
/* Going back to a previous write. If there were changes after
* the last write, count that as moving one file-write, so
@@ -1880,14 +1878,16 @@ void undo_time(long step, int sec, int file, int absolute)
target = 0;
closest = -1;
} else {
- if (dosec)
- closest = (long)(time(NULL) - starttime + 1);
- else if (dofile)
+ if (dosec) {
+ closest = (long)(os_time() + 1);
+ } else if (dofile) {
closest = curbuf->b_u_save_nr_last + 2;
- else
+ } else {
closest = curbuf->b_u_seq_last + 2;
- if (target >= closest)
+ }
+ if (target >= closest) {
target = closest - 1;
+ }
}
}
closest_start = closest;
@@ -1916,12 +1916,13 @@ void undo_time(long step, int sec, int file, int absolute)
while (uhp != NULL) {
uhp->uh_walk = mark;
- if (dosec)
- val = (long)(uhp->uh_time - starttime);
- else if (dofile)
+ if (dosec) {
+ val = (long)(uhp->uh_time);
+ } else if (dofile) {
val = uhp->uh_save_nr;
- else
+ } else {
val = uhp->uh_seq;
+ }
if (round == 1 && !(dofile && val == 0)) {
/* Remember the header that is closest to the target.
@@ -2095,8 +2096,8 @@ void undo_time(long step, int sec, int file, int absolute)
uhp = uhp->uh_prev.ptr;
if (uhp == NULL || uhp->uh_walk != mark) {
- /* Need to redo more but can't find it... */
- EMSG2(_(e_intern2), "undo_time()");
+ // Need to redo more but can't find it...
+ internal_error("undo_time()");
break;
}
}
@@ -2162,8 +2163,8 @@ static void u_undoredo(int undo)
if (top > curbuf->b_ml.ml_line_count || top >= bot
|| bot > curbuf->b_ml.ml_line_count + 1) {
unblock_autocmds();
- EMSG(_("E438: u_undo: line numbers wrong"));
- changed(); /* don't want UNCHANGED now */
+ IEMSG(_("E438: u_undo: line numbers wrong"));
+ changed(); // don't want UNCHANGED now
return;
}
@@ -2267,12 +2268,14 @@ static void u_undoredo(int undo)
curhead->uh_entry = newlist;
curhead->uh_flags = new_flags;
- if ((old_flags & UH_EMPTYBUF) && bufempty())
+ if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) {
curbuf->b_ml.ml_flags |= ML_EMPTY;
- if (old_flags & UH_CHANGED)
+ }
+ if (old_flags & UH_CHANGED) {
changed();
- else
+ } else {
unchanged(curbuf, FALSE);
+ }
/*
* restore marks from before undo/redo
@@ -2654,7 +2657,7 @@ static void u_unch_branch(u_header_T *uhp)
static u_entry_T *u_get_headentry(void)
{
if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL) {
- EMSG(_("E439: undo list corrupt"));
+ IEMSG(_("E439: undo list corrupt"));
return NULL;
}
return curbuf->b_u_newhead->uh_entry;
@@ -2683,11 +2686,11 @@ static void u_getbot(void)
extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count) {
- EMSG(_("E440: undo line missing"));
- uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
- * get all the old lines back
- * without deleting the current
- * ones */
+ IEMSG(_("E440: undo line missing"));
+ uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
+ // get all the old lines back
+ // without deleting the current
+ // ones
}
curbuf->b_u_newhead->uh_getbot_entry = NULL;
@@ -2940,17 +2943,20 @@ bool curbufIsChanged(void)
&& (curbuf->b_changed || file_ff_differs(curbuf, true)));
}
-/*
- * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
- * Recursive.
- */
-void u_eval_tree(u_header_T *first_uhp, list_T *list)
+/// Append the list of undo blocks to a newly allocated list
+///
+/// For use in undotree(). Recursive.
+///
+/// @param[in] first_uhp Undo blocks list to start with.
+///
+/// @return [allocated] List with a representation of undo blocks.
+list_T *u_eval_tree(const u_header_T *const first_uhp)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
{
- u_header_T *uhp = first_uhp;
- dict_T *dict;
+ list_T *const list = tv_list_alloc(kListLenMayKnow);
- while (uhp != NULL) {
- dict = tv_dict_alloc();
+ for (const u_header_T *uhp = first_uhp; uhp != NULL; uhp = uhp->uh_prev.ptr) {
+ dict_T *const dict = tv_dict_alloc();
tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq);
tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time);
if (uhp == curbuf->b_u_newhead) {
@@ -2964,14 +2970,12 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list)
}
if (uhp->uh_alt_next.ptr != NULL) {
- list_T *alt_list = tv_list_alloc();
-
// Recursive call to add alternate undo tree.
- u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
- tv_dict_add_list(dict, S_LEN("alt"), alt_list);
+ tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr));
}
tv_list_append_dict(list, dict);
- uhp = uhp->uh_prev.ptr;
}
+
+ return list;
}
diff --git a/src/nvim/undo.h b/src/nvim/undo.h
index ab8584fbb2..802cdc5583 100644
--- a/src/nvim/undo.h
+++ b/src/nvim/undo.h
@@ -2,6 +2,7 @@
#define NVIM_UNDO_H
#include "nvim/undo_defs.h"
+#include "nvim/ex_cmds_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "undo.h.generated.h"
diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h
index d841210815..6c7e2bba41 100644
--- a/src/nvim/undo_defs.h
+++ b/src/nvim/undo_defs.h
@@ -4,9 +4,10 @@
#include <time.h> // for time_t
#include "nvim/pos.h"
-#include "nvim/buffer_defs.h"
#include "nvim/mark_defs.h"
+typedef struct u_header u_header_T;
+
/* Structure to store info about the Visual area. */
typedef struct {
pos_T vi_start; /* start pos of last VIsual */
@@ -15,8 +16,9 @@ typedef struct {
colnr_T vi_curswant; /* MAXCOL from w_curswant */
} visualinfo_T;
+#include "nvim/buffer_defs.h"
+
typedef struct u_entry u_entry_T;
-typedef struct u_header u_header_T;
struct u_entry {
u_entry_T *ue_next; /* pointer to next entry in list */
linenr_T ue_top; /* number of line above undo block */
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 094ca29b01..77ae849d2e 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -22,6 +22,7 @@
#include "nvim/message.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
+#include "nvim/lua/executor.h"
// version info generated by the build system
#include "auto/versiondef.h"
@@ -29,8 +30,8 @@
// for ":version", ":intro", and "nvim --version"
#ifndef NVIM_VERSION_MEDIUM
#define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR)\
- "." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\
- NVIM_VERSION_PRERELEASE
+"." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\
+NVIM_VERSION_PRERELEASE
#endif
#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM
@@ -46,2110 +47,1303 @@ char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS;
static char *features[] = {
#ifdef HAVE_ACL
- "+acl",
+"+acl",
#else
- "-acl",
+"-acl",
#endif
#if (defined(HAVE_ICONV_H) && defined(USE_ICONV)) || defined(DYNAMIC_ICONV)
# ifdef DYNAMIC_ICONV
- "+iconv/dyn",
+"+iconv/dyn",
# else
- "+iconv",
+"+iconv",
# endif
#else
- "-iconv",
+"-iconv",
#endif
#ifdef HAVE_JEMALLOC
- "+jemalloc",
+"+jemalloc",
#else
- "-jemalloc",
+"-jemalloc",
#endif
#ifdef FEAT_TUI
- "+tui",
+"+tui",
#else
- "-tui",
+"-tui",
#endif
- NULL
+NULL
};
// clang-format off
static const int included_patches[] = {
- // 2367,NA
- // 2366 NA
- // 2365 NA
- // 2364,NA
- // 2363 NA
- 2362,
- // 2361 NA
- 2360,
- 2359,
- // 2358 NA
- 2357,
- 2356,
- 2355,
- // 2354,
- 2353,
- // 2352 NA
- // 2351 NA
- // 2350,
- 2349,
- 2348,
- 2347,
- 2346,
- // 2345 NA
- // 2344 NA
- 2343,
- // 2342 NA
- 2341,
- // 2340 NA
- 2339,
- // 2338 NA
- 2337,
- 2336,
- 2335,
- 2334,
- 2333,
- 2332,
- 2331,
- 2330,
- 2329,
- 2328,
- // 2327 NA
- 2326,
- // 2325 NA
- 2324,
- 2323,
- 2322,
- 2321,
- 2320,
- // 2319 NA
- 2318,
- 2317,
- // 2316 NA
- 2315,
- 2314,
- 2313,
- 2312,
- // 2311 NA
- // 2310 NA
- 2309,
- // 2308 NA
- 2307,
- 2306,
- 2305,
- 2304,
- 2303,
- // 2302 NA
- // 2301 NA
- 2300,
- 2299,
- // 2298 NA
- // 2297 NA
- 2296,
- 2295,
- 2294,
- 2293,
- 2292,
- 2291,
- // 2290 NA
- // 2289 NA
- // 2288 NA
- // 2287 NA
- // 2286 NA
- // 2285 NA
- 2284,
- 2283,
- // 2282 NA
- 2281,
- 2280,
- 2279,
- // 2278 NA
- 2277,
- 2276,
- 2275,
- 2274,
- 2273,
- 2272,
- // 2271 NA
- // 2270 NA
- 2269,
- 2268,
- // 2267 NA
- 2266,
- 2265,
- 2264,
- 2263,
- // 2262 NA
- // 2261 NA
- // 2260 NA
- 2259,
- // 2258 NA
- // 2257 NA
- 2256,
- 2255,
- // 2254 NA
- // 2253 NA
- // 2252 NA
- 2251,
- // 2250,
- 2249,
- 2248,
- // 2247 NA
- 2246,
- 2245,
- 2244,
- // 2243 NA
- 2242,
- 2241,
- 2240,
- 2239,
- // 2238 NA
- 2237,
- 2236,
- 2235,
- // 2234 NA
- 2233,
- // 2232 NA
- 2231,
- 2230,
- // 2229,
- 2228,
- 2227,
- 2226,
- 2225,
- 2224,
- 2223,
- 2222,
- 2221,
- 2220,
- 2219,
- // 2218 NA
- 2217,
- // 2216 NA
- 2215,
- // 2214 NA
- 2213,
- 2212,
- // 2211 NA
- // 2210 NA
- 2209,
- 2208,
- // 2207 NA
- // 2206 NA
- 2205,
- 2204,
- // 2203 NA
- // 2202 NA
- 2201,
- 2200,
- // 2199 NA
- // 2198 NA
- 2197,
- 2196,
- // 2195 NA
- 2194,
- // 2193 NA
- // 2192 NA
- // 2191 NA
- 2190,
- // 2189,
- 2188,
- 2187,
- // 2186 NA
- 2185,
- 2184,
- 2183,
- // 2182 NA
- // 2181 NA
- 2180,
- 2179,
- 2178,
- 2177,
- // 2176 NA
- 2175,
- 2174,
- 2173,
- 2172,
- // 2171 NA
- 2170,
- 2169,
- // 2168 NA
- // 2167 NA
- // 2166 NA
- 2165,
- 2164,
- 2163,
- 2162,
- 2161,
- 2160,
- 2159,
- 2158,
- // 2157 NA
- // 2156 NA
- // 2155 NA
- // 2154 NA
- // 2153 NA
- 2152,
- 2151,
- // 2150 NA
- 2149,
- 2148,
- 2147,
- 2146,
- // 2145 NA
- 2144,
- 2143,
- 2142,
- 2141,
- // 2140 NA
- 2139,
- // 2138 NA
- 2137,
- 2136,
- 2135,
- 2134,
- // 2133 NA
- // 2132 NA
- // 2131 NA
- // 2130 NA
- // 2129 NA
- 2128,
- 2127,
- 2126,
- // 2125 NA
- 2124,
- 2123,
- // 2122 NA
- 2121,
- 2120,
- 2119,
- // 2118 NA
- 2117,
- // 2116 NA
- // 2115 NA
- // 2114 NA
- 2113,
- 2112,
- // 2111 NA
- 2110,
- 2109,
- // 2108 NA
- 2107,
- 2106,
- // 2105 NA
- 2104,
- 2103,
- // 2102 NA
- 2101,
- 2100,
- 2099,
- 2098,
- 2097,
- 2096,
- 2095,
- // 2094 NA
- // 2093 NA
- // 2092 NA
- // 2091 NA
- 2090,
- // 2089 NA
- 2088,
- 2087,
- 2086,
- 2085,
- 2084,
- // 2083 NA
- 2082,
- 2081,
- // 2080 NA
- // 2079 NA
- // 2078 NA
- 2077,
- 2076,
- 2075,
- 2074,
- // 2073 NA
- 2072,
- 2071,
- // 2070 NA
- 2069,
- 2068,
- 2067,
- 2066,
- 2065,
- 2064,
- // 2063 NA
- 2062,
- 2061,
- // 2060 NA
- // 2059 NA
- // 2058 NA
- // 2057 NA
- // 2056 NA
- // 2055 NA
- // 2054 NA
- // 2053 NA
- // 2052 NA
- 2051,
- 2050,
- 2049,
- // 2048 NA
- 2047,
- 2046,
- // 2045 NA
- 2044,
- 2043,
- // 2042 NA
- // 2041 NA
- // 2040 NA
- // 2039 NA
- // 2038 NA
- // 2037 NA
- 2036,
- // 2035 NA
- // 2034 NA
- 2033,
- // 2032 NA
- 2031,
- // 2030 NA
- 2029,
- 2028,
- // 2027 NA
- // 2026 NA
- // 2025 NA
- 2024,
- 2023,
- 2022,
- 2021,
- // 2020 NA
- 2019,
- 2018,
- 2017,
- // 2016 NA
- 2015,
- 2014,
- 2013,
- 2012,
- 2011,
- 2010,
- 2009,
- 2008,
- 2007,
- 2006,
- 2005,
- // 2004 NA
- // 2003 NA
- 2002,
- // 2001 NA
- 2000,
- 1999,
- // 1998 NA
- 1997,
- 1996,
- // 1995 NA
- 1994,
- 1993,
- 1992,
- 1991,
- 1990,
- 1989,
- // 1988 NA
- // 1987 NA
- 1986,
- // 1985 NA
- 1984,
- // 1983 NA
- // 1982 NA
- 1981,
- 1980,
- 1979,
- 1978,
- 1977,
- 1976,
- 1975,
- // 1974 NA
- 1973,
- // 1972 NA
- 1971,
- 1970,
- // 1969 NA
- 1968,
- 1967,
- 1966,
- // 1965 NA
- 1964,
- // 1963 NA
- 1962,
- 1961,
- 1960,
- // 1959 NA
- // 1958 NA
- // 1957 NA
- 1956,
- // 1955 NA
- 1954,
- 1953,
- 1952,
- // 1951 NA
- 1950,
- 1949,
- 1948,
- // 1947 NA
- // 1946 NA
- // 1945 NA
- // 1944 NA
- // 1943 NA
- // 1942 NA
- 1941,
- 1940,
- // 1939 NA
- // 1938 NA
- 1937,
- // 1936 NA
- // 1935 NA
- // 1934 NA
- // 1933 NA
- // 1932 NA
- // 1931 NA
- // 1930 NA
- // 1929 NA
- 1928,
- // 1927 NA
- // 1926 NA
- 1925,
- // 1924 NA
- 1923,
- // 1922 NA
- // 1921 NA
- // 1920 NA
- // 1919 NA
- // 1918 NA
- // 1917 NA
- // 1916 NA
- // 1915 NA
- // 1914 NA
- 1913,
- 1912,
- // 1911 NA
- 1910,
- 1909,
- // 1908 NA
- // 1907 NA
- // 1906 NA
- // 1905 NA
- // 1904 NA
- // 1903 NA
- // 1902 NA
- // 1901 NA
- 1900,
- // 1899 NA
- 1898,
- 1897,
- 1896,
- 1895,
- 1894,
- 1893,
- 1892,
- // 1891 NA
- // 1890 NA
- 1889,
- // 1888 NA
- // 1887 NA
- // 1886 NA
- // 1885 NA
- 1884,
- // 1883 NA
- 1882,
- 1881,
- // 1880 NA
- // 1879 NA
- // 1878 NA
- // 1877 NA
- 1876,
- 1875,
- // 1874 NA
- // 1873 NA
- // 1872 NA
- 1871,
- // 1870 NA
- // 1869 NA
- 1868,
- 1867,
- 1866,
- // 1865 NA
- // 1864 NA
- // 1863 NA
- // 1862 NA
- 1861,
- 1860,
- // 1859 NA
- // 1858 NA
- // 1857 NA
- // 1856 NA
- // 1855 NA
- // 1854 NA
- // 1853 NA
- // 1852 NA
- 1851,
- // 1850 NA
- // 1849 NA
- // 1848 NA
- 1847,
- // 1846 NA
- // 1845 NA
- // 1844 NA
- // 1843 NA
- 1842,
- 1841,
- 1840,
- 1839,
- 1838,
- 1837,
- 1836,
- 1835,
- 1834,
- 1833,
- 1832,
- 1831,
- // 1830 NA
- // 1829 NA
- // 1828 NA
- // 1827 NA
- // 1826 NA
- // 1825 NA
- // 1824 NA
- 1823,
- // 1822 NA
- 1821,
- 1820,
- // 1819 NA
- 1818,
- // 1817 NA
- 1816,
- 1815,
- // 1814 NA
- 1813,
- // 1812 NA
- // 1811 NA
- // 1810 NA
- 1809,
- 1808,
- // 1807 NA
- 1806,
- // 1805 NA
- // 1804 NA
- // 1803 NA
- 1802,
- // 1801 NA
- // 1800 NA
- 1799,
- // 1798 NA
- // 1797 NA
- // 1796 NA
- // 1795 NA
- // 1794 NA
- 1793,
- // 1792 NA
- // 1791 NA
- // 1790 NA
- // 1789 NA
- // 1789 NA
- // 1788 NA
- // 1787 NA
- // 1786 NA
- 1785,
- // 1784 NA
- 1783,
- 1782,
- 1781,
- 1780,
- 1779,
- // 1778 NA
- // 1777 NA
- // 1776 NA
- // 1775 NA
- // 1774 NA
- // 1773 NA
- // 1772 NA
- // 1771 NA
- // 1770 NA
- // 1769 NA
- 1768,
- // 1767 NA
- // 1766 NA
- 1765,
- // 1764 NA
- 1763,
- // 1762 NA
- // 1761 NA
- // 1760 NA
- 1759,
- 1758,
- 1757,
- // 1756 NA
- 1755,
- 1754,
- 1753,
- 1752,
- 1751,
- // 1750 NA
- // 1749 NA
- 1748,
- // 1747 NA
- // 1746 NA
- // 1745 NA
- // 1744 NA
- // 1743 NA
- 1742,
- 1741,
- 1740,
- 1739,
- 1738,
- // 1737 NA
- // 1736 NA
- 1735,
- 1734,
- // 1733 NA
- 1732,
- // 1731 NA
- 1730,
- // 1729 NA
- 1728,
- 1727,
- // 1726 NA
- // 1725 NA
- // 1724 NA
- 1723,
- // 1722 NA
- // 1721 NA
- // 1720 NA
- 1719,
- 1718,
- // 1717 NA
- 1716,
- 1715,
- 1714,
- // 1713 NA
- 1712,
- 1711,
- // 1710 NA
- // 1709 NA
- 1708,
- 1707,
- // 1706 NA
- // 1705 NA
- 1704,
- 1703,
- 1702,
- 1701,
- 1700,
- 1699,
- // 1698 NA
- 1697,
- 1696,
- 1695,
- // 1694 NA
- // 1693 NA
- 1692,
- 1691,
- // 1690 NA
- // 1689 NA
- // 1688 NA
- // 1687 NA
- 1686,
- 1685,
- // 1684 NA
- // 1683 NA
- 1682,
- 1681,
- // 1680 NA
- 1679,
- // 1678 NA
- // 1677 NA
- 1676,
- 1675,
- // 1674 NA
- 1673,
- // 1672 NA
- 1671,
- 1670,
- // 1669 NA
- // 1668 NA
- // 1667 NA
- // 1666 NA
- // 1665 NA
- 1664,
- 1663,
- // 1662 NA
- // 1661 NA
- 1660,
- // 1659 NA
- 1658,
- // 1657 NA
- 1656,
- // 1655 NA
- 1654,
- // 1653 NA
- 1652,
- // 1651 NA
- 1650,
- 1649,
- 1648,
- 1647,
- // 1646 NA
- 1645,
- // 1644 NA
- 1643,
- 1642,
- 1641,
- 1640,
- 1639,
- 1638,
- // 1637 NA
- // 1636 NA
- // 1635 NA
- 1634,
- // 1633 NA
- // 1632 NA
- // 1631 NA
- 1630,
- 1629,
- // 1628 NA
- // 1627 NA
- // 1626 NA
- // 1625 NA
- // 1624 NA
- // 1623 NA
- // 1622 NA
- // 1621 NA
- 1620,
- 1619,
- // 1618 NA
- // 1617 NA
- // 1616 NA
- // 1615 NA
- 1614,
- // 1613 NA
- // 1612 NA
- // 1611 NA
- // 1610 NA
- // 1609 NA
- 1608,
- 1607,
- 1606,
- 1605,
- 1604,
- 1603,
- // 1602 NA
- // 1601 NA
- // 1600 NA
- // 1599 NA
- // 1598 NA
- // 1597 NA
- 1596,
- // 1595 NA
- // 1594 NA
- // 1593 NA
- 1592,
- 1591,
- 1590,
- 1589,
- 1588,
- // 1587 NA
- 1586,
- 1585,
- // 1584 NA
- // 1583 NA
- 1582,
- 1581,
- 1580,
- // 1579 NA
- 1578,
- 1577,
- 1576,
- // 1575 NA
- 1574,
- // 1573 NA
- // 1572 NA
- 1571,
- 1570,
- 1569,
- 1568,
- 1567,
- // 1566 NA
- 1565,
- 1564,
- 1563,
- // 1562 NA
- // 1561 NA
- // 1560 NA
- 1559,
- 1558,
- 1557,
- // 1556 NA
- // 1555 NA
- 1554,
- 1553,
- 1552,
- 1551,
- 1550,
- 1549,
- 1548,
- 1547,
- 1546,
- // 1545 NA
- // 1544 NA
- // 1543 NA
- // 1542 NA
- // 1541 NA
- // 1540 NA
- // 1539 NA
- // 1538 NA
- // 1537 NA
- // 1536 NA
- 1535,
- // 1534 NA
- 1533,
- // 1532 NA
- // 1531 NA
- // 1530 NA
- // 1529 NA
- 1528,
- // 1527 NA
- // 1526 NA
- // 1525 NA
- // 1524 NA
- // 1523 NA
- // 1522 NA
- 1521,
- // 1520 NA
- // 1519 NA
- // 1518 NA
- // 1517 NA
- 1516,
- // 1515 NA
- // 1514 NA
- 1513,
- // 1512 NA
- 1511,
- // 1510 NA
- // 1509 NA
- // 1508 NA
- // 1507 NA
- // 1506 NA
- // 1505 NA
- // 1504 NA
- // 1503 NA
- // 1502 NA
- // 1501 NA
- 1500,
- 1499,
- // 1498 NA
- // 1497 NA
- // 1496 NA
- // 1495 NA
- 1494,
- // 1493 NA
- 1492,
- 1491,
- // 1490 NA
- // 1489 NA
- // 1488 NA
- // 1487 NA
- 1486,
- // 1485 NA
- // 1484 NA
- // 1483 NA
- // 1482 NA
- // 1481 NA
- 1480,
- 1479,
- 1478,
- 1477,
- // 1476 NA
- // 1475 NA
- // 1474 NA
- // 1473 NA
- // 1472 NA
- // 1471 NA
- // 1470 NA
- // 1469 NA
- 1468,
- // 1467 NA
- // 1466 NA
- // 1465 NA
- 1464,
- // 1463 NA
- // 1462 NA
- // 1461 NA
- // 1460 NA
- // 1459 NA
- // 1458 NA
- // 1457 NA
- // 1456 NA
- // 1455 NA
- // 1454 NA
- // 1453 NA
- // 1452 NA
- // 1451 NA
- // 1450 NA
- // 1449 NA
- // 1448 NA
- // 1447 NA
- // 1446 NA
- // 1445 NA
- // 1444 NA
- // 1443 NA
- // 1442 NA
- // 1441 NA
- // 1440 NA
- // 1439 NA
- // 1438 NA
- // 1437 NA
- // 1436 NA
- // 1435 NA
- // 1434 NA
- // 1433 NA
- // 1432 NA
- // 1431 NA
- // 1430 NA
- // 1429 NA
- // 1428 NA
- // 1427 NA
- // 1426 NA
- 1425,
- // 1424 NA
- // 1423 NA
- // 1422 NA
- // 1421 NA
- // 1420 NA
- // 1419 NA
- // 1418 NA
- // 1417 NA
- // 1416 NA
- // 1415 NA
- // 1414 NA
- // 1413 NA
- // 1412 NA
- // 1411 NA
- 1410,
- // 1409 NA
- // 1408 NA
- // 1407 NA
- 1406,
- 1405,
- // 1404 NA
- // 1403 NA
- // 1402 NA
- 1401,
- // 1400 NA
- // 1399 NA
- // 1398 NA
- 1397,
+ 1561,
+ // 1560,
+ // 1559,
+ // 1558,
+ // 1557,
+ // 1556,
+ // 1555,
+ // 1554,
+ // 1553,
+ // 1552,
+ // 1551,
+ // 1550,
+ // 1549,
+ // 1548,
+ // 1547,
+ // 1546,
+ // 1545,
+ // 1544,
+ // 1543,
+ // 1542,
+ // 1541,
+ // 1540,
+ // 1539,
+ // 1538,
+ // 1537,
+ // 1536,
+ // 1535,
+ // 1534,
+ // 1533,
+ // 1532,
+ // 1531,
+ // 1530,
+ // 1529,
+ // 1528,
+ // 1527,
+ // 1526,
+ // 1525,
+ // 1524,
+ // 1523,
+ // 1522,
+ // 1521,
+ // 1520,
+ // 1519,
+ // 1518,
+ // 1517,
+ // 1516,
+ // 1515,
+ // 1514,
+ // 1513,
+ // 1512,
+ // 1511,
+ // 1510,
+ // 1509,
+ // 1508,
+ // 1507,
+ // 1506,
+ // 1505,
+ // 1504,
+ // 1503,
+ // 1502,
+ // 1501,
+ // 1500,
+ // 1499,
+ // 1498,
+ // 1497,
+ // 1496,
+ // 1495,
+ // 1494,
+ 1493,
+ // 1492,
+ // 1491,
+ // 1490,
+ // 1489,
+ // 1488,
+ // 1487,
+ // 1486,
+ // 1485,
+ // 1484,
+ 1483,
+ // 1482,
+ // 1481,
+ // 1480,
+ // 1479,
+ // 1478,
+ // 1477,
+ // 1476,
+ 1475,
+ // 1474,
+ // 1473,
+ // 1472,
+ // 1471,
+ // 1470,
+ // 1469,
+ // 1468,
+ // 1467,
+ // 1466,
+ // 1465,
+ // 1464,
+ // 1463,
+ // 1462,
+ // 1461,
+ // 1460,
+ // 1459,
+ // 1458,
+ // 1457,
+ // 1456,
+ // 1455,
+ // 1454,
+ // 1453,
+ // 1452,
+ // 1451,
+ // 1450,
+ // 1449,
+ // 1448,
+ // 1447,
+ // 1446,
+ // 1445,
+ // 1444,
+ // 1443,
+ 1442,
+ // 1441,
+ // 1440,
+ 1439,
+ // 1438,
+ // 1437,
+ // 1436,
+ 1435,
+ // 1434,
+ // 1433,
+ // 1432,
+ // 1431,
+ // 1430,
+ // 1429,
+ // 1428,
+ // 1427,
+ // 1426,
+ // 1425,
+ // 1424,
+ // 1423,
+ // 1422,
+ // 1421,
+ // 1420,
+ 1419,
+ // 1418,
+ // 1417,
+ // 1416,
+ // 1415,
+ // 1414,
+ // 1413,
+ // 1412,
+ // 1411,
+ // 1410,
+ // 1409,
+ // 1408,
+ // 1407,
+ // 1406,
+ // 1405,
+ // 1404,
+ 1403,
+ 1402,
+ // 1401,
+ // 1400,
+ // 1399,
+ // 1398,
+ // 1397,
1396,
- // 1395 NA
- 1394,
- // 1393 NA
- // 1392 NA
- // 1391 NA
- // 1390 NA
- // 1389 NA
- 1388,
- // 1387 NA
- // 1386 NA
- // 1385 NA
- 1384,
- // 1383 NA
- // 1382 NA
- // 1381 NA
- // 1380 NA
- // 1379 NA
- // 1378 NA
- // 1377 NA
- // 1376 NA
- // 1375 NA
- // 1374 NA
- // 1373 NA
- // 1372 NA
- // 1371 NA
- // 1370 NA
- // 1369 NA
- // 1368 NA
- // 1367 NA
- 1366,
+ // 1395,
+ // 1394,
+ 1393,
+ // 1392,
+ // 1391,
+ // 1390,
+ // 1389,
+ // 1388,
+ // 1387,
+ // 1386,
+ // 1385,
+ // 1384,
+ // 1383,
+ // 1382,
+ // 1381,
+ // 1380,
+ // 1379,
+ // 1378,
+ // 1377,
+ // 1376,
+ // 1375,
+ // 1374,
+ // 1373,
+ // 1372,
+ // 1371,
+ 1370,
+ // 1369,
+ // 1368,
+ // 1367,
+ // 1366,
1365,
- // 1364 NA
- // 1363 NA
- // 1362 NA
- // 1361 NA
- // 1360 NA
- // 1359 NA
- // 1358 NA
- // 1357 NA
- // 1356 NA
- // 1355 NA
- // 1354 NA
- // 1353 NA
- 1352,
- // 1351 NA
- // 1350 NA
- // 1349 NA
- // 1348 NA
- 1347,
- 1346,
- // 1345 NA
- // 1344 NA
- // 1343 NA
- // 1342 NA
- // 1341 NA
- // 1340 NA
- // 1339 NA
- // 1338 NA
- // 1337 NA
- // 1336 NA
- // 1335 NA
- // 1334 NA
- // 1333 NA
- // 1332 NA
- // 1331 NA
- // 1330 NA
- // 1329 NA
- // 1328 NA
- // 1327 NA
- // 1326 NA
- // 1325 NA
- // 1324 NA
- // 1323 NA
- // 1322 NA
- // 1321 NA
- // 1320 NA
- // 1319 NA
- // 1318 NA
- // 1317 NA
- // 1316 NA
- // 1315 NA
- // 1314 NA
- // 1313 NA
- // 1312 NA
- // 1311 NA
- // 1310 NA
- 1309,
- // 1308 NA
- // 1307 NA
- // 1306 NA
- 1305,
+ // 1364,
+ // 1363,
+ // 1362,
+ // 1361,
+ // 1360,
+ // 1359,
+ // 1358,
+ 1357,
+ // 1356,
+ // 1355,
+ // 1354,
+ // 1353,
+ // 1352,
+ // 1351,
+ // 1350,
+ // 1349,
+ // 1348,
+ // 1347,
+ // 1346,
+ // 1345,
+ // 1344,
+ // 1343,
+ // 1342,
+ // 1341,
+ // 1340,
+ // 1339,
+ // 1338,
+ // 1337,
+ // 1336,
+ // 1335,
+ // 1334,
+ 1333,
+ // 1332,
+ // 1331,
+ // 1330,
+ 1329,
+ // 1328,
+ // 1327,
+ // 1326,
+ // 1325,
+ // 1324,
+ // 1323,
+ // 1322,
+ // 1321,
+ // 1320,
+ // 1319,
+ // 1318,
+ // 1317,
+ // 1316,
+ // 1315,
+ // 1314,
+ // 1313,
+ // 1312,
+ // 1311,
+ // 1310,
+ // 1309,
+ // 1308,
+ // 1307,
+ // 1306,
+ // 1305,
1304,
- // 1303 NA
- // 1302 NA
- // 1301 NA
- // 1300 NA
- // 1299 NA
- // 1298 NA
- // 1297 NA
- 1296,
- // 1295 NA
- // 1294 NA
- // 1293 NA
- 1292,
- // 1291 NA
- // 1290 NA
- // 1289 NA
- // 1288 NA
- // 1287 NA
- // 1286 NA
- 1285,
- 1284,
- // 1283 NA
- 1282,
- 1281,
- // 1280 NA
- // 1279 NA
- // 1278 NA
- // 1277 NA
- 1276,
- // 1275 NA
- // 1274 NA
- // 1273 NA
- // 1272 NA
+ // 1303,
+ 1302,
+ // 1301,
+ // 1300,
+ // 1299,
+ // 1298,
+ // 1297,
+ // 1296,
+ // 1295,
+ // 1294,
+ // 1293,
+ // 1292,
+ // 1291,
+ // 1290,
+ 1289,
+ // 1288,
+ // 1287,
+ // 1286,
+ // 1285,
+ // 1284,
+ // 1283,
+ // 1282,
+ // 1281,
+ // 1280,
+ // 1279,
+ // 1278,
+ // 1277,
+ // 1276,
+ // 1275,
+ // 1274,
+ // 1273,
+ // 1272,
1271,
- // 1270 NA
- 1269,
- // 1268 NA
- 1267,
- 1266,
- // 1265 NA
- // 1264 NA
- // 1263 NA
- // 1262 NA
- // 1261 NA
- // 1260 NA
- 1259,
- // 1258 NA
- // 1257 NA
- // 1256 NA
- // 1255 NA
- // 1254 NA
- // 1253 NA
- // 1252 NA
- // 1251 NA
- // 1250 NA
- // 1249 NA
- // 1248 NA
- // 1247 NA
- // 1246 NA
- // 1245 NA
- // 1244 NA
- // 1243 NA
- // 1242 NA
- // 1241 NA
- // 1240 NA
- // 1239 NA
- // 1238 NA
- 1237,
- 1236,
- // 1235 NA
- // 1234 NA
- // 1233 NA
- // 1232 NA
- // 1231 NA
- // 1230 NA
- // 1229 NA
- 1228,
- // 1227 NA
- // 1226 NA
- // 1225 NA
- // 1224 NA
+ // 1270,
+ // 1269,
+ // 1268,
+ // 1267,
+ // 1266,
+ // 1265,
+ // 1264,
+ // 1263,
+ 1262,
+ // 1261,
+ // 1260,
+ // 1259,
+ // 1258,
+ // 1257,
+ // 1256,
+ // 1255,
+ // 1254,
+ // 1253,
+ // 1252,
+ // 1251,
+ 1250,
+ // 1249,
+ // 1248,
+ // 1247,
+ // 1246,
+ // 1245,
+ // 1244,
+ // 1243,
+ // 1242,
+ // 1241,
+ // 1240,
+ // 1239,
+ 1238,
+ // 1237,
+ // 1236,
+ // 1235,
+ // 1234,
+ // 1233,
+ // 1232,
+ // 1231,
+ 1230,
+ 1229,
+ // 1228,
+ // 1227,
+ 1226,
+ 1225,
+ 1224,
1223,
- // 1222 NA
- // 1221 NA
- // 1220 NA
- // 1219 NA
- // 1218 NA
- // 1217 NA
- // 1216 NA
- // 1215 NA
- // 1214 NA
- // 1213 NA
- // 1212 NA
- // 1211 NA
- // 1210 NA
- // 1209 NA
- // 1208 NA
- // 1207 NA
- // 1206 NA
- // 1205 NA
- // 1204 NA
- // 1203 NA
- // 1202 NA
- // 1201 NA
- // 1200 NA
- // 1199 NA
- // 1198 NA
- // 1197 NA
- // 1196 NA
- // 1195 NA
- // 1194 NA
- // 1193 NA
- // 1192 NA
- // 1191 NA
- // 1190 NA
- // 1189 NA
- // 1188 NA
- // 1187 NA
- // 1186 NA
- // 1185 NA
- // 1184 NA
- // 1183 NA
- // 1182 NA
- 1181,
- 1180,
- 1179,
- 1178,
- // 1177 NA
- // 1176 NA
- // 1175 NA
- // 1174 NA
- 1173,
- // 1172 NA
- // 1171 NA
- // 1170 NA
- // 1169 NA
- 1168,
- 1167,
- 1166,
- // 1165 NA
- 1164,
- 1163,
- // 1162 NA
- 1161,
- 1160,
- // 1159 NA
- // 1158 NA
- 1157,
- // 1156 NA
- // 1155 NA
- // 1154 NA
- 1153,
- // 1152 NA
- 1151,
- 1150,
- 1149,
- // 1148 NA
- 1147,
- // 1146 NA
- // 1145 NA
- 1144,
- 1143,
- 1142,
- 1141,
- 1140,
- // 1139 NA
- // 1138 NA
- 1137,
- 1136,
- // 1135 NA
- // 1134 NA
- // 1133 NA
- 1132,
- // 1131 NA
- // 1130 NA
- // 1129 NA
- // 1128 NA
- // 1127 NA
- 1126,
- // 1125 NA
- // 1124 NA
- 1123,
- // 1122 NA
- 1121,
- 1120,
- 1119,
- 1118,
- 1117,
- 1116,
- // 1115 NA
- 1114,
- 1113,
- 1112,
- 1111,
- 1110,
- // 1109 NA
+ 1222,
+ 1221,
+ // 1220,
+ 1219,
+ // 1218,
+ // 1217,
+ // 1216,
+ // 1215,
+ // 1214,
+ // 1213,
+ // 1212,
+ // 1211,
+ 1210,
+ // 1209,
+ // 1208,
+ 1207,
+ 1206,
+ 1205,
+ 1204,
+ // 1203,
+ // 1202,
+ // 1201,
+ 1200,
+ // 1199,
+ // 1198,
+ // 1197,
+ // 1196,
+ // 1195,
+ // 1194,
+ // 1193,
+ // 1192,
+ // 1191,
+ // 1190,
+ 1189,
+ 1188,
+ // 1187,
+ 1186,
+ // 1185,
+ // 1184,
+ // 1183,
+ // 1182,
+ // 1181,
+ // 1180,
+ // 1179,
+ // 1178,
+ // 1177,
+ // 1176,
+ // 1175,
+ // 1174,
+ // 1173,
+ // 1172,
+ // 1171,
+ // 1170,
+ // 1169,
+ // 1168,
+ // 1167,
+ // 1166,
+ // 1165,
+ // 1164,
+ // 1163,
+ // 1162,
+ // 1161,
+ // 1160,
+ // 1159,
+ 1158,
+ // 1157,
+ // 1156,
+ // 1155,
+ // 1154,
+ // 1153,
+ // 1152,
+ // 1151,
+ // 1150,
+ // 1149,
+ // 1148,
+ // 1147,
+ // 1146,
+ // 1145,
+ // 1144,
+ // 1143,
+ // 1142,
+ // 1141,
+ // 1140,
+ // 1139,
+ // 1138,
+ // 1137,
+ // 1136,
+ // 1135,
+ // 1134,
+ // 1133,
+ // 1132,
+ // 1131,
+ // 1130,
+ // 1129,
+ // 1128,
+ // 1127,
+ // 1126,
+ // 1125,
+ // 1124,
+ // 1123,
+ // 1122,
+ // 1121,
+ // 1120,
+ // 1119,
+ // 1118,
+ // 1117,
+ // 1116,
+ // 1115,
+ // 1114,
+ // 1113,
+ // 1112,
+ // 1111,
+ // 1110,
+ // 1109,
1108,
- 1107,
- // 1106 NA
- 1105,
- // 1104 NA
- // 1103 NA
- 1102,
- 1101,
- // 1100 NA
- // 1099 NA
- // 1098 NA
- // 1097 NA
- 1096,
- // 1095 NA
- 1094,
- 1093,
- 1092,
- 1091,
- 1090,
- 1089,
- 1088,
- 1087,
- 1086,
- 1085,
- 1084,
- // 1083 NA
- // 1082 NA
- 1081,
- // 1080 NA
- // 1079 NA
- // 1078 NA
- // 1077 NA
- 1076,
- 1075,
- // 1074 NA
- // 1073 NA
- 1072,
- 1071,
- // 1070 NA
- // 1069 NA
- 1068,
- // 1067 NA
- // 1066 NA
- 1065,
- 1064,
- // 1063 NA
- // 1062 NA
- 1061,
- // 1060 NA
- 1059,
- // 1058 NA
- 1057,
- 1056,
- 1055,
- 1054,
- 1053,
- 1052,
- 1051,
- 1050,
- 1049,
- 1048,
- 1047,
- 1046,
- // 1045 NA
- // 1044 NA
- // 1043 NA
- 1042,
- 1041,
- // 1040 NA
- // 1039 NA
- // 1038 NA
- 1037,
- 1036,
- 1035,
- 1034,
- // 1033 NA
- 1032,
- // 1031 NA
- 1030,
- 1029,
- // 1028 NA
- 1027,
- // 1026 NA
- // 1025 NA
- // 1024 NA
- // 1023 NA
- // 1022 NA
- // 1021 NA
- // 1020 NA
- // 1019 NA
- 1018,
- 1017,
- // 1016 NA
- 1015,
- // 1014 NA
- 1013,
- // 1012 NA
- // 1011 NA
- // 1010 NA
- // 1009 NA
- // 1008 NA
+ // 1107,
+ // 1106,
+ // 1105,
+ // 1104,
+ // 1103,
+ // 1102,
+ // 1101,
+ // 1100,
+ // 1099,
+ // 1098,
+ // 1097,
+ // 1096,
+ // 1095,
+ // 1094,
+ // 1093,
+ // 1092,
+ // 1091,
+ // 1090,
+ // 1089,
+ // 1088,
+ // 1087,
+ // 1086,
+ // 1085,
+ // 1084,
+ // 1083,
+ // 1082,
+ // 1081,
+ // 1080,
+ // 1079,
+ // 1078,
+ // 1077,
+ // 1076,
+ // 1075,
+ // 1074,
+ // 1073,
+ // 1072,
+ // 1071,
+ // 1070,
+ // 1069,
+ // 1068,
+ // 1067,
+ // 1066,
+ // 1065,
+ // 1064,
+ // 1063,
+ // 1062,
+ // 1061,
+ // 1060,
+ // 1059,
+ // 1058,
+ // 1057,
+ // 1056,
+ // 1055,
+ // 1054,
+ // 1053,
+ // 1052,
+ // 1051,
+ // 1050,
+ // 1049,
+ // 1048,
+ // 1047,
+ // 1046,
+ // 1045,
+ // 1044,
+ // 1043,
+ // 1042,
+ // 1041,
+ // 1040,
+ // 1039,
+ // 1038,
+ // 1037,
+ // 1036,
+ // 1035,
+ // 1034,
+ // 1033,
+ // 1032,
+ // 1031,
+ // 1030,
+ // 1029,
+ // 1028,
+ // 1027,
+ // 1026,
+ 1025,
+ 1024,
+ // 1023,
+ // 1022,
+ // 1021,
+ // 1020,
+ 1019,
+ // 1018,
+ // 1017,
+ // 1016,
+ // 1015,
+ // 1014,
+ // 1013,
+ // 1012,
+ // 1011,
+ // 1010,
+ // 1009,
+ // 1008,
1007,
- 1006,
- // 1005 NA
- // 1004 NA
- // 1003 NA
- // 1002 NA
- 1001,
- 1000,
- // 999 NA
- 998,
- // 997 NA
- // 996 NA
- // 995 NA
- // 994 NA
- // 993 NA
- // 992 NA
- 991,
- // 990 NA
- 989,
- // 988 NA
- // 987 NA
- // 986 NA
- // 985 NA
- 984,
- // 983 NA
- // 982 NA
- 981,
- 980,
- // 979 NA
- 978,
- 977,
- // 976 NA
+ // 1006,
+ // 1005,
+ // 1004,
+ // 1003,
+ // 1002,
+ // 1001,
+ // 1000,
+ // 999,
+ // 998,
+ // 997,
+ // 996,
+ // 995,
+ // 994,
+ // 993,
+ // 992,
+ // 991,
+ // 990,
+ // 989,
+ // 988,
+ // 987,
+ // 986,
+ // 985,
+ // 984,
+ // 983,
+ // 982,
+ // 981,
+ // 980,
+ // 979,
+ // 978,
+ // 977,
+ // 976,
975,
974,
- 973,
- 972,
- // 971 NA
- // 970 NA
- // 969 NA
- // 968 NA
- // 967 NA
- // 966 NA
- // 965 NA
- // 964 NA
- 963,
- // 962 NA
- 961,
- // 960 NA
- // 959 NA
- 958,
- 957,
- // 956 NA
+ // 973,
+ // 972,
+ // 971,
+ // 970,
+ // 969,
+ // 968,
+ // 967,
+ // 966,
+ // 965,
+ // 964,
+ // 963,
+ 962,
+ // 961,
+ // 960,
+ // 959,
+ // 958,
+ // 957,
+ // 956,
955,
- // 954 NA
- 953,
- 952,
- 951,
- 950,
- 949,
- // 948 NA
- // 947 NA
- 946,
- 945,
+ // 954,
+ // 953,
+ // 952,
+ // 951,
+ // 950,
+ // 949,
+ // 948,
+ // 947,
+ // 946,
+ // 945,
944,
- // 943 NA
- 942,
- 941,
- // 940 NA
- 939,
- // 938 NA
- 937,
- 936,
- // 935 NA
- // 934 NA
- 933,
- 932,
- // 931 NA
- // 930 NA
- 929,
- // 928 NA
- // 927 NA
- 926,
- 925,
- // 924 NA
- // 923 NA
- 922,
- // 921 NA
- // 920 NA
- // 919 NA
- // 918 NA
- // 917 NA
- 916,
- 915,
- // 914 NA
- // 913 NA
- 912,
- // 911 NA
- // 910 NA
- // 909 NA
- // 908 NA
- // 907 NA
- // 906 NA
- // 905 NA
- 904,
- 903,
- // 902 NA
- 901,
- // 900 NA
- // 899 NA
- 898,
- // 897 NA
- 896,
+ // 943,
+ // 942,
+ // 941,
+ // 940,
+ // 939,
+ // 938,
+ // 937,
+ // 936,
+ // 935,
+ // 934,
+ // 933,
+ // 932,
+ // 931,
+ // 930,
+ // 929,
+ // 928,
+ // 927,
+ // 926,
+ // 925,
+ // 924,
+ // 923,
+ // 922,
+ // 921,
+ // 920,
+ // 919,
+ // 918,
+ // 917,
+ // 916,
+ // 915,
+ // 914,
+ // 913,
+ // 912,
+ // 911,
+ // 910,
+ // 909,
+ // 908,
+ // 907,
+ 906,
+ // 905,
+ // 904,
+ // 903,
+ // 902,
+ // 901,
+ // 900,
+ // 899,
+ // 898,
+ // 897,
+ // 896,
895,
- // 894 NA
- 893,
- // 892 NA
- 891,
- // 890 NA
- 889,
- 888,
- 887,
- // 886 NA
- 885,
- // 884 NA
- 883,
- 882,
- 881,
- // 880 NA
- 879,
- 878,
- 877,
- // 876 NA
- // 875 NA
- // 874 NA
- // 873 NA
- // 872 NA
- 871,
- 870,
- // 869 NA
- 868,
- // 867 NA
- // 866 NA
- // 865 NA
- // 864 NA
- // 863 NA
- // 862 NA
- // 861 NA
- // 860 NA
- 859,
+ 894,
+ // 893,
+ // 892,
+ // 891,
+ // 890,
+ // 889,
+ // 888,
+ // 887,
+ // 886,
+ // 885,
+ // 884,
+ // 883,
+ // 882,
+ // 881,
+ // 880,
+ // 879,
+ // 878,
+ // 877,
+ // 876,
+ // 875,
+ // 874,
+ // 873,
+ // 872,
+ // 871,
+ // 870,
+ // 869,
+ // 868,
+ // 867,
+ // 866,
+ // 865,
+ // 864,
+ // 863,
+ 862,
+ 861,
+ // 860,
+ // 859,
858,
- 857,
- 856,
- // 855 NA
- // 854 NA
- 853,
- // 852 NA
- // 851 NA
- // 850 NA
- 849,
- 848,
+ // 857,
+ // 856,
+ // 855,
+ // 854,
+ // 853,
+ // 852,
+ // 851,
+ // 850,
+ // 849,
+ // 848,
847,
- // 846 NA
- 845,
- 844,
- 843,
- // 842 NA
- // 841 NA
- // 840 NA
- // 839 NA
- // 838 NA
- // 837 NA
- 836,
- 835,
- 834,
- 833,
- 832,
- 831,
- 830,
- // 829 NA
- 828,
- // 827 NA
- 826,
- 825,
- // 824 NA
- 823,
- 822,
- // 821 NA
- 820,
- 819,
- 818,
- 817,
- 816,
- 815,
- 814,
- 813,
- // 812 NA
- 811,
- 810,
- 809,
- // 808 NA
- 807,
- 806,
- 805,
- // 804 NA
- 803,
- 802,
- 801,
- 800,
- 799,
- 798,
- // 797 NA
- // 796 NA
- 795,
- // 794 NA
- 793,
+ // 846,
+ // 845,
+ // 844,
+ // 843,
+ // 842,
+ // 841,
+ // 840,
+ // 839,
+ // 838,
+ // 837,
+ // 836,
+ // 835,
+ // 834,
+ // 833,
+ // 832,
+ // 831,
+ // 830,
+ // 829,
+ // 828,
+ // 827,
+ // 826,
+ // 825,
+ // 824,
+ // 823,
+ // 822,
+ // 821,
+ // 820,
+ // 819,
+ // 818,
+ // 817,
+ // 816,
+ // 815,
+ // 814,
+ // 813,
+ // 812,
+ // 811,
+ // 810,
+ // 809,
+ // 808,
+ // 807,
+ // 806,
+ // 805,
+ // 804,
+ // 803,
+ // 802,
+ // 801,
+ // 800,
+ // 799,
+ // 798,
+ // 797,
+ // 796,
+ // 795,
+ // 794,
+ // 793,
792,
- 791,
- 790,
- 789,
- // 788 NA
- 787,
- 786,
- 785,
- 784,
- // 783 NA
- 782,
- 781,
- 780,
- 779,
- 778,
- // 777 NA
- 776,
- 775,
- 774,
- 773,
- // 772 NA
- 771,
- // 770 NA
- 769,
- 768,
- // 767 NA
- // 766 NA
- 765,
- 764,
- // 763 NA
- // 762 NA
- // 761 NA
- 760,
- // 759 NA
- 758,
- // 757 NA
- // 756 NA
- 755,
- 754,
- 753,
- // 752 NA
- // 751 NA
- // 750 NA
- 749,
- 748,
- 747,
- 746,
- 745,
- // 744 NA
- 743,
- 742,
- 741,
- 740,
- 739,
- // 738 NA
- 737,
+ // 791,
+ // 790,
+ // 789,
+ // 788,
+ // 787,
+ // 786,
+ // 785,
+ // 784,
+ // 783,
+ // 782,
+ // 781,
+ // 780,
+ // 779,
+ // 778,
+ // 777,
+ // 776,
+ // 775,
+ // 774,
+ // 773,
+ // 772,
+ // 771,
+ // 770,
+ // 769,
+ // 768,
+ // 767,
+ // 766,
+ // 765,
+ // 764,
+ // 763,
+ // 762,
+ // 761,
+ // 760,
+ // 759,
+ // 758,
+ // 757,
+ // 756,
+ // 755,
+ // 754,
+ // 753,
+ // 752,
+ // 751,
+ // 750,
+ // 749,
+ // 748,
+ // 747,
+ // 746,
+ // 745,
+ // 744,
+ // 743,
+ // 742,
+ // 741,
+ // 740,
+ // 739,
+ // 738,
+ // 737,
736,
- // 735 NA
- 734,
- // 733 NA
- 732,
- // 731 NA
- // 730 NA
- 729,
- // 728 NA
- // 727 NA
- // 726 NA
- // 725 NA
- // 724 NA
+ // 735,
+ // 734,
+ // 733,
+ // 732,
+ // 731,
+ // 730,
+ // 729,
+ // 728,
+ // 727,
+ // 726,
+ // 725,
+ // 724,
723,
- 722,
+ // 722,
721,
- // 720 NA
- 719,
- 718,
- 717,
- 716,
- 715,
- 714,
- 713,
- 712,
- 711,
+ // 720,
+ // 719,
+ // 718,
+ // 717,
+ // 716,
+ // 715,
+ // 714,
+ // 713,
+ // 712,
+ // 711,
710,
- 709,
- 708,
- 707,
- 706,
- // 705 NA
- 704,
- // 703 NA
- 702,
- // 701 NA
+ // 709,
+ // 708,
+ // 707,
+ // 706,
+ // 705,
+ // 704,
+ 703,
+ // 702,
+ // 701,
700,
699,
- 698,
- 697,
- 696,
- 695,
- 694,
- 693,
- // 692 NA
- // 691 NA
- 690,
+ // 698,
+ // 697,
+ // 696,
+ // 695,
+ // 694,
+ // 693,
+ 692,
+ // 691,
+ // 690,
689,
- 688,
- // 687 NA
- 686,
- 685,
- // 684 NA
- // 683 NA
- 682,
- // 681 NA
+ // 688,
+ // 687,
+ // 686,
+ // 685,
+ 684,
+ // 683,
+ // 682,
+ // 681,
680,
- // 679 NA
- // 678 NA
- // 677 NA
- // 676 NA
- 675,
- // 674 NA
+ 679,
+ 678,
+ // 677,
+ 676,
+ // 675,
+ 674,
673,
672,
- 671,
- 670,
- // 669 NA
+ // 671,
+ // 670,
+ // 669,
668,
667,
- // 666 NA
+ 666,
665,
- // 664 NA
- // 663 NA
+ 664,
+ // 663,
662,
- // 661 NA
+ 661,
660,
659,
658,
- // 657 NA
+ 657,
656,
655,
654,
- 653,
- // 652 NA
- 651,
- // 650 NA
- 649,
- // 648 NA
- // 647 NA
- 646,
- 645,
- // 644 NA
- 643,
- 642,
- // 641 NA
- 640,
- 639,
- // 638 NA
- 637,
- 636,
- 635,
+ // 653,
+ 652,
+ // 651,
+ 650,
+ // 649,
+ // 648,
+ // 647,
+ // 646,
+ // 645,
+ // 644,
+ // 643,
+ // 642,
+ 641,
+ // 640,
+ // 639,
+ // 638,
+ // 637,
+ // 636,
+ // 635,
634,
- 633,
- // 632 NA
- 631,
- 630,
- 629,
- // 628 NA
- // 627 NA
- // 626 NA
- // 625 NA
- // 624 NA
- 623,
- // 622 NA
- // 621 NA
- // 620 NA
- // 619 NA
- // 618 NA
- 617,
- 616,
- 615,
+ // 633,
+ // 632,
+ // 631,
+ // 630,
+ // 629,
+ // 628,
+ // 627,
+ // 626,
+ // 625,
+ // 624,
+ // 623,
+ 622,
+ // 621,
+ // 620,
+ // 619,
+ 618,
+ // 617,
+ // 616,
+ // 615,
614,
613,
612,
- // 611 NA
- // 610 NA
- 609,
+ // 611,
+ // 610,
+ // 609,
608,
- // 607 NA
+ 607,
606,
605,
- 604,
- 603,
- 602,
+ // 604,
+ // 603,
+ // 602,
601,
600,
599,
- 598,
+ // 598,
597,
- // 596 NA
+ // 596,
595,
- 594,
- 593,
- // 592 NA
- // 591 NA
+ // 594,
+ // 593,
+ // 592,
+ 591,
590,
- // 589 NA
- 588,
- 587,
- // 586 NA
- 585,
- // 584 NA
- // 583 NA
+ // 589,
+ // 588,
+ // 587,
+ // 586,
+ // 585,
+ 584,
+ // 583,
582,
- // 581 NA
+ // 581,
580,
579,
- 578,
- 577,
- 576,
- 575,
+ // 578,
+ // 577,
+ // 576,
+ // 575,
574,
- 573,
- 572,
- // 571 NA
- // 570 NA
- 569,
- 568,
- 567,
- 566,
+ // 573,
+ // 572,
+ 571,
+ // 570,
+ // 569,
+ // 568,
+ // 567,
+ // 566,
565,
- // 564 NA
- 563,
- 562,
+ // 564,
+ // 563,
+ // 562,
561,
- // 560 NA
- 559,
- // 558 NA
- // 557 NA
- // 556 NA
- // 555 NA
+ // 560,
+ // 559,
+ // 558,
+ // 557,
+ // 556,
+ 555,
554,
553,
552,
- 551,
+ // 551,
550,
- 549,
- // 548 NA
- 547,
- 546,
- 545,
- // 544 NA
- 543,
- 542,
- 541,
- // 540 NA
- 539,
- 538,
- 537,
+ // 549,
+ // 548,
+ // 547,
+ // 546,
+ // 545,
+ // 544,
+ // 543,
+ // 542,
+ // 541,
+ // 540,
+ // 539,
+ // 538,
+ // 537,
536,
- 535,
- // 534 NA
- 533,
- 532,
- 531,
- 530,
- 529,
+ // 535,
+ // 534,
+ // 533,
+ // 532,
+ // 531,
+ // 530,
+ // 529,
528,
- 527,
- 526,
- 525,
+ // 527,
+ // 526,
+ // 525,
524,
- // 523 NA
- // 522 NA
- 521,
- 520,
- 519,
+ // 523,
+ // 522,
+ // 521,
+ // 520,
+ // 519,
518,
517,
- 516,
- 515,
- 514,
- 513,
- // 512 NA
- // 511 NA
- // 510 NA
- // 509 NA
- 508,
- // 507 NA
- // 506 NA
- // 505 NA
- // 504 NA
- 503,
+ // 516,
+ // 515,
+ // 514,
+ // 513,
+ // 512,
+ // 511,
+ // 510,
+ // 509,
+ // 508,
+ // 507,
+ // 506,
+ // 505,
+ // 504,
+ // 503,
502,
- // 501 NA
- 500,
+ // 501,
+ // 500,
499,
- // 498 NA
- 497,
- // 496 NA
- // 495 NA
- 494,
- 493,
- 492,
- 491,
+ 498,
+ // 497,
+ // 496,
+ // 495,
+ // 494,
+ // 493,
+ // 492,
+ // 491,
490,
- 489,
- 488,
+ // 489,
+ // 488,
487,
486,
485,
- // 484 NA
+ 484,
483,
- // 482 NA
- // 481 NA
- // 480 NA
- // 479 NA
+ 482,
+ // 481,
+ 480,
+ 479,
478,
477,
- // 476 NA
- // 475 NA
- 474,
+ // 476,
+ // 475,
+ // 474,
473,
472,
- // 471 NA
+ 471,
470,
- // 469 NA
- 468,
- 467,
- 466,
- // 465 NA
- // 464 NA
- 463,
- 462,
- // 461 NA
- // 460 NA
- // 459 NA
+ // 469,
+ // 468,
+ // 467,
+ // 466,
+ 465,
+ // 464,
+ // 463,
+ // 462,
+ 461,
+ // 460,
+ 459,
458,
457,
- 456,
- 455,
- 454,
- // 453 NA
- 452,
- // 451 NA
- 450,
- 449,
- // 448 NA
- 447,
+ // 456,
+ // 455,
+ // 454,
+ 453,
+ // 452,
+ // 451,
+ // 450,
+ // 449,
+ 448,
+ // 447,
446,
- 445,
+ // 445,
444,
- // 443 NA
+ 443,
442,
- 441,
+ // 441,
440,
439,
- 438,
+ // 438,
437,
- 436,
- 435,
+ // 436,
+ // 435,
434,
433,
- // 432 NA
- // 431 NA
- // 430 NA
- // 429 NA
- // 428 NA
+ // 432,
+ 431,
+ // 430,
+ // 429,
+ // 428,
427,
- // 426 NA
- 425,
- // 424 NA
+ 426,
+ // 425,
+ 424,
423,
- 422,
+ // 422,
421,
- // 420 NA
- 419,
- 418,
- 417,
- 416,
- 415,
- 414,
- // 413 NA
- // 412 NA
- 411,
- 410,
- // 409 NA
+ 420,
+ // 419,
+ // 418,
+ // 417,
+ // 416,
+ // 415,
+ // 414,
+ // 413,
+ // 412,
+ // 411,
+ // 410,
+ // 409,
408,
407,
406,
405,
- // 404 NA
- // 403 NA
- // 402 NA
- // 401 NA
- // 400 NA
- // 399 NA
- // 398 NA
- 397,
- 396,
- 395,
- // 394 NA
- // 393 NA
- 392,
+ 404,
+ // 403,
+ 402,
+ // 401,
+ 400,
+ // 399,
+ 398,
+ // 397,
+ // 396,
+ // 395,
+ 394,
+ 393,
+ // 392,
391,
390,
389,
388,
- 387,
- 386,
- // 385 NA
- // 384 NA
- 383,
- 382,
+ // 387,
+ // 386,
+ 385,
+ // 384,
+ // 383,
+ // 382,
381,
- // 380 NA
+ 380,
379,
378,
377,
376,
- 375,
+ // 375,
374,
- 373,
- 372,
- 371,
- 370,
- 369,
- 368,
- 367,
- // 366 NA
- 365,
+ // 373,
+ // 372,
+ // 371,
+ // 370,
+ // 369,
+ // 368,
+ // 367,
+ // 366,
+ // 365,
364,
- // 363 NA
- 362,
- 361,
+ // 363,
+ // 362,
+ // 361,
360,
359,
358,
- 357,
- // 356 NA
- 355,
- // 354 NA
+ // 357,
+ // 356,
+ // 355,
+ // 354,
353,
352,
351,
- 350,
- 349,
- 348,
+ // 350,
+ // 349,
+ // 348,
347,
- 346,
+ // 346,
345,
- 344,
+ // 344,
343,
- // 342 NA
+ // 342,
341,
- // 340 NA
+ // 340,
339,
338,
337,
336,
335,
334,
- // 333 NA
- // 332 NA
+ 333,
+ // 332,
331,
330,
- 329,
+ // 329,
328,
327,
- // 326 NA
+ 326,
325,
324,
323,
- // 322 NA
- // 321 NA
+ 322,
+ // 321,
320,
- // 319 NA
- 318,
- 317,
- // 316 NA
+ 319,
+ // 318,
+ // 317,
+ // 316,
315,
314,
- 313,
- 312,
+ // 313,
+ // 312,
311,
310,
309,
308,
- // 307 NA
+ 307,
306,
305,
- // 304 NA
- 303,
+ // 304,
+ // 303,
302,
301,
- // 300 NA
- // 299 NA
+ 300,
+ 299,
298,
297,
- 296,
+ // 296,
295,
294,
293,
@@ -2158,11 +1352,11 @@ static const int included_patches[] = {
290,
289,
288,
- // 287 NA
- 286,
+ 287,
+ // 286,
285,
284,
- // 283 NA
+ 283,
282,
281,
280,
@@ -2172,119 +1366,119 @@ static const int included_patches[] = {
276,
275,
274,
- // 273 NA
+ 273,
272,
- // 271 NA
- // 270 NA
+ 271,
+ 270,
269,
268,
267,
266,
- 265,
- 264,
- // 263 NA
- 262,
- 261,
+ // 265,
+ // 264,
+ // 263,
+ // 262,
+ // 261,
260,
- // 259 NA
- // 258 NA
- // 257 NA
- 256,
- // 255 NA
- // 254 NA
+ 259,
+ 258,
+ 257,
+ // 256,
+ // 255,
+ // 254,
253,
- // 252 NA
- 251,
- // 250 NA
+ // 252,
+ // 251,
+ 250,
249,
248,
247,
- // 246 NA
+ 246,
245,
- // 244 NA
+ 244,
243,
242,
241,
240,
239,
- // 238 NA
+ // 238,
237,
236,
235,
234,
- 233,
+ // 233,
232,
- 231,
- 230,
+ // 231,
+ // 230,
229,
- // 228 NA
- // 227 NA
+ // 228,
+ 227,
226,
- // 225 NA
- // 224 NA
- // 223 NA
- // 222 NA
+ 225,
+ 224,
+ 223,
+ 222,
221,
220,
219,
218,
- // 217 NA
- // 216 NA
+ 217,
+ // 216,
215,
- // 214 NA
+ 214,
213,
- // 212 NA
+ 212,
211,
- 210,
+ // 210,
209,
- // 208 NA
- 207,
- // 206 NA
+ 208,
+ // 207,
+ 206,
205,
204,
203,
- // 202 NA
+ 202,
201,
- // 200 NA
+ 200,
199,
- // 198 NA
- // 197 NA
- // 196 NA
- // 195 NA
- // 194 NA
+ 198,
+ // 197,
+ 196,
+ 195,
+ 194,
193,
192,
191,
- // 190 NA
- // 189 NA
- // 188 NA
+ 190,
+ 189,
+ 188,
187,
186,
- // 185 NA
- 184,
- // 183 NA
- // 182 NA
+ 185,
+ // 184,
+ 183,
+ 182,
181,
- // 180 NA
- // 179 NA
+ 180,
+ 179,
178,
- // 177 NA
- // 176 NA
- // 175 NA
- // 174 NA
+ 177,
+ 176,
+ 175,
+ 174,
173,
172,
171,
170,
169,
- // 168 NA
+ 168,
167,
166,
165,
- // 164 NA
- // 163 NA
- // 162 NA
- // 161 NA
+ 164,
+ 163,
+ 162,
+ 161,
160,
159,
158,
@@ -2445,20 +1639,10 @@ static const int included_patches[] = {
3,
2,
1,
- 0
+ 0,
};
// clang-format on
-/// Place to put a short description when adding a feature with a patch.
-/// Keep it short, e.g.,: "relative numbers", "persistent undo".
-/// Also add a comment marker to separate the lines.
-/// See the official Vim patches for the diff format: It must use a context of
-/// one line only. Create it by hand or use "diff -C2" and edit the patch.
-static char *(extra_patches[]) = {
- // Add your patch description below this line
- NULL
-};
-
/// Compares a version string to the current Nvim version.
///
/// @param version Version string like "1.3.42"
@@ -2582,13 +1766,7 @@ static void list_features(void)
msg_putchar('\n');
}
} else {
- while (msg_col % width) {
- int old_msg_col = msg_col;
- msg_putchar(' ');
- if (old_msg_col == msg_col) {
- break; // XXX: Avoid infinite loop.
- }
- }
+ msg_putchar(' ');
}
} else {
if (msg_col > 0) {
@@ -2596,30 +1774,27 @@ static void list_features(void)
}
}
}
- MSG_PUTS("For differences from Vim, see :help vim-differences\n\n");
+ MSG_PUTS("See \":help feature-compile\"\n\n");
+}
+
+void list_lua_version(void)
+{
+ typval_T luaver_tv;
+ typval_T arg = { .v_type = VAR_UNKNOWN }; // No args.
+ char *luaver_expr = "((jit and jit.version) and jit.version or _VERSION)";
+ executor_eval_lua(cstr_as_string(luaver_expr), &arg, &luaver_tv);
+ assert(luaver_tv.v_type == VAR_STRING);
+ MSG(luaver_tv.vval.v_string);
+ xfree(luaver_tv.vval.v_string);
}
void list_version(void)
{
- // When adding features here, don't forget to update the list of
- // internal variables in eval.c!
MSG(longVersion);
MSG(version_buildtype);
+ list_lua_version();
MSG(version_cflags);
- // Print the list of extra patch descriptions if there is at least one.
- char *s = "";
- if (extra_patches[0] != NULL) {
- MSG_PUTS(_("\nExtra patches: "));
- s = "";
-
- for (int i = 0; extra_patches[i] != NULL; ++i) {
- MSG_PUTS(s);
- s = ", ";
- MSG_PUTS(extra_patches[i]);
- }
- }
-
#ifdef HAVE_PATHDEF
if ((*compiled_user != NUL) || (*compiled_sys != NUL)) {
@@ -2637,7 +1812,7 @@ void list_version(void)
}
#endif // ifdef HAVE_PATHDEF
- version_msg(_("\n\nOptional features included (+) or not (-): "));
+ version_msg(_("\n\nFeatures: "));
list_features();
@@ -2660,6 +1835,8 @@ void list_version(void)
version_msg("\"\n");
}
#endif // ifdef HAVE_PATHDEF
+
+ version_msg("\nRun :checkhealth for more info");
}
/// Output a string for the version message. If it's going to wrap, output a
@@ -2686,7 +1863,7 @@ static void version_msg(char *s)
/// Show the intro message when not editing a file.
void maybe_intro_message(void)
{
- if (bufempty()
+ if (BUFEMPTY()
&& (curbuf->b_fname == NULL)
&& (firstwin->w_next == NULL)
&& (vim_strchr(p_shm, SHM_INTRO) == NULL)) {
@@ -2709,12 +1886,11 @@ void intro_message(int colon)
static char *(lines[]) = {
N_(NVIM_VERSION_LONG),
"",
- N_("by Bram Moolenaar et al."),
N_("Nvim is open source and freely distributable"),
N_("https://neovim.io/community"),
"",
N_("type :help nvim<Enter> if you are new! "),
- N_("type :CheckHealth<Enter> to optimize Nvim"),
+ N_("type :checkhealth<Enter> to optimize Nvim"),
N_("type :q<Enter> to exit "),
N_("type :help<Enter> for help "),
"",
diff --git a/src/nvim/version.h b/src/nvim/version.h
index a0babfb156..c10f6fa534 100644
--- a/src/nvim/version.h
+++ b/src/nvim/version.h
@@ -10,14 +10,14 @@ extern char* longVersion;
//
// Vim version number, name, etc. Patchlevel is defined in version.c.
//
-#define VIM_VERSION_MAJOR 7
-#define VIM_VERSION_MINOR 4
+#define VIM_VERSION_MAJOR 8
+#define VIM_VERSION_MINOR 0
#define VIM_VERSION_100 (VIM_VERSION_MAJOR * 100 + VIM_VERSION_MINOR)
// used for the runtime directory name
-#define VIM_VERSION_NODOT "vim74"
+#define VIM_VERSION_NODOT "vim80"
// swap file compatibility (max. length is 6 chars)
-#define VIM_VERSION_SHORT "7.4"
+#define VIM_VERSION_SHORT "8.0"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "version.h.generated.h"
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index f29ccdd296..0c13d331c8 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -4,37 +4,28 @@
#include "nvim/types.h"
#include "nvim/pos.h" // for linenr_T, MAXCOL, etc...
-/* Some defines from the old feature.h */
+// Some defines from the old feature.h
#define SESSION_FILE "Session.vim"
#define MAX_MSG_HIST_LEN 200
#define SYS_OPTWIN_FILE "$VIMRUNTIME/optwin.vim"
#define RUNTIME_DIRNAME "runtime"
-/* end */
+
#include "auto/config.h"
#define HAVE_PATHDEF
-/*
- * Check if configure correctly managed to find sizeof(int). If this failed,
- * it becomes zero. This is likely a problem of not being able to run the
- * test program. Other items from configure may also be wrong then!
- */
+// Check if configure correctly managed to find sizeof(int). If this failed,
+// it becomes zero. This is likely a problem of not being able to run the
+// test program. Other items from configure may also be wrong then!
#if (SIZEOF_INT == 0)
# error Configure did not run properly.
#endif
-#include "nvim/os/os_defs.h" /* bring lots of system header files */
+#include "nvim/os/os_defs.h" // bring lots of system header files
/// length of a buffer to store a number in ASCII (64 bits binary + NUL)
enum { NUMBUFLEN = 65 };
-// flags for vim_str2nr()
-#define STR2NR_BIN 1
-#define STR2NR_OCT 2
-#define STR2NR_HEX 4
-#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX)
-#define STR2NR_FORCE 8 // only when ONE of the above is used
-
#define MAX_TYPENR 65535
#define ROOT_UID 0
@@ -44,41 +35,41 @@ enum { NUMBUFLEN = 65 };
#include "nvim/gettext.h"
-/* special attribute addition: Put message in history */
+// special attribute addition: Put message in history
#define MSG_HIST 0x1000
-/*
- * values for State
- *
- * The lower bits up to 0x20 are used to distinguish normal/visual/op_pending
- * and cmdline/insert+replace mode. This is used for mapping. If none of
- * these bits are set, no mapping is done.
- * The upper bits are used to distinguish between other states.
- */
-#define NORMAL 0x01 /* Normal mode, command expected */
-#define VISUAL 0x02 /* Visual mode - use get_real_state() */
-#define OP_PENDING 0x04 /* Normal mode, operator is pending - use
- get_real_state() */
-#define CMDLINE 0x08 /* Editing command line */
-#define INSERT 0x10 /* Insert mode */
-#define LANGMAP 0x20 /* Language mapping, can be combined with
- INSERT and CMDLINE */
-
-#define REPLACE_FLAG 0x40 /* Replace mode flag */
+
+// values for State
+//
+// The lower bits up to 0x20 are used to distinguish normal/visual/op_pending
+// and cmdline/insert+replace mode. This is used for mapping. If none of
+// these bits are set, no mapping is done.
+// The upper bits are used to distinguish between other states.
+
+#define NORMAL 0x01 // Normal mode, command expected
+#define VISUAL 0x02 // Visual mode - use get_real_state()
+#define OP_PENDING 0x04 // Normal mode, operator is pending - use
+ // get_real_state()
+#define CMDLINE 0x08 // Editing command line
+#define INSERT 0x10 // Insert mode
+#define LANGMAP 0x20 // Language mapping, can be combined with
+ // INSERT and CMDLINE
+
+#define REPLACE_FLAG 0x40 // Replace mode flag
#define REPLACE (REPLACE_FLAG + INSERT)
-# define VREPLACE_FLAG 0x80 /* Virtual-replace mode flag */
+# define VREPLACE_FLAG 0x80 // Virtual-replace mode flag
# define VREPLACE (REPLACE_FLAG + VREPLACE_FLAG + INSERT)
#define LREPLACE (REPLACE_FLAG + LANGMAP)
-#define NORMAL_BUSY (0x100 + NORMAL) /* Normal mode, busy with a command */
-#define HITRETURN (0x200 + NORMAL) /* waiting for return or command */
-#define ASKMORE 0x300 /* Asking if you want --more-- */
-#define SETWSIZE 0x400 /* window size has changed */
-#define ABBREV 0x500 /* abbreviation instead of mapping */
-#define EXTERNCMD 0x600 /* executing an external command */
-#define SHOWMATCH (0x700 + INSERT) /* show matching paren */
-#define CONFIRM 0x800 /* ":confirm" prompt */
-#define SELECTMODE 0x1000 /* Select mode, only for mappings */
+#define NORMAL_BUSY (0x100 + NORMAL) // Normal mode, busy with a command
+#define HITRETURN (0x200 + NORMAL) // waiting for return or command
+#define ASKMORE 0x300 // Asking if you want --more--
+#define SETWSIZE 0x400 // window size has changed
+#define ABBREV 0x500 // abbreviation instead of mapping
+#define EXTERNCMD 0x600 // executing an external command
+#define SHOWMATCH (0x700 + INSERT) // show matching paren
+#define CONFIRM 0x800 // ":confirm" prompt
+#define SELECTMODE 0x1000 // Select mode, only for mappings
#define TERM_FOCUS 0x2000 // Terminal focus mode
#define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview.
@@ -94,13 +85,13 @@ typedef enum {
BACKWARD_FILE = (-3),
} Direction;
-/* return values for functions */
+// return values for functions
#if !(defined(OK) && (OK == 1))
-/* OK already defined to 1 in MacOS X curses, skip this */
+// OK already defined to 1 in MacOS X curses, skip this
# define OK 1
#endif
#define FAIL 0
-#define NOTDONE 2 /* not OK or FAIL but skipped */
+#define NOTDONE 2 // not OK or FAIL but skipped
// Type values for type().
#define VAR_TYPE_NUMBER 0
@@ -111,9 +102,9 @@ typedef enum {
#define VAR_TYPE_FLOAT 5
#define VAR_TYPE_BOOL 6
-/*
- * values for xp_context when doing command line completion
- */
+
+// values for xp_context when doing command line completion
+
enum {
EXPAND_UNSUCCESSFUL = -2,
EXPAND_OK = -1,
@@ -163,62 +154,61 @@ enum {
EXPAND_SYNTIME,
EXPAND_USER_ADDR_TYPE,
EXPAND_PACKADD,
+ EXPAND_MESSAGES,
+ EXPAND_CHECKHEALTH,
};
+// Minimal size for block 0 of a swap file.
+// NOTE: This depends on size of struct block0! It's not done with a sizeof(),
+// because struct block0 is defined in memline.c (Sorry).
+// The maximal block size is arbitrary.
-/*
- * Minimal size for block 0 of a swap file.
- * NOTE: This depends on size of struct block0! It's not done with a sizeof(),
- * because struct block0 is defined in memline.c (Sorry).
- * The maximal block size is arbitrary.
- */
#define MIN_SWAP_PAGE_SIZE 1048
#define MAX_SWAP_PAGE_SIZE 50000
-/*
- * Boolean constants
- */
+// Boolean constants
+
#ifndef TRUE
-# define FALSE 0 /* note: this is an int, not a long! */
+# define FALSE 0 // note: this is an int, not a long!
# define TRUE 1
#endif
-#define MAYBE 2 /* sometimes used for a variant on TRUE */
+#define MAYBE 2 // sometimes used for a variant on TRUE
+
+#define STATUS_HEIGHT 1 // height of a status line under a window
+#define QF_WINHEIGHT 10 // default height for quickfix window
-#define STATUS_HEIGHT 1 /* height of a status line under a window */
-#define QF_WINHEIGHT 10 /* default height for quickfix window */
-/*
- * Buffer sizes
- */
+// Buffer sizes
+
#ifndef CMDBUFFSIZE
-# define CMDBUFFSIZE 256 /* size of the command processing buffer */
+# define CMDBUFFSIZE 256 // size of the command processing buffer
#endif
-#define LSIZE 512 /* max. size of a line in the tags file */
+#define LSIZE 512 // max. size of a line in the tags file
-#define DIALOG_MSG_SIZE 1000 /* buffer size for dialog_msg() */
+#define DIALOG_MSG_SIZE 1000 // buffer size for dialog_msg()
enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
-/*
- * Maximum length of key sequence to be mapped.
- * Must be able to hold an Amiga resize report.
- */
+
+// Maximum length of key sequence to be mapped.
+// Must be able to hold an Amiga resize report.
+
#define MAXMAPLEN 50
-/* Size in bytes of the hash used in the undo file. */
+// Size in bytes of the hash used in the undo file.
#define UNDO_HASH_SIZE 32
-/*
- * defines to avoid typecasts from (char_u *) to (char *) and back
- * (vim_strchr() and vim_strrchr() are now in alloc.c)
- */
+
+// defines to avoid typecasts from (char_u *) to (char *) and back
+// (vim_strchr() and vim_strrchr() are now in alloc.c)
+
#define STRLEN(s) strlen((char *)(s))
#define STRCPY(d, s) strcpy((char *)(d), (char *)(s))
#define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n))
@@ -235,7 +225,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
# endif
#endif
-/* Like strcpy() but allows overlapped source and destination. */
+// Like strcpy() but allows overlapped source and destination.
#define STRMOVE(d, s) memmove((d), (s), STRLEN(s) + 1)
#ifdef HAVE_STRNCASECMP
@@ -260,8 +250,8 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// destination and mess up the screen.
#define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno))
-#define SHOWCMD_COLS 10 /* columns needed by shown command */
-#define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */
+#define SHOWCMD_COLS 10 // columns needed by shown command
+#define STL_MAX_ITEM 80 // max nr of %<flag> in statusline
/// Compare file names
///
@@ -280,28 +270,27 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
(const char *)(y), \
(size_t)(n))
-/*
- * Enums need a typecast to be used as array index (for Ultrix).
- */
+
+// Enums need a typecast to be used as array index (for Ultrix).
#define hl_attr(n) highlight_attr[(int)(n)]
#define term_str(n) term_strings[(int)(n)]
-/* Maximum number of bytes in a multi-byte character. It can be one 32-bit
- * character of up to 6 bytes, or one 16-bit character of up to three bytes
- * plus six following composing characters of three bytes each. */
+/// Maximum number of bytes in a multi-byte character. It can be one 32-bit
+/// character of up to 6 bytes, or one 16-bit character of up to three bytes
+/// plus six following composing characters of three bytes each.
#define MB_MAXBYTES 21
-/* This has to go after the include of proto.h, as proto/gui.pro declares
- * functions of these names. The declarations would break if the defines had
- * been seen at that stage. But it must be before globals.h, where error_ga
- * is declared. */
+// This has to go after the include of proto.h, as proto/gui.pro declares
+// functions of these names. The declarations would break if the defines had
+// been seen at that stage. But it must be before globals.h, where error_ga
+// is declared.
#define mch_errmsg(str) fprintf(stderr, "%s", (str))
#define display_errors() fflush(stderr)
#define mch_msg(str) printf("%s", (str))
-#include "nvim/globals.h" /* global variables and messages */
-#include "nvim/buffer_defs.h" /* buffer and windows */
-#include "nvim/ex_cmds_defs.h" /* Ex command defines */
+#include "nvim/globals.h" // global variables and messages
+#include "nvim/buffer_defs.h" // buffer and windows
+#include "nvim/ex_cmds_defs.h" // Ex command defines
# define SET_NO_HLSEARCH(flag) no_hlsearch = (flag); set_vim_var_nr( \
VV_HLSEARCH, !no_hlsearch && p_hls)
@@ -319,4 +308,13 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// Lowest number used for window ID. Cannot have this many windows per tab.
#define LOWEST_WIN_ID 1000
-#endif /* NVIM_VIM_H */
+// BSD is supposed to cover FreeBSD and similar systems.
+#if (defined(BSD) || defined(__FreeBSD_kernel__)) && defined(S_ISCHR)
+# define OPEN_CHR_FILES
+#endif
+
+// Replacement for nchar used by nv_replace().
+#define REPLACE_CR_NCHAR -1
+#define REPLACE_NL_NCHAR -2
+
+#endif // NVIM_VIM_H
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
new file mode 100644
index 0000000000..4196ecb9d2
--- /dev/null
+++ b/src/nvim/viml/parser/expressions.c
@@ -0,0 +1,3102 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+/// VimL expression parser
+
+// Planned incompatibilities (to be included into vim_diff.txt when this parser
+// will be an actual part of VimL evaluation process):
+//
+// 1. Expressions are first fully parsed and only then executed. This means
+// that while ":echo [system('touch abc')" will create file "abc" in Vim and
+// only then raise syntax error regarding missing comma in list in Neovim
+// trying to execute that will immediately raise syntax error regarding
+// missing list end without actually executing anything.
+// 2. Expressions are first fully parsed, without considering any runtime
+// information. This means things like that "d.a" does not change its
+// meaning depending on type of "d" (or whether Vim is currently executing or
+// skipping). For compatibility reasons the dot thus may either be “concat
+// or subscript” operator or just “concat” operator.
+// 3. Expressions parser is aware whether it is called for :echo or <C-r>=.
+// This means that while "<C-r>=1 | 2<CR>" is equivalent to "<C-r>=1<CR>"
+// because "| 2" part is left to be treated as a command separator and then
+// ignored in Neovim it is an error.
+// 4. Expressions parser has generally better error reporting. But for
+// compatibility reasons most errors have error code E15 while error messages
+// are significantly different from Vim’s E15. Also some error codes were
+// retired because of being harder to emulate or because of them being
+// a result of differences in parsing process: e.g. with ":echo {a, b}" Vim
+// will attempt to parse expression as lambda, fail, check whether it is
+// a curly-braces-name, fail again, and evaluate that as a dictionary, giving
+// error regarding undefined variable "a" (or about missing colon). Neovim
+// will not try to evaluate anything here: comma right after an argument name
+// means that expression may not be anything, but lambda, so the resulting
+// error message will never be about missing variable or colon: it will be
+// about missing arrow (or a continuation of argument list).
+// 5. Failing to parse expression always gives exactly one error message: no
+// more stack of error messages like >
+//
+// :echo [1,
+// E697: Missing end of List ']':
+// E15: Invalid expression: [1,
+//
+// < , just exactly one E697 message.
+// 6. Some expressions involving calling parenthesis which are treated
+// separately by Vim even when not separated by spaces are treated as one
+// expression by Neovim: e.g. ":echo (1)(1)" will yield runtime error after
+// failing to call "1", while Vim will echo "1 1". Reasoning is the same:
+// type of what is in the first expression is generally not known when
+// parsing, so to have separate expressions like this separate them with
+// spaces.
+// 7. 'isident' no longer applies to environment variables, they always include
+// ASCII alphanumeric characters and underscore and nothing except this.
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+
+#include "nvim/vim.h"
+#include "nvim/memory.h"
+#include "nvim/types.h"
+#include "nvim/charset.h"
+#include "nvim/ascii.h"
+#include "nvim/assert.h"
+#include "nvim/lib/kvec.h"
+#include "nvim/eval/typval.h"
+
+#include "nvim/viml/parser/expressions.h"
+#include "nvim/viml/parser/parser.h"
+
+#define vim_str2nr(s, ...) vim_str2nr((const char_u *)(s), __VA_ARGS__)
+
+typedef kvec_withinit_t(ExprASTNode **, 16) ExprASTStack;
+
+/// Which nodes may be wanted
+typedef enum {
+ /// Operators: function call, subscripts, binary operators, …
+ ///
+ /// For unrestricted expressions.
+ kENodeOperator,
+ /// Values: literals, variables, nested expressions, unary operators.
+ ///
+ /// For unrestricted expressions as well, implies that top item in AST stack
+ /// points to NULL.
+ kENodeValue,
+} ExprASTWantedNode;
+
+/// Parse type: what is being parsed currently
+typedef enum {
+ /// Parsing regular VimL expression
+ kEPTExpr = 0,
+ /// Parsing lambda arguments
+ ///
+ /// Just like parsing function arguments, but it is valid to be ended with an
+ /// arrow only.
+ kEPTLambdaArguments,
+ /// Assignment: parsing for :let
+ kEPTAssignment,
+ /// Single assignment: used when lists are not allowed (i.e. when nesting)
+ kEPTSingleAssignment,
+} ExprASTParseType;
+
+typedef kvec_withinit_t(ExprASTParseType, 4) ExprASTParseTypeStack;
+
+/// Operator priority level
+typedef enum {
+ kEOpLvlInvalid = 0,
+ kEOpLvlComplexIdentifier,
+ kEOpLvlParens,
+ kEOpLvlAssignment,
+ kEOpLvlArrow,
+ kEOpLvlComma,
+ kEOpLvlColon,
+ kEOpLvlTernaryValue,
+ kEOpLvlTernary,
+ kEOpLvlOr,
+ kEOpLvlAnd,
+ kEOpLvlComparison,
+ kEOpLvlAddition, ///< Addition, subtraction and concatenation.
+ kEOpLvlMultiplication, ///< Multiplication, division and modulo.
+ kEOpLvlUnary, ///< Unary operations: not, minus, plus.
+ kEOpLvlSubscript, ///< Subscripts.
+ kEOpLvlValue, ///< Values: literals, variables, nested expressions, …
+} ExprOpLvl;
+
+/// Operator associativity
+typedef enum {
+ kEOpAssNo= 'n', ///< Not associative / not applicable.
+ kEOpAssLeft = 'l', ///< Left associativity.
+ kEOpAssRight = 'r', ///< Right associativity.
+} ExprOpAssociativity;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "viml/parser/expressions.c.generated.h"
+#endif
+
+/// Character used as a separator in autoload function/variable names.
+#define AUTOLOAD_CHAR '#'
+
+/// Scale number by a given factor
+///
+/// Used to apply exponent to a number. Idea taken from uClibc.
+///
+/// @param[in] num Number to scale. Does not bother doing anything if it is
+/// zero.
+/// @param[in] base Base, should be 10 since non-decimal floating-point
+/// numbers are not supported.
+/// @param[in] exponent Exponent to scale by.
+/// @param[in] exponent_negative True if exponent is negative.
+static inline float_T scale_number(const float_T num,
+ const uint8_t base,
+ const uvarnumber_T exponent,
+ const bool exponent_negative)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST
+{
+ if (num == 0 || exponent == 0) {
+ return num;
+ }
+ assert(base);
+ uvarnumber_T exp = exponent;
+ float_T p_base = (float_T)base;
+ float_T ret = num;
+ while (exp) {
+ if (exp & 1) {
+ if (exponent_negative) {
+ ret /= p_base;
+ } else {
+ ret *= p_base;
+ }
+ }
+ exp >>= 1;
+ p_base *= p_base;
+ }
+ return ret;
+}
+
+/// Get next token for the VimL expression input
+///
+/// @param pstate Parser state.
+/// @param[in] flags Flags, @see LexExprFlags.
+///
+/// @return Next token.
+LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ LexExprToken ret = {
+ .type = kExprLexInvalid,
+ .start = pstate->pos,
+ };
+ ParserLine pline;
+ if (!viml_parser_get_remaining_line(pstate, &pline)) {
+ ret.type = kExprLexEOC;
+ return ret;
+ }
+ if (pline.size <= 0) {
+ ret.len = 0;
+ ret.type = kExprLexEOC;
+ goto viml_pexpr_next_token_adv_return;
+ }
+ ret.len = 1;
+ const uint8_t schar = (uint8_t)pline.data[0];
+#define GET_CCS(ret, pline) \
+ do { \
+ if (ret.len < pline.size \
+ && strchr("?#", pline.data[ret.len]) != NULL) { \
+ ret.data.cmp.ccs = \
+ (ExprCaseCompareStrategy)pline.data[ret.len]; \
+ ret.len++; \
+ } else { \
+ ret.data.cmp.ccs = kCCStrategyUseOption; \
+ } \
+ } while (0)
+ switch (schar) {
+ // Paired brackets.
+#define BRACKET(typ, opning, clsing) \
+ case opning: \
+ case clsing: { \
+ ret.type = typ; \
+ ret.data.brc.closing = (schar == clsing); \
+ break; \
+ }
+ BRACKET(kExprLexParenthesis, '(', ')')
+ BRACKET(kExprLexBracket, '[', ']')
+ BRACKET(kExprLexFigureBrace, '{', '}')
+#undef BRACKET
+
+ // Single character tokens without data.
+#define CHAR(typ, ch) \
+ case ch: { \
+ ret.type = typ; \
+ break; \
+ }
+ CHAR(kExprLexQuestion, '?')
+ CHAR(kExprLexColon, ':')
+ CHAR(kExprLexComma, ',')
+#undef CHAR
+
+ // Multiplication/division/modulo.
+#define MUL(mul_type, ch) \
+ case ch: { \
+ ret.type = kExprLexMultiplication; \
+ ret.data.mul.type = mul_type; \
+ break; \
+ }
+ MUL(kExprLexMulMul, '*')
+ MUL(kExprLexMulDiv, '/')
+ MUL(kExprLexMulMod, '%')
+#undef MUL
+
+#define CHARREG(typ, cond) \
+ do { \
+ ret.type = typ; \
+ for (; (ret.len < pline.size \
+ && cond(pline.data[ret.len])) \
+ ; ret.len++) { \
+ } \
+ } while (0)
+
+ // Whitespace.
+ case ' ':
+ case TAB: {
+ CHARREG(kExprLexSpacing, ascii_iswhite);
+ break;
+ }
+
+ // Control character, except for NUL, NL and TAB.
+ case Ctrl_A: case Ctrl_B: case Ctrl_C: case Ctrl_D: case Ctrl_E:
+ case Ctrl_F: case Ctrl_G: case Ctrl_H:
+
+ case Ctrl_K: case Ctrl_L: case Ctrl_M: case Ctrl_N: case Ctrl_O:
+ case Ctrl_P: case Ctrl_Q: case Ctrl_R: case Ctrl_S: case Ctrl_T:
+ case Ctrl_U: case Ctrl_V: case Ctrl_W: case Ctrl_X: case Ctrl_Y:
+ case Ctrl_Z: {
+#define ISCTRL(schar) (schar < ' ')
+ CHARREG(kExprLexInvalid, ISCTRL);
+ ret.data.err.type = kExprLexSpacing;
+ ret.data.err.msg =
+ _("E15: Invalid control character present in input: %.*s");
+ break;
+#undef ISCTRL
+ }
+
+ // Number.
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9': {
+ ret.data.num.is_float = false;
+ ret.data.num.base = 10;
+ size_t frac_start = 0;
+ size_t exp_start = 0;
+ size_t frac_end = 0;
+ bool exp_negative = false;
+ CHARREG(kExprLexNumber, ascii_isdigit);
+ if (flags & kELFlagAllowFloat) {
+ const LexExprToken non_float_ret = ret;
+ if (pline.size > ret.len + 1
+ && pline.data[ret.len] == '.'
+ && ascii_isdigit(pline.data[ret.len + 1])) {
+ ret.len++;
+ frac_start = ret.len;
+ frac_end = ret.len;
+ ret.data.num.is_float = true;
+ for (; ret.len < pline.size && ascii_isdigit(pline.data[ret.len])
+ ; ret.len++) {
+ // A small optimization: trailing zeroes in fractional part do not
+ // add anything to significand, so it is useless to include them in
+ // frac_end.
+ if (pline.data[ret.len] != '0') {
+ frac_end = ret.len + 1;
+ }
+ }
+ if (pline.size > ret.len + 1
+ && (pline.data[ret.len] == 'e'
+ || pline.data[ret.len] == 'E')
+ && ((pline.size > ret.len + 2
+ && (pline.data[ret.len + 1] == '+'
+ || pline.data[ret.len + 1] == '-')
+ && ascii_isdigit(pline.data[ret.len + 2]))
+ || ascii_isdigit(pline.data[ret.len + 1]))) {
+ ret.len++;
+ if (pline.data[ret.len] == '+'
+ || (exp_negative = (pline.data[ret.len] == '-'))) {
+ ret.len++;
+ }
+ exp_start = ret.len;
+ CHARREG(kExprLexNumber, ascii_isdigit);
+ }
+ }
+ if (pline.size > ret.len
+ && (pline.data[ret.len] == '.'
+ || ASCII_ISALPHA(pline.data[ret.len]))) {
+ ret = non_float_ret;
+ }
+ }
+ // TODO(ZyX-I): detect overflows
+ if (ret.data.num.is_float) {
+ // Vim used to use string2float here which in turn uses strtod(). There
+ // are two problems with this approach:
+ // 1. strtod() is locale-dependent. Not sure how it is worked around so
+ // that I do not see relevant bugs, but it still does not look like
+ // a good idea.
+ // 2. strtod() does not accept length argument.
+ //
+ // The below variant of parsing floats was recognized as acceptable
+ // because it is basically how uClibc does the thing: it generates
+ // a number ignoring decimal point (but recording its position), then
+ // uses recorded position to scale number down when processing exponent.
+ float_T significand_part = 0;
+ uvarnumber_T exp_part = 0;
+ const size_t frac_size = (size_t)(frac_end - frac_start);
+ for (size_t i = 0; i < frac_end; i++) {
+ if (i == frac_start - 1) {
+ continue;
+ }
+ significand_part = significand_part * 10 + (pline.data[i] - '0');
+ }
+ if (exp_start) {
+ vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part,
+ (int)(ret.len - exp_start));
+ }
+ if (exp_negative) {
+ exp_part += frac_size;
+ } else {
+ if (exp_part < frac_size) {
+ exp_negative = true;
+ exp_part = frac_size - exp_part;
+ } else {
+ exp_part -= frac_size;
+ }
+ }
+ ret.data.num.val.floating = scale_number(significand_part, 10, exp_part,
+ exp_negative);
+ } else {
+ int len;
+ int prep;
+ vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL,
+ &ret.data.num.val.integer, (int)pline.size);
+ ret.len = (size_t)len;
+ const uint8_t bases[] = {
+ [0] = 10,
+ ['0'] = 8,
+ ['x'] = 16, ['X'] = 16,
+ ['b'] = 2, ['B'] = 2,
+ };
+ ret.data.num.base = bases[prep];
+ }
+ break;
+ }
+
+#define ISWORD_OR_AUTOLOAD(x) \
+ (ascii_isident(x) || (x) == AUTOLOAD_CHAR)
+
+ // Environment variable.
+ case '$': {
+ CHARREG(kExprLexEnv, ascii_isident);
+ break;
+ }
+
+ // Normal variable/function name.
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '_': {
+ ret.data.var.scope = 0;
+ ret.data.var.autoload = false;
+ CHARREG(kExprLexPlainIdentifier, ascii_isident);
+ // "is" and "isnot" operators.
+ if (!(flags & kELFlagIsNotCmp)
+ && ((ret.len == 2 && memcmp(pline.data, "is", 2) == 0)
+ || (ret.len == 5 && memcmp(pline.data, "isnot", 5) == 0))) {
+ ret.type = kExprLexComparison;
+ ret.data.cmp.type = kExprCmpIdentical;
+ ret.data.cmp.inv = (ret.len == 5);
+ GET_CCS(ret, pline);
+ // Scope: `s:`, etc.
+ } else if (ret.len == 1
+ && pline.size > 1
+ && memchr(EXPR_VAR_SCOPE_LIST, schar,
+ sizeof(EXPR_VAR_SCOPE_LIST)) != NULL
+ && pline.data[ret.len] == ':'
+ && !(flags & kELFlagForbidScope)) {
+ ret.len++;
+ ret.data.var.scope = (ExprVarScope)schar;
+ CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD);
+ ret.data.var.autoload = (
+ memchr(pline.data + 2, AUTOLOAD_CHAR, ret.len - 2)
+ != NULL);
+ // Previous CHARREG stopped at autoload character in order to make it
+ // possible to detect `is#`. Continue now with autoload characters
+ // included.
+ //
+ // Warning: there is ambiguity for the lexer: `is#Foo(1)` is a call of
+ // function `is#Foo()`, `1is#Foo(1)` is a comparison `1 is# Foo(1)`. This
+ // needs to be resolved on the higher level where context is available.
+ } else if (pline.size > ret.len
+ && pline.data[ret.len] == AUTOLOAD_CHAR) {
+ ret.data.var.autoload = true;
+ CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD);
+ }
+ break;
+ }
+
+#undef ISWORD_OR_AUTOLOAD
+#undef CHARREG
+
+ // Option.
+ case '&': {
+#define OPTNAMEMISS(ret) \
+ do { \
+ ret.type = kExprLexInvalid; \
+ ret.data.err.type = kExprLexOption; \
+ ret.data.err.msg = _("E112: Option name missing: %.*s"); \
+ } while (0)
+ if (pline.size > 1 && pline.data[1] == '&') {
+ ret.type = kExprLexAnd;
+ ret.len++;
+ break;
+ }
+ if (pline.size == 1 || !ASCII_ISALPHA(pline.data[1])) {
+ OPTNAMEMISS(ret);
+ break;
+ }
+ ret.type = kExprLexOption;
+ if (pline.size > 2
+ && pline.data[2] == ':'
+ && memchr(EXPR_OPT_SCOPE_LIST, pline.data[1],
+ sizeof(EXPR_OPT_SCOPE_LIST)) != NULL) {
+ ret.len += 2;
+ ret.data.opt.scope = (ExprOptScope)pline.data[1];
+ ret.data.opt.name = pline.data + 3;
+ } else {
+ ret.data.opt.scope = kExprOptScopeUnspecified;
+ ret.data.opt.name = pline.data + 1;
+ }
+ const char *p = ret.data.opt.name;
+ const char *const e = pline.data + pline.size;
+ if (e - p >= 4 && p[0] == 't' && p[1] == '_') {
+ ret.data.opt.len = 4;
+ ret.len += 4;
+ } else {
+ for (; p < e && ASCII_ISALPHA(*p); p++) {
+ }
+ ret.data.opt.len = (size_t)(p - ret.data.opt.name);
+ if (ret.data.opt.len == 0) {
+ OPTNAMEMISS(ret);
+ } else {
+ ret.len += ret.data.opt.len;
+ }
+ }
+ break;
+#undef OPTNAMEMISS
+ }
+
+ // Register.
+ case '@': {
+ ret.type = kExprLexRegister;
+ if (pline.size > 1) {
+ ret.len++;
+ ret.data.reg.name = (uint8_t)pline.data[1];
+ } else {
+ ret.data.reg.name = -1;
+ }
+ break;
+ }
+
+ // Single quoted string.
+ case '\'': {
+ ret.type = kExprLexSingleQuotedString;
+ ret.data.str.closed = false;
+ for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) {
+ if (pline.data[ret.len] == '\'') {
+ if (ret.len + 1 < pline.size && pline.data[ret.len + 1] == '\'') {
+ ret.len++;
+ } else {
+ ret.data.str.closed = true;
+ }
+ }
+ }
+ break;
+ }
+
+ // Double quoted string.
+ case '"': {
+ ret.type = kExprLexDoubleQuotedString;
+ ret.data.str.closed = false;
+ for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) {
+ if (pline.data[ret.len] == '\\') {
+ if (ret.len + 1 < pline.size) {
+ ret.len++;
+ }
+ } else if (pline.data[ret.len] == '"') {
+ ret.data.str.closed = true;
+ }
+ }
+ break;
+ }
+
+ // Unary not, (un)equality and regex (not) match comparison operators.
+ case '!':
+ case '=': {
+ if (pline.size == 1) {
+ ret.type = (schar == '!' ? kExprLexNot : kExprLexAssignment);
+ ret.data.ass.type = kExprAsgnPlain;
+ break;
+ }
+ ret.type = kExprLexComparison;
+ ret.data.cmp.inv = (schar == '!');
+ if (pline.data[1] == '=') {
+ ret.data.cmp.type = kExprCmpEqual;
+ ret.len++;
+ } else if (pline.data[1] == '~') {
+ ret.data.cmp.type = kExprCmpMatches;
+ ret.len++;
+ } else if (schar == '!') {
+ ret.type = kExprLexNot;
+ } else {
+ ret.type = kExprLexAssignment;
+ ret.data.ass.type = kExprAsgnPlain;
+ }
+ GET_CCS(ret, pline);
+ break;
+ }
+
+ // Less/greater [or equal to] comparison operators.
+ case '>':
+ case '<': {
+ ret.type = kExprLexComparison;
+ const bool haseqsign = (pline.size > 1 && pline.data[1] == '=');
+ if (haseqsign) {
+ ret.len++;
+ }
+ GET_CCS(ret, pline);
+ ret.data.cmp.inv = (schar == '<');
+ ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign)
+ ? kExprCmpGreaterOrEqual
+ : kExprCmpGreater);
+ break;
+ }
+
+ // Minus sign, arrow from lambdas or augmented assignment.
+ case '-': {
+ if (pline.size > 1 && pline.data[1] == '>') {
+ ret.len++;
+ ret.type = kExprLexArrow;
+ } else if (pline.size > 1 && pline.data[1] == '=') {
+ ret.len++;
+ ret.type = kExprLexAssignment;
+ ret.data.ass.type = kExprAsgnSubtract;
+ } else {
+ ret.type = kExprLexMinus;
+ }
+ break;
+ }
+
+ // Sign or augmented assignment.
+#define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \
+ case ch: { \
+ if (pline.size > 1 && pline.data[1] == '=') { \
+ ret.len++; \
+ ret.type = kExprLexAssignment; \
+ ret.data.ass.type = ass_type; \
+ } else { \
+ ret.type = ch_type; \
+ } \
+ break; \
+ }
+ CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd)
+ CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat)
+#undef CHAR_OR_ASSIGN
+
+ // Expression end because Ex command ended.
+ case NUL:
+ case NL: {
+ if (flags & kELFlagForbidEOC) {
+ ret.type = kExprLexInvalid;
+ ret.data.err.msg = _("E15: Unexpected EOC character: %.*s");
+ ret.data.err.type = kExprLexSpacing;
+ } else {
+ ret.type = kExprLexEOC;
+ }
+ break;
+ }
+
+ case '|': {
+ if (pline.size >= 2 && pline.data[ret.len] == '|') {
+ // "||" is or.
+ ret.len++;
+ ret.type = kExprLexOr;
+ } else if (flags & kELFlagForbidEOC) {
+ // Note: `<C-r>=1 | 2<CR>` actually yields 1 in Vim without any
+ // errors. This will be changed here.
+ ret.type = kExprLexInvalid;
+ ret.data.err.msg = _("E15: Unexpected EOC character: %.*s");
+ ret.data.err.type = kExprLexOr;
+ } else {
+ ret.type = kExprLexEOC;
+ }
+ break;
+ }
+
+ // Everything else is not valid.
+ default: {
+ ret.len = (size_t)utfc_ptr2len_len((const char_u *)pline.data,
+ (int)pline.size);
+ ret.type = kExprLexInvalid;
+ ret.data.err.type = kExprLexPlainIdentifier;
+ ret.data.err.msg = _("E15: Unidentified character: %.*s");
+ break;
+ }
+ }
+#undef GET_CCS
+viml_pexpr_next_token_adv_return:
+ if (!(flags & kELFlagPeek)) {
+ viml_parser_advance(pstate, ret.len);
+ }
+ return ret;
+}
+
+static const char *const eltkn_type_tab[] = {
+ [kExprLexInvalid] = "Invalid",
+ [kExprLexMissing] = "Missing",
+ [kExprLexSpacing] = "Spacing",
+ [kExprLexEOC] = "EOC",
+
+ [kExprLexQuestion] = "Question",
+ [kExprLexColon] = "Colon",
+ [kExprLexOr] = "Or",
+ [kExprLexAnd] = "And",
+ [kExprLexComparison] = "Comparison",
+ [kExprLexPlus] = "Plus",
+ [kExprLexMinus] = "Minus",
+ [kExprLexDot] = "Dot",
+ [kExprLexMultiplication] = "Multiplication",
+
+ [kExprLexNot] = "Not",
+
+ [kExprLexNumber] = "Number",
+ [kExprLexSingleQuotedString] = "SingleQuotedString",
+ [kExprLexDoubleQuotedString] = "DoubleQuotedString",
+ [kExprLexOption] = "Option",
+ [kExprLexRegister] = "Register",
+ [kExprLexEnv] = "Env",
+ [kExprLexPlainIdentifier] = "PlainIdentifier",
+
+ [kExprLexBracket] = "Bracket",
+ [kExprLexFigureBrace] = "FigureBrace",
+ [kExprLexParenthesis] = "Parenthesis",
+ [kExprLexComma] = "Comma",
+ [kExprLexArrow] = "Arrow",
+ [kExprLexAssignment] = "Assignment",
+};
+
+const char *const eltkn_cmp_type_tab[] = {
+ [kExprCmpEqual] = "Equal",
+ [kExprCmpMatches] = "Matches",
+ [kExprCmpGreater] = "Greater",
+ [kExprCmpGreaterOrEqual] = "GreaterOrEqual",
+ [kExprCmpIdentical] = "Identical",
+};
+
+const char *const expr_asgn_type_tab[] = {
+ [kExprAsgnPlain] = "Plain",
+ [kExprAsgnAdd] = "Add",
+ [kExprAsgnSubtract] = "Subtract",
+ [kExprAsgnConcat] = "Concat",
+};
+
+const char *const ccs_tab[] = {
+ [kCCStrategyUseOption] = "UseOption",
+ [kCCStrategyMatchCase] = "MatchCase",
+ [kCCStrategyIgnoreCase] = "IgnoreCase",
+};
+
+static const char *const eltkn_mul_type_tab[] = {
+ [kExprLexMulMul] = "Mul",
+ [kExprLexMulDiv] = "Div",
+ [kExprLexMulMod] = "Mod",
+};
+
+static const char *const eltkn_opt_scope_tab[] = {
+ [kExprOptScopeUnspecified] = "Unspecified",
+ [kExprOptScopeGlobal] = "Global",
+ [kExprOptScopeLocal] = "Local",
+};
+
+/// Represent token as a string
+///
+/// Intended for testing and debugging purposes.
+///
+/// @param[in] pstate Parser state, needed to get token string from it. May be
+/// NULL, in which case in place of obtaining part of the
+/// string represented by token only token length is
+/// returned.
+/// @param[in] token Token to represent.
+/// @param[out] ret_size Return string size, for cases like NULs inside
+/// a string. May be NULL.
+///
+/// @return Token represented in a string form, in a static buffer (overwritten
+/// on each call).
+const char *viml_pexpr_repr_token(const ParserState *const pstate,
+ const LexExprToken token,
+ size_t *const ret_size)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ static char ret[1024];
+ char *p = ret;
+ const char *const e = &ret[1024] - 1;
+#define ADDSTR(...) \
+ do { \
+ p += snprintf(p, (size_t)(sizeof(ret) - (size_t)(p - ret)), __VA_ARGS__); \
+ if (p >= e) { \
+ goto viml_pexpr_repr_token_end; \
+ } \
+ } while (0)
+ ADDSTR("%zu:%zu:%s", token.start.line, token.start.col,
+ eltkn_type_tab[token.type]);
+ switch (token.type) {
+#define TKNARGS(tkn_type, ...) \
+ case tkn_type: { \
+ ADDSTR(__VA_ARGS__); \
+ break; \
+ }
+ TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)",
+ eltkn_cmp_type_tab[token.data.cmp.type],
+ ccs_tab[token.data.cmp.ccs],
+ (int)token.data.cmp.inv)
+ TKNARGS(kExprLexMultiplication, "(type=%s)",
+ eltkn_mul_type_tab[token.data.mul.type])
+ TKNARGS(kExprLexAssignment, "(type=%s)",
+ expr_asgn_type_tab[token.data.ass.type])
+ TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name))
+ case kExprLexDoubleQuotedString:
+ TKNARGS(kExprLexSingleQuotedString, "(closed=%i)",
+ (int)token.data.str.closed)
+ TKNARGS(kExprLexOption, "(scope=%s,name=%.*s)",
+ eltkn_opt_scope_tab[token.data.opt.scope],
+ (int)token.data.opt.len, token.data.opt.name)
+ TKNARGS(kExprLexPlainIdentifier, "(scope=%s,autoload=%i)",
+ intchar2str(token.data.var.scope), (int)token.data.var.autoload)
+ TKNARGS(kExprLexNumber, "(is_float=%i,base=%i,val=%lg)",
+ (int)token.data.num.is_float,
+ (int)token.data.num.base,
+ (double)(token.data.num.is_float
+ ? (double)token.data.num.val.floating
+ : (double)token.data.num.val.integer))
+ TKNARGS(kExprLexInvalid, "(msg=%s)", token.data.err.msg)
+ default: {
+ // No additional arguments.
+ break;
+ }
+#undef TKNARGS
+ }
+ if (pstate == NULL) {
+ ADDSTR("::%zu", token.len);
+ } else {
+ *p++ = ':';
+ memmove(
+ p, &pstate->reader.lines.items[token.start.line].data[token.start.col],
+ token.len);
+ p += token.len;
+ *p = NUL;
+ }
+#undef ADDSTR
+viml_pexpr_repr_token_end:
+ if (ret_size != NULL) {
+ *ret_size = (size_t)(p - ret);
+ }
+ return ret;
+}
+
+const char *const east_node_type_tab[] = {
+ [kExprNodeMissing] = "Missing",
+ [kExprNodeOpMissing] = "OpMissing",
+ [kExprNodeTernary] = "Ternary",
+ [kExprNodeTernaryValue] = "TernaryValue",
+ [kExprNodeRegister] = "Register",
+ [kExprNodeSubscript] = "Subscript",
+ [kExprNodeListLiteral] = "ListLiteral",
+ [kExprNodeUnaryPlus] = "UnaryPlus",
+ [kExprNodeBinaryPlus] = "BinaryPlus",
+ [kExprNodeNested] = "Nested",
+ [kExprNodeCall] = "Call",
+ [kExprNodePlainIdentifier] = "PlainIdentifier",
+ [kExprNodePlainKey] = "PlainKey",
+ [kExprNodeComplexIdentifier] = "ComplexIdentifier",
+ [kExprNodeUnknownFigure] = "UnknownFigure",
+ [kExprNodeLambda] = "Lambda",
+ [kExprNodeDictLiteral] = "DictLiteral",
+ [kExprNodeCurlyBracesIdentifier] = "CurlyBracesIdentifier",
+ [kExprNodeComma] = "Comma",
+ [kExprNodeColon] = "Colon",
+ [kExprNodeArrow] = "Arrow",
+ [kExprNodeComparison] = "Comparison",
+ [kExprNodeConcat] = "Concat",
+ [kExprNodeConcatOrSubscript] = "ConcatOrSubscript",
+ [kExprNodeInteger] = "Integer",
+ [kExprNodeFloat] = "Float",
+ [kExprNodeSingleQuotedString] = "SingleQuotedString",
+ [kExprNodeDoubleQuotedString] = "DoubleQuotedString",
+ [kExprNodeOr] = "Or",
+ [kExprNodeAnd] = "And",
+ [kExprNodeUnaryMinus] = "UnaryMinus",
+ [kExprNodeBinaryMinus] = "BinaryMinus",
+ [kExprNodeNot] = "Not",
+ [kExprNodeMultiplication] = "Multiplication",
+ [kExprNodeDivision] = "Division",
+ [kExprNodeMod] = "Mod",
+ [kExprNodeOption] = "Option",
+ [kExprNodeEnvironment] = "Environment",
+ [kExprNodeAssignment] = "Assignment",
+};
+
+/// Represent `int` character as a string
+///
+/// Converts
+/// - ASCII digits into '{digit}'
+/// - ASCII printable characters into a single-character strings
+/// - everything else to numbers.
+///
+/// @param[in] ch Character to convert.
+///
+/// @return Converted string, stored in a static buffer (overriden after each
+/// call).
+static const char *intchar2str(const int ch)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ static char buf[sizeof(int) * 3 + 1];
+ if (' ' <= ch && ch < 0x7f) {
+ if (ascii_isdigit(ch)) {
+ buf[0] = '\'';
+ buf[1] = (char)ch;
+ buf[2] = '\'';
+ buf[3] = NUL;
+ } else {
+ buf[0] = (char)ch;
+ buf[1] = NUL;
+ }
+ } else {
+ snprintf(buf, sizeof(buf), "%i", ch);
+ }
+ return buf;
+}
+
+#ifdef UNIT_TESTING
+#include <stdio.h>
+
+REAL_FATTR_UNUSED
+static inline void viml_pexpr_debug_print_ast_node(
+ const ExprASTNode *const *const eastnode_p,
+ const char *const prefix)
+{
+ if (*eastnode_p == NULL) {
+ fprintf(stderr, "%s %p : NULL\n", prefix, (void *)eastnode_p);
+ } else {
+ fprintf(stderr, "%s %p : %p : %s : %zu:%zu:%zu\n",
+ prefix, (void *)eastnode_p, (void *)(*eastnode_p),
+ east_node_type_tab[(*eastnode_p)->type], (*eastnode_p)->start.line,
+ (*eastnode_p)->start.col, (*eastnode_p)->len);
+ }
+}
+
+REAL_FATTR_UNUSED
+static inline void viml_pexpr_debug_print_ast_stack(
+ const ExprASTStack *const ast_stack,
+ const char *const msg)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
+{
+ fprintf(stderr, "\n%sstack: %zu:\n", msg, kv_size(*ast_stack));
+ for (size_t i = 0; i < kv_size(*ast_stack); i++) {
+ viml_pexpr_debug_print_ast_node(
+ (const ExprASTNode *const *)kv_A(*ast_stack, i),
+ "-");
+ }
+}
+
+REAL_FATTR_UNUSED
+static inline void viml_pexpr_debug_print_token(
+ const ParserState *const pstate, const LexExprToken token)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ fprintf(stderr, "\ntkn: %s\n", viml_pexpr_repr_token(pstate, token, NULL));
+}
+#define PSTACK(msg) \
+ viml_pexpr_debug_print_ast_stack(&ast_stack, #msg)
+#define PSTACK_P(msg) \
+ viml_pexpr_debug_print_ast_stack(ast_stack, #msg)
+#define PNODE_P(eastnode_p, msg) \
+ viml_pexpr_debug_print_ast_node((const ExprASTNode *const *)eastnode_p, \
+ (#msg))
+#define PTOKEN(tkn) \
+ viml_pexpr_debug_print_token(pstate, tkn)
+#endif
+
+const uint8_t node_maxchildren[] = {
+ [kExprNodeMissing] = 0,
+ [kExprNodeOpMissing] = 2,
+ [kExprNodeTernary] = 2,
+ [kExprNodeTernaryValue] = 2,
+ [kExprNodeRegister] = 0,
+ [kExprNodeSubscript] = 2,
+ [kExprNodeListLiteral] = 1,
+ [kExprNodeUnaryPlus] = 1,
+ [kExprNodeBinaryPlus] = 2,
+ [kExprNodeNested] = 1,
+ [kExprNodeCall] = 2,
+ [kExprNodePlainIdentifier] = 0,
+ [kExprNodePlainKey] = 0,
+ [kExprNodeComplexIdentifier] = 2,
+ [kExprNodeUnknownFigure] = 1,
+ [kExprNodeLambda] = 2,
+ [kExprNodeDictLiteral] = 1,
+ [kExprNodeCurlyBracesIdentifier] = 1,
+ [kExprNodeComma] = 2,
+ [kExprNodeColon] = 2,
+ [kExprNodeArrow] = 2,
+ [kExprNodeComparison] = 2,
+ [kExprNodeConcat] = 2,
+ [kExprNodeConcatOrSubscript] = 2,
+ [kExprNodeInteger] = 0,
+ [kExprNodeFloat] = 0,
+ [kExprNodeSingleQuotedString] = 0,
+ [kExprNodeDoubleQuotedString] = 0,
+ [kExprNodeOr] = 2,
+ [kExprNodeAnd] = 2,
+ [kExprNodeUnaryMinus] = 1,
+ [kExprNodeBinaryMinus] = 2,
+ [kExprNodeNot] = 1,
+ [kExprNodeMultiplication] = 2,
+ [kExprNodeDivision] = 2,
+ [kExprNodeMod] = 2,
+ [kExprNodeOption] = 0,
+ [kExprNodeEnvironment] = 0,
+ [kExprNodeAssignment] = 2,
+};
+
+/// Free memory occupied by AST
+///
+/// @param ast AST stack to free.
+void viml_pexpr_free_ast(ExprAST ast)
+{
+ ExprASTStack ast_stack;
+ kvi_init(ast_stack);
+ kvi_push(ast_stack, &ast.root);
+ while (kv_size(ast_stack)) {
+ ExprASTNode **const cur_node = kv_last(ast_stack);
+#ifndef NDEBUG
+ // Explicitly check for AST recursiveness.
+ for (size_t i = 0 ; i < kv_size(ast_stack) - 1 ; i++) {
+ assert(*kv_A(ast_stack, i) != *cur_node);
+ }
+#endif
+ if (*cur_node == NULL) {
+ assert(kv_size(ast_stack) == 1);
+ kv_drop(ast_stack, 1);
+ } else if ((*cur_node)->children != NULL) {
+#ifndef NDEBUG
+ const uint8_t maxchildren = node_maxchildren[(*cur_node)->type];
+ assert(maxchildren > 0);
+ assert(maxchildren <= 2);
+ assert(maxchildren == 1
+ ? (*cur_node)->children->next == NULL
+ : ((*cur_node)->children->next == NULL
+ || (*cur_node)->children->next->next == NULL));
+#endif
+ kvi_push(ast_stack, &(*cur_node)->children);
+ } else if ((*cur_node)->next != NULL) {
+ kvi_push(ast_stack, &(*cur_node)->next);
+ } else if (*cur_node != NULL) {
+ kv_drop(ast_stack, 1);
+ switch ((*cur_node)->type) {
+ case kExprNodeDoubleQuotedString:
+ case kExprNodeSingleQuotedString: {
+ xfree((*cur_node)->data.str.value);
+ break;
+ }
+ case kExprNodeMissing:
+ case kExprNodeOpMissing:
+ case kExprNodeTernary:
+ case kExprNodeTernaryValue:
+ case kExprNodeRegister:
+ case kExprNodeSubscript:
+ case kExprNodeListLiteral:
+ case kExprNodeUnaryPlus:
+ case kExprNodeBinaryPlus:
+ case kExprNodeNested:
+ case kExprNodeCall:
+ case kExprNodePlainIdentifier:
+ case kExprNodePlainKey:
+ case kExprNodeComplexIdentifier:
+ case kExprNodeUnknownFigure:
+ case kExprNodeLambda:
+ case kExprNodeDictLiteral:
+ case kExprNodeCurlyBracesIdentifier:
+ case kExprNodeAssignment:
+ case kExprNodeComma:
+ case kExprNodeColon:
+ case kExprNodeArrow:
+ case kExprNodeComparison:
+ case kExprNodeConcat:
+ case kExprNodeConcatOrSubscript:
+ case kExprNodeInteger:
+ case kExprNodeFloat:
+ case kExprNodeOr:
+ case kExprNodeAnd:
+ case kExprNodeUnaryMinus:
+ case kExprNodeBinaryMinus:
+ case kExprNodeNot:
+ case kExprNodeMultiplication:
+ case kExprNodeDivision:
+ case kExprNodeMod:
+ case kExprNodeOption:
+ case kExprNodeEnvironment: {
+ break;
+ }
+ }
+ xfree(*cur_node);
+ *cur_node = NULL;
+ }
+ }
+ kvi_destroy(ast_stack);
+}
+
+// Binary operator precedence and associativity:
+//
+// Operator | Precedence | Associativity
+// ---------+------------+-----------------
+// || | 2 | left
+// && | 3 | left
+// cmp* | 4 | not associative
+// + - . | 5 | left
+// * / % | 6 | left
+//
+// * comparison operators:
+//
+// == ==# ==? != !=# !=?
+// =~ =~# =~? !~ !~# !~?
+// > ># >? <= <=# <=?
+// < <# <? >= >=# >=?
+// is is# is? isnot isnot# isnot?
+
+/// Allocate a new node and set some of the values
+///
+/// @param[in] type Node type to allocate.
+/// @param[in] level Node level to allocate
+static inline ExprASTNode *viml_pexpr_new_node(const ExprASTNodeType type)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
+{
+ ExprASTNode *ret = xmalloc(sizeof(*ret));
+ ret->type = type;
+ ret->children = NULL;
+ ret->next = NULL;
+ return ret;
+}
+
+static struct {
+ ExprOpLvl lvl;
+ ExprOpAssociativity ass;
+} node_type_to_node_props[] = {
+ [kExprNodeMissing] = { kEOpLvlInvalid, kEOpAssNo, },
+ [kExprNodeOpMissing] = { kEOpLvlMultiplication, kEOpAssNo },
+
+ [kExprNodeNested] = { kEOpLvlParens, kEOpAssNo },
+ // Note: below nodes are kEOpLvlSubscript for “binary operator” itself, but
+ // kEOpLvlParens when it comes to inside the parenthesis.
+ [kExprNodeCall] = { kEOpLvlParens, kEOpAssNo },
+ [kExprNodeSubscript] = { kEOpLvlParens, kEOpAssNo },
+
+ [kExprNodeUnknownFigure] = { kEOpLvlParens, kEOpAssLeft },
+ [kExprNodeLambda] = { kEOpLvlParens, kEOpAssNo },
+ [kExprNodeDictLiteral] = { kEOpLvlParens, kEOpAssNo },
+ [kExprNodeListLiteral] = { kEOpLvlParens, kEOpAssNo },
+
+ [kExprNodeArrow] = { kEOpLvlArrow, kEOpAssNo },
+
+ // Right associativity for comma because this means easier access to arguments
+ // list, etc: for "[a, b, c, d]" you can access "a" in one step if it is
+ // represented as "list(comma(a, comma(b, comma(c, d))))" then if it is
+ // "list(comma(comma(comma(a, b), c), d))" in which case you will need to
+ // traverse all three comma() structures. And with comma operator (including
+ // actual comma operator from C which is not present in VimL) nobody cares
+ // about associativity, only about order of execution.
+ [kExprNodeComma] = { kEOpLvlComma, kEOpAssRight },
+
+ // Colons are not eligible for chaining, so nobody cares about associativity.
+ [kExprNodeColon] = { kEOpLvlColon, kEOpAssNo },
+
+ [kExprNodeTernary] = { kEOpLvlTernary, kEOpAssRight },
+
+ [kExprNodeOr] = { kEOpLvlOr, kEOpAssLeft },
+
+ [kExprNodeAnd] = { kEOpLvlAnd, kEOpAssLeft },
+
+ [kExprNodeTernaryValue] = { kEOpLvlTernaryValue, kEOpAssRight },
+
+ [kExprNodeComparison] = { kEOpLvlComparison, kEOpAssRight },
+
+ [kExprNodeBinaryPlus] = { kEOpLvlAddition, kEOpAssLeft },
+ [kExprNodeBinaryMinus] = { kEOpLvlAddition, kEOpAssLeft },
+ [kExprNodeConcat] = { kEOpLvlAddition, kEOpAssLeft },
+
+ [kExprNodeMultiplication] = { kEOpLvlMultiplication, kEOpAssLeft },
+ [kExprNodeDivision] = { kEOpLvlMultiplication, kEOpAssLeft },
+ [kExprNodeMod] = { kEOpLvlMultiplication, kEOpAssLeft },
+
+ [kExprNodeUnaryPlus] = { kEOpLvlUnary, kEOpAssNo },
+ [kExprNodeUnaryMinus] = { kEOpLvlUnary, kEOpAssNo },
+ [kExprNodeNot] = { kEOpLvlUnary, kEOpAssNo },
+
+ [kExprNodeConcatOrSubscript] = { kEOpLvlSubscript, kEOpAssLeft },
+
+ [kExprNodeCurlyBracesIdentifier] = { kEOpLvlComplexIdentifier, kEOpAssLeft },
+
+ [kExprNodeAssignment] = { kEOpLvlAssignment, kEOpAssLeft },
+
+ [kExprNodeComplexIdentifier] = { kEOpLvlValue, kEOpAssLeft },
+
+ [kExprNodePlainIdentifier] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodePlainKey] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeRegister] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeInteger] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeFloat] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeDoubleQuotedString] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeSingleQuotedString] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeOption] = { kEOpLvlValue, kEOpAssNo },
+ [kExprNodeEnvironment] = { kEOpLvlValue, kEOpAssNo },
+};
+
+/// Get AST node priority level
+///
+/// Used primary to reduce line length, so keep the name short.
+///
+/// @param[in] node Node to get priority for.
+///
+/// @return Node priority level.
+static inline ExprOpLvl node_lvl(const ExprASTNode node)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return node_type_to_node_props[node.type].lvl;
+}
+
+/// Get AST node associativity, to be used for operator nodes primary
+///
+/// Used primary to reduce line length, so keep the name short.
+///
+/// @param[in] node Node to get priority for.
+///
+/// @return Node associativity.
+static inline ExprOpAssociativity node_ass(const ExprASTNode node)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return node_type_to_node_props[node.type].ass;
+}
+
+/// Handle binary operator
+///
+/// This function is responsible for handling priority levels as well.
+///
+/// @param[in] pstate Parser state, used for error reporting.
+/// @param ast_stack AST stack. May be popped of some values and will
+/// definitely receive new ones.
+/// @param bop_node New node to handle.
+/// @param[out] want_node_p New value of want_node.
+/// @param[out] ast_err Location where error is saved, if any.
+///
+/// @return True if no errors occurred, false otherwise.
+static bool viml_pexpr_handle_bop(const ParserState *const pstate,
+ ExprASTStack *const ast_stack,
+ ExprASTNode *const bop_node,
+ ExprASTWantedNode *const want_node_p,
+ ExprASTError *const ast_err)
+ FUNC_ATTR_NONNULL_ALL
+{
+ bool ret = true;
+ ExprASTNode **top_node_p = NULL;
+ ExprASTNode *top_node;
+ ExprOpLvl top_node_lvl;
+ ExprOpAssociativity top_node_ass;
+ assert(kv_size(*ast_stack));
+ const ExprOpLvl bop_node_lvl = ((bop_node->type == kExprNodeCall
+ || bop_node->type == kExprNodeSubscript)
+ ? kEOpLvlSubscript
+ : node_lvl(*bop_node));
+#ifndef NDEBUG
+ const ExprOpAssociativity bop_node_ass = (
+ (bop_node->type == kExprNodeCall
+ || bop_node->type == kExprNodeSubscript)
+ ? kEOpAssLeft
+ : node_ass(*bop_node));
+#endif
+ do {
+ ExprASTNode **new_top_node_p = kv_last(*ast_stack);
+ ExprASTNode *new_top_node = *new_top_node_p;
+ assert(new_top_node != NULL);
+ const ExprOpLvl new_top_node_lvl = node_lvl(*new_top_node);
+ const ExprOpAssociativity new_top_node_ass = node_ass(*new_top_node);
+ assert(bop_node_lvl != new_top_node_lvl
+ || bop_node_ass == new_top_node_ass);
+ if (top_node_p != NULL
+ && ((bop_node_lvl > new_top_node_lvl
+ || (bop_node_lvl == new_top_node_lvl
+ && new_top_node_ass == kEOpAssNo)))) {
+ break;
+ }
+ kv_drop(*ast_stack, 1);
+ top_node_p = new_top_node_p;
+ top_node = new_top_node;
+ top_node_lvl = new_top_node_lvl;
+ top_node_ass = new_top_node_ass;
+ if (bop_node_lvl == top_node_lvl && top_node_ass == kEOpAssRight) {
+ break;
+ }
+ } while (kv_size(*ast_stack));
+ if (top_node_ass == kEOpAssLeft || top_node_lvl != bop_node_lvl) {
+ // outer(op(x,y)) -> outer(new_op(op(x,y),*))
+ //
+ // Before: top_node_p = outer(*), points to op(x,y)
+ // Other stack elements unknown
+ //
+ // After: top_node_p = outer(*), points to new_op(op(x,y))
+ // &bop_node->children->next = new_op(op(x,y),*), points to NULL
+ *top_node_p = bop_node;
+ bop_node->children = top_node;
+ assert(bop_node->children->next == NULL);
+ kvi_push(*ast_stack, top_node_p);
+ kvi_push(*ast_stack, &bop_node->children->next);
+ } else {
+ assert(top_node_lvl == bop_node_lvl && top_node_ass == kEOpAssRight);
+ assert(top_node->children != NULL && top_node->children->next != NULL);
+ // outer(op(x,y)) -> outer(op(x,new_op(y,*)))
+ //
+ // Before: top_node_p = outer(*), points to op(x,y)
+ // Other stack elements unknown
+ //
+ // After: top_node_p = outer(*), points to op(x,new_op(y))
+ // &top_node->children->next = op(x,*), points to new_op(y)
+ // &bop_node->children->next = new_op(y,*), points to NULL
+ bop_node->children = top_node->children->next;
+ top_node->children->next = bop_node;
+ assert(bop_node->children->next == NULL);
+ kvi_push(*ast_stack, top_node_p);
+ kvi_push(*ast_stack, &top_node->children->next);
+ kvi_push(*ast_stack, &bop_node->children->next);
+ // TODO(ZyX-I): Make this not error, but treat like Python does
+ if (bop_node->type == kExprNodeComparison) {
+ east_set_error(pstate, ast_err,
+ _("E15: Operator is not associative: %.*s"),
+ bop_node->start);
+ ret = false;
+ }
+ }
+ *want_node_p = kENodeValue;
+ return ret;
+}
+
+/// ParserPosition literal based on ParserPosition pos with columns shifted
+///
+/// Function does not check whether resulting position is valid.
+///
+/// @param[in] pos Position to shift.
+/// @param[in] shift Number of bytes to shift.
+///
+/// @return Shifted position.
+static inline ParserPosition shifted_pos(const ParserPosition pos,
+ const size_t shift)
+ FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return (ParserPosition) { .line = pos.line, .col = pos.col + shift };
+}
+
+/// ParserPosition literal based on ParserPosition pos with specified column
+///
+/// Function does not check whether remaining position is valid.
+///
+/// @param[in] pos Position to adjust.
+/// @param[in] new_col New column.
+///
+/// @return Shifted position.
+static inline ParserPosition recol_pos(const ParserPosition pos,
+ const size_t new_col)
+ FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return (ParserPosition) { .line = pos.line, .col = new_col };
+}
+
+/// Get highlight group name
+#define HL(g) (is_invalid ? "NvimInvalid" #g : "Nvim" #g)
+
+/// Highlight current token with the given group
+#define HL_CUR_TOKEN(g) \
+ viml_parser_highlight(pstate, cur_token.start, cur_token.len, \
+ HL(g))
+
+/// Allocate new node, saving some values
+#define NEW_NODE(type) \
+ viml_pexpr_new_node(type)
+
+/// Set position of the given node to position from the given token
+///
+/// @param cur_node Node to modify.
+/// @param cur_token Token to set position from.
+#define POS_FROM_TOKEN(cur_node, cur_token) \
+ do { \
+ (cur_node)->start = cur_token.start; \
+ (cur_node)->len = cur_token.len; \
+ } while (0)
+
+/// Allocate new node and set its position from the current token
+///
+/// If previous token happened to contain spacing then it will be included.
+///
+/// @param cur_node Variable to save allocated node to.
+/// @param typ Node type.
+#define NEW_NODE_WITH_CUR_POS(cur_node, typ) \
+ do { \
+ (cur_node) = NEW_NODE(typ); \
+ POS_FROM_TOKEN((cur_node), cur_token); \
+ if (prev_token.type == kExprLexSpacing) { \
+ (cur_node)->start = prev_token.start; \
+ (cur_node)->len += prev_token.len; \
+ } \
+ } while (0)
+
+/// Check whether it is possible to have next expression after current
+///
+/// For :echo: `:echo @a @a` is a valid expression. `:echo (@a @a)` is not.
+#define MAY_HAVE_NEXT_EXPR \
+ (kv_size(ast_stack) == 1)
+
+/// Add operator node
+///
+/// @param[in] cur_node Node to add.
+#define ADD_OP_NODE(cur_node) \
+ is_invalid |= !viml_pexpr_handle_bop(pstate, &ast_stack, cur_node, \
+ &want_node, &ast.err)
+
+/// Record missing operator: for things like
+///
+/// :echo @a @a
+///
+/// (allowed) or
+///
+/// :echo (@a @a)
+///
+/// (parsed as OpMissing(@a, @a)).
+#define OP_MISSING \
+ do { \
+ if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { \
+ /* Multiple expressions allowed, return without calling */ \
+ /* viml_parser_advance(). */ \
+ goto viml_pexpr_parse_end; \
+ } else { \
+ assert(*top_node_p != NULL); \
+ ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Missing operator: %.*s")); \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOpMissing); \
+ cur_node->len = 0; \
+ ADD_OP_NODE(cur_node); \
+ goto viml_pexpr_parse_process_token; \
+ } \
+ } while (0)
+
+/// Record missing value: for things like "* 5"
+///
+/// @param[in] msg Error message.
+#define ADD_VALUE_IF_MISSING(msg) \
+ do { \
+ if (want_node == kENodeValue) { \
+ ERROR_FROM_TOKEN_AND_MSG(cur_token, (msg)); \
+ NEW_NODE_WITH_CUR_POS((*top_node_p), kExprNodeMissing); \
+ (*top_node_p)->len = 0; \
+ want_node = kENodeOperator; \
+ } \
+ } while (0)
+
+/// Set AST error, unless AST already is not correct
+///
+/// @param[out] ret_ast AST to set error in.
+/// @param[in] pstate Parser state, used to get error message argument.
+/// @param[in] msg Error message, assumed to be already translated and
+/// containing a single %token "%.*s".
+/// @param[in] start Position at which error occurred.
+static inline void east_set_error(const ParserState *const pstate,
+ ExprASTError *const ret_ast_err,
+ const char *const msg,
+ const ParserPosition start)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
+{
+ if (ret_ast_err->msg != NULL) {
+ return;
+ }
+ const ParserLine pline = pstate->reader.lines.items[start.line];
+ ret_ast_err->msg = msg;
+ ret_ast_err->arg_len = (int)(pline.size - start.col);
+ ret_ast_err->arg = pline.data + start.col;
+}
+
+/// Set error from the given token and given message
+#define ERROR_FROM_TOKEN_AND_MSG(cur_token, msg) \
+ do { \
+ is_invalid = true; \
+ east_set_error(pstate, &ast.err, msg, cur_token.start); \
+ } while (0)
+
+/// Like #ERROR_FROM_TOKEN_AND_MSG, but gets position from a node
+#define ERROR_FROM_NODE_AND_MSG(node, msg) \
+ do { \
+ is_invalid = true; \
+ east_set_error(pstate, &ast.err, msg, node->start); \
+ } while (0)
+
+/// Set error from the given kExprLexInvalid token
+#define ERROR_FROM_TOKEN(cur_token) \
+ ERROR_FROM_TOKEN_AND_MSG(cur_token, cur_token.data.err.msg)
+
+/// Select figure brace type, altering highlighting as well if needed
+///
+/// @param[out] node Node to modify type.
+/// @param[in] new_type New type, one of ExprASTNodeType values without
+/// kExprNode prefix.
+/// @param[in] hl Corresponding highlighting, passed as an argument to #HL.
+#define SELECT_FIGURE_BRACE_TYPE(node, new_type, hl) \
+ do { \
+ ExprASTNode *const node_ = (node); \
+ assert(node_->type == kExprNodeUnknownFigure \
+ || node_->type == kExprNode##new_type); \
+ node_->type = kExprNode##new_type; \
+ if (pstate->colors) { \
+ kv_A(*pstate->colors, node_->data.fig.opening_hl_idx).group = \
+ HL(hl); \
+ } \
+ } while (0)
+
+/// Add identifier which should constitute complex identifier node
+///
+/// This one is to be called only in case want_node is kENodeOperator.
+///
+/// @param new_ident_node_code Code used to create a new identifier node and
+/// update want_node and ast_stack, without
+/// a trailing semicolon.
+/// @param hl Highlighting name to use, passed as an argument to #HL.
+#define ADD_IDENT(new_ident_node_code, hl) \
+ do { \
+ assert(want_node == kENodeOperator); \
+ /* Operator: may only be curly braces name, but only under certain */ \
+ /* conditions. */ \
+\
+ /* First condition is that there is no space before a part of complex */ \
+ /* identifier. */ \
+ if (prev_token.type == kExprLexSpacing) { \
+ OP_MISSING; \
+ } \
+ switch ((*top_node_p)->type) { \
+ /* Second is that previous node is one of the identifiers: */ \
+ /* complex, plain, curly braces. */ \
+\
+ /* TODO(ZyX-I): Extend syntax to allow ${expr}. This is needed to */ \
+ /* handle environment variables like those bash uses for */ \
+ /* `export -f`: their names consist not only of alphanumeric */ \
+ /* characetrs. */ \
+ case kExprNodeComplexIdentifier: \
+ case kExprNodePlainIdentifier: \
+ case kExprNodeCurlyBracesIdentifier: { \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \
+ cur_node->len = 0; \
+ cur_node->children = *top_node_p; \
+ *top_node_p = cur_node; \
+ kvi_push(ast_stack, &cur_node->children->next); \
+ ExprASTNode **const new_top_node_p = kv_last(ast_stack); \
+ assert(*new_top_node_p == NULL); \
+ new_ident_node_code; \
+ *new_top_node_p = cur_node; \
+ HL_CUR_TOKEN(hl); \
+ break; \
+ } \
+ default: { \
+ OP_MISSING; \
+ break; \
+ } \
+ } \
+ } while (0)
+
+/// Determine whether given parse type is an assignment
+///
+/// @param[in] pt Checked parse type.
+///
+/// @return true if parsing an assignment, false otherwise.
+static inline bool pt_is_assignment(const ExprASTParseType pt)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return (pt == kEPTAssignment || pt == kEPTSingleAssignment);
+}
+
+/// Structure used to define “string shifts” necessary to map string
+/// highlighting to actual strings.
+typedef struct {
+ size_t start; ///< Where special character starts in original string.
+ size_t orig_len; ///< Length of orininal string (e.g. 4 for "\x80").
+ size_t act_len; ///< Length of resulting character(s) (e.g. 1 for "\x80").
+ bool escape_not_known; ///< True if escape sequence in original is not known.
+} StringShift;
+
+/// Parse and highlight single- or double-quoted string
+///
+/// Function is supposed to detect and highlight regular expressions (but does
+/// not do now).
+///
+/// @param[out] pstate Parser state which also contains a place where
+/// highlighting is saved.
+/// @param[out] node Node where string parsing results are saved.
+/// @param[in] token Token to highlight.
+/// @param[in] ast_stack Parser AST stack, used to detect whether current
+/// string is a regex.
+/// @param[in] is_invalid Whether currently processed token is not valid.
+static void parse_quoted_string(ParserState *const pstate,
+ ExprASTNode *const node,
+ const LexExprToken token,
+ const ExprASTStack ast_stack,
+ const bool is_invalid)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const ParserLine pline = pstate->reader.lines.items[token.start.line];
+ const char *const s = pline.data + token.start.col;
+ const char *const e = s + token.len - token.data.str.closed;
+ const char *p = s + 1;
+ const bool is_double = (token.type == kExprLexDoubleQuotedString);
+ size_t size = token.len - token.data.str.closed - 1;
+ kvec_withinit_t(StringShift, 16) shifts;
+ kvi_init(shifts);
+ if (!is_double) {
+ viml_parser_highlight(pstate, token.start, 1, HL(SingleQuote));
+ while (p < e) {
+ const char *const chunk_e = memchr(p, '\'', (size_t)(e - p));
+ if (chunk_e == NULL) {
+ break;
+ }
+ size--;
+ p = chunk_e + 2;
+ if (pstate->colors) {
+ kvi_push(shifts, ((StringShift) {
+ .start = token.start.col + (size_t)(chunk_e - s),
+ .orig_len = 2,
+ .act_len = 1,
+ .escape_not_known = false,
+ }));
+ }
+ }
+ node->data.str.size = size;
+ if (size == 0) {
+ node->data.str.value = NULL;
+ } else {
+ char *v_p;
+ v_p = node->data.str.value = xmallocz(size);
+ p = s + 1;
+ while (p < e) {
+ const char *const chunk_e = memchr(p, '\'', (size_t)(e - p));
+ if (chunk_e == NULL) {
+ memcpy(v_p, p, (size_t)(e - p));
+ break;
+ }
+ memcpy(v_p, p, (size_t)(chunk_e - p));
+ v_p += (size_t)(chunk_e - p) + 1;
+ v_p[-1] = '\'';
+ p = chunk_e + 2;
+ }
+ }
+ } else {
+ viml_parser_highlight(pstate, token.start, 1, HL(DoubleQuote));
+ for (p = s + 1; p < e; p++) {
+ if (*p == '\\' && p + 1 < e) {
+ p++;
+ if (p + 1 == e) {
+ size--;
+ break;
+ }
+ switch (*p) {
+ // A "\<x>" form occupies at least 4 characters, and produces up to
+ // 6 characters: reserve space for 2 extra, but do not compute actual
+ // length just now, it would be costy.
+ case '<': {
+ size += 2;
+ break;
+ }
+ // Hexadecimal, always single byte, but at least three bytes each.
+ case 'x': case 'X': {
+ size--;
+ if (ascii_isxdigit(p[1])) {
+ size--;
+ if (p + 2 < e && ascii_isxdigit(p[2])) {
+ size--;
+ }
+ }
+ break;
+ }
+ // Unicode
+ //
+ // \uF takes 1 byte which is 2 bytes less then escape sequence.
+ // \uFF: 2 bytes, 2 bytes less.
+ // \uFFF: 3 bytes, 2 bytes less.
+ // \uFFFF: 3 bytes, 3 bytes less.
+ // \UFFFFF: 4 bytes, 3 bytes less.
+ // \UFFFFFF: 5 bytes, 3 bytes less.
+ // \UFFFFFFF: 6 bytes, 3 bytes less.
+ // \U7FFFFFFF: 6 bytes, 4 bytes less.
+ case 'u': case 'U': {
+ const char *const esc_start = p;
+ size_t n = (*p == 'u' ? 4 : 8);
+ int nr = 0;
+ p++;
+ while (p + 1 < e && n-- && ascii_isxdigit(p[1])) {
+ p++;
+ nr = (nr << 4) + hex2nr(*p);
+ }
+ // Escape length: (esc_start - 1) points to "\\", esc_start to "u"
+ // or "U", p to the byte after last byte. So escape sequence
+ // occupies p - (esc_start - 1), but it stands for a utf_char2len
+ // bytes.
+ size -= (size_t)((p - (esc_start - 1)) - utf_char2len(nr));
+ p--;
+ break;
+ }
+ // Octal, always single byte, but at least two bytes each.
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': {
+ size--;
+ p++;
+ if (*p >= '0' && *p <= '7') {
+ size--;
+ p++;
+ if (p < e && *p >= '0' && *p <= '7') {
+ size--;
+ p++;
+ }
+ }
+ break;
+ }
+ default: {
+ size--;
+ break;
+ }
+ }
+ }
+ }
+ if (size == 0) {
+ node->data.str.value = NULL;
+ node->data.str.size = 0;
+ } else {
+ char *v_p;
+ v_p = node->data.str.value = xmalloc(size);
+ p = s + 1;
+ while (p < e) {
+ const char *const chunk_e = memchr(p, '\\', (size_t)(e - p));
+ if (chunk_e == NULL) {
+ memcpy(v_p, p, (size_t)(e - p));
+ v_p += e - p;
+ break;
+ }
+ memcpy(v_p, p, (size_t)(chunk_e - p));
+ v_p += (size_t)(chunk_e - p);
+ p = chunk_e + 1;
+ if (p == e) {
+ *v_p++ = '\\';
+ break;
+ }
+ bool is_unknown = false;
+ const char *const v_p_start = v_p;
+ switch (*p) {
+#define SINGLE_CHAR_ESC(ch, real_ch) \
+ case ch: { \
+ *v_p++ = real_ch; \
+ p++; \
+ break; \
+ }
+ SINGLE_CHAR_ESC('b', BS)
+ SINGLE_CHAR_ESC('e', ESC)
+ SINGLE_CHAR_ESC('f', FF)
+ SINGLE_CHAR_ESC('n', NL)
+ SINGLE_CHAR_ESC('r', CAR)
+ SINGLE_CHAR_ESC('t', TAB)
+ SINGLE_CHAR_ESC('"', '"')
+ SINGLE_CHAR_ESC('\\', '\\')
+#undef SINGLE_CHAR_ESC
+
+ // Hexadecimal or unicode.
+ case 'X': case 'x': case 'u': case 'U': {
+ if (p + 1 < e && ascii_isxdigit(p[1])) {
+ size_t n;
+ int nr;
+ bool is_hex = (*p == 'x' || *p == 'X');
+
+ if (is_hex) {
+ n = 2;
+ } else if (*p == 'u') {
+ n = 4;
+ } else {
+ n = 8;
+ }
+ nr = 0;
+ while (p + 1 < e && n-- && ascii_isxdigit(p[1])) {
+ p++;
+ nr = (nr << 4) + hex2nr(*p);
+ }
+ p++;
+ if (is_hex) {
+ *v_p++ = (char)nr;
+ } else {
+ v_p += utf_char2bytes(nr, (char_u *)v_p);
+ }
+ } else {
+ is_unknown = true;
+ *v_p++ = *p;
+ p++;
+ }
+ break;
+ }
+ // Octal: "\1", "\12", "\123".
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': {
+ uint8_t ch = (uint8_t)(*p++ - '0');
+ if (p < e && *p >= '0' && *p <= '7') {
+ ch = (uint8_t)((ch << 3) + *p++ - '0');
+ if (p < e && *p >= '0' && *p <= '7') {
+ ch = (uint8_t)((ch << 3) + *p++ - '0');
+ }
+ }
+ *v_p++ = (char)ch;
+ break;
+ }
+ // Special key, e.g.: "\<C-W>"
+ case '<': {
+ const size_t special_len = (
+ trans_special((const char_u **)&p, (size_t)(e - p),
+ (char_u *)v_p, true, true));
+ if (special_len != 0) {
+ v_p += special_len;
+ } else {
+ is_unknown = true;
+ mb_copy_char((const char_u **)&p, (char_u **)&v_p);
+ }
+ break;
+ }
+ default: {
+ is_unknown = true;
+ mb_copy_char((const char_u **)&p, (char_u **)&v_p);
+ break;
+ }
+ }
+ if (pstate->colors) {
+ kvi_push(shifts, ((StringShift) {
+ .start = token.start.col + (size_t)(chunk_e - s),
+ .orig_len = (size_t)(p - chunk_e),
+ .act_len = (size_t)(v_p - (char *)v_p_start),
+ .escape_not_known = is_unknown,
+ }));
+ }
+ }
+ node->data.str.size = (size_t)(v_p - node->data.str.value);
+ }
+ }
+ if (pstate->colors) {
+ // TODO(ZyX-I): use ast_stack to determine and highlight regular expressions
+ // TODO(ZyX-I): use ast_stack to determine and highlight printf format str
+ // TODO(ZyX-I): use ast_stack to determine and highlight expression strings
+ size_t next_col = token.start.col + 1;
+ const char *const body_str = (is_double
+ ? HL(DoubleQuotedBody)
+ : HL(SingleQuotedBody));
+ const char *const esc_str = (is_double
+ ? HL(DoubleQuotedEscape)
+ : HL(SingleQuotedQuote));
+ const char *const ukn_esc_str = (is_double
+ ? HL(DoubleQuotedUnknownEscape)
+ : HL(SingleQuotedUnknownEscape));
+ for (size_t i = 0; i < kv_size(shifts); i++) {
+ const StringShift cur_shift = kv_A(shifts, i);
+ if (cur_shift.start > next_col) {
+ viml_parser_highlight(pstate, recol_pos(token.start, next_col),
+ cur_shift.start - next_col,
+ body_str);
+ }
+ viml_parser_highlight(pstate, recol_pos(token.start, cur_shift.start),
+ cur_shift.orig_len,
+ (cur_shift.escape_not_known
+ ? ukn_esc_str
+ : esc_str));
+ next_col = cur_shift.start + cur_shift.orig_len;
+ }
+ if (next_col - token.start.col < token.len - token.data.str.closed) {
+ viml_parser_highlight(pstate, recol_pos(token.start, next_col),
+ (token.start.col
+ + token.len
+ - token.data.str.closed
+ - next_col),
+ body_str);
+ }
+ }
+ if (token.data.str.closed) {
+ if (is_double) {
+ viml_parser_highlight(pstate, shifted_pos(token.start, token.len - 1),
+ 1, HL(DoubleQuote));
+ } else {
+ viml_parser_highlight(pstate, shifted_pos(token.start, token.len - 1),
+ 1, HL(SingleQuote));
+ }
+ }
+ kvi_destroy(shifts);
+}
+
+/// Additional flags to pass to lexer depending on want_node
+static const int want_node_to_lexer_flags[] = {
+ [kENodeValue] = kELFlagIsNotCmp,
+ [kENodeOperator] = kELFlagForbidScope,
+};
+
+/// Number of characters to highlight as NumberPrefix depending on the base
+static const uint8_t base_to_prefix_length[] = {
+ [2] = 2,
+ [8] = 1,
+ [10] = 0,
+ [16] = 2,
+};
+
+/// Parse one VimL expression
+///
+/// @param pstate Parser state.
+/// @param[in] flags Additional flags, see ExprParserFlags
+///
+/// @return Parsed AST.
+ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ ExprAST ast = {
+ .err = {
+ .msg = NULL,
+ .arg_len = 0,
+ .arg = NULL,
+ },
+ .root = NULL,
+ };
+ // Expression stack contains current branch in AST tree: that is
+ // - Stack item 0 contains root of the tree, i.e. &ast->root.
+ // - Stack item i points to the previous stack items’ last child.
+ //
+ // When parser expects “value” node that is something like identifier or "["
+ // (list start) last stack item contains NULL. Otherwise last stack item is
+ // supposed to contain last “finished” value: e.g. "1" or "+(1, 1)" (node
+ // representing "1+1").
+ ExprASTStack ast_stack;
+ kvi_init(ast_stack);
+ kvi_push(ast_stack, &ast.root);
+ ExprASTWantedNode want_node = kENodeValue;
+ ExprASTParseTypeStack pt_stack;
+ kvi_init(pt_stack);
+ kvi_push(pt_stack, kEPTExpr);
+ if (flags & kExprFlagsParseLet) {
+ kvi_push(pt_stack, kEPTAssignment);
+ }
+ LexExprToken prev_token = { .type = kExprLexMissing };
+ bool highlighted_prev_spacing = false;
+ // Lambda node, valid when parsing lambda arguments only.
+ ExprASTNode *lambda_node = NULL;
+ size_t asgn_level = 0;
+ do {
+ const bool is_concat_or_subscript = (
+ want_node == kENodeValue
+ && kv_size(ast_stack) > 1
+ && (*kv_Z(ast_stack, 1))->type == kExprNodeConcatOrSubscript);
+ const int lexer_additional_flags = (
+ kELFlagPeek
+ | ((flags & kExprFlagsDisallowEOC) ? kELFlagForbidEOC : 0)
+ | ((want_node == kENodeValue
+ && (kv_size(ast_stack) == 1
+ || ((*kv_Z(ast_stack, 1))->type != kExprNodeConcat
+ && ((*kv_Z(ast_stack, 1))->type
+ != kExprNodeConcatOrSubscript))))
+ ? kELFlagAllowFloat
+ : 0));
+ LexExprToken cur_token = viml_pexpr_next_token(
+ pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags);
+ if (cur_token.type == kExprLexEOC) {
+ break;
+ }
+ LexExprTokenType tok_type = cur_token.type;
+ const bool token_invalid = (tok_type == kExprLexInvalid);
+ bool is_invalid = token_invalid;
+viml_pexpr_parse_process_token:
+ // May use different flags this time.
+ cur_token = viml_pexpr_next_token(
+ pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags);
+ if (tok_type == kExprLexSpacing) {
+ if (is_invalid) {
+ HL_CUR_TOKEN(Spacing);
+ } else {
+ // Do not do anything: let regular spacing be highlighted as normal.
+ // This also allows later to highlight spacing as invalid.
+ }
+ goto viml_pexpr_parse_cycle_end;
+ } else if (is_invalid && prev_token.type == kExprLexSpacing
+ && !highlighted_prev_spacing) {
+ viml_parser_highlight(pstate, prev_token.start, prev_token.len,
+ HL(Spacing));
+ is_invalid = false;
+ highlighted_prev_spacing = true;
+ }
+ const ParserLine pline = pstate->reader.lines.items[cur_token.start.line];
+ ExprASTNode **const top_node_p = kv_last(ast_stack);
+ assert(kv_size(ast_stack) >= 1);
+ ExprASTNode *cur_node = NULL;
+#ifndef NDEBUG
+ const bool want_value = (want_node == kENodeValue);
+ assert(want_value == (*top_node_p == NULL));
+ assert(kv_A(ast_stack, 0) == &ast.root);
+ // Check that stack item i + 1 points to stack items’ i *last* child.
+ for (size_t i = 0; i + 1 < kv_size(ast_stack); i++) {
+ const bool item_null = (want_value && i + 2 == kv_size(ast_stack));
+ assert((&(*kv_A(ast_stack, i))->children == kv_A(ast_stack, i + 1)
+ && (item_null
+ ? (*kv_A(ast_stack, i))->children == NULL
+ : (*kv_A(ast_stack, i))->children->next == NULL))
+ || ((&(*kv_A(ast_stack, i))->children->next
+ == kv_A(ast_stack, i + 1))
+ && (item_null
+ ? (*kv_A(ast_stack, i))->children->next == NULL
+ : (*kv_A(ast_stack, i))->children->next->next == NULL)));
+ }
+#endif
+ // Note: in Vim whether expression "cond?d.a:2" is valid depends both on
+ // "cond" and whether "d" is a dictionary: expression is valid if condition
+ // is true and "d" is a dictionary (with "a" key or it will complain about
+ // missing one, but this is not relevant); if any of the requirements is
+ // broken then this thing is parsed as "d . a:2" yielding missing colon
+ // error. This parser does not allow such ambiguity, especially because it
+ // simply can’t: whether "d" is a dictionary is not known at the parsing
+ // time.
+ //
+ // Here example will always contain a concat with "a:2" sucking colon,
+ // making expression invalid both because there is no longer a spare colon
+ // for ternary and because concatenating dictionary with anything is not
+ // valid. There are more cases when this will make a difference though.
+ const bool node_is_key = (
+ is_concat_or_subscript
+ && (cur_token.type == kExprLexPlainIdentifier
+ ? (!cur_token.data.var.autoload
+ && cur_token.data.var.scope == kExprVarScopeMissing)
+ : (cur_token.type == kExprLexNumber))
+ && prev_token.type != kExprLexSpacing);
+ if (is_concat_or_subscript && !node_is_key) {
+ // Note: in Vim "d. a" (this is the reason behind `prev_token.type !=
+ // kExprLexSpacing` part of the condition) as well as any other "d.{expr}"
+ // where "{expr}" does not look like a key is invalid whenever "d" happens
+ // to be a dictionary. Since parser has no idea whether preceding
+ // expression is actually a dictionary it can’t outright reject anything,
+ // so it turns kExprNodeConcatOrSubscript into kExprNodeConcat instead,
+ // which will yield different errors then Vim does in a number of
+ // circumstances, and in any case runtime and not parse time errors.
+ (*kv_Z(ast_stack, 1))->type = kExprNodeConcat;
+ }
+ // Pop some stack pt_stack items in case of misplaced nodes.
+ const bool is_single_assignment = kv_last(pt_stack) == kEPTSingleAssignment;
+ switch (kv_last(pt_stack)) {
+ case kEPTExpr: {
+ break;
+ }
+ case kEPTLambdaArguments: {
+ if ((want_node == kENodeOperator
+ && tok_type != kExprLexComma
+ && tok_type != kExprLexArrow)
+ || (want_node == kENodeValue
+ && !(cur_token.type == kExprLexPlainIdentifier
+ && cur_token.data.var.scope == kExprVarScopeMissing
+ && !cur_token.data.var.autoload)
+ && tok_type != kExprLexArrow)) {
+ lambda_node->data.fig.type_guesses.allow_lambda = false;
+ if (lambda_node->children != NULL
+ && lambda_node->children->type == kExprNodeComma) {
+ // If lambda has comma child this means that parser has already seen
+ // at least "{arg1,", so node cannot possibly be anything, but
+ // lambda.
+
+ // Vim may give E121 or E720 in this case, but it does not look
+ // right to have either because both are results of reevaluation
+ // possibly-lambda node as a dictionary and here this is not going
+ // to happen.
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Expected lambda arguments list or arrow: %.*s"));
+ } else {
+ // Else it may appear that possibly-lambda node is actually
+ // a dictionary or curly-braces-name identifier.
+ lambda_node = NULL;
+ kv_drop(pt_stack, 1);
+ }
+ }
+ break;
+ }
+ case kEPTSingleAssignment:
+ case kEPTAssignment: {
+ if (want_node == kENodeValue
+ && tok_type != kExprLexBracket
+ && tok_type != kExprLexPlainIdentifier
+ && (tok_type != kExprLexFigureBrace || cur_token.data.brc.closing)
+ && !(node_is_key && tok_type == kExprLexNumber)
+ && tok_type != kExprLexEnv
+ && tok_type != kExprLexOption
+ && tok_type != kExprLexRegister) {
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Expected value part of assignment lvalue: %.*s"));
+ kv_drop(pt_stack, 1);
+ } else if (want_node == kENodeOperator
+ && tok_type != kExprLexBracket
+ && (tok_type != kExprLexFigureBrace
+ || cur_token.data.brc.closing)
+ && tok_type != kExprLexDot
+ && (tok_type != kExprLexComma || !is_single_assignment)
+ && tok_type != kExprLexAssignment
+ // Curly brace identifiers: will contain plain identifier or
+ // another curly brace in position where operator is wanted.
+ && !((tok_type == kExprLexPlainIdentifier
+ || (tok_type == kExprLexFigureBrace
+ && !cur_token.data.brc.closing))
+ && prev_token.type != kExprLexSpacing)) {
+ if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) {
+ goto viml_pexpr_parse_end;
+ }
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Expected assignment operator or subscript: %.*s"));
+ kv_drop(pt_stack, 1);
+ }
+ assert(kv_size(pt_stack));
+ break;
+ }
+ }
+ assert(kv_size(pt_stack));
+ const ExprASTParseType cur_pt = kv_last(pt_stack);
+ assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments);
+ switch (tok_type) {
+ case kExprLexMissing:
+ case kExprLexSpacing:
+ case kExprLexEOC: {
+ assert(false);
+ }
+ case kExprLexInvalid: {
+ ERROR_FROM_TOKEN(cur_token);
+ tok_type = cur_token.data.err.type;
+ goto viml_pexpr_parse_process_token;
+ }
+ case kExprLexRegister: {
+ if (want_node == kENodeOperator) {
+ // Register in operator position: e.g. @a @a
+ OP_MISSING;
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister);
+ cur_node->data.reg.name = cur_token.data.reg.name;
+ *top_node_p = cur_node;
+ want_node = kENodeOperator;
+ HL_CUR_TOKEN(Register);
+ break;
+ }
+#define SIMPLE_UB_OP(op) \
+ case kExprLex##op: { \
+ if (want_node == kENodeValue) { \
+ /* Value level: assume unary operator. */ \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \
+ *top_node_p = cur_node; \
+ kvi_push(ast_stack, &cur_node->children); \
+ HL_CUR_TOKEN(Unary##op); \
+ } else { \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \
+ ADD_OP_NODE(cur_node); \
+ HL_CUR_TOKEN(Binary##op); \
+ } \
+ want_node = kENodeValue; \
+ break; \
+ }
+ SIMPLE_UB_OP(Plus)
+ SIMPLE_UB_OP(Minus)
+#undef SIMPLE_UB_OP
+#define SIMPLE_B_OP(op, msg) \
+ case kExprLex##op: { \
+ ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \
+ HL_CUR_TOKEN(op); \
+ ADD_OP_NODE(cur_node); \
+ break; \
+ }
+ SIMPLE_B_OP(Or, "or operator")
+ SIMPLE_B_OP(And, "and operator")
+#undef SIMPLE_B_OP
+ case kExprLexMultiplication: {
+ ADD_VALUE_IF_MISSING(
+ _("E15: Unexpected multiplication-like operator: %.*s"));
+ switch (cur_token.data.mul.type) {
+#define MUL_OP(lex_op_tail, node_op_tail) \
+ case kExprLexMul##lex_op_tail: { \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \
+ HL_CUR_TOKEN(node_op_tail); \
+ break; \
+ }
+ MUL_OP(Mul, Multiplication)
+ MUL_OP(Div, Division)
+ MUL_OP(Mod, Mod)
+#undef MUL_OP
+ }
+ ADD_OP_NODE(cur_node);
+ break;
+ }
+ case kExprLexOption: {
+ if (want_node == kENodeOperator) {
+ OP_MISSING;
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOption);
+ if (cur_token.type == kExprLexInvalid) {
+ assert(cur_token.len == 1
+ || (cur_token.len == 3
+ && pline.data[cur_token.start.col + 2] == ':'));
+ cur_node->data.opt.ident = (
+ pline.data + cur_token.start.col + cur_token.len);
+ cur_node->data.opt.ident_len = 0;
+ cur_node->data.opt.scope = (
+ cur_token.len == 3
+ ? (ExprOptScope)pline.data[cur_token.start.col + 1]
+ : kExprOptScopeUnspecified);
+ } else {
+ cur_node->data.opt.ident = cur_token.data.opt.name;
+ cur_node->data.opt.ident_len = cur_token.data.opt.len;
+ cur_node->data.opt.scope = cur_token.data.opt.scope;
+ }
+ *top_node_p = cur_node;
+ want_node = kENodeOperator;
+ viml_parser_highlight(pstate, cur_token.start, 1, HL(OptionSigil));
+ const size_t scope_shift = (
+ cur_token.data.opt.scope == kExprOptScopeUnspecified ? 0 : 2);
+ if (scope_shift) {
+ viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1,
+ HL(OptionScope));
+ viml_parser_highlight(pstate, shifted_pos(cur_token.start, 2), 1,
+ HL(OptionScopeDelimiter));
+ }
+ viml_parser_highlight(
+ pstate, shifted_pos(cur_token.start, scope_shift + 1),
+ cur_token.len - (scope_shift + 1), HL(OptionName));
+ break;
+ }
+ case kExprLexEnv: {
+ if (want_node == kENodeOperator) {
+ OP_MISSING;
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeEnvironment);
+ cur_node->data.env.ident = pline.data + cur_token.start.col + 1;
+ cur_node->data.env.ident_len = cur_token.len - 1;
+ if (cur_node->data.env.ident_len == 0) {
+ ERROR_FROM_TOKEN_AND_MSG(cur_token,
+ _("E15: Environment variable name missing"));
+ }
+ *top_node_p = cur_node;
+ want_node = kENodeOperator;
+ viml_parser_highlight(pstate, cur_token.start, 1, HL(EnvironmentSigil));
+ viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1),
+ cur_token.len - 1, HL(EnvironmentName));
+ break;
+ }
+ case kExprLexNot: {
+ if (want_node == kENodeOperator) {
+ OP_MISSING;
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNot);
+ *top_node_p = cur_node;
+ kvi_push(ast_stack, &cur_node->children);
+ HL_CUR_TOKEN(Not);
+ break;
+ }
+ case kExprLexComparison: {
+ ADD_VALUE_IF_MISSING(
+ _("E15: Expected value, got comparison operator: %.*s"));
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComparison);
+ if (cur_token.type == kExprLexInvalid) {
+ cur_node->data.cmp.ccs = kCCStrategyUseOption;
+ cur_node->data.cmp.type = kExprCmpEqual;
+ cur_node->data.cmp.inv = false;
+ } else {
+ cur_node->data.cmp.ccs = cur_token.data.cmp.ccs;
+ cur_node->data.cmp.type = cur_token.data.cmp.type;
+ cur_node->data.cmp.inv = cur_token.data.cmp.inv;
+ }
+ ADD_OP_NODE(cur_node);
+ if (cur_token.data.cmp.ccs != kCCStrategyUseOption) {
+ viml_parser_highlight(pstate, cur_token.start, cur_token.len - 1,
+ HL(Comparison));
+ viml_parser_highlight(
+ pstate, shifted_pos(cur_token.start, cur_token.len - 1), 1,
+ HL(ComparisonModifier));
+ } else {
+ HL_CUR_TOKEN(Comparison);
+ }
+ want_node = kENodeValue;
+ break;
+ }
+ case kExprLexComma: {
+ assert(!(want_node == kENodeValue && cur_pt == kEPTLambdaArguments));
+ if (want_node == kENodeValue) {
+ // Value level: comma appearing here is not valid.
+ // Note: in Vim string(,x) will give E116, this is not the case here.
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Expected value, got comma: %.*s"));
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing);
+ cur_node->len = 0;
+ *top_node_p = cur_node;
+ want_node = kENodeOperator;
+ }
+ if (cur_pt == kEPTLambdaArguments) {
+ assert(lambda_node != NULL);
+ assert(lambda_node->data.fig.type_guesses.allow_lambda);
+ SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda);
+ }
+ if (kv_size(ast_stack) < 2) {
+ goto viml_pexpr_parse_invalid_comma;
+ }
+ for (size_t i = 1; i < kv_size(ast_stack); i++) {
+ ExprASTNode *const *const eastnode_p =
+ (ExprASTNode *const *)kv_Z(ast_stack, i);
+ const ExprASTNodeType eastnode_type = (*eastnode_p)->type;
+ const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p);
+ if (eastnode_type == kExprNodeLambda) {
+ assert(cur_pt == kEPTLambdaArguments
+ && want_node == kENodeOperator);
+ break;
+ } else if (eastnode_type == kExprNodeDictLiteral
+ || eastnode_type == kExprNodeListLiteral
+ || eastnode_type == kExprNodeCall) {
+ break;
+ } else if (eastnode_type == kExprNodeComma
+ || eastnode_type == kExprNodeColon
+ || eastnode_lvl > kEOpLvlComma) {
+ // Do nothing
+ } else {
+viml_pexpr_parse_invalid_comma:
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Comma outside of call, lambda or literal: %.*s"));
+ break;
+ }
+ if (i == kv_size(ast_stack) - 1) {
+ goto viml_pexpr_parse_invalid_comma;
+ }
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma);
+ ADD_OP_NODE(cur_node);
+ HL_CUR_TOKEN(Comma);
+ break;
+ }
+#define EXP_VAL_COLON "E15: Expected value, got colon: %.*s"
+ case kExprLexColon: {
+ bool is_ternary = false;
+ if (kv_size(ast_stack) < 2) {
+ goto viml_pexpr_parse_invalid_colon;
+ }
+ bool can_be_ternary = true;
+ bool is_subscript = false;
+ for (size_t i = 1; i < kv_size(ast_stack); i++) {
+ ExprASTNode *const *const eastnode_p =
+ (ExprASTNode *const *)kv_Z(ast_stack, i);
+ const ExprASTNodeType eastnode_type = (*eastnode_p)->type;
+ const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p);
+ STATIC_ASSERT(kEOpLvlTernary > kEOpLvlComma,
+ "Unexpected operator priorities");
+ if (can_be_ternary && eastnode_type == kExprNodeTernaryValue
+ && !(*eastnode_p)->data.ter.got_colon) {
+ kv_drop(ast_stack, i);
+ (*eastnode_p)->start = cur_token.start;
+ (*eastnode_p)->len = cur_token.len;
+ if (prev_token.type == kExprLexSpacing) {
+ (*eastnode_p)->start = prev_token.start;
+ (*eastnode_p)->len += prev_token.len;
+ }
+ is_ternary = true;
+ (*eastnode_p)->data.ter.got_colon = true;
+ ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON));
+ assert((*eastnode_p)->children != NULL);
+ assert((*eastnode_p)->children->next == NULL);
+ kvi_push(ast_stack, &(*eastnode_p)->children->next);
+ break;
+ } else if (eastnode_type == kExprNodeUnknownFigure) {
+ SELECT_FIGURE_BRACE_TYPE(*eastnode_p, DictLiteral, Dict);
+ break;
+ } else if (eastnode_type == kExprNodeDictLiteral) {
+ break;
+ } else if (eastnode_type == kExprNodeSubscript) {
+ is_subscript = true;
+ can_be_ternary = false;
+ assert(!is_ternary);
+ break;
+ } else if (eastnode_type == kExprNodeColon) {
+ goto viml_pexpr_parse_invalid_colon;
+ } else if (eastnode_lvl >= kEOpLvlTernaryValue) {
+ // Do nothing
+ } else if (eastnode_lvl >= kEOpLvlComma) {
+ can_be_ternary = false;
+ } else {
+ goto viml_pexpr_parse_invalid_colon;
+ }
+ if (i == kv_size(ast_stack) - 1) {
+ goto viml_pexpr_parse_invalid_colon;
+ }
+ }
+ if (is_subscript) {
+ assert(kv_size(ast_stack) > 1);
+ // Colon immediately following subscript start: it is empty subscript
+ // part like a[:2].
+ if (want_node == kENodeValue
+ && (*kv_Z(ast_stack, 1))->type == kExprNodeSubscript) {
+ NEW_NODE_WITH_CUR_POS(*top_node_p, kExprNodeMissing);
+ (*top_node_p)->len = 0;
+ want_node = kENodeOperator;
+ } else {
+ ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON));
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
+ ADD_OP_NODE(cur_node);
+ HL_CUR_TOKEN(SubscriptColon);
+ } else {
+ goto viml_pexpr_parse_valid_colon;
+viml_pexpr_parse_invalid_colon:
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Colon outside of dictionary or ternary operator: %.*s"));
+viml_pexpr_parse_valid_colon:
+ ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON));
+ if (is_ternary) {
+ HL_CUR_TOKEN(TernaryColon);
+ } else {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
+ ADD_OP_NODE(cur_node);
+ HL_CUR_TOKEN(Colon);
+ }
+ }
+ want_node = kENodeValue;
+ break;
+ }
+#undef EXP_VAL_COLON
+ case kExprLexBracket: {
+ if (cur_token.data.brc.closing) {
+ ExprASTNode **new_top_node_p = NULL;
+ // Always drop the topmost value:
+ //
+ // 1. When want_node != kENodeValue topmost item on stack is
+ // a *finished* left operand, which may as well be "{@a}" which
+ // needs not be finished again.
+ // 2. Otherwise it is pointing to NULL what nobody wants.
+ kv_drop(ast_stack, 1);
+ if (!kv_size(ast_stack)) {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral);
+ cur_node->len = 0;
+ if (want_node != kENodeValue) {
+ cur_node->children = *top_node_p;
+ }
+ *top_node_p = cur_node;
+ goto viml_pexpr_parse_bracket_closing_error;
+ }
+ if (want_node == kENodeValue) {
+ // It is OK to want value if
+ //
+ // 1. It is empty list literal, in which case top node will be
+ // ListLiteral.
+ // 2. It is list literal with trailing comma, in which case top node
+ // will be that comma.
+ // 3. It is subscript with colon, but without one of the values:
+ // e.g. "a[:]", "a[1:]", top node will be colon in this case.
+ if ((*kv_last(ast_stack))->type != kExprNodeListLiteral
+ && (*kv_last(ast_stack))->type != kExprNodeComma
+ && (*kv_last(ast_stack))->type != kExprNodeColon) {
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Expected value, got closing bracket: %.*s"));
+ }
+ } else {
+ if (!kv_size(ast_stack)) {
+ new_top_node_p = top_node_p;
+ goto viml_pexpr_parse_bracket_closing_error;
+ }
+ }
+ do {
+ new_top_node_p = kv_pop(ast_stack);
+ } while (kv_size(ast_stack)
+ && (new_top_node_p == NULL
+ || ((*new_top_node_p)->type != kExprNodeListLiteral
+ && (*new_top_node_p)->type != kExprNodeSubscript)));
+ ExprASTNode *new_top_node = *new_top_node_p;
+ switch (new_top_node->type) {
+ case kExprNodeListLiteral: {
+ if (pt_is_assignment(cur_pt) && new_top_node->children == NULL) {
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E475: Unable to assign to empty list: %.*s"));
+ }
+ HL_CUR_TOKEN(List);
+ break;
+ }
+ case kExprNodeSubscript: {
+ HL_CUR_TOKEN(SubscriptBracket);
+ break;
+ }
+ default: {
+viml_pexpr_parse_bracket_closing_error:
+ assert(!kv_size(ast_stack));
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Unexpected closing figure brace: %.*s"));
+ HL_CUR_TOKEN(List);
+ break;
+ }
+ }
+ kvi_push(ast_stack, new_top_node_p);
+ want_node = kENodeOperator;
+ if (kv_size(ast_stack) <= asgn_level) {
+ assert(kv_size(ast_stack) == asgn_level);
+ asgn_level = 0;
+ if (cur_pt == kEPTAssignment) {
+ assert(ast.err.msg);
+ } else if (cur_pt == kEPTExpr
+ && kv_size(pt_stack) > 1
+ && pt_is_assignment(kv_Z(pt_stack, 1))) {
+ kv_drop(pt_stack, 1);
+ }
+ }
+ if (cur_pt == kEPTSingleAssignment && kv_size(ast_stack) == 1) {
+ kv_drop(pt_stack, 1);
+ }
+ } else {
+ if (want_node == kENodeValue) {
+ // Value means list literal or list assignment.
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral);
+ *top_node_p = cur_node;
+ kvi_push(ast_stack, &cur_node->children);
+ want_node = kENodeValue;
+ if (cur_pt == kEPTAssignment) {
+ // Additional assignment parse type allows to easily forbid nested
+ // lists.
+ kvi_push(pt_stack, kEPTSingleAssignment);
+ } else if (cur_pt == kEPTSingleAssignment) {
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E475: Nested lists not allowed when assigning: %.*s"));
+ }
+ HL_CUR_TOKEN(List);
+ } else {
+ // Operator means subscript, also in assignment. But in assignment
+ // subscript may be pretty much any expression, so need to push
+ // kEPTExpr.
+ if (prev_token.type == kExprLexSpacing) {
+ OP_MISSING;
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript);
+ ADD_OP_NODE(cur_node);
+ HL_CUR_TOKEN(SubscriptBracket);
+ if (pt_is_assignment(cur_pt)) {
+ assert(want_node == kENodeValue); // Subtract 1 for NULL at top.
+ asgn_level = kv_size(ast_stack) - 1;
+ kvi_push(pt_stack, kEPTExpr);
+ }
+ }
+ }
+ break;
+ }
+ case kExprLexFigureBrace: {
+ if (cur_token.data.brc.closing) {
+ ExprASTNode **new_top_node_p = NULL;
+ // Always drop the topmost value:
+ //
+ // 1. When want_node != kENodeValue topmost item on stack is
+ // a *finished* left operand, which may as well be "{@a}" which
+ // needs not be finished again.
+ // 2. Otherwise it is pointing to NULL what nobody wants.
+ kv_drop(ast_stack, 1);
+ if (!kv_size(ast_stack)) {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure);
+ cur_node->data.fig.type_guesses.allow_lambda = false;
+ cur_node->data.fig.type_guesses.allow_dict = false;
+ cur_node->data.fig.type_guesses.allow_ident = false;
+ cur_node->len = 0;
+ if (want_node != kENodeValue) {
+ cur_node->children = *top_node_p;
+ }
+ *top_node_p = cur_node;
+ new_top_node_p = top_node_p;
+ goto viml_pexpr_parse_figure_brace_closing_error;
+ }
+ if (want_node == kENodeValue) {
+ if ((*kv_last(ast_stack))->type != kExprNodeUnknownFigure
+ && (*kv_last(ast_stack))->type != kExprNodeComma) {
+ // kv_last being UnknownFigure may occur for empty dictionary
+ // literal, while Comma is expected in case of non-empty one.
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E15: Expected value, got closing figure brace: %.*s"));
+ }
+ } else {
+ if (!kv_size(ast_stack)) {
+ new_top_node_p = top_node_p;
+ goto viml_pexpr_parse_figure_brace_closing_error;
+ }
+ }
+ do {
+ new_top_node_p = kv_pop(ast_stack);
+ } while (kv_size(ast_stack)
+ && (new_top_node_p == NULL
+ || ((*new_top_node_p)->type != kExprNodeUnknownFigure
+ && (*new_top_node_p)->type != kExprNodeDictLiteral
+ && ((*new_top_node_p)->type
+ != kExprNodeCurlyBracesIdentifier)
+ && (*new_top_node_p)->type != kExprNodeLambda)));
+ ExprASTNode *new_top_node = *new_top_node_p;
+ switch (new_top_node->type) {
+ case kExprNodeUnknownFigure: {
+ if (new_top_node->children == NULL) {
+ // No children of curly braces node indicates empty dictionary.
+ assert(want_node == kENodeValue);
+ assert(new_top_node->data.fig.type_guesses.allow_dict);
+ SELECT_FIGURE_BRACE_TYPE(new_top_node, DictLiteral, Dict);
+ HL_CUR_TOKEN(Dict);
+ } else if (new_top_node->data.fig.type_guesses.allow_ident) {
+ SELECT_FIGURE_BRACE_TYPE(new_top_node, CurlyBracesIdentifier,
+ Curly);
+ HL_CUR_TOKEN(Curly);
+ } else {
+ // If by this time type of the node has not already been
+ // guessed, but it definitely is not a curly braces name then
+ // it is invalid for sure.
+ ERROR_FROM_NODE_AND_MSG(
+ new_top_node,
+ _("E15: Don't know what figure brace means: %.*s"));
+ if (pstate->colors) {
+ // Will reset to NvimInvalidFigureBrace.
+ kv_A(*pstate->colors,
+ new_top_node->data.fig.opening_hl_idx).group = (
+ HL(FigureBrace));
+ }
+ HL_CUR_TOKEN(FigureBrace);
+ }
+ break;
+ }
+ case kExprNodeDictLiteral: {
+ HL_CUR_TOKEN(Dict);
+ break;
+ }
+ case kExprNodeCurlyBracesIdentifier: {
+ HL_CUR_TOKEN(Curly);
+ break;
+ }
+ case kExprNodeLambda: {
+ HL_CUR_TOKEN(Lambda);
+ break;
+ }
+ default: {
+viml_pexpr_parse_figure_brace_closing_error:
+ assert(!kv_size(ast_stack));
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Unexpected closing figure brace: %.*s"));
+ HL_CUR_TOKEN(FigureBrace);
+ break;
+ }
+ }
+ kvi_push(ast_stack, new_top_node_p);
+ want_node = kENodeOperator;
+ if (kv_size(ast_stack) <= asgn_level) {
+ assert(kv_size(ast_stack) == asgn_level);
+ if (cur_pt == kEPTExpr
+ && kv_size(pt_stack) > 1
+ && pt_is_assignment(kv_Z(pt_stack, 1))) {
+ kv_drop(pt_stack, 1);
+ asgn_level = 0;
+ }
+ }
+ } else {
+ if (want_node == kENodeValue) {
+ HL_CUR_TOKEN(FigureBrace);
+ // Value: may be any of lambda, dictionary literal and curly braces
+ // name.
+
+ // Though if we are in an assignment this may only be a curly braces
+ // name.
+ if (pt_is_assignment(cur_pt)) {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCurlyBracesIdentifier);
+ cur_node->data.fig.type_guesses.allow_lambda = false;
+ cur_node->data.fig.type_guesses.allow_dict = false;
+ cur_node->data.fig.type_guesses.allow_ident = true;
+ kvi_push(pt_stack, kEPTExpr);
+ } else {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure);
+ cur_node->data.fig.type_guesses.allow_lambda = true;
+ cur_node->data.fig.type_guesses.allow_dict = true;
+ cur_node->data.fig.type_guesses.allow_ident = true;
+ }
+ if (pstate->colors) {
+ cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors) - 1;
+ }
+ *top_node_p = cur_node;
+ kvi_push(ast_stack, &cur_node->children);
+ kvi_push(pt_stack, kEPTLambdaArguments);
+ lambda_node = cur_node;
+ } else {
+ ADD_IDENT(
+ do {
+ NEW_NODE_WITH_CUR_POS(cur_node,
+ kExprNodeCurlyBracesIdentifier);
+ cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors);
+ cur_node->data.fig.type_guesses.allow_lambda = false;
+ cur_node->data.fig.type_guesses.allow_dict = false;
+ cur_node->data.fig.type_guesses.allow_ident = true;
+ kvi_push(ast_stack, &cur_node->children);
+ if (pt_is_assignment(cur_pt)) {
+ kvi_push(pt_stack, kEPTExpr);
+ }
+ want_node = kENodeValue;
+ } while (0),
+ Curly);
+ }
+ if (pt_is_assignment(cur_pt)
+ && !pt_is_assignment(kv_last(pt_stack))) {
+ assert(want_node == kENodeValue); // Subtract 1 for NULL at top.
+ asgn_level = kv_size(ast_stack) - 1;
+ }
+ }
+ break;
+ }
+ case kExprLexArrow: {
+ if (cur_pt == kEPTLambdaArguments) {
+ kv_drop(pt_stack, 1);
+ assert(kv_size(pt_stack));
+ if (want_node == kENodeValue) {
+ // Wanting value means trailing comma and NULL at the top of the
+ // stack.
+ kv_drop(ast_stack, 1);
+ }
+ assert(kv_size(ast_stack) >= 1);
+ while ((*kv_last(ast_stack))->type != kExprNodeLambda
+ && (*kv_last(ast_stack))->type != kExprNodeUnknownFigure) {
+ kv_drop(ast_stack, 1);
+ }
+ assert((*kv_last(ast_stack)) == lambda_node);
+ SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda);
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow);
+ if (lambda_node->children == NULL) {
+ assert(want_node == kENodeValue);
+ lambda_node->children = cur_node;
+ kvi_push(ast_stack, &lambda_node->children);
+ } else {
+ assert(lambda_node->children->next == NULL);
+ lambda_node->children->next = cur_node;
+ kvi_push(ast_stack, &lambda_node->children->next);
+ }
+ kvi_push(ast_stack, &cur_node->children);
+ lambda_node = NULL;
+ } else {
+ // Only first branch is valid.
+ ADD_VALUE_IF_MISSING(_("E15: Unexpected arrow: %.*s"));
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Arrow outside of lambda: %.*s"));
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow);
+ ADD_OP_NODE(cur_node);
+ }
+ want_node = kENodeValue;
+ HL_CUR_TOKEN(Arrow);
+ break;
+ }
+ case kExprLexPlainIdentifier: {
+ const ExprVarScope scope = (cur_token.type == kExprLexInvalid
+ ? kExprVarScopeMissing
+ : cur_token.data.var.scope);
+ if (want_node == kENodeValue) {
+ want_node = kENodeOperator;
+ NEW_NODE_WITH_CUR_POS(cur_node,
+ (node_is_key
+ ? kExprNodePlainKey
+ : kExprNodePlainIdentifier));
+ cur_node->data.var.scope = scope;
+ const size_t scope_shift = (scope == kExprVarScopeMissing ? 0 : 2);
+ cur_node->data.var.ident = (pline.data + cur_token.start.col
+ + scope_shift);
+ cur_node->data.var.ident_len = cur_token.len - scope_shift;
+ *top_node_p = cur_node;
+ if (scope_shift) {
+ assert(!node_is_key);
+ viml_parser_highlight(pstate, cur_token.start, 1,
+ HL(IdentifierScope));
+ viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1,
+ HL(IdentifierScopeDelimiter));
+ }
+ viml_parser_highlight(pstate, shifted_pos(cur_token.start,
+ scope_shift),
+ cur_token.len - scope_shift,
+ (node_is_key
+ ? HL(IdentifierKey)
+ : HL(IdentifierName)));
+ } else {
+ if (scope == kExprVarScopeMissing) {
+ ADD_IDENT(
+ do {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier);
+ cur_node->data.var.scope = scope;
+ cur_node->data.var.ident = pline.data + cur_token.start.col;
+ cur_node->data.var.ident_len = cur_token.len;
+ want_node = kENodeOperator;
+ } while (0),
+ IdentifierName);
+ } else {
+ OP_MISSING;
+ }
+ }
+ break;
+ }
+ case kExprLexNumber: {
+ if (want_node != kENodeValue) {
+ OP_MISSING;
+ }
+ if (node_is_key) {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainKey);
+ cur_node->data.var.ident = pline.data + cur_token.start.col;
+ cur_node->data.var.ident_len = cur_token.len;
+ HL_CUR_TOKEN(IdentifierKey);
+ } else if (cur_token.data.num.is_float) {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeFloat);
+ cur_node->data.flt.value = cur_token.data.num.val.floating;
+ HL_CUR_TOKEN(Float);
+ } else {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeInteger);
+ cur_node->data.num.value = cur_token.data.num.val.integer;
+ const uint8_t prefix_length = base_to_prefix_length[
+ cur_token.data.num.base];
+ viml_parser_highlight(pstate, cur_token.start, prefix_length,
+ HL(NumberPrefix));
+ viml_parser_highlight(
+ pstate, shifted_pos(cur_token.start, prefix_length),
+ cur_token.len - prefix_length, HL(Number));
+ }
+ want_node = kENodeOperator;
+ *top_node_p = cur_node;
+ break;
+ }
+ case kExprLexDot: {
+ ADD_VALUE_IF_MISSING(_("E15: Unexpected dot: %.*s"));
+ if (prev_token.type == kExprLexSpacing) {
+ if (cur_pt == kEPTAssignment) {
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Cannot concatenate in assignments: %.*s"));
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcat);
+ HL_CUR_TOKEN(Concat);
+ } else {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcatOrSubscript);
+ HL_CUR_TOKEN(ConcatOrSubscript);
+ }
+ ADD_OP_NODE(cur_node);
+ break;
+ }
+ case kExprLexParenthesis: {
+ if (cur_token.data.brc.closing) {
+ if (want_node == kENodeValue) {
+ if (kv_size(ast_stack) > 1) {
+ const ExprASTNode *const prev_top_node = *kv_Z(ast_stack, 1);
+ if (prev_top_node->type == kExprNodeCall) {
+ // Function call without arguments, this is not an error.
+ // But further code does not expect NULL nodes.
+ kv_drop(ast_stack, 1);
+ goto viml_pexpr_parse_no_paren_closing_error;
+ }
+ }
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Expected value, got parenthesis: %.*s"));
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing);
+ cur_node->len = 0;
+ *top_node_p = cur_node;
+ } else {
+ // Always drop the topmost value: when want_node != kENodeValue
+ // topmost item on stack is a *finished* left operand, which may as
+ // well be "(@a)" which needs not be finished again.
+ kv_drop(ast_stack, 1);
+ }
+viml_pexpr_parse_no_paren_closing_error: {}
+ ExprASTNode **new_top_node_p = NULL;
+ while (kv_size(ast_stack)
+ && (new_top_node_p == NULL
+ || ((*new_top_node_p)->type != kExprNodeNested
+ && (*new_top_node_p)->type != kExprNodeCall))) {
+ new_top_node_p = kv_pop(ast_stack);
+ }
+ if (new_top_node_p != NULL
+ && ((*new_top_node_p)->type == kExprNodeNested
+ || (*new_top_node_p)->type == kExprNodeCall)) {
+ if ((*new_top_node_p)->type == kExprNodeNested) {
+ HL_CUR_TOKEN(NestingParenthesis);
+ } else {
+ HL_CUR_TOKEN(CallingParenthesis);
+ }
+ } else {
+ // “Always drop the topmost value” branch has got rid of the single
+ // value stack had, so there is nothing known to enclose. Correct
+ // this.
+ if (new_top_node_p == NULL) {
+ new_top_node_p = top_node_p;
+ }
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Unexpected closing parenthesis: %.*s"));
+ HL_CUR_TOKEN(NestingParenthesis);
+ cur_node = NEW_NODE(kExprNodeNested);
+ cur_node->start = cur_token.start;
+ cur_node->len = 0;
+ // Unexpected closing parenthesis, assume that it was wanted to
+ // enclose everything in ().
+ cur_node->children = *new_top_node_p;
+ *new_top_node_p = cur_node;
+ assert(cur_node->next == NULL);
+ }
+ kvi_push(ast_stack, new_top_node_p);
+ want_node = kENodeOperator;
+ } else {
+ if (want_node == kENodeValue) {
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested);
+ *top_node_p = cur_node;
+ kvi_push(ast_stack, &cur_node->children);
+ HL_CUR_TOKEN(NestingParenthesis);
+ } else if (want_node == kENodeOperator) {
+ if (prev_token.type == kExprLexSpacing) {
+ // For some reason "function (args)" is a function call, but
+ // "(funcref) (args)" is not. AFAIR this somehow involves
+ // compatibility and Bram was commenting that this is
+ // intentionally inconsistent and he is not very happy with the
+ // situation himself.
+ if ((*top_node_p)->type != kExprNodePlainIdentifier
+ && (*top_node_p)->type != kExprNodeComplexIdentifier
+ && (*top_node_p)->type != kExprNodeCurlyBracesIdentifier) {
+ OP_MISSING;
+ }
+ }
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall);
+ ADD_OP_NODE(cur_node);
+ HL_CUR_TOKEN(CallingParenthesis);
+ } else {
+ // Currently it is impossible to reach this.
+ assert(false);
+ }
+ want_node = kENodeValue;
+ }
+ break;
+ }
+ case kExprLexQuestion: {
+ ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s"));
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary);
+ ADD_OP_NODE(cur_node);
+ HL_CUR_TOKEN(Ternary);
+ ExprASTNode *ter_val_node;
+ NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue);
+ ter_val_node->data.ter.got_colon = false;
+ assert(cur_node->children != NULL);
+ assert(cur_node->children->next == NULL);
+ assert(kv_last(ast_stack) == &cur_node->children->next);
+ *kv_last(ast_stack) = ter_val_node;
+ kvi_push(ast_stack, &ter_val_node->children);
+ break;
+ }
+ case kExprLexDoubleQuotedString:
+ case kExprLexSingleQuotedString: {
+ const bool is_double = (tok_type == kExprLexDoubleQuotedString);
+ if (!cur_token.data.str.closed) {
+ // It is weird, but Vim has two identical errors messages with
+ // different error numbers: "E114: Missing quote" and
+ // "E115: Missing quote".
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, (is_double
+ ? _("E114: Missing double quote: %.*s")
+ : _("E115: Missing single quote: %.*s")));
+ }
+ if (want_node == kENodeOperator) {
+ OP_MISSING;
+ }
+ NEW_NODE_WITH_CUR_POS(
+ cur_node, (is_double
+ ? kExprNodeDoubleQuotedString
+ : kExprNodeSingleQuotedString));
+ *top_node_p = cur_node;
+ parse_quoted_string(pstate, cur_node, cur_token, ast_stack, is_invalid);
+ want_node = kENodeOperator;
+ break;
+ }
+ case kExprLexAssignment: {
+ if (cur_pt == kEPTAssignment) {
+ kv_drop(pt_stack, 1);
+ } else if (cur_pt == kEPTSingleAssignment) {
+ kv_drop(pt_stack, 2);
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token,
+ _("E475: Expected closing bracket to end list assignment "
+ "lvalue: %.*s"));
+ } else {
+ ERROR_FROM_TOKEN_AND_MSG(
+ cur_token, _("E15: Misplaced assignment: %.*s"));
+ }
+ assert(kv_size(pt_stack));
+ assert(kv_last(pt_stack) == kEPTExpr);
+ ADD_VALUE_IF_MISSING(_("E15: Unexpected assignment: %.*s"));
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeAssignment);
+ cur_node->data.ass.type = cur_token.data.ass.type;
+ switch (cur_token.data.ass.type) {
+#define HL_ASGN(asgn, hl) \
+ case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; }
+ HL_ASGN(Plain, PlainAssignment)
+ HL_ASGN(Add, AssignmentWithAddition)
+ HL_ASGN(Subtract, AssignmentWithSubtraction)
+ HL_ASGN(Concat, AssignmentWithConcatenation)
+#undef HL_ASGN
+ }
+ ADD_OP_NODE(cur_node);
+ break;
+ }
+ }
+viml_pexpr_parse_cycle_end:
+ prev_token = cur_token;
+ highlighted_prev_spacing = false;
+ viml_parser_advance(pstate, cur_token.len);
+ } while (true);
+viml_pexpr_parse_end:
+ assert(kv_size(pt_stack));
+ assert(kv_size(ast_stack));
+ if (want_node == kENodeValue
+ // Blacklist some parse type entries as their presence means better error
+ // message in the other branch.
+ && kv_last(pt_stack) != kEPTLambdaArguments) {
+ east_set_error(pstate, &ast.err, _("E15: Expected value, got EOC: %.*s"),
+ pstate->pos);
+ } else if (kv_size(ast_stack) != 1) {
+ // Something may be wrong, check whether it really is.
+
+ // Pointer to ast.root must never be dropped, so “!= 1” is expected to be
+ // the same as “> 1”.
+ assert(kv_size(ast_stack));
+ // Topmost stack item must be a *finished* value, so it must not be
+ // analyzed. E.g. it may contain an already finished nested expression.
+ kv_drop(ast_stack, 1);
+ while (ast.err.msg == NULL && kv_size(ast_stack)) {
+ const ExprASTNode *const cur_node = (*kv_pop(ast_stack));
+ // This should only happen when want_node == kENodeValue.
+ assert(cur_node != NULL);
+ // TODO(ZyX-I): Rehighlight as invalid?
+ switch (cur_node->type) {
+ case kExprNodeOpMissing:
+ case kExprNodeMissing: {
+ // Error should’ve been already reported.
+ break;
+ }
+ case kExprNodeCall: {
+ east_set_error(
+ pstate, &ast.err,
+ _("E116: Missing closing parenthesis for function call: %.*s"),
+ cur_node->start);
+ break;
+ }
+ case kExprNodeNested: {
+ east_set_error(
+ pstate, &ast.err,
+ _("E110: Missing closing parenthesis for nested expression"
+ ": %.*s"),
+ cur_node->start);
+ break;
+ }
+ case kExprNodeListLiteral: {
+ // For whatever reason "[1" yields "E696: Missing comma in list" error
+ // in Vim while "[1," yields E697.
+ east_set_error(
+ pstate, &ast.err,
+ _("E697: Missing end of List ']': %.*s"),
+ cur_node->start);
+ break;
+ }
+ case kExprNodeDictLiteral: {
+ // Same problem like with list literal with E722 (missing comma) vs
+ // E723, but additionally just "{" yields only E15.
+ east_set_error(
+ pstate, &ast.err,
+ _("E723: Missing end of Dictionary '}': %.*s"),
+ cur_node->start);
+ break;
+ }
+ case kExprNodeUnknownFigure: {
+ east_set_error(
+ pstate, &ast.err,
+ _("E15: Missing closing figure brace: %.*s"),
+ cur_node->start);
+ break;
+ }
+ case kExprNodeLambda: {
+ east_set_error(
+ pstate, &ast.err,
+ _("E15: Missing closing figure brace for lambda: %.*s"),
+ cur_node->start);
+ break;
+ }
+ case kExprNodeCurlyBracesIdentifier: {
+ // Until trailing "}" it is impossible to distinguish curly braces
+ // identifier and dictionary, so it must not appear in the stack like
+ // this.
+ assert(false);
+ }
+ case kExprNodeInteger:
+ case kExprNodeFloat:
+ case kExprNodeSingleQuotedString:
+ case kExprNodeDoubleQuotedString:
+ case kExprNodeOption:
+ case kExprNodeEnvironment:
+ case kExprNodeRegister:
+ case kExprNodePlainIdentifier:
+ case kExprNodePlainKey: {
+ // These are plain values and not containers, for them it should only
+ // be possible to show up in the topmost stack element, but it was
+ // unconditionally popped at the start.
+ assert(false);
+ }
+ case kExprNodeComma:
+ case kExprNodeColon:
+ case kExprNodeArrow: {
+ // It is actually only valid inside something else, but everything
+ // where one of the above is valid requires to be closed and thus is
+ // to be caught later.
+ break;
+ }
+ case kExprNodeSubscript:
+ case kExprNodeConcatOrSubscript:
+ case kExprNodeComplexIdentifier:
+ case kExprNodeAssignment:
+ case kExprNodeMod:
+ case kExprNodeDivision:
+ case kExprNodeMultiplication:
+ case kExprNodeNot:
+ case kExprNodeAnd:
+ case kExprNodeOr:
+ case kExprNodeConcat:
+ case kExprNodeComparison:
+ case kExprNodeUnaryMinus:
+ case kExprNodeUnaryPlus:
+ case kExprNodeBinaryMinus:
+ case kExprNodeTernary:
+ case kExprNodeBinaryPlus: {
+ // It is OK to see these in the stack.
+ break;
+ }
+ case kExprNodeTernaryValue: {
+ if (!cur_node->data.ter.got_colon) {
+ // Actually Vim throws E109 in more cases.
+ east_set_error(
+ pstate, &ast.err, _("E109: Missing ':' after '?': %.*s"),
+ cur_node->start);
+ }
+ break;
+ }
+ }
+ }
+ }
+ kvi_destroy(ast_stack);
+ return ast;
+} // NOLINT(readability/fn_size)
+
+#undef NEW_NODE
+#undef HL
diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h
new file mode 100644
index 0000000000..23e172da75
--- /dev/null
+++ b/src/nvim/viml/parser/expressions.h
@@ -0,0 +1,389 @@
+#ifndef NVIM_VIML_PARSER_EXPRESSIONS_H
+#define NVIM_VIML_PARSER_EXPRESSIONS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "nvim/types.h"
+#include "nvim/viml/parser/parser.h"
+#include "nvim/eval/typval.h"
+
+// Defines whether to ignore case:
+// == kCCStrategyUseOption
+// ==# kCCStrategyMatchCase
+// ==? kCCStrategyIgnoreCase
+typedef enum {
+ kCCStrategyUseOption = 0, // 0 for xcalloc
+ kCCStrategyMatchCase = '#',
+ kCCStrategyIgnoreCase = '?',
+} ExprCaseCompareStrategy;
+
+/// Lexer token type
+typedef enum {
+ kExprLexInvalid = 0, ///< Invalid token, indicaten an error.
+ kExprLexMissing, ///< Missing token, for use in parser.
+ kExprLexSpacing, ///< Spaces, tabs, newlines, etc.
+ kExprLexEOC, ///< End of command character: NL, |, just end of stream.
+
+ kExprLexQuestion, ///< Question mark, for use in ternary.
+ kExprLexColon, ///< Colon, for use in ternary.
+ kExprLexOr, ///< Logical or operator.
+ kExprLexAnd, ///< Logical and operator.
+ kExprLexComparison, ///< One of the comparison operators.
+ kExprLexPlus, ///< Plus sign.
+ kExprLexMinus, ///< Minus sign.
+ kExprLexDot, ///< Dot: either concat or subscript, also part of the float.
+ kExprLexMultiplication, ///< Multiplication, division or modulo operator.
+
+ kExprLexNot, ///< Not: !.
+
+ kExprLexNumber, ///< Integer number literal, or part of a float.
+ kExprLexSingleQuotedString, ///< Single quoted string literal.
+ kExprLexDoubleQuotedString, ///< Double quoted string literal.
+ kExprLexOption, ///< &optionname option value.
+ kExprLexRegister, ///< @r register value.
+ kExprLexEnv, ///< Environment $variable value.
+ kExprLexPlainIdentifier, ///< Identifier without scope: `abc`, `foo#bar`.
+
+ kExprLexBracket, ///< Bracket, either opening or closing.
+ kExprLexFigureBrace, ///< Figure brace, either opening or closing.
+ kExprLexParenthesis, ///< Parenthesis, either opening or closing.
+ kExprLexComma, ///< Comma.
+ kExprLexArrow, ///< Arrow, like from lambda expressions.
+ kExprLexAssignment, ///< Assignment: `=` or `{op}=`.
+ // XXX When modifying this enum you need to also modify eltkn_type_tab in
+ // expressions.c and tests and, possibly, viml_pexpr_repr_token.
+} LexExprTokenType;
+
+typedef enum {
+ kExprCmpEqual, ///< Equality, unequality.
+ kExprCmpMatches, ///< Matches regex, not matches regex.
+ kExprCmpGreater, ///< `>` or `<=`
+ kExprCmpGreaterOrEqual, ///< `>=` or `<`.
+ kExprCmpIdentical, ///< `is` or `isnot`
+} ExprComparisonType;
+
+/// All possible option scopes
+typedef enum {
+ kExprOptScopeUnspecified = 0,
+ kExprOptScopeGlobal = 'g',
+ kExprOptScopeLocal = 'l',
+} ExprOptScope;
+
+/// All possible assignment types: `=` and `{op}=`.
+typedef enum {
+ kExprAsgnPlain = 0, ///< Plain assignment: `=`.
+ kExprAsgnAdd, ///< Assignment augmented with addition: `+=`.
+ kExprAsgnSubtract, ///< Assignment augmented with subtraction: `-=`.
+ kExprAsgnConcat, ///< Assignment augmented with concatenation: `.=`.
+} ExprAssignmentType;
+
+#define EXPR_OPT_SCOPE_LIST \
+ ((char[]){ kExprOptScopeGlobal, kExprOptScopeLocal })
+
+/// All possible variable scopes
+typedef enum {
+ kExprVarScopeMissing = 0,
+ kExprVarScopeScript = 's',
+ kExprVarScopeGlobal = 'g',
+ kExprVarScopeVim = 'v',
+ kExprVarScopeBuffer = 'b',
+ kExprVarScopeWindow = 'w',
+ kExprVarScopeTabpage = 't',
+ kExprVarScopeLocal = 'l',
+ kExprVarScopeArguments = 'a',
+} ExprVarScope;
+
+#define EXPR_VAR_SCOPE_LIST \
+ ((char[]) { \
+ kExprVarScopeScript, kExprVarScopeGlobal, kExprVarScopeVim, \
+ kExprVarScopeBuffer, kExprVarScopeWindow, kExprVarScopeTabpage, \
+ kExprVarScopeLocal, kExprVarScopeBuffer, kExprVarScopeArguments, \
+ })
+
+/// Lexer token
+typedef struct {
+ ParserPosition start;
+ size_t len;
+ LexExprTokenType type;
+ union {
+ struct {
+ ExprComparisonType type; ///< Comparison type.
+ ExprCaseCompareStrategy ccs; ///< Case comparison strategy.
+ bool inv; ///< True if comparison is to be inverted.
+ } cmp; ///< For kExprLexComparison.
+
+ struct {
+ enum {
+ kExprLexMulMul, ///< Real multiplication.
+ kExprLexMulDiv, ///< Division.
+ kExprLexMulMod, ///< Modulo.
+ } type; ///< Multiplication type.
+ } mul; ///< For kExprLexMultiplication.
+
+ struct {
+ bool closing; ///< True if bracket/etc is a closing one.
+ } brc; ///< For brackets/braces/parenthesis.
+
+ struct {
+ int name; ///< Register name, may be -1 if name not present.
+ } reg; ///< For kExprLexRegister.
+
+ struct {
+ bool closed; ///< True if quote was closed.
+ } str; ///< For kExprLexSingleQuotedString and kExprLexDoubleQuotedString.
+
+ struct {
+ const char *name; ///< Option name start.
+ size_t len; ///< Option name length.
+ ExprOptScope scope; ///< Option scope: &l:, &g: or not specified.
+ } opt; ///< Option properties.
+
+ struct {
+ ExprVarScope scope; ///< Scope character or 0 if not present.
+ bool autoload; ///< Has autoload characters.
+ } var; ///< For kExprLexPlainIdentifier
+
+ struct {
+ LexExprTokenType type; ///< Suggested type for parsing incorrect code.
+ const char *msg; ///< Error message.
+ } err; ///< For kExprLexInvalid
+
+ struct {
+ union {
+ float_T floating;
+ uvarnumber_T integer;
+ } val; ///< Number value.
+ uint8_t base; ///< Base: 2, 8, 10 or 16.
+ bool is_float; ///< True if number is a floating-point.
+ } num; ///< For kExprLexNumber
+
+ struct {
+ ExprAssignmentType type;
+ } ass; ///< For kExprLexAssignment
+ } data; ///< Additional data, if needed.
+} LexExprToken;
+
+typedef enum {
+ /// If set, “pointer” to the current byte in pstate will not be shifted
+ kELFlagPeek = (1 << 0),
+ /// Determines whether scope is allowed to come before the identifier
+ kELFlagForbidScope = (1 << 1),
+ /// Determines whether floating-point numbers are allowed
+ ///
+ /// I.e. whether dot is a decimal point separator or is not a part of
+ /// a number at all.
+ kELFlagAllowFloat = (1 << 2),
+ /// Determines whether `is` and `isnot` are seen as comparison operators
+ ///
+ /// If set they are supposed to be just regular identifiers.
+ kELFlagIsNotCmp = (1 << 3),
+ /// Determines whether EOC tokens are allowed
+ ///
+ /// If set then it will yield Invalid token with E15 in place of EOC one if
+ /// “EOC” is something like "|". It is fine with emitting EOC at the end of
+ /// string still, with or without this flag set.
+ kELFlagForbidEOC = (1 << 4),
+ // XXX Whenever you add a new flag, alter klee_assume() statement in
+ // viml_expressions_lexer.c.
+} LexExprFlags;
+
+/// Expression AST node type
+typedef enum {
+ kExprNodeMissing = 0,
+ kExprNodeOpMissing,
+ kExprNodeTernary, ///< Ternary operator.
+ kExprNodeTernaryValue, ///< Ternary operator, colon.
+ kExprNodeRegister, ///< Register.
+ kExprNodeSubscript, ///< Subscript.
+ kExprNodeListLiteral, ///< List literal.
+ kExprNodeUnaryPlus,
+ kExprNodeBinaryPlus,
+ kExprNodeNested, ///< Nested parenthesised expression.
+ kExprNodeCall, ///< Function call.
+ /// Plain identifier: simple variable/function name
+ ///
+ /// Looks like "string", "g:Foo", etc: consists from a single
+ /// kExprLexPlainIdentifier token.
+ kExprNodePlainIdentifier,
+ /// Plain dictionary key, for use with kExprNodeConcatOrSubscript
+ kExprNodePlainKey,
+ /// Complex identifier: variable/function name with curly braces
+ kExprNodeComplexIdentifier,
+ /// Figure brace expression which is not yet known
+ ///
+ /// May resolve to any of kExprNodeDictLiteral, kExprNodeLambda or
+ /// kExprNodeCurlyBracesIdentifier.
+ kExprNodeUnknownFigure,
+ kExprNodeLambda, ///< Lambda.
+ kExprNodeDictLiteral, ///< Dictionary literal.
+ kExprNodeCurlyBracesIdentifier, ///< Part of the curly braces name.
+ kExprNodeComma, ///< Comma “operator”.
+ kExprNodeColon, ///< Colon “operator”.
+ kExprNodeArrow, ///< Arrow “operator”.
+ kExprNodeComparison, ///< Various comparison operators.
+ /// Concat operator
+ ///
+ /// To be only used in cases when it is known for sure it is not a subscript.
+ kExprNodeConcat,
+ /// Concat or subscript operator
+ ///
+ /// For cases when it is not obvious whether expression is a concat or
+ /// a subscript. May only have either number or plain identifier as the second
+ /// child. To make it easier to avoid curly braces in place of
+ /// kExprNodePlainIdentifier node kExprNodePlainKey is used.
+ kExprNodeConcatOrSubscript,
+ kExprNodeInteger, ///< Integral number.
+ kExprNodeFloat, ///< Floating-point number.
+ kExprNodeSingleQuotedString,
+ kExprNodeDoubleQuotedString,
+ kExprNodeOr,
+ kExprNodeAnd,
+ kExprNodeUnaryMinus,
+ kExprNodeBinaryMinus,
+ kExprNodeNot,
+ kExprNodeMultiplication,
+ kExprNodeDivision,
+ kExprNodeMod,
+ kExprNodeOption,
+ kExprNodeEnvironment,
+ kExprNodeAssignment,
+ // XXX When modifying this list also modify east_node_type_tab both in parser
+ // and in tests, and you most likely will also have to alter list of
+ // highlight groups stored in highlight_init_cmdline variable.
+} ExprASTNodeType;
+
+typedef struct expr_ast_node ExprASTNode;
+
+/// Structure representing one AST node
+struct expr_ast_node {
+ ExprASTNodeType type; ///< Node type.
+ /// Node children: e.g. for 1 + 2 nodes 1 and 2 will be children of +.
+ ExprASTNode *children;
+ /// Next node: e.g. for 1 + 2 child nodes 1 and 2 are put into a single-linked
+ /// list: `(+)->children` references only node 1, node 2 is in
+ /// `(+)->children->next`.
+ ExprASTNode *next;
+ ParserPosition start;
+ size_t len;
+ union {
+ struct {
+ int name; ///< Register name, may be -1 if name not present.
+ } reg; ///< For kExprNodeRegister.
+ struct {
+ /// Which nodes UnknownFigure can’t possibly represent.
+ struct {
+ /// True if UnknownFigure may actually represent dictionary literal.
+ bool allow_dict;
+ /// True if UnknownFigure may actually represent lambda.
+ bool allow_lambda;
+ /// True if UnknownFigure may actually be part of curly braces name.
+ bool allow_ident;
+ } type_guesses;
+ /// Highlight chunk index, used for rehighlighting if needed
+ size_t opening_hl_idx;
+ } fig; ///< For kExprNodeUnknownFigure.
+ struct {
+ ExprVarScope scope; ///< Scope character or 0 if not present.
+ /// Actual identifier without scope.
+ ///
+ /// Points to inside parser reader state.
+ const char *ident;
+ size_t ident_len; ///< Actual identifier length.
+ } var; ///< For kExprNodePlainIdentifier and kExprNodePlainKey.
+ struct {
+ bool got_colon; ///< True if colon was seen.
+ } ter; ///< For kExprNodeTernaryValue.
+ struct {
+ ExprComparisonType type; ///< Comparison type.
+ ExprCaseCompareStrategy ccs; ///< Case comparison strategy.
+ bool inv; ///< True if comparison is to be inverted.
+ } cmp; ///< For kExprNodeComparison.
+ struct {
+ uvarnumber_T value;
+ } num; ///< For kExprNodeInteger.
+ struct {
+ float_T value;
+ } flt; ///< For kExprNodeFloat.
+ struct {
+ char *value;
+ size_t size;
+ } str; ///< For kExprNodeSingleQuotedString and
+ ///< kExprNodeDoubleQuotedString.
+ struct {
+ const char *ident; ///< Option name start.
+ size_t ident_len; ///< Option name length.
+ ExprOptScope scope; ///< Option scope: &l:, &g: or not specified.
+ } opt; ///< For kExprNodeOption.
+ struct {
+ const char *ident; ///< Environment variable name start.
+ size_t ident_len; ///< Environment variable name length.
+ } env; ///< For kExprNodeEnvironment.
+ struct {
+ ExprAssignmentType type;
+ } ass; ///< For kExprNodeAssignment
+ } data;
+};
+
+enum {
+ /// Allow multiple expressions in a row: e.g. for :echo
+ ///
+ /// Parser will still parse only one of them though.
+ kExprFlagsMulti = (1 << 0),
+ /// Allow NL, NUL and bar to be EOC
+ ///
+ /// When parsing expressions input by user bar is assumed to be a binary
+ /// operator and other two are spacings.
+ kExprFlagsDisallowEOC = (1 << 1),
+ /// Parse :let argument
+ ///
+ /// That mean that top level node must be an assignment and first nodes
+ /// belong to lvalues.
+ kExprFlagsParseLet = (1 << 2),
+ // XXX whenever you add a new flag, alter klee_assume() statement in
+ // viml_expressions_parser.c, nvim_parse_expression() flags parsing
+ // alongside with its documentation and flag sets in check_parsing()
+ // function in expressions parser functional and unit tests.
+} ExprParserFlags;
+
+/// AST error definition
+typedef struct {
+ /// Error message. Must contain a single printf format atom: %.*s.
+ const char *msg;
+ /// Error message argument: points to the location of the error.
+ const char *arg;
+ /// Message argument length: length till the end of string.
+ int arg_len;
+} ExprASTError;
+
+/// Structure representing complety AST for one expression
+typedef struct {
+ /// When AST is not correct this message will be printed.
+ ///
+ /// Uses `emsgf(msg, arg_len, arg);`, `msg` is assumed to contain only `%.*s`.
+ ExprASTError err;
+ /// Root node of the AST.
+ ExprASTNode *root;
+} ExprAST;
+
+/// Array mapping ExprASTNodeType to maximum amount of children node may have
+extern const uint8_t node_maxchildren[];
+
+/// Array mapping ExprASTNodeType values to their stringified versions
+extern const char *const east_node_type_tab[];
+
+/// Array mapping ExprComparisonType values to their stringified versions
+extern const char *const eltkn_cmp_type_tab[];
+
+/// Array mapping ExprCaseCompareStrategy values to their stringified versions
+extern const char *const ccs_tab[];
+
+/// Array mapping ExprAssignmentType values to their stringified versions
+extern const char *const expr_asgn_type_tab[];
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "viml/parser/expressions.h.generated.h"
+#endif
+
+#endif // NVIM_VIML_PARSER_EXPRESSIONS_H
diff --git a/src/nvim/viml/parser/parser.c b/src/nvim/viml/parser/parser.c
new file mode 100644
index 0000000000..8d26d08ea7
--- /dev/null
+++ b/src/nvim/viml/parser/parser.c
@@ -0,0 +1,16 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/viml/parser/parser.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "viml/parser/parser.c.generated.h"
+#endif
+
+
+void parser_simple_get_line(void *cookie, ParserLine *ret_pline)
+{
+ ParserLine **plines_p = (ParserLine **)cookie;
+ *ret_pline = **plines_p;
+ (*plines_p)++;
+}
diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h
new file mode 100644
index 0000000000..7ac49709d8
--- /dev/null
+++ b/src/nvim/viml/parser/parser.h
@@ -0,0 +1,244 @@
+#ifndef NVIM_VIML_PARSER_PARSER_H
+#define NVIM_VIML_PARSER_PARSER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+
+#include "nvim/lib/kvec.h"
+#include "nvim/func_attr.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+
+/// One parsed line
+typedef struct {
+ const char *data; ///< Parsed line pointer
+ size_t size; ///< Parsed line size
+ bool allocated; ///< True if line may be freed.
+} ParserLine;
+
+/// Line getter type for parser
+///
+/// Line getter must return {NULL, 0} for EOF.
+typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline);
+
+/// Parser position in the input
+typedef struct {
+ size_t line; ///< Line index in ParserInputReader.lines.
+ size_t col; ///< Byte index in the line.
+} ParserPosition;
+
+/// Parser state item.
+typedef struct {
+ enum {
+ kPTopStateParsingCommand = 0,
+ kPTopStateParsingExpression,
+ } type;
+ union {
+ struct {
+ enum {
+ kExprUnknown = 0,
+ } type;
+ } expr;
+ } data;
+} ParserStateItem;
+
+/// Structure defining input reader
+typedef struct {
+ /// Function used to get next line.
+ ParserLineGetter get_line;
+ /// Data for get_line function.
+ void *cookie;
+ /// All lines obtained by get_line.
+ kvec_withinit_t(ParserLine, 4) lines;
+ /// Conversion, for :scriptencoding.
+ vimconv_T conv;
+} ParserInputReader;
+
+/// Highlighted region definition
+///
+/// Note: one chunk may highlight only one line.
+typedef struct {
+ ParserPosition start; ///< Start of the highlight: line and column.
+ size_t end_col; ///< End column, points to the start of the next character.
+ const char *group; ///< Highlight group.
+} ParserHighlightChunk;
+
+/// Highlighting defined by a parser
+typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight;
+
+/// Structure defining parser state
+typedef struct {
+ /// Line reader.
+ ParserInputReader reader;
+ /// Position up to which input was parsed.
+ ParserPosition pos;
+ /// Parser state stack.
+ kvec_withinit_t(ParserStateItem, 16) stack;
+ /// Highlighting support.
+ ParserHighlight *colors;
+ /// True if line continuation can be used.
+ bool can_continuate;
+} ParserState;
+
+static inline void viml_parser_init(
+ ParserState *const ret_pstate,
+ const ParserLineGetter get_line, void *const cookie,
+ ParserHighlight *const colors)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2);
+
+/// Initialize a new parser state instance
+///
+/// @param[out] ret_pstate Parser state to initialize.
+/// @param[in] get_line Line getter function.
+/// @param[in] cookie Argument for the get_line function.
+/// @param[in] colors Where to save highlighting. May be NULL if it is not
+/// needed.
+static inline void viml_parser_init(
+ ParserState *const ret_pstate,
+ const ParserLineGetter get_line, void *const cookie,
+ ParserHighlight *const colors)
+{
+ *ret_pstate = (ParserState) {
+ .reader = {
+ .get_line = get_line,
+ .cookie = cookie,
+ .conv = MBYTE_NONE_CONV,
+ },
+ .pos = { 0, 0 },
+ .colors = colors,
+ .can_continuate = false,
+ };
+ kvi_init(ret_pstate->reader.lines);
+ kvi_init(ret_pstate->stack);
+}
+
+static inline void viml_parser_destroy(ParserState *const pstate)
+ REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
+
+/// Free all memory allocated by the parser on heap
+///
+/// @param pstate Parser state to free.
+static inline void viml_parser_destroy(ParserState *const pstate)
+{
+ for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) {
+ ParserLine pline = kv_A(pstate->reader.lines, i);
+ if (pline.allocated) {
+ xfree((void *)pline.data);
+ }
+ }
+ kvi_destroy(pstate->reader.lines);
+ kvi_destroy(pstate->stack);
+}
+
+static inline void viml_preader_get_line(ParserInputReader *const preader,
+ ParserLine *const ret_pline)
+ REAL_FATTR_NONNULL_ALL;
+
+/// Get one line from ParserInputReader
+static inline void viml_preader_get_line(ParserInputReader *const preader,
+ ParserLine *const ret_pline)
+{
+ ParserLine pline;
+ preader->get_line(preader->cookie, &pline);
+ if (preader->conv.vc_type != CONV_NONE && pline.size) {
+ ParserLine cpline = {
+ .allocated = true,
+ .size = pline.size,
+ };
+ cpline.data = (char *)string_convert(&preader->conv,
+ (char_u *)pline.data,
+ &cpline.size);
+ if (pline.allocated) {
+ xfree((void *)pline.data);
+ }
+ pline = cpline;
+ }
+ kvi_push(preader->lines, pline);
+ *ret_pline = pline;
+}
+
+static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
+ ParserLine *const ret_pline)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
+
+/// Get currently parsed line, shifted to pstate->pos.col
+///
+/// @param pstate Parser state to operate on.
+///
+/// @return True if there is a line, false in case of EOF.
+static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
+ ParserLine *const ret_pline)
+{
+ const size_t num_lines = kv_size(pstate->reader.lines);
+ if (pstate->pos.line == num_lines) {
+ viml_preader_get_line(&pstate->reader, ret_pline);
+ } else {
+ *ret_pline = kv_last(pstate->reader.lines);
+ }
+ assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1);
+ if (ret_pline->data != NULL) {
+ ret_pline->data += pstate->pos.col;
+ ret_pline->size -= pstate->pos.col;
+ }
+ return ret_pline->data != NULL;
+}
+
+static inline void viml_parser_advance(ParserState *const pstate,
+ const size_t len)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
+
+/// Advance position by a given number of bytes
+///
+/// At maximum advances to the next line.
+///
+/// @param pstate Parser state to advance.
+/// @param[in] len Number of bytes to advance.
+static inline void viml_parser_advance(ParserState *const pstate,
+ const size_t len)
+{
+ assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1);
+ const ParserLine pline = kv_last(pstate->reader.lines);
+ if (pstate->pos.col + len >= pline.size) {
+ pstate->pos.line++;
+ pstate->pos.col = 0;
+ } else {
+ pstate->pos.col += len;
+ }
+}
+
+static inline void viml_parser_highlight(ParserState *const pstate,
+ const ParserPosition start,
+ const size_t end_col,
+ const char *const group)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
+
+/// Record highlighting of some region of text
+///
+/// @param pstate Parser state to work with.
+/// @param[in] start Start position of the highlight.
+/// @param[in] len Highlighting chunk length.
+/// @param[in] group Highlight group.
+static inline void viml_parser_highlight(ParserState *const pstate,
+ const ParserPosition start,
+ const size_t len,
+ const char *const group)
+{
+ if (pstate->colors == NULL || len == 0) {
+ return;
+ }
+ assert(kv_size(*pstate->colors) == 0
+ || kv_Z(*pstate->colors, 0).start.line < start.line
+ || kv_Z(*pstate->colors, 0).end_col <= start.col);
+ kvi_push(*pstate->colors, ((ParserHighlightChunk) {
+ .start = start,
+ .end_col = start.col + len,
+ .group = group,
+ }));
+}
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "viml/parser/parser.h.generated.h"
+#endif
+
+#endif // NVIM_VIML_PARSER_PARSER_H
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 43af2e5e4f..b4ef901d94 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -193,7 +193,7 @@ newwindow:
/* cursor to previous window with wrap around */
case 'W':
CHECK_CMDWIN
- if (firstwin == lastwin && Prenum != 1) /* just one window */
+ if (ONE_WINDOW && Prenum != 1) /* just one window */
beep_flush();
else {
if (Prenum) { /* go to specified window */
@@ -574,7 +574,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
oldwin = curwin;
/* add a status line when p_ls == 1 and splitting the first window */
- if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) {
+ if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) {
if (oldwin->w_height <= p_wmh && new_wp == NULL) {
EMSG(_(e_noroom));
return FAIL;
@@ -641,11 +641,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
if (oldwin->w_width - new_size - 1 < p_wmw)
do_equal = TRUE;
- /* We don't like to take lines for the new window from a
- * 'winfixwidth' window. Take them from a window to the left or right
- * instead, if possible. */
- if (oldwin->w_p_wfw)
- win_setwidth_win(oldwin->w_width + new_size, oldwin);
+ // We don't like to take lines for the new window from a
+ // 'winfixwidth' window. Take them from a window to the left or right
+ // instead, if possible. Add one for the separator.
+ if (oldwin->w_p_wfw) {
+ win_setwidth_win(oldwin->w_width + new_size + 1, oldwin);
+ }
/* Only make all windows the same width if one of them (except oldwin)
* is wider than one of the split windows. */
@@ -1182,7 +1183,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 +1272,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 +1344,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;
}
@@ -1724,11 +1725,10 @@ void close_windows(buf_T *buf, int keep_curwin)
{
tabpage_T *tp, *nexttp;
int h = tabline_height();
- int count = tabpage_index(NULL);
++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) {
@@ -1762,10 +1762,6 @@ void close_windows(buf_T *buf, int keep_curwin)
--RedrawingDisabled;
- if (count != tabpage_index(NULL)) {
- apply_autocmds(EVENT_TABCLOSED, NULL, NULL, false, curbuf);
- }
-
redraw_tabline = true;
if (h != tabline_height()) {
shell_new_rows();
@@ -1810,7 +1806,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;
@@ -1846,15 +1842,8 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf,
shell_new_rows();
}
- if (term) {
- // When a window containing a terminal buffer is closed, recalculate its
- // size
- terminal_resize(term, 0, 0);
- }
-
// Since goto_tabpage_tp above did not trigger *Enter autocommands, do
// that now.
- apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, curbuf);
apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
if (old_curbuf != curbuf) {
@@ -1878,6 +1867,7 @@ int win_close(win_T *win, int free_buf)
int dir;
int help_window = FALSE;
tabpage_T *prev_curtab = curtab;
+ frame_T *win_frame = win->w_frame->fr_parent;
if (last_window()) {
EMSG(_("E444: Cannot close last window"));
@@ -1996,6 +1986,14 @@ int win_close(win_T *win, int free_buf)
* the screen space. */
wp = win_free_mem(win, &dir, NULL);
+ if (help_window) {
+ // Closing the help window moves the cursor back to the original window.
+ win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX);
+ if (tmpwp != NULL) {
+ wp = tmpwp;
+ }
+ }
+
/* Make sure curwin isn't invalid. It can cause severe trouble when
* printing an error message. For win_equal() curbuf needs to be valid
* too. */
@@ -2027,7 +2025,9 @@ int win_close(win_T *win, int free_buf)
check_cursor();
}
if (p_ea && (*p_ead == 'b' || *p_ead == dir)) {
- win_equal(curwin, true, dir);
+ // If the frame of the closed window contains the new current window,
+ // only resize that frame. Otherwise resize all windows.
+ win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
} else {
win_comp_pos();
}
@@ -2103,19 +2103,29 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
/* When closing the last window in a tab page remove the tab page. */
if (tp->tp_firstwin == tp->tp_lastwin) {
- if (tp == first_tabpage)
+ char_u prev_idx[NUMBUFLEN];
+ if (has_event(EVENT_TABCLOSED)) {
+ vim_snprintf((char *)prev_idx, NUMBUFLEN, "%i", tabpage_index(tp));
+ }
+
+ if (tp == first_tabpage) {
first_tabpage = tp->tp_next;
- else {
+ } else {
for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp;
- ptp = ptp->tp_next)
- ;
+ ptp = ptp->tp_next) {
+ // loop
+ }
if (ptp == NULL) {
- EMSG2(_(e_intern2), "win_close_othertab()");
+ internal_error("win_close_othertab()");
return;
}
ptp->tp_next = tp->tp_next;
}
- free_tp = TRUE;
+ free_tp = true;
+
+ if (has_event(EVENT_TABCLOSED)) {
+ apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, win->w_buffer);
+ }
}
/* Free the memory used for the window. */
@@ -2194,7 +2204,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 +2341,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;
@@ -2848,10 +2858,10 @@ close_others (
if (bufIsChanged(wp->w_buffer))
continue;
}
- win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
+ win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
}
- if (message && lastwin != firstwin)
+ if (message && !ONE_WINDOW)
EMSG(_("E445: Other window contains changes"));
}
@@ -3742,10 +3752,6 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid,
/* Change directories when the 'acd' option is set. */
do_autochdir();
-
- if (curbuf->terminal) {
- terminal_resize(curbuf->terminal, curwin->w_width, curwin->w_height);
- }
}
@@ -4925,9 +4931,7 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
}
-/*
- * Set the width of a window.
- */
+/// Set the width of a window.
void win_new_width(win_T *wp, int width)
{
wp->w_width = width;
@@ -4943,7 +4947,9 @@ void win_new_width(win_T *wp, int width)
if (wp->w_buffer->terminal) {
if (wp->w_height != 0) {
- terminal_resize(wp->w_buffer->terminal, wp->w_width, 0);
+ terminal_resize(wp->w_buffer->terminal,
+ (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))),
+ 0);
}
}
}
@@ -5173,7 +5179,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)
@@ -5428,6 +5434,27 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
return wp;
}
+/// Gets the focused window (the one holding the cursor) of the snapshot.
+static win_T *get_snapshot_focus(int idx)
+{
+ if (curtab->tp_snapshot[idx] == NULL) {
+ return NULL;
+ }
+
+ frame_T *sn = curtab->tp_snapshot[idx];
+ // This should be equivalent to the recursive algorithm found in
+ // restore_snapshot as far as traveling nodes go.
+ while (sn->fr_child != NULL || sn->fr_next != NULL) {
+ while (sn->fr_child != NULL) {
+ sn = sn->fr_child;
+ }
+ if (sn->fr_next != NULL) {
+ sn = sn->fr_next;
+ }
+ }
+
+ return sn->fr_win;
+}
/*
* Set "win" to be the curwin and "tp" to be the current tab page.
@@ -5512,11 +5539,14 @@ void restore_buffer(bufref_T *save_curbuf)
}
-// Add match to the match list of window 'wp'. The pattern 'pat' will be
-// highlighted with the group 'grp' with priority 'prio'.
-// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
-// If no particular ID is desired, -1 must be specified for 'id'.
-// Return ID of added match, -1 on failure.
+/// Add match to the match list of window 'wp'. The pattern 'pat' will be
+/// highlighted with the group 'grp' with priority 'prio'.
+/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
+///
+/// @param[in] id a desired ID 'id' can be specified
+/// (greater than or equal to 1). -1 must be specified if no
+/// particular ID is desired
+/// @return ID of added match, -1 on failure.
int match_add(win_T *wp, const char *const grp, const char *const pat,
int prio, int id, list_T *pos_list,
const char *const conceal_char)
@@ -5532,8 +5562,8 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
return -1;
}
if (id < -1 || id == 0) {
- EMSGN("E799: Invalid ID: %" PRId64
- " (must be greater than or equal to 1)",
+ EMSGN(_("E799: Invalid ID: %" PRId64
+ " (must be greater than or equal to 1)"),
id);
return -1;
}
@@ -5541,7 +5571,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
cur = wp->w_match_head;
while (cur != NULL) {
if (cur->id == id) {
- EMSGN("E801: ID already taken: %" PRId64, id);
+ EMSGN(_("E801: ID already taken: %" PRId64), id);
return -1;
}
cur = cur->next;
@@ -5581,49 +5611,48 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
}
// Set up position matches
- if (pos_list != NULL)
- {
- linenr_T toplnum = 0;
- linenr_T botlnum = 0;
- listitem_T *li;
- int i;
-
- for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
- i++, li = li->li_next) {
- linenr_T lnum = 0;
- colnr_T col = 0;
- int len = 1;
- list_T *subl;
- listitem_T *subli;
+ if (pos_list != NULL) {
+ linenr_T toplnum = 0;
+ linenr_T botlnum = 0;
+
+ int i = 0;
+ TV_LIST_ITER(pos_list, li, {
+ linenr_T lnum = 0;
+ colnr_T col = 0;
+ int len = 1;
bool error = false;
- if (li->li_tv.v_type == VAR_LIST) {
- subl = li->li_tv.vval.v_list;
- if (subl == NULL) {
- goto fail;
- }
- subli = subl->lv_first;
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
+ const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list;
+ const listitem_T *subli = tv_list_first(subl);
if (subli == NULL) {
+ emsgf(_("E5030: Empty list at position %d"),
+ (int)tv_list_idx_of_item(pos_list, li));
goto fail;
}
- lnum = tv_get_number_chk(&subli->li_tv, &error);
+ lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) {
goto fail;
}
- if (lnum == 0) {
- --i;
+ if (lnum <= 0) {
continue;
}
m->pos.pos[i].lnum = lnum;
- subli = subli->li_next;
+ subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) {
- col = tv_get_number_chk(&subli->li_tv, &error);
+ col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) {
goto fail;
}
- subli = subli->li_next;
+ if (col < 0) {
+ continue;
+ }
+ subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) {
- len = tv_get_number_chk(&subli->li_tv, &error);
+ len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
+ if (len < 0) {
+ continue;
+ }
if (error) {
goto fail;
}
@@ -5631,16 +5660,16 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
}
m->pos.pos[i].col = col;
m->pos.pos[i].len = len;
- } else if (li->li_tv.v_type == VAR_NUMBER) {
- if (li->li_tv.vval.v_number == 0) {
- --i;
+ } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) {
+ if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) {
continue;
}
- m->pos.pos[i].lnum = li->li_tv.vval.v_number;
+ m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number;
m->pos.pos[i].col = 0;
m->pos.pos[i].len = 0;
} else {
- EMSG(_("List or number required"));
+ emsgf(_("E5031: List or number required at position %d"),
+ (int)tv_list_idx_of_item(pos_list, li));
goto fail;
}
if (toplnum == 0 || lnum < toplnum) {
@@ -5649,7 +5678,11 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
if (botlnum == 0 || lnum >= botlnum) {
botlnum = lnum + 1;
}
- }
+ i++;
+ if (i >= MAXPOSMATCH) {
+ break;
+ }
+ });
// Calculate top and bottom lines for redrawing area
if (toplnum != 0){
@@ -5694,10 +5727,9 @@ fail:
return -1;
}
-/*
- * Delete match with ID 'id' in the match list of window 'wp'.
- * Print error messages if 'perr' is TRUE.
- */
+
+/// Delete match with ID 'id' in the match list of window 'wp'.
+/// Print error messages if 'perr' is TRUE.
int match_delete(win_T *wp, int id, int perr)
{
matchitem_T *cur = wp->w_match_head;
@@ -5705,10 +5737,11 @@ int match_delete(win_T *wp, int id, int perr)
int rtype = SOME_VALID;
if (id < 1) {
- if (perr == TRUE)
- EMSGN("E802: Invalid ID: %" PRId64
- " (must be greater than or equal to 1)",
+ if (perr) {
+ EMSGN(_("E802: Invalid ID: %" PRId64
+ " (must be greater than or equal to 1)"),
id);
+ }
return -1;
}
while (cur != NULL && cur->id != id) {
@@ -5716,8 +5749,9 @@ int match_delete(win_T *wp, int id, int perr)
cur = cur->next;
}
if (cur == NULL) {
- if (perr == TRUE)
- EMSGN("E803: ID not found: %" PRId64, id);
+ if (perr) {
+ EMSGN(_("E803: ID not found: %" PRId64), id);
+ }
return -1;
}
if (cur == prev)
@@ -5888,13 +5922,15 @@ void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
}
}
-void win_id2tabwin(typval_T *argvars, list_T *list)
+void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
{
int winnr = 1;
int tabnr = 1;
handle_T id = (handle_T)tv_get_number(&argvars[0]);
win_get_tabwin(id, &tabnr, &winnr);
+
+ list_T *const list = tv_list_alloc_ret(rettv, 2);
tv_list_append_number(list, tabnr);
tv_list_append_number(list, winnr);
}