- 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."
291 lines
11 KiB
JavaScript
291 lines
11 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.WorkspaceFeature = exports.TextDocumentLanguageFeature = exports.TextDocumentEventFeature = exports.DynamicDocumentFeature = exports.DynamicFeature = exports.StaticFeature = exports.ensure = exports.LSPCancellationError = void 0;
|
|
const vscode_1 = require("vscode");
|
|
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
|
|
const Is = require("./utils/is");
|
|
const UUID = require("./utils/uuid");
|
|
class LSPCancellationError extends vscode_1.CancellationError {
|
|
constructor(data) {
|
|
super();
|
|
this.data = data;
|
|
}
|
|
}
|
|
exports.LSPCancellationError = LSPCancellationError;
|
|
function ensure(target, key) {
|
|
if (target[key] === undefined) {
|
|
target[key] = {};
|
|
}
|
|
return target[key];
|
|
}
|
|
exports.ensure = ensure;
|
|
var StaticFeature;
|
|
(function (StaticFeature) {
|
|
function is(value) {
|
|
const candidate = value;
|
|
return candidate !== undefined && candidate !== null &&
|
|
Is.func(candidate.fillClientCapabilities) && Is.func(candidate.initialize) && Is.func(candidate.getState) && Is.func(candidate.clear) &&
|
|
(candidate.fillInitializeParams === undefined || Is.func(candidate.fillInitializeParams));
|
|
}
|
|
StaticFeature.is = is;
|
|
})(StaticFeature || (exports.StaticFeature = StaticFeature = {}));
|
|
var DynamicFeature;
|
|
(function (DynamicFeature) {
|
|
function is(value) {
|
|
const candidate = value;
|
|
return candidate !== undefined && candidate !== null &&
|
|
Is.func(candidate.fillClientCapabilities) && Is.func(candidate.initialize) && Is.func(candidate.getState) && Is.func(candidate.clear) &&
|
|
(candidate.fillInitializeParams === undefined || Is.func(candidate.fillInitializeParams)) && Is.func(candidate.register) &&
|
|
Is.func(candidate.unregister) && candidate.registrationType !== undefined;
|
|
}
|
|
DynamicFeature.is = is;
|
|
})(DynamicFeature || (exports.DynamicFeature = DynamicFeature = {}));
|
|
/**
|
|
* An abstract dynamic feature implementation that operates on documents (e.g. text
|
|
* documents or notebooks).
|
|
*/
|
|
class DynamicDocumentFeature {
|
|
constructor(client) {
|
|
this._client = client;
|
|
}
|
|
/**
|
|
* Returns the state the feature is in.
|
|
*/
|
|
getState() {
|
|
const selectors = this.getDocumentSelectors();
|
|
let count = 0;
|
|
for (const selector of selectors) {
|
|
count++;
|
|
for (const document of vscode_1.workspace.textDocuments) {
|
|
if (vscode_1.languages.match(selector, document) > 0) {
|
|
return { kind: 'document', id: this.registrationType.method, registrations: true, matches: true };
|
|
}
|
|
}
|
|
}
|
|
const registrations = count > 0;
|
|
return { kind: 'document', id: this.registrationType.method, registrations, matches: false };
|
|
}
|
|
}
|
|
exports.DynamicDocumentFeature = DynamicDocumentFeature;
|
|
/**
|
|
* An abstract base class to implement features that react to events
|
|
* emitted from text documents.
|
|
*/
|
|
class TextDocumentEventFeature extends DynamicDocumentFeature {
|
|
static textDocumentFilter(selectors, textDocument) {
|
|
for (const selector of selectors) {
|
|
if (vscode_1.languages.match(selector, textDocument) > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
constructor(client, event, type, middleware, createParams, textDocument, selectorFilter) {
|
|
super(client);
|
|
this._event = event;
|
|
this._type = type;
|
|
this._middleware = middleware;
|
|
this._createParams = createParams;
|
|
this._textDocument = textDocument;
|
|
this._selectorFilter = selectorFilter;
|
|
this._selectors = new Map();
|
|
this._onNotificationSent = new vscode_1.EventEmitter();
|
|
}
|
|
getStateInfo() {
|
|
return [this._selectors.values(), false];
|
|
}
|
|
getDocumentSelectors() {
|
|
return this._selectors.values();
|
|
}
|
|
register(data) {
|
|
if (!data.registerOptions.documentSelector) {
|
|
return;
|
|
}
|
|
if (!this._listener) {
|
|
this._listener = this._event((data) => {
|
|
this.callback(data).catch((error) => {
|
|
this._client.error(`Sending document notification ${this._type.method} failed.`, error);
|
|
});
|
|
});
|
|
}
|
|
this._selectors.set(data.id, this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector));
|
|
}
|
|
async callback(data) {
|
|
const doSend = async (data) => {
|
|
const params = this._createParams(data);
|
|
await this._client.sendNotification(this._type, params);
|
|
this.notificationSent(this.getTextDocument(data), this._type, params);
|
|
};
|
|
if (this.matches(data)) {
|
|
const middleware = this._middleware();
|
|
return middleware ? middleware(data, (data) => doSend(data)) : doSend(data);
|
|
}
|
|
}
|
|
matches(data) {
|
|
if (this._client.hasDedicatedTextSynchronizationFeature(this._textDocument(data))) {
|
|
return false;
|
|
}
|
|
return !this._selectorFilter || this._selectorFilter(this._selectors.values(), data);
|
|
}
|
|
get onNotificationSent() {
|
|
return this._onNotificationSent.event;
|
|
}
|
|
notificationSent(textDocument, type, params) {
|
|
this._onNotificationSent.fire({ textDocument, type, params });
|
|
}
|
|
unregister(id) {
|
|
this._selectors.delete(id);
|
|
if (this._selectors.size === 0 && this._listener) {
|
|
this._listener.dispose();
|
|
this._listener = undefined;
|
|
}
|
|
}
|
|
clear() {
|
|
this._selectors.clear();
|
|
this._onNotificationSent.dispose();
|
|
if (this._listener) {
|
|
this._listener.dispose();
|
|
this._listener = undefined;
|
|
}
|
|
}
|
|
getProvider(document) {
|
|
for (const selector of this._selectors.values()) {
|
|
if (vscode_1.languages.match(selector, document) > 0) {
|
|
return {
|
|
send: (data) => {
|
|
return this.callback(data);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|
|
exports.TextDocumentEventFeature = TextDocumentEventFeature;
|
|
/**
|
|
* A abstract feature implementation that registers language providers
|
|
* for text documents using a given document selector.
|
|
*/
|
|
class TextDocumentLanguageFeature extends DynamicDocumentFeature {
|
|
constructor(client, registrationType) {
|
|
super(client);
|
|
this._registrationType = registrationType;
|
|
this._registrations = new Map();
|
|
}
|
|
*getDocumentSelectors() {
|
|
for (const registration of this._registrations.values()) {
|
|
const selector = registration.data.registerOptions.documentSelector;
|
|
if (selector === null) {
|
|
continue;
|
|
}
|
|
yield this._client.protocol2CodeConverter.asDocumentSelector(selector);
|
|
}
|
|
}
|
|
get registrationType() {
|
|
return this._registrationType;
|
|
}
|
|
register(data) {
|
|
if (!data.registerOptions.documentSelector) {
|
|
return;
|
|
}
|
|
let registration = this.registerLanguageProvider(data.registerOptions, data.id);
|
|
this._registrations.set(data.id, { disposable: registration[0], data, provider: registration[1] });
|
|
}
|
|
unregister(id) {
|
|
let registration = this._registrations.get(id);
|
|
if (registration !== undefined) {
|
|
registration.disposable.dispose();
|
|
}
|
|
}
|
|
clear() {
|
|
this._registrations.forEach((value) => {
|
|
value.disposable.dispose();
|
|
});
|
|
this._registrations.clear();
|
|
}
|
|
getRegistration(documentSelector, capability) {
|
|
if (!capability) {
|
|
return [undefined, undefined];
|
|
}
|
|
else if (vscode_languageserver_protocol_1.TextDocumentRegistrationOptions.is(capability)) {
|
|
const id = vscode_languageserver_protocol_1.StaticRegistrationOptions.hasId(capability) ? capability.id : UUID.generateUuid();
|
|
const selector = capability.documentSelector ?? documentSelector;
|
|
if (selector) {
|
|
return [id, Object.assign({}, capability, { documentSelector: selector })];
|
|
}
|
|
}
|
|
else if (Is.boolean(capability) && capability === true || vscode_languageserver_protocol_1.WorkDoneProgressOptions.is(capability)) {
|
|
if (!documentSelector) {
|
|
return [undefined, undefined];
|
|
}
|
|
const options = (Is.boolean(capability) && capability === true ? { documentSelector } : Object.assign({}, capability, { documentSelector }));
|
|
return [UUID.generateUuid(), options];
|
|
}
|
|
return [undefined, undefined];
|
|
}
|
|
getRegistrationOptions(documentSelector, capability) {
|
|
if (!documentSelector || !capability) {
|
|
return undefined;
|
|
}
|
|
return (Is.boolean(capability) && capability === true ? { documentSelector } : Object.assign({}, capability, { documentSelector }));
|
|
}
|
|
getProvider(textDocument) {
|
|
for (const registration of this._registrations.values()) {
|
|
let selector = registration.data.registerOptions.documentSelector;
|
|
if (selector !== null && vscode_1.languages.match(this._client.protocol2CodeConverter.asDocumentSelector(selector), textDocument) > 0) {
|
|
return registration.provider;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
getAllProviders() {
|
|
const result = [];
|
|
for (const item of this._registrations.values()) {
|
|
result.push(item.provider);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
exports.TextDocumentLanguageFeature = TextDocumentLanguageFeature;
|
|
class WorkspaceFeature {
|
|
constructor(client, registrationType) {
|
|
this._client = client;
|
|
this._registrationType = registrationType;
|
|
this._registrations = new Map();
|
|
}
|
|
getState() {
|
|
const registrations = this._registrations.size > 0;
|
|
return { kind: 'workspace', id: this._registrationType.method, registrations };
|
|
}
|
|
get registrationType() {
|
|
return this._registrationType;
|
|
}
|
|
register(data) {
|
|
const registration = this.registerLanguageProvider(data.registerOptions);
|
|
this._registrations.set(data.id, { disposable: registration[0], provider: registration[1] });
|
|
}
|
|
unregister(id) {
|
|
let registration = this._registrations.get(id);
|
|
if (registration !== undefined) {
|
|
registration.disposable.dispose();
|
|
}
|
|
}
|
|
clear() {
|
|
this._registrations.forEach((registration) => {
|
|
registration.disposable.dispose();
|
|
});
|
|
this._registrations.clear();
|
|
}
|
|
getProviders() {
|
|
const result = [];
|
|
for (const registration of this._registrations.values()) {
|
|
result.push(registration.provider);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
exports.WorkspaceFeature = WorkspaceFeature;
|