about summary refs log tree commit diff stats
path: root/tree-sitter/dsk/dsk-cli/src/commands/highlight.ts
diff options
context:
space:
mode:
Diffstat (limited to 'tree-sitter/dsk/dsk-cli/src/commands/highlight.ts')
-rw-r--r--tree-sitter/dsk/dsk-cli/src/commands/highlight.ts141
1 files changed, 141 insertions, 0 deletions
diff --git a/tree-sitter/dsk/dsk-cli/src/commands/highlight.ts b/tree-sitter/dsk/dsk-cli/src/commands/highlight.ts
new file mode 100644
index 0000000..fd0d419
--- /dev/null
+++ b/tree-sitter/dsk/dsk-cli/src/commands/highlight.ts
@@ -0,0 +1,141 @@
+/**
+ * Highlight Command - Generate Tree-sitter highlights and editor scaffolds
+ */
+
+import { Command } from 'commander';
+import chalk from 'chalk';
+import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
+import { join } from 'path';
+
+/**
+ * Create the highlight command
+ */
+export function createHighlightCommand(): Command {
+  const highlightCommand = new Command('highlight');
+
+  highlightCommand
+    .description('Generate Tree-sitter highlights and editor scaffolds (tree-sitter, neovim, emacs, vscode)')
+    .action(async () => {
+      if (!existsSync('grammar.js')) {
+        console.error(chalk.red('❌ No grammar.js found. Are you in a DSL project directory?'));
+        process.exit(1);
+      }
+
+      const languageName = getProjectName();
+
+      // Tree-sitter highlights
+      const tsOutDir = 'generated/editors/tree-sitter';
+      mkdirSync(tsOutDir, { recursive: true });
+      const highlights = `
+; Minimal highlights scaffold. Extend as needed.
+
+; Comments
+(comment) @comment
+
+; Strings
+(string) @string
+
+; Numbers
+(number) @number
+
+; Keywords (example)
+[("if") ("else") ("let") ("function")] @keyword
+`;
+      const tsFile = join(tsOutDir, 'highlights.scm');
+      writeFileSync(tsFile, highlights.trim() + '\n', 'utf-8');
+      console.log(chalk.green(`✅ Generated ${tsFile}`));
+
+      // Neovim instructions
+      const nvimDir = 'generated/editors/neovim';
+      mkdirSync(nvimDir, { recursive: true });
+      const nvimMd = `# Neovim setup for ${languageName}
+
+1. Copy queries to your runtimepath: queries/${languageName}/highlights.scm
+2. Configure nvim-treesitter:
+
+\`\`\`lua
+require('nvim-treesitter.configs').setup({
+  ensure_installed = {},
+  highlight = { enable = true },
+})
+\`\`\`
+`;
+      writeFileSync(join(nvimDir, 'setup-instructions.md'), nvimMd, 'utf-8');
+      console.log(chalk.green(`✅ Generated ${join(nvimDir, 'setup-instructions.md')}`));
+
+      // Emacs major mode (minimal)
+      const emacsDir = 'generated/editors/emacs';
+      mkdirSync(emacsDir, { recursive: true });
+      const emacsEl = `;;; ${languageName}-mode.el --- ${languageName} mode -*- lexical-binding: t; -*-
+
+(require 'treesit)
+
+(define-derived-mode ${languageName}-mode prog-mode "${languageName}"
+  "Major mode for ${languageName} using Tree-sitter."
+  (when (treesit-ready-p '${languageName})
+    (treesit-parser-create '${languageName})))
+
+(add-to-list 'auto-mode-alist '("\\.${languageName}$" . ${languageName}-mode))
+
+(provide '${languageName}-mode)
+`;
+      writeFileSync(join(emacsDir, `${languageName}-mode.el`), emacsEl, 'utf-8');
+      console.log(chalk.green(`✅ Generated ${join(emacsDir, `${languageName}-mode.el`)}`));
+
+      // VS Code: TextMate grammar + language configuration (basic placeholders)
+      const vscodeSyntaxDir = 'generated/editors/vscode/syntaxes';
+      const vscodeDir = 'generated/editors/vscode';
+      mkdirSync(vscodeSyntaxDir, { recursive: true });
+      mkdirSync(vscodeDir, { recursive: true });
+      const scope = `source.${languageName}`;
+      const tmLanguage = {
+        name: languageName,
+        scopeName: scope,
+        patterns: [
+          { include: '#comment' },
+          { include: '#string' },
+          { include: '#number' },
+          { include: '#keyword' }
+        ],
+        repository: {
+          comment: { patterns: [{ name: 'comment.line.double-slash', match: '//.*$' }] },
+          string: { patterns: [{ name: 'string.quoted.double', begin: '"', end: '"' }] },
+          number: { patterns: [{ name: 'constant.numeric', match: '-?\\b[0-9]+(\\.[0-9]+)?\\b' }] },
+          keyword: { patterns: [{ name: 'keyword.control', match: '\\b(if|else|let|function)\\b' }] }
+        }
+      } as const;
+      writeFileSync(join(vscodeSyntaxDir, `${languageName}.tmLanguage.json`), JSON.stringify(tmLanguage, null, 2));
+      const langConfig = {
+        comments: { lineComment: '//' },
+        brackets: [["{","}"],["[","]"],["(",")"]],
+        autoClosingPairs: [
+          { open: '"', close: '"' },
+          { open: '{', close: '}' },
+          { open: '(', close: ')' },
+          { open: '[', close: ']' }
+        ],
+        surroundingPairs: [
+          { open: '"', close: '"' },
+          { open: '{', close: '}' },
+          { open: '(', close: ')' },
+          { open: '[', close: ']' }
+        ]
+      };
+      writeFileSync(join(vscodeDir, 'language-configuration.json'), JSON.stringify(langConfig, null, 2));
+      console.log(chalk.green(`✅ Generated VS Code syntax and configuration`));
+    });
+
+  return highlightCommand;
+}
+
+function getProjectName(): string {
+  try {
+    const grammarContent = readFileSync('grammar.js', 'utf-8');
+    const nameMatch = grammarContent.match(/name:\s*['"]([^'"]+)['"]/);
+    if (nameMatch) return nameMatch[1];
+  } catch {}
+  const parts = process.cwd().split(/[\\/]/);
+  return parts[parts.length - 1] || 'dsl';
+}
+
+