blob: 1f4e4218bc948132db268d1af6387468a056e784 [file] [log] [blame]
'use strict';
import * as vscode from 'vscode';
import { spawnSync } from 'child_process';
import {
guessFidlFormatToolPath,
getFidlFormatToolPathFromPreferences,
FormatToolError,
FormatToolErrorCause
} from './tool_finder';
const notDefined = "FIDL format tool not specified. Please set the path to fidl-format in Settings.";
const notExecutable = "FIDL format tool doesn't exist or can't be run. Please verify the path to fidl-format in Settings.";
const formatterError = "Error formatting FIDL";
const editSettingLabel = "Edit setting";
async function showError(e: FormatToolError) {
let message;
let showEdit = false;
switch (e.cause) {
case FormatToolErrorCause.ToolNotSpecified:
message = notDefined;
showEdit = true;
break;
case FormatToolErrorCause.ToolNotExecutable:
message = notExecutable;
showEdit = true;
break;
case FormatToolErrorCause.FormatterError:
message = `${formatterError}: ${e.formatterMessage}`
break;
default:
// This shouldn't happen. Since we don't know what kind of error we
// have, we rethrow so this error appears in the console during
// development.
throw e;
}
let items: string[] = [];
if (showEdit) {
items.push(editSettingLabel);
}
let item = await vscode.window.showErrorMessage(message, ...items);
switch (item) {
case undefined:
case null:
// The user did not choose to edit setting.
return;
case editSettingLabel:
vscode.commands.executeCommand('workbench.action.openSettings', 'fidl.formatTool');
}
}
/**
* A formatting edit provider that runs fidl-format over the code in the
* current FIDL file.
*
* Objects of this class will check for the presence of the fidl-format path in
* settings when instantiated and on every formatting request. If the path is
* left empty (the default) and the path to fidl-format isn't specified,
* objects of this class will show a notification to the user each time they
* try to format a FIDL file.
*/
export default class FidlFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
/**
* The cached path to the autodetected fidl-format tool. This is set once
* for the session if the user hasn't explicitly set a path in preferences.
*/
cachedGuessedFidlFormatToolPath: string | null = null;
constructor() {
if (getFidlFormatToolPathFromPreferences()) {
// The fidl-format tool path is specified in preferences, so no
// caching is needed.
return;
}
guessFidlFormatToolPath().then(
(p) => this.cachedGuessedFidlFormatToolPath = p,
(e: FormatToolError) => showError(e));
}
async provideDocumentFormattingEdits(document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
const fromPrefs = getFidlFormatToolPathFromPreferences();
let formatTool: string;
if (fromPrefs) {
// If the user has set a fidl-format path, that takes precedence.
formatTool = fromPrefs;
// Remove any cached (i.e. autodetected) tool.
this.cachedGuessedFidlFormatToolPath = null;
} else if (this.cachedGuessedFidlFormatToolPath) {
// If there is a cached fidl-format path, use that for this
// session.
formatTool = this.cachedGuessedFidlFormatToolPath;
} else {
// Find the fidl-format tool.
try {
formatTool = await guessFidlFormatToolPath();
} catch (e) {
showError(e);
return [];
}
}
// FIDL files are assumed to always be UTF-8.
const format = spawnSync(formatTool, [], { encoding: 'utf8', input: document.getText() });
if (format.status) {
// fidl-format exited with an error.
const e = new FormatToolError(FormatToolErrorCause.FormatterError);
e.formatterMessage = format.stderr;
showError(e);
// Return no edits. i.e. no changes to document.
return [];
}
const fileStart = new vscode.Position(0, 0);
const fileEnd = document.lineAt(document.lineCount - 1).range.end;
// Casting: because spawnSync was called with an encoding,
// format.stdout is already a string.
return [new vscode.TextEdit(new vscode.Range(fileStart, fileEnd), format.stdout as string)];
}
}