Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
#pragma once
|
|
|
|
|
// Step 52: Syntax highlighting via tree-sitter
|
|
|
|
|
//
|
|
|
|
|
// SyntaxHighlighter parses source text with tree-sitter and walks the CST
|
|
|
|
|
// to produce a list of colored spans. Each span has a byte range and a
|
|
|
|
|
// token category (keyword, string, comment, number, identifier, operator,
|
|
|
|
|
// type, punctuation, function, parameter).
|
|
|
|
|
//
|
2026-02-09 21:09:24 -07:00
|
|
|
// The highlight data is language-agnostic at the output level — consumers
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
// map categories to colors via a theme.
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
2026-02-08 16:21:22 -07:00
|
|
|
#include <algorithm>
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
#include <cstring>
|
Add Sprints 46-59: governance, porting foundation, and language graduation prep (Steps 689-828)
Sprints 46-58 implement the cross-language porting foundation: language-to-IR
adapters, equivalence checking, gate validation, legacy ingestion, managed/dynamic
families, low-level/logic-actor semantics, debug workflow tooling, AST-native
family tools, Rust/CPP raising tools, system-level orchestration, query family,
and porting gates. Sprint 59 adds the governance layer (policy packs, review
boards, waiver packets, ambiguity triage, decision ledger) with the
whetstone_review_porting_decision MCP tool.
Also includes: sprint plans 46-130, MCP taskitem pipeline scripts,
CLAUDE.md, docs, and full test matrix (steps 689-828).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 13:18:10 -07:00
|
|
|
#include <cctype>
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
#include <tree_sitter/api.h>
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
const TSLanguage* tree_sitter_python();
|
|
|
|
|
const TSLanguage* tree_sitter_cpp();
|
|
|
|
|
const TSLanguage* tree_sitter_elisp();
|
2026-02-09 09:22:03 -07:00
|
|
|
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();
|
2026-02-09 18:26:28 -07:00
|
|
|
#if !defined(WHETSTONE_ENABLE_ORG)
|
|
|
|
|
#define WHETSTONE_ENABLE_ORG 0
|
|
|
|
|
#endif
|
|
|
|
|
#if WHETSTONE_ENABLE_ORG
|
|
|
|
|
const TSLanguage* tree_sitter_org();
|
|
|
|
|
#endif
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum class TokenCategory {
|
|
|
|
|
Plain,
|
|
|
|
|
Keyword,
|
|
|
|
|
String,
|
|
|
|
|
Comment,
|
|
|
|
|
Number,
|
|
|
|
|
Identifier,
|
|
|
|
|
Operator,
|
|
|
|
|
Type,
|
|
|
|
|
Punctuation,
|
|
|
|
|
Function,
|
|
|
|
|
Parameter,
|
|
|
|
|
Builtin
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct HighlightSpan {
|
|
|
|
|
uint32_t start; // byte offset
|
|
|
|
|
uint32_t end; // byte offset (exclusive)
|
|
|
|
|
TokenCategory category;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class SyntaxHighlighter {
|
|
|
|
|
public:
|
|
|
|
|
static std::vector<HighlightSpan> highlight(const std::string& source,
|
|
|
|
|
const std::string& language) {
|
|
|
|
|
std::vector<HighlightSpan> spans;
|
|
|
|
|
if (source.empty()) return spans;
|
Add Sprints 46-59: governance, porting foundation, and language graduation prep (Steps 689-828)
Sprints 46-58 implement the cross-language porting foundation: language-to-IR
adapters, equivalence checking, gate validation, legacy ingestion, managed/dynamic
families, low-level/logic-actor semantics, debug workflow tooling, AST-native
family tools, Rust/CPP raising tools, system-level orchestration, query family,
and porting gates. Sprint 59 adds the governance layer (policy packs, review
boards, waiver packets, ambiguity triage, decision ledger) with the
whetstone_review_porting_decision MCP tool.
Also includes: sprint plans 46-130, MCP taskitem pipeline scripts,
CLAUDE.md, docs, and full test matrix (steps 689-828).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 13:18:10 -07:00
|
|
|
if (language == "markdown") {
|
|
|
|
|
walkMarkdownSimple(source, spans);
|
|
|
|
|
std::sort(spans.begin(), spans.end(),
|
|
|
|
|
[](const HighlightSpan& a, const HighlightSpan& b) {
|
|
|
|
|
return a.start < b.start;
|
|
|
|
|
});
|
|
|
|
|
return spans;
|
|
|
|
|
}
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
|
|
|
|
|
TSParser* parser = ts_parser_new();
|
|
|
|
|
const TSLanguage* lang = nullptr;
|
|
|
|
|
|
|
|
|
|
if (language == "python") lang = tree_sitter_python();
|
|
|
|
|
else if (language == "cpp") lang = tree_sitter_cpp();
|
|
|
|
|
else if (language == "elisp") lang = tree_sitter_elisp();
|
2026-02-09 09:22:03 -07:00
|
|
|
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();
|
2026-02-09 18:26:28 -07:00
|
|
|
#if WHETSTONE_ENABLE_ORG
|
|
|
|
|
else if (language == "org") lang = tree_sitter_org();
|
|
|
|
|
#endif
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
|
|
|
|
|
if (!lang) {
|
|
|
|
|
ts_parser_delete(parser);
|
|
|
|
|
return spans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ts_parser_set_language(parser, lang);
|
|
|
|
|
TSTree* tree = ts_parser_parse_string(parser, nullptr,
|
|
|
|
|
source.c_str(),
|
|
|
|
|
(uint32_t)source.size());
|
|
|
|
|
TSNode root = ts_tree_root_node(tree);
|
|
|
|
|
|
|
|
|
|
if (language == "python") walkPython(root, source, spans);
|
|
|
|
|
else if (language == "cpp") walkCpp(root, source, spans);
|
|
|
|
|
else if (language == "elisp") walkElisp(root, source, spans);
|
2026-02-09 09:22:03 -07:00
|
|
|
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);
|
2026-02-09 18:26:28 -07:00
|
|
|
#if WHETSTONE_ENABLE_ORG
|
|
|
|
|
else if (language == "org") walkOrgSimple(source, spans);
|
|
|
|
|
#endif
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
|
|
|
|
|
ts_tree_delete(tree);
|
|
|
|
|
ts_parser_delete(parser);
|
|
|
|
|
|
|
|
|
|
// Sort by start position
|
|
|
|
|
std::sort(spans.begin(), spans.end(),
|
|
|
|
|
[](const HighlightSpan& a, const HighlightSpan& b) {
|
|
|
|
|
return a.start < b.start;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return spans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char* categoryName(TokenCategory cat) {
|
|
|
|
|
switch (cat) {
|
|
|
|
|
case TokenCategory::Plain: return "plain";
|
|
|
|
|
case TokenCategory::Keyword: return "keyword";
|
|
|
|
|
case TokenCategory::String: return "string";
|
|
|
|
|
case TokenCategory::Comment: return "comment";
|
|
|
|
|
case TokenCategory::Number: return "number";
|
|
|
|
|
case TokenCategory::Identifier: return "identifier";
|
|
|
|
|
case TokenCategory::Operator: return "operator";
|
|
|
|
|
case TokenCategory::Type: return "type";
|
|
|
|
|
case TokenCategory::Punctuation: return "punctuation";
|
|
|
|
|
case TokenCategory::Function: return "function";
|
|
|
|
|
case TokenCategory::Parameter: return "parameter";
|
|
|
|
|
case TokenCategory::Builtin: return "builtin";
|
|
|
|
|
}
|
|
|
|
|
return "plain";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
static std::string nodeType(TSNode node) {
|
|
|
|
|
return ts_node_type(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::string nodeText(TSNode node, const std::string& source) {
|
|
|
|
|
uint32_t s = ts_node_start_byte(node);
|
|
|
|
|
uint32_t e = ts_node_end_byte(node);
|
|
|
|
|
if (s >= source.size() || e > source.size()) return "";
|
|
|
|
|
return source.substr(s, e - s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void addSpan(std::vector<HighlightSpan>& spans, TSNode node,
|
|
|
|
|
TokenCategory cat) {
|
|
|
|
|
uint32_t s = ts_node_start_byte(node);
|
|
|
|
|
uint32_t e = ts_node_end_byte(node);
|
|
|
|
|
if (s < e) {
|
|
|
|
|
spans.push_back({s, e, cat});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add Sprints 46-59: governance, porting foundation, and language graduation prep (Steps 689-828)
Sprints 46-58 implement the cross-language porting foundation: language-to-IR
adapters, equivalence checking, gate validation, legacy ingestion, managed/dynamic
families, low-level/logic-actor semantics, debug workflow tooling, AST-native
family tools, Rust/CPP raising tools, system-level orchestration, query family,
and porting gates. Sprint 59 adds the governance layer (policy packs, review
boards, waiver packets, ambiguity triage, decision ledger) with the
whetstone_review_porting_decision MCP tool.
Also includes: sprint plans 46-130, MCP taskitem pipeline scripts,
CLAUDE.md, docs, and full test matrix (steps 689-828).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 13:18:10 -07:00
|
|
|
static void walkMarkdownSimple(const std::string& source,
|
|
|
|
|
std::vector<HighlightSpan>& spans) {
|
|
|
|
|
size_t lineStart = 0;
|
|
|
|
|
bool inFence = false;
|
|
|
|
|
while (lineStart <= source.size()) {
|
|
|
|
|
size_t lineEnd = source.find('\n', lineStart);
|
|
|
|
|
if (lineEnd == std::string::npos) lineEnd = source.size();
|
|
|
|
|
std::string line = source.substr(lineStart, lineEnd - lineStart);
|
|
|
|
|
std::string trimmed = line;
|
|
|
|
|
while (!trimmed.empty() && std::isspace((unsigned char)trimmed.front())) {
|
|
|
|
|
trimmed.erase(trimmed.begin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trimmed.rfind("```", 0) == 0 || trimmed.rfind("~~~", 0) == 0) {
|
|
|
|
|
spans.push_back({(uint32_t)lineStart, (uint32_t)lineEnd, TokenCategory::Keyword});
|
|
|
|
|
inFence = !inFence;
|
|
|
|
|
} else if (inFence) {
|
|
|
|
|
spans.push_back({(uint32_t)lineStart, (uint32_t)lineEnd, TokenCategory::String});
|
|
|
|
|
} else if (!trimmed.empty() && trimmed[0] == '#') {
|
|
|
|
|
spans.push_back({(uint32_t)lineStart, (uint32_t)lineEnd, TokenCategory::Keyword});
|
|
|
|
|
} else {
|
|
|
|
|
size_t tickStart = line.find('`');
|
|
|
|
|
while (tickStart != std::string::npos) {
|
|
|
|
|
size_t tickEnd = line.find('`', tickStart + 1);
|
|
|
|
|
if (tickEnd == std::string::npos) break;
|
|
|
|
|
spans.push_back({
|
|
|
|
|
(uint32_t)(lineStart + tickStart),
|
|
|
|
|
(uint32_t)(lineStart + tickEnd + 1),
|
|
|
|
|
TokenCategory::Builtin
|
|
|
|
|
});
|
|
|
|
|
tickStart = line.find('`', tickEnd + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (lineEnd == source.size()) break;
|
|
|
|
|
lineStart = lineEnd + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 21:09:24 -07:00
|
|
|
#include "SyntaxLanguages.h"
|
|
|
|
|
#include "SyntaxHighlighterPython.h"
|
|
|
|
|
#include "SyntaxHighlighterCpp.h"
|
|
|
|
|
#include "SyntaxHighlighterJavaScript.h"
|
|
|
|
|
#include "SyntaxHighlighterJava.h"
|
|
|
|
|
#include "SyntaxHighlighterRust.h"
|
|
|
|
|
#include "SyntaxHighlighterGo.h"
|
|
|
|
|
#include "SyntaxHighlighterElisp.h"
|
|
|
|
|
#include "SyntaxHighlighterOrg.h"
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
// --- Python --------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- C++ -----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-09 09:22:03 -07:00
|
|
|
// --- JavaScript / TypeScript --------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- Java -----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- Rust -----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- Go -------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- Elisp ---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
2026-02-09 18:26:28 -07:00
|
|
|
|
|
|
|
|
// --- Org -----------------------------------------------------------
|
|
|
|
|
|
Phase 3c complete: syntax highlighting and configurable keybindings (Steps 52, 54)
Step 52: SyntaxHighlighter walks tree-sitter CST to produce colored
token spans (keyword, string, comment, number, function, parameter,
type, operator, etc.) for Python, C++, and Elisp.
Step 54: KeybindingManager with swappable profiles — VSCode (default),
JetBrains, and Emacs. Each profile maps 25+ actions (file, edit,
search, nav, view, code, build) to key combos. Supports custom
overrides and reverse lookup.
Design direction: editor targets VSCode/JetBrains look-and-feel.
Keybinding profile selectable at startup. VSCode is default.
Phase 3c totals: 37/37 tests pass across 5 steps (50-54).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:12:10 -07:00
|
|
|
};
|