blob: e4bc1c2d120f95cd8ffc2bf4c246ac31c1429c7a [file] [log] [blame]
/* eslint-disable @typescript-eslint/no-explicit-any */
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
'use strict';
import cp = require('child_process');
import vscode = require('vscode');
import { toolExecutionEnvironment } from './goEnv';
import { promptForMissingTool } from './goInstallTools';
import { byteOffsetAt, getBinPath, getFileArchive, makeMemoizedByteOffsetConverter } from './util';
// Interface for the output from fillstruct
interface GoFillStructOutput {
start: number;
end: number;
code: string;
}
export function runFillStruct(editor: vscode.TextEditor): Promise<void> {
const args = getCommonArgs(editor);
if (!args) {
return Promise.reject('No args');
}
return execFillStruct(editor, args);
}
function getCommonArgs(editor: vscode.TextEditor): string[] | undefined {
if (!editor) {
vscode.window.showInformationMessage('No editor is active.');
return;
}
if (!editor.document.fileName.endsWith('.go')) {
vscode.window.showInformationMessage('Current file is not a Go file.');
return;
}
const args = ['-modified', '-file', editor.document.fileName];
if (editor.selection.isEmpty) {
const offset = byteOffsetAt(editor.document, editor.selection.start);
args.push('-offset');
args.push(offset.toString());
} else {
args.push('-line');
args.push(`${editor.selection.start.line + 1}`);
}
return args;
}
function getTabsCount(editor: vscode.TextEditor): number {
const startline = editor.selection.start.line;
const tabs = editor.document.lineAt(startline).text.match('^\t*');
return tabs ? tabs.length : 0;
}
function execFillStruct(editor: vscode.TextEditor, args: string[]): Promise<void> {
const fillstruct = getBinPath('fillstruct');
const input = getFileArchive(editor.document);
const tabsCount = getTabsCount(editor);
return new Promise<void>((resolve, reject) => {
const p = cp.execFile(fillstruct, args, { env: toolExecutionEnvironment() }, (err, stdout, stderr) => {
try {
if (err && (<any>err).code === 'ENOENT') {
promptForMissingTool('fillstruct');
return reject();
}
if (err) {
vscode.window.showInformationMessage(`Cannot fill struct: ${stderr}`);
return reject();
}
const output = <GoFillStructOutput[]>JSON.parse(stdout);
if (output.length === 0) {
vscode.window.showInformationMessage('Got empty fillstruct output');
return reject();
}
const indent = '\t'.repeat(tabsCount);
const offsetConverter = makeMemoizedByteOffsetConverter(Buffer.from(editor.document.getText()));
editor
.edit((editBuilder) => {
output.forEach((structToFill) => {
const out = structToFill.code.replace(/\n/g, '\n' + indent);
const rangeToReplace = new vscode.Range(
editor.document.positionAt(offsetConverter(structToFill.start)),
editor.document.positionAt(offsetConverter(structToFill.end))
);
editBuilder.replace(rangeToReplace, out);
});
})
.then(() => resolve());
} catch (e) {
reject(e);
}
});
if (p.pid) {
p.stdin.end(input);
}
});
}