aboutsummaryrefslogtreecommitdiff
path: root/editors/vscode/client/extension.js
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-14 08:29:46 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-14 08:29:46 +0330
commit1c53304d03414890af0da7432d1aea87e754a14a (patch)
tree97190d86cf548c9bc63f68c86a4d2a4f09809713 /editors/vscode/client/extension.js
parenta (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.js108
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() {