瀏覽代碼

Builtin-type colors for Modula-2 editors

Predeclared type names (INTEGER, CARDINAL, ... via the shared
TYPE_BUILTINS set, now exported from the checker) emit semantic type
tokens when they resolve to no declaration. User shadowing still wins:
a user-declared INTEGER resolves first and keeps its own token kind.
GUI-verified teal rendering; diagnostics behavior unchanged.
Eric Streit 6 天之前
父節點
當前提交
664296ea79
共有 3 個文件被更改,包括 34 次插入 和 25 次删除
  1. 8 14
      PLAN.md
  2. 2 1
      extensions/modula2-language/src/m2/checker.ts
  3. 24 10
      extensions/modula2-language/src/m2/semantic.ts

+ 8 - 14
PLAN.md

@@ -1,29 +1,23 @@
-# Plan — next steps (from v0.24.0)
+# Plan — next steps (from v0.25.0)
 
-Status: IDE complete through v0.24.0, distributables fresh at v0.24.0
+Status: IDE complete through v0.25.0, distributables fresh at v0.24.0
 (AppImage + deb in gitignored `apps/electron/dist/`). Tree clean except
 this file; all tags local.
 
 ## Done since the last plan
 
-- v0.22.0 definition search paths (project folders + gm2 libraries).
-- v0.23.0 WITH-block internals, v0.24.0 format selection.
-- Fresh 0.24.0 AppImage + deb, GUI-verified from the packaged build;
-  `desktopName` set so the window associates with its `.desktop` entry.
+- v0.25.0 builtin-type colors (semantic `type` tokens for predeclared
+  identifiers; user shadowing still wins, GUI-verified teal).
 
 ## Next, in priority order
 
-1. **Builtin-type colors** (recommended) — semantic `type` tokens for
-   predeclared identifiers (`INTEGER`, …), currently TextMate-blue.
-2. **WITH hover support** — hover and go-to-definition for bare field
-   names inside `WITH` bodies (checker side done in v0.23.0).
+1. **WITH hover support** (recommended) — hover and go-to-definition
+   for bare field names inside `WITH` bodies (checker side done in
+   v0.23.0).
 
 ## Also pending (undecided)
 
-- Push commits + tags to `origin` (all local so far, through v0.24.0).
+- Push commits + tags to `origin` (all local so far, through v0.25.0).
 - Clean superseded artifacts from `apps/electron/dist/`.
 - Coco/R grammar differential testing against `gm2`.
 - Right-click context menu visual check (CDP cannot open context menus).
-- Builtin-type semantic color: `INTEGER`/`CARDINAL`/etc. currently fall
-  back to TextMate blue (resolver emits no token); consider emitting
-  `type` tokens for predeclared identifiers.

+ 2 - 1
extensions/modula2-language/src/m2/checker.ts

@@ -23,7 +23,8 @@ import {
 } from './types';
 import * as path from 'path';
 
-const TYPE_BUILTINS = new Set([
+/** Predeclared type names (shared with semantic tokens). */
+export const TYPE_BUILTINS = new Set([
   'INTEGER', 'CARDINAL', 'LONGINT', 'SHORTINT', 'LONGCARD', 'SHORTCARD',
   'REAL', 'LONGREAL', 'CHAR', 'BOOLEAN', 'BITSET', 'ADDRESS', 'WORD', 'BYTE',
   'OCTET', 'LOC',

+ 24 - 10
extensions/modula2-language/src/m2/semantic.ts

@@ -9,6 +9,7 @@
 
 import { M2Symbol, M2Unit, parseUnitText } from './parser';
 import { flattenUnit, identifierOccurrences, resolveName } from './resolve';
+import { TYPE_BUILTINS } from './checker';
 
 export const SEMANTIC_TOKEN_TYPES = [
   'namespace', // modules
@@ -57,16 +58,29 @@ export function computeSemanticTokens(text: string, filePath: string, docDir: st
   for (const occ of identifierOccurrences(text)) {
     if (declared.has(`${occ.line}:${occ.ch}`)) continue;
     const resolved = resolveName(unit, docDir, occ.line, occ.ch, occ.qualifier, occ.name);
-    if (!resolved) continue;
-    const kind = useTokenKind(resolved.sym);
-    if (kind === null) continue;
-    out.push({
-      line: occ.line,
-      ch: occ.ch,
-      length: Math.max(1, occ.endCh - occ.ch),
-      type: kind,
-      modifiers: 0,
-    });
+    if (resolved) {
+      const kind = useTokenKind(resolved.sym);
+      if (kind === null) continue;
+      out.push({
+        line: occ.line,
+        ch: occ.ch,
+        length: Math.max(1, occ.endCh - occ.ch),
+        type: kind,
+        modifiers: 0,
+      });
+      continue;
+    }
+    // Predeclared type names (`INTEGER`, …) resolve to no declaration.
+    // User shadowing still wins: anything declared resolves above.
+    if (TYPE_BUILTINS.has(occ.name)) {
+      out.push({
+        line: occ.line,
+        ch: occ.ch,
+        length: Math.max(1, occ.endCh - occ.ch),
+        type: typeIndex('type'),
+        modifiers: 0,
+      });
+    }
   }
   out.sort((a, b) => a.line - b.line || a.ch - b.ch);
   const data: number[] = [];