Skip to content
Snippets Groups Projects
sidebar.ts 10.8 KiB
Newer Older
import * as vscode from "vscode";
import {
    TextDocument
} from "vscode";
import {
    NotificationType,
    ProgressType,
    TextDocumentIdentifier
} from "vscode-languageclient/node";
import { analysisResultsProvider, client, provider } from './extension';


export let localToolNames: Array<string> | undefined;
export let localDocumentVars: Array<{ uri: string, constants: Array<{ name: string, value: string }>, distributions: Array<{ name: string, value: string }> }> = [];
export let localParameters: Array<{ toolName: string, parameters: Array<{ id: string, defaultValue: string, type: ParameterType, category: string }> }> = [];

//#region interfaces
interface ParameterDefinitions {
    parameterDefinitions: Array<ParameterDefinition>,
}
interface ParameterDefinition {
    id: string,
    name: string,
    description: string,
    category: string,
    type: ParameterType
    isOptional: boolean,
    defaultValue: string,
}

interface ParameterType {
    valueType: string,
    innerType: Array<ParameterType>,
    possibleValues: Array<string>,
}

interface ProgressIndication {
    message: string,
    progress: number
}

interface ResultNotification {
    progressToken: string,
    data: string
}
//#endregion

export function initializeTools() {
    client?.sendRequest<any>("modest/getTools").then(data => {
        localToolNames = data.availableTools;
            tools: localToolNames
export function getDocumentVars(document: TextDocument) {
    if (document.languageId === "modest") {
        if (document.uri) {
            let uri = document.uri.toString();
            let jsonObject = { "textDocument": TextDocumentIdentifier.create(uri) };
            client?.sendRequest<{ constants: Array<string>, distributions: Array<string> }>("modest/getDocumentVars", jsonObject).then(data => {
                const index = localDocumentVars.findIndex(x => x.uri === uri);
                const newConstants = data.constants.map(constant => {
                const newDistributions = data.distributions.map(distribution => {
                    return { name: distribution, value: "" };
                });
                    localDocumentVars.push({ "uri": uri, constants: newConstants, distributions: newDistributions });
                    localDocumentVars[index].constants = newConstants;
                    localDocumentVars[index].distributions = newDistributions;
                    type: "updateDocumentVars",
                    documentVars: localDocumentVars,
                    "uri": uri
                });
            });
        }
    }
}

function getParameters(toolName: string) {
    let jsonObject = { "toolName": toolName };
    client?.sendRequest<ParameterDefinitions>("modest/getParameters", jsonObject).then(data => {
        const index = localParameters.findIndex(x => x.toolName === toolName);
        const newParameters = data.parameterDefinitions.map(parameter => {
            return { id: parameter.id, defaultValue: parameter.defaultValue, type: parameter.type, category: parameter.category };
            localParameters.push({ toolName: toolName, parameters: newParameters });
            localParameters[index].parameters = newParameters;
            parameters: localParameters,
            toolName: toolName
        });
    });
}

function runTool(uri: string, toolName: string, constants: { name: string; value: string; }[], suppliedParameters: { id: string; value: string; }[]) {
    const toolIndex = localParameters.findIndex(x => x.toolName === toolName);
    let serverParameters: Array<{ id: string, value: string }> = [];
    if (toolIndex !== -1) {
        for (const parameter of suppliedParameters) {
            const parameterIndex = localParameters[toolIndex].parameters.findIndex(x => x.id === parameter.id);
                if (localParameters[toolIndex].parameters[parameterIndex].defaultValue !== parameter.value) {
                    serverParameters.push(parameter);
                }
            }
        }
    }

    let jsonObject = {
        textDocument: TextDocumentIdentifier.create(uri),
        toolName: toolName,
        constants: constants,
        parameters: serverParameters,
        progressToken: uri + toolName + constants + serverParameters + Date.now()
    };
    vscode.window.activeTextEditor?.document.save();
    vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: "Running " + toolName }, async (progress, token) => {
        await new Promise<null>(async (resolve, _) => {
            let progressHandler = client?.onProgress(new ProgressType<ProgressIndication>(), jsonObject.progressToken, indication => {
                progress.report({ message: indication.message, increment: indication.progress * 100 });
            });
            let resultHandler = client?.onNotification(new NotificationType<ResultNotification>("modest/toolResult"), data => {
                if (data.progressToken === jsonObject.progressToken) {
                    if (data.data && data.data !== "") {
                        try {
                            analysisResultsProvider.setJsonString(data.data);
                    progressHandler?.dispose();
                    resultHandler?.dispose();
                    resolve(null);
                }
            });
            if (client === null) {
                vscode.window.showErrorMessage("Server not ready yet, try again later");
                resolve(null);
            } else {
                await client?.sendRequest<string>("modest/runTool", jsonObject, token);
            }
        });
    });
}
export class ModestSidebarProvider implements vscode.WebviewViewProvider {
    public static readonly viewType = "modest.modestSidebar";

    private _view?: vscode.WebviewView;

    constructor(private readonly _extensionUri: vscode.Uri) { }

    resolveWebviewView(
        webviewView: vscode.WebviewView,
        context: vscode.WebviewViewResolveContext<unknown>,
        token: vscode.CancellationToken
    ): void | Thenable<void> {
        this._view = webviewView;

        webviewView.webview.options = {
            // Allow scripts in the webview
            enableScripts: true,

            localResourceRoots: [this._extensionUri],
        };

        webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
        webviewView.webview.onDidReceiveMessage(data => {
            console.log(data);
            switch (data.type) {
                case 'init': {
                    if (localToolNames) {
                            tools: localToolNames
                    if (localDocumentVars) {
                            type: "updateDocumentVars",
                            documentVars: localDocumentVars
                        });
                    }
                    break;
                }
                case 'toolSelected': {
                    getParameters(data.toolName);
                    break;
                }

                case 'runTool': {
                    runTool(data.uri, data.toolName, data.constants, data.parameters);
                    break;
                }
            }
        });
    }

    /**
     * sendMessage
     * @param {any} message
     */
    public sendMessage(message: any) {
        this._view?.show(true);
        this._view?.webview?.postMessage(message);
    }

    private _getHtmlForWebview(webview: vscode.Webview) {
        // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview.
        const scriptUri = webview.asWebviewUri(
            vscode.Uri.joinPath(this._extensionUri, "media", "main.js")
        );

        // Do the same for the stylesheet.
        const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css'));
        const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css'));
        const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.css'));
        const styleCodicons = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'node_modules', 'vscode-codicons', 'dist', 'codicon.css'));
        const fontCodicons = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'node_modules', 'vscode-codicons', 'dist', 'codicon.ttf'));

        // Use a nonce to only allow a specific script to be run.
        const nonce = getNonce();

        return `<!DOCTYPE html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<!--
					Use a content security policy to only allow loading images from https or from our extension directory,
					and only allow scripts that have a specific nonce.
				-->
				<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${fontCodicons}; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
				<meta name="viewport" content="width=device-width, initial-scale=1.0">
				<link href="${styleResetUri}" rel="stylesheet">
				<link href="${styleVSCodeUri}" rel="stylesheet">
				<link href="${styleMainUri}" rel="stylesheet">
                <link href="${styleCodicons}" rel="stylesheet">

				<title>Modest run dialog</title>
			</head>
			<body>
				<h3>Select tool</h3>
                <div id="run-box">
                    <select class="tools-dropdown" id="tools"> </select>
                    <button id="run-button"><i class="codicon codicon-play"></i></button>
                </div>
                <h3>Open constants</h3>
				<ul class="option-list" id="constants">There are no undefined constants.</ul>
                <h3>Parameters</h3>
                <ul class="option-list" id="parameters">There are no parameters.</ul>
				<script nonce="${nonce}" src="${scriptUri}"></script>
			</body>
			</html>`;

        function getNonce() {
            let text = "";
            const possible =
                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            for (let i = 0; i < 32; i++) {
                text += possible.charAt(
                    Math.floor(Math.random() * possible.length)
                );
            }
            return text;
        }
    }
}