From 6ff34bf152aba42d78a6e75e1ba501d135ec4a79 Mon Sep 17 00:00:00 2001 From: Andrey Bogoyavlensky Date: Sun, 12 Jan 2020 15:31:47 +0300 Subject: [PATCH] Use VS Code built-in provider for code formatting --- package.json | 4 --- src/clojureFormat.ts | 59 +++++++++++++++++++++++++++----------------- src/clojureMain.ts | 4 +-- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index de5090b..e0b9d11 100644 --- a/package.json +++ b/package.json @@ -95,10 +95,6 @@ { "command": "clojureVSCode.stopDisconnectNRepl", "title": "Clojure: Disconnect from nREPL" - }, - { - "command": "clojureVSCode.formatFile", - "title": "Clojure: Format file or selection" } ], "views": { diff --git a/src/clojureFormat.ts b/src/clojureFormat.ts index b1e452d..df8c1c5 100644 --- a/src/clojureFormat.ts +++ b/src/clojureFormat.ts @@ -1,7 +1,6 @@ import * as vscode from 'vscode'; import { cljConnection } from './cljConnection'; -import { cljParser } from './cljParser'; import { nreplClient } from './nreplClient'; function slashEscape(contents: string) { @@ -18,15 +17,14 @@ function slashUnescape(contents: string) { }); } -export const formatFile = (textEditor: vscode.TextEditor, edit?: vscode.TextEditorEdit): void => { + +export const formatFile = (document: vscode.TextDocument, range: vscode.Range): Promise => { if (!cljConnection.isConnected()) { - vscode.window.showErrorMessage("Formatting functions don't work, connect to nREPL first."); - return; + return Promise.reject("Formatting functions don't work, connect to nREPL first."); } - const selection = textEditor.selection; - let contents: string = selection.isEmpty ? textEditor.document.getText() : textEditor.document.getText(selection); + let contents: string = document.getText(range); // Escaping the string before sending it to nREPL contents = slashEscape(contents) @@ -41,28 +39,20 @@ export const formatFile = (textEditor: vscode.TextEditor, edit?: vscode.TextEdit // time it is called. I have no idea what causes this behavior so I decided to put the require // statement right here - don't think it does any harm. If someone knows how to fix it // please send a pull request with a fix. - nreplClient.evaluate(`(require 'cljfmt.core) (cljfmt.core/reformat-string "${contents}" ${cljfmtParams})`) + return nreplClient.evaluate(`(require 'cljfmt.core) (cljfmt.core/reformat-string "${contents}" ${cljfmtParams})`) .then(value => { if ('ex' in value[0]) { - vscode.window.showErrorMessage(value[1].err); - return; + return Promise.reject(value[1].err); }; if (('value' in value[1]) && (value[1].value != 'nil')) { let new_content: string = value[1].value.slice(1, -1); new_content = slashUnescape(new_content); - let selection = textEditor.selection; - if (textEditor.selection.isEmpty) { - const lines: string[] = textEditor.document.getText().split(/\r?\n/g); - const lastChar: number = lines[lines.length - 1].length; - selection = new vscode.Selection(new vscode.Position(0, 0), new vscode.Position(textEditor.document.lineCount, lastChar)); - } - textEditor.edit(editBuilder => { - editBuilder.replace(selection, new_content); - }); + return Promise.resolve([vscode.TextEdit.replace(range, new_content)]); }; }); } + export const maybeActivateFormatOnSave = () => { vscode.workspace.onWillSaveTextDocument(e => { const document = e.document; @@ -70,14 +60,39 @@ export const maybeActivateFormatOnSave = () => { return; } let textEditor = vscode.window.activeTextEditor; - if (!textEditor) { + if (!textEditor || textEditor.document.isClosed) { return } let editorConfig = vscode.workspace.getConfiguration('editor'); - const globalEditorFormatOnSave = editorConfig && editorConfig.has('formatOnSave') && editorConfig.get('formatOnSave') === true; - let clojureConfig = vscode.workspace.getConfiguration('clojureVSCode'); + const globalEditorFormatOnSave = editorConfig && editorConfig.has('formatOnSave') && editorConfig.get('formatOnSave') === true, + clojureConfig = vscode.workspace.getConfiguration('clojureVSCode'), + currentText = textEditor.document.getText(), + lastLine = textEditor.document.lineCount - 1, + lastPosition = textEditor.document.lineAt(lastLine).range.end, + range = new vscode.Range(new vscode.Position(0, 0), lastPosition); + if ((clojureConfig.formatOnSave || globalEditorFormatOnSave) && textEditor.document === document) { - formatFile(textEditor, undefined); + formatFile(textEditor.document, range).then(value => { + if (textEditor && value && currentText != value[0].newText) { + textEditor.edit(editBuilder => { + editBuilder.replace(range, value[0].newText); + }); + } + }).catch(reason => { + vscode.window.showErrorMessage(reason); + }); } }); } + + +export class ClojureRangeFormattingEditProvider implements vscode.DocumentRangeFormattingEditProvider { + provideDocumentRangeFormattingEdits( + document: vscode.TextDocument, + range: vscode.Range, + options: vscode.FormattingOptions, + token: vscode.CancellationToken): vscode.ProviderResult { + + return formatFile(document, range); + } +} diff --git a/src/clojureMain.ts b/src/clojureMain.ts index 496c1ac..df10bf0 100644 --- a/src/clojureMain.ts +++ b/src/clojureMain.ts @@ -10,7 +10,7 @@ import { ClojureSignatureProvider } from './clojureSignature'; import { JarContentProvider } from './jarContentProvider'; import { nreplController } from './nreplController'; import { cljConnection } from './cljConnection'; -import { formatFile, maybeActivateFormatOnSave } from './clojureFormat'; +import { ClojureRangeFormattingEditProvider, maybeActivateFormatOnSave } from './clojureFormat'; import { buildTestProvider } from './testRunner' @@ -39,7 +39,7 @@ export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand('clojureVSCode.runAllTests', () => runAllTests(evaluationResultChannel, testResultDataProvidier)); vscode.window.registerTreeDataProvider('clojure', testResultDataProvidier); - vscode.commands.registerTextEditorCommand('clojureVSCode.formatFile', formatFile); + context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider(CLOJURE_MODE, new ClojureRangeFormattingEditProvider())); context.subscriptions.push(vscode.languages.registerCompletionItemProvider(CLOJURE_MODE, new ClojureCompletionItemProvider(), '.', '/')); context.subscriptions.push(vscode.languages.registerDefinitionProvider(CLOJURE_MODE, new ClojureDefinitionProvider()));