Skip to content

Commit

Permalink
refactor: Allow to register a new dialect through API
Browse files Browse the repository at this point in the history
  • Loading branch information
Nurkambay committed Jan 16, 2023
1 parent 535c449 commit 9b8f428
Show file tree
Hide file tree
Showing 17 changed files with 150 additions and 99 deletions.
22 changes: 0 additions & 22 deletions clients/cobol-lsp-vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,28 +108,6 @@
"configuration": {
"title": "COBOL Language Support",
"properties": {
"cobol-lsp.dialect.registry": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"description": {
"type": "string"
},
"extensionId": {
"type": "string"
}
}
},
"uniqueItems": true,
"default": []
},
"cobol-lsp.smart-tab": {
"oneOf": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,30 @@
* Broadcom, Inc. - initial API and implementation
*/

import * as vscode from "../../__mocks__/vscode";
import { DialectRegistry } from "../../services/DialectRegistry";

describe("DialectRegistry test", () => {
const config = {
get: function(path: string) {
return [{ name: "dialect", path: "path", description: "" }];
},
update: jest.fn().mockReturnValue("")
};

beforeEach(() => {
vscode.workspace.getConfiguration = jest.fn().mockReturnValue(config);
});

it("register new dialect in the registry", () => {
let spy = jest.spyOn(config, "update");

DialectRegistry.register("new", "path", "desc", "dialectId", "path");
expect(spy).toBeCalled();
DialectRegistry.clear();
});

it("unregister new dialect from the registry", () => {
let spy = jest.spyOn(config, "update");
it("register/unregister new dialect in the registry", () => {
DialectRegistry.register("dialectId", "new", "path", "desc", "path");
expect(DialectRegistry.getDialects().length).toBe(1);

DialectRegistry.unregister("new");
expect(spy).toBeCalled();
expect(DialectRegistry.getDialects().length).toBe(0);
});

it("retrieve dialects from the registry", () => {
DialectRegistry.register("dialect", "path", "", "id", "path");
DialectRegistry.register("id", "dialect", "path", "desc", "snippetPath");
const result = DialectRegistry.getDialects();

expect(result.length).toBe(1);
expect(result[0].name).toBe("dialect");
expect(result[0].description).toBe("desc");
expect(result[0].extensionId).toBe("id");
expect(result[0].path).toBe("path");
expect(result[0].snippetPath).toBe("snippetPath");
});
});
2 changes: 0 additions & 2 deletions clients/cobol-lsp-vscode-extension/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* Broadcom, Inc. - initial API and implementation
*/
export const SETTINGS_CPY_SECTION: string = "cobol-lsp.cpy-manager";
export const SETTINGS_DIALECT_REGISTRY: string = "cobol-lsp.dialect.registry";
export const SETTINGS_DIALECT = "cobol-lsp.dialects";
export const SETTINGS_SUBROUTINE_LOCAL_KEY = "cobol-lsp.subroutine-manager.paths-local";
export const SETTINGS_TAB_CONFIG: string = "cobol-lsp.smart-tab";
Expand Down Expand Up @@ -49,7 +48,6 @@ export const EXTENSION_ID = "broadcommfd.cobol-language-support";
export const TELEMETRY_DEFAULT_CONTENT = "INVALID_INSTRUMENTATION_KEY";
export const ZOWE_EXT_MISSING_MSG = "Zowe Explorer version 1.15.0 or higher is required to download copybooks from the mainframe.";
export const INSTALL_ZOWE = "Install Zowe Explorer";
export const DACO_DIALECT = "DaCo";
export const DEFAULT_DIALECT = "COBOL";
export const CLEARING_COPYBOOK_CACHE = "Clearing downloaded copybook cache";
export const COPYBOOK_CACHE_CLEARED_INFO = "Downloaded copybooks removed";
Expand Down
53 changes: 31 additions & 22 deletions clients/cobol-lsp-vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as vscode from "vscode";

import { fetchCopybookCommand } from "./commands/FetchCopybookCommand";
import { gotoCopybookSettings } from "./commands/OpenSettingsCommand";
import { C4Z_FOLDER, GITIGNORE_FILE, LANGUAGE_ID, SERVER_TYPE } from "./constants";
import { LANGUAGE_ID, SERVER_TYPE } from "./constants";
import { CopybookDownloadService } from "./services/copybook/CopybookDownloadService";
import { CopybooksCodeActionProvider } from "./services/copybook/CopybooksCodeActionProvider";

Expand All @@ -25,7 +25,7 @@ import { CommentAction, commentCommand } from "./commands/CommentCommand";
import { initSmartTab, RangeTabShiftStore } from "./commands/SmartTabCommand";
import { LanguageClientService } from "./services/LanguageClientService";
import { TelemetryService } from "./services/reporter/TelemetryService";
import { createFileWithGivenPath, SettingsService } from "./services/Settings";
import { configHandler, SettingsService } from "./services/Settings";
import { pickSnippet, SnippetCompletionProvider } from "./services/snippetcompletion/SnippetCompletionProvider";
import { resolveSubroutineURI } from "./services/util/SubroutineUtils";
import {
Expand All @@ -35,33 +35,23 @@ import {
import { DialectRegistry } from "./services/DialectRegistry";

let languageClientService: LanguageClientService;
let outputChannel: vscode.OutputChannel;

function initialize() {
// We need lazy initialization to be able to mock this for unit testing
const copyBooksDownloader = new CopybookDownloadService();
const outputChannel = vscode.window.createOutputChannel( "COBOL Language Support");
outputChannel = vscode.window.createOutputChannel( "COBOL Language Support");
languageClientService = new LanguageClientService(outputChannel);
return {copyBooksDownloader, outputChannel};
return copyBooksDownloader;
}

export async function activate(context: vscode.ExtensionContext) {
DialectRegistry.clear();

const { copyBooksDownloader, outputChannel} = initialize();
const copyBooksDownloader = initialize();
initSmartTab(context);

TelemetryService.registerEvent("log", ["bootstrap", "experiment-tag"], "Extension activation event was triggered");
context.subscriptions.push(vscode.commands.registerCommand("cobol-lsp.dialect.register",
(name: string, path: string, description: string, extensionId: string, snippetPath: string) => {
DialectRegistry.register(name, path, description, extensionId, snippetPath);
}));

TelemetryService.registerEvent("log", ["bootstrap", "experiment-tag"], "Extension activation event was triggered");
context.subscriptions.push(vscode.commands.registerCommand("cobol-lsp.dialect.unregister",
(name: string, extensionId: string) => {
DialectRegistry.unregister(name);
}));

TelemetryService.registerEvent("log", ["bootstrap", "experiment-tag"], "Extension activation event was triggered");
copyBooksDownloader.start();

// Commands
Expand Down Expand Up @@ -123,17 +113,36 @@ export async function activate(context: vscode.ExtensionContext) {
languageClientService.addRequestHandler("cobol/resolveSubroutine", resolveSubroutineURI);
languageClientService.addRequestHandler("copybook/resolve", resolveCopybookHandler);
languageClientService.addRequestHandler("copybook/download", downloadCopybookHandler.bind(copyBooksDownloader));
languageClientService.addRequestHandler("workspace/configuration", configHandler);

context.subscriptions.push(languageClientService.start());

// 'export' public api-surface
return {
analysis(uri: string, text: string): Promise<any> {
return languageClientService.retrieveAnalysis(uri, text);
},
};
return openApi();
}

export function deactivate() {
return Promise.resolve(languageClientService.stop());
}

function openApi() {
return {
analysis(uri: string, text: string): Promise<any> {
return languageClientService.retrieveAnalysis(uri, text);
},
//name: extensionId: string, string, path: string, description: string, snippetPath: string
async registerDialect(dialect) {
outputChannel.appendLine("Register new dialect: \r\n" + JSON.stringify(dialect));
const dialectExtension = vscode.extensions.getExtension(dialect.extensionId);
await dialectExtension.activate();

DialectRegistry.register(dialect.extensionId, dialect.name, dialect.path, dialect.description, dialect.snippetPath);
outputChannel.appendLine("Restart analysis");
await languageClientService.invalidateConfiguration();
},
async unregisterDialect(extensionId, dialectName) {
DialectRegistry.unregister(dialectName);
await languageClientService.invalidateConfiguration();
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
*/

import * as vscode from "vscode";
import { SETTINGS_DIALECT_REGISTRY } from "../constants";

export const DIALECT_REGISTRY_SECTION: string = "cobol-lsp.dialect.registry";

/**
* Holds information about registered dialect
Expand Down Expand Up @@ -46,7 +47,6 @@ const dialectInfoes: Map<string, DialectInfo> = new Map();
*/
public static clear() {
dialectInfoes.clear();
vscode.workspace.getConfiguration().update(SETTINGS_DIALECT_REGISTRY, []);
}

/**
Expand All @@ -57,7 +57,7 @@ const dialectInfoes: Map<string, DialectInfo> = new Map();
* @param extensionId is an extension id
* @param snippets is a spippet map for a dialect
*/
public static register(name: string, path: string, description: string, extensionId: string, snippetPath: string) {
public static register(extensionId: string, name: string, path: string, description: string, snippetPath: string) {
const dialectInfo: DialectInfo = {
name: name,
path: path,
Expand All @@ -66,7 +66,6 @@ const dialectInfoes: Map<string, DialectInfo> = new Map();
snippetPath: snippetPath
};
dialectInfoes.set(dialectInfo.name, dialectInfo);
vscode.workspace.getConfiguration().update(SETTINGS_DIALECT_REGISTRY, Array.from(dialectInfoes.values()));
}

/**
Expand All @@ -75,7 +74,6 @@ const dialectInfoes: Map<string, DialectInfo> = new Map();
*/
public static unregister(name: string) {
dialectInfoes.delete(name);
vscode.workspace.getConfiguration().update(SETTINGS_DIALECT_REGISTRY, Array.from(dialectInfoes.values()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { join } from "path";
import * as vscode from "vscode";

import {
DidChangeConfigurationNotification,
ErrorCodes,
LanguageClient,
LanguageClientOptions,
Expand Down Expand Up @@ -74,6 +75,12 @@ export class LanguageClientService {
return languageClient.sendRequest("extended/analysis", { uri, text });
}

public async invalidateConfiguration() {
const languageClient = this.getLanguageClient();
await languageClient.onReady();
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: null });
}

public start(): vscode.Disposable {
this.initHandlers();
return this.getLanguageClient().start();
Expand Down
20 changes: 19 additions & 1 deletion clients/cobol-lsp-vscode-extension/src/services/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import * as path from "path";
import * as vscode from "vscode";
import {
COPYBOOK_EXTENSIONS,
DACO_DIALECT,
PATHS_LOCAL_KEY,
PATHS_USS,
PATHS_ZOWE,
Expand All @@ -29,6 +28,7 @@ import {
SETTINGS_TAB_CONFIG,
} from "../constants";
import cobolSnippets = require("../services/snippetcompletion/cobolSnippets.json");
import { DialectRegistry, DIALECT_REGISTRY_SECTION } from "./DialectRegistry";

/**
* New file (e.g .gitignore) will be created or edited if exits, under project folder
Expand Down Expand Up @@ -72,6 +72,24 @@ export class TabSettings {
public constructor(public rules: TabRule[], public defaultRule: TabRule) {}
}

export function configHandler(request : any): Array<any> {
const result = new Array<any>();
for (let item of request.items) {
try {
if (item.section === DIALECT_REGISTRY_SECTION) {
const object = DialectRegistry.getDialects();
result.push(object);
} else {
const object = vscode.workspace.getConfiguration().get(item.section)
result.push(object);
}
} catch (error) {
console.log(error);
}
}
return result;
}

/**
* SettingsService provides read/write configurstion settings functionality
*/
Expand Down
32 changes: 27 additions & 5 deletions clients/daco-dialect-support/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,41 @@ import { join } from 'path';
import * as vscode from 'vscode';

const extensionId: string = "DaCo.daco-dialect-for-cobol";
const mainExtension: string = "BroadcomMFD.cobol-language-support";

export function activate(context: vscode.ExtensionContext) {
let mainApi: any = undefined;

export async function activate(context: vscode.ExtensionContext) {

const ext = vscode.extensions.getExtension(extensionId);
if (ext === undefined) {
throw new Error("Cannot find extension");
}

const main = vscode.extensions.getExtension(mainExtension);
if (main === undefined) {
throw new Error("Cannot find COBOL LS extension");
}

mainApi = await main.activate();
if (mainApi === undefined) {
throw new Error("COBOL LS API is invalid");
}

const executablePath = join(ext.extensionPath, "server", "jar");
const snippetPath = join(ext.extensionPath, "snippets.json");
vscode.commands.executeCommand("cobol-lsp.dialect.register", "DaCo", executablePath, "DaCo dialect support", extensionId, snippetPath);

mainApi.registerDialect({
extensionId: extensionId,
name: "DaCo",
path: executablePath,
description: "DaCo dialect support",
snippetPath: snippetPath
});
}

// This method is called when your extension is deactivated
export function deactivate() {
vscode.commands.executeCommand("cobol-lsp.dialect.unregister", "DaCo", extensionId);
export async function deactivate() {
if (mainApi !== undefined) {
mainApi.unregister(extensionId, "DaCo");
}
}
32 changes: 27 additions & 5 deletions clients/idms-dialect-support/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,41 @@ import { join } from 'path';
import * as vscode from 'vscode';

const extensionId: string = "BroadcomMFD.idms-dialect-for-cobol";
const mainExtension: string = "BroadcomMFD.cobol-language-support";

export function activate(context: vscode.ExtensionContext) {
let mainApi: any = undefined;

export async function activate(context: vscode.ExtensionContext) {

const ext = vscode.extensions.getExtension(extensionId);
if (ext === undefined) {
throw new Error("Cannot find extension");
}

const main = vscode.extensions.getExtension(mainExtension);
if (main === undefined) {
throw new Error("Cannot find COBOL LS extension");
}

mainApi = await main.activate();
if (mainApi === undefined) {
throw new Error("COBOL LS API is invalid");
}

const executablePath = join(ext.extensionPath, "server", "jar");
const snippetPath = join(ext.extensionPath, "snippets.json");
vscode.commands.executeCommand("cobol-lsp.dialect.register", "IDMS", executablePath, "IDMS dialect support", extensionId, snippetPath);

mainApi.registerDialect({
extensionId: extensionId,
name: "IDMS",
path: executablePath,
description: "IDMS dialect support",
snippetPath: snippetPath
});
}

// This method is called when your extension is deactivated
export function deactivate() {
vscode.commands.executeCommand("cobol-lsp.dialect.unregister", "IDMS", extensionId);
export async function deactivate() {
if (mainApi !== undefined) {
mainApi.unregister(extensionId, "IDMS");
}
}

0 comments on commit 9b8f428

Please sign in to comment.