Named captures are dynamic

New c api for getting list of syntax_type names.
This commit is contained in:
Timothy Clem 2020-06-18 14:42:30 -07:00
parent 3bcb1f8c94
commit 54586c4e5b
3 changed files with 69 additions and 98 deletions

View file

@ -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

View file

@ -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!());

View file

@ -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<Box<[u8]>>,
c_syntax_type_names: Vec<*const u8>,
capture_map: HashMap<u32, NamedCapture>,
doc_capture_index: Option<u32>,
name_capture_index: Option<u32>,
@ -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<Point>,
pub docs: Option<String>,
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<u32, NamedCapture> = 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<NamedCapture> {
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<regex::Error> for Error {
fn from(error: regex::Error) -> Self {
Error::Regex(error)