aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/health.txt11
-rw-r--r--runtime/doc/lsp.txt2
-rw-r--r--runtime/doc/news.txt4
-rw-r--r--runtime/ftplugin/shaderslang.vim54
-rw-r--r--runtime/ftplugin/tiasm.vim18
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua6
-rw-r--r--runtime/lua/vim/filetype.lua3
-rw-r--r--runtime/lua/vim/filetype/detect.lua15
-rw-r--r--runtime/lua/vim/health.lua41
-rw-r--r--runtime/lua/vim/lsp/util.lua8
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua34
-rw-r--r--runtime/syntax/shaderslang.vim360
-rw-r--r--runtime/syntax/tiasm.vim102
-rw-r--r--runtime/syntax/vim.vim17
-rw-r--r--runtime/syntax/xf86conf.vim8
-rw-r--r--src/nvim/api/command.c3
-rw-r--r--src/nvim/api/keysets_defs.h7
-rw-r--r--src/nvim/buffer.c40
-rw-r--r--src/nvim/decoration.c18
-rw-r--r--src/nvim/decoration.h1
-rw-r--r--src/nvim/decoration_provider.c6
-rw-r--r--src/nvim/digraph.c17
-rw-r--r--src/nvim/drawline.c6
-rw-r--r--src/nvim/drawscreen.c4
-rw-r--r--src/nvim/extmark.c7
-rw-r--r--src/nvim/generators/gen_vimvim.lua8
-rw-r--r--src/nvim/highlight_group.c2
-rw-r--r--src/nvim/insexpand.c323
-rw-r--r--src/nvim/statusline.c103
-rw-r--r--src/nvim/vterm/screen.c2
-rw-r--r--test/functional/api/command_spec.lua9
-rw-r--r--test/functional/plugin/health_spec.lua12
-rw-r--r--test/functional/terminal/buffer_spec.lua13
-rw-r--r--test/functional/treesitter/fold_spec.lua75
-rw-r--r--test/functional/treesitter/inspect_tree_spec.lua29
-rw-r--r--test/functional/ui/decorations_spec.lua24
-rw-r--r--test/old/testdir/test_filetype.vim18
37 files changed, 1116 insertions, 294 deletions
diff --git a/runtime/doc/health.txt b/runtime/doc/health.txt
index cb70961b55..bca145bd8e 100644
--- a/runtime/doc/health.txt
+++ b/runtime/doc/health.txt
@@ -21,6 +21,17 @@ To run all healthchecks, use: >vim
<
Plugin authors are encouraged to write new healthchecks. |health-dev|
+ *g:health*
+g:health This global variable controls the behavior and appearance of the
+ `health` floating window. It should be a dictionary containing the
+ following optional keys:
+ - `style`: string? Determines the display style of the `health` window.
+ Set to `'float'` to enable a floating window. Other
+ styles are not currently supported.
+
+ Example: >lua
+ vim.g.health = { style = 'float' }
+
Commands *health-commands*
*:che* *:checkhealth*
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 71e8a12ca3..16e6abe294 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -2024,7 +2024,7 @@ Lua module: vim.lsp.util *lsp-util*
• {zindex}? (`integer`) override `zindex`, defaults to 50
• {title}? (`string`)
• {title_pos}? (`'left'|'center'|'right'`)
- • {relative}? (`'mouse'|'cursor'`) (default: `'cursor'`)
+ • {relative}? (`'mouse'|'cursor'|'editor'`) (default: `'cursor'`)
• {anchor_bias}? (`'auto'|'above'|'below'`, default: `'auto'`) -
"auto": place window based on which side of the
cursor has more lines
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index cb7916d0e9..960b6f05f9 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -289,6 +289,8 @@ PERFORMANCE
highlighting.
• LSP diagnostics and inlay hints are de-duplicated (new requests cancel
inflight requests). This greatly improves performance with slow LSP servers.
+• 10x speedup for |vim.treesitter.foldexpr()| (when no parser exists for the
+ buffer).
PLUGINS
@@ -359,6 +361,8 @@ UI
• |vim.diagnostic.setqflist()| updates an existing quickfix list with the
given title if found
• |ui-messages| content chunks now also contain the highlight group ID.
+• |:checkhealth| can be display in a floating window and controlled by
+ the |g:health| variable.
==============================================================================
CHANGED FEATURES *news-changed*
diff --git a/runtime/ftplugin/shaderslang.vim b/runtime/ftplugin/shaderslang.vim
new file mode 100644
index 0000000000..f3d1ab8c1c
--- /dev/null
+++ b/runtime/ftplugin/shaderslang.vim
@@ -0,0 +1,54 @@
+" Vim filetype plugin file
+" Language: Slang
+" Maintainer: Austin Shijo <epestr@proton.me>
+" Last Change: 2025 Jan 05
+
+" Only do this when not done yet for this buffer
+if exists("b:did_ftplugin")
+ finish
+endif
+
+" Don't load another plugin for this buffer
+let b:did_ftplugin = 1
+
+" Using line continuation here.
+let s:cpo_save = &cpo
+set cpo-=C
+
+let b:undo_ftplugin = "setl fo< com< cms< inc<"
+
+" Set 'formatoptions' to break comment lines but not other lines,
+" and insert the comment leader when hitting <CR> or using "o".
+setlocal fo-=t fo+=croql
+
+" Set comment string (Slang uses C-style comments)
+setlocal commentstring=//\ %s
+
+" Set 'comments' to format dashed lists in comments
+setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
+
+" When the matchit plugin is loaded, this makes the % command skip parens and
+" braces in comments properly, and adds support for shader-specific keywords
+if exists("loaded_matchit")
+ " Add common shader control structures
+ let b:match_words = '{\|^\s*\<\(if\|for\|while\|switch\|struct\|class\)\>:}\|^\s*\<break\>,' ..
+ \ '^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>,' ..
+ \ '\[:\]'
+ let b:match_skip = 's:comment\|string\|character\|special'
+ let b:match_ignorecase = 0
+ let b:undo_ftplugin ..= " | unlet! b:match_skip b:match_words b:match_ignorecase"
+endif
+
+" Win32 and GTK can filter files in the browse dialog
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Slang Source Files (*.slang)\t*.slang\n"
+ if has("win32")
+ let b:browsefilter ..= "All Files (*.*)\t*\n"
+ else
+ let b:browsefilter ..= "All Files (*)\t*\n"
+ endif
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/ftplugin/tiasm.vim b/runtime/ftplugin/tiasm.vim
new file mode 100644
index 0000000000..13a3dc64f7
--- /dev/null
+++ b/runtime/ftplugin/tiasm.vim
@@ -0,0 +1,18 @@
+" Vim filetype plugin file
+" Language: TI linear assembly language
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2025 Jan 08
+
+if exists("b:did_ftplugin") | finish | endif
+let b:did_ftplugin = 1
+
+setlocal comments=:;
+setlocal commentstring=;\ %s
+
+let b:undo_ftplugin = "setl commentstring< comments<"
+
+if exists("loaded_matchit")
+ let b:match_words = '^\s\+\.if\>:^\s\+\.elseif:^\s\+\.else\>:^\s\+\.endif\>,^\s\+\.group:^\s\+\.gmember:^\s\+\.endgroup,^\s\+\.loop:^\s\+\.break:^\s\+\.endloop,^\s\+\.macro:^\s\+\.mexit:^\s\+\.endm,^\s\+\.asmfunc:^\s\+\.endasmfunc,^\s\+\.c\?struct:^\s\+\.endstruct,^\s\+\.c\?union:^\s\+\.endunion,^\s\+\.c\?proc:^\s\+\.return:^\s\+\.endproc'
+ let b:match_ignorecase = 1
+ let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
+endif
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index 1baae2fd71..98e916115e 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -228,10 +228,10 @@ error('Cannot require a meta file')
--- @field do_source? boolean
--- @class vim.api.keyset.set_decoration_provider
---- @field on_start? fun(_: "start", tick: integer)
+--- @field on_start? fun(_: "start", tick: integer): boolean?
--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer)
---- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer)
---- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer)
+--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer): boolean?
+--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer): boolean?
--- @field on_end? fun(_: "end", tick: integer)
--- @field _on_hl_def? fun(_: "hl_def")
--- @field _on_spell_nav? fun(_: "spell_nav")
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index b6d6906589..dee1bd88ca 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1064,11 +1064,11 @@ local extension = {
builder = 'ruby',
rake = 'ruby',
rs = 'rust',
+ sa = detect.sa,
sage = 'sage',
sls = 'salt',
sas = 'sas',
sass = 'sass',
- sa = 'sather',
sbt = 'sbt',
scala = 'scala',
ss = 'scheme',
@@ -1097,6 +1097,7 @@ local extension = {
la = 'sh',
lai = 'sh',
mdd = 'sh',
+ slang = 'shaderslang',
sieve = 'sieve',
siv = 'sieve',
sig = detect.sig,
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index a1c17bc1af..2d989fdbac 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -34,6 +34,12 @@ local matchregex = vim.filetype._matchregex
-- can be detected from the first five lines of the file.
--- @type vim.filetype.mapfn
function M.asm(path, bufnr)
+ -- tiasm uses `* commment`
+ local lines = table.concat(getlines(bufnr, 1, 10), '\n')
+ if findany(lines, { '^%*', '\n%*', 'Texas Instruments Incorporated' }) then
+ return 'tiasm'
+ end
+
local syntax = vim.b[bufnr].asmsyntax
if not syntax or syntax == '' then
syntax = M.asm_syntax(path, bufnr)
@@ -1424,6 +1430,15 @@ function M.sig(_, bufnr)
end
end
+--- @type vim.filetype.mapfn
+function M.sa(_, bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 4), '\n')
+ if findany(lines, { '^;', '\n;' }) then
+ return 'tiasm'
+ end
+ return 'sather'
+end
+
-- This function checks the first 25 lines of file extension "sc" to resolve
-- detection between scala and SuperCollider
--- @type vim.filetype.mapfn
diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua
index 01166628b1..6dc902489f 100644
--- a/runtime/lua/vim/health.lua
+++ b/runtime/lua/vim/health.lua
@@ -11,6 +11,17 @@
--- <
--- Plugin authors are encouraged to write new healthchecks. |health-dev|
---
+--- *g:health*
+--- g:health This global variable controls the behavior and appearance of the
+--- `health` floating window. It should be a dictionary containing the
+--- following optional keys:
+--- - `style`: string? Determines the display style of the `health` window.
+--- Set to `'float'` to enable a floating window. Other
+--- styles are not currently supported.
+---
+--- Example: >lua
+--- vim.g.health = { style = 'float' }
+---
--- Commands *health-commands*
---
--- *:che* *:checkhealth*
@@ -331,13 +342,31 @@ function M._check(mods, plugin_names)
local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$')
- -- When no command modifiers are used:
- -- - If the current buffer is empty, open healthcheck directly.
- -- - If not specified otherwise open healthcheck in a tab.
- local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer'
-
local bufnr = vim.api.nvim_create_buf(true, true)
- vim.cmd(buf_cmd .. ' ' .. bufnr)
+ if
+ vim.g.health
+ and type(vim.g.health) == 'table'
+ and vim.tbl_get(vim.g.health, 'style') == 'float'
+ then
+ local max_height = math.floor(vim.o.lines * 0.8)
+ local max_width = 80
+ local float_bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', {
+ height = max_height,
+ width = max_width,
+ offset_x = math.floor((vim.o.columns - max_width) / 2),
+ offset_y = math.floor((vim.o.lines - max_height) / 2) - 1,
+ relative = 'editor',
+ })
+ vim.api.nvim_set_current_win(float_winid)
+ vim.bo[float_bufnr].modifiable = true
+ vim.wo[float_winid].list = false
+ else
+ -- When no command modifiers are used:
+ -- - If the current buffer is empty, open healthcheck directly.
+ -- - If not specified otherwise open healthcheck in a tab.
+ local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer'
+ vim.cmd(buf_cmd .. ' ' .. bufnr)
+ end
if vim.fn.bufexists('health://') == 1 then
vim.cmd.bwipe('health://')
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 89b774816b..5cccb3321f 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -875,11 +875,13 @@ function M.make_floating_popup_options(width, height, opts)
return {
anchor = anchor,
+ row = row + (opts.offset_y or 0),
col = col + (opts.offset_x or 0),
height = height,
focusable = opts.focusable,
- relative = opts.relative == 'mouse' and 'mouse' or 'cursor',
- row = row + (opts.offset_y or 0),
+ relative = opts.relative == 'mouse' and 'mouse'
+ or opts.relative == 'editor' and 'editor'
+ or 'cursor',
style = 'minimal',
width = width,
border = opts.border or default_border,
@@ -1494,7 +1496,7 @@ end
--- @field title_pos? 'left'|'center'|'right'
---
--- (default: `'cursor'`)
---- @field relative? 'mouse'|'cursor'
+--- @field relative? 'mouse'|'cursor'|'editor'
---
--- - "auto": place window based on which side of the cursor has more lines
--- - "above": place the window above the cursor unless there are not enough lines
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 207ac1ab67..d16013eca2 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -19,14 +19,19 @@ local api = vim.api
---The range on which to evaluate foldexpr.
---When in insert mode, the evaluation is deferred to InsertLeave.
---@field foldupdate_range? Range2
+---
+---The treesitter parser associated with this buffer.
+---@field parser? vim.treesitter.LanguageTree
local FoldInfo = {}
FoldInfo.__index = FoldInfo
---@private
-function FoldInfo.new()
+---@param bufnr integer
+function FoldInfo.new(bufnr)
return setmetatable({
levels0 = {},
levels = {},
+ parser = ts.get_parser(bufnr, nil, { error = false }),
}, FoldInfo)
end
@@ -69,7 +74,10 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections)
srow = srow or 0
erow = erow or api.nvim_buf_line_count(bufnr)
- local parser = assert(ts.get_parser(bufnr, nil, { error = false }))
+ local parser = info.parser
+ if not parser then
+ return
+ end
parser:parse(parse_injections and { srow, erow } or nil)
@@ -347,13 +355,21 @@ function M.foldexpr(lnum)
lnum = lnum or vim.v.lnum
local bufnr = api.nvim_get_current_buf()
- local parser = ts.get_parser(bufnr, nil, { error = false })
- if not parser then
- return '0'
- end
-
if not foldinfos[bufnr] then
- foldinfos[bufnr] = FoldInfo.new()
+ foldinfos[bufnr] = FoldInfo.new(bufnr)
+ api.nvim_create_autocmd('BufUnload', {
+ buffer = bufnr,
+ once = true,
+ callback = function()
+ foldinfos[bufnr] = nil
+ end,
+ })
+
+ local parser = foldinfos[bufnr].parser
+ if not parser then
+ return '0'
+ end
+
compute_folds_levels(bufnr, foldinfos[bufnr])
parser:register_cbs({
@@ -383,7 +399,7 @@ api.nvim_create_autocmd('OptionSet', {
or foldinfos[buf] and { buf }
or {}
for _, bufnr in ipairs(bufs) do
- foldinfos[bufnr] = FoldInfo.new()
+ foldinfos[bufnr] = FoldInfo.new(bufnr)
api.nvim_buf_call(bufnr, function()
compute_folds_levels(bufnr, foldinfos[bufnr])
end)
diff --git a/runtime/syntax/shaderslang.vim b/runtime/syntax/shaderslang.vim
new file mode 100644
index 0000000000..1cae202b04
--- /dev/null
+++ b/runtime/syntax/shaderslang.vim
@@ -0,0 +1,360 @@
+" Vim syntax file
+" Language: Slang
+" Maintainer: Austin Shijo <epestr@proton.me>
+" Last Change: 2024 Jan 05
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" Read the C syntax to start with
+runtime! syntax/c.vim
+unlet b:current_syntax
+
+" Annotations
+syn match shaderslangAnnotation /<.*;>/
+
+" Attributes
+syn match shaderslangAttribute /^\s*\[maxvertexcount(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[domain(\s*"\(tri\|quad\|isoline\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[earlydepthstencil\]/
+syn match shaderslangAttribute /^\s*\[instance(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[maxtessfactor(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[numthreads(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[outputcontrolpoints(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[outputtopology(\s*"\(point\|line\|triangle_cw\|triangle_ccw\|triangle\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[partitioning(\s*"\(integer\|fractional_even\|fractional_odd\|pow2\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[patchconstantfunc(\s*"\(\d\|\w\|_\)\+"\s*)\]/
+syn match shaderslangAttribute /^\s*\[WaveSize(\s*\w\+\(\s*,\s*\w\+\(\s*,\s*\w\+\)\?\)\?\s*)\]/
+syn match shaderslangAttribute /^\s*\[shader(\s*"\(anyhit\|callable\|closesthit\|intersection\|miss\|raygeneration\)"\s*)\]/
+
+syn match shaderslangAttribute /^\s*\[fastopt\]/
+syn match shaderslangAttribute /^\s*\[loop\]/
+syn match shaderslangAttribute /^\s*\[unroll\]/
+syn match shaderslangAttribute /^\s*\[allow_uav_condition\]/
+syn match shaderslangAttribute /^\s*\[branch\]/
+syn match shaderslangAttribute /^\s*\[flatten\]/
+syn match shaderslangAttribute /^\s*\[forcecase\]/
+syn match shaderslangAttribute /^\s*\[call\]/
+syn match shaderslangAttribute /^\s*\[WaveOpsIncludeHelperLanes\]/
+
+syn match shaderslangAttribute /\[raypayload\]/
+
+" Work graph shader target attributes
+syn match shaderslangAttribute /^\s*\[Shader(\s*"\(\d\|\w\|_\)\+"\s*)\]/
+
+" Work graph shader function attributes
+syn match shaderslangAttribute /^\s*\[NodeLaunch(\s*"\(broadcasting\|coalescing\|thread\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeIsProgramEntry\]/
+syn match shaderslangAttribute /^\s*\[NodeLocalRootArgumentsTableIndex(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NumThreads(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeShareInputOf(\s*"\w\+"\(\s*,\s*\w\+\)\?\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeDispatchGrid(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeMaxDispatchGrid(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeMaxRecursionDepth(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeMaxInputRecordsPerGraphEntryRecord(\s*\w\+\s*,\s*\(true\|false\)\s*)\]/
+
+" Work graph record attributes
+syn match shaderslangAttribute /\[NodeTrackRWInputSharing\]/
+syn match shaderslangAttribute /\[MaxRecords(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /\[NodeID(\s*"\w\+"\(\s*,\s*\w\+\)\?\s*)\]/
+syn match shaderslangAttribute /\[MaxRecordsSharedWith(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /\[AllowSparseNodes\]/
+syn match shaderslangAttribute /\[NodeArraySize(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /\[UnboundedSparseNodes\]/
+
+" Intrinsic functions
+syn keyword shaderslangFunc abs acos acosh asin asinh atan atanh cos cosh exp exp2 floor log log10 log2 round rsqrt sin sincos sinh sqrt tan tanh trunc
+syn keyword shaderslangFunc AllMemoryBarrier AllMemoryBarrierWithGroupSync DeviceMemoryBarrier DeviceMemoryBarrierWithGroupSync GroupMemoryBarrier GroupMemoryBarrierWithGroupSync
+syn keyword shaderslangFunc abort clip errorf printf
+syn keyword shaderslangFunc all any countbits faceforward firstbithigh firstbitlow isfinite isinf isnan max min noise pow reversebits sign
+syn keyword shaderslangFunc asdouble asfloat asint asuint D3DCOLORtoUBYTE4 f16tof32 f32tof16
+syn keyword shaderslangFunc ceil clamp degrees fma fmod frac frexp ldexp lerp mad modf radiants saturate smoothstep step
+syn keyword shaderslangFunc cross determinant distance dot dst length lit msad4 mul normalize rcp reflect refract transpose
+syn keyword shaderslangFunc ddx ddx_coarse ddx_fine ddy ddy_coarse ddy_fine fwidth
+syn keyword shaderslangFunc EvaluateAttributeAtCentroid EvaluateAttributeAtSample EvaluateAttributeSnapped
+syn keyword shaderslangFunc GetRenderTargetSampleCount GetRenderTargetSamplePosition
+syn keyword shaderslangFunc InterlockedAdd InterlockedAnd InterlockedCompareExchange InterlockedCompareStore InterlockedExchange InterlockedMax InterlockedMin InterlockedOr InterlockedXor
+syn keyword shaderslangFunc InterlockedCompareStoreFloatBitwise InterlockedCompareExchangeFloatBitwise
+syn keyword shaderslangFunc Process2DQuadTessFactorsAvg Process2DQuadTessFactorsMax Process2DQuadTessFactorsMin ProcessIsolineTessFactors
+syn keyword shaderslangFunc ProcessQuadTessFactorsAvg ProcessQuadTessFactorsMax ProcessQuadTessFactorsMin ProcessTriTessFactorsAvg ProcessTriTessFactorsMax ProcessTriTessFactorsMin
+syn keyword shaderslangFunc tex1D tex1Dbias tex1Dgrad tex1Dlod tex1Dproj
+syn keyword shaderslangFunc tex2D tex2Dbias tex2Dgrad tex2Dlod tex2Dproj
+syn keyword shaderslangFunc tex3D tex3Dbias tex3Dgrad tex3Dlod tex3Dproj
+syn keyword shaderslangFunc texCUBE texCUBEbias texCUBEgrad texCUBElod texCUBEproj
+syn keyword shaderslangFunc WaveIsFirstLane WaveGetLaneCount WaveGetLaneIndex
+syn keyword shaderslangFunc IsHelperLane
+syn keyword shaderslangFunc WaveActiveAnyTrue WaveActiveAllTrue WaveActiveBallot
+syn keyword shaderslangFunc WaveReadLaneFirst WaveReadLaneAt
+syn keyword shaderslangFunc WaveActiveAllEqual WaveActiveAllEqualBool WaveActiveCountBits
+syn keyword shaderslangFunc WaveActiveSum WaveActiveProduct WaveActiveBitAnd WaveActiveBitOr WaveActiveBitXor WaveActiveMin WaveActiveMax
+syn keyword shaderslangFunc WavePrefixCountBits WavePrefixProduct WavePrefixSum
+syn keyword shaderslangFunc QuadReadAcrossX QuadReadAcrossY QuadReadAcrossDiagonal QuadReadLaneAt
+syn keyword shaderslangFunc QuadAny QuadAll
+syn keyword shaderslangFunc WaveMatch WaveMultiPrefixSum WaveMultiPrefixProduct WaveMultiPrefixCountBits WaveMultiPrefixAnd WaveMultiPrefixOr WaveMultiPrefixXor
+syn keyword shaderslangFunc NonUniformResourceIndex
+syn keyword shaderslangFunc DispatchMesh SetMeshOutputCounts
+syn keyword shaderslangFunc dot4add_u8packed dot4add_i8packed dot2add
+
+syn keyword shaderslangFunc RestartStrip
+syn keyword shaderslangFunc CalculateLevelOfDetail CalculateLevelOfDetailUnclamped Gather GetDimensions GetSamplePosition Load Sample SampleBias SampleCmp SampleCmpLevelZero SampleGrad SampleLevel GatherRaw SampleCmpLevel
+syn keyword shaderslangFunc SampleCmpBias SampleCmpGrad
+syn keyword shaderslangFunc WriteSamplerFeedback WriteSamplerFeedbackBias WriteSamplerFeedbackGrad WriteSamplerFeedbackLevel
+syn keyword shaderslangFunc Append Consume DecrementCounter IncrementCounter
+syn keyword shaderslangFunc Load2 Load3 Load4 Store Store2 Store3 Store4
+syn keyword shaderslangFunc GatherRed GatherGreen GatherBlue GatherAlpha GatherCmp GatherCmpRed GatherCmpGreen GatherCmpBlue GatherCmpAlpha
+syn match shaderslangFunc /\.mips\[\d\+\]\[\d\+\]/
+syn match shaderslangFunc /\.sample\[\d\+\]\[\d\+\]/
+
+" Ray intrinsics
+syn keyword shaderslangFunc AcceptHitAndEndSearch CallShader IgnoreHit ReportHit TraceRay
+syn keyword shaderslangFunc DispatchRaysIndex DispatchRaysDimensions
+syn keyword shaderslangFunc WorldRayOrigin WorldRayDirection RayTMin RayTCurrent RayFlags
+syn keyword shaderslangFunc InstanceIndex InstanceID GeometryIndex PrimitiveIndex ObjectRayOrigin ObjectRayDirection ObjectToWorld3x4 ObjectToWorld4x3 WorldToObject3x4 WorldToObject4x3
+syn keyword shaderslangFunc HitKind
+
+" RayQuery intrinsics
+syn keyword shaderslangFunc TraceRayInline Proceed Abort CommittedStatus
+syn keyword shaderslangFunc CandidateType CandidateProceduralPrimitiveNonOpaque CandidateTriangleRayT CandidateInstanceIndex CandidateInstanceID CandidateInstanceContributionToHitGroupIndex CandidateGeometryIndex
+syn keyword shaderslangFunc CandidatePrimitiveIndex CandidateObjectRayOrigin CandidateObjectRayDirection CandidateObjectToWorld3x4 CandidateObjectToWorld4x3 CandidateWorldToObject3x4 CandidateWorldToObject4x3
+syn keyword shaderslangFunc CommitNonOpaqueTriangleHit CommitProceduralPrimitiveHit CommittedStatus CommittedRayT CommittedInstanceIndex CommittedInstanceID CommittedInstanceContributionToHitGroupIndex
+syn keyword shaderslangFunc CommittedGeometryIndex CommittedPrimitiveIndex CommittedObjectRayOrigin CommittedObjectRayDirection CommittedObjectToWorld3x4 CommittedObjectToWorld4x3 CommittedWorldToObject3x4
+syn keyword shaderslangFunc CommittedWorldToObject4x3 CandidateTriangleBarycentrics CandidateTriangleFrontFace CommittedTriangleBarycentrics CommittedTriangleFrontFace
+
+" Pack/unpack math intrinsics
+syn keyword shaderslangFunc unpack_s8s16 unpack_u8u16 unpack_s8s32 unpack_u8u32
+syn keyword shaderslangFunc pack_u8 pack_s8 pack_clamp_u8 pack_clamp_s8
+
+" Work graph object methods
+syn keyword shaderslangFunc Get FinishedCrossGroupSharing Count GetThreadNodeOutputRecords GetGroupNodeOutputRecords IsValid GroupIncrementOutputCount ThreadIncrementOutputCount OutputComplete
+
+" Work graph free intrinsics
+syn keyword shaderslangFunc GetRemainingRecursionLevels Barrier
+
+" Layout Qualifiers
+syn keyword shaderslangLayoutQual const row_major column_major
+syn keyword shaderslangLayoutQual point line triangle lineadj triangleadj
+syn keyword shaderslangLayoutQual InputPatch OutputPatch
+syn match shaderslangLayoutQual /PointStream<\s*\w\+\s*>/
+syn match shaderslangLayoutQual /LineStream<\s*\w\+\s*>/
+syn match shaderslangLayoutQual /TriangleStream<\s*\w\+\s*>/
+
+" User defined Semantics
+syn match shaderslangSemantic /:\s*[A-Z]\w*/
+syn match shaderslangSemantic /:\s*packoffset(\s*c\d\+\(\.[xyzw]\)\?\s*)/ " packoffset
+syn match shaderslangSemantic /:\s*register(\s*\(r\|x\|v\|t\|s\|cb\|icb\|b\|c\|u\)\d\+\s*)/ " register
+syn match shaderslangSemantic /:\s*read(\s*\(\(anyhit\|closesthit\|miss\|caller\)\s*,\s*\)*\(anyhit\|closesthit\|miss\|caller\)\?\s*)/ " read
+syn match shaderslangSemantic /:\s*write(\s*\(\(anyhit\|closesthit\|miss\|caller\)\s*,\s*\)*\(anyhit\|closesthit\|miss\|caller\)\?\s*)/ " write
+
+" System-Value Semantics
+" Vertex Shader
+syn match shaderslangSemantic /SV_ClipDistance\d\+/
+syn match shaderslangSemantic /SV_CullDistance\d\+/
+syn keyword shaderslangSemantic SV_Position SV_InstanceID SV_PrimitiveID SV_VertexID
+syn keyword shaderslangSemantic SV_StartVertexLocation SV_StartInstanceLocation
+" Tessellation pipeline
+syn keyword shaderslangSemantic SV_DomainLocation SV_InsideTessFactor SV_OutputControlPointID SV_TessFactor
+" Geometry Shader
+syn keyword shaderslangSemantic SV_GSInstanceID SV_RenderTargetArrayIndex
+" Pixel Shader - MSAA
+syn keyword shaderslangSemantic SV_Coverage SV_Depth SV_IsFrontFace SV_SampleIndex
+syn match shaderslangSemantic /SV_Target[0-7]/
+syn keyword shaderslangSemantic SV_ShadingRate SV_ViewID
+syn match shaderslangSemantic /SV_Barycentrics[0-1]/
+" Compute Shader
+syn keyword shaderslangSemantic SV_DispatchThreadID SV_GroupID SV_GroupIndex SV_GroupThreadID
+" Mesh shading pipeline
+syn keyword shaderslangSemantic SV_CullPrimitive
+" Work graph record system values
+syn keyword shaderslangSemantic SV_DispatchGrid
+
+" slang structures
+syn keyword shaderslangStructure cbuffer
+
+" Shader profiles
+" Cg profiles
+syn keyword shaderslangProfile arbfp1 arbvp1 fp20 vp20 fp30 vp30 ps_1_1 ps_1_2 ps_1_3
+" Shader Model 1
+syn keyword shaderslangProfile vs_1_1
+" Shader Model 2
+syn keyword shaderslangProfile ps_2_0 ps_2_x vs_2_0 vs_2_x
+" Shader Model 3
+syn keyword shaderslangProfile ps_3_0 vs_3_0
+" Shader Model 4
+syn keyword shaderslangProfile gs_4_0 ps_4_0 vs_4_0 gs_4_1 ps_4_1 vs_4_1
+" Shader Model 5
+syn keyword shaderslangProfile cs_4_0 cs_4_1 cs_5_0 ds_5_0 gs_5_0 hs_5_0 ps_5_0 vs_5_0
+" Shader Model 6
+syn keyword shaderslangProfile cs_6_0 ds_6_0 gs_6_0 hs_6_0 ps_6_0 vs_6_0 lib_6_0
+
+" Swizzling
+syn match shaderslangSwizzle /\.[xyzw]\{1,4\}\>/
+syn match shaderslangSwizzle /\.[rgba]\{1,4\}\>/
+syn match shaderslangSwizzle /\.\(_m[0-3]\{2}\)\{1,4\}/
+syn match shaderslangSwizzle /\.\(_[1-4]\{2}\)\{1,4\}/
+
+" Other Statements
+syn keyword shaderslangStatement discard
+
+" Storage class
+syn match shaderslangStorageClass /\<in\(\s+pipeline\)\?\>/
+syn match shaderslangStorageClass /\<out\(\s\+indices\|\s\+vertices\|\s\+primitives\)\?\>/
+syn keyword shaderslangStorageClass inout
+syn keyword shaderslangStorageClass extern nointerpolation precise shared groupshared static uniform volatile
+syn keyword shaderslangStorageClass snorm unorm
+syn keyword shaderslangStorageClass linear centroid nointerpolation noperspective sample
+syn keyword shaderslangStorageClass globallycoherent
+
+" Types
+" Buffer types
+syn keyword shaderslangType ConstantBuffer Buffer ByteAddressBuffer ConsumeStructuredBuffer StructuredBuffer
+syn keyword shaderslangType AppendStructuredBuffer RWBuffer RWByteAddressBuffer RWStructuredBuffer
+syn keyword shaderslangType RasterizerOrderedBuffer RasterizerOrderedByteAddressBuffer RasterizerOrderedStructuredBuffer
+
+" Scalar types
+syn keyword shaderslangType bool int uint dword half float double
+syn keyword shaderslangType min16float min10float min16int min12int min16uint
+syn keyword shaderslangType float16_t float32_t float64_t
+
+" Vector types
+syn match shaderslangType /vector<\s*\w\+,\s*[1-4]\s*>/
+syn keyword shaderslangType bool1 bool2 bool3 bool4
+syn keyword shaderslangType int1 int2 int3 int4
+syn keyword shaderslangType uint1 uint2 uint3 uint4
+syn keyword shaderslangType dword1 dword2 dword3 dword4
+syn keyword shaderslangType half1 half2 half3 half4
+syn keyword shaderslangType float1 float2 float3 float4
+syn keyword shaderslangType double1 double2 double3 double4
+syn keyword shaderslangType min16float1 min16float2 min16float3 min16float4
+syn keyword shaderslangType min10float1 min10float2 min10float3 min10float4
+syn keyword shaderslangType min16int1 min16int2 min16int3 min16int4
+syn keyword shaderslangType min12int1 min12int2 min12int3 min12int4
+syn keyword shaderslangType min16uint1 min16uint2 min16uint3 min16uint4
+syn keyword shaderslangType float16_t1 float16_t2 float16_t3 float16_t4
+syn keyword shaderslangType float32_t1 float32_t2 float32_t3 float32_t4
+syn keyword shaderslangType float64_t1 float64_t2 float64_t3 float64_t4
+syn keyword shaderslangType int16_t1 int16_t2 int16_t3 int16_t4
+syn keyword shaderslangType int32_t1 int32_t2 int32_t3 int32_t4
+syn keyword shaderslangType int64_t1 int64_t2 int64_t3 int64_t4
+syn keyword shaderslangType uint16_t1 uint16_t2 uint16_t3 uint16_t4
+syn keyword shaderslangType uint32_t1 uint32_t2 uint32_t3 uint32_t4
+syn keyword shaderslangType uint64_t1 uint64_t2 uint64_t3 uint64_t4
+
+" Packed types
+syn keyword shaderslangType uint8_t4_packed int8_t4_packed
+
+" Matrix types
+syn match shaderslangType /matrix<\s*\w\+\s*,\s*[1-4]\s*,\s*[1-4]\s*>/
+syn keyword shaderslangType bool1x1 bool2x1 bool3x1 bool4x1 bool1x2 bool2x2 bool3x2 bool4x2 bool1x3 bool2x3 bool3x3 bool4x3 bool1x4 bool2x4 bool3x4 bool4x4
+syn keyword shaderslangType int1x1 int2x1 int3x1 int4x1 int1x2 int2x2 int3x2 int4x2 int1x3 int2x3 int3x3 int4x3 int1x4 int2x4 int3x4 int4x4
+syn keyword shaderslangType uint1x1 uint2x1 uint3x1 uint4x1 uint1x2 uint2x2 uint3x2 uint4x2 uint1x3 uint2x3 uint3x3 uint4x3 uint1x4 uint2x4 uint3x4 uint4x4
+syn keyword shaderslangType dword1x1 dword2x1 dword3x1 dword4x1 dword1x2 dword2x2 dword3x2 dword4x2 dword1x3 dword2x3 dword3x3 dword4x3 dword1x4 dword2x4 dword3x4 dword4x4
+syn keyword shaderslangType half1x1 half2x1 half3x1 half4x1 half1x2 half2x2 half3x2 half4x2 half1x3 half2x3 half3x3 half4x3 half1x4 half2x4 half3x4 half4x4
+syn keyword shaderslangType float1x1 float2x1 float3x1 float4x1 float1x2 float2x2 float3x2 float4x2 float1x3 float2x3 float3x3 float4x3 float1x4 float2x4 float3x4 float4x4
+syn keyword shaderslangType double1x1 double2x1 double3x1 double4x1 double1x2 double2x2 double3x2 double4x2 double1x3 double2x3 double3x3 double4x3 double1x4 double2x4 double3x4 double4x4
+syn keyword shaderslangType min16float1x1 min16float2x1 min16float3x1 min16float4x1 min16float1x2 min16float2x2 min16float3x2 min16float4x2 min16float1x3 min16float2x3 min16float3x3 min16float4x3 min16float1x4 min16float2x4 min16float3x4 min16float4x4
+syn keyword shaderslangType min10float1x1 min10float2x1 min10float3x1 min10float4x1 min10float1x2 min10float2x2 min10float3x2 min10float4x2 min10float1x3 min10float2x3 min10float3x3 min10float4x3 min10float1x4 min10float2x4 min10float3x4 min10float4x4
+syn keyword shaderslangType min16int1x1 min16int2x1 min16int3x1 min16int4x1 min16int1x2 min16int2x2 min16int3x2 min16int4x2 min16int1x3 min16int2x3 min16int3x3 min16int4x3 min16int1x4 min16int2x4 min16int3x4 min16int4x4
+syn keyword shaderslangType min12int1x1 min12int2x1 min12int3x1 min12int4x1 min12int1x2 min12int2x2 min12int3x2 min12int4x2 min12int1x3 min12int2x3 min12int3x3 min12int4x3 min12int1x4 min12int2x4 min12int3x4 min12int4x4
+syn keyword shaderslangType min16uint1x1 min16uint2x1 min16uint3x1 min16uint4x1 min16uint1x2 min16uint2x2 min16uint3x2 min16uint4x2 min16uint1x3 min16uint2x3 min16uint3x3 min16uint4x3 min16uint1x4 min16uint2x4 min16uint3x4 min16uint4x4
+syn keyword shaderslangType float16_t1x1 float16_t2x1 float16_t3x1 float16_t4x1 float16_t1x2 float16_t2x2 float16_t3x2 float16_t4x2 float16_t1x3 float16_t2x3 float16_t3x3 float16_t4x3 float16_t1x4 float16_t2x4 float16_t3x4 float16_t4x4
+syn keyword shaderslangType float32_t1x1 float32_t2x1 float32_t3x1 float32_t4x1 float32_t1x2 float32_t2x2 float32_t3x2 float32_t4x2 float32_t1x3 float32_t2x3 float32_t3x3 float32_t4x3 float32_t1x4 float32_t2x4 float32_t3x4 float32_t4x4
+syn keyword shaderslangType float64_t1x1 float64_t2x1 float64_t3x1 float64_t4x1 float64_t1x2 float64_t2x2 float64_t3x2 float64_t4x2 float64_t1x3 float64_t2x3 float64_t3x3 float64_t4x3 float64_t1x4 float64_t2x4 float64_t3x4 float64_t4x4
+syn keyword shaderslangType int16_t1x1 int16_t2x1 int16_t3x1 int16_t4x1 int16_t1x2 int16_t2x2 int16_t3x2 int16_t4x2 int16_t1x3 int16_t2x3 int16_t3x3 int16_t4x3 int16_t1x4 int16_t2x4 int16_t3x4 int16_t4x4
+syn keyword shaderslangType int32_t1x1 int32_t2x1 int32_t3x1 int32_t4x1 int32_t1x2 int32_t2x2 int32_t3x2 int32_t4x2 int32_t1x3 int32_t2x3 int32_t3x3 int32_t4x3 int32_t1x4 int32_t2x4 int32_t3x4 int32_t4x4
+syn keyword shaderslangType int64_t1x1 int64_t2x1 int64_t3x1 int64_t4x1 int64_t1x2 int64_t2x2 int64_t3x2 int64_t4x2 int64_t1x3 int64_t2x3 int64_t3x3 int64_t4x3 int64_t1x4 int64_t2x4 int64_t3x4 int64_t4x4
+syn keyword shaderslangType uint16_t1x1 uint16_t2x1 uint16_t3x1 uint16_t4x1 uint16_t1x2 uint16_t2x2 uint16_t3x2 uint16_t4x2 uint16_t1x3 uint16_t2x3 uint16_t3x3 uint16_t4x3 uint16_t1x4 uint16_t2x4 uint16_t3x4 uint16_t4x4
+syn keyword shaderslangType uint32_t1x1 uint32_t2x1 uint32_t3x1 uint32_t4x1 uint32_t1x2 uint32_t2x2 uint32_t3x2 uint32_t4x2 uint32_t1x3 uint32_t2x3 uint32_t3x3 uint32_t4x3 uint32_t1x4 uint32_t2x4 uint32_t3x4 uint32_t4x4
+syn keyword shaderslangType uint64_t1x1 uint64_t2x1 uint64_t3x1 uint64_t4x1 uint64_t1x2 uint64_t2x2 uint64_t3x2 uint64_t4x2 uint64_t1x3 uint64_t2x3 uint64_t3x3 uint64_t4x3 uint64_t1x4 uint64_t2x4 uint64_t3x4 uint64_t4x4
+
+" Sampler types
+syn keyword shaderslangType SamplerState SamplerComparisonState
+syn keyword shaderslangType sampler sampler1D sampler2D sampler3D samplerCUBE sampler_state
+
+" Texture types
+syn keyword shaderslangType Texture1D Texture1DArray Texture2D Texture2DArray Texture2DMS Texture2DMSArray Texture3D TextureCube TextureCubeArray
+syn keyword shaderslangType RWTexture1D RWTexture2D RWTexture2DArray RWTexture3D RWTextureCubeArray RWTexture2DMS RWTexture2DMSArray
+syn keyword shaderslangType FeedbackTexture2D FeedbackTexture2DArray
+syn keyword shaderslangType RasterizerOrderedTexture1D RasterizerOrderedTexture1DArray RasterizerOrderedTexture2D RasterizerOrderedTexture2DArray RasterizerOrderedTexture3D
+syn keyword shaderslangTypeDeprec texture texture1D texture2D texture3D
+
+" Raytracing types
+syn keyword shaderslangType RaytracingAccelerationStructure RayDesc RayQuery BuiltInTriangleIntersectionAttributes
+
+" Work graph input record objects
+syn keyword shaderslangType DispatchNodeInputRecord RWDispatchNodeInputRecord GroupNodeInputRecords RWGroupNodeInputRecords ThreadNodeInputRecord RWThreadNodeInputRecord EmptyNodeInput
+
+" Work graph output node objects
+syn keyword shaderslangType NodeOutput NodeOutputArray EmptyNodeOutput EmptyNodeOutputArray
+
+" Work graph output record objects
+syn keyword shaderslangType ThreadNodeOutputRecords GroupNodeOutputRecords
+
+" State Groups args
+syn case ignore " This section case insensitive
+
+" Blend state group
+syn keyword shaderslangStateGroupArg AlphaToCoverageEnable BlendEnable SrcBlend DestBlend BlendOp SrcBlendAlpha DestBlendAlpha BlendOpAlpha RenderTargetWriteMask
+syn keyword shaderslangStateGroupVal ZERO ONE SRC_COLOR INV_SRC_COLOR SRC_ALPHA INV_SRC_ALPHA DEST_ALPHA INV_DEST_ALPHA DEST_COLOR INV_DEST_COLOR SRC_ALPHA_SAT BLEND_FACTOR INV_BLEND_FACTOR SRC1_COLOR INV_SRC1_COLOR SRC1_ALPHA INV_SRC1_ALPHA
+syn keyword shaderslangStateGroupVal ADD SUBSTRACT REV_SUBSTRACT MIN MAX
+
+" Rasterizer state group
+syn keyword shaderslangStateGroupArg FillMode CullMode FrontCounterClockwise DepthBias DepthBiasClamp SlopeScaledDepthBias ZClipEnable DepthClipEnable ScissorEnable MultisampleEnable AntialiasedLineEnable
+syn keyword shaderslangStateGroupVal SOLID WIREFRAME
+syn keyword shaderslangStateGroupVal NONE FRONT BACK
+
+" Sampler state group
+syn keyword shaderslangStateGroupArg Filter AddressU AddressV AddressW MipLODBias MaxAnisotropy ComparisonFunc BorderColor MinLOD MaxLOD ComparisonFilter
+syn keyword shaderslangStateGroupVal MIN_MAG_MIP_POINT MIN_MAG_POINT_MIP_LINEAR MIN_POINT_MAG_LINEAR_MIP_POINT MIN_POINT_MAG_MIP_LINEAR MIN_LINEAR_MAG_MIP_POINT MIN_LINEAR_MAG_POINT_MIP_LINEAR MIN_MAG_LINEAR_MIP_POINT MIN_MAG_MIP_LINEAR ANISOTROPIC
+syn keyword shaderslangStateGroupVal COMPARISON_MIN_MAG_MIP_POINT COMPARISON_MIN_MAG_POINT_MIP_LINEAR COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT COMPARISON_MIN_POINT_MAG_MIP_LINEAR COMPARISON_MIN_LINEAR_MAG_MIP_POINT
+syn keyword shaderslangStateGroupVal COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR COMPARISON_MIN_MAG_LINEAR_MIP_POINT COMPARISON_MIN_MAG_MIP_LINEAR COMPARISON_ANISOTROPIC
+syn keyword shaderslangStateGroupVal COMPARISON_NEVER COMPARISON_LESS COMPARISON_EQUAL COMPARISON_LESS_EQUAL COMPARISON_GREATER COMPARISON_NOT_EQUAL COMPARISON_GREATER_EQUAL COMPARISON_ALWAYS
+syn keyword shaderslangStateGroupVal WRAP MIRROR CLAMP BORDER MIRROR_ONCE
+syn keyword shaderslangStateGroupVal SAMPLER_FEEDBACK_MIN_MIP SAMPLER_FEEDBACK_MIP_REGION_USED
+
+" Ray flags
+syn keyword shaderslangStateGroupVal RAY_FLAG_NONE RAY_FLAG_FORCE_OPAQUE RAY_FLAG_FORCE_NON_OPAQUE RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH RAY_FLAG_SKIP_CLOSEST_HIT_SHADER
+syn keyword shaderslangStateGroupVal RAY_FLAG_CULL_BACK_FACING_TRIANGLES RAY_FLAG_CULL_FRONT_FACING_TRIANGLES RAY_FLAG_CULL_OPAQUE RAY_FLAG_CULL_NON_OPAQUE
+syn keyword shaderslangStateGroupVal RAY_FLAG_SKIP_TRIANGLES RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES
+
+" HitKind enum
+syn keyword shaderslangStateGroupVal HIT_KIND_TRIANGLE_FRONT_FACE HIT_KIND_TRIANGLE_BACK_FACE
+
+" RayQuery enums
+syn keyword shaderslangStateGroupVal COMMITTED_NOTHING COMMITTED_TRIANGLE_HIT COMMITTED_PROCEDURAL_PRIMITIVE_HIT
+syn keyword shaderslangStateGroupVal CANDIDATE_NON_OPAQUE_TRIANGLE CANDIDATE_PROCEDURAL_PRIMITIVE
+
+" Heap objects
+syn keyword shaderslangStateGroupVal ResourceDescriptorHeap SamplerDescriptorHeap
+
+" Work graph constants
+syn keyword shaderslangStateGroupVal UAV_MEMORY GROUP_SHARED_MEMORY NODE_INPUT_MEMORY NODE_OUTPUT_MEMORY ALL_MEMORY GROUP_SYNC GROUP_SCOPE DEVICE_SCOPE
+
+syn case match " Case sensitive from now on
+
+" Effect files declarations and functions
+" Effect groups, techniques passes
+syn keyword shaderslangEffectGroup fxgroup technique11 pass
+" Effect functions
+syn keyword shaderslangEffectFunc SetBlendState SetDepthStencilState SetRasterizerState SetVertexShader SetHullShader SetDomainShader SetGeometryShader SetPixelShader SetComputeShader CompileShader ConstructGSWithSO SetRenderTargets
+
+" Default highlighting
+hi def link shaderslangProfile shaderslangStatement
+hi def link shaderslangStateGroupArg shaderslangStatement
+hi def link shaderslangStateGroupVal Number
+hi def link shaderslangStatement Statement
+hi def link shaderslangType Type
+hi def link shaderslangTypeDeprec WarningMsg
+hi def link shaderslangStorageClass StorageClass
+hi def link shaderslangSemantic PreProc
+hi def link shaderslangFunc shaderslangStatement
+hi def link shaderslangLayoutQual shaderslangFunc
+hi def link shaderslangAnnotation PreProc
+hi def link shaderslangStructure Structure
+hi def link shaderslangSwizzle SpecialChar
+hi def link shaderslangAttribute Statement
+
+hi def link shaderslangEffectGroup Type
+hi def link shaderslangEffectFunc Statement
+
+let b:current_syntax = "shaderslang"
diff --git a/runtime/syntax/tiasm.vim b/runtime/syntax/tiasm.vim
new file mode 100644
index 0000000000..bdadc4a0a7
--- /dev/null
+++ b/runtime/syntax/tiasm.vim
@@ -0,0 +1,102 @@
+" Vim syntax file
+" Language: TI linear assembly language
+" Document: https://downloads.ti.com/docs/esd/SPRUI03B/#SPRUI03B_HTML/assembler-description.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2025 Jan 08
+
+if exists("b:current_syntax")
+ finish
+endif
+
+syn case ignore
+
+" storage types
+syn match tiasmType "\.bits"
+syn match tiasmType "\.byte"
+syn match tiasmType "\.char"
+syn match tiasmType "\.cstring"
+syn match tiasmType "\.double"
+syn match tiasmType "\.field"
+syn match tiasmType "\.float"
+syn match tiasmType "\.half"
+syn match tiasmType "\.int"
+syn match tiasmType "\.long"
+syn match tiasmType "\.short"
+syn match tiasmType "\.string"
+syn match tiasmType "\.ubyte"
+syn match tiasmType "\.uchar"
+syn match tiasmType "\.uhalf"
+syn match tiasmType "\.uint"
+syn match tiasmType "\.ulong"
+syn match tiasmType "\.ushort"
+syn match tiasmType "\.uword"
+syn match tiasmType "\.word"
+
+syn match tiasmIdentifier "[a-z_][a-z0-9_]*"
+
+syn match tiasmDecimal "\<[1-9]\d*\>" display
+syn match tiasmOctal "\<0[0-7][0-7]\+\>\|\<[0-7]\+[oO]\>" display
+syn match tiasmHexadecimal "\<0[xX][0-9a-fA-F]\+\>\|\<[0-9][0-9a-fA-F]*[hH]\>" display
+syn match tiasmBinary "\<0[bB][0-1]\+\>\|\<[01]\+[bB]\>" display
+
+syn match tiasmFloat "\<\d\+\.\d*\%(e[+-]\=\d\+\)\=\>" display
+syn match tiasmFloat "\<\d\%(e[+-]\=\d\+\)\>" display
+
+syn match tiasmCharacter "'.'\|''\|'[^']'"
+
+syn region tiasmString start="\"" end="\"" skip="\"\""
+
+syn match tiasmFunction "\$[a-zA-Z_][a-zA-Z_0-9]*\ze("
+
+syn keyword tiasmTodo contained TODO FIXME XXX NOTE
+syn region tiasmComment start=";" end="$" keepend contains=tiasmTodo,@Spell
+syn match tiasmComment "^[*!].*" contains=tiasmTodo,@Spell
+syn match tiasmLabel "^[^ *!;][^ :]*"
+
+syn match tiasmInclude "\.include"
+syn match tiasmCond "\.if"
+syn match tiasmCond "\.else"
+syn match tiasmCond "\.endif"
+syn match tiasmMacro "\.macro"
+syn match tiasmMacro "\.endm"
+
+syn match tiasmDirective "\.[A-Za-z][0-9A-Za-z-_]*"
+
+syn case match
+
+hi def link tiasmLabel Label
+hi def link tiasmComment Comment
+hi def link tiasmTodo Todo
+hi def link tiasmDirective Statement
+
+hi def link tiasmInclude Include
+hi def link tiasmCond PreCondit
+hi def link tiasmMacro Macro
+
+if exists('g:tiasm_legacy_syntax_groups')
+ hi def link hexNumber Number
+ hi def link decNumber Number
+ hi def link octNumber Number
+ hi def link binNumber Number
+ hi def link tiasmHexadecimal hexNumber
+ hi def link tiasmDecimal decNumber
+ hi def link tiasmOctal octNumber
+ hi def link tiasmBinary binNumber
+else
+ hi def link tiasmHexadecimal Number
+ hi def link tiasmDecimal Number
+ hi def link tiasmOctal Number
+ hi def link tiasmBinary Number
+endif
+hi def link tiasmFloat Float
+
+hi def link tiasmString String
+hi def link tiasmStringEscape Special
+hi def link tiasmCharacter Character
+hi def link tiasmCharacterEscape Special
+
+hi def link tiasmIdentifier Identifier
+hi def link tiasmType Type
+hi def link tiasmFunction Function
+
+let b:current_syntax = "lineartiasm"
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 5bea65436d..12798201e2 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -198,7 +198,7 @@ syn case match
syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimCall,vimCatch,vimConst,vimDef,vimDefFold,vimDelcommand,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimFuncFold,vimGlobal,vimHighlight,vimLet,vimLoadkeymap,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimSet,vimSleep,vimSyntax,vimThrow,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate,@vim9CmdList
syn cluster vim9CmdList contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
syn match vimCmdSep "[:|]\+" skipwhite nextgroup=@vimCmdList,vimSubst1
-syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand
+syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" nextgroup=vimBang contains=vimCommand
syn match vimBang contained "!"
syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>"
syn match vimVar "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
@@ -207,7 +207,6 @@ syn match vimVar "\s\zs&t_\S[a-zA-Z0-9]\>"
syn match vimVar "\s\zs&t_k;"
syn match vimFBVar contained "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
syn keyword vimCommand contained in
-syn match vimBang contained "!"
syn cluster vimExprList contains=vimEnvvar,vimFunc,vimNumber,vimOper,vimOperParen,vimLetRegister,vimString,vimVar,@vim9ExprList
syn cluster vim9ExprList contains=vim9Boolean,vim9Null
@@ -275,7 +274,8 @@ syn keyword vimAugroupKey contained aug[roup] skipwhite nextgroup=vimAugroupBan
" Operators: {{{2
" =========
syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimRegister,@vimContinue,vim9Comment,vimVar,vimBoolean,vimNull
-syn match vimOper "||\|&&\|[-+*/%.!]" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "\a\@<!!" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "||\|&&\|[-+*/%.]" skipwhite nextgroup=vimString,vimSpecFile
syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\|!\~#\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile
syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile
syn region vimOperParen matchgroup=vimParenSep start="(" end=")" contains=@vimOperGroup
@@ -553,8 +553,8 @@ syn region vimPatSepZone oneline contained matchgroup=vimPatSepZ start="\\%\
syn region vimPatRegion contained transparent matchgroup=vimPatSepR start="\\[z%]\=(" end="\\)" contains=@vimSubstList oneline
syn match vimNotPatSep contained "\\\\"
syn cluster vimStringGroup contains=vimEscape,vimEscapeBrace,vimPatSep,vimNotPatSep,vimPatSepErr,vimPatSepZone,@Spell
-syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend
-syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]'+lc=1 end=+'+ extend
+syn region vimString oneline keepend start=+[^a-zA-Z>\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend
+syn region vimString oneline keepend start=+[^a-zA-Z>\\@]'+lc=1 end=+'+ extend
"syn region vimString oneline start="\s/\s*\A"lc=1 skip="\\\\\|\\+" end="/" contains=@vimStringGroup " see tst45.vim
syn match vimString contained +"[^"]*\\$+ skipnl nextgroup=vimStringCont
syn match vimStringCont contained +\(\\\\\|.\)\{-}[^\\]"+
@@ -678,10 +678,12 @@ syn keyword vimAbb abc[lear] cabc[lear] iabc[lear] skipwhite nextgroup=vimMapMod
" Autocmd: {{{2
" =======
-syn match vimAutoEventList contained "\(!\s\+\)\=\(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace
+syn match vimAutoCmdBang contained "\a\@1<=!" skipwhite nextgroup=vimAutoEventList
+syn match vimAutoEventList contained "\%(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace
syn match vimAutoCmdSpace contained "\s\+" nextgroup=vimAutoCmdSfxList
syn match vimAutoCmdSfxList contained "\S*" skipwhite nextgroup=vimAutoCmdMod,vimAutoCmdBlock
-syn keyword vimAutoCmd au[tocmd] do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList
+syn keyword vimAutoCmd au[tocmd] skipwhite nextgroup=vimAutoCmdBang,vimAutoEventList
+syn keyword vimAutoCmd do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList
syn match vimAutoCmdMod "\(++\)\=\(once\|nested\)" skipwhite nextgroup=vimAutoCmdBlock
syn region vimAutoCmdBlock contained matchgroup=vimSep start="{" end="}" contains=@vimDefBodyList
@@ -1275,6 +1277,7 @@ if !exists("skip_vim_syntax_inits")
hi def link vimAugroupError vimError
hi def link vimAugroupKey vimCommand
hi def link vimAutoCmd vimCommand
+ hi def link vimAutoCmdBang vimBang
hi def link vimAutoEvent Type
hi def link vimAutoCmdMod Special
hi def link vimBang vimOper
diff --git a/runtime/syntax/xf86conf.vim b/runtime/syntax/xf86conf.vim
index e8162f3a35..0f4e5036ff 100644
--- a/runtime/syntax/xf86conf.vim
+++ b/runtime/syntax/xf86conf.vim
@@ -1,9 +1,9 @@
" Vim syntax file
" Language: XF86Config (XFree86 configuration file)
+" Maintainer: This runtime file is looking for a new maintainer.
+" Last Change: 2025 Jan 06 by Jan-Arvid Harrach (#16397)
" Former Maintainer: David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
" Last Change By David: 2010 Nov 01
-" Last Change: 2023 Jan 23
-" Required Vim Version: 6.0
"
" Options: let xf86conf_xfree86_version = 3 or 4
" to force XFree86 3.x or 4.x XF86Config syntax
@@ -58,7 +58,7 @@ syn match xf86confModeLineValue "\"[^\"]\+\"\(\_s\+[0-9.]\+\)\{9}" nextgroup=xf8
" Sections and subsections
if b:xf86conf_xfree86_version >= 4
- syn region xf86confSection matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\)\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confComment,xf86confOption,xf86confKeyword,xf86confSectionError
+ syn region xf86confSection matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\|OutputClass\)\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confComment,xf86confOption,xf86confKeyword,xf86confSectionError
syn region xf86confSectionModule matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Module\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionAny,xf86confComment,xf86confOption,xf86confKeyword
syn region xf86confSectionMonitor matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Monitor\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionMode,xf86confModeLine,xf86confComment,xf86confOption,xf86confKeyword
syn region xf86confSectionModes matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Modes\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionMode,xf86confModeLine,xf86confComment
@@ -162,7 +162,7 @@ syn match xf86confSync "\(\s\+[+-][CHV]_*Sync\)\+" contained
" Synchronization
if b:xf86conf_xfree86_version >= 4
- syn sync match xf86confSyncSection grouphere xf86confSection "^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\)\""
+ syn sync match xf86confSyncSection grouphere xf86confSection "^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\|OutputClass\)\""
syn sync match xf86confSyncSectionModule grouphere xf86confSectionModule "^\s*Section\s\+\"Module\""
syn sync match xf86confSyncSectionModes groupthere xf86confSectionModes "^\s*Section\s\+\"Modes\""
else
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 7e79133ed4..23e08bd3fe 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -931,6 +931,9 @@ void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
gap = &ucmds;
} else {
buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
gap = &buf->b_ucmds;
}
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 17287e083b..664406ab6e 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -13,10 +13,11 @@ typedef struct {
typedef struct {
OptionalKeys is_set__set_decoration_provider_;
- LuaRefOf(("start" _, Integer tick)) on_start;
+ LuaRefOf(("start" _, Integer tick), *Boolean) on_start;
LuaRefOf(("buf" _, Integer bufnr, Integer tick)) on_buf;
- LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow)) on_win;
- LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row)) on_line;
+ LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow),
+ *Boolean) on_win;
+ LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row), *Boolean) on_line;
LuaRefOf(("end" _, Integer tick)) on_end;
LuaRefOf(("hl_def" _)) _on_hl_def;
LuaRefOf(("spell_nav" _)) _on_spell_nav;
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 5dcf7d8f49..9e6877cbfa 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -3257,8 +3257,8 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate)
n);
validate_virtcol(curwin);
size_t len = strlen(buffer);
- col_print(buffer + len, IOSIZE - len,
- (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
+ (void)col_print(buffer + len, IOSIZE - len,
+ (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
append_arg_number(curwin, buffer, IOSIZE);
@@ -3286,13 +3286,13 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate)
xfree(buffer);
}
-void col_print(char *buf, size_t buflen, int col, int vcol)
+int col_print(char *buf, size_t buflen, int col, int vcol)
{
if (col == vcol) {
- vim_snprintf(buf, buflen, "%d", col);
- } else {
- vim_snprintf(buf, buflen, "%d-%d", col, vcol);
+ return vim_snprintf(buf, buflen, "%d", col);
}
+
+ return vim_snprintf(buf, buflen, "%d-%d", col, vcol);
}
static char *lasttitle = NULL;
@@ -3416,15 +3416,16 @@ void free_titles(void)
/// Get relative cursor position in window into "buf[buflen]", in the localized
/// percentage form like %99, 99%; using "Top", "Bot" or "All" when appropriate.
-void get_rel_pos(win_T *wp, char *buf, int buflen)
+int get_rel_pos(win_T *wp, char *buf, int buflen)
{
// Need at least 3 chars for writing.
if (buflen < 3) {
- return;
+ return 0;
}
linenr_T above; // number of lines above window
linenr_T below; // number of lines below window
+ int len;
above = wp->w_topline - 1;
above += win_get_fill(wp, wp->w_topline) - wp->w_topfill;
@@ -3435,25 +3436,24 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
}
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
if (below <= 0) {
- xstrlcpy(buf, (above == 0 ? _("All") : _("Bot")), (size_t)buflen);
+ len = vim_snprintf(buf, (size_t)buflen, "%s", (above == 0) ? _("All") : _("Bot"));
} else if (above <= 0) {
- xstrlcpy(buf, _("Top"), (size_t)buflen);
+ len = vim_snprintf(buf, (size_t)buflen, "%s", _("Top"));
} else {
int perc = (above > 1000000
? (above / ((above + below) / 100))
: (above * 100 / (above + below)));
-
- char *p = buf;
- size_t l = (size_t)buflen;
- if (perc < 10) {
- // prepend one space
- buf[0] = ' ';
- p++;
- l--;
- }
// localized percentage value
- vim_snprintf(p, l, _("%d%%"), perc);
+ len = vim_snprintf(buf, (size_t)buflen, _("%s%d%%"), (perc < 10) ? " " : "", perc);
}
+ if (len < 0) {
+ buf[0] = NUL;
+ len = 0;
+ } else if (len > buflen - 1) {
+ len = buflen - 1;
+ }
+
+ return len;
}
/// Append (2 of 8) to "buf[buflen]", if editing more than one file.
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 728a917c22..a9f3ba0c3b 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -303,12 +303,24 @@ static void decor_free_inner(DecorVirtText *vt, uint32_t first_idx)
}
}
+/// Check if we are in a callback while drawing, which might invalidate the marktree iterator.
+///
+/// This should be called whenever a structural modification has been done to a
+/// marktree in a public API function (i e any change which adds or deletes marks).
+void decor_state_invalidate(buf_T *buf)
+{
+ if (decor_state.win && decor_state.win->w_buffer == buf) {
+ decor_state.itr_valid = false;
+ }
+}
+
void decor_check_to_be_deleted(void)
{
assert(!decor_state.running_decor_provider);
decor_free_inner(to_free_virt, to_free_sh);
to_free_virt = NULL;
to_free_sh = DECOR_ID_INVALID;
+ decor_state.win = NULL;
}
void decor_state_free(DecorState *state)
@@ -447,6 +459,8 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
{
buf_T *buf = wp->w_buffer;
state->top_row = top_row;
+ state->itr_valid = true;
+
if (!marktree_itr_get_overlap(buf->b_marktree, top_row, 0, state->itr)) {
return false;
}
@@ -489,7 +503,11 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
if (state->row == -1) {
decor_redraw_start(wp, row, state);
+ } else if (!state->itr_valid) {
+ marktree_itr_get(wp->w_buffer->b_marktree, row, 0, state->itr);
+ state->itr_valid = true;
}
+
state->row = row;
state->col_until = -1;
state->eol_col = -1;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 1d268c982b..a2f4fefd45 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -96,6 +96,7 @@ typedef struct {
TriState spell;
bool running_decor_provider;
+ bool itr_valid;
} DecorState;
EXTERN DecorState decor_state INIT( = { 0 });
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index e5d2658720..7c99fbf889 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -155,7 +155,7 @@ void decor_providers_invoke_win(win_T *wp)
/// @param row Row to invoke line callback for
/// @param[out] has_decor Set when at least one provider invokes a line callback
/// @param[out] err Provider error
-void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
+void decor_providers_invoke_line(win_T *wp, int row)
{
decor_state.running_decor_provider = true;
for (size_t i = 0; i < kv_size(decor_providers); i++) {
@@ -165,9 +165,7 @@ void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
ADD_C(args, WINDOW_OBJ(wp->handle));
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
ADD_C(args, INTEGER_OBJ(row));
- if (decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) {
- *has_decor = true;
- } else {
+ if (!decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) {
// return 'false' or error: skip rest of this window
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index aaa77f5fcf..326e929fb6 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -2183,22 +2183,22 @@ static void keymap_unload(void)
/// @param fmt format string containing one %s item
/// @param buf buffer for the result
/// @param len length of buffer
-bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
+int get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
{
char *p;
if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) {
- return false;
+ return 0;
}
buf_T *old_curbuf = curbuf;
win_T *old_curwin = curwin;
+ char to_evaluate[] = "b:keymap_name";
curbuf = wp->w_buffer;
curwin = wp;
- STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
- char *s = p = eval_to_string(buf, false, false);
+ char *s = p = eval_to_string(to_evaluate, false, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;
@@ -2209,9 +2209,12 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
p = "lang";
}
}
- if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) {
+ int plen = vim_snprintf(buf, (size_t)len, fmt, p);
+ xfree(s);
+ if (plen < 0 || plen > len - 1) {
buf[0] = NUL;
+ plen = 0;
}
- xfree(s);
- return buf[0] != NUL;
+
+ return plen;
}
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 3062b0f2a3..d5273ff3d1 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -1051,12 +1051,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
}
- has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
-
if (!end_fill) {
- decor_providers_invoke_line(wp, lnum - 1, &has_decor);
+ decor_providers_invoke_line(wp, lnum - 1);
}
+ has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
+
if (has_decor) {
extra_check = true;
}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index f8aeb02229..7f4de9eab8 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -1045,7 +1045,7 @@ int showmode(void)
if (State & MODE_LANGMAP) {
if (curwin->w_p_arab) {
msg_puts_hl(_(" Arabic"), hl_id, false);
- } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) {
+ } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL) > 0) {
msg_puts_hl(NameBuff, hl_id, false);
}
}
@@ -1936,7 +1936,7 @@ static void win_update(win_T *wp)
pos.lnum += cursor_above ? 1 : -1) {
colnr_T t;
- pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum));
+ pos.col = ml_get_buf_len(wp->w_buffer, pos.lnum);
getvvcol(wp, &pos, NULL, NULL, &t);
toc = MAX(toc, t);
}
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 6119d838f9..79eea718f4 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -95,6 +95,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
MTKey mark = { { row, col }, ns_id, id, flags, decor.data };
marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity);
+ decor_state_invalidate(buf);
revised:
if (decor_flags || decor.ext) {
@@ -184,6 +185,8 @@ void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
}
}
+ decor_state_invalidate(buf);
+
// TODO(bfredl): delete it from current undo header, opportunistically?
}
@@ -237,6 +240,10 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
}
}
+ if (marks_cleared_any) {
+ decor_state_invalidate(buf);
+ }
+
return marks_cleared_any;
}
diff --git a/src/nvim/generators/gen_vimvim.lua b/src/nvim/generators/gen_vimvim.lua
index 0675f04b73..d8053822bf 100644
--- a/src/nvim/generators/gen_vimvim.lua
+++ b/src/nvim/generators/gen_vimvim.lua
@@ -52,11 +52,13 @@ local function is_special_cased_cmd(cmd)
end
local vimcmd_start = 'syn keyword vimCommand contained '
+local vimcmd_end = ' nextgroup=vimBang'
w(vimcmd_start)
+
local prev_cmd = nil
for _, cmd_desc in ipairs(ex_cmds.cmds) do
if lld.line_length > 850 then
- w('\n' .. vimcmd_start)
+ w(vimcmd_end .. '\n' .. vimcmd_start)
end
local cmd = cmd_desc.command
if cmd:match('%w') and cmd ~= 'z' and not is_special_cased_cmd(cmd) then
@@ -79,9 +81,11 @@ for _, cmd_desc in ipairs(ex_cmds.cmds) do
prev_cmd = cmd
end
+w(vimcmd_end .. '\n')
+
local vimopt_start = 'syn keyword vimOption contained '
local vimopt_end = ' skipwhite nextgroup=vimSetEqual,vimSetMod'
-w('\n\n' .. vimopt_start)
+w('\n' .. vimopt_start)
for _, opt_desc in ipairs(options.options) do
if not opt_desc.immutable then
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 6575113e2f..ad4b2732f6 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -150,6 +150,7 @@ static const char *highlight_init_both[] = {
"PmenuMatchSel gui=bold cterm=bold",
"PmenuSel gui=reverse cterm=reverse,underline blend=0",
"RedrawDebugNormal gui=reverse cterm=reverse",
+ "TabLineSel gui=bold cterm=NONE",
"TermCursor gui=reverse cterm=reverse",
"Underlined gui=underline cterm=underline",
"lCursor guifg=bg guibg=fg",
@@ -180,7 +181,6 @@ static const char *highlight_init_both[] = {
"default link StatusLineTermNC StatusLineNC",
"default link TabLine StatusLineNC",
"default link TabLineFill TabLine",
- "default link TabLineSel Normal",
"default link VertSplit WinSeparator",
"default link VisualNOS Visual",
"default link Whitespace NonText",
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 9b77644085..419c806592 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -156,7 +157,7 @@ struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
compl_T *cp_match_next; ///< matched next compl_T
- char *cp_str; ///< matched text
+ String cp_str; ///< matched text
char *(cp_text[CPT_COUNT]); ///< text for the menu
typval_T cp_user_data;
char *cp_fname; ///< file containing the match, allocated when
@@ -220,7 +221,7 @@ static bool compl_enter_selects = false;
/// When "compl_leader" is not NULL only matches that start with this string
/// are used.
-static char *compl_leader = NULL;
+static String compl_leader = STRING_INIT;
static bool compl_get_longest = false; ///< put longest common string in compl_leader
@@ -245,8 +246,7 @@ static bool compl_started = false;
static int ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; ///< number of completion matches
-static char *compl_pattern = NULL;
-static size_t compl_patternlen = 0;
+static String compl_pattern = STRING_INIT;
static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N
@@ -257,8 +257,8 @@ static int compl_length = 0;
static colnr_T compl_col = 0; ///< column where the text starts
///< that is being completed
static colnr_T compl_ins_end_col = 0;
-static char *compl_orig_text = NULL; ///< text as it was before
- ///< completion started
+static String compl_orig_text = STRING_INIT; ///< text as it was before
+ ///< completion started
/// Undo information to restore extmarks for original text.
static extmark_undo_vec_t compl_orig_extmarks;
static int compl_cont_mode = 0;
@@ -639,7 +639,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Rule 1: Were any chars converted to lower?
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -659,7 +659,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// upper case.
if (!has_lower) {
bool was_letter = false;
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
@@ -675,7 +675,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Copy the original case of the part we typed.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -705,7 +705,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
ga_grow(&gap, IOSIZE);
*p = NUL;
STRCPY(gap.ga_data, IObuff);
- gap.ga_len = (int)strlen(IObuff);
+ gap.ga_len = (int)(p - IObuff);
} else {
p += utf_char2bytes(wca[i++], p);
}
@@ -752,7 +752,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// Find actual length of original text.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
@@ -841,8 +841,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match = compl_first_match;
do {
if (!match_at_original_text(match)
- && strncmp(match->cp_str, str, (size_t)len) == 0
- && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) {
+ && strncmp(match->cp_str.data, str, (size_t)len) == 0
+ && ((int)match->cp_str.size <= len || match->cp_str.data[len] == NUL)) {
if (cptext_allocated) {
free_cptext(cptext);
}
@@ -862,7 +862,7 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
if (flags & CP_ORIGINAL_TEXT) {
match->cp_number = 0;
}
- match->cp_str = xstrnsave(str, (size_t)len);
+ match->cp_str = cbuf_to_string(str, (size_t)len);
// match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -944,9 +944,9 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len)
return true;
}
if (match->cp_flags & CP_ICASE) {
- return STRNICMP(match->cp_str, str, len) == 0;
+ return STRNICMP(match->cp_str.data, str, len) == 0;
}
- return strncmp(match->cp_str, str, len) == 0;
+ return strncmp(match->cp_str.data, str, len) == 0;
}
/// when len is -1 mean use whole length of p otherwise part of p
@@ -976,13 +976,13 @@ int ins_compl_col_range_attr(int col)
/// Reduce the longest common string for match "match".
static void ins_compl_longest_match(compl_T *match)
{
- if (compl_leader == NULL) {
+ if (compl_leader.data == NULL) {
// First match, use it as a whole.
- compl_leader = xstrdup(match->cp_str);
+ compl_leader = copy_string(match->cp_str, NULL);
bool had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete(false);
- ins_compl_insert_bytes(compl_leader + get_compl_len(), -1);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
@@ -996,8 +996,8 @@ static void ins_compl_longest_match(compl_T *match)
}
// Reduce the text if this match differs from compl_leader.
- char *p = compl_leader;
- char *s = match->cp_str;
+ char *p = compl_leader.data;
+ char *s = match->cp_str.data;
while (*p != NUL) {
int c1 = utf_ptr2char(p);
int c2 = utf_ptr2char(s);
@@ -1014,9 +1014,11 @@ static void ins_compl_longest_match(compl_T *match)
if (*p != NUL) {
// Leader was shortened, need to change the inserted text.
*p = NUL;
+ compl_leader.size = (size_t)(p - compl_leader.data);
+
bool had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete(false);
- ins_compl_insert_bytes(compl_leader + get_compl_len(), -1);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
@@ -1078,8 +1080,8 @@ bool ins_compl_has_shown_match(void)
/// Return whether the shown match is long enough.
bool ins_compl_long_shown_match(void)
{
- return compl_shown_match != NULL && compl_shown_match->cp_str != NULL
- && (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col;
+ return compl_shown_match != NULL && compl_shown_match->cp_str.data != NULL
+ && (colnr_T)compl_shown_match->cp_str.size > curwin->w_cursor.col - compl_col;
}
/// Get the local or global value of 'completeopt' flags.
@@ -1134,7 +1136,7 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc_lock(VAR_FIXED);
- tv_dict_add_str(dict, S_LEN("word"), match->cp_str);
+ tv_dict_add_str(dict, S_LEN("word"), match->cp_str.data);
tv_dict_add_str(dict, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
tv_dict_add_str(dict, S_LEN("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]);
@@ -1203,7 +1205,6 @@ static int ins_compl_build_pum(void)
XFREE_CLEAR(compl_leader);
}
- const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
int max_fuzzy_score = 0;
unsigned cur_cot_flags = get_cot_flags();
bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
@@ -1214,7 +1215,7 @@ static int ins_compl_build_pum(void)
// match after it, don't highlight anything.
bool shown_match_ok = match_at_original_text(compl_shown_match);
- if (strequal(compl_leader, compl_orig_text) && !shown_match_ok) {
+ if (strequal(compl_leader.data, compl_orig_text.data) && !shown_match_ok) {
compl_shown_match = compl_no_select ? compl_first_match : compl_first_match->cp_next;
}
@@ -1227,13 +1228,13 @@ static int ins_compl_build_pum(void)
comp->cp_in_match_array = false;
// When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
// set the cp_score for later comparisons.
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
- comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader);
+ if (compl_fuzzy_match && compl_leader.data != NULL && compl_leader.size > 0) {
+ comp->cp_score = fuzzy_match_str(comp->cp_str.data, compl_leader.data);
}
if (!match_at_original_text(comp)
- && (compl_leader == NULL
- || ins_compl_equal(comp, compl_leader, (size_t)lead_len)
+ && (compl_leader.data == NULL
+ || ins_compl_equal(comp, compl_leader.data, compl_leader.size)
|| (compl_fuzzy_match && comp->cp_score > 0))) {
compl_match_arraysize++;
comp->cp_in_match_array = true;
@@ -1305,7 +1306,7 @@ static int ins_compl_build_pum(void)
comp = match_head;
while (comp != NULL) {
compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR] != NULL
- ? comp->cp_text[CPT_ABBR] : comp->cp_str;
+ ? comp->cp_text[CPT_ABBR] : comp->cp_str.data;
compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
compl_match_array[i].pum_score = comp->cp_score;
@@ -1318,7 +1319,7 @@ static int ins_compl_build_pum(void)
comp = match_next;
}
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
+ if (compl_fuzzy_match && compl_leader.data != NULL && compl_leader.size > 0) {
for (i = 0; i < compl_match_arraysize; i++) {
compl_match_array[i].pum_idx = i;
}
@@ -1356,7 +1357,7 @@ void ins_compl_show_pum(void)
} else {
// popup menu already exists, only need to find the current item.
for (int i = 0; i < compl_match_arraysize; i++) {
- if (compl_match_array[i].pum_text == compl_shown_match->cp_str
+ if (compl_match_array[i].pum_text == compl_shown_match->cp_str.data
|| compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i;
break;
@@ -1425,7 +1426,13 @@ bool compl_match_curr_select(int selected)
/// Get current completion leader
char *ins_compl_leader(void)
{
- return compl_leader != NULL ? compl_leader : compl_orig_text;
+ return compl_leader.data != NULL ? compl_leader.data : compl_orig_text.data;
+}
+
+/// Get current completion leader length
+size_t ins_compl_leader_len(void)
+{
+ return compl_leader.data != NULL ? compl_leader.size : compl_orig_text.size;
}
/// Add any identifiers that match the given pattern "pat" in the list of
@@ -1671,9 +1678,8 @@ static char *find_line_end(char *ptr)
/// Free the list of completions
static void ins_compl_free(void)
{
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
if (compl_first_match == NULL) {
return;
@@ -1686,7 +1692,7 @@ static void ins_compl_free(void)
do {
compl_T *match = compl_curr_match;
compl_curr_match = compl_curr_match->cp_next;
- xfree(match->cp_str);
+ API_CLEAR_STRING(match->cp_str);
// several entries may use the same fname, free it just once.
if (match->cp_flags & CP_FREE_FNAME) {
xfree(match->cp_fname);
@@ -1707,12 +1713,11 @@ void ins_compl_clear(void)
compl_started = false;
compl_matches = 0;
compl_ins_end_col = 0;
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
edit_submode_extra = NULL;
kv_destroy(compl_orig_extmarks);
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
@@ -1802,8 +1807,9 @@ int ins_compl_bs(void)
// TODO(bfredl): get rid of random update_screen() calls deep inside completion logic
line = get_cursor_line_ptr();
- xfree(compl_leader);
- compl_leader = xstrnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(line + compl_col,
+ (size_t)(p_off - (ptrdiff_t)compl_col));
ins_compl_new_leader();
if (compl_shown_match != NULL) {
@@ -1837,11 +1843,11 @@ static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
ins_compl_delete(true);
- ins_compl_insert_bytes(compl_leader + get_compl_len(), -1);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
compl_used_match = false;
if (compl_started) {
- ins_compl_set_original_text(compl_leader);
+ ins_compl_set_original_text(compl_leader.data, compl_leader.size);
} else {
spell_bad_len = 0; // need to redetect bad word
// Matches were cleared, need to search for them now.
@@ -1901,9 +1907,9 @@ void ins_compl_addleader(int c)
ins_compl_restart();
}
- xfree(compl_leader);
- compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col,
- (size_t)(curwin->w_cursor.col - compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader();
}
@@ -1923,19 +1929,19 @@ static void ins_compl_restart(void)
}
/// Set the first match, the original text.
-static void ins_compl_set_original_text(char *str)
+static void ins_compl_set_original_text(char *str, size_t len)
FUNC_ATTR_NONNULL_ALL
{
// Replace the original text entry.
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
// be at the last item for backward completion
if (match_at_original_text(compl_first_match)) { // safety check
- xfree(compl_first_match->cp_str);
- compl_first_match->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_str);
+ compl_first_match->cp_str = cbuf_to_string(str, len);
} else if (compl_first_match->cp_prev != NULL
&& match_at_original_text(compl_first_match->cp_prev)) {
- xfree(compl_first_match->cp_prev->cp_str);
- compl_first_match->cp_prev->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
+ compl_first_match->cp_prev->cp_str = cbuf_to_string(str, len);
}
}
@@ -1945,8 +1951,8 @@ void ins_compl_addfrommatch(void)
{
int len = (int)curwin->w_cursor.col - (int)compl_col;
assert(compl_shown_match != NULL);
- char *p = compl_shown_match->cp_str;
- if ((int)strlen(p) <= len) { // the match is too short
+ char *p = compl_shown_match->cp_str.data;
+ if ((int)compl_shown_match->cp_str.size <= len) { // the match is too short
// When still at the original match use the first entry that matches
// the leader.
if (!match_at_original_text(compl_shown_match)) {
@@ -1954,15 +1960,17 @@ void ins_compl_addfrommatch(void)
}
p = NULL;
+ size_t plen = 0;
for (compl_T *cp = compl_shown_match->cp_next; cp != NULL
&& !is_first_match(cp); cp = cp->cp_next) {
- if (compl_leader == NULL
- || ins_compl_equal(cp, compl_leader, strlen(compl_leader))) {
- p = cp->cp_str;
+ if (compl_leader.data == NULL
+ || ins_compl_equal(cp, compl_leader.data, compl_leader.size)) {
+ p = cp->cp_str.data;
+ plen = cp->cp_str.size;
break;
}
}
- if (p == NULL || (int)strlen(p) <= len) {
+ if (p == NULL || (int)plen <= len) {
return;
}
}
@@ -2102,7 +2110,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// Get here when we have finished typing a sequence of ^N and
// ^P or other completion characters in CTRL-X mode. Free up
// memory that was used, and make sure we can redo the insert.
- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
+ if (compl_curr_match != NULL || compl_leader.data != NULL || c == Ctrl_E) {
// If any of the original typed text has been changed, eg when
// ignorecase is set, we must add back-spaces to the redo
// buffer. We add as few as necessary to delete just the part
@@ -2111,7 +2119,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// CTRL-E then don't use the current match.
char *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
+ ptr = compl_curr_match->cp_str.data;
} else {
ptr = NULL;
}
@@ -2154,7 +2162,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
if ((c == Ctrl_Y || (compl_enter_selects
&& (c == CAR || c == K_KENTER || c == NL)))
&& pum_visible()) {
- word = xstrdup(compl_shown_match->cp_str);
+ word = xstrdup(compl_shown_match->cp_str.data);
retval = true;
// May need to remove ComplMatchIns highlight.
redrawWinline(curwin, curwin->w_cursor.lnum);
@@ -2165,16 +2173,18 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
if (c == Ctrl_E) {
ins_compl_delete(false);
char *p = NULL;
- if (compl_leader != NULL) {
- p = compl_leader;
+ size_t plen = 0;
+ if (compl_leader.data != NULL) {
+ p = compl_leader.data;
+ plen = compl_leader.size;
} else if (compl_first_match != NULL) {
- p = compl_orig_text;
+ p = compl_orig_text.data;
+ plen = compl_orig_text.size;
}
if (p != NULL) {
const int compl_len = get_compl_len();
- const int len = (int)strlen(p);
- if (len > compl_len) {
- ins_compl_insert_bytes(p + compl_len, len - compl_len);
+ if ((int)plen > compl_len) {
+ ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
}
}
restore_orig_extmarks();
@@ -2324,18 +2334,18 @@ bool ins_compl_prep(int c)
/// "ptr" is the known leader text or NUL.
static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
{
- int len;
+ int len = 0;
char *ptr = ptr_arg;
if (ptr == NULL) {
- if (compl_leader != NULL) {
- ptr = compl_leader;
+ if (compl_leader.data != NULL) {
+ ptr = compl_leader.data;
} else {
return; // nothing to do
}
}
- if (compl_orig_text != NULL) {
- char *p = compl_orig_text;
+ if (compl_orig_text.data != NULL) {
+ char *p = compl_orig_text.data;
for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {}
if (len > 0) {
len -= utf_head_off(p, p + len);
@@ -2343,8 +2353,6 @@ static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
for (p += len; *p != NUL; MB_PTR_ADV(p)) {
AppendCharToRedobuff(K_BS);
}
- } else {
- len = 0;
}
AppendToRedobuffLit(ptr + len, -1);
}
@@ -2730,12 +2738,14 @@ static void set_completion(colnr_T startcol, list_T *list)
compl_col = startcol;
compl_length = curwin->w_cursor.col - startcol;
// compl_pattern doesn't need to be set
- compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, (size_t)compl_length);
+ compl_orig_text = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)compl_length);
save_orig_extmarks();
if (p_ic) {
flags |= CP_ICASE;
}
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
+ if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size,
+ NULL, NULL, false, NULL, 0,
flags | CP_FAST, false, NULL) != OK) {
return;
}
@@ -2940,7 +2950,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
if (has_items || (has_matches && match->cp_in_match_array)) {
dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di);
- tv_dict_add_str(di, S_LEN("word"), match->cp_str);
+ tv_dict_add_str(di, S_LEN("word"), match->cp_str.data);
tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]);
@@ -3137,8 +3147,8 @@ done:
/// included files.
static void get_next_include_file_completion(int compl_type)
{
- find_pattern_in_path(compl_pattern, compl_direction,
- compl_patternlen, false, false,
+ find_pattern_in_path(compl_pattern.data, compl_direction,
+ compl_pattern.size, false, false,
((compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY),
@@ -3150,14 +3160,14 @@ static void get_next_include_file_completion(int compl_type)
static void get_next_dict_tsr_completion(int compl_type, char *dict, int dict_f)
{
if (thesaurus_func_complete(compl_type)) {
- expand_by_function(compl_type, compl_pattern);
+ expand_by_function(compl_type, compl_pattern.data);
} else {
ins_compl_dictionaries(dict != NULL
? dict
: (compl_type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
: (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
- compl_pattern,
+ compl_pattern.data,
dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS);
}
@@ -3168,14 +3178,14 @@ static void get_next_tag_completion(void)
{
// set p_ic according to p_ic, p_scs and pat for find_tags().
const int save_p_ic = p_ic;
- p_ic = ignorecase(compl_pattern);
+ p_ic = ignorecase(compl_pattern.data);
// Find up to TAG_MANY matches. Avoids that an enormous number
// of matches is found when compl_pattern is empty
g_tag_at_cursor = true;
char **matches;
int num_matches;
- if (find_tags(compl_pattern, &num_matches, &matches,
+ if (find_tags(compl_pattern.data, &num_matches, &matches,
TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
| (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
@@ -3190,13 +3200,13 @@ static void get_next_filename_completion(void)
{
char **matches;
int num_matches;
- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+ if (expand_wildcards(1, &compl_pattern.data, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
return;
}
// May change home directory back to "~".
- tilde_replace(compl_pattern, num_matches, matches);
+ tilde_replace(compl_pattern.data, num_matches, matches);
#ifdef BACKSLASH_IN_FILENAME
if (curbuf->b_p_csl[0] != NUL) {
for (int i = 0; i < num_matches; i++) {
@@ -3220,8 +3230,8 @@ static void get_next_cmdline_completion(void)
{
char **matches;
int num_matches;
- if (expand_cmdline(&compl_xp, compl_pattern,
- (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) {
+ if (expand_cmdline(&compl_xp, compl_pattern.data,
+ (int)compl_pattern.size, &num_matches, &matches) == EXPAND_OK) {
ins_compl_add_matches(num_matches, matches, false);
}
}
@@ -3230,7 +3240,7 @@ static void get_next_cmdline_completion(void)
static void get_next_spell_completion(linenr_T lnum)
{
char **matches;
- int num_matches = expand_spelling(lnum, compl_pattern, &matches);
+ int num_matches = expand_spelling(lnum, compl_pattern.data, &matches);
if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic);
} else {
@@ -3249,22 +3259,24 @@ static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_po
{
*match_len = 0;
char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum) + cur_match_pos->col;
- int len;
+ int len = ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col;
if (ctrl_x_mode_line_or_eval()) {
if (compl_status_adding()) {
if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) {
return NULL;
}
ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1);
+ len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1);
if (!p_paste) {
- ptr = skipwhite(ptr);
+ char *tmp_ptr = ptr;
+ ptr = skipwhite(tmp_ptr);
+ len -= (int)(ptr - tmp_ptr);
}
}
- len = (int)strlen(ptr);
} else {
char *tmp_ptr = ptr;
- if (compl_status_adding() && compl_length <= (int)strlen(tmp_ptr)) {
+ if (compl_status_adding() && compl_length <= len) {
tmp_ptr += compl_length;
// Skip if already inside a word.
if (vim_iswordp(tmp_ptr)) {
@@ -3360,10 +3372,11 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
// has added a word that was at the beginning of the line.
if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) {
found_new_match = search_for_exact_line(st->ins_buf, st->cur_match_pos,
- compl_direction, compl_pattern);
+ compl_direction, compl_pattern.data);
} else {
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
- NULL, compl_direction, compl_pattern, compl_patternlen,
+ NULL, compl_direction, compl_pattern.data,
+ compl_pattern.size,
1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
}
msg_silent--;
@@ -3460,7 +3473,7 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_
case CTRL_X_FUNCTION:
case CTRL_X_OMNI:
- expand_by_function(type, compl_pattern);
+ expand_by_function(type, compl_pattern.data);
break;
case CTRL_X_SPELL:
@@ -3491,7 +3504,7 @@ static void get_next_bufname_token(void)
FOR_ALL_BUFFERS(b) {
if (b->b_p_bl && b->b_sfname != NULL) {
char *tail = path_tail(b->b_sfname);
- if (strncmp(tail, compl_orig_text, strlen(compl_orig_text)) == 0) {
+ if (strncmp(tail, compl_orig_text.data, compl_orig_text.size) == 0) {
ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0,
p_ic ? CP_ICASE : 0, false, NULL);
}
@@ -3559,7 +3572,7 @@ static int ins_compl_get_exp(pos_T *ini)
// If complete() was called then compl_pattern has been reset.
// The following won't work then, bail out.
- if (compl_pattern == NULL) {
+ if (compl_pattern.data == NULL) {
break;
}
@@ -3627,7 +3640,7 @@ static int ins_compl_get_exp(pos_T *ini)
static void ins_compl_update_shown_match(void)
{
while (!ins_compl_equal(compl_shown_match,
- compl_leader, strlen(compl_leader))
+ compl_leader.data, compl_leader.size)
&& compl_shown_match->cp_next != NULL
&& !is_first_match(compl_shown_match->cp_next)) {
compl_shown_match = compl_shown_match->cp_next;
@@ -3636,10 +3649,10 @@ static void ins_compl_update_shown_match(void)
// If we didn't find it searching forward, and compl_shows_dir is
// backward, find the last match.
if (compl_shows_dir_backward()
- && !ins_compl_equal(compl_shown_match, compl_leader, strlen(compl_leader))
+ && !ins_compl_equal(compl_shown_match, compl_leader.data, compl_leader.size)
&& (compl_shown_match->cp_next == NULL
|| is_first_match(compl_shown_match->cp_next))) {
- while (!ins_compl_equal(compl_shown_match, compl_leader, strlen(compl_leader))
+ while (!ins_compl_equal(compl_shown_match, compl_leader.data, compl_leader.size)
&& compl_shown_match->cp_prev != NULL
&& !is_first_match(compl_shown_match->cp_prev)) {
compl_shown_match = compl_shown_match->cp_prev;
@@ -3654,13 +3667,13 @@ void ins_compl_delete(bool new_leader)
// allows marks present on the original text to shrink/grow appropriately.
int orig_col = 0;
if (new_leader) {
- char *orig = compl_orig_text;
+ char *orig = compl_orig_text.data;
char *leader = ins_compl_leader();
while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) {
leader += utf_ptr2len(leader);
orig += utf_ptr2len(orig);
}
- orig_col = (int)(orig - compl_orig_text);
+ orig_col = (int)(orig - compl_orig_text.data);
}
// In insert mode: Delete the typed part.
@@ -3688,8 +3701,8 @@ void ins_compl_insert(bool in_compl_func)
int compl_len = get_compl_len();
// Make sure we don't go over the end of the string, this can happen with
// illegal bytes.
- if (compl_len < (int)strlen(compl_shown_match->cp_str)) {
- ins_compl_insert_bytes(compl_shown_match->cp_str + compl_len, -1);
+ if (compl_len < (int)compl_shown_match->cp_str.size) {
+ ins_compl_insert_bytes(compl_shown_match->cp_str.data + compl_len, -1);
}
compl_used_match = !match_at_original_text(compl_shown_match);
@@ -3759,7 +3772,7 @@ static compl_T *find_comp_when_fuzzy(void)
comp = compl_first_match;
do {
if (comp->cp_score == score
- && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) {
+ && (str == comp->cp_str.data || str == comp->cp_text[CPT_ABBR])) {
return comp;
}
comp = comp->cp_next;
@@ -3842,9 +3855,9 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
found_end = false;
}
if (!match_at_original_text(compl_shown_match)
- && compl_leader != NULL
+ && compl_leader.data != NULL
&& !ins_compl_equal(compl_shown_match,
- compl_leader, strlen(compl_leader))
+ compl_leader.data, compl_leader.size)
&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) {
todo++;
} else {
@@ -3900,7 +3913,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
return -1;
}
- if (compl_leader != NULL
+ if (compl_leader.data != NULL
&& !match_at_original_text(compl_shown_match)
&& !compl_fuzzy_match) {
// Update "compl_shown_match" to the actually shown match
@@ -3938,17 +3951,17 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
// Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) {
- ins_compl_insert_bytes(compl_orig_text + get_compl_len(), -1);
+ ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1);
compl_used_match = false;
restore_orig_extmarks();
} else if (insert_match) {
if (!compl_get_longest || compl_used_match) {
ins_compl_insert(in_compl_func);
} else {
- assert(compl_leader != NULL);
- ins_compl_insert_bytes(compl_leader + get_compl_len(), -1);
+ assert(compl_leader.data != NULL);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
}
- if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) {
+ if (strequal(compl_curr_match->cp_str.data, compl_orig_text.data)) {
restore_orig_extmarks();
}
} else {
@@ -4112,8 +4125,7 @@ static bool ins_compl_use_match(int c)
/// Get the pattern, column and length for normal completion (CTRL-N CTRL-P
/// completion)
-/// Sets the global variables: compl_col, compl_length, compl_pattern and
-/// compl_patternlen.
+/// Sets the global variables: compl_col, compl_length and compl_pattern.
/// Uses the global variables: compl_cont_status and ctrl_x_mode
static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
{
@@ -4124,29 +4136,32 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
compl_length = curs_col - startcol;
}
if (p_ic) {
- compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = cstr_as_string(str_foldcase(line + compl_col,
+ compl_length, NULL, 0));
} else {
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
}
} else if (compl_status_adding()) {
char *prefix = "\\<";
size_t prefixlen = STRLEN_LITERAL("\\<");
- // we need up to 2 extra chars for the prefix
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col,
- compl_length) + prefixlen);
if (!vim_iswordp(line + compl_col)
|| (compl_col > 0
&& (vim_iswordp(mb_prevptr(line, line + compl_col))))) {
prefix = "";
prefixlen = 0;
}
- STRCPY(compl_pattern, prefix);
- quote_meta(compl_pattern + prefixlen, line + compl_col, compl_length);
+
+ // we need up to 2 extra chars for the prefix
+ size_t n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen;
+ compl_pattern.data = xmalloc(n);
+ STRCPY(compl_pattern.data, prefix);
+ quote_meta(compl_pattern.data + prefixlen, line + compl_col, compl_length);
+ compl_pattern.size = n - 1;
} else if (--startcol < 0
|| !vim_iswordp(mb_prevptr(line, line + startcol + 1))) {
// Match any word of at least two chars
- compl_pattern = xstrnsave(S_LEN("\\<\\k\\k"));
+ compl_pattern = cbuf_to_string(S_LEN("\\<\\k\\k"));
compl_col += curs_col;
compl_length = 0;
} else {
@@ -4167,19 +4182,20 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
// Only match word with at least two chars -- webb
// there's no need to call quote_meta,
// xmalloc(7) is enough -- Acevedo
- compl_pattern = xmalloc(7);
- STRCPY(compl_pattern, "\\<");
- quote_meta(compl_pattern + 2, line + compl_col, 1);
- strcat(compl_pattern, "\\k");
+ compl_pattern.data = xmalloc(7);
+ STRCPY(compl_pattern.data, "\\<");
+ quote_meta(compl_pattern.data + 2, line + compl_col, 1);
+ strcat(compl_pattern.data, "\\k");
+ compl_pattern.size = strlen(compl_pattern.data);
} else {
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2);
- STRCPY(compl_pattern, "\\<");
- quote_meta(compl_pattern + 2, line + compl_col, compl_length);
+ size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2;
+ compl_pattern.data = xmalloc(n);
+ STRCPY(compl_pattern.data, "\\<");
+ quote_meta(compl_pattern.data + 2, line + compl_col, compl_length);
+ compl_pattern.size = n - 1;
}
}
- compl_patternlen = strlen(compl_pattern);
-
return OK;
}
@@ -4194,13 +4210,12 @@ static int get_wholeline_compl_info(char *line, colnr_T curs_col)
compl_length = 0;
}
if (p_ic) {
- compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = cstr_as_string(str_foldcase(line + compl_col,
+ compl_length, NULL, 0));
} else {
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
}
- compl_patternlen = strlen(compl_pattern);
-
return OK;
}
@@ -4225,8 +4240,8 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
compl_col += startcol;
compl_length = (int)curs_col - startcol;
- compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES);
- compl_patternlen = strlen(compl_pattern);
+ compl_pattern = cstr_as_string(addstar(line + compl_col,
+ (size_t)compl_length, EXPAND_FILES));
return OK;
}
@@ -4235,9 +4250,9 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
/// Sets the global variables: compl_col, compl_length and compl_pattern.
static int get_cmdline_compl_info(char *line, colnr_T curs_col)
{
- compl_pattern = xstrnsave(line, (size_t)curs_col);
- compl_patternlen = (size_t)curs_col;
- set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false);
+ compl_pattern = cbuf_to_string(line, (size_t)curs_col);
+ set_cmd_context(&compl_xp, compl_pattern.data,
+ (int)compl_pattern.size, curs_col, false);
if (compl_xp.xp_context == EXPAND_LUA) {
nlua_expand_pat(&compl_xp);
}
@@ -4247,7 +4262,7 @@ static int get_cmdline_compl_info(char *line, colnr_T curs_col)
// "pattern not found" message.
compl_col = curs_col;
} else {
- compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
+ compl_col = (int)(compl_xp.xp_pattern - compl_pattern.data);
}
compl_length = curs_col - compl_col;
@@ -4325,8 +4340,7 @@ static int get_userdefined_compl_info(colnr_T curs_col)
// it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum);
compl_length = curs_col - compl_col;
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
- compl_patternlen = (size_t)compl_length;
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -4351,8 +4365,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
}
// Need to obtain "line" again, it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum);
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
- compl_patternlen = (size_t)compl_length;
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -4537,19 +4550,19 @@ static int ins_compl_start(void)
ins_compl_fixRedoBufForLeader(NULL);
// Always add completion for the original text.
- xfree(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
- compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_orig_text = cbuf_to_string(line + compl_col, (size_t)compl_length);
save_orig_extmarks();
int flags = CP_ORIGINAL_TEXT;
if (p_ic) {
flags |= CP_ICASE;
}
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
+ if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size,
+ NULL, NULL, false, NULL, 0,
flags, false, NULL) != OK) {
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
return FAIL;
}
@@ -4783,7 +4796,7 @@ static unsigned quote_meta(char *dest, char *src, int len)
#if defined(EXITFREE)
void free_insexpand_stuff(void)
{
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
callback_free(&cfu_cb);
callback_free(&ofu_cb);
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index d47340505a..fe6892cc27 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -96,53 +96,51 @@ void win_redr_status(win_T *wp)
get_trans_bufname(wp->w_buffer);
char *p = NameBuff;
- int len = (int)strlen(p);
+ int plen = (int)strlen(p);
if ((bt_help(wp->w_buffer)
|| wp->w_p_pvw
|| bufIsChanged(wp->w_buffer)
|| wp->w_buffer->b_p_ro)
- && len < MAXPATHL - 1) {
- *(p + len++) = ' ';
+ && plen < MAXPATHL - 1) {
+ *(p + plen++) = ' ';
}
if (bt_help(wp->w_buffer)) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[Help]"));
}
if (wp->w_p_pvw) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[Preview]"));
}
if (bufIsChanged(wp->w_buffer)) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", "[+]");
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", "[+]");
}
if (wp->w_buffer->b_p_ro) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
- // len += (int)strlen(p + len); // dead assignment
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[RO]"));
}
+ (void)plen;
- int this_ru_col = MAX(ru_col - (Columns - stl_width), (stl_width + 1) / 2);
+ int n = (stl_width + 1) / 2;
+ int this_ru_col = ru_col - (Columns - stl_width);
+ this_ru_col = MAX(this_ru_col, n);
if (this_ru_col <= 1) {
p = "<"; // No room for file name!
- len = 1;
+ plen = 1;
} else {
int i;
// Count total number of display cells.
- int clen = (int)mb_string2cells(p);
+ plen = (int)mb_string2cells(p);
// Find first character that will fit.
// Going from start to end is much faster for DBCS.
- for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
+ for (i = 0; p[i] != NUL && plen >= this_ru_col - 1;
i += utfc_ptr2len(p + i)) {
- clen -= utf_ptr2cells(p + i);
+ plen -= utf_ptr2cells(p + i);
}
- len = clen;
if (i > 0) {
p = p + i - 1;
*p = '<';
- len++;
+ plen++;
}
}
@@ -152,16 +150,17 @@ void win_redr_status(win_T *wp)
int width = grid_line_puts(off, p, -1, attr);
grid_line_fill(off + width, off + this_ru_col, fillchar, attr);
- if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL)
- && this_ru_col - len > (int)strlen(NameBuff) + 1) {
- grid_line_puts(off + this_ru_col - (int)strlen(NameBuff) - 1, NameBuff, -1, attr);
+ int NameBufflen = get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL);
+ if (NameBufflen > 0 && this_ru_col - plen > NameBufflen + 1) {
+ grid_line_puts(off + this_ru_col - NameBufflen - 1, NameBuff, -1, attr);
}
win_redr_ruler(wp);
// Draw the 'showcmd' information if 'showcmdloc' == "statusline".
if (p_sc && *p_sloc == 's') {
- const int sc_width = MIN(10, this_ru_col - len - 2);
+ n = this_ru_col - plen - 2; // perform the calculation here so we only do it once
+ const int sc_width = MIN(10, n);
if (sc_width > 0) {
grid_line_puts(off + this_ru_col - sc_width - 1, showcmd_buf, sc_width, attr);
@@ -548,36 +547,40 @@ void win_redr_ruler(win_T *wp)
#define RULER_BUF_LEN 70
char buffer[RULER_BUF_LEN];
- // Some sprintfs return the length, some return a pointer.
- // To avoid portability problems we use strlen() here.
- vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
- (wp->w_buffer->b_ml.ml_flags &
- ML_EMPTY) ? 0 : (int64_t)wp->w_cursor.lnum);
- size_t len = strlen(buffer);
- col_print(buffer + len, RULER_BUF_LEN - len,
- empty_line ? 0 : (int)wp->w_cursor.col + 1,
- (int)virtcol + 1);
+ int bufferlen = vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
+ (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
+ ? 0
+ : (int64_t)wp->w_cursor.lnum);
+ bufferlen += col_print(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen,
+ empty_line ? 0 : (int)wp->w_cursor.col + 1,
+ (int)virtcol + 1);
// Add a "50%" if there is room for it.
// On the last line, don't print in the last column (scrolls the
// screen up on some terminals).
- int i = (int)strlen(buffer);
- get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
- int o = i + vim_strsize(buffer + i + 1);
+ char rel_pos[RULER_BUF_LEN];
+ int rel_poslen = get_rel_pos(wp, rel_pos, RULER_BUF_LEN);
+ int n1 = bufferlen + vim_strsize(rel_pos);
if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
- o++;
+ n1++;
}
+
+ int this_ru_col = ru_col - (Columns - width);
// Never use more than half the window/screen width, leave the other half
// for the filename.
- int this_ru_col = MAX(ru_col - (Columns - width), (width + 1) / 2);
- if (this_ru_col + o < width) {
- // Need at least 3 chars left for get_rel_pos() + NUL.
- while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) {
- i += (int)schar_get(buffer + i, fillchar);
- o++;
- }
- get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
+ int n2 = (width + 1) / 2;
+ this_ru_col = MAX(this_ru_col, n2);
+ if (this_ru_col + n1 < width) {
+ // need at least space for rel_pos + NUL
+ while (this_ru_col + n1 < width
+ && RULER_BUF_LEN > bufferlen + rel_poslen + 1) { // +1 for NUL
+ bufferlen += (int)schar_get(buffer + bufferlen, fillchar);
+ n1++;
+ }
+ bufferlen += vim_snprintf(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen,
+ "%s", rel_pos);
}
+ (void)bufferlen;
if (ui_has(kUIMessages) && !part_of_status) {
MAXSIZE_TEMP_ARRAY(content, 1);
@@ -595,11 +598,11 @@ void win_redr_ruler(win_T *wp)
did_show_ext_ruler = false;
}
// Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
+ for (n1 = 0, n2 = 0; buffer[n1] != NUL; n1 += utfc_ptr2len(buffer + n1)) {
+ n2 += utf_ptr2cells(buffer + n1);
+ if (this_ru_col + n2 > width) {
+ bufferlen = n1;
+ buffer[bufferlen] = NUL;
break;
}
}
@@ -1536,7 +1539,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// Store the position percentage in our temporary buffer.
// Note: We cannot store the value in `num` because
// `get_rel_pos` can return a named position. Ex: "Top"
- get_rel_pos(wp, buf_tmp, TMPLEN);
+ (void)get_rel_pos(wp, buf_tmp, TMPLEN);
str = buf_tmp;
break;
@@ -1564,7 +1567,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
+ if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN) > 0) {
str = buf_tmp;
}
break;
diff --git a/src/nvim/vterm/screen.c b/src/nvim/vterm/screen.c
index f24e47e543..c91c6fb84f 100644
--- a/src/nvim/vterm/screen.c
+++ b/src/nvim/vterm/screen.c
@@ -909,7 +909,7 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe
return 0;
}
- cell->schar = intcell->schar;
+ cell->schar = (intcell->schar == (uint32_t)-1) ? 0 : intcell->schar;
cell->attrs.bold = intcell->pen.bold;
cell->attrs.underline = intcell->pen.underline;
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index a16c6a88e3..fabd9be6d6 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -651,6 +651,11 @@ describe('nvim_create_user_command', function()
api.nvim_set_current_buf(bufnr)
command('Hello')
assert_alive()
+ eq(
+ 'Invalid buffer id: 1234',
+ pcall_err(api.nvim_buf_create_user_command, 1234, 'Hello', '', {})
+ )
+ assert_alive()
end)
it('can use a Lua complete function', function()
@@ -771,5 +776,9 @@ describe('nvim_del_user_command', function()
command('Hello')
api.nvim_buf_del_user_command(0, 'Hello')
matches('Not an editor command: Hello', pcall_err(command, 'Hello'))
+ eq('Invalid command (not found): Hello', pcall_err(api.nvim_buf_del_user_command, 0, 'Hello'))
+ eq('Invalid command (not found): Bye', pcall_err(api.nvim_buf_del_user_command, 0, 'Bye'))
+ eq('Invalid buffer id: 1234', pcall_err(api.nvim_buf_del_user_command, 1234, 'Hello'))
+ assert_alive()
end)
end)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 753da64522..406b5c3c16 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -66,6 +66,18 @@ describe(':checkhealth', function()
eq({}, getcompletion('', 'checkhealth'))
assert_alive()
end)
+
+ it('vim.g.health', function()
+ clear()
+ command("let g:health = {'style':'float'}")
+ command('checkhealth lsp')
+ eq(
+ 'editor',
+ exec_lua([[
+ return vim.api.nvim_win_get_config(0).relative
+ ]])
+ )
+ end)
end)
describe('vim.health', function()
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index b6de687af9..cc807ba555 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -435,6 +435,19 @@ describe(':terminal buffer', function()
]])
end)
+ it('handles unprintable chars', function()
+ local screen = Screen.new(50, 7)
+ feed 'i'
+ local chan = api.nvim_open_term(0, {})
+ api.nvim_chan_send(chan, '\239\187\191') -- '\xef\xbb\xbf'
+ screen:expect([[
+ {18:<feff>}^ |
+ |*5
+ {5:-- TERMINAL --} |
+ ]])
+ eq('\239\187\191', api.nvim_get_current_line())
+ end)
+
it("handles bell respecting 'belloff' and 'visualbell'", function()
local screen = Screen.new(50, 7)
local chan = api.nvim_open_term(0, {})
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua
index e38e58ff92..9f7fdf529f 100644
--- a/test/functional/treesitter/fold_spec.lua
+++ b/test/functional/treesitter/fold_spec.lua
@@ -5,6 +5,7 @@ local Screen = require('test.functional.ui.screen')
local clear = n.clear
local eq = t.eq
local insert = n.insert
+local write_file = t.write_file
local exec_lua = n.exec_lua
local command = n.command
local feed = n.feed
@@ -767,4 +768,78 @@ t2]])
]],
}
end)
+
+ it("doesn't call get_parser too often when parser is not available", function()
+ -- spy on vim.treesitter.get_parser() to keep track of how many times it is called
+ exec_lua(function()
+ _G.count = 0
+ vim.treesitter.get_parser = (function(wrapped)
+ return function(...)
+ _G.count = _G.count + 1
+ return wrapped(...)
+ end
+ end)(vim.treesitter.get_parser)
+ end)
+
+ insert(test_text)
+ command [[
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ eq(
+ 1,
+ exec_lua [[ return _G.count ]],
+ 'count should not be as high as the # of lines; actually only once for the buffer.'
+ )
+ end)
+
+ it('can detect a new parser and refresh folds accordingly', function()
+ write_file('test_fold_file.txt', test_text)
+ command [[
+ e test_fold_file.txt
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ -- reload buffer as c filetype to simulate new parser being found
+ feed('GA// vim: ft=c<Esc>')
+ command([[w | e]])
+
+ eq({
+ [1] = '>1',
+ [2] = '1',
+ [3] = '1',
+ [4] = '1',
+ [5] = '>2',
+ [6] = '2',
+ [7] = '2',
+ [8] = '1',
+ [9] = '1',
+ [10] = '>2',
+ [11] = '2',
+ [12] = '2',
+ [13] = '2',
+ [14] = '2',
+ [15] = '>3',
+ [16] = '3',
+ [17] = '3',
+ [18] = '2',
+ [19] = '1',
+ }, get_fold_levels())
+ end)
end)
diff --git a/test/functional/treesitter/inspect_tree_spec.lua b/test/functional/treesitter/inspect_tree_spec.lua
index 1f7d15cc96..47f3421cfe 100644
--- a/test/functional/treesitter/inspect_tree_spec.lua
+++ b/test/functional/treesitter/inspect_tree_spec.lua
@@ -120,14 +120,17 @@ describe('vim.treesitter.inspect_tree', function()
end)
it('updates source and tree buffer windows and closes them correctly', function()
+ local name = t.tmpname()
+ n.command('edit ' .. name)
insert([[
print()
]])
+ n.command('set filetype=lua | write')
-- setup two windows for the source buffer
exec_lua(function()
_G.source_win = vim.api.nvim_get_current_win()
- vim.api.nvim_open_win(0, false, {
+ _G.source_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
@@ -135,40 +138,44 @@ describe('vim.treesitter.inspect_tree', function()
-- setup three windows for the tree buffer
exec_lua(function()
- vim.treesitter.start(0, 'lua')
vim.treesitter.inspect_tree()
_G.tree_win = vim.api.nvim_get_current_win()
- _G.tree_win_copy_1 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
- _G.tree_win_copy_2 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win3 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
end)
- -- close original source window
- exec_lua('vim.api.nvim_win_close(source_win, false)')
+ -- close original source window without closing tree views
+ exec_lua('vim.api.nvim_set_current_win(source_win)')
+ feed(':quit<CR>')
+ eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win2)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win3)'))
-- navigates correctly to the remaining source buffer window
+ exec_lua('vim.api.nvim_set_current_win(tree_win)')
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close original tree window
exec_lua(function()
- vim.api.nvim_set_current_win(_G.tree_win_copy_1)
+ vim.api.nvim_set_current_win(_G.tree_win2)
vim.api.nvim_win_close(_G.tree_win, false)
end)
-- navigates correctly to the remaining source buffer window
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close source buffer window and all remaining tree windows
- t.pcall_err(exec_lua, 'vim.api.nvim_win_close(0, false)')
-
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_1)'))
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_2)'))
+ n.expect_exit(n.command, 'quit')
end)
end)
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index da0fb9849a..e364c473b7 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -744,6 +744,30 @@ describe('decorations providers', function()
]])
eq(2, exec_lua([[return _G.cnt]]))
end)
+
+ it('can do large changes to the marktree', function()
+ insert("line1 with a lot of text\nline2 with a lot of text")
+ setup_provider([[
+ function on_do(event, _, _, row)
+ if event == 'win' or (event == 'line' and row == 1) then
+ vim.api.nvim_buf_clear_namespace(0, ns1, 0, -1)
+ for i = 0,1 do
+ for j = 0,23 do
+ vim.api.nvim_buf_set_extmark(0, ns1, i, j, {hl_group='ErrorMsg', end_col = j+1})
+ end
+ end
+ end
+ end
+ ]])
+
+ -- Doesn't crash when modifying the marktree between line1 and line2
+ screen:expect([[
+ {2:line1 with a lot of text} |
+ {2:line2 with a lot of tex^t} |
+ {1:~ }|*5
+ |
+ ]])
+ end)
end)
local example_text = [[
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 685f700130..d890884eb5 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -672,7 +672,6 @@ func s:GetFilenameChecks() abort
\ 'samba': ['smb.conf'],
\ 'sas': ['file.sas'],
\ 'sass': ['file.sass'],
- \ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
\ 'scala': ['file.scala'],
\ 'scheme': ['file.scm', 'file.ss', 'file.sld', 'file.stsg', 'any/local/share/supertux2/config', '.lips_repl_history'],
@@ -691,6 +690,7 @@ func s:GetFilenameChecks() abort
\ '.ash_history', 'any/etc/neofetch/config.conf', '.xprofile', 'user-dirs.defaults', 'user-dirs.dirs',
\ 'makepkg.conf', '.makepkg.conf', 'file.mdd', '.env', '.envrc', 'devscripts.conf', '.devscripts', 'file.lo',
\ 'file.la', 'file.lai'],
+ \ 'shaderslang': ['file.slang'],
\ 'sieve': ['file.siv', 'file.sieve'],
\ 'sil': ['file.sil'],
\ 'simula': ['file.sim'],
@@ -2321,6 +2321,22 @@ func Test_cmd_file()
filetype off
endfunc
+func Test_sa_file()
+ filetype on
+
+ call writefile([';* XXX-a.sa: XXX for TI C6000 DSP *;', '.no_mdep'], 'Xfile.sa')
+ split Xfile.sa
+ call assert_equal('tiasm', &filetype)
+ bwipe!
+
+ call writefile(['-- comment'], 'Xfile.sa')
+ split Xfile.sa
+ call assert_equal('sather', &filetype)
+ bwipe!
+
+ filetype off
+endfunc
+
func Test_sig_file()
filetype on