diff --git a/cli/src/tests/tags_test.rs b/cli/src/tests/tags_test.rs index e9ad908c..d293806b 100644 --- a/cli/src/tests/tags_test.rs +++ b/cli/src/tests/tags_test.rs @@ -9,10 +9,8 @@ fn test_tags_javascript() { r#" ((function_definition name: (identifier) @name - body: (block - . (string) @doc)) @function + body: (block . (string) @doc)) @function (set! replace @doc "(^['\s]*)|(['\s]*$)")) - (function_definition name: (identifier) @name) @function (class_definition diff --git a/tags/src/lib.rs b/tags/src/lib.rs index bc4f8e9a..8e48cea0 100644 --- a/tags/src/lib.rs +++ b/tags/src/lib.rs @@ -1,12 +1,19 @@ use serde::{Serialize, Serializer}; -use tree_sitter::{Language, Parser, Query, QueryCursor, QueryError}; +use std::{ops, str}; +use tree_sitter::{Language, Node, Parser, Point, Query, QueryCursor, QueryError}; /// Contains the data neeeded to compute tags for code written in a /// particular language. pub struct TagsConfiguration { pub language: Language, pub query: Query, + call_capture_index: Option, + class_capture_index: Option, + doc_capture_index: Option, + function_capture_index: Option, locals_pattern_index: usize, + module_capture_index: Option, + name_capture_index: Option, } pub struct TagsContext { @@ -14,22 +21,10 @@ pub struct TagsContext { cursor: QueryCursor, } -#[derive(Debug, Serialize)] -pub struct Range { - pub start: i64, - pub end: i64, -} - #[derive(Debug, Serialize)] pub struct Loc { - pub byte_range: Range, - pub span: Span, -} - -#[derive(Debug, Serialize)] -pub struct Span { - pub start: Pos, - pub end: Pos, + pub byte_range: ops::Range, + pub span: ops::Range, } #[derive(Debug, Serialize)] @@ -56,22 +51,6 @@ pub struct Tag<'a> { pub docs: Option<&'a str>, } -impl Serialize for TagKind { - fn serialize(&self, s: S) -> Result - where - S: Serializer, - { - match self { - TagKind::Call => "Call", - TagKind::Module => "Module", - TagKind::Class => "Class", - TagKind::Method => "Method", - TagKind::Function => "Function", - } - .serialize(s) - } -} - impl TagsConfiguration { pub fn new( language: Language, @@ -89,10 +68,35 @@ impl TagsConfiguration { } } + let mut call_capture_index = None; + let mut class_capture_index = None; + let mut doc_capture_index = None; + let mut function_capture_index = None; + let mut module_capture_index = None; + let mut name_capture_index = None; + for (i, name) in query.capture_names().iter().enumerate() { + let index = match name.as_str() { + "call" => &mut call_capture_index, + "class" => &mut class_capture_index, + "doc" => &mut doc_capture_index, + "function" => &mut function_capture_index, + "module" => &mut module_capture_index, + "name" => &mut name_capture_index, + _ => continue, + }; + *index = Some(i as u32); + } + Ok(TagsConfiguration { language, query, locals_pattern_index, + function_capture_index, + class_capture_index, + module_capture_index, + doc_capture_index, + call_capture_index, + name_capture_index, }) } } @@ -105,7 +109,11 @@ impl TagsContext { } } - pub fn generate_tags(&mut self, config: &TagsConfiguration, source: &[u8]) -> Vec { + pub fn generate_tags<'a>( + &mut self, + config: &TagsConfiguration, + source: &'a [u8], + ) -> Vec> { self.parser .set_language(config.language) .expect("Incompatible language"); @@ -119,10 +127,70 @@ impl TagsContext { &source[node.byte_range()] }); matches - .map(|mat| { - for capture in mat.captures {} - unimplemented!(); + .filter_map(|mat| { + let mut call_node = None; + let mut doc_node = None; + let mut class_node = None; + let mut function_node = None; + let mut module_node = None; + let mut name_node = None; + + for capture in mat.captures { + let index = Some(capture.index); + let node = Some(capture.node); + if index == config.call_capture_index { + call_node = node; + } else if index == config.class_capture_index { + class_node = node; + } else if index == config.doc_capture_index { + doc_node = node; + } else if index == config.function_capture_index { + function_node = node; + } else if index == config.module_capture_index { + module_node = node; + } else if index == config.name_capture_index { + name_node = node; + } + } + + if let (Some(function), Some(name)) = (function_node, name_node) { + if let Ok(name) = str::from_utf8(&source[name.byte_range()]) { + return Some(Tag { + name, + line: "", + loc: loc_for_node(function), + kind: TagKind::Function, + docs: doc_node + .and_then(|n| str::from_utf8(&source[n.byte_range()]).ok()), + }); + } + } + + None }) .collect() } } + +impl Serialize for TagKind { + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + match self { + TagKind::Call => "Call", + TagKind::Module => "Module", + TagKind::Class => "Class", + TagKind::Method => "Method", + TagKind::Function => "Function", + } + .serialize(s) + } +} + +fn loc_for_node(node: Node) -> Loc { + Loc { + byte_range: node.byte_range(), + span: node.start_position()..node.start_position(), + } +}