From aee37b8a5200b6e388cf0c59b662a179d95e8b9d Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:17:22 +0200 Subject: [PATCH 1/4] Add ConfigService to server --- server/src/configService.ts | 81 +++++++++++++++++++++++++++++++++++++ server/src/server.ts | 5 +++ 2 files changed, 86 insertions(+) create mode 100644 server/src/configService.ts diff --git a/server/src/configService.ts b/server/src/configService.ts new file mode 100644 index 0000000..6b6a30e --- /dev/null +++ b/server/src/configService.ts @@ -0,0 +1,81 @@ +import { + ClientCapabilities, + Connection, + DidChangeConfigurationNotification, + DidChangeConfigurationParams, +} from "vscode-languageserver"; + +/** Represents all the available settings of the extension. */ +interface ExtensionSettings { + cleaning: CleaningSettings; +} + +/** Contains settings for workflow cleaning. */ +interface CleaningSettings { + /** A list of property names that will be removed from the workflow document when cleaning. */ + cleanableProperties: string[]; +} + +const defaultSettings: ExtensionSettings = { + cleaning: { + cleanableProperties: ["position", "uuid", "errors", "version"], + }, +}; + +let globalSettings: ExtensionSettings = defaultSettings; + +// Cache the settings of all open documents +const documentSettingsCache: Map = new Map(); + +export class ConfigService { + protected hasConfigurationCapability = false; + + constructor(public readonly connection: Connection) { + this.connection.onInitialized(() => this.onInitialized()); + this.connection.onDidChangeConfiguration((params) => this.onDidChangeConfiguration(params)); + } + + public initialize(capabilities: ClientCapabilities): void { + this.hasConfigurationCapability = !!(capabilities.workspace && !!capabilities.workspace.configuration); + } + + public async getDocumentSettings(uri: string): Promise { + if (!this.hasConfigurationCapability) { + return Promise.resolve(globalSettings); + } + let result = documentSettingsCache.get(uri); + if (!result) { + result = await this.connection.workspace.getConfiguration({ + scopeUri: uri, + section: "galaxyWorkflows", + }); + result = result || globalSettings; + this.addToDocumentConfigCache(uri, result); + } + return result; + } + + public onDocumentClose(uri: string): void { + documentSettingsCache.delete(uri); + } + + private onInitialized(): void { + if (this.hasConfigurationCapability) { + this.connection.client.register(DidChangeConfigurationNotification.type); + } + } + + private onDidChangeConfiguration(params: DidChangeConfigurationParams): void { + if (this.hasConfigurationCapability) { + // Reset all cached document settings + documentSettingsCache.clear(); + } else { + globalSettings = (params.settings.galaxyWorkflows || defaultSettings); + } + } + + private addToDocumentConfigCache(uri: string, settings: ExtensionSettings): void { + if (uri.startsWith("temp")) return; // Do not cache config from temp files + documentSettingsCache.set(uri, settings); + } +} diff --git a/server/src/server.ts b/server/src/server.ts index 696604c..6a8608d 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -14,15 +14,18 @@ import { FormattingProvider } from "./providers/formattingProvider"; import { HoverProvider } from "./providers/hover/hoverProvider"; // import { DebugHoverContentContributor } from "./providers/hover/debugHoverContentContributor"; import { CompletionProvider } from "./providers/completionProvider"; +import { ConfigService } from "./configService"; export class GalaxyWorkflowLanguageServer { public readonly languageService: WorkflowLanguageService; + public readonly configService: ConfigService; public readonly documents = new TextDocuments(TextDocument); public readonly workflowDocuments = new WorkflowDocuments(); protected workspaceFolders: WorkspaceFolder[] | null | undefined; constructor(public readonly connection: Connection, languageService: WorkflowLanguageService) { this.languageService = languageService; + this.configService = new ConfigService(connection); // Track open, change and close text document events this.trackDocumentChanges(connection); @@ -40,6 +43,7 @@ export class GalaxyWorkflowLanguageServer { } private async initialize(params: InitializeParams): Promise { + this.configService.initialize(params.capabilities); this.workspaceFolders = params.workspaceFolders; const capabilities: ServerCapabilities = { @@ -87,6 +91,7 @@ export class GalaxyWorkflowLanguageServer { private onDidClose(textDocument: TextDocument): void { this.workflowDocuments.removeWorkflowDocument(textDocument.uri); + this.configService.onDocumentClose(textDocument.uri); this.clearValidation(textDocument); } From 711322414055a344888ddeea6b336c9ea40d815d Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:18:19 +0200 Subject: [PATCH 2/4] Add cleanableProperties setting to extension settings --- package.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/package.json b/package.json index 53ab707..d2c263a 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,27 @@ "path": "./workflow-languages/syntaxes/yml.tmLanguage.json" } ], + "configuration": [ + { + "title": "Galaxy Workflows", + "properties": { + "galaxyWorkflows.cleaning.cleanableProperties": { + "markdownDescription": "These properties will be removed from the workflow document when *cleaning* (or *clean comparing*) workflows.", + "scope": "resource", + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "position", + "uuid", + "errors", + "version" + ] + } + } + } + ], "commands": [ { "command": "galaxy-workflows.previewCleanWorkflow", From f5a1f11d4525c5f865894311cd503eed4f5db268 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:20:05 +0200 Subject: [PATCH 3/4] Replace hardcoded cleanable properties Use the user's settings instead. --- server/src/commands/cleanWorkflow.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/server/src/commands/cleanWorkflow.ts b/server/src/commands/cleanWorkflow.ts index 15307ea..0a8de78 100644 --- a/server/src/commands/cleanWorkflow.ts +++ b/server/src/commands/cleanWorkflow.ts @@ -12,12 +12,6 @@ import { CleanWorkflowDocumentResult, } from "./requestsDefinitions"; -/** - * A set of property names that are unrelated to the workflow logic. - * Usually used by other tools like the workflow editor. - */ -const CLEANABLE_PROPERTY_NAMES = new Set(["position", "uuid", "errors", "version"]); - /** * Command for handling workflow `cleaning` requests. * Supports both, direct contents (raw document text), and document uri requests @@ -72,7 +66,8 @@ export class CleanWorkflowCommand extends CustomCommand { try { const workflowDocument = this.workflowDocuments.get(params.uri); if (workflowDocument) { - const edits = this.getTextEditsToCleanWorkflow(workflowDocument); + const settings = await this.server.configService.getDocumentSettings(workflowDocument.textDocument.uri); + const edits = this.getTextEditsToCleanWorkflow(workflowDocument, settings.cleaning.cleanableProperties); const editParams: ApplyWorkspaceEditParams = { label: "Clean workflow", edit: { @@ -95,8 +90,11 @@ export class CleanWorkflowCommand extends CustomCommand { } } - private getTextEditsToCleanWorkflow(workflowDocument: WorkflowDocument): TextEdit[] { - const nodesToRemove = this.getNonEssentialNodes(workflowDocument, CLEANABLE_PROPERTY_NAMES); + private getTextEditsToCleanWorkflow( + workflowDocument: WorkflowDocument, + cleanablePropertyNames: string[] + ): TextEdit[] { + const nodesToRemove = this.getNonEssentialNodes(workflowDocument, cleanablePropertyNames); const changes: TextEdit[] = []; nodesToRemove.forEach((node) => { const range = this.getFullNodeRange(workflowDocument.textDocument, node); @@ -127,7 +125,8 @@ export class CleanWorkflowCommand extends CustomCommand { } private async cleanWorkflowContentsResult(workflowDocument: WorkflowDocument): Promise { - const nodesToRemove = this.getNonEssentialNodes(workflowDocument, CLEANABLE_PROPERTY_NAMES); + const settings = await this.server.configService.getDocumentSettings(workflowDocument.textDocument.uri); + const nodesToRemove = this.getNonEssentialNodes(workflowDocument, settings.cleaning.cleanableProperties); const contents = this.getCleanContents(workflowDocument.textDocument.getText(), nodesToRemove.reverse()); const result: CleanWorkflowContentsResult = { contents: contents, @@ -135,7 +134,7 @@ export class CleanWorkflowCommand extends CustomCommand { return result; } - private getNonEssentialNodes(workflowDocument: WorkflowDocument, cleanablePropertyNames: Set): ASTNode[] { + private getNonEssentialNodes(workflowDocument: WorkflowDocument, cleanablePropertyNames: string[]): ASTNode[] { const root = workflowDocument.rootNode; if (!root) { return []; @@ -144,6 +143,7 @@ export class CleanWorkflowCommand extends CustomCommand { const toVisit: { node: ASTNode }[] = [{ node: root }]; let nextToVisit = 0; + const cleanablePropertyNamesSet = new Set(cleanablePropertyNames); const collectNonEssentialProperties = (node: ASTNode): void => { if (node.type === "array") { node.items.forEach((node) => { @@ -154,7 +154,7 @@ export class CleanWorkflowCommand extends CustomCommand { } else if (node.type === "object") { node.properties.forEach((property: PropertyASTNode) => { const key = property.keyNode.value; - if (cleanablePropertyNames.has(key)) { + if (cleanablePropertyNamesSet.has(key)) { result.push(property); } if (property.valueNode) { From 0824d7cf5e597151314c6858da75a850134c0418 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:49:31 +0200 Subject: [PATCH 4/4] Rename server commands to services The concept of command is more attached to the client, the server will just 'serve' a request from the client. --- server/src/server.ts | 8 ++++---- server/src/{commands => services}/cleanWorkflow.ts | 10 +++++----- server/src/{commands => services}/common.ts | 4 ++-- .../src/{commands => services}/requestsDefinitions.ts | 0 4 files changed, 11 insertions(+), 11 deletions(-) rename server/src/{commands => services}/cleanWorkflow.ts (97%) rename server/src/{commands => services}/common.ts (75%) rename server/src/{commands => services}/requestsDefinitions.ts (100%) diff --git a/server/src/server.ts b/server/src/server.ts index 6a8608d..66f0abc 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -6,7 +6,7 @@ import { TextDocuments, WorkspaceFolder, } from "vscode-languageserver"; -import { CleanWorkflowCommand } from "./commands/cleanWorkflow"; +import { CleanWorkflowService } from "./services/cleanWorkflow"; import { WorkflowLanguageService, TextDocument, WorkflowDocument } from "./languageTypes"; import { WorkflowDocuments } from "./models/workflowDocuments"; import { SymbolsProvider } from "./providers/symbolsProvider"; @@ -33,7 +33,7 @@ export class GalaxyWorkflowLanguageServer { this.registerProviders(); - this.registerCommands(); + this.registerServices(); this.connection.onShutdown(() => this.cleanup()); } @@ -70,8 +70,8 @@ export class GalaxyWorkflowLanguageServer { CompletionProvider.register(this); } - private registerCommands(): void { - CleanWorkflowCommand.register(this); + private registerServices(): void { + CleanWorkflowService.register(this); } private trackDocumentChanges(connection: Connection): void { diff --git a/server/src/commands/cleanWorkflow.ts b/server/src/services/cleanWorkflow.ts similarity index 97% rename from server/src/commands/cleanWorkflow.ts rename to server/src/services/cleanWorkflow.ts index 0a8de78..8810f47 100644 --- a/server/src/commands/cleanWorkflow.ts +++ b/server/src/services/cleanWorkflow.ts @@ -2,7 +2,7 @@ import { ApplyWorkspaceEditParams, Range, TextDocumentEdit, TextEdit } from "vsc import { TextDocument } from "vscode-languageserver-textdocument"; import { ASTNode, PropertyASTNode, WorkflowDocument } from "../languageTypes"; import { GalaxyWorkflowLanguageServer } from "../server"; -import { CustomCommand } from "./common"; +import { ServiceBase } from "./common"; import { CleanWorkflowContentsParams, CleanWorkflowContentsRequest, @@ -13,15 +13,15 @@ import { } from "./requestsDefinitions"; /** - * Command for handling workflow `cleaning` requests. + * Service for handling workflow `cleaning` requests. * Supports both, direct contents (raw document text), and document uri requests * for cleaning. * When requesting with a document uri, the workflow document must be already registered in the server * as a workflow document. */ -export class CleanWorkflowCommand extends CustomCommand { - public static register(server: GalaxyWorkflowLanguageServer): CleanWorkflowCommand { - return new CleanWorkflowCommand(server); +export class CleanWorkflowService extends ServiceBase { + public static register(server: GalaxyWorkflowLanguageServer): CleanWorkflowService { + return new CleanWorkflowService(server); } constructor(server: GalaxyWorkflowLanguageServer) { diff --git a/server/src/commands/common.ts b/server/src/services/common.ts similarity index 75% rename from server/src/commands/common.ts rename to server/src/services/common.ts index 7915133..83bb32d 100644 --- a/server/src/commands/common.ts +++ b/server/src/services/common.ts @@ -1,7 +1,7 @@ import { ServerContext } from "../languageTypes"; import { GalaxyWorkflowLanguageServer } from "../server"; -export abstract class CustomCommand extends ServerContext { +export abstract class ServiceBase extends ServerContext { constructor(server: GalaxyWorkflowLanguageServer) { super(server); this.listenToRequests(); @@ -9,7 +9,7 @@ export abstract class CustomCommand extends ServerContext { /** * This method should call `this.connection.onRequest` to register - * the proper callback for this command request. + * the proper callback for this service request. */ protected abstract listenToRequests(): void; } diff --git a/server/src/commands/requestsDefinitions.ts b/server/src/services/requestsDefinitions.ts similarity index 100% rename from server/src/commands/requestsDefinitions.ts rename to server/src/services/requestsDefinitions.ts