diff options
Diffstat (limited to 'runtime')
| -rw-r--r-- | runtime/doc/filetype.txt | 5 | ||||
| -rw-r--r-- | runtime/doc/lua.txt | 61 | ||||
| -rw-r--r-- | runtime/filetype.lua | 12 | ||||
| -rw-r--r-- | runtime/filetype.vim | 14 | ||||
| -rw-r--r-- | runtime/lua/vim/filetype.lua | 9 | ||||
| -rw-r--r-- | runtime/lua/vim/inspect.lua | 383 | ||||
| -rw-r--r-- | runtime/lua/vim/keymap.lua | 3 | ||||
| -rw-r--r-- | runtime/lua/vim/treesitter.lua | 2 |
8 files changed, 271 insertions, 218 deletions
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index e1b0e88c95..c50ccc7a91 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -257,7 +257,10 @@ C. If your file type can be detected by the file name or extension. disabled by setting the did_load_filetypes global variable. If this variable exists, $VIMRUNTIME/filetype.vim will not run. Example: > - " Disable filetype.vim + " Disable filetype.vim (but still load filetype.lua if enabled) + let g:did_load_filetypes = 0 + + " Disable filetype.vim and filetype.lua let g:did_load_filetypes = 1 < 3. To use the new filetype detection you must restart Vim. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 16f27486c8..ed4cc77369 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -138,16 +138,16 @@ Lua functions can be called in multiple ways. Consider the function: > end -The first way to call a function is: > - +The first way to call this function is: > + example_func(1, 2) -- ==== Result ==== -- A is: 1 -- B is: 2 < - This way of calling a function is familiar to most scripting languages. - In Lua, it's important to understand that any function arguments that are - not supplied are automatically set to `nil`. For example: > +This way of calling a function is familiar from most scripting languages. +In Lua, it's important to understand that any function arguments that are +not supplied are automatically set to `nil`. For example: > example_func(1) -- ==== Result ==== @@ -155,12 +155,13 @@ The first way to call a function is: > -- B is: nil < - Additionally, if any extra parameters are passed, they are discarded - completely. +Additionally, if any extra parameters are passed, they are discarded +completely. -In Lua, it is also possible (when only one argument is passed) to call the -function without any parentheses. This is most often used to approximate -"keyword"-style arguments with a single dictionary. For example: > +In Lua, it is also possible to omit the parentheses (only) if the function +takes a single string or table literal (`"foo"` or "`{1,2,3}`", respectively). +The latter is most often used to approximate "keyword-style" arguments with a +single dictionary, for example: > local func_with_opts = function(opts) local will_do_foo = opts.foo @@ -172,15 +173,38 @@ function without any parentheses. This is most often used to approximate func_with_opts { foo = true, filename = "hello.world" } < - In this style, each "parameter" is passed via keyword. It is still valid - to call the function in this style: > +In this style, each "parameter" is passed via keyword. It is still valid +to call the function in the standard style: > func_with_opts({ foo = true, filename = "hello.world" }) < - But often in the documentation, you will see the former rather than the - latter style, due to its brevity (this is vim after all!). +But often in the documentation, you will see the former rather than the +latter style due to its brevity. + +============================================================================== +Lua Patterns *lua-patterns* + +For performance reasons, Lua does not support regular expressions natively. +Instead, the Lua `string` standard library allows manipulations using a +restricted set of "patterns", see https://www.lua.org/manual/5.1/manual.html#5.4.1 + +Examples (`string.match` extracts the first match): > + + print(string.match("foo123bar123", "%d+")) + -- -> 123 + + print(string.match("foo123bar123", "[^%d]+")) + -- -> foo + + print(string.match("foo123bar123", "[abc]+")) + -- -> ba + + print(string.match("foo.bar", "%.bar")) + -- -> .bar +For more complex matching, Vim regular expressions can be used from Lua +through |vim.regex()|. ------------------------------------------------------------------------------ LUA PLUGIN EXAMPLE *lua-require-example* @@ -1924,9 +1948,9 @@ add({filetypes}) *vim.filetype.add()* filename (either the "tail" or the full file path). The full file path is checked first, followed by the file name. If a match is not found using the filename, then the filename is - matched against the list of patterns (sorted by priority) - until a match is found. Lastly, if pattern matching does not - find a filetype, then the file extension is used. + matched against the list of |lua-patterns| (sorted by + priority) until a match is found. Lastly, if pattern matching + does not find a filetype, then the file extension is used. The filetype can be either a string (in which case it is used as the filetype directly) or a function. If a function, it @@ -2055,6 +2079,9 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* "silent". In addition to the options listed in |nvim_set_keymap()|, this table also accepts the following keys: + • buffer: (number or boolean) Add a mapping to the + given buffer. When "true" or 0, use the current + buffer. • replace_keycodes: (boolean, default true) When both this and expr is "true", |nvim_replace_termcodes()| is applied to the diff --git a/runtime/filetype.lua b/runtime/filetype.lua index 8224b79534..47d55d8465 100644 --- a/runtime/filetype.lua +++ b/runtime/filetype.lua @@ -17,10 +17,14 @@ vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, { }) -- These *must* be sourced after the autocommand above is created -vim.cmd [[ -runtime! ftdetect/*.vim -runtime! ftdetect/*.lua -]] +if not vim.g.did_load_ftdetect then + vim.cmd [[ + augroup filetypedetect + runtime! ftdetect/*.vim + runtime! ftdetect/*.lua + augroup END + ]] +end -- Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim vim.g.did_load_ftdetect = 1 diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 08dfe6223f..e72d6fc0c0 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -151,14 +151,21 @@ au BufNewFile,BufRead *.asp \ setf aspvbs | \ endif -" Grub (must be before catch *.lst) +" Grub (must be before pattern *.lst) au BufNewFile,BufRead */boot/grub/menu.lst,*/boot/grub/grub.conf,*/etc/grub.conf setf grub +" Maxima, see: +" https://maxima.sourceforge.io/docs/manual/maxima_71.html#file_005ftype_005fmaxima +" Must be before the pattern *.mac. +" *.dem omitted - also used by gnuplot demos +" *.mc omitted - used by dist#ft#McSetf() +au BufNewFile,BufRead *.demo,*.dm{1,2,3,t},*.wxm,maxima-init.mac setf maxima + " Assembly (all kinds) " *.lst is not pure assembly, it has two extra columns (address, byte codes) au BufNewFile,BufRead *.asm,*.[sS],*.[aA],*.mac,*.lst call dist#ft#FTasm() -" Macro (VAX) +" Assembly - Macro (VAX) au BufNewFile,BufRead *.mar setf vmasm " Atlas @@ -1674,7 +1681,8 @@ au BufNewFile,BufRead *.siv,*.sieve setf sieve " Sendmail au BufNewFile,BufRead sendmail.cf setf sm -" Sendmail .mc files are actually m4. Could also be MS Message text file. +" Sendmail .mc files are actually m4. Could also be MS Message text file or +" Maxima. au BufNewFile,BufRead *.mc call dist#ft#McSetf() " Services diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index ac36210b23..420d343a8a 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -389,6 +389,12 @@ local extension = { mason = "mason", master = "master", mas = "master", + demo = "maxima", + dm1 = "maxima", + dm2 = "maxima", + dm3 = "maxima", + dmt = "maxima", + wxm = "maxima", mel = "mel", mf = "mf", mgl = "mgl", @@ -1013,6 +1019,7 @@ local filename = { [".mailcap"] = "mailcap", ["/etc/man.conf"] = "manconf", ["man.config"] = "manconf", + ["maxima-init.mac"] = "maxima", ["meson.build"] = "meson", ["meson_options.txt"] = "meson", ["/etc/conf.modules"] = "modconf", @@ -1480,7 +1487,7 @@ end --- Filetype mappings can be added either by extension or by filename (either --- the "tail" or the full file path). The full file path is checked first, --- followed by the file name. If a match is not found using the filename, then ---- the filename is matched against the list of patterns (sorted by priority) +--- the filename is matched against the list of |lua-patterns| (sorted by priority) --- until a match is found. Lastly, if pattern matching does not find a --- filetype, then the file extension is used. --- diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index 0448ea487f..b19c215dbb 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -1,7 +1,7 @@ -local inspect ={ - _VERSION = 'inspect.lua 3.1.0', - _URL = 'http://github.com/kikito/inspect.lua', - _DESCRIPTION = 'human-readable representations of tables', +local inspect = { + _VERSION = "inspect.lua 3.1.0", + _URL = "http://github.com/kikito/inspect.lua", + _DESCRIPTION = "human-readable representations of tables", _LICENSE = [[ MIT LICENSE @@ -25,13 +25,26 @@ local inspect ={ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ]] + ]], } -local tostring = tostring +inspect.KEY = setmetatable({}, { + __tostring = function() + return "inspect.KEY" + end, +}) +inspect.METATABLE = setmetatable({}, { + __tostring = function() + return "inspect.METATABLE" + end, +}) -inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) -inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) +local tostring = tostring +local rep = string.rep +local match = string.match +local char = string.char +local gsub = string.gsub +local fmt = string.format local function rawpairs(t) return next, t, nil @@ -40,299 +53,289 @@ end -- Apostrophizes the string if it has quotes, but not aphostrophes -- Otherwise, it returns a regular quoted string local function smartQuote(str) - if str:match('"') and not str:match("'") then + if match(str, '"') and not match(str, "'") then return "'" .. str .. "'" end - return '"' .. str:gsub('"', '\\"') .. '"' + return '"' .. gsub(str, '"', '\\"') .. '"' end -- \a => '\\a', \0 => '\\0', 31 => '\31' local shortControlCharEscapes = { - ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", - ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" + ["\a"] = "\\a", + ["\b"] = "\\b", + ["\f"] = "\\f", + ["\n"] = "\\n", + ["\r"] = "\\r", + ["\t"] = "\\t", + ["\v"] = "\\v", + ["\127"] = "\\127", } -local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 -for i=0, 31 do - local ch = string.char(i) +local longControlCharEscapes = { ["\127"] = "\127" } +for i = 0, 31 do + local ch = char(i) if not shortControlCharEscapes[ch] then - shortControlCharEscapes[ch] = "\\"..i - longControlCharEscapes[ch] = string.format("\\%03d", i) + shortControlCharEscapes[ch] = "\\" .. i + longControlCharEscapes[ch] = fmt("\\%03d", i) end end local function escape(str) - return (str:gsub("\\", "\\\\") - :gsub("(%c)%f[0-9]", longControlCharEscapes) - :gsub("%c", shortControlCharEscapes)) + return (gsub(gsub(gsub(str, "\\", "\\\\"), "(%c)%f[0-9]", longControlCharEscapes), "%c", shortControlCharEscapes)) end local function isIdentifier(str) - return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) + return type(str) == "string" and not not str:match("^[_%a][_%a%d]*$") end +local flr = math.floor local function isSequenceKey(k, sequenceLength) - return type(k) == 'number' - and 1 <= k - and k <= sequenceLength - and math.floor(k) == k + return type(k) == "number" and flr(k) == k and 1 <= k and k <= sequenceLength end local defaultTypeOrders = { - ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, - ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 + ["number"] = 1, + ["boolean"] = 2, + ["string"] = 3, + ["table"] = 4, + ["function"] = 5, + ["userdata"] = 6, + ["thread"] = 7, } local function sortKeys(a, b) local ta, tb = type(a), type(b) -- strings and numbers are sorted numerically/alphabetically - if ta == tb and (ta == 'string' or ta == 'number') then return a < b end + if ta == tb and (ta == "string" or ta == "number") then + return a < b + end - local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] + local dta = defaultTypeOrders[ta] or 100 + local dtb = defaultTypeOrders[tb] or 100 -- Two default types are compared according to the defaultTypeOrders table - if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] - elseif dta then return true -- default types before custom ones - elseif dtb then return false -- custom types after default ones - end -- custom types are sorted out alphabetically - return ta < tb + return dta == dtb and ta < tb or dta < dtb end --- For implementation reasons, the behavior of rawlen & # is "undefined" when --- tables aren't pure sequences. So we implement our own # operator. -local function getSequenceLength(t) - local len = 1 - local v = rawget(t,len) - while v ~= nil do - len = len + 1 - v = rawget(t,len) +local function getKeys(t) + local seqLen = 1 + while rawget(t, seqLen) ~= nil do + seqLen = seqLen + 1 end - return len - 1 -end + seqLen = seqLen - 1 -local function getNonSequentialKeys(t) - local keys, keysLength = {}, 0 - local sequenceLength = getSequenceLength(t) - for k,_ in rawpairs(t) do - if not isSequenceKey(k, sequenceLength) then - keysLength = keysLength + 1 - keys[keysLength] = k + local keys, keysLen = {}, 0 + for k in rawpairs(t) do + if not isSequenceKey(k, seqLen) then + keysLen = keysLen + 1 + keys[keysLen] = k end end table.sort(keys, sortKeys) - return keys, keysLength, sequenceLength + return keys, keysLen, seqLen end -local function countTableAppearances(t, tableAppearances) - tableAppearances = tableAppearances or {} - - if type(t) == 'table' then - if not tableAppearances[t] then - tableAppearances[t] = 1 - for k,v in rawpairs(t) do - countTableAppearances(k, tableAppearances) - countTableAppearances(v, tableAppearances) - end - countTableAppearances(getmetatable(t), tableAppearances) +local function countCycles(x, cycles) + if type(x) == "table" then + if cycles[x] then + cycles[x] = cycles[x] + 1 else - tableAppearances[t] = tableAppearances[t] + 1 + cycles[x] = 1 + for k, v in rawpairs(x) do + countCycles(k, cycles) + countCycles(v, cycles) + end + countCycles(getmetatable(x), cycles) end end - - return tableAppearances -end - -local copySequence = function(s) - local copy, len = {}, #s - for i=1, len do copy[i] = s[i] end - return copy, len end -local function makePath(path, ...) - local keys = {...} - local newPath, len = copySequence(path) - for i=1, #keys do - newPath[len + i] = keys[i] +local function makePath(path, a, b) + local newPath = {} + local len = #path + for i = 1, len do + newPath[i] = path[i] end + + newPath[len + 1] = a + newPath[len + 2] = b + return newPath end local function processRecursive(process, item, path, visited) - if item == nil then return nil end - if visited[item] then return visited[item] end + if item == nil then + return nil + end + if visited[item] then + return visited[item] + end local processed = process(item, path) - if type(processed) == 'table' then + if type(processed) == "table" then local processedCopy = {} visited[item] = processedCopy local processedKey - for k,v in rawpairs(processed) do + for k, v in rawpairs(processed) do processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) if processedKey ~= nil then processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) end end - local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) - if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + if type(mt) ~= "table" then + mt = nil + end setmetatable(processedCopy, mt) processed = processedCopy end return processed end - - -------------------------------------------------------------------- - -local Inspector = {} -local Inspector_mt = {__index = Inspector} - -function Inspector:puts(...) - local args = {...} - local buffer = self.buffer - local len = #buffer - for i=1, #args do - len = len + 1 - buffer[len] = args[i] - end +local function puts(buf, str) + buf.n = buf.n + 1 + buf[buf.n] = str end -function Inspector:down(f) - self.level = self.level + 1 - f() - self.level = self.level - 1 -end +local Inspector = {} -function Inspector:tabify() - self:puts(self.newline, string.rep(self.indent, self.level)) -end +local Inspector_mt = { __index = Inspector } -function Inspector:alreadyVisited(v) - return self.ids[v] ~= nil +local function tabify(inspector) + puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level)) end function Inspector:getId(v) local id = self.ids[v] + local ids = self.ids if not id then local tv = type(v) - id = (self.maxIds[tv] or 0) + 1 - self.maxIds[tv] = id - self.ids[v] = id + id = (ids[tv] or 0) + 1 + ids[v], ids[tv] = id, id end return tostring(id) end -function Inspector:putKey(k) - if isIdentifier(k) then return self:puts(k) end - self:puts("[") - self:putValue(k) - self:puts("]") -end +function Inspector:putValue(v) + local buf = self.buf + local tv = type(v) + if tv == "string" then + puts(buf, smartQuote(escape(v))) + elseif + tv == "number" + or tv == "boolean" + or tv == "nil" + or tv == "cdata" + or tv == "ctype" + or (vim and v == vim.NIL) + then + puts(buf, tostring(v)) + elseif tv == "table" and not self.ids[v] then + local t = v + + if t == inspect.KEY or t == inspect.METATABLE then + puts(buf, tostring(t)) + elseif self.level >= self.depth then + puts(buf, "{...}") + else + if self.cycles[t] > 1 then + puts(buf, fmt("<%d>", self:getId(t))) + end -function Inspector:putTable(t) - if t == inspect.KEY or t == inspect.METATABLE then - self:puts(tostring(t)) - elseif self:alreadyVisited(t) then - self:puts('<table ', self:getId(t), '>') - elseif self.level >= self.depth then - self:puts('{...}') - else - if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end - - local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) - local mt = getmetatable(t) - if (vim and sequenceLength == 0 and nonSequentialKeysLength == 0 - and mt == vim._empty_dict_mt) then - self:puts(tostring(t)) - return - end + local keys, keysLen, seqLen = getKeys(t) + local mt = getmetatable(t) - self:puts('{') - self:down(function() - local count = 0 - for i=1, sequenceLength do - if count > 0 then self:puts(',') end - self:puts(' ') - self:putValue(t[i]) - count = count + 1 + if vim and seqLen == 0 and keysLen == 0 and mt == vim._empty_dict_mt then + puts(buf, tostring(t)) + return end - for i=1, nonSequentialKeysLength do - local k = nonSequentialKeys[i] - if count > 0 then self:puts(',') end - self:tabify() - self:putKey(k) - self:puts(' = ') - self:putValue(t[k]) - count = count + 1 + puts(buf, "{") + self.level = self.level + 1 + + for i = 1, seqLen + keysLen do + if i > 1 then + puts(buf, ",") + end + if i <= seqLen then + puts(buf, " ") + self:putValue(t[i]) + else + local k = keys[i - seqLen] + tabify(self) + if isIdentifier(k) then + puts(buf, k) + else + puts(buf, "[") + self:putValue(k) + puts(buf, "]") + end + puts(buf, " = ") + self:putValue(t[k]) + end end - if type(mt) == 'table' then - if count > 0 then self:puts(',') end - self:tabify() - self:puts('<metatable> = ') + if type(mt) == "table" then + if seqLen + keysLen > 0 then + puts(buf, ",") + end + tabify(self) + puts(buf, "<metatable> = ") self:putValue(mt) end - end) - if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing } - self:tabify() - elseif sequenceLength > 0 then -- array tables have one extra space before closing } - self:puts(' ') - end + self.level = self.level - 1 - self:puts('}') - end -end - -function Inspector:putValue(v) - local tv = type(v) + if keysLen > 0 or type(mt) == "table" then + tabify(self) + elseif seqLen > 0 then + puts(buf, " ") + end - if tv == 'string' then - self:puts(smartQuote(escape(v))) - elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or - tv == 'cdata' or tv == 'ctype' or (vim and v == vim.NIL) then - self:puts(tostring(v)) - elseif tv == 'table' then - self:putTable(v) + puts(buf, "}") + end else - self:puts('<', tv, ' ', self:getId(v), '>') + puts(buf, fmt("<%s %d>", tv, self:getId(v))) end end -------------------------------------------------------------------- - function inspect.inspect(root, options) - options = options or {} + options = options or {} - local depth = options.depth or math.huge - local newline = options.newline or '\n' - local indent = options.indent or ' ' + local depth = options.depth or math.huge + local newline = options.newline or "\n" + local indent = options.indent or " " local process = options.process if process then root = processRecursive(process, root, {}, {}) end + local cycles = {} + countCycles(root, cycles) + local inspector = setmetatable({ - depth = depth, - level = 0, - buffer = {}, - ids = {}, - maxIds = {}, - newline = newline, - indent = indent, - tableAppearances = countTableAppearances(root) + buf = { n = 0 }, + ids = {}, + cycles = cycles, + depth = depth, + level = 0, + newline = newline, + indent = indent, }, Inspector_mt) inspector:putValue(root) - return table.concat(inspector.buffer) + return table.concat(inspector.buf) end -setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) +setmetatable(inspect, { + __call = function(_, root, options) + return inspect.inspect(root, options) + end, +}) return inspect diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 0986d21196..d07232f52f 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -40,6 +40,8 @@ local keymap = {} -- ---@param opts table A table of |:map-arguments| such as "silent". In addition to the options --- listed in |nvim_set_keymap()|, this table also accepts the following keys: +--- - buffer: (number or boolean) Add a mapping to the given buffer. When "true" +--- or 0, use the current buffer. --- - replace_keycodes: (boolean, default true) When both this and expr is "true", --- |nvim_replace_termcodes()| is applied to the result of Lua expr maps. --- - remap: (boolean) Make the mapping recursive. This is the @@ -128,7 +130,6 @@ function keymap.del(modes, lhs, opts) local buffer = false if opts.buffer ~= nil then buffer = opts.buffer == true and 0 or opts.buffer - opts.buffer = nil end if buffer == false then diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index f9d539f028..55398d8180 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -90,7 +90,7 @@ function M.get_parser(bufnr, lang, opts) lang = a.nvim_buf_get_option(bufnr, "filetype") end - if parsers[bufnr] == nil then + if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then parsers[bufnr] = M._create_parser(bufnr, lang, opts) end |