diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/autoload/netrw.vim | 2 | ||||
-rw-r--r-- | runtime/doc/api.txt | 2 | ||||
-rw-r--r-- | runtime/doc/change.txt | 1 | ||||
-rw-r--r-- | runtime/doc/deprecated.txt | 1 | ||||
-rw-r--r-- | runtime/doc/lsp.txt | 23 | ||||
-rw-r--r-- | runtime/doc/motion.txt | 2 | ||||
-rw-r--r-- | runtime/doc/news.txt | 6 | ||||
-rw-r--r-- | runtime/doc/treesitter.txt | 23 | ||||
-rw-r--r-- | runtime/doc/visual.txt | 1 | ||||
-rw-r--r-- | runtime/ftplugin/kotlin.vim | 33 | ||||
-rw-r--r-- | runtime/indent/kotlin.vim | 60 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/api.lua | 5 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/api_keysets.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/filetype.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 15 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 178 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/_meta.lua | 3 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/languagetree.lua | 57 | ||||
-rw-r--r-- | runtime/queries/markdown/highlights.scm | 8 | ||||
-rw-r--r-- | runtime/queries/markdown_inline/highlights.scm | 8 | ||||
-rw-r--r-- | runtime/syntax/kotlin.vim | 157 |
21 files changed, 473 insertions, 114 deletions
diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index be170d8aec..2dd5cf45bf 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -2951,7 +2951,7 @@ fun! s:NetrwGetFile(readcmd, tfile, method) " to process this detection correctly. " call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>")) " call Decho("..did_filetype()=".did_filetype()) - setl ft= +" setl ft= " call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">") let iskkeep= &isk setl isk-=/ diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index ffd90ec3d7..f04fe9bb87 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3177,6 +3177,8 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* • noautocmd: If true then no buffer-related autocommand events such as |BufEnter|, |BufLeave| or |BufWinEnter| may fire from calling this function. + • fixed: If true when anchor is NW or SW, the float window + would be kept fixed even if the window would be truncated. Return: ~ Window handle, or 0 on error diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 894557818b..2c47421b02 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -196,6 +196,7 @@ gR Enter Virtual Replace mode: Each character you type *v_r* {Visual}r{char} Replace all selected characters by {char}. + CTRL-C will be inserted literally. *v_C* {Visual}["x]C Delete the highlighted lines [into register x] and diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 2e9312cf74..407d7ae9fb 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -141,6 +141,7 @@ LSP FUNCTIONS `progress` of |vim.lsp.client| - *vim.lsp.get_active_clients()* Use |vim.lsp.get_clients()| - *vim.lsp.for_each_buffer_client()* Use |vim.lsp.get_clients()| +- *vim.lsp.util.trim_empty_lines()* Use |vim.split()| with `trimempty` instead. TREESITTER FUNCTIONS - *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 5103cc223f..15292cd7cf 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1678,20 +1678,24 @@ convert_input_to_markdown_lines({input}, {contents}) window for `textDocument/hover`, for parsing the result of `textDocument/signatureHelp`, and potentially others. + Note that if the input is of type `MarkupContent` and its kind is + `plaintext`, then the corresponding value is returned without further + modifications. + Parameters: ~ • {input} (`MarkedString` | `MarkedString[]` | `MarkupContent`) • {contents} (table|nil) List of strings to extend with converted lines. Defaults to {}. Return: ~ - (table) {contents} extended with lines of converted markdown. + string[] extended with lines of converted markdown. See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover *vim.lsp.util.convert_signature_help_to_markdown_lines()* convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers}) - Converts `textDocument/SignatureHelp` response to markdown lines. + Converts `textDocument/signatureHelp` response to markdown lines. Parameters: ~ • {signature_help} (table) Response of `textDocument/SignatureHelp` @@ -1908,10 +1912,6 @@ open_floating_preview({contents}, {syntax}, {opts}) height when wrap is enabled • max_width: (integer) maximal width of floating window • max_height: (integer) maximal height of floating window - • pad_top: (integer) number of lines to pad contents at - top - • pad_bottom: (integer) number of lines to pad contents at - bottom • focus_id: (string) if a popup with this id is opened, then focus it • close_events: (table) list of events that closes the @@ -2005,8 +2005,6 @@ stylize_markdown({bufnr}, {contents}, {opts}) • wrap_at character to wrap at for computing height • max_width maximal width of floating window • max_height maximal height of floating window - • pad_top number of lines to pad contents at top - • pad_bottom number of lines to pad contents at bottom • separator insert separator after code block Return: ~ @@ -2035,15 +2033,6 @@ text_document_completion_list_to_complete_items({result}, {prefix}) See also: ~ • complete-items -trim_empty_lines({lines}) *vim.lsp.util.trim_empty_lines()* - Removes empty lines from the beginning and end. - - Parameters: ~ - • {lines} (table) list of lines to trim - - Return: ~ - (table) trimmed list of lines - *vim.lsp.util.try_trim_markdown_code_blocks()* try_trim_markdown_code_blocks({lines}) Accepts markdown lines and tries to reduce them to a filetype if they diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 05244cde91..dc92601bfc 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -660,6 +660,7 @@ i` *v_i`* *i`* Special case: With a count of 2 the quotes are included, but no extra white space as with a"/a'/a`. + *o_object-select* When used after an operator: For non-block objects: For the "a" commands: The operator applies to the object and the white @@ -675,6 +676,7 @@ For a block object: the surrounding braces are excluded. For the "a" commands, the braces are included. + *v_object-select* When used in Visual mode: When start and end of the Visual area are the same (just after typing "v"): One object is selected, the same as for using an operator. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 7c971097fb..240eb152d7 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -232,6 +232,11 @@ The following changes to existing APIs or features add new behavior. In addition, |nvim_buf_get_extmarks()| has gained an "overlap" option to return such ranges even if they started before the specified position. +• LSP hover and signature help now use Treesitter for highlighting of Markdown + content. + Note that syntax highlighting of code examples requires a matching parser + and may be affected by custom queries. + ============================================================================== REMOVED FEATURES *news-removed* @@ -267,6 +272,7 @@ release. - |vim.lsp.util.get_progress_messages()| Use |vim.lsp.status()| instead. - |vim.lsp.get_active_clients()| Use |vim.lsp.get_clients()| instead. - |vim.lsp.for_each_buffer_client()| Use |vim.lsp.get_clients()| instead. + - |vim.lsp.util.trim_empty_lines()| Use |vim.split()| with `trimempty` instead. • `vim.loop` has been renamed to `vim.uv`. diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index e1e280c4db..10f689bad7 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -1101,10 +1101,12 @@ LanguageTree:for_each_tree({fn}) *LanguageTree:for_each_tree()* • {fn} fun(tree: TSTree, ltree: LanguageTree) LanguageTree:included_regions() *LanguageTree:included_regions()* - Gets the set of included regions + Gets the set of included regions managed by this LanguageTree . This can be different from the regions set by injection query, because a + partial |LanguageTree:parse()| drops the regions outside the requested + range. Return: ~ - Range6[][] + table<integer, Range6[]> LanguageTree:invalidate({reload}) *LanguageTree:invalidate()* Invalidates this parser and all its children @@ -1113,10 +1115,12 @@ LanguageTree:invalidate({reload}) *LanguageTree:invalidate()* • {reload} (boolean|nil) LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()* - Determines whether this tree is valid. If the tree is invalid, call `parse()` . This will return the updated tree. + Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest state of the + source. If invalid, user should call |LanguageTree:parse()|. Parameters: ~ - • {exclude_children} (boolean|nil) + • {exclude_children} (boolean|nil) whether to ignore the validity of + children (default `false`) Return: ~ (boolean) @@ -1165,7 +1169,7 @@ LanguageTree:parse({range}) *LanguageTree:parse()* injections). Return: ~ - TSTree[] + table<integer, TSTree> *LanguageTree:register_cbs()* LanguageTree:register_cbs({cbs}, {recursive}) @@ -1207,7 +1211,12 @@ LanguageTree:tree_for_range({range}, {opts}) TSTree|nil LanguageTree:trees() *LanguageTree:trees()* - Returns all trees this language tree contains. Does not include child - languages. + Returns all trees of the regions parsed by this parser. Does not include + child languages. The result is list-like if + • this LanguageTree is the root, in which case the result is empty or a singleton list; or + • the root LanguageTree is fully parsed. + + Return: ~ + table<integer, TSTree> vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index 6ca486e8cf..0d1ea937c0 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -169,6 +169,7 @@ If you want to highlight exactly the same area as the last time, you can use CTRL-C In Visual mode: Stop Visual mode. When insert mode is pending (the mode message shows "-- (insert) VISUAL --"), it is also stopped. + On MS-Windows, you may need to press CTRL-Break. ============================================================================== 3. Changing the Visual area *visual-change* diff --git a/runtime/ftplugin/kotlin.vim b/runtime/ftplugin/kotlin.vim new file mode 100644 index 0000000000..b21de603ea --- /dev/null +++ b/runtime/ftplugin/kotlin.vim @@ -0,0 +1,33 @@ +" Vim filetype plugin file +" Language: Kotlin +" Maintainer: Alexander Udalov +" URL: https://github.com/udalov/kotlin-vim +" Last Change: 7 November 2021 +" 2023 Sep 17 by Vim Project (browsefilter) + +if exists('b:did_ftplugin') | finish | endif +let b:did_ftplugin = 1 + +let s:save_cpo = &cpo +set cpo&vim + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s + +setlocal formatoptions-=t formatoptions+=croqnl +silent! setlocal formatoptions+=j + +setlocal includeexpr=substitute(v:fname,'\\.','/','g') +setlocal suffixesadd=.kt + +let b:undo_ftplugin = "setlocal comments< commentstring< ". + \ "formatoptions< includeexpr< suffixesadd<" + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Kotlin Source Files (*.kt, *kts)\t*.kt;*.kts\n" . + \ "All Files (*.*)\t*.*\n" + let b:undo_ftplugin .= " | unlet! b:browsefilter" +endif + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/indent/kotlin.vim b/runtime/indent/kotlin.vim new file mode 100644 index 0000000000..590a5074d1 --- /dev/null +++ b/runtime/indent/kotlin.vim @@ -0,0 +1,60 @@ +" Vim indent file +" Language: Kotlin +" Maintainer: Alexander Udalov +" URL: https://github.com/udalov/kotlin-vim +" Last Change: 7 November 2021 +" 2023 Sep 17 by Vim Project (undo_indent) + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +setlocal cinoptions& cinoptions+=j1,L0 +setlocal indentexpr=GetKotlinIndent() +setlocal indentkeys=0},0),!^F,o,O,e,<CR> +setlocal autoindent " TODO ? + +let b:undo_indent = "setlocal autoindent< cinoptions< indentexpr< indentkeys<" + +" TODO teach it to count bracket balance, etc. +function! GetKotlinIndent() + if v:lnum == 0 + return 0 + endif + + let prev_num = prevnonblank(v:lnum - 1) + let prev = getline(prev_num) + let prev_indent = indent(prev_num) + let cur = getline(v:lnum) + + if cur =~ '^\s*\*' + return cindent(v:lnum) + endif + + if prev =~ '^\s*\*/' + let st = prev + while st > 1 + if getline(st) =~ '^\s*/\*' + break + endif + let st = st - 1 + endwhile + return indent(st) + endif + + let prev_open_paren = prev =~ '^.*(\s*$' + let cur_close_paren = cur =~ '^\s*).*$' + let prev_open_brace = prev =~ '^.*\({\|->\)\s*$' + let cur_close_brace = cur =~ '^\s*}.*$' + + if prev_open_paren && !cur_close_paren || prev_open_brace && !cur_close_brace + return prev_indent + shiftwidth() + endif + + if cur_close_paren && !prev_open_paren || cur_close_brace && !prev_open_brace + return prev_indent - shiftwidth() + endif + + return prev_indent +endfunction diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 6573c68493..41bb5636b8 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -80,6 +80,9 @@ function vim.api.nvim__id_float(flt) end function vim.api.nvim__inspect_cell(grid, row, col) end --- @private +function vim.api.nvim__invalidate_glyph_cache() end + +--- @private --- @return any[] function vim.api.nvim__runtime_inspect() end @@ -1608,6 +1611,8 @@ function vim.api.nvim_open_term(buffer, opts) end --- • noautocmd: If true then no buffer-related autocommand --- events such as `BufEnter`, `BufLeave` or `BufWinEnter` may --- fire from calling this function. +--- • fixed: If true when anchor is NW or SW, the float window +--- would be kept fixed even if the window would be truncated. --- @return integer function vim.api.nvim_open_win(buffer, enter, config) end diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 4d08563ce2..b249f6629f 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -112,6 +112,7 @@ error('Cannot require a meta file') --- @field footer_pos? string --- @field style? string --- @field noautocmd? boolean +--- @field fixed? boolean --- @class vim.api.keyset.get_autocmds --- @field event? any diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index d847c28f5c..c7f025f9e7 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -592,6 +592,7 @@ local extension = { ly = 'lilypond', ily = 'lilypond', liquid = 'liquid', + liq = 'liquidsoap', cl = 'lisp', L = 'lisp', lisp = 'lisp', diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 4ea3dde81c..d43d9a7cfa 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -371,15 +371,21 @@ function M.hover(_, result, ctx, config) end return end - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - markdown_lines = util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then + local format = 'markdown' + local contents ---@type string[] + if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then + format = 'plaintext' + contents = vim.split(result.contents.value or '', '\n', { trimempty = true }) + else + contents = util.convert_input_to_markdown_lines(result.contents) + end + if vim.tbl_isempty(contents) then if config.silent ~= true then vim.notify('No information available') end return end - return util.open_floating_preview(markdown_lines, 'markdown', config) + return util.open_floating_preview(contents, format, config) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover @@ -470,7 +476,6 @@ function M.signature_help(_, result, ctx, config) vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') local ft = vim.bo[ctx.bufnr].filetype local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) - lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then if config.silent ~= true then print('No signature help available') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 54721865b7..988057f5f9 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -102,7 +102,7 @@ end local function split_lines(value) value = string.gsub(value, '\r\n?', '\n') - return split(value, '\n', { plain = true }) + return split(value, '\n', { plain = true, trimempty = true }) end local function create_window_without_focus() @@ -877,9 +877,12 @@ end --- window for `textDocument/hover`, for parsing the result of --- `textDocument/signatureHelp`, and potentially others. --- +--- Note that if the input is of type `MarkupContent` and its kind is `plaintext`, +--- then the corresponding value is returned without further modifications. +--- ---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) ---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}. ----@return table {contents} extended with lines of converted markdown. +---@return string[] extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) contents = contents or {} @@ -887,27 +890,13 @@ function M.convert_input_to_markdown_lines(input, contents) if type(input) == 'string' then list_extend(contents, split_lines(input)) else - assert(type(input) == 'table', 'Expected a table for Hover.contents') + assert(type(input) == 'table', 'Expected a table for LSP input') -- MarkupContent if input.kind then - -- The kind can be either plaintext or markdown. - -- If it's plaintext, then wrap it in a <text></text> block - - -- Some servers send input.value as empty, so let's ignore this :( local value = input.value or '' - - if input.kind == 'plaintext' then - -- wrap this in a <text></text> block so that stylize_markdown - -- can properly process it as plaintext - value = string.format('<text>\n%s\n</text>', value) - end - - -- assert(type(value) == 'string') list_extend(contents, split_lines(value)) -- MarkupString variation 2 elseif input.language then - -- Some servers send input.value as empty, so let's ignore this :( - -- assert(type(input.value) == 'string') table.insert(contents, '```' .. input.language) list_extend(contents, split_lines(input.value or '')) table.insert(contents, '```') @@ -925,7 +914,7 @@ function M.convert_input_to_markdown_lines(input, contents) return contents end ---- Converts `textDocument/SignatureHelp` response to markdown lines. +--- Converts `textDocument/signatureHelp` response to markdown lines. --- ---@param signature_help table Response of `textDocument/SignatureHelp` ---@param ft string|nil filetype that will be use as the `lang` for the label markdown code block @@ -955,10 +944,10 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end local label = signature.label if ft then - -- wrap inside a code block so stylize_markdown can render it properly + -- wrap inside a code block for proper rendering label = ('```%s\n%s\n```'):format(ft, label) end - list_extend(contents, split(label, '\n', { plain = true })) + list_extend(contents, split(label, '\n', { plain = true, trimempty = true })) if signature.documentation then -- if LSP returns plain string, we treat it as plaintext. This avoids -- special characters like underscore or similar from being interpreted @@ -1223,7 +1212,7 @@ function M.preview_location(location, opts) local syntax = vim.bo[bufnr].syntax if syntax == '' then -- When no syntax is set, we use filetype as fallback. This might not result - -- in a valid syntax definition. See also ft detection in stylize_markdown. + -- in a valid syntax definition. -- An empty syntax is more common now with TreeSitter, since TS disables syntax. syntax = vim.bo[bufnr].filetype end @@ -1240,36 +1229,65 @@ local function find_window_by_var(name, value) end end ---- Trims empty lines from input and pad top and bottom with empty lines ---- ----@param contents table of lines to trim and pad ----@param opts table with optional fields ---- - pad_top number of lines to pad contents at top (default 0) ---- - pad_bottom number of lines to pad contents at bottom (default 0) ----@return table table of trimmed and padded lines -function M._trim(contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) - opts = opts or {} - contents = M.trim_empty_lines(contents) - if opts.pad_top then - for _ = 1, opts.pad_top do - table.insert(contents, 1, '') +---Returns true if the line is empty or only contains whitespace. +---@param line string +---@return boolean +local function is_blank_line(line) + return line and line:match('^%s*$') +end + +---Returns true if the line corresponds to a Markdown thematic break. +---@param line string +---@return boolean +local function is_separator_line(line) + return line and line:match('^ ? ? ?%-%-%-+%s*$') +end + +---Replaces separator lines by the given divider and removing surrounding blank lines. +---@param contents string[] +---@param divider string +---@return string[] +local function replace_separators(contents, divider) + local trimmed = {} + local l = 1 + while l <= #contents do + local line = contents[l] + if is_separator_line(line) then + if l > 1 and is_blank_line(contents[l - 1]) then + table.remove(trimmed) + end + table.insert(trimmed, divider) + if is_blank_line(contents[l + 1]) then + l = l + 1 + end + else + table.insert(trimmed, line) end + l = l + 1 end - if opts.pad_bottom then - for _ = 1, opts.pad_bottom do - table.insert(contents, '') + + return trimmed +end + +---Collapses successive blank lines in the input table into a single one. +---@param contents string[] +---@return string[] +local function collapse_blank_lines(contents) + local collapsed = {} + local l = 1 + while l <= #contents do + local line = contents[l] + if is_blank_line(line) then + while is_blank_line(contents[l + 1]) do + l = l + 1 + end end + table.insert(collapsed, line) + l = l + 1 end - return contents + return collapsed end ---- Generates a table mapping markdown code block lang to vim syntax, ---- based on g:markdown_fenced_languages ----@return table table of lang -> syntax mappings local function get_markdown_fences() local fences = {} for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do @@ -1297,8 +1315,6 @@ end --- - wrap_at character to wrap at for computing height --- - max_width maximal width of floating window --- - max_height maximal height of floating window ---- - pad_top number of lines to pad contents at top ---- - pad_bottom number of lines to pad contents at bottom --- - separator insert separator after code block ---@return table stripped content function M.stylize_markdown(bufnr, contents, opts) @@ -1335,7 +1351,7 @@ function M.stylize_markdown(bufnr, contents, opts) end -- Clean up - contents = M._trim(contents, opts) + contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) local stripped = {} local highlights = {} @@ -1484,6 +1500,45 @@ function M.stylize_markdown(bufnr, contents, opts) return stripped end +--- @class lsp.util.NormalizeMarkdownOptions +--- @field width integer Thematic breaks are expanded to this size. Defaults to 80. + +--- Normalizes Markdown input to a canonical form. +--- +--- The returned Markdown adheres to the GitHub Flavored Markdown (GFM) +--- specification. +--- +--- The following transformations are made: +--- +--- 1. Carriage returns ('\r') and empty lines at the beginning and end are removed +--- 2. Successive empty lines are collapsed into a single empty line +--- 3. Thematic breaks are expanded to the given width +--- +---@private +---@param contents string[] +---@param opts? lsp.util.NormalizeMarkdownOptions +---@return string[] table of lines containing normalized Markdown +---@see https://github.github.com/gfm +function M._normalize_markdown(contents, opts) + validate({ + contents = { contents, 't' }, + opts = { opts, 't', true }, + }) + opts = opts or {} + + -- 1. Carriage returns are removed + contents = vim.split(table.concat(contents, '\n'):gsub('\r', ''), '\n', { trimempty = true }) + + -- 2. Successive empty lines are collapsed into a single empty line + contents = collapse_blank_lines(contents) + + -- 3. Thematic breaks are expanded to the given width + local divider = string.rep('─', opts.width or 80) + contents = replace_separators(contents, divider) + + return contents +end + --- Closes the preview window --- ---@param winnr integer window id of preview window @@ -1620,8 +1675,6 @@ end --- - wrap_at: (integer) character to wrap at for computing height when wrap is enabled --- - max_width: (integer) maximal width of floating window --- - max_height: (integer) maximal height of floating window ---- - pad_top: (integer) number of lines to pad contents at top ---- - pad_bottom: (integer) number of lines to pad contents at bottom --- - focus_id: (string) if a popup with this id is opened, then focus it --- - close_events: (table) list of events that closes the floating window --- - focusable: (boolean, default true) Make float focusable @@ -1629,8 +1682,7 @@ end --- is also `true`, focus an existing floating window with the same --- {focus_id} ---@return integer bufnr of newly created float window ----@return integer winid of newly created float window ----preview window +---@return integer winid of newly created float window preview window function M.open_floating_preview(contents, syntax, opts) validate({ contents = { contents, 't' }, @@ -1639,7 +1691,6 @@ function M.open_floating_preview(contents, syntax, opts) }) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default - opts.stylize_markdown = opts.stylize_markdown ~= false and vim.g.syntax_on ~= nil opts.focus = opts.focus ~= false opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' } @@ -1671,16 +1722,21 @@ function M.open_floating_preview(contents, syntax, opts) api.nvim_win_close(existing_float, true) end + -- Create the buffer local floating_bufnr = api.nvim_create_buf(false, true) - local do_stylize = syntax == 'markdown' and opts.stylize_markdown - - -- Clean up input: trim empty lines from the end, pad - contents = M._trim(contents, opts) + -- Set up the contents, using treesitter for markdown + local do_stylize = syntax == 'markdown' and vim.g.syntax_on ~= nil if do_stylize then - -- applies the syntax and sets the lines to the buffer - contents = M.stylize_markdown(floating_bufnr, contents, opts) + local width = M._make_floating_popup_size(contents, opts) + contents = M._normalize_markdown(contents, { width = width }) + vim.bo[floating_bufnr].filetype = 'markdown' + vim.treesitter.start(floating_bufnr) + api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents) else + -- Clean up input: trim empty lines + contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) + if syntax then vim.bo[floating_bufnr].syntax = syntax end @@ -1697,9 +1753,9 @@ function M.open_floating_preview(contents, syntax, opts) local float_option = M.make_floating_popup_options(width, height, opts) local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + if do_stylize then vim.wo[floating_winnr].conceallevel = 2 - vim.wo[floating_winnr].concealcursor = 'n' end -- disable folding vim.wo[floating_winnr].foldenable = false @@ -1708,6 +1764,7 @@ function M.open_floating_preview(contents, syntax, opts) vim.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' + api.nvim_buf_set_keymap( floating_bufnr, 'n', @@ -1908,6 +1965,7 @@ function M.symbols_to_items(symbols, bufnr) end --- Removes empty lines from the beginning and end. +---@deprecated use `vim.split()` with `trimempty` instead ---@param lines table list of lines to trim ---@return table trimmed list of lines function M.trim_empty_lines(lines) diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua index 9a94f12c16..d01b7be3b0 100644 --- a/runtime/lua/vim/treesitter/_meta.lua +++ b/runtime/lua/vim/treesitter/_meta.lua @@ -50,7 +50,8 @@ function TSNode:_rawquery(query, captures, start, end_, opts) end ---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string) ---@class TSParser ----@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean?): TSTree, integer[] +---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: true): TSTree, Range6[] +---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: false|nil): TSTree, Range4[] ---@field reset fun(self: TSParser) ---@field included_ranges fun(self: TSParser, include_bytes: boolean?): integer[] ---@field set_included_ranges fun(self: TSParser, ranges: (Range6|TSNode)[]) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index b555ee231b..f931291ed7 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -78,13 +78,14 @@ local TSCallbackNames = { ---@field private _opts table Options ---@field private _parser TSParser Parser for language ---@field private _has_regions boolean ----@field private _regions Range6[][]? +---@field private _regions table<integer, Range6[]>? ---List of regions this tree should manage and parse. If nil then regions are ---taken from _trees. This is mostly a short-lived cache for included_regions() ---@field private _lang string Language name ---@field private _parent_lang? string Parent language name ---@field private _source (integer|string) Buffer or string to parse ----@field private _trees TSTree[] Reference to parsed tree (one for each language) +---@field private _trees table<integer, TSTree> Reference to parsed tree (one for each language). +---Each key is the index of region, which is synced with _regions and _valid. ---@field private _valid boolean|table<integer,boolean> If the parsed tree is valid ---@field private _logger? fun(logtype: string, msg: string) ---@field private _logfile? file* @@ -211,7 +212,7 @@ function LanguageTree:_log(...) end local info = debug.getinfo(2, 'nl') - local nregions = #self:included_regions() + local nregions = vim.tbl_count(self:included_regions()) local prefix = string.format('%s:%d: (#regions=%d) ', info.name or '???', info.currentline or 0, nregions) @@ -244,8 +245,13 @@ function LanguageTree:invalidate(reload) end end ---- Returns all trees this language tree contains. +--- Returns all trees of the regions parsed by this parser. --- Does not include child languages. +--- The result is list-like if +--- * this LanguageTree is the root, in which case the result is empty or a singleton list; or +--- * the root LanguageTree is fully parsed. +--- +---@return table<integer, TSTree> function LanguageTree:trees() return self._trees end @@ -255,16 +261,15 @@ function LanguageTree:lang() return self._lang end ---- Determines whether this tree is valid. ---- If the tree is invalid, call `parse()`. ---- This will return the updated tree. ----@param exclude_children boolean|nil +--- Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest +--- state of the source. If invalid, user should call |LanguageTree:parse()|. +---@param exclude_children boolean|nil whether to ignore the validity of children (default `false`) ---@return boolean function LanguageTree:is_valid(exclude_children) local valid = self._valid if type(valid) == 'table' then - for i = 1, #self:included_regions() do + for i, _ in pairs(self:included_regions()) do if not valid[i] then return false end @@ -328,7 +333,7 @@ end --- @private --- @param range boolean|Range? ---- @return integer[] changes +--- @return Range6[] changes --- @return integer no_regions_parsed --- @return number total_parse_time function LanguageTree:_parse_regions(range) @@ -370,7 +375,7 @@ function LanguageTree:_add_injections() local seen_langs = {} ---@type table<string,boolean> local query_time, injections_by_lang = tcall(self._get_injections, self) - for lang, injection_ranges in pairs(injections_by_lang) do + for lang, injection_regions in pairs(injections_by_lang) do local has_lang = pcall(language.add, lang) -- Child language trees should just be ignored if not found, since @@ -383,7 +388,7 @@ function LanguageTree:_add_injections() child = self:add_child(lang) end - child:set_included_regions(injection_ranges) + child:set_included_regions(injection_regions) seen_langs[lang] = true end end @@ -408,14 +413,14 @@ end --- Set to `true` to run a complete parse of the source (Note: Can be slow!) --- Set to `false|nil` to only parse regions with empty ranges (typically --- only the root tree without injections). ---- @return TSTree[] +--- @return table<integer, TSTree> function LanguageTree:parse(range) if self:is_valid() then self:_log('valid') return self._trees end - local changes --- @type Range6? + local changes --- @type Range6[]? -- Collect some stats local no_regions_parsed = 0 @@ -565,7 +570,7 @@ function LanguageTree:_iter_regions(fn) local all_valid = true - for i, region in ipairs(self:included_regions()) do + for i, region in pairs(self:included_regions()) do if was_valid or self._valid[i] then self._valid[i] = fn(i, region) if not self._valid[i] then @@ -601,7 +606,7 @@ end --- nodes, which is useful for templating languages like ERB and EJS. --- ---@private ----@param new_regions Range6[][] List of regions this tree should manage and parse. +---@param new_regions (Range4|Range6|TSNode)[][] List of regions this tree should manage and parse. function LanguageTree:set_included_regions(new_regions) self._has_regions = true @@ -609,16 +614,20 @@ function LanguageTree:set_included_regions(new_regions) for _, region in ipairs(new_regions) do for i, range in ipairs(region) do if type(range) == 'table' and #range == 4 then - region[i] = Range.add_bytes(self._source, range) + region[i] = Range.add_bytes(self._source, range --[[@as Range4]]) elseif type(range) == 'userdata' then region[i] = { range:range(true) } end end end + -- included_regions is not guaranteed to be list-like, but this is still sound, i.e. if + -- new_regions is different from included_regions, then outdated regions in included_regions are + -- invalidated. For example, if included_regions = new_regions ++ hole ++ outdated_regions, then + -- outdated_regions is invalidated by _iter_regions in else branch. if #self:included_regions() ~= #new_regions then -- TODO(lewis6991): inefficient; invalidate trees incrementally - for _, t in ipairs(self._trees) do + for _, t in pairs(self._trees) do self:_do_callback('changedtree', t:included_ranges(true), t) end self._trees = {} @@ -632,20 +641,22 @@ function LanguageTree:set_included_regions(new_regions) self._regions = new_regions end ----Gets the set of included regions ----@return Range6[][] +---Gets the set of included regions managed by this LanguageTree. This can be different from the +---regions set by injection query, because a partial |LanguageTree:parse()| drops the regions +---outside the requested range. +---@return table<integer, Range6[]> function LanguageTree:included_regions() if self._regions then return self._regions end - if not self._has_regions or #self._trees == 0 then + if not self._has_regions or next(self._trees) == nil then -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} return { {} } end local regions = {} ---@type Range6[][] - for i, _ in ipairs(self._trees) do + for i, _ in pairs(self._trees) do regions[i] = self._trees[i]:included_ranges(true) end @@ -801,7 +812,7 @@ local function combine_regions(regions) return result end ---- Gets language injection points by language. +--- Gets language injection regions by language. --- --- This is where most of the injection processing occurs. --- diff --git a/runtime/queries/markdown/highlights.scm b/runtime/queries/markdown/highlights.scm index e78d233cc6..2cc5546bac 100644 --- a/runtime/queries/markdown/highlights.scm +++ b/runtime/queries/markdown/highlights.scm @@ -61,3 +61,11 @@ ] @string.escape (inline) @spell + +;; Conceal backticks +(fenced_code_block + (fenced_code_block_delimiter) @conceal + (#set! conceal "")) +(fenced_code_block + (info_string (language) @conceal + (#set! conceal ""))) diff --git a/runtime/queries/markdown_inline/highlights.scm b/runtime/queries/markdown_inline/highlights.scm index cd5da530d7..c75da478af 100644 --- a/runtime/queries/markdown_inline/highlights.scm +++ b/runtime/queries/markdown_inline/highlights.scm @@ -92,3 +92,11 @@ "]" ] @conceal (#set! conceal "")) + +;; Replace common HTML entities. +((entity_reference) @conceal (#eq? @conceal " ") (#set! conceal "")) +((entity_reference) @conceal (#eq? @conceal "<") (#set! conceal "<")) +((entity_reference) @conceal (#eq? @conceal ">") (#set! conceal ">")) +((entity_reference) @conceal (#eq? @conceal "&") (#set! conceal "&")) +((entity_reference) @conceal (#eq? @conceal """) (#set! conceal "\"")) +((entity_reference) @conceal (#any-of? @conceal " " " ") (#set! conceal " ")) diff --git a/runtime/syntax/kotlin.vim b/runtime/syntax/kotlin.vim new file mode 100644 index 0000000000..9b85b8ef5c --- /dev/null +++ b/runtime/syntax/kotlin.vim @@ -0,0 +1,157 @@ +" Vim syntax file +" Language: Kotlin +" Maintainer: Alexander Udalov +" URL: https://github.com/udalov/kotlin-vim +" Last Change: 30 December 2022 + +if exists('b:current_syntax') + finish +endif + +syn keyword ktStatement break continue return +syn keyword ktConditional if else when +syn keyword ktRepeat do for while +syn keyword ktOperator in is by +syn keyword ktKeyword get set out super this where +syn keyword ktException try catch finally throw + +syn keyword ktInclude import package + +" Generated stdlib class names {{{ +" The following is generated by https://github.com/udalov/kotlin-vim/blob/master/extra/generate-stdlib-class-names.main.kts +syn keyword ktType AbstractCollection AbstractCoroutineContextElement AbstractCoroutineContextKey AbstractDoubleTimeSource AbstractIterator AbstractList AbstractLongTimeSource +syn keyword ktType AbstractMap AbstractMutableCollection AbstractMutableList AbstractMutableMap AbstractMutableSet AbstractSet AccessDeniedException Accessor Annotation +syn keyword ktType AnnotationRetention AnnotationTarget Any Appendable ArithmeticException Array ArrayDeque ArrayList AssertionError Boolean BooleanArray BooleanIterator +syn keyword ktType BuilderInference Byte ByteArray ByteIterator CName CallsInPlace CancellationException Char CharArray CharCategory CharDirectionality CharIterator CharProgression +syn keyword ktType CharRange CharSequence CharacterCodingException Charsets ClassCastException Cloneable ClosedFloatingPointRange ClosedRange Collection Comparable +syn keyword ktType ComparableTimeMark Comparator ConcurrentModificationException ConditionalEffect ContextFunctionTypeParams Continuation ContinuationInterceptor ContractBuilder +syn keyword ktType CopyActionContext CopyActionResult CoroutineContext DeepRecursiveFunction DeepRecursiveScope Delegates Deprecated DeprecatedSinceKotlin DeprecationLevel +syn keyword ktType Destructured Double DoubleArray DoubleIterator DslMarker Duration DurationUnit Effect Element EmptyCoroutineContext Entry Enum EnumEntries Error Exception +syn keyword ktType ExperimentalContracts ExperimentalJsExport ExperimentalMultiplatform ExperimentalObjCName ExperimentalObjCRefinement ExperimentalPathApi ExperimentalStdlibApi +syn keyword ktType ExperimentalSubclassOptIn ExperimentalTime ExperimentalTypeInference ExperimentalUnsignedTypes ExtensionFunctionType FileAlreadyExistsException +syn keyword ktType FileSystemException FileTreeWalk FileVisitorBuilder FileWalkDirection Float FloatArray FloatIterator FreezingIsDeprecated Function Function0 Function1 Function10 +syn keyword ktType Function11 Function12 Function13 Function14 Function15 Function16 Function17 Function18 Function19 Function2 Function20 Function21 Function22 Function3 Function4 +syn keyword ktType Function5 Function6 Function7 Function8 Function9 FunctionN Getter Grouping HashMap HashSet HiddenFromObjC HidesFromObjC Ignore IllegalArgumentException +syn keyword ktType IllegalStateException IndexOutOfBoundsException IndexedValue Int IntArray IntIterator IntProgression IntRange InvocationKind Iterable Iterator JsExport JsName +syn keyword ktType JvmDefault JvmDefaultWithCompatibility JvmDefaultWithoutCompatibility JvmField JvmInline JvmMultifileClass JvmName JvmOverloads JvmRecord JvmSerializableLambda +syn keyword ktType JvmStatic JvmSuppressWildcards JvmSynthetic JvmWildcard KAnnotatedElement KCallable KClass KClassifier KDeclarationContainer KFunction KMutableProperty +syn keyword ktType KMutableProperty0 KMutableProperty1 KMutableProperty2 KParameter KProperty KProperty0 KProperty1 KProperty2 KType KTypeParameter KTypeProjection KVariance +syn keyword ktType KVisibility Key Kind KotlinNullPointerException KotlinReflectionNotSupportedError KotlinVersion Lazy LazyThreadSafetyMode Level LinkedHashMap LinkedHashSet List +syn keyword ktType ListIterator Long LongArray LongIterator LongProgression LongRange Map MatchGroup MatchGroupCollection MatchNamedGroupCollection MatchResult Metadata Monotonic +syn keyword ktType MustBeDocumented MutableCollection MutableEntry MutableIterable MutableIterator MutableList MutableListIterator MutableMap MutableSet NoSuchElementException +syn keyword ktType NoSuchFileException NoWhenBranchMatchedException NotImplementedError Nothing NullPointerException Number NumberFormatException ObjCName ObservableProperty +syn keyword ktType OnErrorAction OnErrorResult OpenEndRange OptIn OptionalExpectation OverloadResolutionByLambdaReturnType Pair ParameterName PathWalkOption +syn keyword ktType PropertyDelegateProvider PublishedApi PurelyImplements Random RandomAccess ReadOnlyProperty ReadWriteProperty RefinesInSwift Regex RegexOption Repeatable +syn keyword ktType ReplaceWith RequiresOptIn RestrictsSuspension Result Retention Returns ReturnsNotNull RuntimeException Sequence SequenceScope Set Setter SharedImmutable Short +syn keyword ktType ShortArray ShortIterator ShouldRefineInSwift SimpleEffect SinceKotlin Strictfp String StringBuilder SubclassOptInRequired Suppress Synchronized Target +syn keyword ktType TestTimeSource ThreadLocal Throwable Throws TimeMark TimeSource TimedValue Transient Triple TypeCastException Typography UByte UByteArray UInt UIntArray +syn keyword ktType UIntProgression UIntRange ULong ULongArray ULongProgression ULongRange UShort UShortArray UninitializedPropertyAccessException Unit UnsafeVariance +syn keyword ktType UnsupportedOperationException ValueTimeMark Volatile WithComparableMarks +" }}} + +syn keyword ktModifier annotation companion enum inner abstract final open override sealed vararg dynamic expect actual suspend +syn keyword ktStructure class object interface typealias fun val var constructor init + +syn keyword ktReservedKeyword typeof + +syn keyword ktBoolean true false +syn keyword ktConstant null + +syn keyword ktModifier reified external inline noinline crossinline + +syn match ktModifier "\v<data>\ze\@=.*<(class|object)>" +syn match ktModifier "\v<value>\ze\@=.*<class>" +syn match ktModifier "\v<(tailrec|operator|infix)>\ze\@=.*<fun>" +syn match ktModifier "\v<const>\ze\@=.*<val>" +syn match ktModifier "\v<lateinit>\ze\@=.*<var>" +syn match ktModifier "\v<(internal|private|protected|public)>\ze\@=.*<(class|object|interface|typealias|fun|val|var|constructor|get|set)>" + +syn match ktOperator "\v\?:|::|\<\=? | \>\=?|[!=]\=\=?|<as>\??|[-*+/%]\=?|[!&|]" + +syn keyword ktTodo TODO FIXME XXX contained +syn match ktShebang "\v^#!.*$" +syn match ktLineComment "\v//.*$" contains=ktTodo,@Spell +syn region ktComment matchgroup=ktCommentMatchGroup start="/\*" end="\*/" contains=ktComment,ktTodo,@Spell + +syn region ktDocComment start="/\*\*" end="\*/" contains=ktDocTag,ktTodo,@Spell +syn match ktDocTag "\v\@(author|constructor|receiver|return|since|suppress)>" contained +syn match ktDocTag "\v\@(exception|param|property|throws|see|sample)>\s*\S+" contains=ktDocTagParam contained +syn match ktDocTagParam "\v(\s|\[)\S+" contained +syn match ktComment "/\*\*/" + +syn match ktSpecialCharError "\v\\." contained +syn match ktSpecialChar "\v\\([tbnr'"$\\]|u\x{4})" contained +syn region ktString start='"' skip='\\"' end='"' contains=ktSimpleInterpolation,ktComplexInterpolation,ktSpecialChar,ktSpecialCharError,@Spell +syn region ktString start='"""' end='""""*' contains=ktSimpleInterpolation,ktComplexInterpolation,@Spell +syn match ktCharacter "\v'[^']*'" contains=ktSpecialChar,ktSpecialCharError +syn match ktCharacter "\v'\\''" contains=ktSpecialChar +syn match ktCharacter "\v'[^\\]'" + +syn match ktAnnotation "\v(\w)@<!\@[[:alnum:]_.]*(:[[:alnum:]_.]*)?" +syn match ktLabel "\v\w+\@" +syn match ktLabel "\v(\w)@<=\@\w+" + +syn match ktSimpleInterpolation "\v\$\h\w*" contained +syn region ktComplexInterpolation matchgroup=ktComplexInterpolationBrace start="\v\$\{" end="\v\}" contains=ALLBUT,ktSimpleInterpolation,ktTodo,ktSpecialCharError,ktSpecialChar,ktDocTag,ktDocTagParam + +syn match ktNumber "\v<\d+[_[:digit:]]*(uL?|UL?|[LFf])?" +syn match ktNumber "\v<0[Xx]\x+[_[:xdigit:]]*(uL?|UL?|L)?" +syn match ktNumber "\v<0[Bb][01]+[_01]*(uL?|UL?|L)?" +syn match ktFloat "\v<\d*(\d[eE][-+]?\d+|\.\d+([eE][-+]?\d+)?)[Ff]?" + +syn match ktEscapedName "\v`.*`" + +syn match ktExclExcl "!!" +syn match ktArrow "->" + +syn region ktFold start="{" end="}" transparent fold + +exec "syntax sync ccomment ktComment minlines=10" + +hi def link ktStatement Statement +hi def link ktConditional Conditional +hi def link ktRepeat Repeat +hi def link ktOperator Operator +hi def link ktKeyword Keyword +hi def link ktException Exception +hi def link ktReservedKeyword Error + +hi def link ktInclude Include + +hi def link ktType Type +hi def link ktModifier StorageClass +hi def link ktStructure Structure +hi def link ktTypedef Typedef + +hi def link ktBoolean Boolean +hi def link ktConstant Constant + +hi def link ktTodo Todo +hi def link ktShebang Comment +hi def link ktLineComment Comment +hi def link ktComment Comment +hi def link ktCommentMatchGroup Comment +hi def link ktDocComment Comment +hi def link ktDocTag Special +hi def link ktDocTagParam Identifier + +hi def link ktSpecialChar SpecialChar +hi def link ktSpecialCharError Error +hi def link ktString String +hi def link ktCharacter Character + +hi def link ktAnnotation Identifier +hi def link ktLabel Identifier + +hi def link ktSimpleInterpolation Identifier +hi def link ktComplexInterpolationBrace Identifier + +hi def link ktNumber Number +hi def link ktFloat Float + +hi def link ktExclExcl Special +hi def link ktArrow Structure + +let b:current_syntax = 'kotlin' + +" vim:foldmethod=marker |