mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			664 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			664 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
-- Test suite for testing interactions with API bindings
 | 
						|
local helpers = require('test.functional.helpers')(after_each)
 | 
						|
local Screen = require('test.functional.ui.screen')
 | 
						|
 | 
						|
local clear = helpers.clear
 | 
						|
local eq = helpers.eq
 | 
						|
local insert = helpers.insert
 | 
						|
local exec_lua = helpers.exec_lua
 | 
						|
local feed = helpers.feed
 | 
						|
local pcall_err = helpers.pcall_err
 | 
						|
local matches = helpers.matches
 | 
						|
 | 
						|
before_each(clear)
 | 
						|
 | 
						|
describe('treesitter API', function()
 | 
						|
  -- error tests not requiring a parser library
 | 
						|
  it('handles missing language', function()
 | 
						|
    eq("Error executing lua: .../language.lua: no parser for 'borklang' language, see :help treesitter-parsers",
 | 
						|
       pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')"))
 | 
						|
 | 
						|
    -- actual message depends on platform
 | 
						|
    matches("Error executing lua: Failed to load parser: uv_dlopen: .+",
 | 
						|
       pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')"))
 | 
						|
 | 
						|
    eq("Error executing lua: .../language.lua: no parser for 'borklang' language, see :help treesitter-parsers",
 | 
						|
       pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')"))
 | 
						|
  end)
 | 
						|
 | 
						|
end)
 | 
						|
 | 
						|
describe('treesitter API with C parser', function()
 | 
						|
  local function check_parser()
 | 
						|
    local status, msg = unpack(exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]]))
 | 
						|
    if not status then
 | 
						|
      if helpers.isCI() then
 | 
						|
        error("treesitter C parser not found, required on CI: " .. msg)
 | 
						|
      else
 | 
						|
        pending('no C parser, skipping')
 | 
						|
      end
 | 
						|
    end
 | 
						|
    return status
 | 
						|
  end
 | 
						|
 | 
						|
  it('parses buffer', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert([[
 | 
						|
      int main() {
 | 
						|
        int x = 3;
 | 
						|
      }]])
 | 
						|
 | 
						|
    exec_lua([[
 | 
						|
      parser = vim.treesitter.get_parser(0, "c")
 | 
						|
      tree = parser:parse()
 | 
						|
      root = tree:root()
 | 
						|
      lang = vim.treesitter.inspect_language('c')
 | 
						|
    ]])
 | 
						|
 | 
						|
    eq("<tree>", exec_lua("return tostring(tree)"))
 | 
						|
    eq("<node translation_unit>", exec_lua("return tostring(root)"))
 | 
						|
    eq({0,0,3,0}, exec_lua("return {root:range()}"))
 | 
						|
 | 
						|
    eq(1, exec_lua("return root:child_count()"))
 | 
						|
    exec_lua("child = root:child(0)")
 | 
						|
    eq("<node function_definition>", exec_lua("return tostring(child)"))
 | 
						|
    eq({0,0,2,1}, exec_lua("return {child:range()}"))
 | 
						|
 | 
						|
    eq("function_definition", exec_lua("return child:type()"))
 | 
						|
    eq(true, exec_lua("return child:named()"))
 | 
						|
    eq("number", type(exec_lua("return child:symbol()")))
 | 
						|
    eq({'function_definition', true}, exec_lua("return lang.symbols[child:symbol()]"))
 | 
						|
 | 
						|
    exec_lua("anon = root:descendant_for_range(0,8,0,9)")
 | 
						|
    eq("(", exec_lua("return anon:type()"))
 | 
						|
    eq(false, exec_lua("return anon:named()"))
 | 
						|
    eq("number", type(exec_lua("return anon:symbol()")))
 | 
						|
    eq({'(', false}, exec_lua("return lang.symbols[anon:symbol()]"))
 | 
						|
 | 
						|
    exec_lua("descendant = root:descendant_for_range(1,2,1,12)")
 | 
						|
    eq("<node declaration>", exec_lua("return tostring(descendant)"))
 | 
						|
    eq({1,2,1,12}, exec_lua("return {descendant:range()}"))
 | 
						|
    eq("(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))", exec_lua("return descendant:sexpr()"))
 | 
						|
 | 
						|
    eq(true, exec_lua("return child == child"))
 | 
						|
    -- separate lua object, but represents same node
 | 
						|
    eq(true, exec_lua("return child == root:child(0)"))
 | 
						|
    eq(false, exec_lua("return child == descendant2"))
 | 
						|
    eq(false, exec_lua("return child == nil"))
 | 
						|
    eq(false, exec_lua("return child == tree"))
 | 
						|
 | 
						|
    feed("2G7|ay")
 | 
						|
    exec_lua([[
 | 
						|
      tree2 = parser:parse()
 | 
						|
      root2 = tree2:root()
 | 
						|
      descendant2 = root2:descendant_for_range(1,2,1,13)
 | 
						|
    ]])
 | 
						|
    eq(false, exec_lua("return tree2 == tree1"))
 | 
						|
    eq(false, exec_lua("return root2 == root"))
 | 
						|
    eq("<node declaration>", exec_lua("return tostring(descendant2)"))
 | 
						|
    eq({1,2,1,13}, exec_lua("return {descendant2:range()}"))
 | 
						|
 | 
						|
    -- 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"))
 | 
						|
  end)
 | 
						|
 | 
						|
    local test_text = [[
 | 
						|
void ui_refresh(void)
 | 
						|
{
 | 
						|
  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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}]]
 | 
						|
 | 
						|
  it('allows to iterate over nodes children', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert(test_text);
 | 
						|
 | 
						|
    local res = exec_lua([[
 | 
						|
      parser = vim.treesitter.get_parser(0, "c")
 | 
						|
 | 
						|
      func_node = parser:parse():root():child(0)
 | 
						|
 | 
						|
      res = {}
 | 
						|
      for node, field in func_node:iter_children() do
 | 
						|
        table.insert(res, {node:type(), field})
 | 
						|
      end
 | 
						|
      return res
 | 
						|
    ]])
 | 
						|
 | 
						|
    eq({
 | 
						|
      {"primitive_type", "type"},
 | 
						|
      {"function_declarator", "declarator"},
 | 
						|
      {"compound_statement", "body"}
 | 
						|
    }, res)
 | 
						|
  end)
 | 
						|
 | 
						|
  it('allows to get a child by field', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert(test_text);
 | 
						|
 | 
						|
    local res = exec_lua([[
 | 
						|
      parser = vim.treesitter.get_parser(0, "c")
 | 
						|
 | 
						|
      func_node = parser:parse():root():child(0)
 | 
						|
 | 
						|
      local res = {}
 | 
						|
      for _, node in ipairs(func_node:field("type")) do
 | 
						|
        table.insert(res, {node:type(), node:range()})
 | 
						|
      end
 | 
						|
      return res
 | 
						|
    ]])
 | 
						|
 | 
						|
    eq({{ "primitive_type", 0, 0, 0, 4 }}, res)
 | 
						|
 | 
						|
    local res_fail = exec_lua([[
 | 
						|
      parser = vim.treesitter.get_parser(0, "c")
 | 
						|
 | 
						|
      return #func_node:field("foo") == 0
 | 
						|
    ]])
 | 
						|
 | 
						|
    assert(res_fail)
 | 
						|
  end)
 | 
						|
 | 
						|
  local query = [[
 | 
						|
    ((call_expression function: (identifier) @minfunc (argument_list (identifier) @min_id)) (eq? @minfunc "MIN"))
 | 
						|
    "for" @keyword
 | 
						|
    (primitive_type) @type
 | 
						|
    (field_expression argument: (identifier) @fieldarg)
 | 
						|
  ]]
 | 
						|
 | 
						|
  it('support query and iter by capture', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert(test_text)
 | 
						|
 | 
						|
    local res = exec_lua([[
 | 
						|
      cquery = vim.treesitter.parse_query("c", ...)
 | 
						|
      parser = vim.treesitter.get_parser(0, "c")
 | 
						|
      tree = parser:parse()
 | 
						|
      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
 | 
						|
        table.insert(res, {cquery.captures[cid], node:type(), node:range()})
 | 
						|
      end
 | 
						|
      return res
 | 
						|
    ]], query)
 | 
						|
 | 
						|
    eq({
 | 
						|
      { "type", "primitive_type", 8, 2, 8, 6 },
 | 
						|
      { "keyword", "for", 9, 2, 9, 5 },
 | 
						|
      { "type", "primitive_type", 9, 7, 9, 13 },
 | 
						|
      { "minfunc", "identifier", 11, 12, 11, 15 },
 | 
						|
      { "fieldarg", "identifier", 11, 16, 11, 18 },
 | 
						|
      { "min_id", "identifier", 11, 27, 11, 32 },
 | 
						|
      { "minfunc", "identifier", 12, 13, 12, 16 },
 | 
						|
      { "fieldarg", "identifier", 12, 17, 12, 19 },
 | 
						|
      { "min_id", "identifier", 12, 29, 12, 35 },
 | 
						|
      { "fieldarg", "identifier", 13, 14, 13, 16 }
 | 
						|
    }, res)
 | 
						|
  end)
 | 
						|
 | 
						|
  it('support query and iter by match', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert(test_text)
 | 
						|
 | 
						|
    local res = exec_lua([[
 | 
						|
      cquery = vim.treesitter.parse_query("c", ...)
 | 
						|
      parser = vim.treesitter.get_parser(0, "c")
 | 
						|
      tree = parser:parse()
 | 
						|
      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
 | 
						|
        local mrepr = {}
 | 
						|
        for cid,node in pairs(match) do
 | 
						|
          table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()})
 | 
						|
        end
 | 
						|
        table.insert(res, {pattern, mrepr})
 | 
						|
      end
 | 
						|
      return res
 | 
						|
    ]], query)
 | 
						|
 | 
						|
    eq({
 | 
						|
      { 3, { { "type", "primitive_type", 8, 2, 8, 6 } } },
 | 
						|
      { 2, { { "keyword", "for", 9, 2, 9, 5 } } },
 | 
						|
      { 3, { { "type", "primitive_type", 9, 7, 9, 13 } } },
 | 
						|
      { 4, { { "fieldarg", "identifier", 11, 16, 11, 18 } } },
 | 
						|
      { 1, { { "minfunc", "identifier", 11, 12, 11, 15 }, { "min_id", "identifier", 11, 27, 11, 32 } } },
 | 
						|
      { 4, { { "fieldarg", "identifier", 12, 17, 12, 19 } } },
 | 
						|
      { 1, { { "minfunc", "identifier", 12, 13, 12, 16 }, { "min_id", "identifier", 12, 29, 12, 35 } } },
 | 
						|
      { 4, { { "fieldarg", "identifier", 13, 14, 13, 16 } } }
 | 
						|
    }, res)
 | 
						|
  end)
 | 
						|
 | 
						|
  it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert('char* astring = "Hello World!";')
 | 
						|
 | 
						|
    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()
 | 
						|
      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
 | 
						|
        local mrepr = {}
 | 
						|
        for cid,node in pairs(match) do
 | 
						|
          table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()})
 | 
						|
        end
 | 
						|
        table.insert(res, {pattern, mrepr})
 | 
						|
      end
 | 
						|
      return res
 | 
						|
    ]])
 | 
						|
 | 
						|
    eq({
 | 
						|
      { 1, { { "quote", '"', 0, 16, 0, 17 } } },
 | 
						|
      { 2, { { "quote", '"', 0, 16, 0, 17 } } },
 | 
						|
      { 1, { { "quote", '"', 0, 29, 0, 30 } } },
 | 
						|
      { 2, { { "quote", '"', 0, 29, 0, 30 } } },
 | 
						|
    }, res)
 | 
						|
  end)
 | 
						|
 | 
						|
  it('allows to add predicates', function()
 | 
						|
    insert([[
 | 
						|
    int main(void) {
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    ]])
 | 
						|
 | 
						|
    local custom_query = "((identifier) @main (#is-main? @main))"
 | 
						|
 | 
						|
    local res = exec_lua([[
 | 
						|
    local query = require"vim.treesitter.query"
 | 
						|
 | 
						|
    local function is_main(match, pattern, bufnr, predicate)
 | 
						|
      local node = match[ predicate[2] ]
 | 
						|
 | 
						|
      return query.get_node_text(node, bufnr)
 | 
						|
    end
 | 
						|
 | 
						|
    local parser = vim.treesitter.get_parser(0, "c")
 | 
						|
 | 
						|
    query.add_predicate("is-main?", is_main)
 | 
						|
 | 
						|
    local query = query.parse_query("c", ...)
 | 
						|
 | 
						|
    local nodes = {}
 | 
						|
    for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do
 | 
						|
      table.insert(nodes, {node:range()})
 | 
						|
    end
 | 
						|
 | 
						|
    return nodes
 | 
						|
    ]], custom_query)
 | 
						|
 | 
						|
    eq({{0, 4, 0, 8}}, res)
 | 
						|
 | 
						|
    local res_list = exec_lua[[
 | 
						|
    local query = require'vim.treesitter.query'
 | 
						|
 | 
						|
    local list = query.list_predicates()
 | 
						|
 | 
						|
    table.sort(list)
 | 
						|
 | 
						|
    return list
 | 
						|
    ]]
 | 
						|
 | 
						|
    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 = [[
 | 
						|
/// 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;
 | 
						|
}]]
 | 
						|
 | 
						|
    local hl_query = [[
 | 
						|
(ERROR) @ErrorMsg
 | 
						|
 | 
						|
"if" @keyword
 | 
						|
"else" @keyword
 | 
						|
"for" @keyword
 | 
						|
"return" @keyword
 | 
						|
 | 
						|
"const" @type
 | 
						|
"static" @type
 | 
						|
"struct" @type
 | 
						|
"enum" @type
 | 
						|
"extern" @type
 | 
						|
 | 
						|
(string_literal) @string
 | 
						|
 | 
						|
(number_literal) @number
 | 
						|
(char_literal) @string
 | 
						|
 | 
						|
(type_identifier) @type
 | 
						|
((type_identifier) @Special (#eq? @Special "LuaRef"))
 | 
						|
 | 
						|
(primitive_type) @type
 | 
						|
(sized_type_specifier) @type
 | 
						|
 | 
						|
; Use lua regexes
 | 
						|
((identifier) @Identifier (#contains? @Identifier "lua_"))
 | 
						|
((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$"))
 | 
						|
((identifier) @Normal (#vim-match? @Constant "^lstate$"))
 | 
						|
 | 
						|
((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right))
 | 
						|
 | 
						|
(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);                                    |
 | 
						|
        }                                                              |
 | 
						|
                                                                       |
 | 
						|
        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 highlighter = vim.treesitter.highlighter
 | 
						|
      local query = ...
 | 
						|
      test_hl = highlighter.new(query, 0, "c")
 | 
						|
    ]], 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('inspects language', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    local keys, fields, symbols = unpack(exec_lua([[
 | 
						|
      local lang = vim.treesitter.inspect_language('c')
 | 
						|
      local keys, symbols = {}, {}
 | 
						|
      for k,_ in pairs(lang) do
 | 
						|
        keys[k] = true
 | 
						|
      end
 | 
						|
 | 
						|
      -- symbols array can have "holes" and is thus not a valid msgpack array
 | 
						|
      -- but we don't care about the numbers here (checked in the parser test)
 | 
						|
      for _, v in pairs(lang.symbols) do
 | 
						|
        table.insert(symbols, v)
 | 
						|
      end
 | 
						|
      return {keys, lang.fields, symbols}
 | 
						|
    ]]))
 | 
						|
 | 
						|
    eq({fields=true, symbols=true}, keys)
 | 
						|
 | 
						|
    local fset = {}
 | 
						|
    for _,f in pairs(fields) do
 | 
						|
      eq("string", type(f))
 | 
						|
      fset[f] = true
 | 
						|
    end
 | 
						|
    eq(true, fset["directive"])
 | 
						|
    eq(true, fset["initializer"])
 | 
						|
 | 
						|
    local has_named, has_anonymous
 | 
						|
    for _,s in pairs(symbols) do
 | 
						|
      eq("string", type(s[1]))
 | 
						|
      eq("boolean", type(s[2]))
 | 
						|
      if s[1] == "for_statement" and s[2] == true then
 | 
						|
        has_named = true
 | 
						|
      elseif s[1] == "|=" and s[2] == false then
 | 
						|
        has_anonymous = true
 | 
						|
      end
 | 
						|
    end
 | 
						|
    eq({true,true}, {has_named,has_anonymous})
 | 
						|
  end)
 | 
						|
  it('allows to set simple ranges', function()
 | 
						|
    if not check_parser() then return end
 | 
						|
 | 
						|
    insert(test_text)
 | 
						|
 | 
						|
    local res = exec_lua([[
 | 
						|
    parser = vim.treesitter.get_parser(0, "c")
 | 
						|
    return { parser:parse():root():range() }
 | 
						|
    ]])
 | 
						|
 | 
						|
    eq({0, 0, 19, 0}, res)
 | 
						|
 | 
						|
    -- 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() }
 | 
						|
    ]])
 | 
						|
 | 
						|
    eq({0, 0, 18, 1}, res2)
 | 
						|
  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
 | 
						|
      table.insert(nodes, node)
 | 
						|
    end
 | 
						|
 | 
						|
    parser:set_included_ranges(nodes)
 | 
						|
 | 
						|
    local root = parser:parse():root()
 | 
						|
 | 
						|
    local res = {}
 | 
						|
    for i=0,(root:named_child_count() - 1) do
 | 
						|
      table.insert(res, { root:named_child(i):range() })
 | 
						|
    end
 | 
						|
    return res
 | 
						|
    ]])
 | 
						|
 | 
						|
    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 },
 | 
						|
      { 14, 9, 14, 27 } }, res)
 | 
						|
  end)
 | 
						|
end)
 |