diff options
68 files changed, 1632 insertions, 869 deletions
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d693f5e593..b8a8c01137 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: linux: name: ${{ matrix.flavor }} (cc=${{ matrix.cc }}) - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: matrix: include: @@ -26,12 +26,12 @@ jobs: if: matrix.flavor == 'asan' || matrix.flavor == 'tsan' run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - - sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' + sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main' - name: Install apt packages run: | sudo apt-get update - sudo apt-get install -y autoconf automake build-essential ccache cmake cpanminus cscope gcc-multilib gdb gettext gperf language-pack-tr libtool-bin locales ninja-build pkg-config python python-pip python-setuptools python3 python3-pip python3-setuptools unzip valgrind xclip + sudo apt-get install -y autoconf automake build-essential ccache cmake cpanminus cscope gcc-multilib gdb gettext gperf language-pack-tr libtool-bin locales ninja-build pkg-config python3 python3-pip python3-setuptools unzip valgrind xclip - name: Install new clang if: matrix.flavor == 'asan' || matrix.flavor == 'tsan' diff --git a/ci/install.sh b/ci/install.sh index ebbd820d9f..1edc1138ee 100755 --- a/ci/install.sh +++ b/ci/install.sh @@ -15,8 +15,10 @@ fi # Use default CC to avoid compilation problems when installing Python modules. echo "Install neovim module for Python 3." CC=cc python3 -m pip -q install --user --upgrade pynvim -echo "Install neovim module for Python 2." -CC=cc python2 -m pip -q install --user --upgrade pynvim +if python2 -m pip -c True 2>&1; then + echo "Install neovim module for Python 2." + CC=cc python2 -m pip -q install --user --upgrade pynvim +fi echo "Install neovim RubyGem." gem install --no-document --bindir "$HOME/.local/bin" --user-install --pre neovim diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 94fd7cf505..112dd4354f 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -487,7 +487,7 @@ endfunction " Resolves Python executable path by invoking and checking `sys.executable`. function! s:python_exepath(invocation) abort - return s:normalize_path(system(a:invocation + return s:normalize_path(system(fnameescape(a:invocation) \ . ' -c "import sys; sys.stdout.write(sys.executable)"')) endfunction diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index dab88fde23..486ed99e3f 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -13,8 +13,6 @@ function! man#init() abort try " Check for -l support. call s:get_page(s:get_path('', 'man')) - catch /E145:/ - " Ignore the error in restricted mode catch /command error .*/ let s:localfile_arg = v:false endtry @@ -436,8 +434,11 @@ function! man#goto_tag(pattern, flags, info) abort let l:structured = [] for l:path in l:paths - let l:n = s:extract_sect_and_name_path(l:path)[1] - let l:structured += [{ 'name': l:n, 'path': l:path }] + let [l:sect, l:name] = s:extract_sect_and_name_path(l:path) + let l:structured += [{ + \ 'name': l:name, + \ 'title': l:name . '(' . l:sect . ')' + \ }] endfor if &cscopetag @@ -448,7 +449,7 @@ function! man#goto_tag(pattern, flags, info) abort return map(l:structured, { \ _, entry -> { \ 'name': entry.name, - \ 'filename': 'man://' . entry.path, + \ 'filename': 'man://' . entry.title, \ 'cmd': '1' \ } \ }) diff --git a/runtime/autoload/xmlformat.vim b/runtime/autoload/xmlformat.vim index ea89401977..712e6f7351 100644 --- a/runtime/autoload/xmlformat.vim +++ b/runtime/autoload/xmlformat.vim @@ -1,6 +1,6 @@ " Vim plugin for formatting XML -" Last Change: Thu, 07 Dec 2018 -" Version: 0.1 +" Last Change: 2020 Jan 06 +" Version: 0.3 " Author: Christian Brabandt <cb@256bit.org> " Repository: https://github.com/chrisbra/vim-xml-ftplugin " License: VIM License @@ -15,51 +15,92 @@ let s:keepcpo = &cpo set cpo&vim " Main function: Format the input {{{1 -func! xmlformat#Format() +func! xmlformat#Format() abort " only allow reformatting through the gq command " (e.g. Vim is in normal mode) if mode() != 'n' " do not fall back to internal formatting return 0 endif + let count_orig = v:count let sw = shiftwidth() let prev = prevnonblank(v:lnum-1) let s:indent = indent(prev)/sw let result = [] let lastitem = prev ? getline(prev) : '' let is_xml_decl = 0 - " split on `<`, but don't split on very first opening < - for item in split(join(getline(v:lnum, (v:lnum + v:count - 1))), '.\@<=[>]\zs') - if s:EndTag(item) - let s:indent = s:DecreaseIndent() - call add(result, s:Indent(item)) - elseif s:EmptyTag(lastitem) - call add(result, s:Indent(item)) - elseif s:StartTag(lastitem) && s:IsTag(item) - let s:indent += 1 - call add(result, s:Indent(item)) - else - if !s:IsTag(item) - " Simply split on '<' - let t=split(item, '.<\@=\zs') - let s:indent+=1 - call add(result, s:Indent(t[0])) - let s:indent = s:DecreaseIndent() - call add(result, s:Indent(t[1])) - else + " go through every line, but don't join all content together and join it + " back. We might lose empty lines + let list = getline(v:lnum, (v:lnum + count_orig - 1)) + let current = 0 + for line in list + " Keep empty input lines? + if empty(line) + call add(result, '') + continue + elseif line !~# '<[/]\?[^>]*>' + let nextmatch = match(list, '<[/]\?[^>]*>', current) + if nextmatch > -1 + let line .= ' '. join(list[(current + 1):(nextmatch-1)], " ") + call remove(list, current+1, nextmatch-1) + endif + endif + " split on `>`, but don't split on very first opening < + " this means, items can be like ['<tag>', 'tag content</tag>'] + for item in split(line, '.\@<=[>]\zs') + if s:EndTag(item) + call s:DecreaseIndent() + call add(result, s:Indent(item)) + elseif s:EmptyTag(lastitem) call add(result, s:Indent(item)) + elseif s:StartTag(lastitem) && s:IsTag(item) + let s:indent += 1 + call add(result, s:Indent(item)) + else + if !s:IsTag(item) + " Simply split on '<', if there is one, + " but reformat according to &textwidth + let t=split(item, '.<\@=\zs') + + " if the content fits well within a single line, add it there + " so that the output looks like this: + " + " <foobar>1</foobar> + if s:TagContent(lastitem) is# s:TagContent(t[1]) && strlen(result[-1]) + strlen(item) <= s:Textwidth() + let result[-1] .= item + let lastitem = t[1] + continue + endif + " t should only contain 2 items, but just be safe here + if s:IsTag(lastitem) + let s:indent+=1 + endif + let result+=s:FormatContent([t[0]]) + if s:EndTag(t[1]) + call s:DecreaseIndent() + endif + "for y in t[1:] + let result+=s:FormatContent(t[1:]) + "endfor + else + call add(result, s:Indent(item)) + endif endif - endif - let lastitem = item - endfor + let lastitem = item + endfor + let current += 1 + endfor - if !empty(result) - exe v:lnum. ",". (v:lnum + v:count - 1). 'd' + if !empty(result) + let lastprevline = getline(v:lnum + count_orig) + let delete_lastline = v:lnum + count_orig - 1 == line('$') + exe v:lnum. ",". (v:lnum + count_orig - 1). 'd' call append(v:lnum - 1, result) " Might need to remove the last line, if it became empty because of the " append() call let last = v:lnum + len(result) - if getline(last) is '' + " do not use empty(), it returns true for `empty(0)` + if getline(last) is '' && lastprevline is '' && delete_lastline exe last. 'd' endif endif @@ -68,15 +109,15 @@ func! xmlformat#Format() return 0 endfunc " Check if given tag is XML Declaration header {{{1 -func! s:IsXMLDecl(tag) +func! s:IsXMLDecl(tag) abort return a:tag =~? '^\s*<?xml\s\?\%(version="[^"]*"\)\?\s\?\%(encoding="[^"]*"\)\? ?>\s*$' endfunc " Return tag indented by current level {{{1 -func! s:Indent(item) +func! s:Indent(item) abort return repeat(' ', shiftwidth()*s:indent). s:Trim(a:item) endfu " Return item trimmed from leading whitespace {{{1 -func! s:Trim(item) +func! s:Trim(item) abort if exists('*trim') return trim(a:item) else @@ -84,30 +125,77 @@ func! s:Trim(item) endif endfunc " Check if tag is a new opening tag <tag> {{{1 -func! s:StartTag(tag) +func! s:StartTag(tag) abort let is_comment = s:IsComment(a:tag) return a:tag =~? '^\s*<[^/?]' && !is_comment endfunc -func! s:IsComment(tag) +" Check if tag is a Comment start {{{1 +func! s:IsComment(tag) abort return a:tag =~? '<!--' endfunc " Remove one level of indentation {{{1 -func! s:DecreaseIndent() - return (s:indent > 0 ? s:indent - 1 : 0) +func! s:DecreaseIndent() abort + let s:indent = (s:indent > 0 ? s:indent - 1 : 0) endfunc " Check if tag is a closing tag </tag> {{{1 -func! s:EndTag(tag) +func! s:EndTag(tag) abort return a:tag =~? '^\s*</' endfunc " Check that the tag is actually a tag and not {{{1 " something like "foobar</foobar>" -func! s:IsTag(tag) +func! s:IsTag(tag) abort return s:Trim(a:tag)[0] == '<' endfunc " Check if tag is empty <tag/> {{{1 -func! s:EmptyTag(tag) +func! s:EmptyTag(tag) abort return a:tag =~ '/>\s*$' endfunc +func! s:TagContent(tag) abort "{{{1 + " Return content of a tag + return substitute(a:tag, '^\s*<[/]\?\([^>]*\)>\s*$', '\1', '') +endfunc +func! s:Textwidth() abort "{{{1 + " return textwidth (or 80 if not set) + return &textwidth == 0 ? 80 : &textwidth +endfunc +" Format input line according to textwidth {{{1 +func! s:FormatContent(list) abort + let result=[] + let limit = s:Textwidth() + let column=0 + let idx = -1 + let add_indent = 0 + let cnt = 0 + for item in a:list + for word in split(item, '\s\+\S\+\zs') + if match(word, '^\s\+$') > -1 + " skip empty words + continue + endif + let column += strdisplaywidth(word, column) + if match(word, "^\\s*\n\\+\\s*$") > -1 + call add(result, '') + let idx += 1 + let column = 0 + let add_indent = 1 + elseif column > limit || cnt == 0 + let add = s:Indent(s:Trim(word)) + call add(result, add) + let column = strdisplaywidth(add) + let idx += 1 + else + if add_indent + let result[idx] = s:Indent(s:Trim(word)) + else + let result[idx] .= ' '. s:Trim(word) + endif + let add_indent = 0 + endif + let cnt += 1 + endfor + endfor + return result +endfunc " Restoration And Modelines: {{{1 let &cpo= s:keepcpo unlet s:keepcpo diff --git a/runtime/compiler/xmllint.vim b/runtime/compiler/xmllint.vim index 96cfa55383..79d38b4d14 100644 --- a/runtime/compiler/xmllint.vim +++ b/runtime/compiler/xmllint.vim @@ -1,7 +1,7 @@ " Vim compiler file -" Compiler: xmllint +" Compiler: Libxml2 Command-Line Tool " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2013 Jul 8 +" Last Change: 2020 Jul 30 if exists("current_compiler") finish @@ -13,14 +13,16 @@ if exists(":CompilerSet") != 2 " older Vim always used :setlocal endif let s:cpo_save = &cpo -set cpo-=C +set cpo&vim CompilerSet makeprg=xmllint\ --valid\ --noout - -CompilerSet errorformat=%+E%f:%l:\ %.%#\ error\ :\ %m, - \%+W%f:%l:\ %.%#\ warning\ :\ %m, - \%-Z%p^, - \%-G%.%# +CompilerSet errorformat=%E%f:%l:\ %.%#\ error\ :\ %m, + \%W%f:%l:\ %.%#\ warning\ :\ %m, + \%-Z%p^, + \%C%.%#, + \%terror:\ %m, + \%tarning:\ %m, + \%-G%.%# let &cpo = s:cpo_save unlet s:cpo_save diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b83d2c4484..6c42dd6739 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5166,6 +5166,7 @@ A jump table for the options with a short description can be found at |Q_op|. It is allowed to give an argument to the command, e.g. "csh -f". See |option-backslash| about including spaces and backslashes. Environment variables are expanded |:set_env|. + If the name of the shell contains a space, you might need to enclose it in quotes. Example: > :set shell=\"c:\program\ files\unix\sh.exe\"\ -f diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 58cd535e98..b6a238f158 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -59,15 +59,16 @@ shouldn't be done directly in the change callback anyway as they will be very frequent. Rather a plugin that does any kind of analysis on a tree should use a timer to throttle too frequent updates. -tsparser:set_included_ranges({ranges}) *tsparser:set_included_ranges()* - Changes the ranges the parser should consider. This is used for - language injection. {ranges} should be of the form (all zero-based): > +tsparser:set_included_regions({region_list}) *tsparser:set_included_regions()* + Changes the regions the parser should consider. This is used for + language injection. {region_list} should be of the form (all zero-based): > { - {start_node, end_node}, + {node1, node2}, ... } < - NOTE: `start_node` and `end_node` are both inclusive. + `node1` and `node2` are both considered part of the same region and + will be parsed together with the parser in the same context. Tree methods *lua-treesitter-tree* @@ -253,7 +254,7 @@ Here is a list of built-in predicates : `lua-match?` *ts-predicate-lua-match?* This will match the same way than |match?| but using lua regexes. - + `contains?` *ts-predicate-contains?* Will check if any of the following arguments appears in the text corresponding to the node : > diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 4e617052a9..4e54bcaefd 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -692,12 +692,21 @@ au BufNewFile,BufRead .gtkrc,gtkrc setf gtkrc au BufNewFile,BufRead *.haml setf haml " Hamster Classic | Playground files -au BufNewFile,BufRead *.hsc,*.hsm setf hamster +au BufNewFile,BufRead *.hsm setf hamster +au BufNewFile,BufRead *.hsc + \ if match(join(getline(1,10), "\n"), '\%(^\|\n\)\s*\%({-#\_s*LANGUAGE\>\|\<module\>\)') != -1 | + \ setf haskell | + \ else | + \ setf hamster | + \ endif " Haskell au BufNewFile,BufRead *.hs,*.hs-boot setf haskell au BufNewFile,BufRead *.lhs setf lhaskell au BufNewFile,BufRead *.chs setf chaskell +au BufNewFile,BufRead cabal.project setf cabalproject +au BufNewFile,BufRead $HOME/.cabal/config setf cabalconfig +au BufNewFile,BufRead cabal.config setf cabalconfig " Haste au BufNewFile,BufRead *.ht setf haste @@ -2042,12 +2051,14 @@ au BufNewFile,BufRead bzr_log.* setf bzr " Bazel build file if !has("fname_case") - au BufNewFile,BufRead *.BUILD,BUILD setf bzl + au BufNewFile,BufRead *.BUILD,BUILD setf bzl endif " BIND zone au BufNewFile,BufRead */named/db.*,*/bind/db.* call s:StarSetf('bindzone') +au BufNewFile,BufRead cabal.project.* call s:StarSetf('cabalproject') + " Calendar au BufNewFile,BufRead */.calendar/*, \*/share/calendar/*/calendar.*,*/share/calendar/calendar.* diff --git a/runtime/indent/xml.vim b/runtime/indent/xml.vim index 883af98563..413a3ddb53 100644 --- a/runtime/indent/xml.vim +++ b/runtime/indent/xml.vim @@ -1,9 +1,10 @@ -" Language: xml -" Repository: https://github.com/chrisbra/vim-xml-ftplugin -" Last Changed: July 27, 2019 -" Maintainer: Christian Brabandt <cb@256bit.org> -" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Language: XML +" Maintainer: Christian Brabandt <cb@256bit.org> +" Repository: https://github.com/chrisbra/vim-xml-ftplugin +" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Last Changed: 2019 Dec 02 " Last Change: +" 20191202 - Handle docbk filetype " 20190726 - Correctly handle non-tagged data " 20190204 - correctly handle wrap tags " https://github.com/chrisbra/vim-xml-ftplugin/issues/5 @@ -134,7 +135,7 @@ fun! XmlIndentGet(lnum, use_syntax_check) if syn_name_end =~ 'Comment' && syn_name_start =~ 'Comment' return <SID>XmlIndentComment(a:lnum) - elseif empty(syn_name_start) && empty(syn_name_end) + elseif empty(syn_name_start) && empty(syn_name_end) && a:use_syntax_check " non-xml tag content: use indent from 'autoindent' return pind + shiftwidth() endif @@ -148,7 +149,7 @@ endfun func! <SID>IsXMLContinuation(line) " Checks, whether or not the line matches a start-of-tag - return a:line !~ '^\s*<' + return a:line !~ '^\s*<' && &ft is# 'xml' endfunc func! <SID>HasNoTagEnd(line) diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 19ef148afc..6886f0c178 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -1,98 +1,13 @@ local a = vim.api local query = require'vim.treesitter.query' local language = require'vim.treesitter.language' +local LanguageTree = require'vim.treesitter.languagetree' -- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. -- Consider use weak references to release parser if all plugins are done with -- it. local parsers = {} -local Parser = {} -Parser.__index = Parser - ---- Parses the buffer if needed and returns a tree. --- --- Calling this will call the on_changedtree callbacks if the tree has changed. --- --- @returns An up to date tree --- @returns If the tree changed with this call, the changed ranges -function Parser:parse() - if self.valid then - return self._tree_immutable - end - local changes - - self._tree, changes = self._parser:parse(self._tree, self:input_source()) - - self._tree_immutable = self._tree:copy() - - self.valid = true - - if not vim.tbl_isempty(changes) then - for _, cb in ipairs(self.changedtree_cbs) do - cb(changes) - end - end - - return self._tree_immutable, changes -end - -function Parser:input_source() - return self.bufnr or self.str -end - -function Parser:_on_bytes(bufnr, changed_tick, - start_row, start_col, start_byte, - old_row, old_col, old_byte, - new_row, new_col, new_byte) - local old_end_col = old_col + ((old_row == 0) and start_col or 0) - local new_end_col = new_col + ((new_row == 0) and start_col or 0) - self._tree:edit(start_byte,start_byte+old_byte,start_byte+new_byte, - start_row, start_col, - start_row+old_row, old_end_col, - start_row+new_row, new_end_col) - self.valid = false - - for _, cb in ipairs(self.bytes_cbs) do - cb(bufnr, changed_tick, - start_row, start_col, start_byte, - old_row, old_col, old_byte, - new_row, new_col, new_byte) - end -end - ---- Registers callbacks for the parser --- @param cbs An `nvim_buf_attach`-like table argument with the following keys : --- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback. --- `on_changedtree` : a callback that will be called everytime the tree has syntactical changes. --- it will only be passed one argument, that is a table of the ranges (as node ranges) that --- changed. -function Parser:register_cbs(cbs) - if not cbs then return end - - if cbs.on_changedtree then - table.insert(self.changedtree_cbs, cbs.on_changedtree) - end - - if cbs.on_bytes then - table.insert(self.bytes_cbs, cbs.on_bytes) - end -end - ---- Sets the included ranges for the current parser --- --- @param ranges A table of nodes that will be used as the ranges the parser should include. -function Parser:set_included_ranges(ranges) - self._parser:set_included_ranges(ranges) - -- The buffer will need to be parsed again later - self.valid = false -end - ---- Gets the included ranges for the parsers -function Parser:included_ranges() - return self._parser:included_ranges() -end - local M = vim.tbl_extend("error", query, language) setmetatable(M, { @@ -113,9 +28,9 @@ setmetatable(M, { -- It is not recommended to use this, use vim.treesitter.get_parser() instead. -- -- @param bufnr The buffer the parser will be tied to --- @param lang The language of the parser. --- @param id The id the parser will have -function M._create_parser(bufnr, lang, id) +-- @param lang The language of the parser +-- @param opts Options to pass to the language tree +function M._create_parser(bufnr, lang, opts) language.require_language(lang) if bufnr == 0 then bufnr = a.nvim_get_current_buf() @@ -123,25 +38,22 @@ function M._create_parser(bufnr, lang, id) vim.fn.bufload(bufnr) - local self = setmetatable({bufnr=bufnr, lang=lang, valid=false}, Parser) - self._parser = vim._create_ts_parser(lang) - self.changedtree_cbs = {} - self.bytes_cbs = {} - self:parse() - -- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is - -- using it. + local self = LanguageTree.new(bufnr, lang, opts) + local function bytes_cb(_, ...) - return self:_on_bytes(...) + self:_on_bytes(...) end - local detach_cb = nil - if id ~= nil then - detach_cb = function() - if parsers[id] == self then - parsers[id] = nil - end + + local function detach_cb() + if parsers[bufnr] == self then + parsers[bufnr] = nil end end + a.nvim_buf_attach(self.bufnr, false, {on_bytes=bytes_cb, on_detach=detach_cb}) + + self:parse() + return self end @@ -152,39 +64,36 @@ end -- -- @param bufnr The buffer the parser should be tied to -- @param ft The filetype of this parser --- @param buf_attach_cbs See Parser:register_cbs +-- @param opts Options object to pass to the parser -- -- @returns The parser -function M.get_parser(bufnr, lang, buf_attach_cbs) +function M.get_parser(bufnr, lang, opts) + opts = opts or {} + if bufnr == nil or bufnr == 0 then bufnr = a.nvim_get_current_buf() end if lang == nil then lang = a.nvim_buf_get_option(bufnr, "filetype") end - local id = tostring(bufnr)..'_'..lang - if parsers[id] == nil then - parsers[id] = M._create_parser(bufnr, lang, id) + if parsers[bufnr] == nil then + parsers[bufnr] = M._create_parser(bufnr, lang, opts) end - parsers[id]:register_cbs(buf_attach_cbs) + parsers[bufnr]:register_cbs(opts.buf_attach_cbs) - return parsers[id] + return parsers[bufnr] end -function M.get_string_parser(str, lang) +function M.get_string_parser(str, lang, opts) vim.validate { str = { str, 'string' }, lang = { lang, 'string' } } language.require_language(lang) - local self = setmetatable({str=str, lang=lang, valid=false}, Parser) - self._parser = vim._create_ts_parser(lang) - self:parse() - - return self + return LanguageTree.new(str, lang, opts) end return M diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 6714bb6354..60db7f24cf 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -1,4 +1,5 @@ local a = vim.api +local query = require"vim.treesitter.query" -- support reload for quick experimentation local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} @@ -6,6 +7,9 @@ TSHighlighter.__index = TSHighlighter TSHighlighter.active = TSHighlighter.active or {} +local TSHighlighterQuery = {} +TSHighlighterQuery.__index = TSHighlighterQuery + local ns = a.nvim_create_namespace("treesitter/highlighter") -- These are conventions defined by nvim-treesitter, though it @@ -56,27 +60,83 @@ TSHighlighter.hl_map = { ["include"] = "Include", } -function TSHighlighter.new(parser, query) +local function is_highlight_name(capture_name) + local firstc = string.sub(capture_name, 1, 1) + return firstc ~= string.lower(firstc) +end + +function TSHighlighterQuery.new(lang, query_string) + local self = setmetatable({}, { __index = TSHighlighterQuery }) + + self.hl_cache = setmetatable({}, { + __index = function(table, capture) + local hl = self:get_hl_from_capture(capture) + rawset(table, capture, hl) + + return hl + end + }) + + if query_string then + self._query = query.parse_query(lang, query_string) + else + self._query = query.get_query(lang, "highlights") + end + + return self +end + +function TSHighlighterQuery:query() + return self._query +end + +function TSHighlighterQuery:get_hl_from_capture(capture) + local name = self._query.captures[capture] + + if is_highlight_name(name) then + -- From "Normal.left" only keep "Normal" + return vim.split(name, '.', true)[1] + else + -- Default to false to avoid recomputing + local hl = TSHighlighter.hl_map[name] + return hl and a.nvim_get_hl_id_by_name(hl) or 0 + end +end + +function TSHighlighter.new(tree, opts) local self = setmetatable({}, TSHighlighter) - self.parser = parser - parser:register_cbs { - on_changedtree = function(...) self:on_changedtree(...) end + if type(tree:source()) ~= "number" then + error("TSHighlighter can not be used with a string parser source.") + end + + opts = opts or {} + self.tree = tree + tree:register_cbs { + on_changedtree = function(...) self:on_changedtree(...) end, + on_bytes = function(...) self:on_bytes(...) end } - self:set_query(query) + self.bufnr = tree:source() self.edit_count = 0 self.redraw_count = 0 self.line_count = {} - self.root = self.parser:parse():root() - a.nvim_buf_set_option(self.buf, "syntax", "") - - -- TODO(bfredl): can has multiple highlighters per buffer???? - if not TSHighlighter.active[parser.bufnr] then - TSHighlighter.active[parser.bufnr] = {} + -- A map of highlight states. + -- This state is kept during rendering across each line update. + self._highlight_states = {} + self._queries = {} + + -- Queries for a specific language can be overridden by a custom + -- string query... if one is not provided it will be looked up by file. + if opts.queries then + for lang, query_string in pairs(opts.queries) do + self._queries[lang] = TSHighlighterQuery.new(lang, query_string) + end end - TSHighlighter.active[parser.bufnr][parser.lang] = self + a.nvim_buf_set_option(self.bufnr, "syntax", "") + + TSHighlighter.active[self.bufnr] = self -- Tricky: if syntax hasn't been enabled, we need to reload color scheme -- but use synload.vim rather than syntax.vim to not enable @@ -85,119 +145,112 @@ function TSHighlighter.new(parser, query) if vim.g.syntax_on ~= 1 then vim.api.nvim_command("runtime! syntax/synload.vim") end + + self.tree:parse() + return self end -local function is_highlight_name(capture_name) - local firstc = string.sub(capture_name, 1, 1) - return firstc ~= string.lower(firstc) +function TSHighlighter:destroy() + if TSHighlighter.active[self.bufnr] then + TSHighlighter.active[self.bufnr] = nil + end end -function TSHighlighter:get_hl_from_capture(capture) +function TSHighlighter:get_highlight_state(tstree) + if not self._highlight_states[tstree] then + self._highlight_states[tstree] = { + next_row = 0, + iter = nil + } + end - local name = self.query.captures[capture] + return self._highlight_states[tstree] +end - if is_highlight_name(name) then - -- From "Normal.left" only keep "Normal" - return vim.split(name, '.', true)[1] - else - -- Default to false to avoid recomputing - local hl = TSHighlighter.hl_map[name] - return hl and a.nvim_get_hl_id_by_name(hl) or 0 - end +function TSHighlighter:reset_highlight_state() + self._highlight_states = {} +end + +function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end) + a.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1) end function TSHighlighter:on_changedtree(changes) for _, ch in ipairs(changes or {}) do - a.nvim__buf_redraw_range(self.buf, ch[1], ch[3]+1) + a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3]+1) end end -function TSHighlighter:set_query(query) - if type(query) == "string" then - query = vim.treesitter.parse_query(self.parser.lang, query) +function TSHighlighter:get_query(lang) + if not self._queries[lang] then + self._queries[lang] = TSHighlighterQuery.new(lang) end - self.query = query + return self._queries[lang] +end - self.hl_cache = setmetatable({}, { - __index = function(table, capture) - local hl = self:get_hl_from_capture(capture) - rawset(table, capture, hl) +local function on_line_impl(self, buf, line) + self.tree:for_each_tree(function(tstree, tree) + if not tstree then return end - return hl + local root_node = tstree:root() + local root_start_row, _, root_end_row, _ = root_node:range() + + -- Only worry about trees within the line range + if root_start_row > line or root_end_row < line then return end + + local state = self:get_highlight_state(tstree) + local highlighter_query = self:get_query(tree:lang()) + + if state.iter == nil then + state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) end - }) - a.nvim__buf_redraw_range(self.parser.bufnr, 0, a.nvim_buf_line_count(self.parser.bufnr)) -end + while line >= state.next_row do + local capture, node = state.iter() -local function iter_active_tshl(buf, fn) - for _, hl in pairs(TSHighlighter.active[buf] or {}) do - fn(hl) - end -end + if capture == nil then break end -local function on_line_impl(self, buf, line) - if self.root == nil then - return -- parser bought the farm already - end + local start_row, start_col, end_row, end_col = node:range() + local hl = highlighter_query.hl_cache[capture] - if self.iter == nil then - self.iter = self.query:iter_captures(self.root,buf,line,self.botline) - end - while line >= self.nextrow do - local capture, node = self.iter() - if capture == nil then - break + if hl and end_row >= line then + a.nvim_buf_set_extmark(buf, ns, start_row, start_col, + { end_line = end_row, end_col = end_col, + hl_group = hl, + ephemeral = true + }) + end + if start_row > line then + state.next_row = start_row + end end - local start_row, start_col, end_row, end_col = node:range() - local hl = self.hl_cache[capture] - if hl and end_row >= line then - a.nvim_buf_set_extmark(buf, ns, start_row, start_col, - { end_line = end_row, end_col = end_col, - hl_group = hl, - ephemeral = true, - }) - end - if start_row > line then - self.nextrow = start_row - end - end + end, true) end -function TSHighlighter._on_line(_, _win, buf, line, highlighter) - -- on_line is only called when this is non-nil - if highlighter then - on_line_impl(highlighter, buf, line) - else - iter_active_tshl(buf, function(self) - on_line_impl(self, buf, line) - end) - end +function TSHighlighter._on_line(_, _win, buf, line, _) + local self = TSHighlighter.active[buf] + if not self then return end + + on_line_impl(self, buf, line) end function TSHighlighter._on_buf(_, buf) - iter_active_tshl(buf, function(self) - if self then - local tree = self.parser:parse() - self.root = (tree and tree:root()) or nil - end - end) + local self = TSHighlighter.active[buf] + if self then + self.tree:parse() + end end -function TSHighlighter._on_win(_, _win, buf, _topline, botline) - iter_active_tshl(buf, function(self) - if not self then - return false - end +function TSHighlighter._on_win(_, _win, buf, _topline) + local self = TSHighlighter.active[buf] + if not self then + return false + end - self.iter = nil - self.nextrow = 0 - self.botline = botline - self.redraw_count = self.redraw_count + 1 - return true - end) + self:reset_highlight_state() + self.redraw_count = self.redraw_count + 1 return true end diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua new file mode 100644 index 0000000000..70e2ac4c62 --- /dev/null +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -0,0 +1,454 @@ +local a = vim.api +local query = require'vim.treesitter.query' +local language = require'vim.treesitter.language' + +local LanguageTree = {} +LanguageTree.__index = LanguageTree + +-- Represents a single treesitter parser for a language. +-- The language can contain child languages with in it's range, +-- hence the tree. +-- +-- @param source Can be a bufnr or a string of text to parse +-- @param lang The language this tree represents +-- @param opts Options table +-- @param opts.queries A table of language to injection query strings +-- This is useful for overridding the built in runtime file +-- searching for the injection language query per language. +function LanguageTree.new(source, lang, opts) + language.require_language(lang) + opts = opts or {} + + local custom_queries = opts.queries or {} + local self = setmetatable({ + _source=source, + _lang=lang, + _children = {}, + _regions = {}, + _trees = {}, + _opts = opts, + _injection_query = custom_queries[lang] + and query.parse_query(lang, custom_queries[lang]) + or query.get_query(lang, "injections"), + _valid = false, + _parser = vim._create_ts_parser(lang), + _callbacks = { + changedtree = {}, + bytes = {}, + child_added = {}, + child_removed = {} + }, + }, LanguageTree) + + + return self +end + +-- Invalidates this parser and all it's children +function LanguageTree:invalidate() + self._valid = false + + for _, child in ipairs(self._children) do + child:invalidate() + end +end + +-- Returns all trees this language tree contains. +-- Does not include child languages. +function LanguageTree:trees() + return self._trees +end + +-- Gets the language of this tree layer. +function LanguageTree:lang() + return self._lang +end + +-- Determines whether this tree is valid. +-- If the tree is invalid, `parse()` must be called +-- to get the an updated tree. +function LanguageTree:is_valid() + return self._valid +end + +-- Returns a map of language to child tree. +function LanguageTree:children() + return self._children +end + +-- Returns the source content of the language tree (bufnr or string). +function LanguageTree:source() + return self._source +end + +-- Parses all defined regions using a treesitter parser +-- for the language this tree represents. +-- This will run the injection query for this language to +-- determine if any child languages should be created. +function LanguageTree:parse() + if self._valid then + return self._trees + end + + local parser = self._parser + local changes = {} + + local old_trees = self._trees + self._trees = {} + + -- If there are no ranges, set to an empty list + -- so the included ranges in the parser ar cleared. + if self._regions and #self._regions > 0 then + for i, ranges in ipairs(self._regions) do + local old_tree = old_trees[i] + parser:set_included_ranges(ranges) + + local tree, tree_changes = parser:parse(old_tree, self._source) + + table.insert(self._trees, tree) + vim.list_extend(changes, tree_changes) + end + else + local tree, tree_changes = parser:parse(old_trees[1], self._source) + + table.insert(self._trees, tree) + vim.list_extend(changes, tree_changes) + end + + local injections_by_lang = self:_get_injections() + local seen_langs = {} + + for lang, injection_ranges in pairs(injections_by_lang) do + local child = self._children[lang] + + if not child then + child = self:add_child(lang) + end + + child:set_included_regions(injection_ranges) + + local _, child_changes = child:parse() + + -- Propagate any child changes so they are included in the + -- the change list for the callback. + if child_changes then + vim.list_extend(changes, child_changes) + end + + seen_langs[lang] = true + end + + for lang, _ in pairs(self._children) do + if not seen_langs[lang] then + self:remove_child(lang) + end + end + + self._valid = true + + self:_do_callback('changedtree', changes) + return self._trees, changes +end + +-- Invokes the callback for each LanguageTree and it's children recursively +-- @param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string) +-- @param include_self Whether to include the invoking tree in the results. +function LanguageTree:for_each_child(fn, include_self) + if include_self then + fn(self, self._lang) + end + + for _, child in pairs(self._children) do + child:for_each_child(fn, true) + end +end + +-- Invokes the callback for each treesitter trees recursively. +-- Note, this includes the invoking language tree's trees as well. +-- @param fn The callback to invoke. The callback is invoked with arguments +-- (tree: TSTree, languageTree: LanguageTree) +function LanguageTree:for_each_tree(fn) + for _, tree in ipairs(self._trees) do + fn(tree, self) + end + + for _, child in pairs(self._children) do + child:for_each_tree(fn) + end +end + +-- Adds a child language to this tree. +-- If the language already exists as a child, it will first be removed. +-- @param lang The language to add. +function LanguageTree:add_child(lang) + if self._children[lang] then + self:remove_child(lang) + end + + self._children[lang] = LanguageTree.new(self._source, lang, self._opts) + + self:invalidate() + self:_do_callback('child_added', self._children[lang]) + + return self._children[lang] +end + +-- Removes a child language from this tree. +-- @param lang The language to remove. +function LanguageTree:remove_child(lang) + local child = self._children[lang] + + if child then + self._children[lang] = nil + child:destroy() + self:invalidate() + self:_do_callback('child_removed', child) + end +end + +-- Destroys this language tree and all it's children. +-- Any cleanup logic should be performed here. +-- Note, this DOES NOT remove this tree from a parent. +-- `remove_child` must be called on the parent to remove it. +function LanguageTree:destroy() + -- Cleanup here + for _, child in ipairs(self._children) do + child:destroy() + end +end + +-- Sets the included regions that should be parsed by this parser. +-- A region is a set of nodes and/or ranges that will be parsed in the same context. +-- +-- For example, `{ { node1 }, { node2} }` is two separate regions. +-- This will be parsed by the parser in two different contexts... thus resulting +-- in two separate trees. +-- +-- `{ { node1, node2 } }` is a single region consisting of two nodes. +-- This will be parsed by the parser in a single context... thus resulting +-- in a single tree. +-- +-- This allows for embedded languages to be parsed together across different +-- nodes, which is useful for templating languages like ERB and EJS. +-- +-- Note, this call invalidates the tree and requires it to be parsed again. +-- +-- @param regions A list of regions this tree should manange and parse. +function LanguageTree:set_included_regions(regions) + -- Transform the tables from 4 element long to 6 element long (with byte offset) + for _, region in ipairs(regions) do + for i, range in ipairs(region) do + if type(range) == "table" and #range == 4 then + -- TODO(vigoux): I don't think string parsers are useful for now + if type(self._source) == "number" then + local start_row, start_col, end_row, end_col = unpack(range) + -- Easy case, this is a buffer parser + -- TODO(vigoux): proper byte computation here, and account for EOL ? + local start_byte = a.nvim_buf_get_offset(self.bufnr, start_row) + start_col + local end_byte = a.nvim_buf_get_offset(self.bufnr, end_row) + end_col + + region[i] = { start_row, start_col, start_byte, end_row, end_col, end_byte } + end + end + end + end + + self._regions = regions + -- Trees are no longer valid now that we have changed regions. + -- TODO(vigoux,steelsojka): Look into doing this smarter so we can use some of the + -- old trees for incremental parsing. Currently, this only + -- effects injected languages. + self._trees = {} + self:invalidate() +end + +-- Gets the set of included regions +function LanguageTree:included_regions() + return self._regions +end + +-- Gets language injection points by language. +-- This is where most of the injection processing occurs. +-- TODO: Allow for an offset predicate to tailor the injection range +-- instead of using the entire nodes range. +-- @private +function LanguageTree:_get_injections() + if not self._injection_query then return {} end + + local injections = {} + + for tree_index, tree in ipairs(self._trees) do + local root_node = tree:root() + local start_line, _, end_line, _ = root_node:range() + + for pattern, match in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do + local lang = nil + local injection_node = nil + local combined = false + + -- You can specify the content and language together + -- using a tag with the language, for example + -- @javascript + for id, node in pairs(match) do + local name = self._injection_query.captures[id] + -- TODO add a way to offset the content passed to the parser. + -- Needed to shave off leading quotes and things of that nature. + + -- Lang should override any other language tag + if name == "language" then + lang = query.get_node_text(node, self._source) + elseif name == "combined" then + combined = true + elseif name == "content" then + injection_node = node + -- Ignore any tags that start with "_" + -- Allows for other tags to be used in matches + elseif string.sub(name, 1, 1) ~= "_" then + if lang == nil then + lang = name + end + + if not injection_node then + injection_node = node + end + end + end + + -- Each tree index should be isolated from the other nodes. + if not injections[tree_index] then + injections[tree_index] = {} + end + + if not injections[tree_index][lang] then + injections[tree_index][lang] = {} + end + + -- Key by pattern so we can either combine each node to parse in the same + -- context or treat each node independently. + if not injections[tree_index][lang][pattern] then + injections[tree_index][lang][pattern] = { combined = combined, nodes = {} } + end + + table.insert(injections[tree_index][lang][pattern].nodes, injection_node) + end + end + + local result = {} + + -- Generate a map by lang of node lists. + -- Each list is a set of ranges that should be parsed + -- together. + for _, lang_map in ipairs(injections) do + for lang, patterns in pairs(lang_map) do + if not result[lang] then + result[lang] = {} + end + + for _, entry in pairs(patterns) do + if entry.combined then + table.insert(result[lang], entry.nodes) + else + for _, node in ipairs(entry.nodes) do + table.insert(result[lang], {node}) + end + end + end + end + end + + return result +end + +function LanguageTree:_do_callback(cb_name, ...) + for _, cb in ipairs(self._callbacks[cb_name]) do + cb(...) + end +end + +function LanguageTree:_on_bytes(bufnr, changed_tick, + start_row, start_col, start_byte, + old_row, old_col, old_byte, + new_row, new_col, new_byte) + self:invalidate() + + local old_end_col = old_col + ((old_row == 0) and start_col or 0) + local new_end_col = new_col + ((new_row == 0) and start_col or 0) + + -- Edit all trees recursively, together BEFORE emitting a bytes callback. + -- In most cases this callback should only be called from the root tree. + self:for_each_tree(function(tree) + tree:edit(start_byte,start_byte+old_byte,start_byte+new_byte, + start_row, start_col, + start_row+old_row, old_end_col, + start_row+new_row, new_end_col) + end) + + self:_do_callback('bytes', bufnr, changed_tick, + start_row, start_col, start_byte, + old_row, old_col, old_byte, + new_row, new_col, new_byte) +end + +--- Registers callbacks for the parser +-- @param cbs An `nvim_buf_attach`-like table argument with the following keys : +-- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback. +-- `on_changedtree` : a callback that will be called everytime the tree has syntactical changes. +-- it will only be passed one argument, that is a table of the ranges (as node ranges) that +-- changed. +-- `on_child_added` : emitted when a child is added to the tree. +-- `on_child_removed` : emitted when a child is remvoed from the tree. +function LanguageTree:register_cbs(cbs) + if not cbs then return end + + if cbs.on_changedtree then + table.insert(self._callbacks.changedtree, cbs.on_changedtree) + end + + if cbs.on_bytes then + table.insert(self._callbacks.bytes, cbs.on_bytes) + end + + if cbs.on_child_added then + table.insert(self._callbacks.child_added, cbs.on_child_added) + end + + if cbs.on_child_removed then + table.insert(self._callbacks.child_removed, cbs.on_child_removed) + end +end + +local function region_contains(region, range) + for _, node in ipairs(region) do + local start_row, start_col, end_row, end_col = node:range() + local start_fits = start_row < range[1] or (start_row == range[1] and start_col <= range[2]) + local end_fits = end_row > range[3] or (end_row == range[3] and end_col >= range[4]) + + if start_fits and end_fits then + return true + end + end + + return false +end + +function LanguageTree:contains(range) + for _, region in pairs(self._region) do + if region_contains(region, range) then + return true + end + end + + return false +end + +function LanguageTree:language_for_range(range) + for _, child in pairs(self._children) do + if child:contains(range) then + return child:node_for_range(range) + end + end + + return self +end + +return LanguageTree diff --git a/runtime/syntax/gitconfig.vim b/runtime/syntax/gitconfig.vim index c6c56f77b6..e5eaf10f94 100644 --- a/runtime/syntax/gitconfig.vim +++ b/runtime/syntax/gitconfig.vim @@ -2,24 +2,22 @@ " Language: git config file " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " Filenames: gitconfig, .gitconfig, *.git/config -" Last Change: 2010 May 21 +" Last Change: 2019 Dec 05 if exists("b:current_syntax") finish endif -setlocal iskeyword+=- -setlocal iskeyword-=_ syn case ignore syn sync minlines=10 -syn match gitconfigComment "[#;].*" +syn match gitconfigComment "[#;].*" contains=@Spell syn match gitconfigSection "\%(^\s*\)\@<=\[[a-z0-9.-]\+\]" syn match gitconfigSection '\%(^\s*\)\@<=\[[a-z0-9.-]\+ \+\"\%([^\\"]\|\\.\)*"\]' -syn match gitconfigVariable "\%(^\s*\)\@<=\a\k*\%(\s*\%([=#;]\|$\)\)\@=" nextgroup=gitconfigAssignment skipwhite +syn match gitconfigVariable "\%(^\s*\)\@<=\a[a-z0-9-]*\%(\s*\%([=#;]\|$\)\)\@=" nextgroup=gitconfigAssignment skipwhite syn region gitconfigAssignment matchgroup=gitconfigNone start=+=\s*+ skip=+\\+ end=+\s*$+ contained contains=gitconfigBoolean,gitconfigNumber,gitConfigString,gitConfigEscape,gitConfigError,gitconfigComment keepend syn keyword gitconfigBoolean true false yes no contained -syn match gitconfigNumber "\d\+" contained +syn match gitconfigNumber "\<\d\+\>" contained syn region gitconfigString matchgroup=gitconfigDelim start=+"+ skip=+\\+ end=+"+ matchgroup=gitconfigError end=+[^\\"]\%#\@!$+ contained contains=gitconfigEscape,gitconfigEscapeError syn match gitconfigError +\\.+ contained syn match gitconfigEscape +\\[\\"ntb]+ contained @@ -32,7 +30,7 @@ hi def link gitconfigBoolean Boolean hi def link gitconfigNumber Number hi def link gitconfigString String hi def link gitconfigDelim Delimiter -hi def link gitconfigEscape Delimiter +hi def link gitconfigEscape Special hi def link gitconfigError Error let b:current_syntax = "gitconfig" diff --git a/runtime/syntax/xml.vim b/runtime/syntax/xml.vim index 7b503abf49..7c9791a7cc 100644 --- a/runtime/syntax/xml.vim +++ b/runtime/syntax/xml.vim @@ -1,11 +1,15 @@ " Vim syntax file -" Language: XML -" Maintainer: Johannes Zellner <johannes@zellner.org> -" Author and previous maintainer: -" Paul Siegmann <pauls@euronet.nl> -" Last Change: 2013 Jun 07 +" Language: XML +" Maintainer: Christian Brabandt <cb@256bit.org> +" Repository: https://github.com/chrisbra/vim-xml-ftplugin +" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Author: Paul Siegmann <pauls@euronet.nl> +" Last Changed: Nov 03, 2019 " Filenames: *.xml -" $Id: xml.vim,v 1.3 2006/04/11 21:32:00 vimboss Exp $ +" Last Change: +" 20190923 - Fix xmlEndTag to match xmlTag (vim/vim#884) +" 20190924 - Fix xmlAttribute property (amadeus/vim-xml@d8ce1c946) +" 20191103 - Enable spell checking globally " CONFIGURATION: " syntax folding can be turned on by @@ -49,6 +53,12 @@ set cpo&vim syn case match +" Allow spell checking in tag values, +" there is no syntax region for that, +" so enable spell checking in top-level elements +" <tag>This text is spell checked</tag> +syn spell toplevel + " mark illegal characters syn match xmlError "[<&]" @@ -81,7 +91,7 @@ syn match xmlEqual +=+ display " ^^^^^^^^^^^^^ " syn match xmlAttrib - \ +[-'"<]\@1<!\<[a-zA-Z:_][-.0-9a-zA-Z:_]*\>\%(['">]\@!\|$\)+ + \ +[-'"<]\@1<!\<[a-zA-Z:_][-.0-9a-zA-Z:_]*\>\%(['"]\@!\|$\)+ \ contained \ contains=xmlAttribPunct,@xmlAttribHook \ display @@ -122,7 +132,7 @@ endif " ^^^ " syn match xmlTagName - \ +<\@1<=[^ /!?<>"']\++ + \ +\%(<\|</\)\@2<=[^ /!?<>"']\++ \ contained \ contains=xmlNamespace,xmlAttribPunct,@xmlTagHook \ display @@ -157,11 +167,11 @@ if exists('g:xml_syntax_folding') " </tag> " ^^^^^^ " - syn match xmlEndTag - \ +</[^ /!?<>"']\+>+ + syn region xmlEndTag + \ matchgroup=xmlTag start=+</[^ /!?<>"']\@=+ + \ matchgroup=xmlTag end=+>+ \ contained - \ contains=xmlNamespace,xmlAttribPunct,@xmlTagHook - + \ contains=xmlTagName,xmlNamespace,xmlAttribPunct,@xmlTagHook " tag elements with syntax-folding. " NOTE: NO HIGHLIGHTING -- highlighting is done by contained elements @@ -181,7 +191,7 @@ if exists('g:xml_syntax_folding') \ start=+<\z([^ /!?<>"']\+\)+ \ skip=+<!--\_.\{-}-->+ \ end=+</\z1\_\s\{-}>+ - \ matchgroup=xmlEndTag end=+/>+ + \ end=+/>+ \ fold \ contains=xmlTag,xmlEndTag,xmlCdata,xmlRegion,xmlComment,xmlEntity,xmlProcessing,@xmlRegionHook,@Spell \ keepend @@ -198,9 +208,10 @@ else \ matchgroup=xmlTag end=+>+ \ contains=xmlError,xmlTagName,xmlAttrib,xmlEqual,xmlString,@xmlStartTagHook - syn match xmlEndTag - \ +</[^ /!?<>"']\+>+ - \ contains=xmlNamespace,xmlAttribPunct,@xmlTagHook + syn region xmlEndTag + \ matchgroup=xmlTag start=+</[^ /!?<>"']\@=+ + \ matchgroup=xmlTag end=+>+ + \ contains=xmlTagName,xmlNamespace,xmlAttribPunct,@xmlTagHook endif diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 99cdde300d..ffa44c33cd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3198,7 +3198,7 @@ void maketitle(void) int use_sandbox = false; int save_called_emsg = called_emsg; - use_sandbox = was_set_insecurely((char_u *)"titlestring", 0); + use_sandbox = was_set_insecurely(curwin, (char_u *)"titlestring", 0); called_emsg = false; build_stl_str_hl(curwin, (char_u *)buf, sizeof(buf), p_titlestring, use_sandbox, @@ -3309,7 +3309,7 @@ void maketitle(void) int use_sandbox = false; int save_called_emsg = called_emsg; - use_sandbox = was_set_insecurely((char_u *)"iconstring", 0); + use_sandbox = was_set_insecurely(curwin, (char_u *)"iconstring", 0); called_emsg = false; build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, use_sandbox, diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 402211080b..054b788940 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1178,8 +1178,7 @@ int eval_foldexpr(char_u *arg, int *cp) { typval_T tv; varnumber_T retval; - int use_sandbox = was_set_insecurely((char_u *)"foldexpr", - OPT_LOCAL); + int use_sandbox = was_set_insecurely(curwin, (char_u *)"foldexpr", OPT_LOCAL); ++emsg_off; if (use_sandbox) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b8a0c3184b..d2ccbe3e6d 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1182,6 +1182,7 @@ static void do_filter( char_u *cmd_buf; buf_T *old_curbuf = curbuf; int shell_flags = 0; + const int stmp = p_stmp; if (*cmd == NUL) /* no filter command */ return; @@ -1210,16 +1211,16 @@ static void do_filter( if (do_out) shell_flags |= kShellOptDoOut; - if (!do_in && do_out && !p_stmp) { + if (!do_in && do_out && !stmp) { // Use a pipe to fetch stdout of the command, do not use a temp file. shell_flags |= kShellOptRead; curwin->w_cursor.lnum = line2; - } else if (do_in && !do_out && !p_stmp) { + } else if (do_in && !do_out && !stmp) { // Use a pipe to write stdin of the command, do not use a temp file. shell_flags |= kShellOptWrite; curbuf->b_op_start.lnum = line1; curbuf->b_op_end.lnum = line2; - } else if (do_in && do_out && !p_stmp) { + } else if (do_in && do_out && !stmp) { // Use a pipe to write stdin and fetch stdout of the command, do not // use a temp file. shell_flags |= kShellOptRead | kShellOptWrite; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 42454b7c9a..04624be9a2 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -4232,25 +4232,29 @@ static void script_host_execute(char *name, exarg_T *eap) static void script_host_execute_file(char *name, exarg_T *eap) { - uint8_t buffer[MAXPATHL]; - vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); + if (!eap->skip) { + uint8_t buffer[MAXPATHL]; + vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); - list_T *args = tv_list_alloc(3); - // filename - tv_list_append_string(args, (const char *)buffer, -1); - // current range - tv_list_append_number(args, (int)eap->line1); - tv_list_append_number(args, (int)eap->line2); - (void)eval_call_provider(name, "execute_file", args, true); + list_T *args = tv_list_alloc(3); + // filename + tv_list_append_string(args, (const char *)buffer, -1); + // current range + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); + (void)eval_call_provider(name, "execute_file", args, true); + } } static void script_host_do_range(char *name, exarg_T *eap) { - list_T *args = tv_list_alloc(3); - tv_list_append_number(args, (int)eap->line1); - tv_list_append_number(args, (int)eap->line2); - tv_list_append_string(args, (const char *)eap->arg, -1); - (void)eval_call_provider(name, "do_range", args, true); + if (!eap->skip) { + list_T *args = tv_list_alloc(3); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); + tv_list_append_string(args, (const char *)eap->arg, -1); + (void)eval_call_provider(name, "do_range", args, true); + } } /// ":drop" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index fc699e8826..d65387f83b 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1873,8 +1873,6 @@ static char_u * do_one_cmd(char_u **cmdlinep, case CMD_python3: case CMD_pythonx: case CMD_pyx: - case CMD_pyxdo: - case CMD_pyxfile: case CMD_return: case CMD_rightbelow: case CMD_ruby: diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 5e28ca6538..654aa6d5ba 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1835,10 +1835,11 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, curwin = wp; curbuf = wp->w_buffer; - ++emsg_silent; /* handle exceptions, but don't display errors */ - text = eval_to_string_safe(wp->w_p_fdt, NULL, - was_set_insecurely((char_u *)"foldtext", OPT_LOCAL)); - --emsg_silent; + emsg_silent++; // handle exceptions, but don't display errors + text = eval_to_string_safe( + wp->w_p_fdt, NULL, + was_set_insecurely(wp, (char_u *)"foldtext", OPT_LOCAL)); + emsg_silent--; if (text == NULL || did_emsg) got_fdt_error = TRUE; diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 4a64cc31b1..2c954008b3 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -546,7 +546,7 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, curwin->w_botline = lnum + 63; printer_page_num = pagenum; - use_sandbox = was_set_insecurely((char_u *)"printheader", 0); + use_sandbox = was_set_insecurely(curwin, (char_u *)"printheader", 0); build_stl_str_hl(curwin, tbuf, (size_t)width + IOSIZE, p_header, use_sandbox, ' ', width, NULL, NULL); diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 9e6693afdf..fae971b3b3 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -453,7 +453,8 @@ int get_expr_indent(void) colnr_T save_curswant; int save_set_curswant; int save_State; - int use_sandbox = was_set_insecurely((char_u *)"indentexpr", OPT_LOCAL); + int use_sandbox = was_set_insecurely( + curwin, (char_u *)"indentexpr", OPT_LOCAL); // Save and restore cursor position and curswant, in case it was changed // * via :normal commands. diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index a9a57d386b..a640b97d3b 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -22,6 +22,13 @@ #include "nvim/memline.h" #include "nvim/buffer.h" +#define TS_META_PARSER "treesitter_parser" +#define TS_META_TREE "treesitter_tree" +#define TS_META_NODE "treesitter_node" +#define TS_META_QUERY "treesitter_query" +#define TS_META_QUERYCURSOR "treesitter_querycursor" +#define TS_META_TREECURSOR "treesitter_treecursor" + typedef struct { TSQueryCursor *cursor; int predicated_match; @@ -115,12 +122,12 @@ void tslua_init(lua_State *L) langs = pmap_new(cstr_t)(); // type metatables - build_meta(L, "treesitter_parser", parser_meta); - build_meta(L, "treesitter_tree", tree_meta); - build_meta(L, "treesitter_node", node_meta); - build_meta(L, "treesitter_query", query_meta); - build_meta(L, "treesitter_querycursor", querycursor_meta); - build_meta(L, "treesitter_treecursor", treecursor_meta); + build_meta(L, TS_META_PARSER, parser_meta); + build_meta(L, TS_META_TREE, tree_meta); + build_meta(L, TS_META_NODE, node_meta); + build_meta(L, TS_META_QUERY, query_meta); + build_meta(L, TS_META_QUERYCURSOR, querycursor_meta); + build_meta(L, TS_META_TREECURSOR, treecursor_meta); } int tslua_has_language(lua_State *L) @@ -132,12 +139,8 @@ int tslua_has_language(lua_State *L) int tslua_add_language(lua_State *L) { - if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { - return luaL_error(L, "string expected"); - } - - const char *path = lua_tostring(L, 1); - const char *lang_name = lua_tostring(L, 2); + const char *path = luaL_checkstring(L, 1); + const char *lang_name = luaL_checkstring(L, 2); if (pmap_has(cstr_t)(langs, lang_name)) { return 0; @@ -176,8 +179,9 @@ int tslua_add_language(lua_State *L) || lang_version > TREE_SITTER_LANGUAGE_VERSION) { return luaL_error( L, - "ABI version mismatch : expected %d, found %d", - TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, lang_version); + "ABI version mismatch : supported between %d and %d, found %d", + TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, + TREE_SITTER_LANGUAGE_VERSION, lang_version); } pmap_put(cstr_t)(langs, xstrdup(lang_name), lang); @@ -188,10 +192,7 @@ int tslua_add_language(lua_State *L) int tslua_inspect_lang(lua_State *L) { - if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) { - return luaL_error(L, "string expected"); - } - const char *lang_name = lua_tostring(L, 1); + const char *lang_name = luaL_checkstring(L, 1); TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); if (!lang) { @@ -232,11 +233,9 @@ int tslua_inspect_lang(lua_State *L) int tslua_push_parser(lua_State *L) { - // Gather language - if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) { - return luaL_error(L, "string expected"); - } - const char *lang_name = lua_tostring(L, 1); + // Gather language name + const char *lang_name = luaL_checkstring(L, 1); + TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); @@ -250,14 +249,14 @@ int tslua_push_parser(lua_State *L) return luaL_error(L, "Failed to load language : %s", lang_name); } - lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_parser"); // [udata, meta] + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_PARSER); // [udata, meta] lua_setmetatable(L, -2); // [udata] return 1; } static TSParser ** parser_check(lua_State *L, uint16_t index) { - return luaL_checkudata(L, index, "treesitter_parser"); + return luaL_checkudata(L, index, TS_META_PARSER); } static int parser_gc(lua_State *L) @@ -388,7 +387,7 @@ static int parser_parse(lua_State *L) TSRange *changed = old_tree ? ts_tree_get_changed_ranges( old_tree, new_tree, &n_ranges) : NULL; - tslua_push_tree(L, new_tree, false); // [tree] + push_tree(L, new_tree, false); // [tree] push_ranges(L, changed, n_ranges); // [tree, ranges] @@ -403,7 +402,7 @@ static int tree_copy(lua_State *L) return 0; } - tslua_push_tree(L, *tree, true); // [tree] + push_tree(L, *tree, true); // [tree] return 1; } @@ -435,6 +434,72 @@ static int tree_edit(lua_State *L) return 0; } +// Use the top of the stack (without popping it) to create a TSRange, it can be +// either a lua table or a TSNode +static void range_from_lua(lua_State *L, TSRange *range) +{ + TSNode node; + + if (lua_istable(L, -1)) { + // should be a table of 6 elements + if (lua_objlen(L, -1) != 6) { + goto error; + } + + uint32_t start_row, start_col, start_byte, end_row, end_col, end_byte; + lua_rawgeti(L, -1, 1); // [ range, start_row] + start_row = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 2); // [ range, start_col] + start_col = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 3); // [ range, start_byte] + start_byte = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 4); // [ range, end_row] + end_row = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 5); // [ range, end_col] + end_col = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 6); // [ range, end_byte] + end_byte = luaL_checkinteger(L, -1); + lua_pop(L, 1); // [ range ] + + *range = (TSRange) { + .start_point = (TSPoint) { + .row = start_row, + .column = start_col + }, + .end_point = (TSPoint) { + .row = end_row, + .column = end_col + }, + .start_byte = start_byte, + .end_byte = end_byte, + }; + } else if (node_check(L, -1, &node)) { + *range = (TSRange) { + .start_point = ts_node_start_point(node), + .end_point = ts_node_end_point(node), + .start_byte = ts_node_start_byte(node), + .end_byte = ts_node_end_byte(node) + }; + } else { + goto error; + } + return; +error: + luaL_error( + L, + "Ranges can only be made from 6 element long tables or nodes."); +} + static int parser_set_ranges(lua_State *L) { if (lua_gettop(L) < 2) { @@ -461,22 +526,8 @@ static int parser_set_ranges(lua_State *L) // [ parser, ranges ] for (size_t index = 0; index < tbl_len; index++) { lua_rawgeti(L, 2, index + 1); // [ parser, ranges, range ] - - TSNode node; - if (!node_check(L, -1, &node)) { - xfree(ranges); - return luaL_error( - L, - "ranges should be tables of nodes."); - } - lua_pop(L, 1); // [ parser, ranges ] - - ranges[index] = (TSRange) { - .start_point = ts_node_start_point(node), - .end_point = ts_node_end_point(node), - .start_byte = ts_node_start_byte(node), - .end_byte = ts_node_end_byte(node) - }; + range_from_lua(L, ranges + index); + lua_pop(L, 1); } // This memcpies ranges, thus we can free it afterwards @@ -506,7 +557,7 @@ static int parser_get_ranges(lua_State *L) /// push tree interface on lua stack. /// /// This makes a copy of the tree, so ownership of the argument is unaffected. -void tslua_push_tree(lua_State *L, TSTree *tree, bool do_copy) +void push_tree(lua_State *L, TSTree *tree, bool do_copy) { if (tree == NULL) { lua_pushnil(L); @@ -520,7 +571,7 @@ void tslua_push_tree(lua_State *L, TSTree *tree, bool do_copy) *ud = tree; } - lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_tree"); // [udata, meta] + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_TREE); // [udata, meta] lua_setmetatable(L, -2); // [udata] // table used for node wrappers to keep a reference to tree wrapper @@ -534,7 +585,7 @@ void tslua_push_tree(lua_State *L, TSTree *tree, bool do_copy) static TSTree **tree_check(lua_State *L, uint16_t index) { - TSTree **ud = luaL_checkudata(L, index, "treesitter_tree"); + TSTree **ud = luaL_checkudata(L, index, TS_META_TREE); return ud; } @@ -582,7 +633,7 @@ static void push_node(lua_State *L, TSNode node, int uindex) } TSNode *ud = lua_newuserdata(L, sizeof(TSNode)); // [udata] *ud = node; - lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_node"); // [udata, meta] + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_NODE); // [udata, meta] lua_setmetatable(L, -2); // [udata] lua_getfenv(L, uindex); // [udata, reftable] lua_setfenv(L, -2); // [udata] @@ -590,7 +641,7 @@ static void push_node(lua_State *L, TSNode node, int uindex) static bool node_check(lua_State *L, int index, TSNode *res) { - TSNode *ud = luaL_checkudata(L, index, "treesitter_node"); + TSNode *ud = luaL_checkudata(L, index, TS_META_NODE); if (ud) { *res = *ud; return true; @@ -618,13 +669,12 @@ static int node_eq(lua_State *L) if (!node_check(L, 1, &node)) { return 0; } - // This should only be called if both x and y in "x == y" has the - // treesitter_node metatable. So it is ok to error out otherwise. - TSNode *ud = luaL_checkudata(L, 2, "treesitter_node"); - if (!ud) { + + TSNode node2; + if (!node_check(L, 2, &node2)) { return 0; } - TSNode node2 = *ud; + lua_pushboolean(L, ts_node_eq(node, node2)); return 1; } @@ -859,7 +909,7 @@ static int node_named_descendant_for_range(lua_State *L) static int node_next_child(lua_State *L) { TSTreeCursor *ud = luaL_checkudata( - L, lua_upvalueindex(1), "treesitter_treecursor"); + L, lua_upvalueindex(1), TS_META_TREECURSOR); if (!ud) { return 0; } @@ -909,7 +959,7 @@ static int node_iter_children(lua_State *L) TSTreeCursor *ud = lua_newuserdata(L, sizeof(TSTreeCursor)); // [udata] *ud = ts_tree_cursor_new(source); - lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_treecursor"); // [udata, mt] + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_TREECURSOR); // [udata, mt] lua_setmetatable(L, -2); // [udata] lua_pushvalue(L, 1); // [udata, source_node] lua_pushcclosure(L, node_next_child, 2); @@ -919,7 +969,7 @@ static int node_iter_children(lua_State *L) static int treecursor_gc(lua_State *L) { - TSTreeCursor *ud = luaL_checkudata(L, 1, "treesitter_treecursor"); + TSTreeCursor *ud = luaL_checkudata(L, 1, TS_META_TREECURSOR); ts_tree_cursor_delete(ud); return 0; } @@ -1031,7 +1081,7 @@ static int node_rawquery(lua_State *L) ud->cursor = cursor; ud->predicated_match = -1; - lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_querycursor"); + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); lua_setmetatable(L, -2); // [udata] lua_pushvalue(L, 1); // [udata, node] @@ -1051,7 +1101,7 @@ static int node_rawquery(lua_State *L) static int querycursor_gc(lua_State *L) { - TSLua_cursor *ud = luaL_checkudata(L, 1, "treesitter_querycursor"); + TSLua_cursor *ud = luaL_checkudata(L, 1, TS_META_QUERYCURSOR); ts_query_cursor_delete(ud->cursor); return 0; } @@ -1084,7 +1134,7 @@ int ts_lua_parse_query(lua_State *L) TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata] *ud = query; - lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_query"); // [udata, meta] + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERY); // [udata, meta] lua_setmetatable(L, -2); // [udata] return 1; } @@ -1102,7 +1152,7 @@ static const char *query_err_string(TSQueryError err) { static TSQuery *query_check(lua_State *L, int index) { - TSQuery **ud = luaL_checkudata(L, index, "treesitter_query"); + TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY); return *ud; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 9e7d81fc82..40dd5f0b5c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4117,8 +4117,8 @@ fex_format( int c /* character to be inserted */ ) { - int use_sandbox = was_set_insecurely((char_u *)"formatexpr", - OPT_LOCAL); + int use_sandbox = was_set_insecurely( + curwin, (char_u *)"formatexpr", OPT_LOCAL); int r; char_u *fex; diff --git a/src/nvim/option.c b/src/nvim/option.c index 0a91687352..c1b071b04e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -652,7 +652,14 @@ void set_init_1(bool clean_arg) { const char *shell = os_getenv("SHELL"); if (shell != NULL) { - set_string_default("sh", (char *) shell, false); + if (vim_strchr((const char_u *)shell, ' ') != NULL) { + const size_t len = strlen(shell) + 3; // two quotes and a trailing NUL + char *const cmd = xmalloc(len); + snprintf(cmd, len, "\"%s\"", shell); + set_string_default("sh", cmd, true); + } else { + set_string_default("sh", (char *)shell, false); + } } } @@ -945,7 +952,7 @@ set_option_default( } // The default value is not insecure. - uint32_t *flagsp = insecure_flag(opt_idx, opt_flags); + uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags); *flagsp = *flagsp & ~P_INSECURE; } @@ -987,10 +994,9 @@ static void set_string_default(const char *name, char *val, bool allocated) xfree(options[opt_idx].def_val[VI_DEFAULT]); } - options[opt_idx].def_val[VI_DEFAULT] = (char_u *) ( - allocated - ? (char_u *) val - : (char_u *) xstrdup(val)); + options[opt_idx].def_val[VI_DEFAULT] = allocated + ? (char_u *)val + : (char_u *)xstrdup(val); options[opt_idx].flags |= P_DEF_ALLOCED; } } @@ -1880,7 +1886,7 @@ int do_set( saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0; { - uint32_t *p = insecure_flag(opt_idx, opt_flags); + uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags); const int secure_saved = secure; // When an option is set in the sandbox, from a @@ -2007,7 +2013,7 @@ static void did_set_option( /* When an option is set in the sandbox, from a modeline or in secure mode * set the P_INSECURE flag. Otherwise, if a new value is stored reset the * flag. */ - uint32_t *p = insecure_flag(opt_idx, opt_flags); + uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags); if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) { @@ -2346,12 +2352,12 @@ static void check_string_option(char_u **pp) /// Return true when option "opt" was set from a modeline or in secure mode. /// Return false when it wasn't. /// Return -1 for an unknown option. -int was_set_insecurely(char_u *opt, int opt_flags) +int was_set_insecurely(win_T *const wp, char_u *opt, int opt_flags) { int idx = findoption((const char *)opt); if (idx >= 0) { - uint32_t *flagp = insecure_flag(idx, opt_flags); + uint32_t *flagp = insecure_flag(wp, idx, opt_flags); return (*flagp & P_INSECURE) != 0; } internal_error("was_set_insecurely()"); @@ -2360,16 +2366,16 @@ int was_set_insecurely(char_u *opt, int opt_flags) /// Get a pointer to the flags used for the P_INSECURE flag of option /// "opt_idx". For some local options a local flags field is used. -static uint32_t *insecure_flag(int opt_idx, int opt_flags) +static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags) { if (opt_flags & OPT_LOCAL) switch ((int)options[opt_idx].indir) { - case PV_STL: return &curwin->w_p_stl_flags; - case PV_FDE: return &curwin->w_p_fde_flags; - case PV_FDT: return &curwin->w_p_fdt_flags; - case PV_INDE: return &curbuf->b_p_inde_flags; - case PV_FEX: return &curbuf->b_p_fex_flags; - case PV_INEX: return &curbuf->b_p_inex_flags; + case PV_STL: return &wp->w_p_stl_flags; + case PV_FDE: return &wp->w_p_fde_flags; + case PV_FDT: return &wp->w_p_fdt_flags; + case PV_INDE: return &wp->w_buffer->b_p_inde_flags; + case PV_FEX: return &wp->w_buffer->b_p_fex_flags; + case PV_INEX: return &wp->w_buffer->b_p_inex_flags; } // Nothing special, return global flags field. @@ -7173,46 +7179,6 @@ int get_sts_value(void) return (int)result; } -/// Check matchpairs option for "*initc". -/// If there is a match set "*initc" to the matching character and "*findc" to -/// the opposite character. Set "*backwards" to the direction. -/// When "switchit" is true swap the direction. -void find_mps_values(int *initc, int *findc, int *backwards, int switchit) -{ - char_u *ptr = curbuf->b_p_mps; - - while (*ptr != NUL) { - if (utf_ptr2char(ptr) == *initc) { - if (switchit) { - *findc = *initc; - *initc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1); - *backwards = true; - } else { - *findc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1); - *backwards = false; - } - return; - } - char_u *prev = ptr; - ptr += utfc_ptr2len(ptr) + 1; - if (utf_ptr2char(ptr) == *initc) { - if (switchit) { - *findc = *initc; - *initc = utf_ptr2char(prev); - *backwards = false; - } else { - *findc = utf_ptr2char(prev); - *backwards = true; - } - return; - } - ptr += utfc_ptr2len(ptr); - if (*ptr == ',') { - ptr++; - } - } -} - /// This is called when 'breakindentopt' is changed and when a window is /// initialized static bool briopt_check(win_T *wp) diff --git a/src/nvim/path.c b/src/nvim/path.c index 793f917f06..f52fbbd5c8 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1610,10 +1610,10 @@ void simplify_filename(char_u *filename) static char *eval_includeexpr(const char *const ptr, const size_t len) { - set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t) len); - char *res = (char *) eval_to_string_safe( - curbuf->b_p_inex, NULL, was_set_insecurely((char_u *)"includeexpr", - OPT_LOCAL)); + set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len); + char *res = (char *)eval_to_string_safe( + curbuf->b_p_inex, NULL, + was_set_insecurely(curwin, (char_u *)"includeexpr", OPT_LOCAL)); set_vim_var_string(VV_FNAME, NULL, 0); return res; } diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po index 79048eac39..fa6674469c 100644 --- a/src/nvim/po/af.po +++ b/src/nvim/po/af.po @@ -1348,10 +1348,6 @@ msgstr "E143: Outobevele het nuwe buffer %s onverwags geskrap" msgid "E144: non-numeric argument to :z" msgstr "E144: nie-numeriese parameter vir :z" -#, fuzzy -#~ msgid "E145: Shell commands not allowed in restricted mode" -#~ msgstr "E145: Dop bevele nie toegelaat in rvim" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Patrone kan nie deur letters afgebaken word nie" diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po index 7e83cb08ed..be4206f36e 100644 --- a/src/nvim/po/ca.po +++ b/src/nvim/po/ca.po @@ -1234,10 +1234,6 @@ msgstr "E143: Una auto-ordre ha eliminat el buffer nou %s inesperadament" msgid "E144: non-numeric argument to :z" msgstr "E144: Argument no numric per a :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Les ordres shell no estan permeses en l'rvim" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Les expressions regulars no poden estar delimitades per lletres" diff --git a/src/nvim/po/cs.cp1250.po b/src/nvim/po/cs.cp1250.po index 26bdbe8c45..5b9f3d3a58 100644 --- a/src/nvim/po/cs.cp1250.po +++ b/src/nvim/po/cs.cp1250.po @@ -1249,10 +1249,6 @@ msgstr "E143: Automatick pkazy neoekvan smazaly nov buffer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: neseln argument pro :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim nepovoluje pouit pkaz shellu" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulrn vrazy nesm bt oddleny psmeny" diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po index 986d6753a8..31a90dc514 100644 --- a/src/nvim/po/cs.po +++ b/src/nvim/po/cs.po @@ -1249,10 +1249,6 @@ msgstr "E143: Automatick pkazy neoekvan smazaly nov buffer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: neseln argument pro :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim nepovoluje pouit pkaz shellu" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulrn vrazy nesm bt oddleny psmeny" diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po index 58cd19210b..f35272810b 100644 --- a/src/nvim/po/da.po +++ b/src/nvim/po/da.po @@ -980,9 +980,6 @@ msgstr "E143: Autokommandoer slettede uventede ny buffer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: ikke-numerisk argument til :z" -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Skalkommandoer er ikke tilladt i rvim" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulære udtryk kan ikke afgrænses af bogstaver" diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po index a6ba8476d8..a2e04965e5 100644 --- a/src/nvim/po/de.po +++ b/src/nvim/po/de.po @@ -3,7 +3,7 @@ # Do ":help uganda" in Vim to read copying and usage conditions. # Do ":help credits" in Vim to see a list of people who contributed. # -# Previous-Translator(s): +# Previous-Translator(s): # Johannes Zellner <johannes@zellner.org> # Gerfried Fuchs <alfie@ist.org> msgid "" @@ -659,10 +659,6 @@ msgstr "E143: Autokommandos lschten unerwartet neuen Puffer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: Nicht-numerisches Argument fr :z" -#: ../ex_cmds.c:3398 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Shell-Befehle sind in rvim nicht erlaubt" - #: ../ex_cmds.c:3492 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulre Ausdrcke knnen nicht durch Buchstaben begrenzt werden" diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po index 00a05195b4..7919fc8946 100644 --- a/src/nvim/po/en_GB.po +++ b/src/nvim/po/en_GB.po @@ -1194,10 +1194,6 @@ msgstr "" msgid "E144: non-numeric argument to :z" msgstr "" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regular expressions cannot be delimited by letters" diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po index d1ce47d97c..99c46c7275 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -969,9 +969,6 @@ msgstr "E143: Aŭtokomandoj neatendite forviŝis novan bufron %s" msgid "E144: non-numeric argument to :z" msgstr "E144: nenumera argumento de :z" -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Ŝelkomandoj nepermeseblaj en rvim" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Ne eblas limigi regulesprimon per literoj" diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po index f30e8780f9..eeea27610d 100644 --- a/src/nvim/po/es.po +++ b/src/nvim/po/es.po @@ -1234,10 +1234,6 @@ msgstr "E143: Las auto-órdenes han eliminado al nuevo búfer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: Argumento no numérico para \":z\"" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: No se permiten órdenes de consola en rvim" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Las expresiones regulares no se pueden delimitar con letras" diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po index f568a34b3c..4489139cfb 100644 --- a/src/nvim/po/fi.po +++ b/src/nvim/po/fi.po @@ -1515,10 +1515,6 @@ msgstr "E143: Autocommand poisti uuden puskurin odotuksen vastaisesti %s" msgid "E144: non-numeric argument to :z" msgstr "E144: :z:n argumentti ei ole numero" -#, fuzzy -#~ msgid "E145: Shell commands not allowed in restricted mode" -#~ msgstr "E145: Kuoren komennot eivät toimi rvimissä" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Säännöllistä ilmausta ei voi rajata kirjaimilla" diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index ea6fe302fe..bb60649c91 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -1117,12 +1117,6 @@ msgstr "E143: Une autocommande a effac le nouveau tampon %s" msgid "E144: non-numeric argument to :z" msgstr "E144: L'argument de :z n'est pas numrique" -# AB - La version franaise fera peut-tre mieux passer l'amre pilule. -# La consultation de l'aide donnera l'explication complte ceux qui -# ne comprendraient pas quoi ce message est d. -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Les commandes externes sont indisponibles dans rvim" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "" "E146: Les expressions rgulires ne peuvent pas tre dlimites par des " diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po index 617b805eb8..1104b31c32 100644 --- a/src/nvim/po/ga.po +++ b/src/nvim/po/ga.po @@ -967,9 +967,6 @@ msgstr "E143: Scrios na huathorduithe maoln nua %s go tobann" msgid "E144: non-numeric argument to :z" msgstr "E144: argint neamhuimhriil chun :z" -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: N cheadatear orduithe blaoisce i rvim" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "" "E146: N cheadatear litreacha mar theormharcir ar shloinn ionadaochta" diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po index 9d5709e1ab..511f910b71 100644 --- a/src/nvim/po/it.po +++ b/src/nvim/po/it.po @@ -1220,10 +1220,6 @@ msgstr "" msgid "E144: non-numeric argument to :z" msgstr "E144: argomento non-numerico a :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Comandi Shell non permessi in rvim" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Le espressioni regolari non possono essere delimitate da lettere" diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po index dc3c4368ab..523e9ca4e3 100644 --- a/src/nvim/po/ja.euc-jp.po +++ b/src/nvim/po/ja.euc-jp.po @@ -987,9 +987,6 @@ msgstr "E143: autocommandͽХåե %s ޤ" msgid "E144: non-numeric argument to :z" msgstr "E144: ǤϤʤ :z Ϥޤ" -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvimǤϥ륳ޥɤȤޤ" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: ɽʸǶڤ뤳ȤǤޤ" diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po index 4c5661464a..5a69d0c5bf 100644 --- a/src/nvim/po/ja.po +++ b/src/nvim/po/ja.po @@ -987,9 +987,6 @@ msgstr "E143: autocommandが予期せず新しいバッファ %s を削除しま msgid "E144: non-numeric argument to :z" msgstr "E144: 数ではない引数が :z に渡されました" -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvimではシェルコマンドを使えません" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: 正規表現は文字で区切ることができません" diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po index e90081bcfd..128b238f8b 100644 --- a/src/nvim/po/ko.UTF-8.po +++ b/src/nvim/po/ko.UTF-8.po @@ -1215,10 +1215,6 @@ msgstr "E143: Autocommand가 뜻 밖에 새 버퍼 %s을(를) 지웠습니다" msgid "E144: non-numeric argument to :z" msgstr "E144: 숫자가 아닌 인자가 :z에 주어졌습니다" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim에서는 쉘 명령을 사용할 수 없습니다" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: 정규표현식은 글자로 구분될 수 없습니다" diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po index b99e8ce465..34617ccf18 100644 --- a/src/nvim/po/nb.po +++ b/src/nvim/po/nb.po @@ -1231,10 +1231,6 @@ msgstr "E143: Autokommandoer slettet uventet den nye bufferen %s" msgid "E144: non-numeric argument to :z" msgstr "E144: Ikke-numerisk parameter til :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Skallkommandoer er ikke tillatt i rvim" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulre uttrykk kan ikke bli adskilt av bokstaver" diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po index 56bcd94e79..30f34508f5 100644 --- a/src/nvim/po/nl.po +++ b/src/nvim/po/nl.po @@ -1217,10 +1217,6 @@ msgstr "E143: 'Autocommands' hebben het nieuwe buffer %s onverwacht verwijderd" msgid "E144: non-numeric argument to :z" msgstr "E144: niet-numeriek argument voor :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: in rvim zijn shell-opdrachten zijn niet toegestaan" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: reguliere expressies kunnen niet begrensd worden door letters" diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po index b99e8ce465..34617ccf18 100644 --- a/src/nvim/po/no.po +++ b/src/nvim/po/no.po @@ -1231,10 +1231,6 @@ msgstr "E143: Autokommandoer slettet uventet den nye bufferen %s" msgid "E144: non-numeric argument to :z" msgstr "E144: Ikke-numerisk parameter til :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Skallkommandoer er ikke tillatt i rvim" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulre uttrykk kan ikke bli adskilt av bokstaver" diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po index a348bf6203..f5c452e924 100644 --- a/src/nvim/po/pl.UTF-8.po +++ b/src/nvim/po/pl.UTF-8.po @@ -1201,10 +1201,6 @@ msgstr "E143: Autokomendy nieoczekiwanie skasowały nowy bufor %s" msgid "E144: non-numeric argument to :z" msgstr "E144: nienumeryczny argument dla :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Komendy powłoki są niedozwolone w rvim" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Wzorce regularne nie mogą być rozgraniczane literami" diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po index 543f0bce27..4f39cb5bdb 100644 --- a/src/nvim/po/pt_BR.po +++ b/src/nvim/po/pt_BR.po @@ -4210,10 +4210,6 @@ msgstr "" msgid "E144: non-numeric argument to :z" msgstr "E144: argumento no-numrico passado a :z" -#: ../ex_cmds.c:3398 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Comandos do shell no so permitidos no rvim" - #: ../ex_cmds.c:3492 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Expresses regulares no podem ser delimitadas por letras" diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po index e5be489d72..62f892d257 100644 --- a/src/nvim/po/ru.po +++ b/src/nvim/po/ru.po @@ -1205,10 +1205,6 @@ msgstr "E143: Автокоманды неожиданно убили новый msgid "E144: non-numeric argument to :z" msgstr "E144: Параметр команды :z должен быть числом" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Использование команд оболочки не допускается в rvim." - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Регулярные выражения не могут разделяться буквами" diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po index 74b8e1039c..ced343bf6b 100644 --- a/src/nvim/po/sk.cp1250.po +++ b/src/nvim/po/sk.cp1250.po @@ -1219,10 +1219,6 @@ msgstr "E143: Automatick prkazy neoakvane zmazali nov buffer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: neseln argument pre :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim nepovouje pouitie prkazov shellu" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulrne vrazy nesm by oddelen psmenami" diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po index d3f954f6d8..66b3d5abb1 100644 --- a/src/nvim/po/sk.po +++ b/src/nvim/po/sk.po @@ -1219,10 +1219,6 @@ msgstr "E143: Automatick prkazy neoakvane zmazali nov buffer %s" msgid "E144: non-numeric argument to :z" msgstr "E144: neseln argument pre :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim nepovouje pouitie prkazov shellu" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regulrne vrazy nesm by oddelen psmenami" diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po index 88c5d18866..4c157658e5 100644 --- a/src/nvim/po/sr.po +++ b/src/nvim/po/sr.po @@ -424,7 +424,7 @@ msgstr "Опција 'dictionary' је празна" msgid "'thesaurus' option is empty" msgstr "Опција 'thesaurus' је празна" -#, c-format +#, c-format msgid "Scanning dictionary: %s" msgstr "Скенирање речника: %s" @@ -702,7 +702,7 @@ msgstr "E785: complete() може да се користи само у режи msgid "&Ok" msgstr "&Ок" -#, c-format +#, c-format msgid "+-%s%3ld line: " msgid_plural "+-%s%3ld lines: " msgstr[0] "+-%s%3ld линија: " @@ -810,7 +810,7 @@ msgstr "E677: Грешка при упису temp датотеке" msgid "E921: Invalid callback argument" msgstr "E921: Неисправан callback аргумент" -#, c-format +#, c-format msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" msgstr "<%s>%s%s %d, Хекс %02x, Окт %03o, Дигр %s" @@ -818,11 +818,11 @@ msgstr "<%s>%s%s %d, Хекс %02x, Окт %03o, Дигр %s" msgid "<%s>%s%s %d, Hex %02x, Octal %03o" msgstr "<%s>%s%s %d, Хекс %02x, Октално %03o" -#, c-format +#, c-format msgid "> %d, Hex %04x, Oct %o, Digr %s" msgstr "> %d, Хекс %04x, Окт %o, Дигр %s" -#, c-format +#, c-format msgid "> %d, Hex %08x, Oct %o, Digr %s" msgstr "> %d, Хекс %08x, Окт %o, Дигр %s" @@ -981,9 +981,6 @@ msgstr "E143: Аутокоманде су неочекивано обрисал msgid "E144: non-numeric argument to :z" msgstr "E144: ненумерички аргумент за :z" -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Shell команде нису дозвољене у rvim" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Регуларни изрази не могу да се раздвајају словима" diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po index 4770db15de..db7bada888 100644 --- a/src/nvim/po/sv.po +++ b/src/nvim/po/sv.po @@ -2622,10 +2622,6 @@ msgstr "E143: Autokommandon tog ovntat bort ny buffert %s" msgid "E144: non-numeric argument to :z" msgstr "E144: ickenumeriskt argument till :z" -#: ../ex_cmds.c:3398 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Skalkommandon inte tilltna i rvim" - #: ../ex_cmds.c:3492 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Reguljra uttryck kan inte vara tskilda av bokstver" diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index f2179f4f1b..604e425bd0 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -1459,10 +1459,6 @@ msgstr "E143: Автокоманди несподівано знищили но msgid "E144: non-numeric argument to :z" msgstr "E144: нечисловий аргумент для :z" -msgid "" -"E145: Shell commands and some functionality not allowed in restricted mode" -msgstr "E145: У обмеженому режимі не дозволені команди оболонки і деяка функіональність" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Регулярні вирази не можна розділяти літерами" diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po index 7dbf19c263..a954ea6e34 100644 --- a/src/nvim/po/vi.po +++ b/src/nvim/po/vi.po @@ -1229,10 +1229,6 @@ msgstr "E143: Các lệnh tự động xóa bộ đệm mới ngoài ý muốn % msgid "E144: non-numeric argument to :z" msgstr "E144: Tham số của lệnh :z phải là số" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Không cho phép sử dụng lệnh shell trong rvim." - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Không thể phân cách biểu thức chính quy bằng chữ cái" diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po index 76204a43a8..542157002a 100644 --- a/src/nvim/po/zh_CN.UTF-8.po +++ b/src/nvim/po/zh_CN.UTF-8.po @@ -1222,10 +1222,6 @@ msgstr "E143: 自动命令意外地删除了新缓冲区 %s" msgid "E144: non-numeric argument to :z" msgstr "E144: :z 不接受非数字的参数" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim 中禁止使用 shell 命令" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: 正则表达式不能用字母作分界" diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po index 3c1dd463b7..6a11b5e669 100644 --- a/src/nvim/po/zh_TW.UTF-8.po +++ b/src/nvim/po/zh_TW.UTF-8.po @@ -32,8 +32,8 @@ # So please change [行"] to [行 "] # # Q. How to use UTF8 mode on Win32? -# A. A simple configuration: -# set encoding=utf-8; let $LANG='zh_TW.UTF-8'; +# A. A simple configuration: +# set encoding=utf-8; let $LANG='zh_TW.UTF-8'; # (set langmenu=none or ..) # set fileencodings=ucs-bom,utf-8,japan,taiwan,prc # set fileencoding=taiwan (or utf-8) @@ -1262,10 +1262,6 @@ msgstr "E143: Autocommands 意外地刪除新緩衝區 %s" msgid "E144: non-numeric argument to :z" msgstr "E144: :z 不接受非數字的參數" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: rvim 中禁止使用 shell 命令" - #: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Regular expression 無法用字母分隔 (?)" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d3ca65c53c..3d7d587ed2 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3608,11 +3608,15 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) } if (qf_buf != NULL) { // Use the existing quickfix buffer - (void)do_ecmd(qf_buf->b_fnum, NULL, NULL, NULL, ECMD_ONE, - ECMD_HIDE + ECMD_OLDBUF, oldwin); + if (do_ecmd(qf_buf->b_fnum, NULL, NULL, NULL, ECMD_ONE, + ECMD_HIDE + ECMD_OLDBUF, oldwin) == FAIL) { + return FAIL; + } } else { // Create a new quickfix buffer - (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); + if (do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin) == FAIL) { + return FAIL; + } } // Set the options for the quickfix buffer/window (if not already done) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 5f09912116..425458f210 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2196,6 +2196,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (wp->w_p_spell + && foldinfo.fi_lines == 0 && *wp->w_s->b_p_spl != NUL && !GA_EMPTY(&wp->w_s->b_langp) && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { @@ -5205,7 +5206,7 @@ win_redr_custom ( fillchar = ' '; attr = HL_ATTR(HLF_TPF); maxwidth = Columns; - use_sandbox = was_set_insecurely((char_u *)"tabline", 0); + use_sandbox = was_set_insecurely(wp, (char_u *)"tabline", 0); } else { row = W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); @@ -5236,14 +5237,14 @@ win_redr_custom ( attr = HL_ATTR(HLF_MSG); } - use_sandbox = was_set_insecurely((char_u *)"rulerformat", 0); + use_sandbox = was_set_insecurely(wp, (char_u *)"rulerformat", 0); } else { if (*wp->w_p_stl != NUL) stl = wp->w_p_stl; else stl = p_stl; - use_sandbox = was_set_insecurely((char_u *)"statusline", - *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); + use_sandbox = was_set_insecurely( + wp, (char_u *)"statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); } col += wp->w_wincol; diff --git a/src/nvim/search.c b/src/nvim/search.c index f979889540..90e1e25de2 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1658,6 +1658,48 @@ static bool find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos) return found; } +/// Check matchpairs option for "*initc". +/// If there is a match set "*initc" to the matching character and "*findc" to +/// the opposite character. Set "*backwards" to the direction. +/// When "switchit" is true swap the direction. +static void find_mps_values(int *initc, int *findc, bool *backwards, + bool switchit) + FUNC_ATTR_NONNULL_ALL +{ + char_u *ptr = curbuf->b_p_mps; + + while (*ptr != NUL) { + if (utf_ptr2char(ptr) == *initc) { + if (switchit) { + *findc = *initc; + *initc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1); + *backwards = true; + } else { + *findc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1); + *backwards = false; + } + return; + } + char_u *prev = ptr; + ptr += utfc_ptr2len(ptr) + 1; + if (utf_ptr2char(ptr) == *initc) { + if (switchit) { + *findc = *initc; + *initc = utf_ptr2char(prev); + *backwards = false; + } else { + *findc = utf_ptr2char(prev); + *backwards = true; + } + return; + } + ptr += utfc_ptr2len(ptr); + if (*ptr == ',') { + ptr++; + } + } +} + /* * findmatchlimit -- find the matching paren or brace, if it exists within * maxtravel lines of the cursor. A maxtravel of 0 means search until falling @@ -1684,7 +1726,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) static pos_T pos; // current search position int findc = 0; // matching brace int count = 0; // cumulative number of braces - int backwards = false; // init for gcc + bool backwards = false; // init for gcc bool raw_string = false; // search for raw string bool inquote = false; // true when inside quotes char_u *ptr; @@ -1729,9 +1771,10 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) raw_string = (initc == 'R'); initc = NUL; } else if (initc != '#' && initc != NUL) { - find_mps_values(&initc, &findc, &backwards, TRUE); - if (findc == NUL) + find_mps_values(&initc, &findc, &backwards, true); + if (findc == NUL) { return NULL; + } } else { /* * Either initc is '#', or no initc was given and we need to look @@ -1759,20 +1802,20 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) else if (linep[pos.col] == '/') { if (linep[pos.col + 1] == '*') { comment_dir = FORWARD; - backwards = FALSE; + backwards = false; pos.col++; } else if (pos.col > 0 && linep[pos.col - 1] == '*') { comment_dir = BACKWARD; - backwards = TRUE; + backwards = true; pos.col--; } } else if (linep[pos.col] == '*') { if (linep[pos.col + 1] == '/') { comment_dir = BACKWARD; - backwards = TRUE; + backwards = true; } else if (pos.col > 0 && linep[pos.col - 1] == '/') { comment_dir = FORWARD; - backwards = FALSE; + backwards = false; } } } @@ -1794,9 +1837,10 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) if (initc == NUL) break; - find_mps_values(&initc, &findc, &backwards, FALSE); - if (findc) + find_mps_values(&initc, &findc, &backwards, false); + if (findc) { break; + } pos.col += utfc_ptr2len(linep + pos.col); } if (!findc) { diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index af8482bdbe..ed75bda7a5 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -86,6 +86,8 @@ let s:filename_checks = { \ 'bzr': ['bzr_log.any'], \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c'], \ 'cabal': ['file.cabal'], + \ 'cabalconfig': ['cabal.config'], + \ 'cabalproject': ['cabal.project', 'cabal.project.local'], \ 'calendar': ['calendar'], \ 'catalog': ['catalog', 'sgml.catalogfile'], \ 'cdl': ['file.cdl'], diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 00e42733a7..a80a73161f 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -516,7 +516,7 @@ func Test_termguicolors() if !exists('+termguicolors') return endif - if has('vtp') && !has('vcon') + if has('vtp') && !has('vcon') && !has('gui_running') " Win32: 'guicolors' doesn't work without virtual console. call assert_fails('set termguicolors', 'E954:') return diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 3e97cd2dd0..9c5f0777c6 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -4423,4 +4423,39 @@ func Test_add_invalid_entry_with_qf_window() cclose endfunc +" Test for very weird problem: autocommand causes a failure, resulting opening +" the quickfix window to fail. This still splits the window, but otherwise +" should not mess up buffers. +func Test_quickfix_window_fails_to_open() + CheckScreendump + + let lines =<< trim END + anything + try + anything + endtry + END + call writefile(lines, 'XquickfixFails') + + let lines =<< trim END + split XquickfixFails + silent vimgrep anything % + normal o + au BufLeave * ++once source XquickfixFails + " This will trigger the autocommand, which causes an error, what follows + " is aborted but the window was already split. + silent! cwindow + END + call writefile(lines, 'XtestWinFails') + let buf = RunVimInTerminal('-S XtestWinFails', #{rows: 13}) + call VerifyScreenDump(buf, 'Test_quickfix_window_fails', {}) + + " clean up + call term_sendkeys(buf, ":bwipe!\<CR>") + call term_wait(buf) + call StopVimInTerminal(buf) + call delete('XtestWinFails') + call delete('XquickfixFails') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 7fe0168356..6214975ef5 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -581,6 +581,27 @@ func Test_read_stdin() call delete('Xtestout') endfunc +func Test_set_shell() + let after =<< trim [CODE] + call writefile([&shell], "Xtestout") + quit! + [CODE] + + if has('win32') + let $SHELL = 'C:\with space\cmd.exe' + let expected = '"C:\with space\cmd.exe"' + else + let $SHELL = '/bin/with space/sh' + let expected = '"/bin/with space/sh"' + endif + + if RunVimPiped([], after, '', '') + let lines = readfile('Xtestout') + call assert_equal(expected, lines[0]) + endif + call delete('Xtestout') +endfunc + func Test_progpath() " Tests normally run with "./vim" or "../vim", these must have been expanded " to a full path. diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index d3c0594c03..424cb4abd0 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -1,5 +1,8 @@ " Tests for system() and systemlist() +source shared.vim +source check.vim + function! Test_System() if !executable('echo') || !executable('cat') || !executable('wc') return @@ -88,3 +91,54 @@ function! Test_system_exmode() let a = system(v:progpath. cmd) call assert_notequal(0, v:shell_error) endfunc + +func Test_system_with_shell_quote() + throw 'skipped: enable after porting method patches' + CheckMSWindows + + call mkdir('Xdir with spaces', 'p') + call system('copy "%COMSPEC%" "Xdir with spaces\cmd.exe"') + + let shell_save = &shell + let shellxquote_save = &shellxquote + try + " Set 'shell' always needs noshellslash. + let shellslash_save = &shellslash + set noshellslash + let shell_tests = [ + \ expand('$COMSPEC'), + \ '"' . fnamemodify('Xdir with spaces\cmd.exe', ':p') . '"', + \] + let &shellslash = shellslash_save + + let sxq_tests = ['', '(', '"'] + + " Matrix tests: 'shell' * 'shellxquote' + for shell in shell_tests + let &shell = shell + for sxq in sxq_tests + let &shellxquote = sxq + + let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote) + + try + let out = 'echo 123'->system() + catch + call assert_report(printf('%s: %s', msg, v:exception)) + continue + endtry + + " On Windows we may get a trailing space and CR. + if out != "123 \n" + call assert_equal("123\n", out, msg) + endif + + endfor + endfor + + finally + let &shell = shell_save + let &shellxquote = shellxquote_save + call delete('Xdir with spaces', 'rf') + endtry +endfunc diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index 4c1083c386..65dc1b3e03 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -50,7 +50,7 @@ describe('treesitter API with C parser', function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse() + tree = parser:parse()[1] root = tree:root() lang = vim.treesitter.inspect_language('c') ]]) @@ -82,7 +82,7 @@ describe('treesitter API with C parser', function() feed("2G7|ay") exec_lua([[ - tree2 = parser:parse() + tree2 = parser:parse()[1] root2 = tree2:root() descendant2 = root2:descendant_for_range(1,2,1,13) ]]) @@ -106,11 +106,8 @@ describe('treesitter API with C parser', function() eq(false, exec_lua("return child:id() == nil")) eq(false, exec_lua("return child:id() == tree")) - -- orginal tree did not change - eq({1,2,1,12}, exec_lua("return {descendant:range()}")) - -- unchanged buffer: return the same tree - eq(true, exec_lua("return parser:parse() == tree2")) + eq(true, exec_lua("return parser:parse()[1] == tree2")) end) local test_text = [[ @@ -142,7 +139,7 @@ void ui_refresh(void) local res = exec_lua([[ parser = vim.treesitter.get_parser(0, "c") - func_node = parser:parse():root():child(0) + func_node = parser:parse()[1]:root():child(0) res = {} for node, field in func_node:iter_children() do @@ -166,7 +163,7 @@ void ui_refresh(void) local res = exec_lua([[ parser = vim.treesitter.get_parser(0, "c") - func_node = parser:parse():root():child(0) + func_node = parser:parse()[1]:root():child(0) local res = {} for _, node in ipairs(func_node:field("type")) do @@ -211,7 +208,7 @@ void ui_refresh(void) local res = exec_lua([[ cquery = vim.treesitter.parse_query("c", ...) parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse() + tree = parser:parse()[1] res = {} for cid, node in cquery:iter_captures(tree:root(), 0, 7, 14) do -- can't transmit node over RPC. just check the name and range @@ -242,7 +239,7 @@ void ui_refresh(void) local res = exec_lua([[ cquery = vim.treesitter.parse_query("c", ...) parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse() + tree = parser:parse()[1] res = {} for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do -- can't transmit node over RPC. just check the name and range @@ -275,7 +272,7 @@ void ui_refresh(void) local res = exec_lua([[ cquery = vim.treesitter.parse_query("c", '((_) @quote (vim-match? @quote "^\\"$")) ((_) @quote (lua-match? @quote "^\\"$"))') parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse() + tree = parser:parse()[1] res = {} for pattern, match in cquery:iter_matches(tree:root(), 0, 0, 1) do -- can't transmit node over RPC. just check the name and range @@ -321,7 +318,7 @@ void ui_refresh(void) local query = query.parse_query("c", ...) local nodes = {} - for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do table.insert(nodes, {node:range()}) end @@ -343,10 +340,7 @@ void ui_refresh(void) eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) end) - it('supports highlighting', function() - if not check_parser() then return end - - local hl_text = [[ + local hl_text = [[ /// Schedule Lua callback on main loop's event queue static int nlua_schedule(lua_State *const lstate) { @@ -363,7 +357,7 @@ static int nlua_schedule(lua_State *const lstate) return 0; }]] - local hl_query = [[ +local hl_query = [[ (ERROR) @ErrorMsg "if" @keyword @@ -398,249 +392,319 @@ static int nlua_schedule(lua_State *const lstate) (comment) @comment ]] - local screen = Screen.new(65, 18) - screen:attach() - screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {foreground = Screen.colors.Blue1}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, - [4] = {bold = true, foreground = Screen.colors.Brown}, - [5] = {foreground = Screen.colors.Magenta}, - [6] = {foreground = Screen.colors.Red}, - [7] = {bold = true, foreground = Screen.colors.SlateBlue}, - [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - [9] = {foreground = Screen.colors.Magenta, background = Screen.colors.Red}, - [10] = {foreground = Screen.colors.Red, background = Screen.colors.Red}, - [11] = {foreground = Screen.colors.Cyan4}, - }) - - insert(hl_text) - screen:expect{grid=[[ - /// Schedule Lua callback on main loop's event queue | - static int nlua_schedule(lua_State *const lstate) | - { | - if (lua_type(lstate, 1) != LUA_TFUNCTION | - || lstate != lstate) { | - lua_pushliteral(lstate, "vim.schedule: expected function"); | - return lua_error(lstate); | + describe('when highlighting', function() + local screen + + before_each(function() + screen = Screen.new(65, 18) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Blue1}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true, foreground = Screen.colors.Brown}, + [5] = {foreground = Screen.colors.Magenta}, + [6] = {foreground = Screen.colors.Red}, + [7] = {bold = true, foreground = Screen.colors.SlateBlue}, + [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [9] = {foreground = Screen.colors.Magenta, background = Screen.colors.Red}, + [10] = {foreground = Screen.colors.Red, background = Screen.colors.Red}, + [11] = {foreground = Screen.colors.Cyan4}, + }) + end) + + it('supports highlighting', function() + if not check_parser() then return end + + insert(hl_text) + screen:expect{grid=[[ + /// Schedule Lua callback on main loop's event queue | + static int nlua_schedule(lua_State *const lstate) | + { | + if (lua_type(lstate, 1) != LUA_TFUNCTION | + || lstate != lstate) { | + lua_pushliteral(lstate, "vim.schedule: expected function"); | + return lua_error(lstate); | + } | + | + LuaRef cb = nlua_ref(lstate, 1); | + | + multiqueue_put(main_loop.events, nlua_schedule_event, | + 1, (void *)(ptrdiff_t)cb); | + return 0; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c") + local highlighter = vim.treesitter.highlighter + local query = ... + test_hl = highlighter.new(parser, {queries = {c = query}}) + ]], hl_query) + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + + feed("5Goc<esc>dd") + + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + } | + {1:~ }| + {1:~ }| + | + ]]} + + feed('7Go*/<esc>') + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + {8:*^/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + } | + {1:~ }| + | + ]]} + + feed('3Go/*<esc>') + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {2:/^*} | + {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | + {2: || lstate != lstate) {} | + {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | + {2: return lua_error(lstate);} | + {2:*/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + {8:}} | + | + ]]} + + feed("gg$") + feed("~") + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queu^E} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {2:/*} | + {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | + {2: || lstate != lstate) {} | + {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | + {2: return lua_error(lstate);} | + {2:*/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + {8:}} | + | + ]]} + + + feed("re") + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queu^e} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {2:/*} | + {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | + {2: || lstate != lstate) {} | + {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | + {2: return lua_error(lstate);} | + {2:*/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + {8:}} | + | + ]]} + end) + + it("supports highlighting with custom parser", function() + if not check_parser() then return end + + screen:set_default_attr_ids({ {bold = true, foreground = Screen.colors.SeaGreen4} }) + + insert(test_text) + + screen:expect{ grid= [[ + int width = INT_MAX, height = INT_MAX; | + bool ext_widgets[kUIExtCount]; | + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | } | | - LuaRef cb = nlua_ref(lstate, 1); | - | - multiqueue_put(main_loop.events, nlua_schedule_event, | - 1, (void *)(ptrdiff_t)cb); | - return 0; | - ^} | - {1:~ }| - {1:~ }| - | - ]]} - - exec_lua([[ - local parser = vim.treesitter.get_parser(0, "c") - local highlighter = vim.treesitter.highlighter - local query = ... - test_hl = highlighter.new(parser, query) - ]], hl_query) - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | - || {6:lstate} != {6:lstate}) { | - {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | - {4:return} {11:lua_error}(lstate); | + bool inclusive = ui_override(); | + for (size_t i = 0; i < ui_count; i++) { | + UI *ui = uis[i]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | ^} | - {1:~ }| - {1:~ }| - | - ]]} - - feed("5Goc<esc>dd") - - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | - || {6:lstate} != {6:lstate}) { | - {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | - {4:return} {11:lua_error}(lstate); | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - } | - {1:~ }| - {1:~ }| - | - ]]} - - feed('7Go*/<esc>') - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | - || {6:lstate} != {6:lstate}) { | - {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | - {4:return} {11:lua_error}(lstate); | - {8:*^/} | - } | | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - } | - {1:~ }| - | - ]]} - - feed('3Go/*<esc>') - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {2:/^*} | - {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | - {2: || lstate != lstate) {} | - {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | - {2: return lua_error(lstate);} | - {2:*/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - {8:}} | - | - ]]} - - feed("gg$") - feed("~") - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queu^E} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {2:/*} | - {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | - {2: || lstate != lstate) {} | - {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | - {2: return lua_error(lstate);} | - {2:*/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - {8:}} | - | - ]]} - - - feed("re") - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queu^e} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {2:/*} | - {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | - {2: || lstate != lstate) {} | - {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | - {2: return lua_error(lstate);} | - {2:*/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - {8:}} | - | - ]]} - end) + ]] } - it("supports highlighting with custom parser", function() - if not check_parser() then return end - - local screen = Screen.new(65, 18) - screen:attach() - screen:set_default_attr_ids({ {bold = true, foreground = Screen.colors.SeaGreen4} }) - - insert(test_text) - - screen:expect{ grid= [[ - int width = INT_MAX, height = INT_MAX; | - bool ext_widgets[kUIExtCount]; | - for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | - ext_widgets[i] = true; | - } | - | - bool inclusive = ui_override(); | - for (size_t i = 0; i < ui_count; i++) { | - UI *ui = uis[i]; | - width = MIN(ui->width, width); | - height = MIN(ui->height, height); | - foo = BAR(ui->bazaar, bazaar); | - for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | - ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | - } | - } | - ^} | - | - ]] } - - exec_lua([[ - parser = vim.treesitter.get_parser(0, "c") - query = vim.treesitter.parse_query("c", "(declaration) @decl") + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + query = vim.treesitter.parse_query("c", "(declaration) @decl") - local nodes = {} - for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do - table.insert(nodes, node) - end + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do + table.insert(nodes, node) + end - parser:set_included_ranges(nodes) + parser:set_included_regions({nodes}) - local hl = vim.treesitter.highlighter.new(parser, "(identifier) @type") - ]]) + local hl = vim.treesitter.highlighter.new(parser, {queries = {c = "(identifier) @type"}}) + ]]) - screen:expect{ grid = [[ - int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | - bool {1:ext_widgets}[{1:kUIExtCount}]; | - for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | - ext_widgets[i] = true; | - } | - | - bool {1:inclusive} = {1:ui_override}(); | - for (size_t {1:i} = 0; i < ui_count; i++) { | - UI *{1:ui} = {1:uis}[{1:i}]; | - width = MIN(ui->width, width); | - height = MIN(ui->height, height); | - foo = BAR(ui->bazaar, bazaar); | - for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | - ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | - } | - } | - ^} | - | - ]] } + screen:expect{ grid = [[ + int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | + bool {1:ext_widgets}[{1:kUIExtCount}]; | + for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool {1:inclusive} = {1:ui_override}(); | + for (size_t {1:i} = 0; i < ui_count; i++) { | + UI *{1:ui} = {1:uis}[{1:i}]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + end) + + it("supports highlighting injected languages", function() + if not check_parser() then return end + + insert([[ + int x = INT_MAX; + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + #define foo void main() { \ + return 42; \ + } + ]]) + + screen:expect{grid=[[ + int x = INT_MAX; | + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))| + #define foo void main() { \ | + return 42; \ | + } | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c", { + queries = {c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"} + }) + local highlighter = vim.treesitter.highlighter + local query = ... + test_hl = highlighter.new(parser, {queries = {c = query}}) + ]], hl_query) + + screen:expect{grid=[[ + {3:int} x = {5:INT_MAX}; | + #define {5:READ_STRING}(x, y) ({3:char_u} *)read_string((x), ({3:size_t})(y))| + #define foo {3:void} main() { \ | + {4:return} {5:42}; \ | + } | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) it('inspects language', function() @@ -690,7 +754,7 @@ static int nlua_schedule(lua_State *const lstate) local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") - return { parser:parse():root():range() } + return { parser:parse()[1]:root():range() } ]] eq({0, 0, 19, 0}, res) @@ -698,38 +762,51 @@ static int nlua_schedule(lua_State *const lstate) -- The following sets the included ranges for the current parser -- As stated here, this only includes the function (thus the whole buffer, without the last line) local res2 = exec_lua [[ - local root = parser:parse():root() - parser:set_included_ranges({root:child(0)}) - parser.valid = false - return { parser:parse():root():range() } + local root = parser:parse()[1]:root() + parser:set_included_regions({{root:child(0)}}) + parser:invalidate() + return { parser:parse()[1]:root():range() } ]] eq({0, 0, 18, 1}, res2) local range = exec_lua [[ - return parser:included_ranges() + local res = {} + for _, region in ipairs(parser:included_regions()) do + for _, node in ipairs(region) do + table.insert(res, {node:range()}) + end + end + return res ]] eq(range, { { 0, 0, 18, 1 } }) + + local range_tbl = exec_lua [[ + parser:set_included_regions { { { 0, 0, 17, 1 } } } + parser:parse() + return parser:included_regions() + ]] + + eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) end) it("allows to set complex ranges", function() if not check_parser() then return end insert(test_text) - local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") query = vim.treesitter.parse_query("c", "(declaration) @decl") local nodes = {} - for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do table.insert(nodes, node) end - parser:set_included_ranges(nodes) + parser:set_included_regions({nodes}) - local root = parser:parse():root() + local root = parser:parse()[1]:root() local res = {} for i=0,(root:named_child_count() - 1) do @@ -740,21 +817,18 @@ static int nlua_schedule(lua_State *const lstate) eq({ { 2, 2, 2, 40 }, - { 3, 3, 3, 32 }, - { 4, 7, 4, 8 }, - { 4, 8, 4, 25 }, - { 8, 2, 8, 6 }, - { 8, 7, 8, 33 }, - { 9, 8, 9, 20 }, - { 10, 4, 10, 5 }, - { 10, 5, 10, 20 }, + { 3, 2, 3, 32 }, + { 4, 7, 4, 25 }, + { 8, 2, 8, 33 }, + { 9, 7, 9, 20 }, + { 10, 4, 10, 20 }, { 14, 9, 14, 27 } }, res) end) it("allows to create string parsers", function() local ret = exec_lua [[ local parser = vim.treesitter.get_string_parser("int foo = 42;", "c") - return { parser:parse():root():range() } + return { parser:parse()[1]:root():range() } ]] eq({ 0, 0, 0, 13 }, ret) @@ -773,7 +847,7 @@ static int nlua_schedule(lua_State *const lstate) local nodes = {} local query = vim.treesitter.parse_query("c", '((identifier) @id (eq? @id "foo"))') - for _, node in query:iter_captures(parser:parse():root(), str, 0, 2) do + for _, node in query:iter_captures(parser:parse()[1]:root(), str, 0, 2) do table.insert(nodes, { node:range() }) end @@ -781,4 +855,68 @@ static int nlua_schedule(lua_State *const lstate) eq({ {0, 10, 0, 13} }, ret) end) + + describe("when creating a language tree", function() + local function get_ranges() + return exec_lua([[ + local result = {} + parser:for_each_tree(function(tree) table.insert(result, {tree:root():range()}) end) + return result + ]]) + end + + before_each(function() + insert([[ + int x = INT_MAX; + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + #define READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + #define VALUE 0 + #define VALUE1 1 + #define VALUE2 2 + ]]) + end) + + describe("when parsing regions independently", function() + it("should inject a language", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(5, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 2, 7, 0}, -- root tree + {3, 16, 3, 17}, -- VALUE 0 + {4, 17, 4, 18}, -- VALUE1 1 + {5, 17, 5, 18}, -- VALUE2 2 + {1, 28, 1, 67}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + {2, 31, 2, 70} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + + describe("when parsing regions combined", function() + it("should inject a language", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(2, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 2, 7, 0}, -- root tree + {3, 16, 5, 18}, -- VALUE 0 + -- VALUE1 1 + -- VALUE2 2 + {1, 28, 2, 70} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + end) + end) diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 669ec5d3ed..018049d2f4 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -117,6 +117,33 @@ describe("folded lines", function() end end) + it("work with spell", function() + command("set spell") + insert([[ + This is a + valid English + sentence composed by + an exhausted developer + in his cave. + ]]) + + feed("gg") + feed("zf3j") + if not multigrid then + -- screen:snapshot_util() + screen:expect{grid=[[ + {5:^+-- 4 lines: This is a······················}| + in his cave. | + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end + end) + it("works with multibyte fillchars", function() insert([[ aa diff --git a/test/helpers.lua b/test/helpers.lua index 84148dc1a8..8dbd82cb8c 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -777,11 +777,12 @@ end function module.isCI(name) local any = (name == nil) - assert(any or name == 'appveyor' or name == 'travis' or name == 'sourcehut') + assert(any or name == 'appveyor' or name == 'travis' or name == 'sourcehut' or name == 'github') local av = ((any or name == 'appveyor') and nil ~= os.getenv('APPVEYOR')) local tr = ((any or name == 'travis') and nil ~= os.getenv('TRAVIS')) local sh = ((any or name == 'sourcehut') and nil ~= os.getenv('SOURCEHUT')) - return tr or av or sh + local gh = ((any or name == 'github') and nil ~= os.getenv('GITHUB_ACTIONS')) + return tr or av or sh or gh end |