|
|
@@ -11,7 +11,7 @@ import { lex, Token } from './lexer';
|
|
|
import { M2Import, M2Symbol, M2Unit, parseUnitText, rangeContains } from './parser';
|
|
|
import { gm2LibraryDirs, projectSearchDirs } from './defpaths';
|
|
|
import {
|
|
|
- findFieldInType, findRecordField, resolveDeclaredType, resolveType, unitOfFile,
|
|
|
+ findFieldInType, findRecordField, findTypeByDecl, resolveDeclaredType, resolveType, unitOfFile,
|
|
|
} from './types';
|
|
|
import type { M2Type } from './types';
|
|
|
|
|
|
@@ -501,6 +501,51 @@ function skipBalanced(tokens: Token[], from: number, open: string, close: string
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+/** Field declarations visible bare through enclosing WITH guards.
|
|
|
+ *
|
|
|
+ * Innermost guard first, shadowed names dropped — mirroring what the
|
|
|
+ * checker accepts and what hover resolves, for completion lists.
|
|
|
+ * Unknowable guards contribute nothing. */
|
|
|
+export function withFieldSymbols(
|
|
|
+ unit: M2Unit, docDir: string, tokens: Token[], line: number, ch: number,
|
|
|
+): M2Symbol[] {
|
|
|
+ const blocks = withBlocksOf(tokens)
|
|
|
+ .filter(b => posInBody(b, tokens, line, ch))
|
|
|
+ .sort((a, b) => (a.endIdx - a.bodyFromIdx) - (b.endIdx - b.bodyFromIdx));
|
|
|
+ const seen = new Set<string>();
|
|
|
+ const out: M2Symbol[] = [];
|
|
|
+ for (const b of blocks) {
|
|
|
+ const gt = guardRecord(unit, docDir, tokens, b, 0);
|
|
|
+ if (!gt) continue;
|
|
|
+ for (const f of recordFieldDecls(unit, docDir, gt, line, ch)) {
|
|
|
+ if (f.name && !seen.has(f.name)) { seen.add(f.name); out.push(f); }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+/** All field declarations of a record type: declaration identity first
|
|
|
+ * (reaches types invisible under their own name), name lookup after. */
|
|
|
+function recordFieldDecls(
|
|
|
+ unit: M2Unit, docDir: string, t: M2Type, line: number, ch: number,
|
|
|
+): M2Symbol[] {
|
|
|
+ const b = t.kind === 'subrange' ? t.base : t;
|
|
|
+ if (b.kind !== 'record' || !b.name) return [];
|
|
|
+ const fields = (s: M2Symbol): M2Symbol[] => s.children.filter(c => c.kind === 'field');
|
|
|
+ if (b.decl) {
|
|
|
+ const r = findTypeByDecl(unit, b.decl);
|
|
|
+ if (r && r.sym.kind === 'type') return fields(r.sym);
|
|
|
+ }
|
|
|
+ const dot = b.name.lastIndexOf('.');
|
|
|
+ const r = resolveName(
|
|
|
+ unit, docDir, line, ch,
|
|
|
+ dot >= 0 ? b.name.slice(0, dot) : null,
|
|
|
+ dot >= 0 ? b.name.slice(dot + 1) : b.name,
|
|
|
+ );
|
|
|
+ if (r && r.sym.kind === 'type') return fields(r.sym);
|
|
|
+ return [];
|
|
|
+}
|
|
|
+
|
|
|
/** Exported symbols of a module (for `Module.` completion). */
|
|
|
export function moduleExports(docDir: string, moduleName: string): M2Symbol[] {
|
|
|
const def = loadDefModule(docDir, moduleName);
|