code-moniker-core 0.2.0

Core symbol-graph types and per-language extractors for code-moniker (pure Rust, no pgrx). Consumed by the CLI and the PostgreSQL extension.
Documentation
use tree_sitter::Node;

use crate::core::code_graph::{CodeGraph, Position};
use crate::core::moniker::Moniker;

pub enum NodeShape<'src> {
	Symbol(Symbol<'src>),
	Annotation { kind: &'static [u8] },
	Skip,
	Recurse,
}

pub struct Symbol<'src> {
	pub moniker: Moniker,
	pub kind: &'static [u8],
	pub visibility: &'static [u8],
	pub signature: Option<Vec<u8>>,
	pub body: Option<Node<'src>>,
	pub position: Position,
	pub annotated_by: Vec<RefSpec>,
}

pub struct RefSpec {
	pub kind: &'static [u8],
	pub target: Moniker,
	pub confidence: &'static [u8],
	pub position: Position,
	pub receiver_hint: &'static [u8],
	pub alias: &'static [u8],
}

pub trait LangStrategy {
	fn classify<'src>(
		&self,
		node: Node<'src>,
		scope: &Moniker,
		source: &'src [u8],
		graph: &mut CodeGraph,
	) -> NodeShape<'src>;

	#[allow(unused_variables)]
	fn before_body(
		&self,
		node: Node<'_>,
		kind: &[u8],
		moniker: &Moniker,
		source: &[u8],
		graph: &mut CodeGraph,
	) {
	}

	#[allow(unused_variables)]
	fn after_body(&self, kind: &[u8], moniker: &Moniker) {}

	#[allow(unused_variables)]
	fn on_symbol_emitted(
		&self,
		node: Node<'_>,
		sym_kind: &[u8],
		sym_moniker: &Moniker,
		source: &[u8],
		graph: &mut CodeGraph,
	) {
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::core::moniker::MonikerBuilder;

	struct FakeStrategy;

	impl LangStrategy for FakeStrategy {
		fn classify<'src>(
			&self,
			node: Node<'src>,
			_scope: &Moniker,
			_source: &'src [u8],
			_graph: &mut CodeGraph,
		) -> NodeShape<'src> {
			match node.kind() {
				"line_comment" => NodeShape::Annotation { kind: b"comment" },
				_ => NodeShape::Recurse,
			}
		}
	}

	fn anchor() -> Moniker {
		MonikerBuilder::new().project(b"app").build()
	}

	#[test]
	fn fake_strategy_classifies_line_comment_as_annotation() {
		let s = FakeStrategy;
		let mut p = tree_sitter::Parser::new();
		p.set_language(&tree_sitter_rust::LANGUAGE.into()).unwrap();
		let src = b"// hi\nfn main() {}";
		let tree = p.parse(src, None).unwrap();
		let mut cursor = tree.root_node().walk();
		let scope = anchor();
		let mut g = CodeGraph::new(scope.clone(), b"module");
		let mut saw_comment = false;
		for child in tree.root_node().children(&mut cursor) {
			if let NodeShape::Annotation { kind } = s.classify(child, &scope, src, &mut g) {
				assert_eq!(kind, b"comment");
				saw_comment = true;
			}
		}
		assert!(saw_comment);
	}

	#[test]
	fn fake_strategy_recurses_on_unknown_kinds() {
		let s = FakeStrategy;
		let mut p = tree_sitter::Parser::new();
		p.set_language(&tree_sitter_rust::LANGUAGE.into()).unwrap();
		let src = b"fn main() {}";
		let tree = p.parse(src, None).unwrap();
		let mut cursor = tree.root_node().walk();
		let scope = anchor();
		let mut g = CodeGraph::new(scope.clone(), b"module");
		for child in tree.root_node().children(&mut cursor) {
			assert!(matches!(
				s.classify(child, &scope, src, &mut g),
				NodeShape::Recurse
			));
		}
	}
}