-
Sytze de Witte authoredSytze de Witte authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
extension.ts 13.91 KiB
import * as fs from "fs";
import { existsSync } from "fs";
import * as vscode from "vscode";
import {
ExtensionContext
} from "vscode";
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
Trace,
TransportKind
} from "vscode-languageclient/node";
import { AnalysisResultsProvider, Result } from "./analysisResults";
import { ModestCommands } from "./commands";
import { getDocumentVars, initializeTools, ModestSidebarProvider } from "./sidebar";
export let client: LanguageClient | undefined;
export let provider: ModestSidebarProvider;
export let analysisResultsProvider: AnalysisResultsProvider;
export let analysisResultsCompProvider: AnalysisResultsProvider;
export let treeView: vscode.TreeView<Result>;
export let extensionUri: vscode.Uri;
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 = 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;
client.onReady().then(() =>
initializeTools()
);
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.viewDot);
extensionUri = context.extensionUri;
//#region listeners
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);
}
}
});
vscode.window.onDidChangeActiveTextEditor(textEditor => {
if (textEditor) {
getDocumentVars(textEditor.document);
}
});
vscode.workspace.onDidSaveTextDocument(textEditor => {
getDocumentVars(textEditor);
});
//#endregion
provider = new ModestSidebarProvider(context.extensionUri);
analysisResultsProvider = new AnalysisResultsProvider();
analysisResultsCompProvider = new AnalysisResultsProvider();
treeView = vscode.window.createTreeView("analysisResults", {
treeDataProvider: analysisResultsProvider
});
vscode.window.createTreeView("analysisResultsCompView", {
treeDataProvider: analysisResultsCompProvider,
});
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
ModestSidebarProvider.viewType,
provider
)
);
//#region register commands
vscode.commands.registerCommand(
"analysisResults.copyValue",
(res: Result) => copyToClipboard(res.getValue())
);
vscode.commands.registerCommand(
"analysisResults.copyItem",
(res: Result) => copyItemToClipboard(res)
);
vscode.commands.registerCommand(
"analysisResults.openInEditor",
(res: Result) => openInEditor(analysisResultsProvider, res)
);
vscode.commands.registerCommand(
"analysisResults.highlight",
(res: Result) => highlight(analysisResultsProvider, res)
);
function highlight(prov: AnalysisResultsProvider, res: Result) {
res.highlight();
prov.refresh();
}
vscode.commands.registerCommand(
"analysisResults.load",
() => loadResults(analysisResultsProvider)
);
vscode.commands.registerCommand(
"analysisResultsCompView.load",
() => loadResults(analysisResultsCompProvider)
);
vscode.commands.registerCommand(
"analysisResults.export",
() => exportResults(analysisResultsProvider)
);
vscode.commands.registerCommand(
"analysisResultsCompView.export",
() => exportResults(analysisResultsCompProvider)
);
vscode.commands.registerCommand(
"analysisResults.openFileInEditor",
() => openFileInEditor(analysisResultsProvider)
);
vscode.commands.registerCommand(
"analysisResultsCompView.openFileInEditor",
() => openFileInEditor(analysisResultsCompProvider)
);
vscode.commands.registerCommand(
"analysisResults.clear",
() => clearView(analysisResultsProvider)
);
vscode.commands.registerCommand(
"analysisResultsCompView.clearCompView",
() => clearView(analysisResultsCompProvider)
);
vscode.commands.registerCommand(
"analysisResults.openInCompView",
() => analysisResultsCompProvider.setJsonObject(analysisResultsProvider.getJsonObject())
);
//#endregion
}
export function deactivate(): Thenable<void> | undefined {
if (!client) {
return undefined;
}
return client.stop();
}
//#region analysis results functions
function copyToClipboard(text: string) {
vscode.env.clipboard.writeText(text);
vscode.window.showInformationMessage(`Copied ${text} to clipboard.`);
}
function copyItemToClipboard(res: Result) {
if (res.getValue() !== "") {
return copyToClipboard(`${res.getLabel()}: ${res.getValue()}`);
} else {
return copyToClipboard(res.getLabel());
}
}
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()}.`);
}
});
}
function exportResults(prov: AnalysisResultsProvider) {
if (prov.getJsonObject()) {
const defaultUri = vscode.workspace.workspaceFolders
? vscode.Uri.parse(`${vscode.workspace.workspaceFolders[0].uri.fsPath}/analysis/${prov.getModestFile()}-results.json`)
: undefined;
vscode.window.showSaveDialog({defaultUri : defaultUri}).then(f => {
if (f) {
fs.writeFileSync(f.fsPath, JSON.stringify(prov.getJsonObject()));
}
});
} else {
vscode.window.showErrorMessage("There are no analysis results to be exported.");
}
}
function openInEditor(prov: AnalysisResultsProvider, res: Result) {
if(vscode.workspace.workspaceFolders !== undefined) {
let wsPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
const date = new Date().toISOString().split('.')[0];
const parent = res.getParentName() ? res.getParentName() + "-" : "";
const newPath = `${wsPath}/analysis/${prov.getModestFile()}-${parent}${res.getLabel()}.txt`;
const regex = new RegExp('(\\)|\\.),', 'gm');
const newText = res.getValue().replace(regex, "$1,\n");
if (pathExists(newPath)) {
vscode.workspace.openTextDocument(newPath).then(async document => {
var overwrite = await vscode.window.showInformationMessage(`This value already has been saved as a text file.`,
"Open existing file", "Create new file", "Overwrite file");
if (overwrite === "Overwrite file") {
const edit = new vscode.WorkspaceEdit();
edit.replace(vscode.Uri.parse(newPath), new vscode.Range(
new vscode.Position(0,0),
document.positionAt(document.getText().length - 1)),
newText
);
const success = await vscode.workspace.applyEdit(edit);
if (success) {
vscode.window.showTextDocument(document);
}
} else if (overwrite === "Open existing file") {
vscode.window.showTextDocument(document);
} else if (overwrite === "Create new file") {
const newFile = vscode.Uri.parse(`untitled:${newPath.replace(".txt", "")}-${date}.txt`);
vscode.workspace.openTextDocument(newFile).then(async document => {
const edit = new vscode.WorkspaceEdit();
edit.insert(newFile, new vscode.Position(0, 0), newText);
const success = await vscode.workspace.applyEdit(edit);
if (success) {
vscode.window.showTextDocument(document);
}
});
}
});
} else {
const newFile = vscode.Uri.parse("untitled:" + newPath);
vscode.workspace.openTextDocument(newFile).then(async document => {
const edit = new vscode.WorkspaceEdit();
edit.insert(newFile, new vscode.Position(0, 0), newText);
const success = await vscode.workspace.applyEdit(edit);
if (success) {
vscode.window.showTextDocument(document);
}
});
}
}
}
function openFileInEditor(prov: AnalysisResultsProvider) {
if (prov.getJsonObject()) {
if(vscode.workspace.workspaceFolders !== undefined) {
let wsPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
const date = new Date().toISOString().split('.')[0];
const newPath = `${wsPath}/analysis/${prov.getModestFile()}-results.json`;
const newText = JSON.stringify(prov.getJsonObject());
if (pathExists(newPath)) {
vscode.workspace.openTextDocument(newPath).then(async document => {
var overwrite = await vscode.window.showInformationMessage(`${newPath.split("/").pop()} already exists.`,
"Open existing file", "Create new file", "Overwrite file");
if (overwrite === "Overwrite file") {
const edit = new vscode.WorkspaceEdit();
edit.replace(vscode.Uri.parse(newPath), new vscode.Range(
new vscode.Position(0,0),
document.positionAt(document.getText().length - 1)),
newText
);
const success = await vscode.workspace.applyEdit(edit);
if (success) {
vscode.window.showTextDocument(document);
vscode.commands.executeCommand("editor.action.formatDocument", document.uri);
}
} else if (overwrite === "Open existing file") {
vscode.window.showTextDocument(document);
} else if (overwrite === "Create new file") {
const newFile = vscode.Uri.parse(`untitled:${newPath.replace(".json", "")}-${date}.json`);
vscode.workspace.openTextDocument(newFile).then(async document => {
const edit = new vscode.WorkspaceEdit();
edit.insert(newFile, new vscode.Position(0, 0), newText);
const success = await vscode.workspace.applyEdit(edit);
if (success) {
vscode.window.showTextDocument(document);
vscode.commands.executeCommand("editor.action.formatDocument", document.uri);
}
});
}
});
} else {
const newFile = vscode.Uri.parse("untitled:" + newPath);
vscode.workspace.openTextDocument(newFile).then(async document => {
const edit = new vscode.WorkspaceEdit();
edit.insert(newFile, new vscode.Position(0, 0), newText);
const success = await vscode.workspace.applyEdit(edit);
if (success) {
vscode.window.showTextDocument(document);
vscode.commands.executeCommand("editor.action.formatDocument", document.uri);
}
});
}
}
} else {
vscode.window.showErrorMessage("There are no analysis results to be opened in an editor.");
}
}
async function clearView(prov: AnalysisResultsProvider) {
if (prov.getJsonObject()) {
const jsonObject = prov.getJsonObject();
prov.setJsonObject(null);
var choice = await vscode.window.showInformationMessage("Analysis results view has been cleared.", "Undo");
if (choice === "Undo") {
prov.setJsonObject(jsonObject);
}
}
}
function pathExists(p: string): boolean {
try {
fs.accessSync(p);
} catch (err) {
return false;
}
return true;
}
//#endregion