diff --git a/tags/include/tree_sitter/tags.h b/tags/include/tree_sitter/tags.h index e1ed68bd..f6113a0f 100644 --- a/tags/include/tree_sitter/tags.h +++ b/tags/include/tree_sitter/tags.h @@ -18,17 +18,6 @@ typedef enum { TSTagsInvalidQuery, } TSTagsError; -typedef enum { - TSSyntaxTypeFunction, - TSSyntaxTypeMethod, - TSSyntaxTypeClass, - TSSyntaxTypeModule, - TSSyntaxTypeCall, - TSSyntaxTypeType, - TSSyntaxTypeInterface, - TSSyntaxTypeImplementation, -} TSTagSyntaxType; - typedef struct { uint32_t start_byte; uint32_t end_byte; @@ -40,7 +29,7 @@ typedef struct { TSPoint end_point; uint32_t docs_start_byte; uint32_t docs_end_byte; - TSTagSyntaxType syntax_type; + uint32_t syntax_type_id; bool is_definition; } TSTag; @@ -93,6 +82,9 @@ uint32_t ts_tags_buffer_tags_len(const TSTagsBuffer *); const char *ts_tags_buffer_docs(const TSTagsBuffer *); uint32_t ts_tags_buffer_docs_len(const TSTagsBuffer *); +// Get the syntax kinds for a scope. +const char **ts_tagger_syntax_kinds_for_scope_name(const TSTagger *, const char *scope_name, uint32_t *len); + #ifdef __cplusplus } #endif diff --git a/tags/src/c_lib.rs b/tags/src/c_lib.rs index 72c708d0..6dc48195 100644 --- a/tags/src/c_lib.rs +++ b/tags/src/c_lib.rs @@ -1,4 +1,4 @@ -use super::{Error, SyntaxType, TagsConfiguration, TagsContext}; +use super::{Error, TagsConfiguration, TagsContext}; use std::collections::HashMap; use std::ffi::CStr; use std::process::abort; @@ -19,19 +19,6 @@ pub enum TSTagsError { Unknown, } -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TSSyntaxType { - Function, - Method, - Class, - Module, - Call, - Type, - Interface, - Implementation, -} - #[repr(C)] pub struct TSPoint { row: u32, @@ -50,7 +37,7 @@ pub struct TSTag { pub end_point: TSPoint, pub docs_start_byte: u32, pub docs_end_byte: u32, - pub syntax_type: TSSyntaxType, + pub syntax_type_id: u32, pub is_definition: bool, } @@ -173,16 +160,7 @@ pub extern "C" fn ts_tagger_tag( }, docs_start_byte: prev_docs_len as u32, docs_end_byte: buffer.docs.len() as u32, - syntax_type: match tag.syntax_type { - SyntaxType::Function => TSSyntaxType::Function, - SyntaxType::Method => TSSyntaxType::Method, - SyntaxType::Class => TSSyntaxType::Class, - SyntaxType::Module => TSSyntaxType::Module, - SyntaxType::Call => TSSyntaxType::Call, - SyntaxType::Type => TSSyntaxType::Type, - SyntaxType::Interface => TSSyntaxType::Interface, - SyntaxType::Implementation => TSSyntaxType::Implementation, - }, + syntax_type_id: tag.syntax_type_id, is_definition: tag.is_definition, }); } @@ -231,6 +209,24 @@ pub extern "C" fn ts_tags_buffer_docs_len(this: *const TSTagsBuffer) -> u32 { buffer.docs.len() as u32 } +#[no_mangle] +pub extern "C" fn ts_tagger_syntax_kinds_for_scope_name( + this: *mut TSTagger, + scope_name: *const i8, + len: *mut u32, +) -> *const *const i8 { + let tagger = unwrap_mut_ptr(this); + let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) }; + let len = unwrap_mut_ptr(len); + + *len = 0; + if let Some(config) = tagger.languages.get(scope_name) { + *len = config.c_syntax_type_names.len() as u32; + return config.c_syntax_type_names.as_ptr() as *const *const i8 + } + std::ptr::null() +} + fn unwrap_ptr<'a, T>(result: *const T) -> &'a T { unsafe { result.as_ref() }.unwrap_or_else(|| { eprintln!("{}:{} - pointer must not be null", file!(), line!()); diff --git a/tags/src/lib.rs b/tags/src/lib.rs index 1959c753..3d5ce770 100644 --- a/tags/src/lib.rs +++ b/tags/src/lib.rs @@ -5,6 +5,7 @@ use regex::Regex; use std::ops::Range; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{fmt, mem, str}; +use std::ffi::CStr; use std::collections::HashMap; use tree_sitter::{ Language, Parser, Point, Query, QueryCursor, QueryError, QueryPredicateArg, Tree, @@ -19,6 +20,8 @@ const CANCELLATION_CHECK_INTERVAL: usize = 100; pub struct TagsConfiguration { pub language: Language, pub query: Query, + syntax_type_names: Vec>, + c_syntax_type_names: Vec<*const u8>, capture_map: HashMap, doc_capture_index: Option, name_capture_index: Option, @@ -30,24 +33,10 @@ pub struct TagsConfiguration { #[derive(Debug)] pub struct NamedCapture { - pub syntax_type: SyntaxType, + pub syntax_type_id: u32, pub is_definition: bool, } -// Should stay in sync with list of valid syntax types in semantic. -// See: https://github.com/github/semantic/blob/621696f5bc523a651f1cf9fc2ac58c557ea02d07/proto/semantic.proto#L165-L174 -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SyntaxType { - Function, - Method, - Class, - Module, - Call, - Type, - Interface, - Implementation, -} - pub struct TagsContext { parser: Parser, cursor: QueryCursor, @@ -61,7 +50,7 @@ pub struct Tag { pub span: Range, pub docs: Option, pub is_definition: bool, - pub syntax_type: SyntaxType, + pub syntax_type_id: u32, } #[derive(Debug, PartialEq)] @@ -70,6 +59,7 @@ pub enum Error { Regex(regex::Error), Cancelled, InvalidLanguage, + InvalidCapture(String), } #[derive(Debug, Default)] @@ -120,11 +110,13 @@ impl TagsConfiguration { } } - let mut capture_map: HashMap = HashMap::new(); + let mut capture_map = HashMap::new(); + let mut syntax_type_names = Vec::new(); let mut doc_capture_index = None; let mut name_capture_index = None; let mut local_scope_capture_index = None; let mut local_definition_capture_index = None; + let mut syntax_type_id = 0; for (i, name) in query.capture_names().iter().enumerate() { match name.as_str() { "" => continue, @@ -132,12 +124,32 @@ impl TagsConfiguration { "doc" => doc_capture_index = Some(i as u32), "local.scope" => local_scope_capture_index = Some(i as u32), "local.definition" => local_definition_capture_index = Some(i as u32), - _ => if let Some(nc) = NamedCapture::new(name) { - capture_map.insert(i as u32, nc); + "local.reference" => continue, + _ => { + let mut is_definition = false; + + let kind = if name.starts_with("definition.") { + is_definition = true; + name.trim_start_matches("definition.") + } else if name.starts_with("reference.") { + name.trim_start_matches("reference.") + } else { + return Err(Error::InvalidCapture(name.to_string())) + }.to_string()+"\0"; + + capture_map.insert(i as u32, NamedCapture{ syntax_type_id, is_definition }); + syntax_type_id+=1; + if let Ok(cstr) = CStr::from_bytes_with_nul(kind.as_bytes()) { + syntax_type_names.push(cstr.to_bytes_with_nul().to_vec().into_boxed_slice()); + } } } } + let c_syntax_type_names = syntax_type_names.iter().map( |s| { + s.as_ptr() + }).collect(); + let pattern_info = (0..query.pattern_count()) .map(|pattern_index| { let mut info = PatternInfo::default(); @@ -182,6 +194,8 @@ impl TagsConfiguration { Ok(TagsConfiguration { language, query, + syntax_type_names, + c_syntax_type_names, capture_map, doc_capture_index, name_capture_index, @@ -191,6 +205,13 @@ impl TagsConfiguration { pattern_info, }) } + + pub fn syntax_type_name(&self, id: u32) -> &str { + unsafe { + let cstr = CStr::from_ptr(self.syntax_type_names[id as usize].as_ptr() as *const i8).to_bytes(); + str::from_utf8(cstr).expect("syntax type name was not valid utf-8") + } + } } impl TagsContext { @@ -301,7 +322,7 @@ where let mut name_range = None; let mut doc_nodes = Vec::new(); let mut tag_node = None; - let mut syntax_type = SyntaxType::Function; + let mut syntax_type_id = 0; let mut is_definition = false; let mut docs_adjacent_node = None; @@ -320,7 +341,7 @@ where if let Some(named_capture) = self.config.capture_map.get(&capture.index) { tag_node = Some(capture.node); - syntax_type = named_capture.syntax_type; + syntax_type_id = named_capture.syntax_type_id; is_definition = named_capture.is_definition; } } @@ -407,7 +428,7 @@ where range, name_range, docs, - syntax_type, + syntax_type_id, is_definition, }; } @@ -421,7 +442,7 @@ where range, name_range, docs, - syntax_type, + syntax_type_id, is_definition, }, mat.pattern_index, @@ -440,44 +461,6 @@ where } } -impl NamedCapture { - pub fn new(name: &String) -> Option { - let mut is_definition = false; - - let kind = if name.starts_with("definition.") { - is_definition = true; - name.trim_start_matches("definition.") - } else if name.starts_with("reference.") { - name.trim_start_matches("reference.") - } else { - name - }; - - let syntax_type = match kind.as_ref() { - "function" => {is_definition = true; SyntaxType::Function}, - "method" => {is_definition = true; SyntaxType::Method}, - "class" => {is_definition = true; SyntaxType::Class}, - "module" => {is_definition = true; SyntaxType::Module}, - "call" => SyntaxType::Call, - "type" => SyntaxType::Type, - "interface" => SyntaxType::Interface, - "implementation" => SyntaxType::Implementation, - _ => return None, - }; - - return Some(NamedCapture{ - syntax_type, - is_definition - }) - } -} - -impl fmt::Display for SyntaxType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - format!("{:?}", self).fmt(f) - } -} - impl From for Error { fn from(error: regex::Error) -> Self { Error::Regex(error)