From 31db2fde2eb07087401dbcc6bbc72f5b4fde9497 Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Mon, 12 Jun 2017 18:14:51 -0700 Subject: [PATCH 1/6] Manually send textdocument/formatting request --- src/features/DocumentFormatter.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/features/DocumentFormatter.ts b/src/features/DocumentFormatter.ts index 9bf9805411..12fd176403 100644 --- a/src/features/DocumentFormatter.ts +++ b/src/features/DocumentFormatter.ts @@ -17,7 +17,8 @@ import { TextEditor, TextLine } from 'vscode'; -import { LanguageClient, RequestType } from 'vscode-languageclient'; +import { LanguageClient, RequestType, DocumentFormattingRequest } from 'vscode-languageclient'; +import { TextDocumentIdentifier } from "vscode-languageserver-types"; import Window = vscode.window; import { IFeature } from '../feature'; import * as Settings from '../settings'; @@ -195,7 +196,15 @@ class PSDocumentFormattingEditProvider implements document: TextDocument, options: FormattingOptions, token: CancellationToken): TextEdit[] | Thenable { - return this.provideDocumentRangeFormattingEdits(document, null, options, token); + return this.languageClient.sendRequest( + DocumentFormattingRequest.type, + { + textDocument: TextDocumentIdentifier.create(document.uri.toString()), + options: { + insertSpaces: true, + tabSize: 4 + } + }); } provideDocumentRangeFormattingEdits( From 9976c97b1084f7d975ea500eec0213020c282cdb Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Mon, 12 Jun 2017 18:23:35 -0700 Subject: [PATCH 2/6] Add method to get formatting options from configuration --- src/features/DocumentFormatter.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/features/DocumentFormatter.ts b/src/features/DocumentFormatter.ts index 12fd176403..baa4d91b91 100644 --- a/src/features/DocumentFormatter.ts +++ b/src/features/DocumentFormatter.ts @@ -200,10 +200,7 @@ class PSDocumentFormattingEditProvider implements DocumentFormattingRequest.type, { textDocument: TextDocumentIdentifier.create(document.uri.toString()), - options: { - insertSpaces: true, - tabSize: 4 - } + options: this.getEditorSettings() }); } @@ -449,6 +446,14 @@ class PSDocumentFormattingEditProvider implements return settings; } + private getEditorSettings(): { insertSpaces: boolean, tabSize: number} { + let editorConfiguration = vscode.workspace.getConfiguration("editor"); + return { + insertSpaces: editorConfiguration.get("insertSpaces"), + tabSize: editorConfiguration.get("tabSize") + }; + } + private static showStatusBar(document: TextDocument, hideWhenDone: Thenable): void { let statusBar = AnimatedStatusBar.showAnimatedStatusBarMessage("Formatting PowerShell document", hideWhenDone); this.statusBarTracker[document.uri.toString()] = statusBar; From d56694fcd9d7b2e59459b15436ef0f9d1dd11e3b Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Tue, 13 Jun 2017 14:57:25 -0700 Subject: [PATCH 3/6] Manually send textdocument/rangeFormatting request --- src/features/DocumentFormatter.ts | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/features/DocumentFormatter.ts b/src/features/DocumentFormatter.ts index baa4d91b91..c83f7cbbb3 100644 --- a/src/features/DocumentFormatter.ts +++ b/src/features/DocumentFormatter.ts @@ -17,7 +17,13 @@ import { TextEditor, TextLine } from 'vscode'; -import { LanguageClient, RequestType, DocumentFormattingRequest } from 'vscode-languageclient'; +import { + LanguageClient, + RequestType, + DocumentFormattingRequest, + DocumentRangeFormattingParams, + DocumentRangeFormattingRequest +} from 'vscode-languageclient'; import { TextDocumentIdentifier } from "vscode-languageserver-types"; import Window = vscode.window; import { IFeature } from '../feature'; @@ -221,7 +227,24 @@ class PSDocumentFormattingEditProvider implements return this.emptyPromise; } - let textEdits: Thenable = this.executeRulesInOrder(editor, range, options, 0); + let requestParams: DocumentRangeFormattingParams = { + textDocument: TextDocumentIdentifier.create(document.uri.toString()), + range: { + start: { + line: range.start.line, + character: range.start.character + }, + end: { + line: range.end.line, + character: range.end.character + } + }, + options: this.getEditorSettings() + }; + + let textEdits = this.languageClient.sendRequest( + DocumentRangeFormattingRequest.type, + requestParams); this.lockDocument(document, textEdits); PSDocumentFormattingEditProvider.showStatusBar(document, textEdits); return textEdits; From 3935924213c41aa789a71e7a604f1661d35f3ce1 Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Tue, 13 Jun 2017 15:15:14 -0700 Subject: [PATCH 4/6] Use range formatting for full document formatting --- src/features/DocumentFormatter.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/features/DocumentFormatter.ts b/src/features/DocumentFormatter.ts index c83f7cbbb3..0f89103c16 100644 --- a/src/features/DocumentFormatter.ts +++ b/src/features/DocumentFormatter.ts @@ -202,13 +202,8 @@ class PSDocumentFormattingEditProvider implements document: TextDocument, options: FormattingOptions, token: CancellationToken): TextEdit[] | Thenable { - return this.languageClient.sendRequest( - DocumentFormattingRequest.type, - { - textDocument: TextDocumentIdentifier.create(document.uri.toString()), - options: this.getEditorSettings() - }); - } + return this.provideDocumentRangeFormattingEdits(document, null, options, token); + } provideDocumentRangeFormattingEdits( document: TextDocument, @@ -227,9 +222,12 @@ class PSDocumentFormattingEditProvider implements return this.emptyPromise; } - let requestParams: DocumentRangeFormattingParams = { - textDocument: TextDocumentIdentifier.create(document.uri.toString()), - range: { + + // somehow range object gets serialized to an array of Position objects, + // so we need to use the object literal syntax to initialize it. + let rangeParam = null; + if (range != null) { + rangeParam = { start: { line: range.start.line, character: range.start.character @@ -238,7 +236,12 @@ class PSDocumentFormattingEditProvider implements line: range.end.line, character: range.end.character } - }, + }; + }; + + let requestParams: DocumentRangeFormattingParams = { + textDocument: TextDocumentIdentifier.create(document.uri.toString()), + range: rangeParam, options: this.getEditorSettings() }; From d5b153e568fd77d6e9cc7758ffee7c406fa5b335 Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Tue, 13 Jun 2017 15:15:34 -0700 Subject: [PATCH 5/6] Format DocumentFormatter.ts file --- src/features/DocumentFormatter.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/features/DocumentFormatter.ts b/src/features/DocumentFormatter.ts index 0f89103c16..0c12a2d845 100644 --- a/src/features/DocumentFormatter.ts +++ b/src/features/DocumentFormatter.ts @@ -168,9 +168,9 @@ class DocumentLocker { } class PSDocumentFormattingEditProvider implements - DocumentFormattingEditProvider, - DocumentRangeFormattingEditProvider, - OnTypeFormattingEditProvider { + DocumentFormattingEditProvider, + DocumentRangeFormattingEditProvider, + OnTypeFormattingEditProvider { private static documentLocker = new DocumentLocker(); private static statusBarTracker = new Object(); private languageClient: LanguageClient; @@ -303,7 +303,8 @@ class PSDocumentFormattingEditProvider implements private snapRangeToEdges(range: Range, document: TextDocument): Range { return range.with({ start: range.start.with({ character: 0 }), - end: document.lineAt(range.end.line).range.end }); + end: document.lineAt(range.end.line).range.end + }); } private getEditor(document: TextDocument): TextEditor { @@ -472,7 +473,7 @@ class PSDocumentFormattingEditProvider implements return settings; } - private getEditorSettings(): { insertSpaces: boolean, tabSize: number} { + private getEditorSettings(): { insertSpaces: boolean, tabSize: number } { let editorConfiguration = vscode.workspace.getConfiguration("editor"); return { insertSpaces: editorConfiguration.get("insertSpaces"), From 601b26f7e994fab6d8c2f0a5dd459693815c325a Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Tue, 13 Jun 2017 15:18:15 -0700 Subject: [PATCH 6/6] Remove unused items from DocumentFormatter.ts --- src/features/DocumentFormatter.ts | 230 +----------------------------- 1 file changed, 2 insertions(+), 228 deletions(-) diff --git a/src/features/DocumentFormatter.ts b/src/features/DocumentFormatter.ts index 0c12a2d845..9bacc9b44f 100644 --- a/src/features/DocumentFormatter.ts +++ b/src/features/DocumentFormatter.ts @@ -31,10 +31,6 @@ import * as Settings from '../settings'; import * as Utils from '../utils'; import * as AnimatedStatusBar from '../controls/animatedStatusBar'; -export namespace ScriptFileMarkersRequest { - export const type = new RequestType("powerShell/getScriptFileMarkers"); -} - export namespace ScriptRegionRequest { export const type = new RequestType("powerShell/getScriptRegion"); } @@ -50,29 +46,6 @@ interface ScriptRegionRequestResult { scriptRegion: ScriptRegion; } -// TODO move some of the common interface to a separate file? -interface ScriptFileMarkersRequestParams { - fileUri: string; - settings: any; -} - -interface ScriptFileMarkersRequestResultParams { - markers: ScriptFileMarker[]; -} - -interface ScriptFileMarker { - message: string; - level: ScriptFileMarkerLevel; - scriptRegion: ScriptRegion; - correction: MarkerCorrection; -} - -enum ScriptFileMarkerLevel { - Information = 0, - Warning, - Error -} - interface ScriptRegion { file: string; text: string; @@ -84,11 +57,6 @@ interface ScriptRegion { endOffset: number; } -interface MarkerCorrection { - name: string; - edits: ScriptRegion[]; -} - function toRange(scriptRegion: ScriptRegion): vscode.Range { return new vscode.Range( scriptRegion.startLineNumber - 1, @@ -101,24 +69,6 @@ function toOneBasedPosition(position: Position): Position { return position.translate({ lineDelta: 1, characterDelta: 1 }); } -function editComparer(leftOperand: ScriptRegion, rightOperand: ScriptRegion): number { - if (leftOperand.startLineNumber < rightOperand.startLineNumber) { - return -1; - } else if (leftOperand.startLineNumber > rightOperand.startLineNumber) { - return 1; - } else { - if (leftOperand.startColumnNumber < rightOperand.startColumnNumber) { - return -1; - } - else if (leftOperand.startColumnNumber > rightOperand.startColumnNumber) { - return 1; - } - else { - return 0; - } - } -} - class DocumentLocker { private lockedDocuments: Object; @@ -171,31 +121,16 @@ class PSDocumentFormattingEditProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider { + private static documentLocker = new DocumentLocker(); private static statusBarTracker = new Object(); private languageClient: LanguageClient; - private lineDiff: number; - - // The order in which the rules will be executed starting from the first element. - private readonly ruleOrder: string[] = [ - "PSPlaceCloseBrace", - "PSPlaceOpenBrace", - "PSUseConsistentWhitespace", - "PSUseConsistentIndentation", - "PSAlignAssignmentStatement"] - - // Allows edits to be undone and redone is a single step. - // It is usefuld to have undo stops after every edit while debugging - // hence we keep this as an option but set it true by default. - private aggregateUndoStop: boolean; private get emptyPromise(): Promise { return Promise.resolve(TextEdit[0]); } - constructor(aggregateUndoStop = true) { - this.aggregateUndoStop = aggregateUndoStop; - this.lineDiff = 0; + constructor() { } provideDocumentFormattingEdits( @@ -300,13 +235,6 @@ class PSDocumentFormattingEditProvider implements }); } - private snapRangeToEdges(range: Range, document: TextDocument): Range { - return range.with({ - start: range.start.with({ character: 0 }), - end: document.lineAt(range.end.line).range.end - }); - } - private getEditor(document: TextDocument): TextEditor { return Window.visibleTextEditors.find((e, n, obj) => { return e.document === document; }); } @@ -319,160 +247,6 @@ class PSDocumentFormattingEditProvider implements PSDocumentFormattingEditProvider.documentLocker.lock(document, unlockWhenDone); } - private executeRulesInOrder( - editor: TextEditor, - range: Range, - options: FormattingOptions, - index: number): Thenable { - if (this.languageClient !== null && index < this.ruleOrder.length) { - let rule: string = this.ruleOrder[index]; - let uniqueEdits: ScriptRegion[] = []; - let document: TextDocument = editor.document; - let edits: ScriptRegion[]; - - return this.languageClient.sendRequest( - ScriptFileMarkersRequest.type, - { - fileUri: document.uri.toString(), - settings: this.getSettings(rule) - }) - .then((result: ScriptFileMarkersRequestResultParams) => { - edits = result.markers.map(marker => { return marker.correction.edits[0]; }); - - // sort in decending order of the edits - edits.sort((left: ScriptRegion, right: ScriptRegion) => { - return -1 * editComparer(left, right); - }); - - - // we need to update the range as the edits might - // have changed the original layout - if (range !== null) { - if (this.lineDiff !== 0) { - range = range.with({ end: range.end.translate({ lineDelta: this.lineDiff }) }); - } - - // extend the range such that it starts at the first character of the - // start line of the range. - range = this.snapRangeToEdges(range, document); - - // filter edits that are contained in the input range - edits = edits.filter(edit => range.contains(toRange(edit).start)); - } - - // We cannot handle multiple edits at the same point hence we - // filter the markers so that there is only one edit per region - if (edits.length > 0) { - uniqueEdits.push(edits[0]); - for (let edit of edits.slice(1)) { - let lastEdit: ScriptRegion = uniqueEdits[uniqueEdits.length - 1]; - if (lastEdit.startLineNumber !== edit.startLineNumber - || (edit.startColumnNumber + edit.text.length) < lastEdit.startColumnNumber) { - uniqueEdits.push(edit); - } - } - } - - // reset line difference to 0 - this.lineDiff = 0; - - // we do not return a valid array because our text edits - // need to be executed in a particular order and it is - // easier if we perform the edits ourselves - return this.applyEdit(editor, uniqueEdits, 0, index); - }) - .then(() => { - // execute the same rule again if we left out violations - // on the same line - let newIndex: number = index + 1; - if (uniqueEdits.length !== edits.length) { - newIndex = index; - } - - return this.executeRulesInOrder(editor, range, options, newIndex); - }); - } else { - return this.emptyPromise; - } - } - - private applyEdit( - editor: TextEditor, - edits: ScriptRegion[], - markerIndex: number, - ruleIndex: number): Thenable { - if (markerIndex >= edits.length) { - return; - } - - let undoStopAfter = !this.aggregateUndoStop || (ruleIndex === this.ruleOrder.length - 1 && markerIndex === edits.length - 1); - let undoStopBefore = !this.aggregateUndoStop || (ruleIndex === 0 && markerIndex === 0); - let edit: ScriptRegion = edits[markerIndex]; - let editRange: Range = toRange(edit); - - - // accumulate the changes in number of lines - // get the difference between the number of lines in the replacement text and - // that of the original text - this.lineDiff += this.getNumLines(edit.text) - (editRange.end.line - editRange.start.line + 1); - return editor.edit((editBuilder) => { - editBuilder.replace( - editRange, - edit.text); - }, - { - undoStopAfter: undoStopAfter, - undoStopBefore: undoStopBefore - }).then((isEditApplied) => { - return this.applyEdit(editor, edits, markerIndex + 1, ruleIndex); - }); // TODO handle rejection - } - - private getNumLines(text: string): number { - return text.split(/\r?\n/).length; - } - - private getSettings(rule: string): any { - let psSettings: Settings.ISettings = Settings.load(Utils.PowerShellLanguageId); - let ruleSettings = new Object(); - ruleSettings["Enable"] = true; - - switch (rule) { - case "PSPlaceOpenBrace": - ruleSettings["OnSameLine"] = psSettings.codeFormatting.openBraceOnSameLine; - ruleSettings["NewLineAfter"] = psSettings.codeFormatting.newLineAfterOpenBrace; - ruleSettings["IgnoreOneLineBlock"] = psSettings.codeFormatting.ignoreOneLineBlock; - break; - - case "PSPlaceCloseBrace": - ruleSettings["IgnoreOneLineBlock"] = psSettings.codeFormatting.ignoreOneLineBlock; - ruleSettings["NewLineAfter"] = psSettings.codeFormatting.newLineAfterCloseBrace; - break; - - case "PSUseConsistentIndentation": - ruleSettings["IndentationSize"] = vscode.workspace.getConfiguration("editor").get("tabSize"); - break; - - case "PSUseConsistentWhitespace": - ruleSettings["CheckOpenBrace"] = psSettings.codeFormatting.whitespaceBeforeOpenBrace; - ruleSettings["CheckOpenParen"] = psSettings.codeFormatting.whitespaceBeforeOpenParen; - ruleSettings["CheckOperator"] = psSettings.codeFormatting.whitespaceAroundOperator; - ruleSettings["CheckSeparator"] = psSettings.codeFormatting.whitespaceAfterSeparator; - break; - - case "PSAlignAssignmentStatement": - ruleSettings["CheckHashtable"] = psSettings.codeFormatting.alignPropertyValuePairs; - break; - - default: - break; - } - - let settings: Object = new Object(); - settings[rule] = ruleSettings; - return settings; - } - private getEditorSettings(): { insertSpaces: boolean, tabSize: number } { let editorConfiguration = vscode.workspace.getConfiguration("editor"); return {