diff options
Diffstat (limited to 'tree-sitter/dsk/dsk-cli/src/commands/highlight.ts')
-rw-r--r-- | tree-sitter/dsk/dsk-cli/src/commands/highlight.ts | 141 |
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'; +} + + |