mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	tests: make treesitter its own category
This commit is contained in:
		@@ -725,6 +725,19 @@ function module.pending_win32(pending_fn)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function module.pending_c_parser(pending_fn)
 | 
			
		||||
  local status, msg = unpack(module.exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]]))
 | 
			
		||||
  if not status then
 | 
			
		||||
    if module.isCI() then
 | 
			
		||||
      error("treesitter C parser not found, required on CI: " .. msg)
 | 
			
		||||
    else
 | 
			
		||||
      pending_fn 'no C parser, skipping'
 | 
			
		||||
      return true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Calls pending() and returns `true` if the system is too slow to
 | 
			
		||||
-- run fragile or expensive tests. Else returns `false`.
 | 
			
		||||
function module.skip_fragile(pending_fn, cond)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										400
									
								
								test/functional/treesitter/highlight_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								test/functional/treesitter/highlight_spec.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,400 @@
 | 
			
		||||
local helpers = require('test.functional.helpers')(after_each)
 | 
			
		||||
local Screen = require('test.functional.ui.screen')
 | 
			
		||||
 | 
			
		||||
local clear = helpers.clear
 | 
			
		||||
local insert = helpers.insert
 | 
			
		||||
local exec_lua = helpers.exec_lua
 | 
			
		||||
local feed = helpers.feed
 | 
			
		||||
local pending_c_parser = helpers.pending_c_parser
 | 
			
		||||
 | 
			
		||||
before_each(clear)
 | 
			
		||||
 | 
			
		||||
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 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 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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}]]
 | 
			
		||||
 | 
			
		||||
describe('treesitter 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};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exec_lua([[ hl_query = ... ]], hl_query)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('is updated with edits', function()
 | 
			
		||||
    if pending_c_parser(pending) 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
 | 
			
		||||
      test_hl = highlighter.new(parser, {queries = {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("supports with custom parser", function()
 | 
			
		||||
    if pending_c_parser(pending) then return end
 | 
			
		||||
 | 
			
		||||
    screen:set_default_attr_ids {
 | 
			
		||||
      [1] = {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")
 | 
			
		||||
 | 
			
		||||
      local nodes = {}
 | 
			
		||||
      for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do
 | 
			
		||||
        table.insert(nodes, node)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      parser:set_included_regions({nodes})
 | 
			
		||||
 | 
			
		||||
      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);            |
 | 
			
		||||
        }                                                            |
 | 
			
		||||
      }                                                              |
 | 
			
		||||
    ^}                                                                |
 | 
			
		||||
                                                                     |
 | 
			
		||||
    ]] }
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it("supports injected languages", function()
 | 
			
		||||
    if pending_c_parser(pending) 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
 | 
			
		||||
      test_hl = highlighter.new(parser, {queries = {c = 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)
 | 
			
		||||
							
								
								
									
										71
									
								
								test/functional/treesitter/language_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								test/functional/treesitter/language_spec.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
local helpers = require('test.functional.helpers')(after_each)
 | 
			
		||||
 | 
			
		||||
local clear = helpers.clear
 | 
			
		||||
local eq = helpers.eq
 | 
			
		||||
local exec_lua = helpers.exec_lua
 | 
			
		||||
local pcall_err = helpers.pcall_err
 | 
			
		||||
local matches = helpers.matches
 | 
			
		||||
local pending_c_parser = helpers.pending_c_parser
 | 
			
		||||
 | 
			
		||||
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:0: 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')"))
 | 
			
		||||
 | 
			
		||||
    -- Should not throw an error when silent
 | 
			
		||||
    eq(false, exec_lua("return vim.treesitter.require_language('borklang', nil, true)"))
 | 
			
		||||
    eq(false, exec_lua("return vim.treesitter.require_language('borklang', 'borkbork.so', true)"))
 | 
			
		||||
 | 
			
		||||
    eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
 | 
			
		||||
       pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')"))
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('inspects language', function()
 | 
			
		||||
    if pending_c_parser(pending) 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)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										576
									
								
								test/functional/treesitter/parser_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								test/functional/treesitter/parser_spec.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,576 @@
 | 
			
		||||
local helpers = require('test.functional.helpers')(after_each)
 | 
			
		||||
 | 
			
		||||
local clear = helpers.clear
 | 
			
		||||
local eq = helpers.eq
 | 
			
		||||
local insert = helpers.insert
 | 
			
		||||
local exec_lua = helpers.exec_lua
 | 
			
		||||
local feed = helpers.feed
 | 
			
		||||
local pending_c_parser = helpers.pending_c_parser
 | 
			
		||||
 | 
			
		||||
before_each(clear)
 | 
			
		||||
 | 
			
		||||
describe('treesitter parser API', function()
 | 
			
		||||
 | 
			
		||||
  it('parses buffer', function()
 | 
			
		||||
    if helpers.pending_win32(pending) or pending_c_parser(pending) then return end
 | 
			
		||||
 | 
			
		||||
    insert([[
 | 
			
		||||
      int main() {
 | 
			
		||||
        int x = 3;
 | 
			
		||||
      }]])
 | 
			
		||||
 | 
			
		||||
    exec_lua([[
 | 
			
		||||
      parser = vim.treesitter.get_parser(0, "c")
 | 
			
		||||
      tree = parser:parse()[1]
 | 
			
		||||
      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()"))
 | 
			
		||||
 | 
			
		||||
    feed("2G7|ay")
 | 
			
		||||
    exec_lua([[
 | 
			
		||||
      tree2 = parser:parse()[1]
 | 
			
		||||
      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()}"))
 | 
			
		||||
 | 
			
		||||
    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"))
 | 
			
		||||
 | 
			
		||||
    eq("string", exec_lua("return type(child:id())"))
 | 
			
		||||
    eq(true, exec_lua("return child:id() == child:id()"))
 | 
			
		||||
    -- separate lua object, but represents same node
 | 
			
		||||
    eq(true, exec_lua("return child:id() == root:child(0):id()"))
 | 
			
		||||
    eq(false, exec_lua("return child:id() == descendant2:id()"))
 | 
			
		||||
    eq(false, exec_lua("return child:id() == nil"))
 | 
			
		||||
    eq(false, exec_lua("return child:id() == tree"))
 | 
			
		||||
 | 
			
		||||
    -- unchanged buffer: return the same tree
 | 
			
		||||
    eq(true, exec_lua("return parser:parse()[1] == 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 pending_c_parser(pending) then return end
 | 
			
		||||
 | 
			
		||||
    insert(test_text);
 | 
			
		||||
 | 
			
		||||
    local res = exec_lua([[
 | 
			
		||||
      parser = vim.treesitter.get_parser(0, "c")
 | 
			
		||||
 | 
			
		||||
      func_node = parser:parse()[1]: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 pending_c_parser(pending) then return end
 | 
			
		||||
 | 
			
		||||
    insert(test_text);
 | 
			
		||||
 | 
			
		||||
    local res = exec_lua([[
 | 
			
		||||
      parser = vim.treesitter.get_parser(0, "c")
 | 
			
		||||
 | 
			
		||||
      func_node = parser:parse()[1]: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("supports runtime queries", function()
 | 
			
		||||
    if pending_c_parser(pending) then return end
 | 
			
		||||
 | 
			
		||||
    local ret = exec_lua [[
 | 
			
		||||
      return require"vim.treesitter.query".get_query("c", "highlights").captures[1]
 | 
			
		||||
    ]]
 | 
			
		||||
 | 
			
		||||
    eq('variable', ret)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('support query and iter by capture', function()
 | 
			
		||||
    if pending_c_parser(pending) 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()[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
 | 
			
		||||
        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 pending_c_parser(pending) 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()[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
 | 
			
		||||
        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 pending_c_parser(pending) 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()[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
 | 
			
		||||
        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()[1]: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('allows to set simple ranges', function()
 | 
			
		||||
    if pending_c_parser(pending) then return end
 | 
			
		||||
 | 
			
		||||
    insert(test_text)
 | 
			
		||||
 | 
			
		||||
    local res = exec_lua [[
 | 
			
		||||
    parser = vim.treesitter.get_parser(0, "c")
 | 
			
		||||
    return { parser:parse()[1]: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()[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 [[
 | 
			
		||||
      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 pending_c_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()[1]:root(), 0, 0, 19) do
 | 
			
		||||
      table.insert(nodes, node)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    parser:set_included_regions({nodes})
 | 
			
		||||
 | 
			
		||||
    local root = parser:parse()[1]: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, 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()[1]:root():range() }
 | 
			
		||||
    ]]
 | 
			
		||||
 | 
			
		||||
    eq({ 0, 0, 0, 13 }, ret)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it("allows to run queries with string parsers", function()
 | 
			
		||||
    local txt = [[
 | 
			
		||||
      int foo = 42;
 | 
			
		||||
      int bar = 13;
 | 
			
		||||
    ]]
 | 
			
		||||
 | 
			
		||||
    local ret = exec_lua([[
 | 
			
		||||
    local str = ...
 | 
			
		||||
    local parser = vim.treesitter.get_string_parser(str, "c")
 | 
			
		||||
 | 
			
		||||
    local nodes = {}
 | 
			
		||||
    local query = vim.treesitter.parse_query("c", '((identifier) @id (eq? @id "foo"))')
 | 
			
		||||
 | 
			
		||||
    for _, node in query:iter_captures(parser:parse()[1]:root(), str, 0, 2) do
 | 
			
		||||
      table.insert(nodes, { node:range() })
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    return nodes]], txt)
 | 
			
		||||
 | 
			
		||||
    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 123
 | 
			
		||||
#define VALUE1 123
 | 
			
		||||
#define VALUE2 123
 | 
			
		||||
      ]])
 | 
			
		||||
    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, 0, 7, 0},   -- root tree
 | 
			
		||||
          {3, 14, 3, 17}, -- VALUE 123
 | 
			
		||||
          {4, 15, 4, 18}, -- VALUE1 123
 | 
			
		||||
          {5, 15, 5, 18}, -- VALUE2 123
 | 
			
		||||
          {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
 | 
			
		||||
          {2, 29, 2, 68}  -- 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, 0, 7, 0},   -- root tree
 | 
			
		||||
          {3, 14, 5, 18}, -- VALUE 123
 | 
			
		||||
                          -- VALUE1 123
 | 
			
		||||
                          -- VALUE2 123
 | 
			
		||||
          {1, 26, 2, 68}  -- 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)
 | 
			
		||||
 | 
			
		||||
    describe("when using the offset directive", function()
 | 
			
		||||
      it("should shift the range by the directive amount", function()
 | 
			
		||||
        exec_lua([[
 | 
			
		||||
        parser = vim.treesitter.get_parser(0, "c", {
 | 
			
		||||
          queries = {
 | 
			
		||||
            c = "(preproc_def ((preproc_arg) @c (#offset! @c 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @c)"}})
 | 
			
		||||
        ]])
 | 
			
		||||
 | 
			
		||||
        eq("table", exec_lua("return type(parser:children().c)"))
 | 
			
		||||
        eq({
 | 
			
		||||
          {0, 0, 7, 0},   -- root tree
 | 
			
		||||
          {3, 15, 3, 16}, -- VALUE 123
 | 
			
		||||
          {4, 16, 4, 17}, -- VALUE1 123
 | 
			
		||||
          {5, 16, 5, 17}, -- VALUE2 123
 | 
			
		||||
          {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
 | 
			
		||||
          {2, 29, 2, 68}  -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
 | 
			
		||||
        }, get_ranges())
 | 
			
		||||
      end)
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  describe("when getting the language for a range", function()
 | 
			
		||||
    before_each(function()
 | 
			
		||||
      insert([[
 | 
			
		||||
int x = INT_MAX;
 | 
			
		||||
#define VALUE 123456789
 | 
			
		||||
      ]])
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it("should return the correct language tree", function()
 | 
			
		||||
      local result = exec_lua([[
 | 
			
		||||
      parser = vim.treesitter.get_parser(0, "c", {
 | 
			
		||||
        queries = { c = "(preproc_def (preproc_arg) @c)"}})
 | 
			
		||||
 | 
			
		||||
      local sub_tree = parser:language_for_range({1, 18, 1, 19})
 | 
			
		||||
 | 
			
		||||
      return sub_tree == parser:children().c
 | 
			
		||||
      ]])
 | 
			
		||||
 | 
			
		||||
      eq(result, true)
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  describe("when getting/setting match data", function()
 | 
			
		||||
    describe("when setting for the whole match", function()
 | 
			
		||||
      it("should set/get the data correctly", function()
 | 
			
		||||
        insert([[
 | 
			
		||||
          int x = 3;
 | 
			
		||||
        ]])
 | 
			
		||||
 | 
			
		||||
        local result = exec_lua([[
 | 
			
		||||
        local result
 | 
			
		||||
 | 
			
		||||
        query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! "key" "value"))')
 | 
			
		||||
        parser = vim.treesitter.get_parser(0, "c")
 | 
			
		||||
 | 
			
		||||
        for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, 1) do
 | 
			
		||||
          result = metadata.key
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
        ]])
 | 
			
		||||
 | 
			
		||||
        eq(result, "value")
 | 
			
		||||
      end)
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    describe("when setting for a capture match", function()
 | 
			
		||||
      it("should set/get the data correctly", function()
 | 
			
		||||
        insert([[
 | 
			
		||||
          int x = 3;
 | 
			
		||||
        ]])
 | 
			
		||||
 | 
			
		||||
        local result = exec_lua([[
 | 
			
		||||
        local result
 | 
			
		||||
 | 
			
		||||
        query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))')
 | 
			
		||||
        parser = vim.treesitter.get_parser(0, "c")
 | 
			
		||||
 | 
			
		||||
        for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, 1) do
 | 
			
		||||
          result = metadata[pattern].key
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
        ]])
 | 
			
		||||
 | 
			
		||||
        eq(result, "value")
 | 
			
		||||
      end)
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
		Reference in New Issue
	
	Block a user