diff options
| author | 2026-10-14 08:29:46 +0330 | |
|---|---|---|
| committer | 2026-10-14 08:29:46 +0330 | |
| commit | 1c53304d03414890af0da7432d1aea87e754a14a (patch) | |
| tree | 97190d86cf548c9bc63f68c86a4d2a4f09809713 /editors/vscode/client/extension.js | |
| parent | a (diff) | |
feat: grow memory limits, add icon/actor libraries, overhaul LSP diagnostics
Compiler and VM
- make every memory map limit dynamic and adjustable at both build time and
runtime, including compiler tables, stacks, and every execution region
- reserve and grow tables, operand and return stacks, frame segments, and
image-building output without relocating live code or globals
- rename i/o and string helpers consistently (puts -> put_s, putn -> put_n,
scat -> cat_s) across compiler, runtime, examples, tests, and docs
- recast and consume values assigned to globals from inside functions
- allow fixed and const together, add typed closure returns (fn:t(...)),
multi-include syntax (include rt, run, core;), and recurse type inference
- never infer string literals as word, anywhere, as argument, variable, or
return type
- draft docs/BORROW.md: a borrow checker less intrusive than Rust's but more
intelligent
LSP and editor integration
- require explicit includes and give every toolchain area its own namespace
(rt, lsp, run, core)
- hover for built-in types, inferred types, values known at hover time, array
and pointer lifetime/size, library names in include statements, and inferred
lifetimes with explanations
- honor /* */ comments as documentation for variables and arguments, and stop
misattributing section headers or trailing comments as variable hints
- stream diagnostics and cancel stale requests on change
- jump to definition and references, including cross-file
- quick fixes with automatic library import, and namespace completion
Hints and diagnostics
- render hints as blue squigglies by default, with hints on/small/off per file
- suppress magic-number hints via a preceding /* */ comment, and say so
- warn on unused arguments; allow multiple _ discards in parameters, pattern
matching, and tagged enums, warning if a _ is actually used
- rewrite hint and comment prose in professional compiler-doc register
Libraries
- add icon.zc: run-time self-modification, find/replace syntax, type and block
edits, and step-through inspection of locals and globals
- add actor.zc: async actors with Actor<I,O>, implicit this, spawn/recv/send,
injected into the compiler machinery at include time rather than built in
Tooling and docs
- reorganize sources, tests, and designs into subdirectories
- unify windows entry points in tinyvm.bat and add a matching tinyvm.sh
- add --help across the toolchain
- polish the learning guide, stdlib docs, and design notes
Tests
- add accept, reject, and regression coverage across the compiler, LSP,
- runtime, icon, actors, threads, formatting, and the toolchain contracts,
plus fuzzers and property tests
Diffstat (limited to 'editors/vscode/client/extension.js')
| -rw-r--r-- | editors/vscode/client/extension.js | 108 |
1 files changed, 107 insertions, 1 deletions
diff --git a/editors/vscode/client/extension.js b/editors/vscode/client/extension.js index c51b12e..3846eca 100644 --- a/editors/vscode/client/extension.js +++ b/editors/vscode/client/extension.js @@ -13,10 +13,12 @@ const vscode = require('vscode'); const cp = require('child_process'); const path = require('path'); const fs = require('fs'); +const { convertCodeActions } = require('./codeactions'); const { vmProcess } = require('./vmproc'); const { wasmProcess } = require('./wasmproc'); const { DiagCache, diagKey, fileStamp, sha256 } = require('./diagcache'); const { hoverContents } = require('./hover'); +const { createSourceProvider } = require('./sourceprovider'); const CHANNEL = 'ZC Language Server'; const SLOW_MS = 2000; // a request that takes longer than this is logged as slow @@ -40,6 +42,8 @@ let store = null; // the extension's globalState, where the cache is saved let cache = new DiagCache(null); let serverStamp = 'none'; // identifies the server build; it is part of every key const docKeys = new Map(); // uri -> key of the document as it is now (null: not cacheable) +const docFiles = new Map(); // uri -> the texts of the files the document includes, sent to the server before it checks the document +const sentFiles = new Map(); // path -> the text the server was last sent for it const docDeps = new Map(); // uri -> the files the document reads, for checking it again when one changes const serverKeys = new Map(); // uri -> key of the text the server holds const sentKeys = new Map(); // uri -> keys sent to the server whose diagnostics have not come back @@ -190,6 +194,7 @@ function keyOf(doc) { const r = diagKey(serverStamp, doc.getText(), doc.uri.fsPath, workspaceDir()); docKeys.set(uri, r.key); docDeps.set(uri, r.deps); + docFiles.set(uri, r.texts); return r.key; } @@ -238,6 +243,17 @@ function showCached(doc) { return true; } +// the server of this extension has no files of its own: it is sent the text of every file a document +// includes, keyed by its full path, and looks an include up there. only what changed is sent again. +function sendIncludes(uri) { + const texts = docFiles.get(uri); + if (!texts) return; + for (const [p, text] of texts) { + if (sentFiles.get(p) === text) continue; + sentFiles.set(p, text); + send({ jsonrpc: '2.0', method: 'zc/file', params: { path: p.split(path.sep).join('/'), text } }); + } +} // sends doc's text to the server: didOpen the first time, didChange after that function sendDoc(doc, key) { if (!proc || !proc.stdin.writable || !ready) return false; @@ -248,6 +264,7 @@ function sendDoc(doc, key) { q.push({ key, version }); sentKeys.set(uri, q); serverKeys.set(uri, key); + sendIncludes(uri); if (!opened.has(uri)) { opened.add(uri); send({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version, text: doc.getText() } } }); @@ -346,6 +363,7 @@ function startServer() { opened.clear(); serverKeys.clear(); sentKeys.clear(); + sentFiles.clear(); docVersions.clear(); serverStamp = serverStampFor(bin); const t0 = Date.now(); @@ -398,7 +416,7 @@ function startServer() { } }); watchInit(gen, !!bin); - request('initialize', { processId: process.pid, capabilities: {} }).then(res => { + request('initialize', { processId: process.pid, rootUri: workspaceDir() ? vscode.Uri.file(workspaceDir()).toString() : null, capabilities: {} }).then(res => { if (gen !== generation || stopping || !proc) return; if (initTimer) { clearTimeout(initTimer); initTimer = null; } const caps = res && res.capabilities ? Object.keys(res.capabilities) : []; @@ -527,6 +545,94 @@ function activate(context) { } }; context.subscriptions.push(vscode.languages.registerHoverProvider({ language: 'zc' }, hover)); + + // the sources the server carries (the runtime, compiler, standard library and icon module): shown read only at tinyvm:/NAME.zc + // when go to definition lands on a built-in. the text comes from the server, so it is the code the editor's checks run on + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('tinyvm', createSourceProvider(request, () => ready))); + + // completions refresh as the prefix changes; deprecated lifetime entries stay visible at the end. + context.subscriptions.push(vscode.languages.registerCompletionItemProvider({ language: 'zc' }, { + async provideCompletionItems(doc, position, token) { + if (!isZc(doc) || !ready || token.isCancellationRequested) return null; + ensureServer(doc); + const version = doc.version; + try { + const res = await request('textDocument/completion', { textDocument: { uri: doc.uri.toString() }, position: { line: position.line, character: position.character } }); + if (!res || token.isCancellationRequested || doc.version !== version) return null; + const items = (res.items || []).map(c => { + const item = new vscode.CompletionItem(c.label, c.kind ? c.kind - 1 : vscode.CompletionItemKind.Variable); + item.detail = c.detail; + item.sortText = c.sortText; + item.filterText = c.filterText; + if (c.tags && c.tags.includes(1)) item.tags = [vscode.CompletionItemTag.Deprecated]; + if (c.documentation) { + item.documentation = new vscode.MarkdownString(c.documentation.value || c.documentation); + item.documentation.isTrusted = false; + } + if (c.textEdit) { + const r = c.textEdit.range; + item.range = new vscode.Range(r.start.line, r.start.character, r.end.line, r.end.character); + item.insertText = c.textEdit.newText; + } + return item; + }); + return new vscode.CompletionList(items, !!res.isIncomplete); + } catch (err) { return null; } + } + }, '.', '>')); + + // compiler-backed import fixes use the same document version as their diagnostics. + if (vscode.languages.registerCodeActionsProvider) context.subscriptions.push(vscode.languages.registerCodeActionsProvider({ language: 'zc' }, { + async provideCodeActions(doc, range, actionContext, token) { + if (!isZc(doc) || !ready || token.isCancellationRequested) return []; + ensureServer(doc); + const version = doc.version; + const serverVersion = docVersions.get(doc.uri.toString()); + try { + const results = await request('textDocument/codeAction', { + textDocument: { uri: doc.uri.toString() }, + range: { start: { line: range.start.line, character: range.start.character }, end: { line: range.end.line, character: range.end.character } }, + context: { diagnostics: [] } + }); + if (token.isCancellationRequested || doc.version !== version || docVersions.get(doc.uri.toString()) !== serverVersion) return []; + return convertCodeActions(vscode, results, doc, serverVersion); + } catch (err) { return []; } + } + }, { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] })); + + // go to definition and find references: the compiler's own records, including names from included files + const locations = (res) => (Array.isArray(res) ? res : []).map(l => new vscode.Location(vscode.Uri.parse(l.uri), + new vscode.Range(l.range.start.line, l.range.start.character, l.range.end.line, l.range.end.character))); + context.subscriptions.push(vscode.languages.registerDefinitionProvider({ language: 'zc' }, { + async provideDefinition(doc, position) { + if (!isZc(doc) || !ready) return null; + ensureServer(doc); + try { + return locations(await request('textDocument/definition', { textDocument: { uri: doc.uri.toString() }, position: { line: position.line, character: position.character } })); + } catch (err) { return null; } + } + })); + // formatting: the server's pretty-printer, with the editor's tab settings (a .zcformat file overrides them) + context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider({ language: 'zc' }, { + async provideDocumentFormattingEdits(doc, options) { + if (!isZc(doc) || !ready) return []; + ensureServer(doc); + try { + const res = await request('textDocument/formatting', { textDocument: { uri: doc.uri.toString() }, options: { tabSize: options.tabSize, insertSpaces: options.insertSpaces } }); + return (Array.isArray(res) ? res : []).map(e => vscode.TextEdit.replace( + new vscode.Range(e.range.start.line, e.range.start.character, e.range.end.line, e.range.end.character), e.newText)); + } catch (err) { return []; } + } + })); + context.subscriptions.push(vscode.languages.registerReferenceProvider({ language: 'zc' }, { + async provideReferences(doc, position, refContext) { + if (!isZc(doc) || !ready) return null; + ensureServer(doc); + try { + return locations(await request('textDocument/references', { textDocument: { uri: doc.uri.toString() }, position: { line: position.line, character: position.character }, context: { includeDeclaration: !!refContext.includeDeclaration } })); + } catch (err) { return null; } + } + })); } function deactivate() { |
