1172 lines
34 KiB
C++
1172 lines
34 KiB
C++
|
|
/////////////////////////////////////////////
|
|
// TEMP until I implement more generic language stuff
|
|
/////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////
|
|
// C
|
|
|
|
/////////////////////////////////////////////
|
|
// C++
|
|
|
|
TSQuery* tree_sitter_cpp_index_query;
|
|
String_Const_u8 TS_CPP_INDEX_QUERY = string_u8_litexpr("(_ \"{\" @Start \"}\" @End ) @ScopeNest\n");
|
|
|
|
String_Const_u8 TS_CPP_FUNCTION_QUERY = string_u8_litexpr(R"DONE(
|
|
(function_declarator) @function_identifier
|
|
)DONE");
|
|
|
|
String_Const_u8 TS_CPP_TYPE_QUERY = string_u8_litexpr(R"DONE(
|
|
(struct_specifier
|
|
name: (type_identifier) @prefixStruct
|
|
)
|
|
(enum_specifier
|
|
name: (type_identifier) @prefixEnum
|
|
)
|
|
(class_specifier
|
|
name: (type_identifier) @prefixClass
|
|
)
|
|
)DONE");
|
|
|
|
String_Const_u8 TS_CPP_HIGHLIGHT_QUERY = string_u8_litexpr(R"DONE(
|
|
(call_expression function: [
|
|
(identifier) @defcolor_function
|
|
(field_expression field: (field_identifier) @defcolor_function)])
|
|
|
|
(function_declarator
|
|
declarator: [(identifier) (field_identifier)] @defcolor_function)
|
|
|
|
(preproc_def
|
|
name: (identifier) @defcolor_macro)
|
|
|
|
(preproc_function_def
|
|
name: (identifier) @defcolor_macro)
|
|
|
|
(type_identifier) @defcolor_type
|
|
|
|
(call_expression
|
|
function: (parenthesized_expression
|
|
(identifier) @defcolor_type))
|
|
|
|
[(primitive_type) (type_qualifier) (storage_class_specifier)
|
|
(break_statement) (continue_statement) "union" "return" "do"
|
|
"while" "for" "if" "class" "struct" "enum" "sizeof"
|
|
"else" "switch" "case"] @defcolor_keyword
|
|
|
|
[(number_literal) (string_literal) (raw_string_literal)] @defcolor_str_constant
|
|
|
|
[(preproc_directive) "#define" "#if" "#elif" "#else" "#endif"
|
|
"#include"] @defcolor_preproc
|
|
|
|
["{" "}" ";" ":" ","] @defcolor_text_default
|
|
|
|
(comment) @defcolor_comment
|
|
)DONE");
|
|
|
|
/////////////////////////////////////////////
|
|
// Jai
|
|
|
|
String_Const_u8 TS_JAI_FUNCTION_QUERY = string_u8_litexpr(R"DONE(
|
|
|
|
(procedure_declaration
|
|
name: (identifier) @print1
|
|
(procedure
|
|
(named_parameters) @print2
|
|
(procedure_returns) @print
|
|
)
|
|
)
|
|
|
|
)DONE");
|
|
|
|
String_Const_u8 TS_JAI_TYPE_QUERY = string_u8_litexpr(R"DONE(
|
|
|
|
(struct_declaration
|
|
name: (identifier) @prefixStruct
|
|
)
|
|
|
|
(enum_declaration
|
|
name: (identifier) @prefixEnum
|
|
)
|
|
|
|
)DONE");
|
|
|
|
String_Const_u8 TS_JAI_HIGHLIGHT_QUERY = string_u8_litexpr(R"DONE(
|
|
|
|
; keywords
|
|
[
|
|
"if"
|
|
"else"
|
|
"break"
|
|
"continue"
|
|
"return"
|
|
"struct"
|
|
"enum"
|
|
"for"
|
|
"defer"
|
|
"cast"
|
|
"xx"
|
|
"ifx"
|
|
"null"
|
|
] @defcolor_keyword
|
|
|
|
; # preceeded
|
|
[
|
|
(compiler_directive)
|
|
(import)
|
|
(char_string)
|
|
] @defcolor_macro
|
|
|
|
(import (identifier) @defcolor_type)
|
|
|
|
; Identifiers
|
|
(struct_declaration
|
|
name: (identifier) @defcolor_type
|
|
)
|
|
|
|
(struct_literal
|
|
type: (identifier) @defcolor_type
|
|
)
|
|
|
|
(enum_declaration
|
|
name: (identifier) @defcolor_type
|
|
)
|
|
|
|
(enum_declaration "{" (identifier) @defcolor_type)
|
|
|
|
(variable_declaration
|
|
type: (types) @defcolor_type
|
|
)
|
|
|
|
(procedure_declaration
|
|
name: (identifier) @defcolor_function
|
|
)
|
|
|
|
(call_expression
|
|
function: (identifier) @defcolor_function
|
|
)
|
|
|
|
(procedure
|
|
result: (procedure_returns) @defcolor_type
|
|
)
|
|
|
|
; Constants & Literals
|
|
[
|
|
(string)
|
|
(string_directive)
|
|
] @defcolor_str_constant
|
|
(escape_sequence) @defcolor_special_character
|
|
(integer) @defcolor_int_constant
|
|
(float) @defcolor_float_constant
|
|
(boolean) @defcolor_bool_constant
|
|
|
|
(array_literal
|
|
type: (identifier) @defcolor_type
|
|
)
|
|
|
|
; Comments
|
|
(note) @defcolor_comment
|
|
(block_comment) @defcolor_comment
|
|
(block_comment_text) @defcolor_comment
|
|
|
|
)DONE");
|
|
|
|
// NOTE(PS): source: https://github.com/St0wy/tree-sitter-jai/blob/main/queries/highlights.scm
|
|
String_Const_u8 TS_JAI_HIGHLIGHT_QUERY_ = string_u8_litexpr(R"DONE(
|
|
|
|
[
|
|
(compiler_directive)
|
|
(import)
|
|
] @defcolor_macro
|
|
|
|
; Keywords
|
|
; TODO : complete this list
|
|
[
|
|
"struct"
|
|
"enum"
|
|
"defer"
|
|
"cast"
|
|
"xx"
|
|
"return"
|
|
] @defcolor_keyword
|
|
|
|
; Conditionals
|
|
[
|
|
"if"
|
|
"else"
|
|
"case"
|
|
"break"
|
|
] @defcolor_keyword
|
|
|
|
((if_expression
|
|
[
|
|
"then"
|
|
"ifx"
|
|
"else"
|
|
] @defcolor_keyword)
|
|
(#set! "priority" 105))
|
|
|
|
; Repeats
|
|
|
|
[
|
|
"for"
|
|
"while"
|
|
"continue"
|
|
] @defcolor_keyword
|
|
|
|
; Variables
|
|
|
|
(identifier) @defcolor_text_default
|
|
|
|
; Namespaces
|
|
|
|
(import (identifier) @defcolor_text_default)
|
|
|
|
; Parameters
|
|
|
|
(parameter (identifier) @defcolor_text_default ":" "="? (identifier)? @defcolor_str_constant)
|
|
|
|
(default_parameter (identifier) @defcolor_text_default ":=")
|
|
|
|
(call_expression argument: (identifier) @defcolor_text_default "=")
|
|
|
|
; Functions
|
|
|
|
(procedure_declaration (identifier) @defcolor_function)
|
|
|
|
(procedure_declaration (identifier) @defcolor_function (procedure (block)))
|
|
|
|
(call_expression function: (identifier) @defcolor_function)
|
|
|
|
; Types
|
|
|
|
(type (identifier) @defcolor_type)
|
|
|
|
((type (identifier) @defcolor_type)
|
|
(#any-of? @type.builtin
|
|
"bool"
|
|
"int" "s8" "s16" "s32" "s64"
|
|
"u8" "u16" "u32" "u64"
|
|
"string"))
|
|
|
|
|
|
(struct_declaration (identifier) @defcolor_type "::")
|
|
|
|
(enum_declaration (identifier) @defcolor_type "::")
|
|
|
|
;(union_declaration (identifier) @defcolor_type "::")
|
|
|
|
(const_declaration (identifier) @defcolor_type "::" [(array_type) (pointer_type)])
|
|
|
|
(struct . (identifier) @defcolor_type)
|
|
|
|
;(field_type . (identifier) @namespace "." (identifier) @defcolor_type)
|
|
|
|
;(bit_set_type (identifier) @defcolor_type ";")
|
|
|
|
;(procedure_type (parameters (parameter (identifier) @defcolor_type)))
|
|
|
|
;(polymorphic_parameters (identifier) @defcolor_type)
|
|
|
|
((identifier) @defcolor_type
|
|
(#lua-match? @defcolor_type "^[A-Z][a-zA-Z0-9]*$")
|
|
(#not-has-parent? @defcolor_type parameter procedure_declaration call_expression))
|
|
|
|
; Fields
|
|
|
|
(member_expression "." (identifier) @defcolor_text_default)
|
|
|
|
;(struct_type "{" (identifier) @defcolor_text_default)
|
|
|
|
(struct_field (identifier) @defcolor_text_default "="?)
|
|
|
|
(field (identifier) @defcolor_text_default)
|
|
|
|
; Constants
|
|
|
|
((identifier) @defcolor_text_default
|
|
(#lua-match? @defcolor_str_constnat "^_*[A-Z][A-Z0-9_]*$")
|
|
(#not-has-parent? @text_default type parameter))
|
|
|
|
(member_expression . "." (identifier) @defcolor_text_default)
|
|
|
|
(enum_declaration "{" (identifier) @defcolor_text_default)
|
|
|
|
; Literals
|
|
|
|
(number) @defcolor_int_constant
|
|
|
|
(float) @defcolor_float_constant
|
|
|
|
(string) @defcolor_str_constnat
|
|
|
|
;(character) @defcolor_str_constnat
|
|
|
|
(escape_sequence) @defcolor_str_constant
|
|
|
|
(boolean) @defcolor_bool_constant
|
|
|
|
[
|
|
(uninitialized)
|
|
(null)
|
|
] @defcolor_text_default
|
|
|
|
((identifier) @defcolor_text_default
|
|
(#any-of? @defcolor_text_default "context"))
|
|
|
|
; Operators
|
|
|
|
[
|
|
":="
|
|
"="
|
|
"+"
|
|
"-"
|
|
"*"
|
|
"/"
|
|
"%"
|
|
"%%"
|
|
">"
|
|
">="
|
|
"<"
|
|
"<="
|
|
"=="
|
|
"!="
|
|
"~="
|
|
"|"
|
|
"~"
|
|
"&"
|
|
"&~"
|
|
"<<"
|
|
">>"
|
|
"||"
|
|
"&&"
|
|
"!"
|
|
".."
|
|
"+="
|
|
"-="
|
|
"*="
|
|
"/="
|
|
"%="
|
|
"&="
|
|
"|="
|
|
"^="
|
|
"<<="
|
|
">>="
|
|
"||="
|
|
"&&="
|
|
"&~="
|
|
;"..="
|
|
;"..<"
|
|
;"?"
|
|
] @defcolor_operator
|
|
|
|
; Punctuation
|
|
|
|
[ "{" "}" ] @defcolor_text_default
|
|
|
|
[ "(" ")" ] @defcolor_text_default
|
|
|
|
[ "[" "]" ] @defcolor_text_default
|
|
|
|
[
|
|
"::"
|
|
"->"
|
|
"."
|
|
","
|
|
":"
|
|
";"
|
|
] @defcolor_text_default
|
|
|
|
; Comments
|
|
|
|
[
|
|
(comment)
|
|
(block_comment)
|
|
] @defcolor_comment
|
|
|
|
; Errors
|
|
|
|
(ERROR) @defcolor_comment_pop
|
|
)DONE");
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Language Management
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
function TSQuery*
|
|
tree_sitter_query_new(Application_Links* app, TSLanguage* language, String_Const_u8 query_string)
|
|
{
|
|
u32 error_offset;
|
|
TSQueryError query_error;
|
|
|
|
TSQuery* result = ts_query_new(
|
|
language, (const char *)query_string.str, (u32)query_string.size,
|
|
&error_offset, &query_error
|
|
);
|
|
|
|
if (!result)
|
|
{
|
|
print_message(app, SCu8("Error creating query\n"));
|
|
printf("%.*s\n", (int)Min(query_string.size-error_offset, 100), query_string.str + error_offset);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function void
|
|
tree_sitter_register_language(String_Const_u8 ext, TSLanguage* language, Tree_Sitter_Language_Queries queries)
|
|
{
|
|
Tree_Sitter_Language_Definition* lang = 0;
|
|
u64 hash = table_hash_u8(ext.str, ext.size);
|
|
u64 slot = hash % ArrayCount(tree_sitter_languages.languages);
|
|
for (Tree_Sitter_Language_Definition* l = tree_sitter_languages.languages[slot]; l != 0; l = l->next)
|
|
{
|
|
if (l->extension_hash == hash && string_match(l->extension, ext))
|
|
{
|
|
lang = l; break;
|
|
}
|
|
}
|
|
|
|
if (lang == 0)
|
|
{
|
|
lang = push_array(&tree_sitter_languages.arena, Tree_Sitter_Language_Definition, 1);
|
|
lang->next = tree_sitter_languages.languages[slot];
|
|
tree_sitter_languages.languages[slot] = lang;
|
|
|
|
lang->extension_hash = hash;
|
|
lang->extension = push_string_copy(&tree_sitter_languages.arena, ext);
|
|
lang->language = language;
|
|
lang->queries = queries;
|
|
}
|
|
}
|
|
|
|
function Tree_Sitter_Language_Definition*
|
|
tree_sitter_language_from_file_extension(String_Const_u8 ext)
|
|
{
|
|
Tree_Sitter_Language_Definition* result = 0;
|
|
u64 ext_hash = table_hash_u8(ext.str, ext.size);
|
|
u64 slot = ext_hash % ArrayCount(tree_sitter_languages.languages);
|
|
for (Tree_Sitter_Language_Definition* l = tree_sitter_languages.languages[slot]; l != 0; l = l->next)
|
|
{
|
|
if (l->extension_hash == ext_hash && string_match(l->extension, ext))
|
|
{
|
|
result = l;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function Tree_Sitter_Language_Definition*
|
|
tree_sitter_language_for_buffer(Application_Links* app, Buffer_ID buffer_id, Arena* arena)
|
|
{
|
|
Tree_Sitter_Language_Definition* result = 0;
|
|
String_Const_u8 file_name = push_buffer_file_name(app, arena, buffer_id);
|
|
String_Const_u8 extension = string_file_extension(file_name);
|
|
result = tree_sitter_language_from_file_extension(extension);
|
|
return result;
|
|
}
|
|
|
|
function Tree_Sitter_Language_Definition*
|
|
tree_sitter_language_for_buffer(Application_Links* app, Buffer_ID buffer_id)
|
|
{
|
|
Scratch_Block scratch(app);
|
|
return tree_sitter_language_for_buffer(app, buffer_id, scratch);
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
// Tree Sitter Hook Internals
|
|
/////////////////////////////////////////////
|
|
|
|
function void
|
|
tree_sitter_init(Application_Links* app)
|
|
{
|
|
Buffer_ID buffer = create_buffer(
|
|
app,
|
|
string_u8_litexpr("*tree*"),
|
|
BufferCreate_NeverAttachToFile | BufferCreate_AlwaysNew
|
|
);
|
|
buffer_set_setting(app, buffer, BufferSetting_Unimportant, true);
|
|
buffer_set_setting(app, buffer, BufferSetting_ReadOnly, true);
|
|
|
|
tree_sitter_languages.arena = make_arena_system(KB(16));
|
|
|
|
u32 error_offset;
|
|
TSQueryError query_error;
|
|
|
|
{ // Register CPP
|
|
TSLanguage* language = tree_sitter_cpp();
|
|
String_Const_u8 highlight_query_str = TS_CPP_HIGHLIGHT_QUERY;
|
|
|
|
Tree_Sitter_Language_Queries queries = {};
|
|
queries.ptr[Tree_Sitter_Language_Query_Highlights] = tree_sitter_query_new(app, language, TS_CPP_HIGHLIGHT_QUERY);
|
|
queries.ptr[Tree_Sitter_Language_Query_Functions] = tree_sitter_query_new(app, language, TS_CPP_FUNCTION_QUERY);
|
|
queries.ptr[Tree_Sitter_Language_Query_Types] = tree_sitter_query_new(app, language, TS_CPP_TYPE_QUERY);
|
|
tree_sitter_register_language(SCu8("c"), language, queries);
|
|
tree_sitter_register_language(SCu8("cpp"), language, queries);
|
|
tree_sitter_register_language(SCu8("h"), language, queries);
|
|
tree_sitter_register_language(SCu8("hpp"), language, queries);
|
|
tree_sitter_register_language(SCu8("cc"), language, queries);
|
|
}
|
|
|
|
{ // Register Jai
|
|
TSLanguage* language = tree_sitter_jai();
|
|
Tree_Sitter_Language_Queries queries = {};
|
|
queries.ptr[Tree_Sitter_Language_Query_Highlights] = tree_sitter_query_new(app, language, TS_JAI_HIGHLIGHT_QUERY);
|
|
queries.ptr[Tree_Sitter_Language_Query_Functions] = tree_sitter_query_new(app, language, TS_JAI_FUNCTION_QUERY);
|
|
queries.ptr[Tree_Sitter_Language_Query_Types] = tree_sitter_query_new(app, language, TS_JAI_TYPE_QUERY);
|
|
tree_sitter_register_language(SCu8("jai"), language, queries);
|
|
}
|
|
}
|
|
|
|
function void
|
|
tree_sitter_begin_buffer(Application_Links* app, Buffer_ID buffer_id)
|
|
{
|
|
Managed_Scope buffer_scope = buffer_get_managed_scope(app, buffer_id);
|
|
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, buffer_scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
tree_data->tree_mutex = system_mutex_make();
|
|
}
|
|
|
|
function void
|
|
tree_sitter_end_buffer(Application_Links* app, Buffer_ID buffer_id)
|
|
{
|
|
Managed_Scope buffer_scope = buffer_get_managed_scope(app, buffer_id);
|
|
|
|
Async_Task *tree_sitter_parse_task = scope_attachment(app, buffer_scope, buffer_tree_sitter_parse_task_id, Async_Task);
|
|
if (tree_sitter_parse_task && async_task_is_running_or_pending(&global_async_system, *tree_sitter_parse_task))
|
|
{
|
|
async_task_cancel(app, &global_async_system, *tree_sitter_parse_task);
|
|
}
|
|
|
|
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, buffer_scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
if (!tree_data || !tree_data->tree) return;
|
|
system_mutex_acquire(tree_data->tree_mutex);
|
|
ts_tree_delete(tree_data->tree);
|
|
system_mutex_release(tree_data->tree_mutex);
|
|
system_mutex_free(tree_data->tree_mutex);
|
|
}
|
|
|
|
function TSTree*
|
|
tree_sitter_buffer_get_tree_copy(Buffer_Tree_Sitter_Data* tree_data)
|
|
{
|
|
TSTree* result = 0;
|
|
// system_mutex_acquire(tree_data->tree_mutex);
|
|
if (tree_data->tree) result = ts_tree_copy(tree_data->tree);
|
|
// system_mutex_release(tree_data->tree_mutex);
|
|
return result;
|
|
}
|
|
|
|
function void
|
|
tree_sitter_parse_async__inner(Async_Context* actx, Buffer_ID buffer_id)
|
|
{
|
|
Application_Links *app = actx->app;
|
|
|
|
Arena arena = make_arena_system(KB(16));
|
|
|
|
TSParser *parser = ts_parser_new();
|
|
ts_parser_set_timeout_micros(parser, 5000);
|
|
|
|
acquire_global_frame_mutex(app);
|
|
Tree_Sitter_Language_Definition* lang = tree_sitter_language_for_buffer(app, buffer_id);
|
|
String_Const_u8 src = push_whole_buffer(app, &arena, buffer_id);
|
|
Managed_Scope scope = buffer_get_managed_scope(app, buffer_id);
|
|
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
TSTree *old_tree = tree_sitter_buffer_get_tree_copy(tree_data);
|
|
bool lang_set = ts_parser_set_language(parser, lang->language);
|
|
release_global_frame_mutex(app);
|
|
|
|
if (!lang_set)
|
|
{
|
|
AssertMessageAlways("Failed to set the language for the parser."
|
|
"This probably means a language wasn't set"
|
|
"in the BeginBuffer hook.\n");
|
|
}
|
|
|
|
// Iterate until we get a tree or we find that we should cancel the parse
|
|
TSTree *new_tree = 0;
|
|
b32 canceled = false;
|
|
for (;;)
|
|
{
|
|
new_tree = ts_parser_parse_string(parser, old_tree, (char *)src.str, (u32)src.size);
|
|
if (async_check_canceled(actx))
|
|
{
|
|
canceled = true;
|
|
break;
|
|
}
|
|
if (new_tree) break;
|
|
}
|
|
|
|
if (!canceled && new_tree)
|
|
{
|
|
TSTree* old_buffer_tree;
|
|
acquire_global_frame_mutex(app);
|
|
{
|
|
// NOTE(jack): Copy the old pointer to delete it outside the mutex.
|
|
system_mutex_acquire(tree_data->tree_mutex);
|
|
old_buffer_tree = tree_data->tree;
|
|
tree_data->tree = new_tree;
|
|
system_mutex_acquire(tree_data->tree_mutex);
|
|
|
|
print_message(app, SCu8("Finished Parse\n"));
|
|
|
|
// TODO(PS): Just put the code index update call here
|
|
// NOTE(jack): This feels kinda hacky, this is here to trigger
|
|
// the code index update tick. The buffer is also makred by the
|
|
// async lexer so we will update the index too frequently. We
|
|
// should probably change the lexer to not mark as modified.
|
|
// TODO(jack): Should we instead trigger another async task here to
|
|
// update the code index once this is done?
|
|
buffer_mark_as_modified(buffer_id);
|
|
|
|
// Force a frame refresh by requesting another frame
|
|
animate_in_n_milliseconds(app, 0);
|
|
}
|
|
release_global_frame_mutex(app);
|
|
ts_tree_delete(old_buffer_tree);
|
|
}
|
|
|
|
ts_parser_delete(parser);
|
|
ts_tree_delete(old_tree);
|
|
linalloc_clear(&arena);
|
|
}
|
|
|
|
function void
|
|
tree_sitter_parse_async(Async_Context* actx, String_Const_u8 data)
|
|
{
|
|
if (data.size != sizeof(Buffer_ID)) return;
|
|
Buffer_ID buffer_id = *(Buffer_ID*)data.str;
|
|
tree_sitter_parse_async__inner(actx, buffer_id);
|
|
}
|
|
|
|
function Range_i64
|
|
tree_sitter_node_to_range(TSNode node)
|
|
{
|
|
Range_i64 result;
|
|
result.start = ts_node_start_byte(node);
|
|
result.end = ts_node_end_byte(node);
|
|
return result;
|
|
}
|
|
|
|
function void
|
|
tree_sitter_code_index_update_tick(Application_Links* app)
|
|
{
|
|
Scratch_Block scratch(app);
|
|
|
|
#if 0
|
|
// TODO(PS): this should be done when we register the language
|
|
if (!tree_sitter_cpp_index_query)
|
|
{
|
|
u32 error_offset;
|
|
TSQueryError query_error;
|
|
tree_sitter_cpp_index_query = ts_query_new(
|
|
tree_sitter_cpp(), TS_CPP_INDEX_QUERY, (u32)TS_CPP_INDEX_QUERY.size, &error_offset, &query_error
|
|
);
|
|
if (!tree_sitter_cpp_index_query)
|
|
{
|
|
print_message(app, string_u8_litexpr("Failed to create cpp index query\n");
|
|
}
|
|
}
|
|
|
|
for (Buffer_Modified_Node* modified_node = global_buffer_modified_set.first;
|
|
modified_node != 0;
|
|
modified_node = modified_node->next
|
|
){
|
|
Temp_Memory_Block temp(scratch);
|
|
Buffer_ID buffer_id = modified_node->buffer;
|
|
|
|
Arena arena = make_arena_system(KB(16));
|
|
Code_Index_File* index = push_array_zero(&arena, Code_Index_File, 1);
|
|
index->buffer = buffer_id;
|
|
|
|
Tree_Sitter_Code_Index_Nest_List nests = {};
|
|
|
|
Managed_Scope buffer_scope = buffer_get_managed_scope(app, buffer_id);
|
|
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, buffer_scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
TSTree* tree = tree_sitter_buffer_get_tree_copy(tree_data);
|
|
if (tree)
|
|
{
|
|
TSQueryCursor* query_cursor = ts_query_cursor_new();
|
|
ts_query_cursor_exec(query_cursor, tree_sitter_cpp_index_query, ts_tree_root_node(tree));
|
|
|
|
TSQueryMatch query_match;
|
|
while (ts_query_cursor_next_match(query_cursor, &match))
|
|
{
|
|
TSQueryCapture type_capture = match.captures[0];
|
|
TSNode type_node = type_capture.node;
|
|
Range_i64 type_range = tree_sitter_node_to_range(type_node);
|
|
|
|
// TODO(PS): PICK UP HERE
|
|
}
|
|
}
|
|
ts_tree_delete(tree);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Async Management
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
function void
|
|
async_task_cancel_nowait(Application_Links* app, Async_System* async_system, Async_Task task)
|
|
{
|
|
system_mutex_acquire(async_system->mutex);
|
|
Async_Node *node = async_get_pending_node(async_system, task);
|
|
if (node != 0)
|
|
{
|
|
dll_remove(&node->node);
|
|
async_system->task_count -= 1;
|
|
async_free_node(async_system, node);
|
|
}
|
|
else
|
|
{
|
|
node = async_get_running_node(async_system, task);
|
|
if (node != 0)
|
|
{
|
|
b32 *cancel_signal = &node->thread->cancel_signal;
|
|
atomic_write_b32(cancel_signal, true);
|
|
// async_task_wait__inner(app, async_system, task);
|
|
}
|
|
}
|
|
system_mutex_release(async_system->mutex);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Token Highlighting
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
function void
|
|
tree_sitter_highlight_node(
|
|
Application_Links* app,
|
|
TSQuery* query,
|
|
TSNode top_node,
|
|
TSQueryCursor* query_cursor,
|
|
Text_Layout_ID text_layout_id
|
|
){
|
|
ts_query_cursor_exec(query_cursor, query, top_node);
|
|
|
|
TSQueryMatch query_match;
|
|
u32 capture_index;
|
|
while (ts_query_cursor_next_capture(query_cursor, &query_match, &capture_index))
|
|
{
|
|
TSQueryCapture capture = query_match.captures[capture_index];
|
|
TSNode node = capture.node;
|
|
|
|
u32 length;
|
|
const char* tmp = ts_query_capture_name_for_id(query, capture.index, &length);
|
|
String_Const_u8 capture_name = SCu8((char*)tmp, length);
|
|
|
|
Range_i64 highlight_range = tree_sitter_node_to_range(node);
|
|
Managed_ID color_id = managed_id_get(app, SCu8("colors"), capture_name);
|
|
if (color_id != 0)
|
|
{
|
|
paint_text_color_fcolor(app, text_layout_id, highlight_range, fcolor_id(color_id));
|
|
}
|
|
}
|
|
}
|
|
|
|
function void
|
|
draw_tree_sitter_node_colors(Application_Links* app, Text_Layout_ID text_layout_id, Buffer_ID buffer_id)
|
|
{
|
|
Tree_Sitter_Language_Definition* lang = tree_sitter_language_for_buffer(app, buffer_id);
|
|
TSQuery* query = lang->queries.ptr[Tree_Sitter_Language_Query_Highlights];
|
|
|
|
Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id);
|
|
Managed_Scope buffer_scope = buffer_get_managed_scope(app, buffer_id);
|
|
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, buffer_scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
TSTree* tree = tree_sitter_buffer_get_tree_copy(tree_data);
|
|
if (tree)
|
|
{
|
|
TSNode root = ts_tree_root_node(tree);
|
|
// Get the smallest node that fully contains the visible range
|
|
TSNode visible_container = ts_node_descendant_for_byte_range(root, (u32)visible_range.start, (u32)visible_range.end);
|
|
TSQueryCursor* query_cursor = ts_query_cursor_new();
|
|
if (ts_node_eq(root, visible_container))
|
|
{
|
|
TSTreeCursor tree_cursor = ts_tree_cursor_new(visible_container);
|
|
if (ts_tree_cursor_goto_first_child_for_byte(&tree_cursor, (u32)visible_range.start) != -1)
|
|
{
|
|
do {
|
|
TSNode node = ts_tree_cursor_current_node(&tree_cursor);
|
|
Range_i64 child_range = tree_sitter_node_to_range(node);
|
|
if (child_range.start > visible_range.end) break;
|
|
tree_sitter_highlight_node(app, query, node, query_cursor, text_layout_id);
|
|
} while(ts_tree_cursor_goto_next_sibling(&tree_cursor));
|
|
}
|
|
else
|
|
{
|
|
// Pathological case - just highligh the whole document. This is probably bad
|
|
tree_sitter_highlight_node(app, query, root, query_cursor, text_layout_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tree_sitter_highlight_node(app, query, visible_container, query_cursor, text_layout_id);
|
|
}
|
|
ts_query_cursor_delete(query_cursor);
|
|
}
|
|
ts_tree_delete(tree);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Queries
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
struct Tree_Sitter_Query_Cursor
|
|
{
|
|
Buffer_ID buffer_id;
|
|
TSQuery* query;
|
|
TSQueryCursor* query_cursor;
|
|
TSTree* tree;
|
|
TSNode first_node;
|
|
bool ok;
|
|
};
|
|
|
|
function Tree_Sitter_Query_Cursor
|
|
tree_sitter_query_init(Application_Links* app, Buffer_ID buffer_id, TSQuery* query)
|
|
{
|
|
Tree_Sitter_Query_Cursor result = {};
|
|
result.buffer_id = buffer_id;
|
|
result.query = query;
|
|
result.ok = false;
|
|
|
|
Managed_Scope buffer_scope = buffer_get_managed_scope(app, buffer_id);
|
|
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, buffer_scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
result.tree = tree_sitter_buffer_get_tree_copy(tree_data);
|
|
result.first_node = ts_tree_root_node(result.tree);
|
|
|
|
result.ok = (
|
|
result.query != 0 &&
|
|
result.tree != 0
|
|
);
|
|
return result;
|
|
}
|
|
|
|
function bool
|
|
tree_sitter_query_continue(Tree_Sitter_Query_Cursor* cursor, TSQueryMatch* match, u32* capture_index)
|
|
{
|
|
if (cursor->ok)
|
|
{
|
|
if (!cursor->query_cursor)
|
|
{
|
|
cursor->query_cursor = ts_query_cursor_new();
|
|
ts_query_cursor_exec(cursor->query_cursor, cursor->query, cursor->first_node);
|
|
}
|
|
|
|
cursor->ok = ts_query_cursor_next_capture(cursor->query_cursor, match, capture_index);
|
|
}
|
|
return cursor->ok;
|
|
}
|
|
|
|
function void
|
|
tree_sitter_query_end(Tree_Sitter_Query_Cursor* cursor)
|
|
{
|
|
if (cursor->query_cursor) ts_query_cursor_delete(cursor->query_cursor);
|
|
ts_tree_delete(cursor->tree);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Lists
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
function String_Const_u8
|
|
convert_to_single_line_in_place(String_Const_u8 str)
|
|
{
|
|
String_Const_u8 result = str;
|
|
int dst = 0;
|
|
for (int src = 0; src < str.size; src++)
|
|
{
|
|
if (str.str[src] == '\n')
|
|
{
|
|
while (src < str.size && character_is_whitespace(str.str[src])) { src += 1; }
|
|
if (src >= str.size) break;
|
|
}
|
|
result.str[dst] = str.str[src];
|
|
dst += 1;
|
|
}
|
|
result.size = dst;
|
|
return result;
|
|
}
|
|
|
|
function void
|
|
print_position(
|
|
Application_Links* app,
|
|
Buffer_Insertion* out,
|
|
Buffer_ID buffer,
|
|
String_Const_u8 buffer_name,
|
|
Range_i64 buffer_range,
|
|
String_Const_u8 prefix,
|
|
Arena* arena,
|
|
bool newlines_to_spaces
|
|
){
|
|
i64 line_number = get_line_number_from_pos(app, buffer, buffer_range.start);
|
|
insertf(out, "%.*s:%lld: ", string_expand(buffer_name), line_number);
|
|
|
|
if (prefix.size > 0) insertf(out, "%.*s ", string_expand(prefix));
|
|
|
|
Temp_Memory token_temp = begin_temp(arena);
|
|
String_Const_u8 line = push_buffer_range(app, arena, buffer, buffer_range);
|
|
if (newlines_to_spaces) line = convert_to_single_line_in_place(line);
|
|
insert_string(out, line);
|
|
end_temp(token_temp);
|
|
|
|
insertc(out, '\n');
|
|
}
|
|
|
|
function void
|
|
tree_sitter_list_all_query_results(
|
|
Application_Links *app,
|
|
Buffer_ID optional_target_buffer,
|
|
Tree_Sitter_Language_Query_Kind query_kind
|
|
){
|
|
String_Const_u8 decls_name = string_u8_litexpr("*decls*");
|
|
Buffer_ID decls_buffer = get_buffer_by_name(app, decls_name, Access_Always);
|
|
if (!buffer_exists(app, decls_buffer))
|
|
{
|
|
decls_buffer = create_buffer(app, decls_name, BufferCreate_AlwaysNew);
|
|
buffer_set_setting(app, decls_buffer, BufferSetting_Unimportant, true);
|
|
buffer_set_setting(app, decls_buffer, BufferSetting_ReadOnly, true);
|
|
}
|
|
else
|
|
{
|
|
clear_buffer(app, decls_buffer);
|
|
buffer_send_end_signal(app, decls_buffer);
|
|
}
|
|
|
|
Scratch_Block scratch(app);
|
|
|
|
Cursor insertion_cursor = make_cursor(push_array(scratch, u8, KB(256)), KB(256));
|
|
Buffer_Insertion out = begin_buffer_insertion_at_buffered(app, decls_buffer, 0, &insertion_cursor);
|
|
|
|
for (Buffer_ID buffer_it = get_buffer_next(app, 0, Access_Always);
|
|
buffer_it != 0;
|
|
buffer_it = get_buffer_next(app, buffer_it, Access_Always))
|
|
{
|
|
Buffer_ID buffer = buffer_it;
|
|
if (optional_target_buffer != 0) buffer = optional_target_buffer;
|
|
|
|
String_Const_u8 buffer_name = push_buffer_unique_name(app, scratch, buffer);
|
|
|
|
Token_Array array = get_token_array_from_buffer(app, buffer);
|
|
if (array.tokens != 0)
|
|
{
|
|
Tree_Sitter_Language_Definition* lang = tree_sitter_language_for_buffer(app, buffer);
|
|
if (!lang) continue;
|
|
TSQuery* ts_query = lang->queries.ptr[query_kind];
|
|
Tree_Sitter_Query_Cursor query = tree_sitter_query_init(app, buffer, ts_query);
|
|
|
|
i64 last_query_match_id = -1;
|
|
i64 last_query_match_printed = -1;
|
|
i64 last_query_line_number = 0;
|
|
Range_i64 last_query_range = {};
|
|
String_Const_u8 last_query_prefix = {};
|
|
|
|
TSQueryMatch query_match;
|
|
u32 capture_index;
|
|
bool reached_end = false;
|
|
while (tree_sitter_query_continue(&query, &query_match, &capture_index))
|
|
{
|
|
TSQueryCapture capture = query_match.captures[capture_index];
|
|
|
|
if (last_query_match_id != query_match.id)
|
|
{
|
|
if (last_query_match_id >= 0)
|
|
{
|
|
print_position(
|
|
app, &out, buffer, buffer_name, last_query_range, last_query_prefix, scratch, true
|
|
);
|
|
last_query_match_printed = last_query_match_id;
|
|
}
|
|
last_query_range.start = (i64)ts_node_start_byte(capture.node);
|
|
last_query_range.end = last_query_range.start;
|
|
last_query_prefix = {};
|
|
}
|
|
|
|
last_query_match_id = query_match.id;
|
|
last_query_range.end = Max((i64)ts_node_end_byte(capture.node), last_query_range.end);
|
|
|
|
String_Const_u8 name;
|
|
u32 name_length;
|
|
name.str = (u8*)ts_query_capture_name_for_id(ts_query, capture.index, &name_length);
|
|
name.size = (u64)name_length;
|
|
|
|
String_Const_u8 prefix_identifier = SCu8("prefix");
|
|
u64 prefix_loc = string_find_first(name, prefix_identifier);
|
|
if (prefix_loc < name.size)
|
|
{
|
|
last_query_prefix = name;
|
|
last_query_prefix.str += prefix_loc + prefix_identifier.size;
|
|
last_query_prefix.size -= prefix_loc + prefix_identifier.size;
|
|
}
|
|
}
|
|
|
|
if (last_query_match_printed != last_query_match_id)
|
|
{
|
|
print_position(
|
|
app, &out, buffer, buffer_name, last_query_range, last_query_prefix, scratch, true
|
|
);
|
|
}
|
|
tree_sitter_query_end(&query);
|
|
|
|
if (optional_target_buffer != 0) break;
|
|
}
|
|
}
|
|
|
|
end_buffer_insertion(&out);
|
|
|
|
View_ID view = get_active_view(app, Access_Always);
|
|
view_set_buffer(app, view, decls_buffer, 0);
|
|
|
|
lock_jump_buffer(app, decls_name);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(tree_sitter_list_all_functions_current_buffer)
|
|
CUSTOM_DOC("Creates a jump list of lines of the current buffer that appear to define or declare functions. Uses tree sitter")
|
|
{
|
|
View_ID view = get_active_view(app, Access_ReadVisible);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
|
|
if (buffer != 0) tree_sitter_list_all_query_results(app, buffer, Tree_Sitter_Language_Query_Functions);
|
|
}
|
|
|
|
CUSTOM_UI_COMMAND_SIG(tree_sitter_list_all_functions_current_buffer_lister)
|
|
CUSTOM_DOC("Creates a lister of locations that look like function definitions and declarations in the buffer. Uses tree sitter")
|
|
{
|
|
Heap *heap = &global_heap;
|
|
View_ID view = get_active_view(app, Access_ReadVisible);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
|
|
if (buffer != 0)
|
|
{
|
|
tree_sitter_list_all_query_results(app, buffer, Tree_Sitter_Language_Query_Functions);
|
|
view = get_active_view(app, Access_Always);
|
|
buffer = view_get_buffer(app, view, Access_Always);
|
|
Marker_List *list = get_or_make_list_for_buffer(app, heap, buffer);
|
|
if (list != 0)
|
|
{
|
|
Jump_Lister_Result jump = get_jump_index_from_user(app, list, "Function:");
|
|
jump_to_jump_lister_result(app, view, list, &jump);
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(tree_sitter_list_all_functions_all_buffers)
|
|
CUSTOM_DOC("Creates a jump list of lines from all buffers that appear to define or declare functions. Uses tree sitter")
|
|
{
|
|
tree_sitter_list_all_query_results(app, 0, Tree_Sitter_Language_Query_Functions);
|
|
}
|
|
|
|
CUSTOM_UI_COMMAND_SIG(tree_sitter_list_all_functions_all_buffers_lister)
|
|
CUSTOM_DOC("Creates a lister of locations that look like function definitions and declarations all buffers. Uses tree sitter")
|
|
{
|
|
Heap *heap = &global_heap;
|
|
tree_sitter_list_all_query_results(app, 0, Tree_Sitter_Language_Query_Functions);
|
|
View_ID view = get_active_view(app, Access_Always);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
|
|
Marker_List *list = get_or_make_list_for_buffer(app, heap, buffer);
|
|
if (list != 0)
|
|
{
|
|
Jump_Lister_Result jump = get_jump_index_from_user(app, list, "Function:");
|
|
jump_to_jump_lister_result(app, view, list, &jump);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(tree_sitter_list_all_types_current_buffer)
|
|
CUSTOM_DOC("Creates a jump list of lines of the current buffer that appear to define or declare types. Uses tree sitter")
|
|
{
|
|
View_ID view = get_active_view(app, Access_ReadVisible);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
|
|
if (buffer != 0) tree_sitter_list_all_query_results(app, buffer, Tree_Sitter_Language_Query_Types);
|
|
}
|
|
|
|
CUSTOM_UI_COMMAND_SIG(tree_sitter_list_all_types_current_buffer_lister)
|
|
CUSTOM_DOC("Creates a lister of locations that look like function definitions and declarations in the buffer. Uses tree sitter")
|
|
{
|
|
Heap *heap = &global_heap;
|
|
View_ID view = get_active_view(app, Access_ReadVisible);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
|
|
if (buffer != 0)
|
|
{
|
|
tree_sitter_list_all_query_results(app, buffer, Tree_Sitter_Language_Query_Types);
|
|
view = get_active_view(app, Access_Always);
|
|
buffer = view_get_buffer(app, view, Access_Always);
|
|
Marker_List *list = get_or_make_list_for_buffer(app, heap, buffer);
|
|
if (list != 0)
|
|
{
|
|
Jump_Lister_Result jump = get_jump_index_from_user(app, list, "Type:");
|
|
jump_to_jump_lister_result(app, view, list, &jump);
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(tree_sitter_list_all_types_all_buffers)
|
|
CUSTOM_DOC("Creates a jump list of lines from all buffers that appear to define or declare types. Uses tree sitter")
|
|
{
|
|
tree_sitter_list_all_query_results(app, 0, Tree_Sitter_Language_Query_Types);
|
|
}
|
|
|
|
CUSTOM_UI_COMMAND_SIG(tree_sitter_list_all_types_all_buffers_lister)
|
|
CUSTOM_DOC("Creates a lister of locations that look like type definitions and declarations all buffers. Uses tree sitter")
|
|
{
|
|
Heap *heap = &global_heap;
|
|
tree_sitter_list_all_query_results(app, 0, Tree_Sitter_Language_Query_Types);
|
|
View_ID view = get_active_view(app, Access_Always);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
|
|
Marker_List *list = get_or_make_list_for_buffer(app, heap, buffer);
|
|
if (list != 0)
|
|
{
|
|
Jump_Lister_Result jump = get_jump_index_from_user(app, list, "Type:");
|
|
jump_to_jump_lister_result(app, view, list, &jump);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// DEBUG
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
char* prefix_buffer = " ";
|
|
|
|
function void
|
|
write_tree_sitter_tree_to_buffer__inner(Application_Links *app, Arena *arena, Buffer_ID buffer_id,
|
|
TSNode cur_node, i32 level = 0, const char *field="")
|
|
{
|
|
TSPoint start = ts_node_start_point(cur_node);
|
|
TSPoint end = ts_node_end_point(cur_node);
|
|
// + 1 on ts positions becuase the first line/column are zero in treesitter,
|
|
// but 4coder displays as 1 indexed in the filebar.
|
|
String_Const_u8 string = push_stringf(arena, "%.*s%s: %s [%d, %d] - [%d, %d]\n",
|
|
level*2, prefix_buffer, field, ts_node_type(cur_node),
|
|
start.row + 1, start.column + 1,
|
|
end.row + 1, end.column + 1);
|
|
|
|
buffer_replace_range(app, buffer_id, Ii64(buffer_get_size(app, buffer_id)), string);
|
|
|
|
u32 child_count = ts_node_child_count(cur_node);
|
|
for (u32 i = 0; i < child_count; ++i)
|
|
{
|
|
TSNode child = ts_node_child(cur_node, i);
|
|
if (ts_node_is_named(child))
|
|
{
|
|
field = ts_node_field_name_for_child(cur_node, i);
|
|
if (!field) field = "";
|
|
write_tree_sitter_tree_to_buffer__inner(app, arena, buffer_id, child, level + 1, field);
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(tree_sitter_write_tree)
|
|
CUSTOM_DOC("Write the current buffer's tree sitter tree to *tree*")
|
|
{
|
|
Scratch_Block scratch(app);
|
|
Buffer_ID out_buffer = get_buffer_by_name(app, string_u8_litexpr("*tree*"), Access_Always);
|
|
|
|
View_ID view = get_active_view(app, Access_Always);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_Visible);
|
|
|
|
Managed_Scope scope = buffer_get_managed_scope(app, buffer);
|
|
Buffer_Tree_Sitter_Data *tree_data = scope_attachment(app, scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
|
|
|
|
if (tree_data->tree)
|
|
{
|
|
TSNode root = ts_tree_root_node(tree_data->tree);
|
|
write_tree_sitter_tree_to_buffer__inner(app, scratch, out_buffer, root);
|
|
}
|
|
}
|