import * as vscode from 'vscode'; import { createDiagnostics } from './diagnostics'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node'; const KEYWORDS = new Set([ 'AND', 'ARRAY', 'BEGIN', 'CASE', 'CONST', 'DIV', 'DO', 'DOWNTO', 'ELSE', 'END', 'FILE', 'FOR', 'FUNCTION', 'GOTO', 'IF', 'IN', 'LABEL', 'MOD', 'NIL', 'NOT', 'OF', 'OR', 'PACKED', 'PROCEDURE', 'PROGRAM', 'RECORD', 'REPEAT', 'SET', 'THEN', 'TO', 'TYPE', 'UNIT', 'UNTIL', 'USES', 'VAR', 'WHILE', 'WITH', 'FORWARD' ]); let applyingEdit = false; function isIdentifierCharacter(ch: string | undefined): boolean { return !!ch && /[A-Za-z0-9_]/.test(ch); } export function activate(context: vscode.ExtensionContext): void { createDiagnostics(context); const serverModule = context.asAbsolutePath('out/server.js'); const serverOptions: ServerOptions = { run: { module: serverModule, transport: TransportKind.ipc }, debug: { module: serverModule, transport: TransportKind.ipc } }; const cfg = vscode.workspace.getConfiguration('modula2'); const clientOptions: LanguageClientOptions = { documentSelector: [ { scheme: 'file', language: 'pascal' } ], synchronize: { configurationSection: 'modula2' }, initializationOptions: { pascalDialect: cfg.get('pascal.dialect', 'freepascal'), pascalValidators: cfg.get('pascal.validators', {}), format: { indentSize: cfg.get('pascal.format.indentSize', 2), useTabs: cfg.get('pascal.format.useTabs', false), keywordCase: cfg.get('pascal.format.keywordCase', 'upper'), spaceAroundOperators: cfg.get('pascal.format.spaceAroundOperators', true), spaceAfterComma: cfg.get('pascal.format.spaceAfterComma', true), spaceBeforeColon: cfg.get('pascal.format.spaceBeforeColon', false), spaceAroundRange: cfg.get('pascal.format.spaceAroundRange', true), emptyLineLimit: cfg.get('pascal.format.emptyLineLimit', 1), trimTrailingWhitespace: cfg.get('pascal.format.trimTrailingWhitespace', true), insertFinalNewline: cfg.get('pascal.format.insertFinalNewline', true) } } }; const client = new LanguageClient('pascalCocoR', 'Pascal Coco/R Language Server', serverOptions, clientOptions); context.subscriptions.push(client); void client.start(); context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(async event => { if (applyingEdit || event.document.languageId !== 'pascal' || !vscode.workspace.getConfiguration('modula2').get('pascal.uppercaseKeywords', true)) { return; } const editor = vscode.window.activeTextEditor; if (!editor || editor.document.uri.toString() !== event.document.uri.toString()) { return; } for (const change of event.contentChanges) { const position = change.range.start.translate(0, change.text.length); const line = event.document.lineAt(position.line).text; let start = position.character; while (start > 0 && isIdentifierCharacter(line[start - 1])) { start--; } let end = position.character; while (end < line.length && isIdentifierCharacter(line[end])) { end++; } // Do not modify a token while it is still being typed. // This is what prevents "beginning" from becoming "BEGINning". if (end >= line.length || isIdentifierCharacter(line[end])) { continue; } const token = line.slice(start, end); const upper = token.toUpperCase(); if (!KEYWORDS.has(upper) || token === upper) { continue; } applyingEdit = true; try { await editor.edit(builder => { builder.replace( new vscode.Range(position.line, start, position.line, end), upper ); }); } finally { applyingEdit = false; } } })); } export function deactivate(): void {}