Step 83: add new tree-sitter grammars

This commit is contained in:
Bill
2026-02-09 09:22:03 -07:00
parent c040f6f936
commit c9553590e7
6 changed files with 566 additions and 1 deletions

View File

@@ -42,6 +42,11 @@ enum class EditorModeType {
Python,
Cpp,
Elisp,
JavaScript,
TypeScript,
Java,
Rust,
Go,
PlainText
};
@@ -56,6 +61,11 @@ public:
if (language == "python") loadPython();
else if (language == "cpp") loadCpp();
else if (language == "elisp") loadElisp();
else if (language == "javascript") loadJavaScript();
else if (language == "typescript") loadTypeScript();
else if (language == "java") loadJava();
else if (language == "rust") loadRust();
else if (language == "go") loadGo();
else loadPlainText();
}
@@ -146,6 +156,11 @@ public:
case EditorModeType::Python: return "Python";
case EditorModeType::Cpp: return "C++";
case EditorModeType::Elisp: return "Elisp";
case EditorModeType::JavaScript:return "JavaScript";
case EditorModeType::TypeScript:return "TypeScript";
case EditorModeType::Java: return "Java";
case EditorModeType::Rust: return "Rust";
case EditorModeType::Go: return "Go";
case EditorModeType::PlainText: return "Plain Text";
}
return "Unknown";
@@ -155,6 +170,11 @@ public:
if (lang == "python") return EditorModeType::Python;
if (lang == "cpp") return EditorModeType::Cpp;
if (lang == "elisp") return EditorModeType::Elisp;
if (lang == "javascript") return EditorModeType::JavaScript;
if (lang == "typescript") return EditorModeType::TypeScript;
if (lang == "java") return EditorModeType::Java;
if (lang == "rust") return EditorModeType::Rust;
if (lang == "go") return EditorModeType::Go;
return EditorModeType::PlainText;
}
@@ -231,6 +251,62 @@ private:
snippets_ = {};
}
void loadJavaScript() {
type_ = EditorModeType::JavaScript;
indent_ = {"{", "}", 2, false};
comment_ = {"//", "/*", "*/"};
brackets_ = {{'(', ')'}, {'[', ']'}, {'{', '}'}, {'\'', '\''}, {'"', '"'}};
snippets_ = {
{"fn", "function $1($2) {\\n $0\\n}", "Function"},
{"if", "if ($1) {\\n $0\\n}", "If statement"},
{"for", "for (let $1 = 0; $1 < $2; $1++) {\\n $0\\n}", "For loop"},
};
}
void loadTypeScript() {
type_ = EditorModeType::TypeScript;
indent_ = {"{", "}", 2, false};
comment_ = {"//", "/*", "*/"};
brackets_ = {{'(', ')'}, {'[', ']'}, {'{', '}'}, {'\'', '\''}, {'"', '"'}, {'<', '>'}};
snippets_ = {
{"fn", "function $1($2): $3 {\\n $0\\n}", "Function"},
{"if", "if ($1) {\\n $0\\n}", "If statement"},
};
}
void loadJava() {
type_ = EditorModeType::Java;
indent_ = {"{", "}", 4, false};
comment_ = {"//", "/*", "*/"};
brackets_ = {{'(', ')'}, {'[', ']'}, {'{', '}'}, {'\'', '\''}, {'"', '"'}};
snippets_ = {
{"class", "class $1 {\\n $0\\n}", "Class"},
{"if", "if ($1) {\\n $0\\n}", "If statement"},
};
}
void loadRust() {
type_ = EditorModeType::Rust;
indent_ = {"{", "}", 4, false};
comment_ = {"//", "/*", "*/"};
brackets_ = {{'(', ')'}, {'[', ']'}, {'{', '}'}, {'\'', '\''}, {'\"', '\"'}};
snippets_ = {
{"fn", "fn $1($2) -> $3 {\\n $0\\n}", "Function"},
{"if", "if $1 {\\n $0\\n}", "If statement"},
};
}
void loadGo() {
type_ = EditorModeType::Go;
indent_ = {"{", "}", 4, false};
comment_ = {"//", "/*", "*/"};
brackets_ = {{'(', ')'}, {'[', ']'}, {'{', '}'}, {'\'', '\''}, {'\"', '\"'}};
snippets_ = {
{"fn", "func $1($2) $3 {\\n $0\\n}", "Function"},
{"if", "if $1 {\\n $0\\n}", "If statement"},
};
}
std::string language_;
EditorModeType type_;
IndentRule indent_;

View File

@@ -19,6 +19,11 @@ extern "C" {
const TSLanguage* tree_sitter_python();
const TSLanguage* tree_sitter_cpp();
const TSLanguage* tree_sitter_elisp();
const TSLanguage* tree_sitter_javascript();
const TSLanguage* tree_sitter_typescript();
const TSLanguage* tree_sitter_java();
const TSLanguage* tree_sitter_rust();
const TSLanguage* tree_sitter_go();
}
enum class TokenCategory {
@@ -55,6 +60,11 @@ public:
if (language == "python") lang = tree_sitter_python();
else if (language == "cpp") lang = tree_sitter_cpp();
else if (language == "elisp") lang = tree_sitter_elisp();
else if (language == "javascript") lang = tree_sitter_javascript();
else if (language == "typescript") lang = tree_sitter_typescript();
else if (language == "java") lang = tree_sitter_java();
else if (language == "rust") lang = tree_sitter_rust();
else if (language == "go") lang = tree_sitter_go();
if (!lang) {
ts_parser_delete(parser);
@@ -70,6 +80,11 @@ public:
if (language == "python") walkPython(root, source, spans);
else if (language == "cpp") walkCpp(root, source, spans);
else if (language == "elisp") walkElisp(root, source, spans);
else if (language == "javascript") walkJavaScript(root, source, spans);
else if (language == "typescript") walkTypeScript(root, source, spans);
else if (language == "java") walkJava(root, source, spans);
else if (language == "rust") walkRust(root, source, spans);
else if (language == "go") walkGo(root, source, spans);
ts_tree_delete(tree);
ts_parser_delete(parser);
@@ -265,6 +280,281 @@ private:
return false;
}
// --- JavaScript / TypeScript --------------------------------------
static bool isJsKeyword(const std::string& text) {
static const char* keywords[] = {
"function", "return", "if", "else", "for", "while", "class",
"const", "let", "var", "import", "export", "new", "try", "catch",
"finally", "switch", "case", "break", "continue", "throw",
"async", "await", "yield", nullptr
};
for (const char** k = keywords; *k; ++k) {
if (text == *k) return true;
}
return false;
}
static void walkJavaScript(TSNode node, const std::string& source,
std::vector<HighlightSpan>& spans) {
if (ts_node_is_null(node)) return;
std::string type = nodeType(node);
std::string text = nodeText(node, source);
bool descend = true;
if (type.find("comment") != std::string::npos) {
addSpan(spans, node, TokenCategory::Comment);
descend = false;
} else if (type == "string" || type == "string_fragment" || type == "template_string" ||
type == "template_substitution") {
addSpan(spans, node, TokenCategory::String);
descend = false;
} else if (type == "number" || type == "number_literal") {
addSpan(spans, node, TokenCategory::Number);
descend = false;
} else if (type == "identifier" || type == "property_identifier") {
TSNode parent = ts_node_parent(node);
std::string parentType = ts_node_is_null(parent) ? "" : nodeType(parent);
if (parentType == "function_declaration" || parentType == "method_definition") {
addSpan(spans, node, TokenCategory::Function);
} else {
addSpan(spans, node, TokenCategory::Identifier);
}
descend = false;
} else if (type == "true" || type == "false" || type == "null" || type == "undefined") {
addSpan(spans, node, TokenCategory::Builtin);
descend = false;
} else if (!ts_node_is_named(node)) {
if (isJsKeyword(text)) {
addSpan(spans, node, TokenCategory::Keyword);
} else if (text == "(" || text == ")" || text == "[" || text == "]" ||
text == "{" || text == "}" || text == ";" || text == "," ||
text == "." || text == ":" ) {
addSpan(spans, node, TokenCategory::Punctuation);
} else {
addSpan(spans, node, TokenCategory::Operator);
}
descend = false;
}
if (descend) {
uint32_t count = ts_node_child_count(node);
for (uint32_t i = 0; i < count; ++i) {
walkJavaScript(ts_node_child(node, i), source, spans);
}
}
}
static void walkTypeScript(TSNode node, const std::string& source,
std::vector<HighlightSpan>& spans) {
walkJavaScript(node, source, spans);
}
// --- Java -----------------------------------------------------------
static bool isJavaKeyword(const std::string& text) {
static const char* keywords[] = {
"class", "interface", "enum", "public", "private", "protected",
"static", "final", "void", "int", "float", "double", "boolean",
"return", "if", "else", "for", "while", "switch", "case",
"break", "continue", "new", "try", "catch", "finally", "throw",
"extends", "implements", "import", "package", "this", "super",
nullptr
};
for (const char** k = keywords; *k; ++k) {
if (text == *k) return true;
}
return false;
}
static void walkJava(TSNode node, const std::string& source,
std::vector<HighlightSpan>& spans) {
if (ts_node_is_null(node)) return;
std::string type = nodeType(node);
std::string text = nodeText(node, source);
bool descend = true;
if (type.find("comment") != std::string::npos) {
addSpan(spans, node, TokenCategory::Comment);
descend = false;
} else if (type.find("string") != std::string::npos || type == "character_literal") {
addSpan(spans, node, TokenCategory::String);
descend = false;
} else if (type.find("integer") != std::string::npos || type.find("floating") != std::string::npos ||
type == "decimal_integer_literal" || type == "decimal_floating_point_literal") {
addSpan(spans, node, TokenCategory::Number);
descend = false;
} else if (type == "identifier") {
TSNode parent = ts_node_parent(node);
std::string parentType = ts_node_is_null(parent) ? "" : nodeType(parent);
if (parentType == "method_declaration" || parentType == "constructor_declaration") {
addSpan(spans, node, TokenCategory::Function);
} else {
addSpan(spans, node, TokenCategory::Identifier);
}
descend = false;
} else if (type == "type_identifier" || type == "integral_type" || type == "floating_point_type") {
addSpan(spans, node, TokenCategory::Type);
descend = false;
} else if (type == "true" || type == "false" || type == "null_literal") {
addSpan(spans, node, TokenCategory::Builtin);
descend = false;
} else if (!ts_node_is_named(node)) {
if (isJavaKeyword(text)) {
addSpan(spans, node, TokenCategory::Keyword);
} else if (text == "(" || text == ")" || text == "[" || text == "]" ||
text == "{" || text == "}" || text == ";" || text == "," ||
text == "." || text == ":" ) {
addSpan(spans, node, TokenCategory::Punctuation);
} else {
addSpan(spans, node, TokenCategory::Operator);
}
descend = false;
}
if (descend) {
uint32_t count = ts_node_child_count(node);
for (uint32_t i = 0; i < count; ++i) {
walkJava(ts_node_child(node, i), source, spans);
}
}
}
// --- Rust -----------------------------------------------------------
static bool isRustKeyword(const std::string& text) {
static const char* keywords[] = {
"fn", "let", "mut", "pub", "impl", "trait", "struct", "enum",
"use", "mod", "crate", "self", "super", "return", "if", "else",
"for", "while", "loop", "match", "break", "continue", "const",
"static", "async", "await", "move", nullptr
};
for (const char** k = keywords; *k; ++k) {
if (text == *k) return true;
}
return false;
}
static void walkRust(TSNode node, const std::string& source,
std::vector<HighlightSpan>& spans) {
if (ts_node_is_null(node)) return;
std::string type = nodeType(node);
std::string text = nodeText(node, source);
bool descend = true;
if (type.find("comment") != std::string::npos) {
addSpan(spans, node, TokenCategory::Comment);
descend = false;
} else if (type == "string_literal" || type == "char_literal" ||
type == "raw_string_literal") {
addSpan(spans, node, TokenCategory::String);
descend = false;
} else if (type.find("integer") != std::string::npos || type.find("float") != std::string::npos ||
type == "number") {
addSpan(spans, node, TokenCategory::Number);
descend = false;
} else if (type == "identifier") {
TSNode parent = ts_node_parent(node);
std::string parentType = ts_node_is_null(parent) ? "" : nodeType(parent);
if (parentType == "function_item") {
addSpan(spans, node, TokenCategory::Function);
} else {
addSpan(spans, node, TokenCategory::Identifier);
}
descend = false;
} else if (type == "type_identifier" || type == "primitive_type") {
addSpan(spans, node, TokenCategory::Type);
descend = false;
} else if (!ts_node_is_named(node)) {
if (isRustKeyword(text)) {
addSpan(spans, node, TokenCategory::Keyword);
} else if (text == "(" || text == ")" || text == "[" || text == "]" ||
text == "{" || text == "}" || text == ";" || text == "," ||
text == ":" ) {
addSpan(spans, node, TokenCategory::Punctuation);
} else {
addSpan(spans, node, TokenCategory::Operator);
}
descend = false;
}
if (descend) {
uint32_t count = ts_node_child_count(node);
for (uint32_t i = 0; i < count; ++i) {
walkRust(ts_node_child(node, i), source, spans);
}
}
}
// --- Go -------------------------------------------------------------
static bool isGoKeyword(const std::string& text) {
static const char* keywords[] = {
"func", "package", "import", "return", "if", "else", "for",
"switch", "case", "break", "continue", "struct", "interface",
"type", "var", "const", "go", "defer", "range", nullptr
};
for (const char** k = keywords; *k; ++k) {
if (text == *k) return true;
}
return false;
}
static void walkGo(TSNode node, const std::string& source,
std::vector<HighlightSpan>& spans) {
if (ts_node_is_null(node)) return;
std::string type = nodeType(node);
std::string text = nodeText(node, source);
bool descend = true;
if (type.find("comment") != std::string::npos) {
addSpan(spans, node, TokenCategory::Comment);
descend = false;
} else if (type == "interpreted_string_literal" || type == "raw_string_literal" ||
type == "rune_literal") {
addSpan(spans, node, TokenCategory::String);
descend = false;
} else if (type.find("int") != std::string::npos || type.find("float") != std::string::npos ||
type == "number") {
addSpan(spans, node, TokenCategory::Number);
descend = false;
} else if (type == "identifier") {
TSNode parent = ts_node_parent(node);
std::string parentType = ts_node_is_null(parent) ? "" : nodeType(parent);
if (parentType == "function_declaration" || parentType == "method_declaration") {
addSpan(spans, node, TokenCategory::Function);
} else {
addSpan(spans, node, TokenCategory::Identifier);
}
descend = false;
} else if (type == "type_identifier" || type == "primitive_type") {
addSpan(spans, node, TokenCategory::Type);
descend = false;
} else if (!ts_node_is_named(node)) {
if (isGoKeyword(text)) {
addSpan(spans, node, TokenCategory::Keyword);
} else if (text == "(" || text == ")" || text == "[" || text == "]" ||
text == "{" || text == "}" || text == ";" || text == "," ||
text == ":" ) {
addSpan(spans, node, TokenCategory::Punctuation);
} else {
addSpan(spans, node, TokenCategory::Operator);
}
descend = false;
}
if (descend) {
uint32_t count = ts_node_child_count(node);
for (uint32_t i = 0; i < count; ++i) {
walkGo(ts_node_child(node, i), source, spans);
}
}
}
static void walkCpp(TSNode node, const std::string& source,
std::vector<HighlightSpan>& spans) {
if (ts_node_is_null(node)) return;

View File

@@ -188,6 +188,16 @@ struct EditorState {
language = "cpp";
else if (path.size() > 3 && path.substr(path.size() - 3) == ".el")
language = "elisp";
else if (path.size() > 3 && path.substr(path.size() - 3) == ".js")
language = "javascript";
else if (path.size() > 3 && path.substr(path.size() - 3) == ".ts")
language = "typescript";
else if (path.size() > 5 && path.substr(path.size() - 5) == ".java")
language = "java";
else if (path.size() > 3 && path.substr(path.size() - 3) == ".rs")
language = "rust";
else if (path.size() > 3 && path.substr(path.size() - 3) == ".go")
language = "go";
editor.setContent(editBuf, language);
sync.setText(editBuf, language);
sync.syncNow();
@@ -574,6 +584,16 @@ int main(int, char**) {
state.setLanguage("cpp");
if (ImGui::MenuItem("Elisp", nullptr, state.language == "elisp"))
state.setLanguage("elisp");
if (ImGui::MenuItem("JavaScript", nullptr, state.language == "javascript"))
state.setLanguage("javascript");
if (ImGui::MenuItem("TypeScript", nullptr, state.language == "typescript"))
state.setLanguage("typescript");
if (ImGui::MenuItem("Java", nullptr, state.language == "java"))
state.setLanguage("java");
if (ImGui::MenuItem("Rust", nullptr, state.language == "rust"))
state.setLanguage("rust");
if (ImGui::MenuItem("Go", nullptr, state.language == "go"))
state.setLanguage("go");
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Keybindings")) {