| '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)]; |
| } |
| } |