Skip to content

Commit

Permalink
Merge pull request #40 from davelopez/add_setting_cleanable_props
Browse files Browse the repository at this point in the history
Make `cleanable` properties user configurable
  • Loading branch information
davelopez authored Jun 2, 2022
2 parents fb8fb55 + 0824d7c commit 85e8164
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 23 deletions.
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
81 changes: 81 additions & 0 deletions server/src/configService.ts
Original file line number Diff line number Diff line change
@@ -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<string, ExtensionSettings> = 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<ExtensionSettings> {
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 = <ExtensionSettings>(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);
}
}
13 changes: 9 additions & 4 deletions server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,34 @@ 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";
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);

this.connection.onInitialize((params) => this.initialize(params));

this.registerProviders();

this.registerCommands();
this.registerServices();

this.connection.onShutdown(() => this.cleanup());
}
Expand All @@ -40,6 +43,7 @@ export class GalaxyWorkflowLanguageServer {
}

private async initialize(params: InitializeParams): Promise<InitializeResult> {
this.configService.initialize(params.capabilities);
this.workspaceFolders = params.workspaceFolders;

const capabilities: ServerCapabilities = {
Expand All @@ -66,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 {
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -13,21 +13,15 @@ import {
} 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.
* 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) {
Expand Down Expand Up @@ -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: {
Expand All @@ -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);
Expand Down Expand Up @@ -127,15 +125,16 @@ export class CleanWorkflowCommand extends CustomCommand {
}

private async cleanWorkflowContentsResult(workflowDocument: WorkflowDocument): Promise<CleanWorkflowContentsResult> {
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,
};
return result;
}

private getNonEssentialNodes(workflowDocument: WorkflowDocument, cleanablePropertyNames: Set<string>): ASTNode[] {
private getNonEssentialNodes(workflowDocument: WorkflowDocument, cleanablePropertyNames: string[]): ASTNode[] {
const root = workflowDocument.rootNode;
if (!root) {
return [];
Expand All @@ -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) => {
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
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();
}

/**
* 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;
}
File renamed without changes.

0 comments on commit 85e8164

Please sign in to comment.