- Hover provider showing entity information and type - Go-to-definition (F12) for entity references - Basic IFC file validation (ISO-10303-21 header check) - Entity parsing with regex-based detection - Proper CommonJS module system (avoiding ES module issues) This replaces the broken baseline from ifc-developer-tools which had: - Non-functional ES module configuration - Circular dependency issues - Parser crashes - Non-working PositionVisitor Built on Microsoft's LSP example template for a clean, maintainable foundation. Next: Add hierarchical entity dependency tree in hover tooltip."
402 lines
19 KiB
JavaScript
402 lines
19 KiB
JavaScript
"use strict";
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
* ------------------------------------------------------------------------------------------ */
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.DidSaveTextDocumentFeature = exports.WillSaveWaitUntilFeature = exports.WillSaveFeature = exports.DidChangeTextDocumentFeature = exports.DidCloseTextDocumentFeature = exports.DidOpenTextDocumentFeature = void 0;
|
|
const vscode_1 = require("vscode");
|
|
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
|
|
const features_1 = require("./features");
|
|
const UUID = require("./utils/uuid");
|
|
class DidOpenTextDocumentFeature extends features_1.TextDocumentEventFeature {
|
|
constructor(client, syncedDocuments) {
|
|
super(client, vscode_1.workspace.onDidOpenTextDocument, vscode_languageserver_protocol_1.DidOpenTextDocumentNotification.type, () => client.middleware.didOpen, (textDocument) => client.code2ProtocolConverter.asOpenTextDocumentParams(textDocument), (data) => data, features_1.TextDocumentEventFeature.textDocumentFilter);
|
|
this._syncedDocuments = syncedDocuments;
|
|
}
|
|
get openDocuments() {
|
|
return this._syncedDocuments.values();
|
|
}
|
|
fillClientCapabilities(capabilities) {
|
|
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').dynamicRegistration = true;
|
|
}
|
|
initialize(capabilities, documentSelector) {
|
|
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
|
|
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.openClose) {
|
|
this.register({ id: UUID.generateUuid(), registerOptions: { documentSelector: documentSelector } });
|
|
}
|
|
}
|
|
get registrationType() {
|
|
return vscode_languageserver_protocol_1.DidOpenTextDocumentNotification.type;
|
|
}
|
|
register(data) {
|
|
super.register(data);
|
|
if (!data.registerOptions.documentSelector) {
|
|
return;
|
|
}
|
|
const documentSelector = this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector);
|
|
vscode_1.workspace.textDocuments.forEach((textDocument) => {
|
|
const uri = textDocument.uri.toString();
|
|
if (this._syncedDocuments.has(uri)) {
|
|
return;
|
|
}
|
|
if (vscode_1.languages.match(documentSelector, textDocument) > 0 && !this._client.hasDedicatedTextSynchronizationFeature(textDocument)) {
|
|
const middleware = this._client.middleware;
|
|
const didOpen = (textDocument) => {
|
|
return this._client.sendNotification(this._type, this._createParams(textDocument));
|
|
};
|
|
(middleware.didOpen ? middleware.didOpen(textDocument, didOpen) : didOpen(textDocument)).catch((error) => {
|
|
this._client.error(`Sending document notification ${this._type.method} failed`, error);
|
|
});
|
|
this._syncedDocuments.set(uri, textDocument);
|
|
}
|
|
});
|
|
}
|
|
getTextDocument(data) {
|
|
return data;
|
|
}
|
|
notificationSent(textDocument, type, params) {
|
|
this._syncedDocuments.set(textDocument.uri.toString(), textDocument);
|
|
super.notificationSent(textDocument, type, params);
|
|
}
|
|
}
|
|
exports.DidOpenTextDocumentFeature = DidOpenTextDocumentFeature;
|
|
class DidCloseTextDocumentFeature extends features_1.TextDocumentEventFeature {
|
|
constructor(client, syncedDocuments, pendingTextDocumentChanges) {
|
|
super(client, vscode_1.workspace.onDidCloseTextDocument, vscode_languageserver_protocol_1.DidCloseTextDocumentNotification.type, () => client.middleware.didClose, (textDocument) => client.code2ProtocolConverter.asCloseTextDocumentParams(textDocument), (data) => data, features_1.TextDocumentEventFeature.textDocumentFilter);
|
|
this._syncedDocuments = syncedDocuments;
|
|
this._pendingTextDocumentChanges = pendingTextDocumentChanges;
|
|
}
|
|
get registrationType() {
|
|
return vscode_languageserver_protocol_1.DidCloseTextDocumentNotification.type;
|
|
}
|
|
fillClientCapabilities(capabilities) {
|
|
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').dynamicRegistration = true;
|
|
}
|
|
initialize(capabilities, documentSelector) {
|
|
let textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
|
|
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.openClose) {
|
|
this.register({ id: UUID.generateUuid(), registerOptions: { documentSelector: documentSelector } });
|
|
}
|
|
}
|
|
async callback(data) {
|
|
await super.callback(data);
|
|
this._pendingTextDocumentChanges.delete(data.uri.toString());
|
|
}
|
|
getTextDocument(data) {
|
|
return data;
|
|
}
|
|
notificationSent(textDocument, type, params) {
|
|
this._syncedDocuments.delete(textDocument.uri.toString());
|
|
super.notificationSent(textDocument, type, params);
|
|
}
|
|
unregister(id) {
|
|
const selector = this._selectors.get(id);
|
|
// The super call removed the selector from the map
|
|
// of selectors.
|
|
super.unregister(id);
|
|
const selectors = this._selectors.values();
|
|
this._syncedDocuments.forEach((textDocument) => {
|
|
if (vscode_1.languages.match(selector, textDocument) > 0 && !this._selectorFilter(selectors, textDocument) && !this._client.hasDedicatedTextSynchronizationFeature(textDocument)) {
|
|
let middleware = this._client.middleware;
|
|
let didClose = (textDocument) => {
|
|
return this._client.sendNotification(this._type, this._createParams(textDocument));
|
|
};
|
|
this._syncedDocuments.delete(textDocument.uri.toString());
|
|
(middleware.didClose ? middleware.didClose(textDocument, didClose) : didClose(textDocument)).catch((error) => {
|
|
this._client.error(`Sending document notification ${this._type.method} failed`, error);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
exports.DidCloseTextDocumentFeature = DidCloseTextDocumentFeature;
|
|
class DidChangeTextDocumentFeature extends features_1.DynamicDocumentFeature {
|
|
constructor(client, pendingTextDocumentChanges) {
|
|
super(client);
|
|
this._changeData = new Map();
|
|
this._onNotificationSent = new vscode_1.EventEmitter();
|
|
this._onPendingChangeAdded = new vscode_1.EventEmitter();
|
|
this._pendingTextDocumentChanges = pendingTextDocumentChanges;
|
|
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
|
|
}
|
|
get onNotificationSent() {
|
|
return this._onNotificationSent.event;
|
|
}
|
|
get onPendingChangeAdded() {
|
|
return this._onPendingChangeAdded.event;
|
|
}
|
|
get syncKind() {
|
|
return this._syncKind;
|
|
}
|
|
get registrationType() {
|
|
return vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type;
|
|
}
|
|
fillClientCapabilities(capabilities) {
|
|
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').dynamicRegistration = true;
|
|
}
|
|
initialize(capabilities, documentSelector) {
|
|
let textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
|
|
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.change !== undefined && textDocumentSyncOptions.change !== vscode_languageserver_protocol_1.TextDocumentSyncKind.None) {
|
|
this.register({
|
|
id: UUID.generateUuid(),
|
|
registerOptions: Object.assign({}, { documentSelector: documentSelector }, { syncKind: textDocumentSyncOptions.change })
|
|
});
|
|
}
|
|
}
|
|
register(data) {
|
|
if (!data.registerOptions.documentSelector) {
|
|
return;
|
|
}
|
|
if (!this._listener) {
|
|
this._listener = vscode_1.workspace.onDidChangeTextDocument(this.callback, this);
|
|
}
|
|
this._changeData.set(data.id, {
|
|
syncKind: data.registerOptions.syncKind,
|
|
documentSelector: this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector),
|
|
});
|
|
this.updateSyncKind(data.registerOptions.syncKind);
|
|
}
|
|
*getDocumentSelectors() {
|
|
for (const data of this._changeData.values()) {
|
|
yield data.documentSelector;
|
|
}
|
|
}
|
|
async callback(event) {
|
|
// Text document changes are send for dirty changes as well. We don't
|
|
// have dirty / un-dirty events in the LSP so we ignore content changes
|
|
// with length zero.
|
|
if (event.contentChanges.length === 0) {
|
|
return;
|
|
}
|
|
// We need to capture the URI and version here since they might change on the text document
|
|
// until we reach did `didChange` call since the middleware support async execution.
|
|
const uri = event.document.uri;
|
|
const version = event.document.version;
|
|
const promises = [];
|
|
for (const changeData of this._changeData.values()) {
|
|
if (vscode_1.languages.match(changeData.documentSelector, event.document) > 0 && !this._client.hasDedicatedTextSynchronizationFeature(event.document)) {
|
|
const middleware = this._client.middleware;
|
|
if (changeData.syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Incremental) {
|
|
const didChange = async (event) => {
|
|
const params = this._client.code2ProtocolConverter.asChangeTextDocumentParams(event, uri, version);
|
|
await this._client.sendNotification(vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
|
|
this.notificationSent(event.document, vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
|
|
};
|
|
promises.push(middleware.didChange ? middleware.didChange(event, event => didChange(event)) : didChange(event));
|
|
}
|
|
else if (changeData.syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
|
|
const didChange = async (event) => {
|
|
const eventUri = event.document.uri.toString();
|
|
this._pendingTextDocumentChanges.set(eventUri, event.document);
|
|
this._onPendingChangeAdded.fire();
|
|
};
|
|
promises.push(middleware.didChange ? middleware.didChange(event, event => didChange(event)) : didChange(event));
|
|
}
|
|
}
|
|
}
|
|
return Promise.all(promises).then(undefined, (error) => {
|
|
this._client.error(`Sending document notification ${vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type.method} failed`, error);
|
|
throw error;
|
|
});
|
|
}
|
|
notificationSent(textDocument, type, params) {
|
|
this._onNotificationSent.fire({ textDocument, type, params });
|
|
}
|
|
unregister(id) {
|
|
this._changeData.delete(id);
|
|
if (this._changeData.size === 0) {
|
|
if (this._listener) {
|
|
this._listener.dispose();
|
|
this._listener = undefined;
|
|
}
|
|
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
|
|
}
|
|
else {
|
|
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
|
|
for (const changeData of this._changeData.values()) {
|
|
this.updateSyncKind(changeData.syncKind);
|
|
if (this._syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
clear() {
|
|
this._pendingTextDocumentChanges.clear();
|
|
this._changeData.clear();
|
|
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
|
|
if (this._listener) {
|
|
this._listener.dispose();
|
|
this._listener = undefined;
|
|
}
|
|
}
|
|
getPendingDocumentChanges(excludes) {
|
|
if (this._pendingTextDocumentChanges.size === 0) {
|
|
return [];
|
|
}
|
|
let result;
|
|
if (excludes.size === 0) {
|
|
result = Array.from(this._pendingTextDocumentChanges.values());
|
|
this._pendingTextDocumentChanges.clear();
|
|
}
|
|
else {
|
|
result = [];
|
|
for (const entry of this._pendingTextDocumentChanges) {
|
|
if (!excludes.has(entry[0])) {
|
|
result.push(entry[1]);
|
|
this._pendingTextDocumentChanges.delete(entry[0]);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
getProvider(document) {
|
|
for (const changeData of this._changeData.values()) {
|
|
if (vscode_1.languages.match(changeData.documentSelector, document) > 0) {
|
|
return {
|
|
send: (event) => {
|
|
return this.callback(event);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
updateSyncKind(syncKind) {
|
|
if (this._syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
|
|
return;
|
|
}
|
|
switch (syncKind) {
|
|
case vscode_languageserver_protocol_1.TextDocumentSyncKind.Full:
|
|
this._syncKind = syncKind;
|
|
break;
|
|
case vscode_languageserver_protocol_1.TextDocumentSyncKind.Incremental:
|
|
if (this._syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.None) {
|
|
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.Incremental;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
exports.DidChangeTextDocumentFeature = DidChangeTextDocumentFeature;
|
|
class WillSaveFeature extends features_1.TextDocumentEventFeature {
|
|
constructor(client) {
|
|
super(client, vscode_1.workspace.onWillSaveTextDocument, vscode_languageserver_protocol_1.WillSaveTextDocumentNotification.type, () => client.middleware.willSave, (willSaveEvent) => client.code2ProtocolConverter.asWillSaveTextDocumentParams(willSaveEvent), (event) => event.document, (selectors, willSaveEvent) => features_1.TextDocumentEventFeature.textDocumentFilter(selectors, willSaveEvent.document));
|
|
}
|
|
get registrationType() {
|
|
return vscode_languageserver_protocol_1.WillSaveTextDocumentNotification.type;
|
|
}
|
|
fillClientCapabilities(capabilities) {
|
|
let value = (0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization');
|
|
value.willSave = true;
|
|
}
|
|
initialize(capabilities, documentSelector) {
|
|
let textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
|
|
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.willSave) {
|
|
this.register({
|
|
id: UUID.generateUuid(),
|
|
registerOptions: { documentSelector: documentSelector }
|
|
});
|
|
}
|
|
}
|
|
getTextDocument(data) {
|
|
return data.document;
|
|
}
|
|
}
|
|
exports.WillSaveFeature = WillSaveFeature;
|
|
class WillSaveWaitUntilFeature extends features_1.DynamicDocumentFeature {
|
|
constructor(client) {
|
|
super(client);
|
|
this._selectors = new Map();
|
|
}
|
|
getDocumentSelectors() {
|
|
return this._selectors.values();
|
|
}
|
|
get registrationType() {
|
|
return vscode_languageserver_protocol_1.WillSaveTextDocumentWaitUntilRequest.type;
|
|
}
|
|
fillClientCapabilities(capabilities) {
|
|
let value = (0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization');
|
|
value.willSaveWaitUntil = true;
|
|
}
|
|
initialize(capabilities, documentSelector) {
|
|
let textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
|
|
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.willSaveWaitUntil) {
|
|
this.register({
|
|
id: UUID.generateUuid(),
|
|
registerOptions: { documentSelector: documentSelector }
|
|
});
|
|
}
|
|
}
|
|
register(data) {
|
|
if (!data.registerOptions.documentSelector) {
|
|
return;
|
|
}
|
|
if (!this._listener) {
|
|
this._listener = vscode_1.workspace.onWillSaveTextDocument(this.callback, this);
|
|
}
|
|
this._selectors.set(data.id, this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector));
|
|
}
|
|
callback(event) {
|
|
if (features_1.TextDocumentEventFeature.textDocumentFilter(this._selectors.values(), event.document) && !this._client.hasDedicatedTextSynchronizationFeature(event.document)) {
|
|
let middleware = this._client.middleware;
|
|
let willSaveWaitUntil = (event) => {
|
|
return this._client.sendRequest(vscode_languageserver_protocol_1.WillSaveTextDocumentWaitUntilRequest.type, this._client.code2ProtocolConverter.asWillSaveTextDocumentParams(event)).then(async (edits) => {
|
|
let vEdits = await this._client.protocol2CodeConverter.asTextEdits(edits);
|
|
return vEdits === undefined ? [] : vEdits;
|
|
});
|
|
};
|
|
event.waitUntil(middleware.willSaveWaitUntil
|
|
? middleware.willSaveWaitUntil(event, willSaveWaitUntil)
|
|
: willSaveWaitUntil(event));
|
|
}
|
|
}
|
|
unregister(id) {
|
|
this._selectors.delete(id);
|
|
if (this._selectors.size === 0 && this._listener) {
|
|
this._listener.dispose();
|
|
this._listener = undefined;
|
|
}
|
|
}
|
|
clear() {
|
|
this._selectors.clear();
|
|
if (this._listener) {
|
|
this._listener.dispose();
|
|
this._listener = undefined;
|
|
}
|
|
}
|
|
}
|
|
exports.WillSaveWaitUntilFeature = WillSaveWaitUntilFeature;
|
|
class DidSaveTextDocumentFeature extends features_1.TextDocumentEventFeature {
|
|
constructor(client) {
|
|
super(client, vscode_1.workspace.onDidSaveTextDocument, vscode_languageserver_protocol_1.DidSaveTextDocumentNotification.type, () => client.middleware.didSave, (textDocument) => client.code2ProtocolConverter.asSaveTextDocumentParams(textDocument, this._includeText), (data) => data, features_1.TextDocumentEventFeature.textDocumentFilter);
|
|
this._includeText = false;
|
|
}
|
|
get registrationType() {
|
|
return vscode_languageserver_protocol_1.DidSaveTextDocumentNotification.type;
|
|
}
|
|
fillClientCapabilities(capabilities) {
|
|
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').didSave = true;
|
|
}
|
|
initialize(capabilities, documentSelector) {
|
|
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
|
|
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.save) {
|
|
const saveOptions = typeof textDocumentSyncOptions.save === 'boolean'
|
|
? { includeText: false }
|
|
: { includeText: !!textDocumentSyncOptions.save.includeText };
|
|
this.register({
|
|
id: UUID.generateUuid(),
|
|
registerOptions: Object.assign({}, { documentSelector: documentSelector }, saveOptions)
|
|
});
|
|
}
|
|
}
|
|
register(data) {
|
|
this._includeText = !!data.registerOptions.includeText;
|
|
super.register(data);
|
|
}
|
|
getTextDocument(data) {
|
|
return data;
|
|
}
|
|
}
|
|
exports.DidSaveTextDocumentFeature = DidSaveTextDocumentFeature;
|