|
@@ -1,7 +1,23 @@
|
|
|
import * as vscode from 'vscode';
|
|
import * as vscode from 'vscode';
|
|
|
|
|
+import { createDiagnostics } from './diagnostics';
|
|
|
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node';
|
|
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node';
|
|
|
|
|
|
|
|
|
|
+const KEYWORDS = new Set([
|
|
|
|
|
+ 'ARRAY', 'BEGIN', 'BY', 'CASE', 'CONST', 'DEFINITION', 'DIV', 'DO',
|
|
|
|
|
+ 'ELSE', 'ELSIF', 'END', 'EXIT', 'FOR', 'IF', 'IMPORT', 'IN', 'IS',
|
|
|
|
|
+ 'LOOP', 'MOD', 'MODULE', 'NIL', 'OF', 'OR', 'POINTER', 'PROCEDURE',
|
|
|
|
|
+ 'RECORD', 'REPEAT', 'RETURN', 'THEN', 'TO', 'TYPE', 'UNTIL', 'VAR',
|
|
|
|
|
+ 'WHILE', 'WITH'
|
|
|
|
|
+]);
|
|
|
|
|
+
|
|
|
|
|
+let applyingEdit = false;
|
|
|
|
|
+
|
|
|
|
|
+function isIdentifierCharacter(ch: string | undefined): boolean {
|
|
|
|
|
+ return !!ch && /[A-Za-z0-9_]/.test(ch);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export function activate(context: vscode.ExtensionContext): void {
|
|
export function activate(context: vscode.ExtensionContext): void {
|
|
|
|
|
+ createDiagnostics(context);
|
|
|
const serverModule = context.asAbsolutePath('out/server.js');
|
|
const serverModule = context.asAbsolutePath('out/server.js');
|
|
|
const serverOptions: ServerOptions = {
|
|
const serverOptions: ServerOptions = {
|
|
|
run: { module: serverModule, transport: TransportKind.ipc },
|
|
run: { module: serverModule, transport: TransportKind.ipc },
|
|
@@ -15,12 +31,74 @@ export function activate(context: vscode.ExtensionContext): void {
|
|
|
synchronize: { configurationSection: 'modula2' },
|
|
synchronize: { configurationSection: 'modula2' },
|
|
|
initializationOptions: {
|
|
initializationOptions: {
|
|
|
oberonDialect: cfg.get<string>('oberon.dialect', 'oberon2'),
|
|
oberonDialect: cfg.get<string>('oberon.dialect', 'oberon2'),
|
|
|
- oberonValidators: cfg.get<object>('oberon.validators', {})
|
|
|
|
|
|
|
+ oberonValidators: cfg.get<object>('oberon.validators', {}),
|
|
|
|
|
+ format: {
|
|
|
|
|
+ indentSize: cfg.get<number>('oberon.format.indentSize', 2),
|
|
|
|
|
+ useTabs: cfg.get<boolean>('oberon.format.useTabs', false),
|
|
|
|
|
+ keywordCase: cfg.get<string>('oberon.format.keywordCase', 'upper'),
|
|
|
|
|
+ spaceAroundOperators: cfg.get<boolean>('oberon.format.spaceAroundOperators', true),
|
|
|
|
|
+ spaceAfterComma: cfg.get<boolean>('oberon.format.spaceAfterComma', true),
|
|
|
|
|
+ spaceBeforeColon: cfg.get<boolean>('oberon.format.spaceBeforeColon', false),
|
|
|
|
|
+ spaceAroundRange: cfg.get<boolean>('oberon.format.spaceAroundRange', true),
|
|
|
|
|
+ emptyLineLimit: cfg.get<number>('oberon.format.emptyLineLimit', 1),
|
|
|
|
|
+ trimTrailingWhitespace: cfg.get<boolean>('oberon.format.trimTrailingWhitespace', true),
|
|
|
|
|
+ insertFinalNewline: cfg.get<boolean>('oberon.format.insertFinalNewline', true)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
const client = new LanguageClient('oberonCocoR', 'Oberon Coco/R Language Server', serverOptions, clientOptions);
|
|
const client = new LanguageClient('oberonCocoR', 'Oberon Coco/R Language Server', serverOptions, clientOptions);
|
|
|
context.subscriptions.push(client);
|
|
context.subscriptions.push(client);
|
|
|
void client.start();
|
|
void client.start();
|
|
|
|
|
+ context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(async event => {
|
|
|
|
|
+ if (applyingEdit || event.document.languageId !== 'oberon' ||
|
|
|
|
|
+ !vscode.workspace.getConfiguration('modula2').get<boolean>('oberon.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.
|
|
|
|
|
+ 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 {}
|
|
export function deactivate(): void {}
|