/* -------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ import { createConnection, TextDocuments, Diagnostic, DiagnosticSeverity, ProposedFeatures, InitializeParams, DidChangeConfigurationNotification, TextDocumentSyncKind, InitializeResult, Position, } from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; import * as NativeWrapper from './wrapper' import * as fs from 'fs'; import * as path from 'path'; // Create a connection for the server, using Node's IPC as a transport. // Also include all preview / proposed LSP features. const connection = createConnection(ProposedFeatures.all); const database = NativeWrapper.BuildDatabase(); //TODO: change these based on user settings database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_DISALLOW_EMPTY_LIST_ELEMENTS, 1); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE, 0); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_ALLOW_UNSAFE_REFERENCES, 1); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT, 1); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_AUTO_GARBAGE_COLLECT, 0); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_REQUIRE_ENUM_SCOPE, 1); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_PROPERTY_ACCESSOR_MODE, 2); database.setEngineProperty(NativeWrapper.ASEngineProperty.asEP_COMPILER_WARNINGS, 2); // Create a simple text document manager. let documents: TextDocuments = new TextDocuments(TextDocument); let allFiles: Map = new Map(); let hasConfigurationCapability: boolean = false; let hasWorkspaceFolderCapability: boolean = false; let hasDiagnosticRelatedInformationCapability: boolean = false; function registerFiles(directoryName: string) { let files = fs.readdirSync(directoryName); files.forEach(function (file) { let f = fs.statSync(directoryName + path.sep + file); if (f.isDirectory()) { registerFiles(directoryName + path.sep + file) } else { if (path.extname(file) == ".as") { const filepath = directoryName + path.sep + file; var uri = "file://" + filepath; if (!allFiles.get(uri)) { connection.console.log("Loaded new file at uri " + uri); let td = TextDocument.create(uri, "Angelscript", 0, fs.readFileSync(filepath, 'utf8')); allFiles.set(uri, td); } } } }) } connection.onInitialize((params: InitializeParams) => { let capabilities = params.capabilities; hasConfigurationCapability = !!( capabilities.workspace && !!capabilities.workspace.configuration ); hasWorkspaceFolderCapability = !!( capabilities.workspace && !!capabilities.workspace.workspaceFolders ); hasDiagnosticRelatedInformationCapability = !!( capabilities.textDocument && capabilities.textDocument.publishDiagnostics && capabilities.textDocument.publishDiagnostics.relatedInformation ); const result: InitializeResult = { capabilities: { textDocumentSync: TextDocumentSyncKind.Incremental, } }; if (hasWorkspaceFolderCapability) { result.capabilities.workspace = { workspaceFolders: { supported: true } }; } return result; }); connection.onInitialized(() => { if (hasConfigurationCapability) { // Register for all configuration changes. connection.client.register(DidChangeConfigurationNotification.type, undefined); } if (hasWorkspaceFolderCapability) { connection.workspace. connection.workspace.onDidChangeWorkspaceFolders(_event => { connection.console.log('Workspace folder change event received.'); }); } connection.workspace.getWorkspaceFolders().then((ws) => { ws?.forEach(element => { registerFiles(element.uri.substr(7)) }); database.reset(); allFiles.forEach(loadScript); validateBuild(); }) }); connection.onDidChangeConfiguration(change => { }); // The content of a text document has changed. This event is emitted // when the text document first opened or when its content has changed. documents.onDidChangeContent(change => { if (change.document.languageId != "Angelscript") return; var current = allFiles.get(change.document.uri); if (current != null && current != change.document){ if (current.version == 0){ allFiles.set(change.document.uri, change.document); return; } } else if (current == null){ allFiles.set(change.document.uri, change.document); } database.reset(); allFiles.forEach(loadScript); validateBuild(); }); function convertSeverity(type: NativeWrapper.MessageType): DiagnosticSeverity { switch (type) { case NativeWrapper.MessageType.Information: return DiagnosticSeverity.Information; case NativeWrapper.MessageType.Warning: return DiagnosticSeverity.Warning; case NativeWrapper.MessageType.Error: return DiagnosticSeverity.Error; } } function loadScript(textDocument: TextDocument): void { if (textDocument.languageId != "Angelscript") return; if (!fs.existsSync(textDocument.uri.substr(7))){ allFiles.delete(textDocument.uri); return; } let text = textDocument.getText(); database.loadScript(textDocument.uri, text); } async function validateBuild(): Promise { var r = database.build(); if (r < -1) { connection.console.log(r.toString()); } connection.console.log("Building"); var messages = database.messages(); let diagnostics: Map = new Map(); for (let i = 0; i < messages.length; i++) { let msg = messages[i]; if (msg.type == NativeWrapper.MessageType.Information) continue; if (msg.section == ""){ if (msg.type == NativeWrapper.MessageType.Error){ connection.window.showErrorMessage(msg.message); } else if (msg.type == NativeWrapper.MessageType.Warning){ connection.window.showWarningMessage(msg.message); } continue; } let startPos = Position.create(msg.row - 1, msg.col - 1); if (startPos.line < 0) startPos.line = 0; if (startPos.character < 0) startPos.character = 0; let textDocument = documents.get(msg.section); if (textDocument == undefined) continue; let offset = textDocument.offsetAt(startPos); let diagnostic: Diagnostic = { severity: convertSeverity(msg.type), range: { start: startPos, end: textDocument.positionAt(offset + 1) }, message: msg.message, source: 'Angelscript' }; if (msg.type == NativeWrapper.MessageType.Error){ connection.console.error(`[${msg.section}:${msg.row - 1},${msg.col - 1}] ${msg.message}`) } var diag = diagnostics.get(msg.section); if (diag) { diag.push(diagnostic) } else { diagnostics.set(msg.section, [diagnostic]); } } documents.all().forEach(element => { let v = diagnostics.get(element.uri); if (v) connection.sendDiagnostics({ uri: element.uri, diagnostics: v }); else connection.sendDiagnostics({ uri: element.uri, diagnostics: [] }); }); // Send the computed diagnostics to VSCode. } connection.onDidChangeWatchedFiles(_change => { // Monitored files have change in VSCode connection.console.log('We received an file change event'); }); // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection); // Listen on the connection connection.listen();