-
Sytze de Witte authoredSytze de Witte authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
extension.ts 10.12 KiB
// tslint:disable
"use strict";
import {
workspace,
ExtensionContext,
TextDocument,
CancellationToken,
ProviderResult,
} from "vscode";
import * as vscode from "vscode";
import {
DocumentFormattingParams,
DocumentFormattingRegistrationOptions,
DocumentFormattingRequest,
FormattingOptions,
LanguageClient,
LanguageClientOptions,
ServerOptions,
TextEdit,
Trace,
TransportKind,
} from "vscode-languageclient/node";
import { ModestCommands } from "./commands";
import { AnalysisResultsProvider, Result } from "./analysisResults";
import * as path from "path";
import { fstat, existsSync } from "fs";
import * as fs from "fs";
let client: LanguageClient | undefined;
export let provider: ModestSidebarProvider;
export let analysisResultsProvider: AnalysisResultsProvider;
export function modestExecutable(): string | undefined {
let executable: string | undefined = vscode.workspace
.getConfiguration("modest")
.get("executableLocation");
if (executable === undefined || executable === "") {
vscode.window
.showErrorMessage(
"It looks like you don't have the modest executable located yet.",
"Go to settings"
)
.then((result) => {
if (result !== undefined) {
vscode.commands.executeCommand(
"workbench.action.openSettings",
"modest.executableLocation"
);
}
});
return;
}
return executable;
}
function createClient(): LanguageClient | undefined {
// TODO: Check if path exists for a better user experience(so it doesn't spam the user with errors)
// The server is implemented in node
//let serverExe = "D:\\Desktop\\designproject\\modest-toolset\\Binaries\\Release\\win-x64\\Modest.LanguageServer.exe";
let serverExe = modestExecutable();
if (serverExe === undefined) {
return;
}
if (!existsSync(serverExe)) {
vscode.window
.showErrorMessage(
"The specified modest executable could not be found",
"Go to settings"
)
.then((result) => {
if (result !== undefined) {
vscode.commands.executeCommand(
"workbench.action.openSettings",
"modest.executableLocation"
);
}
});
return;
}
// If the extension is launched in debug mode then the debug server options are used
// Otherwise the run options are used
let serverOptions: ServerOptions = {
// run: { command: serverExe, args: ['-lsp', '-d'] },
run: {
command: serverExe,
args: ["startlspserver"],
transport: TransportKind.stdio,
},
// debug: { command: serverExe, args: ['-lsp', '-d'] }
debug: {
command: "dotnet",
transport: TransportKind.stdio,
args: [serverExe.replace(".exe", "") + ".dll", "startlspserver"], // Hacky
runtime: "",
},
};
// Options to control the language client
let clientOptions: LanguageClientOptions = {
// Register the server for plain text documents
documentSelector: [
{
pattern: "**/*.modest",
},
],
progressOnInitialization: true,
//synchronize: {
// Synchronize the setting section 'languageServerExample' to the server
// configurationSection: "languageServerExample",
// fileEvents: workspace.createFileSystemWatcher("**/*.modest"),
//},
};
// Create the language client and start the client.
const client = new LanguageClient(
"ModestExtension",
"Modest Extension",
serverOptions,
clientOptions
);
client.registerProposedFeatures();
client.trace = Trace.Verbose;
return client;
}
export function activate(context: ExtensionContext) {
client = createClient();
if (client) {
let langClient = client.start();
// Push the disposable to the context's subscriptions so that the
// client can be deactivated on extension deactivation
context.subscriptions.push(langClient);
}
context.subscriptions.push(ModestCommands.simCommand);
context.subscriptions.push(ModestCommands.addParameters);
vscode.workspace.onDidChangeConfiguration((a) => {
if (a.affectsConfiguration("modest.executableLocation")) {
if (client) {
client.stop();
client = undefined;
}
client = createClient();
if (client) {
let langClient = client.start();
context.subscriptions.push(langClient);
}
}
});
provider = new ModestSidebarProvider(context.extensionUri);
analysisResultsProvider = new AnalysisResultsProvider();
vscode.window.createTreeView("analysisResults", {
treeDataProvider: analysisResultsProvider,
});
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
ModestSidebarProvider.viewType,
provider
)
);
vscode.commands.registerCommand(
"analysisResults.copyValue",
(res: Result) => copyToClipboard(res.getValue())
);
vscode.commands.registerCommand(
"analysisResults.copyItem",
(res: Result) => copyToClipboard(`${res.getLabel()}: ${res.getValue()}`)
);
vscode.commands.registerCommand(
"analysisResults.load",
() => loadResults(analysisResultsProvider)
);
vscode.commands.registerCommand(
"analysisResults.export",
() => exportResults(analysisResultsProvider)
);
}
export function deactivate(): Thenable<void> | undefined {
if (!client) {
return undefined;
}
return client.stop();
}
function copyToClipboard(text: string) {
vscode.env.clipboard.writeText(text);
vscode.window.showInformationMessage(`Copied ${text} to clipboard.`);
}
function loadResults(provider: AnalysisResultsProvider) {
vscode.window.showOpenDialog().then(fileUri => {
if (fileUri) {
var fpath = fileUri[0].fsPath;
provider.setJsonPath(fpath);
vscode.window.showInformationMessage(`Opened ${fpath.split("/").pop()}.`);
} else {
vscode.window.showInformationMessage(`Could not open file.`);
}
});
}
function exportResults(provider: AnalysisResultsProvider) {
vscode.window.showSaveDialog().then(f => {
if (f) {
fs.writeFileSync(f.path, JSON.stringify(provider.getJsonObject()));
}
});
}
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) => {
switch (data.type) {
case "colorSelected": {
vscode.window.activeTextEditor?.insertSnippet(
new vscode.SnippetString(`#${data.value}`)
);
break;
}
}
});
}
/**
* sendMessage
*/
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")
);
// 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'; 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">
<title>Modest run dialog</title>
</head>
<body>
<h3>Undefined constants</h3>
<ul class="option-list" id="undefined-constants"> </ul>
<h3>Defined constants</h3>
<ul class="option-list" id="defined-constants"> </ul>
<h3>Options</h3>
<ul class="option-list" id="options"> </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;
}
}
}