Skip to content

Commit

Permalink
refactor: Code actions + configuration nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
jirimosinger authored Jul 14, 2023
1 parent fd86c3a commit 0a6b5e9
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 180 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2023 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*/

import * as vscode from 'vscode';
import { relative } from 'path';
import { ConfigurationNodeDetails, ConfigurationNodes, anyConfigurationNodeExists } from '../configurationNodes';
import { proc_grps_file, pgm_conf_file } from '../constants'

function codeActionFactory(create: boolean, filename: string | undefined, args: any[] | undefined): vscode.CodeAction {
const filesDescription: string = filename ? filename + ' configuration file' : 'configuration files';

return {
title: (create ? 'Create ' : 'Modify ') + filesDescription,
command: {
title: (create ? 'Create ' : 'Open ') + filesDescription,
command: create ? 'extension.hlasm-plugin.createCompleteConfig' : 'vscode.open',
arguments: args
},
kind: vscode.CodeActionKind.QuickFix
};
}

function generateProcGrpsCodeAction(procGrps: ConfigurationNodeDetails, wsUri: vscode.Uri): vscode.CodeAction {
return procGrps.exists ? codeActionFactory(false, proc_grps_file, [procGrps.uri]) : codeActionFactory(true, proc_grps_file, [wsUri, null, '']);
}

function generatePgmConfCodeAction(configNodes: ConfigurationNodes, wsUri: vscode.Uri, document: vscode.TextDocument): vscode.CodeAction {
if (configNodes.pgmConf.exists)
return codeActionFactory(false, pgm_conf_file, [configNodes.pgmConf.uri]);
else {
if (configNodes.bridgeJson.exists || configNodes.ebgFolder.exists) {
// TODO: could we trigger B4G sync?
}
return codeActionFactory(true, pgm_conf_file, [wsUri, relative(wsUri.path, document.uri.path), null]);
}
}

export function generateConfigurationFilesCodeActions(suggestProcGrpsChange: boolean, suggestPgmConfChange: boolean, configNodes: ConfigurationNodes, wsUri: vscode.Uri, document: vscode.TextDocument): vscode.CodeAction[] {
if (!suggestProcGrpsChange && !suggestPgmConfChange)
return [];

if (!anyConfigurationNodeExists(configNodes))
return [codeActionFactory(true, undefined, [wsUri, relative(wsUri.path, document.uri.path), 'GRP1'])];

const result: vscode.CodeAction[] = [];

if (suggestProcGrpsChange)
result.push(generateProcGrpsCodeAction(configNodes.procGrps, wsUri));

if (suggestPgmConfChange)
result.push(generatePgmConfCodeAction(configNodes, wsUri, document));

return result;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*/

import * as vscode from 'vscode';

export function generateDownloadDependenciesCodeActions(): vscode.CodeAction[] {
const result: vscode.CodeAction[] = [];

result.push({
title: 'Download missing dependencies',
command: {
title: 'Download dependencies',
command: 'extension.hlasm-plugin.downloadDependencies',
arguments: ['newOnly']
},
kind: vscode.CodeActionKind.QuickFix
});
result.push({
title: 'Download all dependencies',
command: {
title: 'Download dependencies',
command: 'extension.hlasm-plugin.downloadDependencies'
},
kind: vscode.CodeActionKind.QuickFix
});

return result;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2023 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*/

import * as vscode from 'vscode';
import * as vscodelc from 'vscode-languageclient';

type OpcodeSuggestionList = {
[key: string]: {
opcode: string;
distance: number;
}[]
};

interface OpcodeSuggestionResponse {
uri: string;
suggestions: OpcodeSuggestionList;
};

function unique(a: string[]) {
return [...new Set(a)];
}

async function gatherOpcodeSuggestions(opcodes: string[], client: vscodelc.BaseLanguageClient, uri: vscode.Uri): Promise<OpcodeSuggestionList> {
const suggestionsResponse = await client.sendRequest<OpcodeSuggestionResponse>("textDocument/$/opcode_suggestion", {
textDocument: { uri: uri.toString() },
opcodes: opcodes,
extended: false,
});

return suggestionsResponse.suggestions;

}

async function opcodeTimeout(timeout: number): Promise<OpcodeSuggestionList> {
return new Promise<OpcodeSuggestionList>((resolve, _) => { setTimeout(() => { resolve({}); }, timeout); })
}

async function generateCodeActions(opcodeTasks: {
diag: vscode.Diagnostic;
opcode: string;
}[], client: vscodelc.BaseLanguageClient, uri: vscode.Uri, timeout: number): Promise<vscode.CodeAction[]> {
const result: vscode.CodeAction[] = [];
const suggestions = await Promise.race([gatherOpcodeSuggestions(unique(opcodeTasks.map(x => x.opcode)), client, uri), opcodeTimeout(timeout)]);

for (const { diag, opcode } of opcodeTasks) {
if (opcode in suggestions) {
const subst = suggestions[opcode];
for (const s of subst) {
const edit = new vscode.WorkspaceEdit();
edit.replace(uri, diag.range, s.opcode)
result.push({
title: `Did you mean '${s.opcode}'?`,
diagnostics: [diag],
kind: vscode.CodeActionKind.QuickFix,
edit: edit
});
}
}
}
return result;
}

const invalidUTF16Sequences = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;

export async function generateOpcodeSuggestionsCodeActions(diags: vscode.Diagnostic[], client: vscodelc.BaseLanguageClient, document: vscode.TextDocument): Promise<vscode.CodeAction[]> {
return await generateCodeActions(diags.map(diag => { return { diag, opcode: document.getText(diag.range).replace(invalidUTF16Sequences, '').toUpperCase() }; }), client, document.uri, 1000);
}
46 changes: 46 additions & 0 deletions clients/vscode-hlasmplugin/src/configurationNodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*/

import * as vscode from 'vscode';
import { uriExists } from './helpers'
import { hlasmplugin_folder, proc_grps_file, pgm_conf_file, bridge_json_file, ebg_folder as ebg_folder } from './constants';

export interface ConfigurationNodeDetails {
uri: vscode.Uri | typeof vscode.Uri;
exists: boolean;
}

export interface ConfigurationNodes {
procGrps: ConfigurationNodeDetails;
pgmConf: ConfigurationNodeDetails;
bridgeJson: ConfigurationNodeDetails;
ebgFolder: ConfigurationNodeDetails;
}

export async function retrieveConfigurationNodes(workspace: vscode.Uri, documentUri: vscode.Uri | undefined, fs: vscode.FileSystem = vscode.workspace.fs): Promise<ConfigurationNodes> {
const procGrps = vscode.Uri.joinPath(workspace, hlasmplugin_folder, proc_grps_file);
const pgmConf = vscode.Uri.joinPath(workspace, hlasmplugin_folder, pgm_conf_file);
const bridgeJson = documentUri ? vscode.Uri.joinPath(documentUri, "..", bridge_json_file) : undefined;
const ebgFolder = vscode.Uri.joinPath(workspace, ebg_folder);
return Promise.all([
uriExists(procGrps, fs).then(b => { return { uri: procGrps, exists: b }; }),
uriExists(pgmConf, fs).then(b => { return { uri: pgmConf, exists: b }; }),
bridgeJson ? uriExists(bridgeJson, fs).then(b => { return { uri: bridgeJson, exists: b }; }) : { uri: vscode.Uri, exists: false },
uriExists(ebgFolder, fs).then(b => { return { uri: ebgFolder, exists: b }; }),
]).then(arr => { return { procGrps: arr[0], pgmConf: arr[1], bridgeJson: arr[2], ebgFolder: arr[3] }; });
}

export function anyConfigurationNodeExists(configNodes: ConfigurationNodes) {
return configNodes.procGrps.exists || configNodes.pgmConf.exists || configNodes.bridgeJson.exists || configNodes.ebgFolder.exists;
}
14 changes: 7 additions & 7 deletions clients/vscode-hlasmplugin/src/configurationsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import * as vscode from 'vscode';
import { hlasmplugin_folder, proc_grps_file, pgm_conf_file } from './constants';
import { TextDecoder, TextEncoder } from 'util';
import { configurationExists } from './helpers';
import { retrieveConfigurationNodes } from './configurationNodes';

/**
* Handles changes in configurations files.
Expand Down Expand Up @@ -46,12 +46,12 @@ export class ConfigurationsHandler {
* Creates proc_grps.json or pgm_conf.json on demand if not
*/
async checkConfigs(workspace: vscode.Uri, documentUri: vscode.Uri) {
const [g, p, b, e] = await configurationExists(workspace, documentUri);
const configNodes = await retrieveConfigurationNodes(workspace, documentUri);

const doNotShowAgain = 'Do not track';

// give option to create proc_grps
if (!g.exists)
if (!configNodes.procGrps.exists)
vscode.window.showWarningMessage('proc_grps.json not found',
...['Create empty proc_grps.json', doNotShowAgain])
.then((selection) => {
Expand All @@ -63,7 +63,7 @@ export class ConfigurationsHandler {
ConfigurationsHandler.createProcTemplate(workspace).then(uri => vscode.commands.executeCommand("vscode.open", uri));
}
});
if (!p.exists && !b.exists && !e.exists)
if (!configNodes.pgmConf.exists && !configNodes.bridgeJson.exists && !configNodes.ebgFolder.exists)
vscode.window.showWarningMessage('pgm_conf.json not found',
...['Create empty pgm_conf.json', 'Create pgm_conf.json with this file', doNotShowAgain])
.then((selection) => {
Expand Down Expand Up @@ -181,14 +181,14 @@ export class ConfigurationsHandler {
+ '$');
}


/**
* Checks if the configs are there and stores their complete paths
*/
public static async configFilesExist(workspace: vscode.Uri): Promise<boolean> {
const [g, p] = await configurationExists(workspace, undefined);
return g.exists && p.exists;
const configNodes = await retrieveConfigurationNodes(workspace, undefined);
return configNodes.procGrps.exists && configNodes.pgmConf.exists;
}

public static createCompleteConfig(workspace: vscode.Uri, program: string, group: string) {
if (program)
ConfigurationsHandler.createPgmTemplate(program, workspace, group || '').then((uri) => vscode.commands.executeCommand('vscode.open', uri, { preview: false }));
Expand Down
14 changes: 0 additions & 14 deletions clients/vscode-hlasmplugin/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,11 @@
* Broadcom, Inc. - initial API and implementation
*/
import * as vscode from 'vscode';
import { hlasmplugin_folder, proc_grps_file, pgm_conf_file, bridge_json_file, ebg_folder } from './constants';

export async function uriExists(uri: vscode.Uri, fs: vscode.FileSystem = vscode.workspace.fs): Promise<boolean> {
return fs.stat(uri).then(() => { return true; }, () => { return false; })
}

export async function configurationExists(workspace: vscode.Uri, documentUri: vscode.Uri | undefined, fs: vscode.FileSystem = vscode.workspace.fs) {
const procGrps = vscode.Uri.joinPath(workspace, hlasmplugin_folder, proc_grps_file);
const pgmConf = vscode.Uri.joinPath(workspace, hlasmplugin_folder, pgm_conf_file);
const bridgeJson = documentUri ? vscode.Uri.joinPath(documentUri, "..", bridge_json_file) : undefined;
const ebgPath = vscode.Uri.joinPath(workspace, ebg_folder);
return Promise.all([
uriExists(procGrps, fs).then(b => { return { uri: procGrps, exists: b }; }),
uriExists(pgmConf, fs).then(b => { return { uri: pgmConf, exists: b }; }),
bridgeJson ? uriExists(bridgeJson, fs).then(b => { return { uri: bridgeJson, exists: b }; }) : { uri: vscode.Uri, exists: false },
uriExists(ebgPath, fs).then(b => { return { uri: ebgPath, exists: b }; }),
]);
}

export function isCancellationError(e: any) {
return e instanceof vscode.CancellationError || e instanceof Error && e.message == new vscode.CancellationError().message;
}
Expand Down
Loading

0 comments on commit 0a6b5e9

Please sign in to comment.