diff --git a/.vscode/launch.json b/.vscode/launch.json index 47c0133..5635930 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -34,13 +34,21 @@ } }, { - "name": "Attach to Server", + "name": "Attach to Native Server", "type": "node", "request": "attach", "port": 6009, - "restart": true, "sourceMapPathOverrides": { - "webpack://?:*/*": "${workspaceFolder}/server/*" + "webpack://?:*/*": "${workspaceFolder}/server/gx-workflow-ls-native/*" + } + }, + { + "name": "Attach to gxformat2 Server", + "type": "node", + "request": "attach", + "port": 6010, + "sourceMapPathOverrides": { + "webpack://?:*/*": "${workspaceFolder}/server/gx-workflow-ls-format2/*" } }, { @@ -74,8 +82,8 @@ ], "compounds": [ { - "name": "Debug Extension + Server", - "configurations": ["Launch Extension", "Attach to Server"], + "name": "Debug Extension + Servers", + "configurations": ["Launch Extension", "Attach to Native Server", "Attach to gxformat2 Server"], "presentation": { "hidden": false, "group": "", diff --git a/.vscode/settings.json b/.vscode/settings.json index 83d481a..b02afd5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,6 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, - "gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"] + "gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"], + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/client/src/browser/extension.ts b/client/src/browser/extension.ts index b788a95..77f4571 100644 --- a/client/src/browser/extension.ts +++ b/client/src/browser/extension.ts @@ -1,21 +1,29 @@ import { ExtensionContext, Uri } from "vscode"; import { LanguageClientOptions } from "vscode-languageclient"; import { LanguageClient } from "vscode-languageclient/browser"; -import { buildLanguageClientOptions, initExtension } from "../common"; +import { buildBasicLanguageClientOptions, initExtension } from "../common"; +import { Constants } from "../common/constants"; export function activate(context: ExtensionContext): void { - const client = buildWebLanguageClient(context); + const nativeLanguageClient = createWebWorkerLanguageClient( + Constants.NATIVE_WORKFLOW_LANGUAGE_ID, + Uri.joinPath(context.extensionUri, "server/gx-workflow-ls-native/dist/web/nativeServer.js") + ); + const gxFormat2LanguageClient = createWebWorkerLanguageClient( + Constants.GXFORMAT2_WORKFLOW_LANGUAGE_ID, + Uri.joinPath(context.extensionUri, "server/gx-workflow-ls-format2/dist/web/gxFormat2Server.js") + ); - initExtension(context, client); + initExtension(context, nativeLanguageClient, gxFormat2LanguageClient); } export function deactivate(): void { // Nothing to do yet } -function buildWebLanguageClient(context: ExtensionContext): LanguageClient { - const clientOptions: LanguageClientOptions = buildLanguageClientOptions(); - const serverPath = Uri.joinPath(context.extensionUri, "server/dist/web/nativeServer.js"); - const worker = new Worker(serverPath.toString()); - return new LanguageClient("galaxy-workflow-language-client-native", "Galaxy Workflows LS", clientOptions, worker); +function createWebWorkerLanguageClient(languageId: string, serverUri: Uri): LanguageClient { + const documentSelector = [{ language: languageId }]; + const clientOptions: LanguageClientOptions = buildBasicLanguageClientOptions(documentSelector); + const worker = new Worker(serverUri.toString()); + return new LanguageClient(`${languageId}-language-client`, `Galaxy Workflows (${languageId})`, clientOptions, worker); } diff --git a/client/src/commands/cleanWorkflow.ts b/client/src/commands/cleanWorkflow.ts index 3f47f44..74a8e23 100644 --- a/client/src/commands/cleanWorkflow.ts +++ b/client/src/commands/cleanWorkflow.ts @@ -1,6 +1,6 @@ import { window } from "vscode"; -import { CleanWorkflowDocumentParams, CleanWorkflowDocumentRequest } from "../requestsDefinitions"; -import { CustomCommand, getCommandFullIdentifier } from "./common"; +import { CleanWorkflowDocumentParams, CleanWorkflowDocumentRequest } from "../common/requestsDefinitions"; +import { CustomCommand, getCommandFullIdentifier } from "."; /** * Command to 'clean' the selected workflow document. diff --git a/client/src/commands/compareCleanWith.ts b/client/src/commands/compareCleanWith.ts index da1b182..207eff6 100644 --- a/client/src/commands/compareCleanWith.ts +++ b/client/src/commands/compareCleanWith.ts @@ -2,8 +2,8 @@ import { commands, Uri } from "vscode"; import { CommonLanguageClient } from "vscode-languageclient"; import { toCleanWorkflowUri } from "../providers/cleanWorkflowDocumentProvider"; import { CleanWorkflowProvider } from "../providers/cleanWorkflowProvider"; -import { addRefToUri } from "../utils"; -import { ComparableWorkflow, ComparableWorkflowProvider, CustomCommand, getCommandFullIdentifier } from "./common"; +import { addRefToUri } from "../common/utils"; +import { ComparableWorkflow, ComparableWorkflowProvider, CustomCommand, getCommandFullIdentifier } from "."; /** * Compares (diff) a previously selected workflow document revision with diff --git a/client/src/commands/common.ts b/client/src/commands/index.ts similarity index 100% rename from client/src/commands/common.ts rename to client/src/commands/index.ts diff --git a/client/src/commands/previewCleanWorkflow.ts b/client/src/commands/previewCleanWorkflow.ts index a8a311c..57a6f48 100644 --- a/client/src/commands/previewCleanWorkflow.ts +++ b/client/src/commands/previewCleanWorkflow.ts @@ -1,6 +1,6 @@ import { window, workspace } from "vscode"; import { toCleanWorkflowUri } from "../providers/cleanWorkflowDocumentProvider"; -import { CustomCommand, getCommandFullIdentifier } from "./common"; +import { CustomCommand, getCommandFullIdentifier } from "."; /** * Command to display a 'clean' version of the selected workflow document. diff --git a/client/src/commands/selectForCleanCompare.ts b/client/src/commands/selectForCleanCompare.ts index 524865a..ee74148 100644 --- a/client/src/commands/selectForCleanCompare.ts +++ b/client/src/commands/selectForCleanCompare.ts @@ -1,5 +1,5 @@ import { commands } from "vscode"; -import { ComparableWorkflow, ComparableWorkflowProvider, CustomCommand, getCommandFullIdentifier } from "./common"; +import { ComparableWorkflow, ComparableWorkflowProvider, CustomCommand, getCommandFullIdentifier } from "."; /** * Command to select a workflow for comparison with another. This workflow will diff --git a/client/src/commands/setup.ts b/client/src/commands/setup.ts index af87018..94de3d8 100644 --- a/client/src/commands/setup.ts +++ b/client/src/commands/setup.ts @@ -4,7 +4,7 @@ import { CompareCleanWithWorkflowsCommand } from "./compareCleanWith"; import { SelectForCleanCompareCommand } from "./selectForCleanCompare"; import { PreviewCleanWorkflowCommand } from "./previewCleanWorkflow"; import { CleanWorkflowProvider } from "../providers/cleanWorkflowProvider"; -import { GitProvider } from "../providers/git/common"; +import { GitProvider } from "../providers/git"; import { CleanWorkflowCommand } from "./cleanWorkflow"; /** diff --git a/client/src/common.ts b/client/src/common.ts deleted file mode 100644 index d0cd637..0000000 --- a/client/src/common.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { commands, ExtensionContext } from "vscode"; -import { CommonLanguageClient, LanguageClientOptions } from "vscode-languageclient"; -import { setupCommands } from "./commands/setup"; -import { Constants } from "./constants"; -import { CleanWorkflowDocumentProvider } from "./providers/cleanWorkflowDocumentProvider"; -import { CleanWorkflowProvider } from "./providers/cleanWorkflowProvider"; -import { GitProvider } from "./providers/git/common"; -import { BuiltinGitProvider } from "./providers/git/gitProvider"; - -export function buildLanguageClientOptions(): LanguageClientOptions { - const documentSelector = [{ language: Constants.NATIVE_WORKFLOW_LANGUAGE_ID }]; - - // Options to control the language client - const clientOptions: LanguageClientOptions = { - documentSelector, - synchronize: {}, - initializationOptions: {}, - }; - return clientOptions; -} - -export function initExtension(context: ExtensionContext, client: CommonLanguageClient): void { - const gitProvider = new BuiltinGitProvider(); - setupProviders(context, client, gitProvider); - setupCommands(context, client, gitProvider); - - const disposable = client.start(); - context.subscriptions.push(disposable); - - gitProvider.initialize().then(() => { - commands.executeCommand("setContext", "galaxy-workflows.gitProviderInitialized", gitProvider.isInitialized); - console.log(`${context.extension.id} Git initialized is ${gitProvider.isInitialized}.`); - }); - - client.onReady().then(() => { - console.log(`${context.extension.id} server is ready.`); - }); -} - -function setupProviders(context: ExtensionContext, client: CommonLanguageClient, gitProvider: GitProvider): void { - const cleanWorkflowProvider = new CleanWorkflowProvider(client, gitProvider); - CleanWorkflowDocumentProvider.register(context, cleanWorkflowProvider); -} diff --git a/client/src/constants.ts b/client/src/common/constants.ts similarity index 78% rename from client/src/constants.ts rename to client/src/common/constants.ts index 69129a3..c2b0d8a 100644 --- a/client/src/constants.ts +++ b/client/src/common/constants.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-namespace */ export namespace Constants { export const NATIVE_WORKFLOW_LANGUAGE_ID = "galaxyworkflow"; + export const GXFORMAT2_WORKFLOW_LANGUAGE_ID = "gxformat2"; export const CLEAN_WORKFLOW_DOCUMENT_SCHEME = "galaxy-clean-workflow"; } diff --git a/client/src/common/index.ts b/client/src/common/index.ts new file mode 100644 index 0000000..2a2df95 --- /dev/null +++ b/client/src/common/index.ts @@ -0,0 +1,56 @@ +import { commands, ExtensionContext } from "vscode"; +import { CommonLanguageClient, DocumentSelector, LanguageClientOptions } from "vscode-languageclient"; +import { setupCommands } from "../commands/setup"; +import { CleanWorkflowDocumentProvider } from "../providers/cleanWorkflowDocumentProvider"; +import { CleanWorkflowProvider } from "../providers/cleanWorkflowProvider"; +import { GitProvider } from "../providers/git"; +import { BuiltinGitProvider } from "../providers/git/gitProvider"; + +export function buildBasicLanguageClientOptions(documentSelector: DocumentSelector): LanguageClientOptions { + // Options to control the language client + const clientOptions: LanguageClientOptions = { + documentSelector, + synchronize: {}, + initializationOptions: {}, + }; + return clientOptions; +} + +export function initExtension( + context: ExtensionContext, + nativeClient: CommonLanguageClient, + gxFormat2Client: CommonLanguageClient +): void { + const gitProvider = initGitProvider(context); + + // Setup native workflow language features + setupProviders(context, nativeClient, gitProvider); + setupCommands(context, nativeClient, gitProvider); + startLanguageClient(context, nativeClient); + + // Setup gxformat2 language features + startLanguageClient(context, gxFormat2Client); +} + +function initGitProvider(context: ExtensionContext): BuiltinGitProvider { + const gitProvider = new BuiltinGitProvider(); + gitProvider.initialize().then(() => { + commands.executeCommand("setContext", "galaxy-workflows.gitProviderInitialized", gitProvider.isInitialized); + console.log(`${context.extension.id} Git initialized is ${gitProvider.isInitialized}.`); + }); + return gitProvider; +} + +function startLanguageClient(context: ExtensionContext, languageClient: CommonLanguageClient): void { + const disposable = languageClient.start(); + context.subscriptions.push(disposable); + + languageClient.onReady().then(() => { + console.log(`${context.extension.id} ${languageClient.outputChannel.name} server is ready.`); + }); +} + +function setupProviders(context: ExtensionContext, client: CommonLanguageClient, gitProvider: GitProvider): void { + const cleanWorkflowProvider = new CleanWorkflowProvider(client, gitProvider); + CleanWorkflowDocumentProvider.register(context, cleanWorkflowProvider); +} diff --git a/client/src/requestsDefinitions.ts b/client/src/common/requestsDefinitions.ts similarity index 100% rename from client/src/requestsDefinitions.ts rename to client/src/common/requestsDefinitions.ts diff --git a/client/src/utils.ts b/client/src/common/utils.ts similarity index 100% rename from client/src/utils.ts rename to client/src/common/utils.ts diff --git a/client/src/extension.ts b/client/src/extension.ts index d040221..07ed1ec 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,36 +1,58 @@ import * as path from "path"; import { ExtensionContext } from "vscode"; -import { LanguageClientOptions } from "vscode-languageclient"; +import { integer, LanguageClientOptions } from "vscode-languageclient"; import { LanguageClient, ServerOptions, TransportKind } from "vscode-languageclient/node"; -import { buildLanguageClientOptions, initExtension } from "./common"; +import { buildBasicLanguageClientOptions, initExtension } from "./common"; +import { Constants } from "./common/constants"; export function activate(context: ExtensionContext): void { - const client = buildLanguageClient(context); + const nativeLanguageClient = buildNodeLanguageClient( + Constants.NATIVE_WORKFLOW_LANGUAGE_ID, + buildNativeServerOptions(context) + ); + const gxFormat2LanguageClient = buildNodeLanguageClient( + Constants.GXFORMAT2_WORKFLOW_LANGUAGE_ID, + buildGxFormat2ServerOptions(context) + ); - initExtension(context, client); + initExtension(context, nativeLanguageClient, gxFormat2LanguageClient); } export function deactivate(): void { // Nothing to do yet } -function buildLanguageClient(context: ExtensionContext): LanguageClient { - const clientOptions: LanguageClientOptions = buildLanguageClientOptions(); - const serverOptions: ServerOptions = buildServerOptions(context); +function buildNodeLanguageClient(languageId: string, serverOptions: ServerOptions): LanguageClient { + const documentSelector = [{ language: languageId }]; + const clientOptions: LanguageClientOptions = buildBasicLanguageClientOptions(documentSelector); return new LanguageClient( - "galaxy-workflow-language-client-native", - "Galaxy Workflows LS", + `${languageId}-language-client`, + `Galaxy Workflows (${languageId})`, serverOptions, clientOptions ); } -function buildServerOptions(context: ExtensionContext): ServerOptions { +function buildNativeServerOptions(context: ExtensionContext): ServerOptions { + // The server is implemented in node + const serverModule = context.asAbsolutePath(path.join("server", "gx-workflow-ls-native", "dist", "nativeServer.js")); + const debugPort = 6009; + return buildBasicNodeServerOptions(serverModule, debugPort); +} + +function buildGxFormat2ServerOptions(context: ExtensionContext): ServerOptions { // The server is implemented in node - const serverModule = context.asAbsolutePath(path.join("server", "dist", "nativeServer.js")); + const serverModule = context.asAbsolutePath( + path.join("server", "gx-workflow-ls-format2", "dist", "gxFormat2Server.js") + ); + const debugPort = 6010; + return buildBasicNodeServerOptions(serverModule, debugPort); +} + +function buildBasicNodeServerOptions(serverModule: string, debugPort: integer): ServerOptions { // The debug options for the server - // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging - const debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; + // --inspect=: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging + const debugOptions = { execArgv: ["--nolazy", `--inspect=${debugPort}`] }; // If the extension is launched in debug mode then the debug server options are used // Otherwise the run options are used diff --git a/client/src/providers/cleanWorkflowDocumentProvider.ts b/client/src/providers/cleanWorkflowDocumentProvider.ts index 19c9e72..e57e0fc 100644 --- a/client/src/providers/cleanWorkflowDocumentProvider.ts +++ b/client/src/providers/cleanWorkflowDocumentProvider.ts @@ -1,5 +1,5 @@ import { EventEmitter, ExtensionContext, TextDocumentContentProvider, Uri, workspace } from "vscode"; -import { Constants } from "../constants"; +import { Constants } from "../common/constants"; import { CleanWorkflowProvider } from "./cleanWorkflowProvider"; /** diff --git a/client/src/providers/cleanWorkflowProvider.ts b/client/src/providers/cleanWorkflowProvider.ts index 8872e36..78cc5c6 100644 --- a/client/src/providers/cleanWorkflowProvider.ts +++ b/client/src/providers/cleanWorkflowProvider.ts @@ -1,8 +1,8 @@ import { Uri, window, workspace } from "vscode"; import { CommonLanguageClient } from "vscode-languageclient"; -import { CleanWorkflowContentsParams, CleanWorkflowContentsRequest } from "../requestsDefinitions"; -import { getWorkspaceScheme, replaceUriScheme } from "../utils"; -import { GitProvider } from "./git/common"; +import { CleanWorkflowContentsParams, CleanWorkflowContentsRequest } from "../common/requestsDefinitions"; +import { getWorkspaceScheme, replaceUriScheme } from "../common/utils"; +import { GitProvider } from "./git"; /** * Provides utilities to clean (remove non-workflow logic related parts) diff --git a/client/src/providers/git/gitProvider.ts b/client/src/providers/git/gitProvider.ts index 201be68..da35510 100644 --- a/client/src/providers/git/gitProvider.ts +++ b/client/src/providers/git/gitProvider.ts @@ -1,6 +1,6 @@ import { Extension, extensions, Uri } from "vscode"; import { GitExtension, API as GitAPI } from "../../@types/git"; -import { GitProvider } from "./common"; +import { GitProvider } from "."; /** * Implementation of a GitProvider using the `vscode.git` extension. diff --git a/client/src/providers/git/common.ts b/client/src/providers/git/index.ts similarity index 100% rename from client/src/providers/git/common.ts rename to client/src/providers/git/index.ts diff --git a/package-lock.json b/package-lock.json index e94c6d5..9c8c5b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "galaxy-workflows", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "galaxy-workflows", - "version": "0.1.0", + "version": "0.2.0", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index 2ea63f4..5f4f2c5 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ { "id": "galaxyworkflow", "aliases": [ - "Galaxy Workflow" + "Galaxy Workflow (JSON)" ], "extensions": [ ".ga" @@ -58,7 +58,7 @@ { "id": "gxformat2", "aliases": [ - "Galaxy Workflow" + "Galaxy Workflow (YAML)" ], "extensions": [ ".yml", @@ -204,15 +204,16 @@ }, "scripts": { "postinstall": "cd client && npm install && cd ../server && npm install && cd ..", - "lint": "eslint ./client/src ./server/src --ext .ts,.tsx", + "lint": "eslint ./client/src ./server --ext .ts,.tsx", "format": "prettier --write .", "clean": "rimraf client/dist && rimraf server/dist", - "compile": "npm run clean && webpack --config ./client/webpack.config.js && webpack --config ./server/webpack.config.js", + "compile-servers": "cd server && npm run compile", + "compile": "npm run clean && webpack --config ./client/webpack.config.js && npm run compile-servers", "vscode:prepublish": "npm run compile", "watch": "concurrently --kill-others \"npm run watch-server\" \"npm run watch-client\"", "watch-server": "cd server && npm run watch", "watch-client": "cd client && npm run watch", - "test": "jest", + "test": "npm run test-unit-client && npm run test-unit-server", "test-unit-client": "cd client && npm run test-unit && cd ..", "test-unit-server": "cd server && npm run test-unit && cd ..", "test-compile": "tsc --project ./client --outDir client/out", @@ -244,5 +245,11 @@ "typescript": "^4.6.3", "webpack": "^5.72.0", "webpack-cli": "^4.9.2" + }, + "__metadata": { + "id": "2306ad21-fa2c-46f9-bba8-a22e32c7cb9a", + "publisherDisplayName": "davelopez", + "publisherId": "b10132ef-3f96-4db4-acb0-d6a0ca1848c7", + "isPreReleaseVersion": false } } diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..9cc5aeb --- /dev/null +++ b/server/README.md @@ -0,0 +1,29 @@ +# Projects in this monorepo + +This monorepo uses [_NPM Workspaces_](https://docs.npmjs.com/cli/v8/using-npm/workspaces) and contains both language servers for Galaxy workflows, `gxformat2` and `native (legacy)` along with some local dependencies. + +To install dependencies in any of the contained projects you need run the `npm install` command in this root directory and provide the `-w` parameter with the name of the target workspace. For example to install a new dependency called `my-dep` for the `gxformat2 language server` you should use: + +``` +npm install my-dep -w gx-workflow-ls-format2 +``` + +## Galaxy Workflow Language Servers + +### gx-workflow-ls-format2 + +This project contains the [LSP](https://microsoft.github.io/language-server-protocol/) implementation for the [gxformat2](https://github.com/galaxyproject/gxformat2) Galaxy workflow format in YAML. + +### gx-workflow-ls-format2 + +This project contains the [LSP](https://microsoft.github.io/language-server-protocol/) implementation for the legacy native (.ga) Galaxy workflow format in JSON. + +## Packages + +### packages/server-common + +This library contains common classes, interfaces and type definitions used by both language server implementations. + +### packages/yaml-language-service + +This library implements a language service to provide basic _smarts_ for YAML documents. It is based and inspired by both the [YAML Language Server](https://github.com/redhat-developer/yaml-language-server) implementation from Red Hat and the [vscode-json-languageservice](https://github.com/microsoft/vscode-json-languageservice) by Microsoft. diff --git a/server/gx-workflow-ls-format2/package.json b/server/gx-workflow-ls-format2/package.json new file mode 100644 index 0000000..9db4126 --- /dev/null +++ b/server/gx-workflow-ls-format2/package.json @@ -0,0 +1,17 @@ +{ + "name": "gx-workflow-ls-format2", + "version": "0.1.0", + "description": "Language server implementation for Galaxy gxformat2 (YAML) workflows.", + "author": "davelopez", + "license": "MIT", + "dependencies": { + "@gxwf/server-common": "*", + "@gxwf/yaml-language-service": "*", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-languageserver": "^7.0.0", + "vscode-uri": "^3.0.3" + }, + "devDependencies": {}, + "scripts": {} +} diff --git a/server/gx-workflow-ls-format2/src/browser/server.ts b/server/gx-workflow-ls-format2/src/browser/server.ts new file mode 100644 index 0000000..8e4d705 --- /dev/null +++ b/server/gx-workflow-ls-format2/src/browser/server.ts @@ -0,0 +1,12 @@ +import { createConnection, BrowserMessageReader, BrowserMessageWriter } from "vscode-languageserver/browser"; +import { GalaxyWorkflowLanguageServer } from "@gxwf/server-common/src/server"; +import { GxFormat2WorkflowLanguageService } from "../languageService"; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +const languageService = new GxFormat2WorkflowLanguageService(); +const server = new GalaxyWorkflowLanguageServer(connection, languageService); +server.start(); diff --git a/server/gx-workflow-ls-format2/src/gxFormat2WorkflowDocument.ts b/server/gx-workflow-ls-format2/src/gxFormat2WorkflowDocument.ts new file mode 100644 index 0000000..2af39ad --- /dev/null +++ b/server/gx-workflow-ls-format2/src/gxFormat2WorkflowDocument.ts @@ -0,0 +1,64 @@ +import { ObjectASTNode } from "vscode-json-languageservice"; +import { TextDocument, Range, Position, ASTNode, WorkflowDocument } from "@gxwf/server-common/src/languageTypes"; + +/** + * This class provides information about a gxformat2 workflow document structure. + */ +export class GxFormat2WorkflowDocument extends WorkflowDocument { + constructor(textDocument: TextDocument) { + super(textDocument); + } + + public get rootNode(): ASTNode | undefined { + return; + } + + public override getNodeAtPosition(position: Position): ASTNode | undefined { + return; + } + + public override getDocumentRange(): Range { + return Range.create(this.textDocument.positionAt(0), this.textDocument.positionAt(1)); + } + + public getNodeRange(node: ASTNode): Range { + return Range.create( + this.textDocument.positionAt(node.offset), + this.textDocument.positionAt(node.offset + node.length) + ); + } + + public getNodeRangeAtPosition(position: Position): Range { + const node = this.getNodeAtPosition(position); + return node ? this.getNodeRange(node) : this.getDefaultRangeAtPosition(position); + } + + public isLastNodeInParent(node: ASTNode): boolean { + const parent = node.parent; + if (!parent || !parent.children) { + return true; // Must be root + } + const lastNode = parent.children[parent.children.length - 1]; + return node === lastNode; + } + + public getPreviousSiblingNode(node: ASTNode): ASTNode | null { + const parent = node.parent; + if (!parent || !parent.children) { + return null; + } + const previousNodeIndex = parent.children.indexOf(node) - 1; + if (previousNodeIndex < 0) { + return null; + } + return parent.children[previousNodeIndex]; + } + + public override getNodeFromPath(path: string): ASTNode | null { + return null; + } + + public override getStepNodes(): ObjectASTNode[] { + return []; + } +} diff --git a/server/gx-workflow-ls-format2/src/languageService.ts b/server/gx-workflow-ls-format2/src/languageService.ts new file mode 100644 index 0000000..923c167 --- /dev/null +++ b/server/gx-workflow-ls-format2/src/languageService.ts @@ -0,0 +1,49 @@ +import { + TextDocument, + Range, + FormattingOptions, + TextEdit, + WorkflowDocument, + WorkflowLanguageService, + Position, + Hover, + CompletionList, + Diagnostic, +} from "@gxwf/server-common/src/languageTypes"; +import { LanguageService, getLanguageService } from "@gxwf/yaml-language-service/src/yamlLanguageService"; +import { GxFormat2WorkflowDocument } from "./gxFormat2WorkflowDocument"; + +/** + * A wrapper around the YAML Language Service to support language features + * for gxformat2 Galaxy workflow files. + */ +export class GxFormat2WorkflowLanguageService extends WorkflowLanguageService { + private _yamlLanguageService: LanguageService; + constructor() { + super(); + this._yamlLanguageService = getLanguageService(); + } + + public override parseWorkflowDocument(document: TextDocument): WorkflowDocument { + return new GxFormat2WorkflowDocument(document); + } + + public override format(document: TextDocument, range: Range, options: FormattingOptions): TextEdit[] { + return []; + } + + public override async doHover(workflowDocument: WorkflowDocument, position: Position): Promise { + return null; + } + + public override async doComplete( + workflowDocument: WorkflowDocument, + position: Position + ): Promise { + return null; + } + + protected override async doValidation(workflowDocument: WorkflowDocument): Promise { + return []; + } +} diff --git a/server/gx-workflow-ls-format2/src/node/server.ts b/server/gx-workflow-ls-format2/src/node/server.ts new file mode 100644 index 0000000..2ec4627 --- /dev/null +++ b/server/gx-workflow-ls-format2/src/node/server.ts @@ -0,0 +1,9 @@ +import { createConnection } from "vscode-languageserver/node"; +import { GalaxyWorkflowLanguageServer } from "@gxwf/server-common/src/server"; +import { GxFormat2WorkflowLanguageService } from "../languageService"; + +const connection = createConnection(); + +const languageService = new GxFormat2WorkflowLanguageService(); +const server = new GalaxyWorkflowLanguageServer(connection, languageService); +server.start(); diff --git a/server/gx-workflow-ls-format2/tsconfig.json b/server/gx-workflow-ls-format2/tsconfig.json new file mode 100644 index 0000000..3d42a4a --- /dev/null +++ b/server/gx-workflow-ls-format2/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2019", + "lib": ["ES2019", "WebWorker"], + "module": "commonjs", + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "sourceMap": true, + "strict": true + } +} diff --git a/server/webpack.config.js b/server/gx-workflow-ls-format2/webpack.config.js similarity index 71% rename from server/webpack.config.js rename to server/gx-workflow-ls-format2/webpack.config.js index 87c0385..877d734 100644 --- a/server/webpack.config.js +++ b/server/gx-workflow-ls-format2/webpack.config.js @@ -7,16 +7,16 @@ //@ts-check "use strict"; -const withDefaults = require("../shared.webpack.config"); +const withDefaults = require("../../shared.webpack.config"); const path = require("path"); /** @type WebpackConfig */ -const nodeServerConfig = withDefaults({ +const nodeGxFormat2ServerConfig = withDefaults({ mode: "none", context: path.join(__dirname), target: "node", // regular extensions run in node context entry: { - nativeServer: "./src/node/nativeServer.ts", + gxFormat2Server: "./src/node/server.ts", }, output: { filename: "[name].js", @@ -27,12 +27,12 @@ const nodeServerConfig = withDefaults({ }); /** @type WebpackConfig */ -const browserServerConfig = withDefaults({ +const browserGxFormat2ServerConfig = withDefaults({ mode: "none", context: path.join(__dirname), target: "webworker", // web extensions run in a webworker context entry: { - nativeServer: "./src/browser/nativeServer.ts", + gxFormat2Server: "./src/browser/server.ts", }, output: { filename: "[name].js", @@ -42,4 +42,4 @@ const browserServerConfig = withDefaults({ }, }); -module.exports = [nodeServerConfig, browserServerConfig]; +module.exports = [nodeGxFormat2ServerConfig, browserGxFormat2ServerConfig]; diff --git a/server/jest.config.js b/server/gx-workflow-ls-native/jest.config.js similarity index 100% rename from server/jest.config.js rename to server/gx-workflow-ls-native/jest.config.js diff --git a/server/gx-workflow-ls-native/package.json b/server/gx-workflow-ls-native/package.json new file mode 100644 index 0000000..a2c1db9 --- /dev/null +++ b/server/gx-workflow-ls-native/package.json @@ -0,0 +1,21 @@ +{ + "name": "gx-workflow-ls-native", + "version": "0.1.0", + "description": "Language server implementation for Galaxy native (JSON) workflows.", + "author": "davelopez", + "license": "MIT", + "dependencies": { + "@gxwf/server-common": "*", + "jsonc-parser": "^3.0.0", + "vscode-json-languageservice": "^4.2.1", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-languageserver": "^7.0.0", + "vscode-uri": "^3.0.3" + }, + "scripts": { + "webpack": "webpack", + "watch": "webpack --watch --progress", + "test-unit": "jest" + } +} diff --git a/server/src/browser/nativeServer.ts b/server/gx-workflow-ls-native/src/browser/server.ts similarity index 86% rename from server/src/browser/nativeServer.ts rename to server/gx-workflow-ls-native/src/browser/server.ts index dbb3f40..4cf84d4 100644 --- a/server/src/browser/nativeServer.ts +++ b/server/gx-workflow-ls-native/src/browser/server.ts @@ -1,6 +1,6 @@ import { createConnection, BrowserMessageReader, BrowserMessageWriter } from "vscode-languageserver/browser"; -import { GalaxyWorkflowLanguageServer } from "../server"; import { NativeWorkflowLanguageService } from "../languageService"; +import { GalaxyWorkflowLanguageServer } from "@gxwf/server-common/src/server"; const messageReader = new BrowserMessageReader(self); const messageWriter = new BrowserMessageWriter(self); diff --git a/server/src/jsonUtils.ts b/server/gx-workflow-ls-native/src/jsonUtils.ts similarity index 95% rename from server/src/jsonUtils.ts rename to server/gx-workflow-ls-native/src/jsonUtils.ts index 02e2d1b..14f6121 100644 --- a/server/src/jsonUtils.ts +++ b/server/gx-workflow-ls-native/src/jsonUtils.ts @@ -1,4 +1,4 @@ -import { ASTNode } from "./languageTypes"; +import { ASTNode } from "@gxwf/server-common/src/languageTypes"; export function getPathSegments(path: string): string[] | null { const segments = path.split(/[/.]/); diff --git a/server/src/languageService.ts b/server/gx-workflow-ls-native/src/languageService.ts similarity index 93% rename from server/src/languageService.ts rename to server/gx-workflow-ls-native/src/languageService.ts index f1d6761..33c616d 100644 --- a/server/src/languageService.ts +++ b/server/gx-workflow-ls-native/src/languageService.ts @@ -1,26 +1,26 @@ import { + DocumentLanguageSettings, getLanguageService, + JSONSchema, LanguageService, LanguageServiceParams, - DocumentLanguageSettings, - Diagnostic, - JSONSchema, LanguageSettings, SchemaConfiguration, } from "vscode-json-languageservice"; import { - TextDocument, - Range, + CompletionList, + Diagnostic, FormattingOptions, + Hover, + Position, + Range, + TextDocument, TextEdit, WorkflowDocument, WorkflowLanguageService, - Position, - Hover, - CompletionList, -} from "./languageTypes"; -import NativeWorkflowSchema from "../../workflow-languages/schemas/native.schema.json"; -import { NativeWorkflowDocument } from "./models/nativeWorkflowDocument"; +} from "@gxwf/server-common/src/languageTypes"; +import NativeWorkflowSchema from "../../../workflow-languages/schemas/native.schema.json"; +import { NativeWorkflowDocument } from "./nativeWorkflowDocument"; /** * A wrapper around the JSON Language Service to support language features diff --git a/server/src/models/nativeWorkflowDocument.ts b/server/gx-workflow-ls-native/src/nativeWorkflowDocument.ts similarity index 93% rename from server/src/models/nativeWorkflowDocument.ts rename to server/gx-workflow-ls-native/src/nativeWorkflowDocument.ts index 52cf929..ff02dfc 100644 --- a/server/src/models/nativeWorkflowDocument.ts +++ b/server/gx-workflow-ls-native/src/nativeWorkflowDocument.ts @@ -1,9 +1,9 @@ import { JSONDocument, ObjectASTNode } from "vscode-json-languageservice"; -import { getPropertyNodeFromPath } from "../jsonUtils"; -import { TextDocument, Range, Position, ASTNode, WorkflowDocument } from "../languageTypes"; +import { getPropertyNodeFromPath } from "./jsonUtils"; +import { TextDocument, Range, Position, ASTNode, WorkflowDocument } from "@gxwf/server-common/src/languageTypes"; /** - * This class contains information about Native workflow semantics. + * This class provides information about a Native workflow document structure. */ export class NativeWorkflowDocument extends WorkflowDocument { private _jsonDocument: JSONDocument; diff --git a/server/src/node/nativeServer.ts b/server/gx-workflow-ls-native/src/node/server.ts similarity index 80% rename from server/src/node/nativeServer.ts rename to server/gx-workflow-ls-native/src/node/server.ts index 0a2c3dd..dfc4bdf 100644 --- a/server/src/node/nativeServer.ts +++ b/server/gx-workflow-ls-native/src/node/server.ts @@ -1,5 +1,5 @@ import { createConnection } from "vscode-languageserver/node"; -import { GalaxyWorkflowLanguageServer } from "../server"; +import { GalaxyWorkflowLanguageServer } from "@gxwf/server-common/src/server"; import { NativeWorkflowLanguageService } from "../languageService"; const connection = createConnection(); diff --git a/server/tests/testHelpers.ts b/server/gx-workflow-ls-native/tests/testHelpers.ts similarity index 61% rename from server/tests/testHelpers.ts rename to server/gx-workflow-ls-native/tests/testHelpers.ts index 878f286..92cf987 100644 --- a/server/tests/testHelpers.ts +++ b/server/gx-workflow-ls-native/tests/testHelpers.ts @@ -1,7 +1,6 @@ -import { ASTNode, getLanguageService, JSONDocument, JSONPath } from "vscode-json-languageservice"; -import { TextDocument } from "../src/languageTypes"; -import * as Json from "jsonc-parser"; -import { NativeWorkflowDocument } from "../src/models/nativeWorkflowDocument"; +import { ASTNode, getLanguageService, JSONDocument } from "vscode-json-languageservice"; +import { TextDocument } from "@gxwf/server-common/src/languageTypes"; +import { NativeWorkflowDocument } from "../src/nativeWorkflowDocument"; export function toJsonDocument(contents: string): { textDoc: TextDocument; jsonDoc: JSONDocument } { const textDoc = TextDocument.create("foo://bar/file.json", "json", 0, contents); @@ -17,14 +16,6 @@ export function getJsonDocumentRoot(contents: string): ASTNode { return jsonDoc.root!; } -export function getNodeValue(node: ASTNode): unknown { - return Json.getNodeValue(node); -} - -export function getNodePath(node: ASTNode): JSONPath { - return Json.getNodePath(node); -} - export function createNativeWorkflowDocument(contents: string): NativeWorkflowDocument { const { textDoc, jsonDoc } = toJsonDocument(contents); return new NativeWorkflowDocument(textDoc, jsonDoc); diff --git a/server/tests/testWorkflowProvider.ts b/server/gx-workflow-ls-native/tests/testWorkflowProvider.ts similarity index 92% rename from server/tests/testWorkflowProvider.ts rename to server/gx-workflow-ls-native/tests/testWorkflowProvider.ts index 7b94c6f..8486b42 100644 --- a/server/tests/testWorkflowProvider.ts +++ b/server/gx-workflow-ls-native/tests/testWorkflowProvider.ts @@ -1,7 +1,7 @@ import * as fs from "fs"; import * as path from "path"; -const TEST_DATA_PATH = path.join(__dirname, "..", "..", "test-data"); +const TEST_DATA_PATH = path.join(__dirname, "..", "..", "..", "test-data"); interface TestJsonWorkflows { /** Workflows for testing validation issues. */ @@ -37,7 +37,7 @@ export class TestWorkflowProvider { }; /** Workflows in native JSON format. */ - public static get nativeJson(): TestJsonWorkflows { + public static get workflows(): TestJsonWorkflows { return this._jsonWorkflows; } } diff --git a/server/tests/unit/customValidationRules.test.ts b/server/gx-workflow-ls-native/tests/unit/customValidationRules.test.ts similarity index 81% rename from server/tests/unit/customValidationRules.test.ts rename to server/gx-workflow-ls-native/tests/unit/customValidationRules.test.ts index 9eef510..95d53ef 100644 --- a/server/tests/unit/customValidationRules.test.ts +++ b/server/gx-workflow-ls-native/tests/unit/customValidationRules.test.ts @@ -1,5 +1,5 @@ import { createNativeWorkflowDocument } from "../testHelpers"; -import { WorkflowOutputLabelValidation } from "../../src/providers/validation/WorkflowOutputLabelValidation"; +import { WorkflowOutputLabelValidation } from "@gxwf/server-common/src/providers/validation/WorkflowOutputLabelValidation"; import { TestWorkflowProvider } from "../testWorkflowProvider"; describe("Custom Validation Rules", () => { @@ -11,20 +11,20 @@ describe("Custom Validation Rules", () => { }); it("should not provide diagnostics when there are no steps", async () => { - const wfDocument = createNativeWorkflowDocument(TestWorkflowProvider.nativeJson.validation.withoutSteps); + const wfDocument = createNativeWorkflowDocument(TestWorkflowProvider.workflows.validation.withoutSteps); const diagnostics = await rule.validate(wfDocument); expect(diagnostics).toHaveLength(0); }); it("should not provide diagnostics when there are no workflow_outputs in the steps", async () => { - const wfDocument = createNativeWorkflowDocument(TestWorkflowProvider.nativeJson.validation.withThreeSteps); + const wfDocument = createNativeWorkflowDocument(TestWorkflowProvider.workflows.validation.withThreeSteps); const diagnostics = await rule.validate(wfDocument); expect(diagnostics).toHaveLength(0); }); it("should not provide diagnostics when the steps contains workflow_outputs with label", async () => { const wfDocument = createNativeWorkflowDocument( - TestWorkflowProvider.nativeJson.validation.withWorkflowOutputLabels + TestWorkflowProvider.workflows.validation.withWorkflowOutputLabels ); const diagnostics = await rule.validate(wfDocument); expect(diagnostics).toHaveLength(0); @@ -32,7 +32,7 @@ describe("Custom Validation Rules", () => { it("should provide diagnostics when the steps contains workflow_outputs without label", async () => { const wfDocument = createNativeWorkflowDocument( - TestWorkflowProvider.nativeJson.validation.withoutWorkflowOutputLabels + TestWorkflowProvider.workflows.validation.withoutWorkflowOutputLabels ); const diagnostics = await rule.validate(wfDocument); expect(diagnostics).toHaveLength(2); diff --git a/server/tests/unit/jsonUtils.test.ts b/server/gx-workflow-ls-native/tests/unit/jsonUtils.test.ts similarity index 100% rename from server/tests/unit/jsonUtils.test.ts rename to server/gx-workflow-ls-native/tests/unit/jsonUtils.test.ts diff --git a/server/tests/unit/nativeWorkflowDocument.test.ts b/server/gx-workflow-ls-native/tests/unit/nativeWorkflowDocument.test.ts similarity index 72% rename from server/tests/unit/nativeWorkflowDocument.test.ts rename to server/gx-workflow-ls-native/tests/unit/nativeWorkflowDocument.test.ts index c1bd22d..feca4ee 100644 --- a/server/tests/unit/nativeWorkflowDocument.test.ts +++ b/server/gx-workflow-ls-native/tests/unit/nativeWorkflowDocument.test.ts @@ -5,9 +5,9 @@ describe("NativeWorkflowDocument", () => { describe("getStepNodes", () => { it.each([ ["", 0], - [TestWorkflowProvider.nativeJson.validation.withoutSteps, 0], - [TestWorkflowProvider.nativeJson.validation.withOneStep, 1], - [TestWorkflowProvider.nativeJson.validation.withThreeSteps, 3], + [TestWorkflowProvider.workflows.validation.withoutSteps, 0], + [TestWorkflowProvider.workflows.validation.withOneStep, 1], + [TestWorkflowProvider.workflows.validation.withThreeSteps, 3], ])("returns the expected number of steps", (wf_content: string, expectedNumSteps: number) => { const wfDocument = createNativeWorkflowDocument(wf_content); const stepNodes = wfDocument.getStepNodes(); diff --git a/server/gx-workflow-ls-native/tsconfig.json b/server/gx-workflow-ls-native/tsconfig.json new file mode 100644 index 0000000..a2c3b2d --- /dev/null +++ b/server/gx-workflow-ls-native/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2019", + "lib": ["ES2019", "WebWorker"], + "module": "commonjs", + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "sourceMap": true, + "strict": true, + "rootDirs": ["src", "tests"] + }, + "include": ["src/**/*.ts", "tests/**/*.ts"] +} diff --git a/server/gx-workflow-ls-native/webpack.config.js b/server/gx-workflow-ls-native/webpack.config.js new file mode 100644 index 0000000..2b707e3 --- /dev/null +++ b/server/gx-workflow-ls-native/webpack.config.js @@ -0,0 +1,47 @@ +/* eslint-disable no-undef */ +/* eslint-disable @typescript-eslint/no-var-requires */ + +//@ts-check +/** @typedef {import('webpack').Configuration} WebpackConfig **/ + +//@ts-check +"use strict"; + +const withDefaults = require("../../shared.webpack.config"); +const path = require("path"); + +/** Native (JSON) Server */ + +/** @type WebpackConfig */ +const nodeNativeServerConfig = withDefaults({ + mode: "none", + context: path.join(__dirname), + target: "node", // regular extensions run in node context + entry: { + nativeServer: "./src/node/server.ts", + }, + output: { + filename: "[name].js", + path: path.join(__dirname, "dist"), + libraryTarget: "var", + library: "serverExportVar", + }, +}); + +/** @type WebpackConfig */ +const browserNativeServerConfig = withDefaults({ + mode: "none", + context: path.join(__dirname), + target: "webworker", // web extensions run in a webworker context + entry: { + nativeServer: "./src/browser/server.ts", + }, + output: { + filename: "[name].js", + path: path.join(__dirname, "dist", "web"), + libraryTarget: "var", + library: "serverExportVar", + }, +}); + +module.exports = [nodeNativeServerConfig, browserNativeServerConfig]; diff --git a/server/package-lock.json b/server/package-lock.json index 4844c2d..36cc5ff 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,25 +1,66 @@ { - "name": "galaxy-workflow-language-server-native", + "name": "gxwf", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "galaxy-workflow-language-server-native", + "name": "gxwf", + "version": "0.1.0", + "license": "MIT", + "workspaces": [ + "gx-workflow-ls-format2", + "gx-workflow-ls-native", + "packages/*" + ] + }, + "gx-workflow-ls-format2": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@gxwf/server-common": "*", + "@gxwf/yaml-language-service": "*", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + }, + "devDependencies": {} + }, + "gx-workflow-ls-native": { "version": "0.1.0", "license": "MIT", "dependencies": { + "@gxwf/server-common": "*", "jsonc-parser": "^3.0.0", "vscode-json-languageservice": "^4.2.1", "vscode-languageserver": "^7.0.0", "vscode-languageserver-textdocument": "^1.0.4", "vscode-languageserver-types": "^3.16.0", "vscode-uri": "^3.0.3" - }, - "engines": { - "node": "*" } }, + "node_modules/@gxwf/server-common": { + "resolved": "packages/server-common", + "link": true + }, + "node_modules/@gxwf/yaml-language-service": { + "resolved": "packages/yaml-language-service", + "link": true + }, + "node_modules/@types/node": { + "version": "17.0.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.42.tgz", + "integrity": "sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ==" + }, + "node_modules/gx-workflow-ls-format2": { + "resolved": "gx-workflow-ls-format2", + "link": true + }, + "node_modules/gx-workflow-ls-native": { + "resolved": "gx-workflow-ls-native", + "link": true + }, "node_modules/jsonc-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", @@ -76,17 +117,92 @@ "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" }, "node_modules/vscode-nls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", - "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.1.tgz", + "integrity": "sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==" }, "node_modules/vscode-uri": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz", "integrity": "sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==" + }, + "packages/common": { + "name": "@gxwf-server/common", + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "devDependencies": {} + }, + "packages/server-common": { + "name": "@gxwf/server-common", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.42", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + } + }, + "packages/yaml-language-service": { + "name": "@gxwf/yaml-language-service", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + } } }, "dependencies": { + "@gxwf/server-common": { + "version": "file:packages/server-common", + "requires": { + "@types/node": "^17.0.42", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + } + }, + "@gxwf/yaml-language-service": { + "version": "file:packages/yaml-language-service", + "requires": { + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + } + }, + "@types/node": { + "version": "17.0.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.42.tgz", + "integrity": "sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ==" + }, + "gx-workflow-ls-format2": { + "version": "file:gx-workflow-ls-format2", + "requires": { + "@gxwf/server-common": "*", + "@gxwf/yaml-language-service": "*", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + } + }, + "gx-workflow-ls-native": { + "version": "file:gx-workflow-ls-native", + "requires": { + "@gxwf/server-common": "*", + "jsonc-parser": "^3.0.0", + "vscode-json-languageservice": "^4.2.1", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + } + }, "jsonc-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", @@ -137,9 +253,9 @@ "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" }, "vscode-nls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", - "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.1.tgz", + "integrity": "sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==" }, "vscode-uri": { "version": "3.0.3", diff --git a/server/package.json b/server/package.json index 0901495..47ed7d1 100644 --- a/server/package.json +++ b/server/package.json @@ -1,30 +1,23 @@ { - "name": "galaxy-workflow-language-server-native", - "description": "Implementation of a language server for (native format) Galaxy Workflows.", + "name": "gxwf", + "private": "true", + "description": "Monorepo containing all Galaxy workflows language servers and dependencies.", "version": "0.1.0", - "author": { - "name": "galaxyproject", - "url": "https://galaxyproject.org/" - }, + "author": "davelopez", "license": "MIT", - "engines": { - "node": "*" - }, - "repository": { - "type": "git", - "url": "https://github.com/davelopez/galaxy-workflows-vscode" - }, - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver": "^7.0.0", - "vscode-languageserver-textdocument": "^1.0.4", - "vscode-languageserver-types": "^3.16.0", - "vscode-json-languageservice": "^4.2.1", - "vscode-uri": "^3.0.3" - }, "scripts": { - "webpack": "webpack", - "watch": "webpack --watch --progress", - "test-unit": "jest" - } + "compile-native-server": "webpack --config ./gx-workflow-ls-native/webpack.config.js", + "compile-format2-server": "webpack --config ./gx-workflow-ls-format2/webpack.config.js", + "compile": "npm run compile-native-server && npm run compile-format2-server", + "watch-native-server": "webpack --watch --progress --config ./gx-workflow-ls-native/webpack.config.js", + "watch-format2-server": "webpack --watch --progress --config ./gx-workflow-ls-format2/webpack.config.js", + "watch": "concurrently --kill-others \"npm run watch-format2-server\" \"npm run watch-native-server\"", + "test-unit-native": "cd gx-workflow-ls-native && npm run test-unit && cd ..", + "test-unit": "npm run test-unit-native" + }, + "workspaces": [ + "gx-workflow-ls-format2", + "gx-workflow-ls-native", + "packages/*" + ] } diff --git a/server/packages/server-common/package.json b/server/packages/server-common/package.json new file mode 100644 index 0000000..0a3231a --- /dev/null +++ b/server/packages/server-common/package.json @@ -0,0 +1,16 @@ +{ + "name": "@gxwf/server-common", + "version": "0.1.0", + "description": "Common library reused by all Galaxy Workflow language servers", + "author": "davelopez", + "license": "MIT", + "type": "module", + "dependencies": { + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3", + "@types/node": "^17.0.42" + }, + "scripts": {} +} diff --git a/server/src/configService.ts b/server/packages/server-common/src/configService.ts similarity index 100% rename from server/src/configService.ts rename to server/packages/server-common/src/configService.ts diff --git a/server/packages/server-common/src/index.ts b/server/packages/server-common/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/server/src/languageTypes.ts b/server/packages/server-common/src/languageTypes.ts similarity index 100% rename from server/src/languageTypes.ts rename to server/packages/server-common/src/languageTypes.ts diff --git a/server/src/models/workflowDocument.ts b/server/packages/server-common/src/models/workflowDocument.ts similarity index 100% rename from server/src/models/workflowDocument.ts rename to server/packages/server-common/src/models/workflowDocument.ts diff --git a/server/src/models/workflowDocuments.ts b/server/packages/server-common/src/models/workflowDocuments.ts similarity index 100% rename from server/src/models/workflowDocuments.ts rename to server/packages/server-common/src/models/workflowDocuments.ts diff --git a/server/src/providers/completionProvider.ts b/server/packages/server-common/src/providers/completionProvider.ts similarity index 100% rename from server/src/providers/completionProvider.ts rename to server/packages/server-common/src/providers/completionProvider.ts diff --git a/server/src/providers/formattingProvider.ts b/server/packages/server-common/src/providers/formattingProvider.ts similarity index 100% rename from server/src/providers/formattingProvider.ts rename to server/packages/server-common/src/providers/formattingProvider.ts diff --git a/server/src/providers/hover/debugHoverContentContributor.ts b/server/packages/server-common/src/providers/hover/debugHoverContentContributor.ts similarity index 100% rename from server/src/providers/hover/debugHoverContentContributor.ts rename to server/packages/server-common/src/providers/hover/debugHoverContentContributor.ts diff --git a/server/src/providers/hover/hoverProvider.ts b/server/packages/server-common/src/providers/hover/hoverProvider.ts similarity index 100% rename from server/src/providers/hover/hoverProvider.ts rename to server/packages/server-common/src/providers/hover/hoverProvider.ts diff --git a/server/src/providers/provider.ts b/server/packages/server-common/src/providers/provider.ts similarity index 100% rename from server/src/providers/provider.ts rename to server/packages/server-common/src/providers/provider.ts diff --git a/server/src/providers/symbolsProvider.ts b/server/packages/server-common/src/providers/symbolsProvider.ts similarity index 100% rename from server/src/providers/symbolsProvider.ts rename to server/packages/server-common/src/providers/symbolsProvider.ts diff --git a/server/src/providers/validation/MissingPropertyValidation.ts b/server/packages/server-common/src/providers/validation/MissingPropertyValidation.ts similarity index 100% rename from server/src/providers/validation/MissingPropertyValidation.ts rename to server/packages/server-common/src/providers/validation/MissingPropertyValidation.ts diff --git a/server/src/providers/validation/WorkflowOutputLabelValidation.ts b/server/packages/server-common/src/providers/validation/WorkflowOutputLabelValidation.ts similarity index 100% rename from server/src/providers/validation/WorkflowOutputLabelValidation.ts rename to server/packages/server-common/src/providers/validation/WorkflowOutputLabelValidation.ts diff --git a/server/src/providers/validation/profiles.ts b/server/packages/server-common/src/providers/validation/profiles.ts similarity index 100% rename from server/src/providers/validation/profiles.ts rename to server/packages/server-common/src/providers/validation/profiles.ts diff --git a/server/src/server.ts b/server/packages/server-common/src/server.ts similarity index 100% rename from server/src/server.ts rename to server/packages/server-common/src/server.ts diff --git a/server/src/services/cleanWorkflow.ts b/server/packages/server-common/src/services/cleanWorkflow.ts similarity index 99% rename from server/src/services/cleanWorkflow.ts rename to server/packages/server-common/src/services/cleanWorkflow.ts index 8810f47..2d06666 100644 --- a/server/src/services/cleanWorkflow.ts +++ b/server/packages/server-common/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 { ServiceBase } from "./common"; +import { ServiceBase } from "."; import { CleanWorkflowContentsParams, CleanWorkflowContentsRequest, diff --git a/server/src/services/common.ts b/server/packages/server-common/src/services/index.ts similarity index 100% rename from server/src/services/common.ts rename to server/packages/server-common/src/services/index.ts diff --git a/server/src/services/requestsDefinitions.ts b/server/packages/server-common/src/services/requestsDefinitions.ts similarity index 100% rename from server/src/services/requestsDefinitions.ts rename to server/packages/server-common/src/services/requestsDefinitions.ts diff --git a/server/packages/server-common/tsconfig.json b/server/packages/server-common/tsconfig.json new file mode 100644 index 0000000..1e1120b --- /dev/null +++ b/server/packages/server-common/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es2019", + "lib": ["ES2019", "WebWorker"], + "module": "commonjs", + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "sourceMap": true, + "strict": true, + "composite": true, + "declaration": true, + "rootDir": "./src", + "baseUrl": "." + }, + "include": ["src/**/*"] +} diff --git a/server/packages/yaml-language-service/package.json b/server/packages/yaml-language-service/package.json new file mode 100644 index 0000000..82b4171 --- /dev/null +++ b/server/packages/yaml-language-service/package.json @@ -0,0 +1,13 @@ +{ + "name": "@gxwf/yaml-language-service", + "version": "0.1.0", + "description": "YAML language service that provides smarts for YAML files editing. Based on the Red Hat yamlLanguageService implementation included in yaml-language-server but tailored for Galaxy Workflows.", + "author": "davelopez", + "license": "MIT", + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.3" + }, + "scripts": {} +} diff --git a/server/packages/yaml-language-service/src/services/yamlFormatter.ts b/server/packages/yaml-language-service/src/services/yamlFormatter.ts new file mode 100644 index 0000000..bd7f19d --- /dev/null +++ b/server/packages/yaml-language-service/src/services/yamlFormatter.ts @@ -0,0 +1,31 @@ +"use strict"; + +import { Range, Position, TextEdit, FormattingOptions } from "vscode-languageserver-types"; +import { CustomFormatterOptions, LanguageSettings } from "../yamlLanguageService"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +export class YAMLFormatter { + private formatterEnabled = true; + + public configure(shouldFormat: LanguageSettings): void { + if (shouldFormat) { + this.formatterEnabled = shouldFormat.format || false; + } + } + + public format(document: TextDocument, options: FormattingOptions & CustomFormatterOptions): TextEdit[] { + if (!this.formatterEnabled) { + return []; + } + + try { + const text = document.getText(); + // TODO implement formatter + const formatted = text; + + return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(text.length)), formatted)]; + } catch (error) { + return []; + } + } +} diff --git a/server/packages/yaml-language-service/src/yamlLanguageService.ts b/server/packages/yaml-language-service/src/yamlLanguageService.ts new file mode 100644 index 0000000..cc1dd40 --- /dev/null +++ b/server/packages/yaml-language-service/src/yamlLanguageService.ts @@ -0,0 +1,36 @@ +import { ASTNode } from "vscode-json-languageservice"; +import { TextDocument } from "vscode-languageserver-textdocument"; +import { FormattingOptions, Hover, Position, TextEdit } from "vscode-languageserver-types"; +import { YAMLFormatter } from "./services/yamlFormatter"; + +export declare type YAMLDocument = { + root: ASTNode | undefined; + getNodeFromOffset(offset: number, includeRightBound?: boolean): ASTNode | undefined; +}; + +export interface LanguageSettings { + format?: boolean; //Setting for whether we want to have the formatter or not +} + +export interface CustomFormatterOptions { + singleQuote?: boolean; + bracketSpacing?: boolean; + proseWrap?: string; + printWidth?: number; + enable?: boolean; +} + +export interface LanguageService { + doFormat(document: TextDocument, options: FormattingOptions & CustomFormatterOptions): TextEdit[]; + doHover(document: TextDocument, position: Position): Hover | null; +} + +export function getLanguageService(): LanguageService { + const formatter = new YAMLFormatter(); + return { + doFormat: formatter.format.bind(formatter), + doHover: (doc, pos) => { + return null; + }, + }; +} diff --git a/server/packages/yaml-language-service/tsconfig.json b/server/packages/yaml-language-service/tsconfig.json new file mode 100644 index 0000000..0ed94f1 --- /dev/null +++ b/server/packages/yaml-language-service/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2019", + "lib": ["ES2019", "WebWorker"], + "module": "commonjs", + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "sourceMap": true, + "strict": true, + "composite": true + }, + "include": ["src"], + "exclude": ["node_modules", ".vscode-test-web"] +} diff --git a/server/tsconfig.json b/server/tsconfig.json index 372dc02..aa03c3f 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -7,8 +7,15 @@ "resolveJsonModule": true, "esModuleInterop": true, "sourceMap": true, - "strict": true + "strict": true, + "composite": true }, - "include": ["src"], - "exclude": ["node_modules", ".vscode-test-web"] + "files": [], + "include": [], + "exclude": [], + "references": [ + { + "path": "packages/server-common" + } + ] } diff --git a/test-data/yaml/test_workflow_01.gxwf.yml b/test-data/yaml/test_wf_01.gxwf.yml similarity index 100% rename from test-data/yaml/test_workflow_01.gxwf.yml rename to test-data/yaml/test_wf_01.gxwf.yml