mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	perf(treesitter): use child_containing_descendant() in is_ancestor()
				
					
				
			**Problem:** `is_ancestor()` uses a slow, bottom-up parent lookup which has performance pitfalls detailed in #28512. **Solution:** Take `is_ancestor()` from $O(n^2)$ to $O(n)$ by incorporating the use of the `child_containing_descendant()` function
This commit is contained in:
		
				
					committed by
					
						
						Christian Clason
					
				
			
			
				
	
			
			
			
						parent
						
							921dc22fc0
						
					
				
				
					commit
					64847fbdc9
				
			@@ -159,16 +159,8 @@ function M.is_ancestor(dest, source)
 | 
				
			|||||||
    return false
 | 
					    return false
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local current = source ---@type TSNode?
 | 
					  -- child_containing_descendant returns nil if dest is a direct parent
 | 
				
			||||||
  while current ~= nil do
 | 
					  return source:parent() == dest or dest:child_containing_descendant(source) ~= nil
 | 
				
			||||||
    if current == dest then
 | 
					 | 
				
			||||||
      return true
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    current = current:parent()
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return false
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Returns the node's range or an unpacked range table
 | 
					--- Returns the node's range or an unpacked range table
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,12 +21,16 @@ describe('treesitter utils', function()
 | 
				
			|||||||
      local parser = vim.treesitter.get_parser(0, 'c')
 | 
					      local parser = vim.treesitter.get_parser(0, 'c')
 | 
				
			||||||
      local tree = parser:parse()[1]
 | 
					      local tree = parser:parse()[1]
 | 
				
			||||||
      local root = tree:root()
 | 
					      local root = tree:root()
 | 
				
			||||||
      _G.ancestor = root:child(0)
 | 
					      _G.ancestor = assert(root:child(0))
 | 
				
			||||||
      _G.child = _G.ancestor:child(0)
 | 
					      _G.child = assert(_G.ancestor:named_child(1))
 | 
				
			||||||
 | 
					      _G.child_sibling = assert(_G.ancestor:named_child(2))
 | 
				
			||||||
 | 
					      _G.grandchild = assert(_G.child:named_child(0))
 | 
				
			||||||
    end)
 | 
					    end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    eq(true, exec_lua('return vim.treesitter.is_ancestor(_G.ancestor, _G.child)'))
 | 
					    eq(true, exec_lua('return vim.treesitter.is_ancestor(_G.ancestor, _G.child)'))
 | 
				
			||||||
 | 
					    eq(true, exec_lua('return vim.treesitter.is_ancestor(_G.ancestor, _G.grandchild)'))
 | 
				
			||||||
    eq(false, exec_lua('return vim.treesitter.is_ancestor(_G.child, _G.ancestor)'))
 | 
					    eq(false, exec_lua('return vim.treesitter.is_ancestor(_G.child, _G.ancestor)'))
 | 
				
			||||||
 | 
					    eq(false, exec_lua('return vim.treesitter.is_ancestor(_G.child, _G.child_sibling)'))
 | 
				
			||||||
  end)
 | 
					  end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('can detect if a position is contained in a node', function()
 | 
					  it('can detect if a position is contained in a node', function()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user