2020-09-17 19:59:10 +00:00
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
* ------------------------------------------------------------------------------------------ */
|
|
|
|
import {
|
2021-10-23 12:03:31 +00:00
|
|
|
createConnection,
|
|
|
|
TextDocuments,
|
|
|
|
Diagnostic,
|
|
|
|
DiagnosticSeverity,
|
|
|
|
ProposedFeatures,
|
|
|
|
InitializeParams,
|
|
|
|
DidChangeConfigurationNotification,
|
|
|
|
TextDocumentSyncKind,
|
|
|
|
InitializeResult,
|
|
|
|
Position,
|
2020-09-17 19:59:10 +00:00
|
|
|
} from 'vscode-languageserver';
|
|
|
|
|
|
|
|
import {
|
2021-10-23 12:03:31 +00:00
|
|
|
TextDocument
|
2020-09-17 19:59:10 +00:00
|
|
|
} from 'vscode-languageserver-textdocument';
|
|
|
|
|
2020-09-17 22:26:14 +00:00
|
|
|
import * as NativeWrapper from './wrapper'
|
2020-09-18 15:48:45 +00:00
|
|
|
import * as fs from 'fs';
|
|
|
|
import * as path from 'path';
|
2020-09-17 22:26:14 +00:00
|
|
|
|
2020-09-17 19:59:10 +00:00
|
|
|
// Create a connection for the server, using Node's IPC as a transport.
|
|
|
|
// Also include all preview / proposed LSP features.
|
2020-09-18 15:48:45 +00:00
|
|
|
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);
|
2020-09-17 19:59:10 +00:00
|
|
|
|
|
|
|
// Create a simple text document manager.
|
|
|
|
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
2020-09-18 15:48:45 +00:00
|
|
|
let allFiles: Map<string, TextDocument> = new Map<string, TextDocument>();
|
2020-09-17 19:59:10 +00:00
|
|
|
|
|
|
|
let hasConfigurationCapability: boolean = false;
|
|
|
|
let hasWorkspaceFolderCapability: boolean = false;
|
|
|
|
let hasDiagnosticRelatedInformationCapability: boolean = false;
|
|
|
|
|
2020-09-18 15:48:45 +00:00
|
|
|
function registerFiles(directoryName: string) {
|
2021-10-23 12:03:31 +00:00
|
|
|
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" || path.extname(file) == ".astypedef") {
|
|
|
|
const filepath = directoryName + path.sep + file;
|
|
|
|
var uri = "file://" + filepath;
|
|
|
|
if (!allFiles.get(uri)) {
|
|
|
|
let td = TextDocument.create(uri, "Angelscript", 0, fs.readFileSync(filepath, 'utf8'));
|
|
|
|
allFiles.set(uri, td);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2020-09-18 15:48:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 19:59:10 +00:00
|
|
|
connection.onInitialize((params: InitializeParams) => {
|
2021-10-23 12:03:31 +00:00
|
|
|
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;
|
2020-09-17 19:59:10 +00:00
|
|
|
});
|
|
|
|
|
2021-10-23 12:03:31 +00:00
|
|
|
let isInitialized =false;
|
|
|
|
|
2020-09-17 19:59:10 +00:00
|
|
|
connection.onInitialized(() => {
|
2021-10-23 12:03:31 +00:00
|
|
|
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.');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-10-24 10:08:34 +00:00
|
|
|
connection.workspace.getConfiguration('angelscript-languageserver.defines').then((config) => {
|
|
|
|
let defines = config as string[];
|
|
|
|
for (let index = 0; index < defines.length; index++) {
|
|
|
|
const define = defines[index];
|
|
|
|
database.addDefine(define);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-10-24 10:35:26 +00:00
|
|
|
connection.workspace.getConfiguration('angelscript-languageserver.engine-settings').then((config) => {
|
|
|
|
for (const key in config) {
|
|
|
|
if (Object.prototype.hasOwnProperty.call(config, key)) {
|
|
|
|
const element = config[key];
|
|
|
|
let keyValue : NativeWrapper.ASEngineProperty = NativeWrapper.ASEngineProperty[key as keyof typeof NativeWrapper.ASEngineProperty];
|
|
|
|
database.setEngineProperty(keyValue, element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2021-10-23 12:03:31 +00:00
|
|
|
connection.workspace.getWorkspaceFolders().then((ws) => {
|
|
|
|
ws?.forEach(element => {
|
|
|
|
registerFiles(element.uri.substr(7))
|
|
|
|
});
|
|
|
|
database.reset();
|
|
|
|
allFiles.forEach(loadScript);
|
|
|
|
validateBuild().then(() => {
|
|
|
|
isInitialized = true;
|
|
|
|
});
|
|
|
|
})
|
2020-09-17 19:59:10 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
2020-09-18 15:48:45 +00:00
|
|
|
connection.onDidChangeConfiguration(change => {
|
2020-09-17 19:59:10 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// 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 => {
|
2021-10-23 12:03:31 +00:00
|
|
|
if (!isInitialized){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (change.document.languageId != "Angelscript" && change.document.languageId != "AngelscriptTypeDefinition")
|
|
|
|
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();
|
2020-09-17 19:59:10 +00:00
|
|
|
});
|
|
|
|
|
2020-09-17 22:26:14 +00:00
|
|
|
function convertSeverity(type: NativeWrapper.MessageType): DiagnosticSeverity {
|
2021-10-23 12:03:31 +00:00
|
|
|
switch (type) {
|
|
|
|
case NativeWrapper.MessageType.Information:
|
|
|
|
return DiagnosticSeverity.Information;
|
|
|
|
case NativeWrapper.MessageType.Warning:
|
|
|
|
return DiagnosticSeverity.Warning;
|
|
|
|
case NativeWrapper.MessageType.Error:
|
|
|
|
return DiagnosticSeverity.Error;
|
|
|
|
}
|
2020-09-17 22:26:14 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 15:48:45 +00:00
|
|
|
function loadScript(textDocument: TextDocument): void {
|
2021-10-23 12:03:31 +00:00
|
|
|
if (textDocument.languageId != "Angelscript" && textDocument.languageId != "AngelscriptTypeDefinition")
|
|
|
|
return;
|
|
|
|
if (!fs.existsSync(textDocument.uri.substr(7))) {
|
|
|
|
allFiles.delete(textDocument.uri);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let text = textDocument.getText();
|
|
|
|
if (path.extname(textDocument.uri) == ".astypedef") {
|
|
|
|
database.loadTypeDef(textDocument.uri, text);
|
|
|
|
} else {
|
|
|
|
database.loadScript(textDocument.uri, text);
|
|
|
|
}
|
2020-09-18 15:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function validateBuild(): Promise<void> {
|
2021-10-23 12:03:31 +00:00
|
|
|
console.log("Building");
|
|
|
|
var r = database.build();
|
|
|
|
if (r <= -1) {
|
|
|
|
console.log(r.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
var messages = database.messages();
|
|
|
|
let diagnostics: Map<string, Diagnostic[]> = new Map<string, Diagnostic[]>();
|
|
|
|
|
|
|
|
for (let i = 0; i < messages.length; i++) {
|
|
|
|
let msg = messages[i];
|
|
|
|
if (msg.type == NativeWrapper.MessageType.Information)
|
|
|
|
continue;
|
|
|
|
console.log('[ ' + msg.section + ' ] ' + msg.message);
|
|
|
|
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: []});
|
|
|
|
});
|
|
|
|
console.log("Finished building");
|
|
|
|
// Send the computed diagnostics to VSCode.
|
2020-09-17 19:59:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
connection.onDidChangeWatchedFiles(_change => {
|
2021-10-23 12:03:31 +00:00
|
|
|
// Monitored files have change in VSCode
|
|
|
|
connection.console.log('We received an file change event');
|
2020-09-17 19:59:10 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// 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();
|