Skip to content

ltk_ritobin: Add Locator API #176

Description

@Crauzer

Proposed API

   pub struct NodePath(Vec<NodeId>);

   impl NodePath {
       pub fn root(&self) -> NodeId;
       pub fn last(&self) -> NodeId;
       pub fn kinds(&self, cst: &Cst) -> Vec<TreeKind>;
       pub fn last_kind(&self, cst: &Cst) -> TreeKind;
   }
   impl std::ops::Deref for NodePath {
       type Target = [NodeId];
   }

   /// A token and the path to it
   pub struct TokenLocator {
       pub token: TokenId,
       pub path: NodePath,
   }

   impl TokenLocator {
       pub fn prev(&self, cst: &Cst) -> Option<TokenLocator>;
       pub fn next(&self, cst: &Cst) -> Option<TokenLocator>;
   }

   pub enum Locator {
       Gap { path: NodePath },
       Token(TokenLocator),
       Boundary { left: TokenLocator, right: TokenLocator },
   }

   impl Locator {
       pub fn path(&self) -> &NodePath;
       pub fn token(&self) -> Option<&TokenLocator>;
       pub fn span(&self, cst: &Cst) -> Span;
   }

   impl Cst {
       pub fn locate(&self, offset: u32) -> Locator;
       pub fn common_ancestors(&self, range: Span) -> NodePath;
   }

How this works

  • 3 states per cursor location is the Locator enum. A Token range has inclusive start/end. 2 tokens can share a Boundary. Gap keeps the enclosing node path which makes scoping simple.

  • common_ancestors is a way to figure out the deepest node which still contains a range

  • prev and next for peeking both directions to get context

  • Span::contains should be half-open so it matches the other

Why ?

These are genuinely useful API additions that we should have in the crate itself. They allow us to build more complex features in the LSP by abstracting away some of the spaghetti from Visitor implementations downstream, where we currently duplicate the behavior for each visitor. Being able to get a locator query like this means we can get rid of the workaround implementations in the LSP.

Span boundary bug

All 3 finders in the LSP have the same bug with descent where Span being inconsistent causes them to build the wrong node chain.
Visitor doc says that exit_tree should run after children end, this also happens when a tree gets skipped. Stop is inconsistent because it does not call exit_tree on the node's open ancestors which causes scope stack corruption.

common_ancestors makes it possible to implement code actions, expansion and formatting for selections. It should also be possible to show a breadcrumb path composed from the returned NodePath.

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions