Skip to content

Commit b3236a4

Browse files
author
Kapil Borle
committed
Add an initial version of code formatting feature
1 parent f8822bb commit b3236a4

File tree

2 files changed

+144
-1
lines changed

2 files changed

+144
-1
lines changed

src/features/DocumentFormatter.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import vscode = require('vscode');
2+
import { languages, TextDocument, TextEdit, FormattingOptions, CancellationToken } from 'vscode'
3+
import { LanguageClient, RequestType, NotificationType } from 'vscode-languageclient';
4+
import Window = vscode.window;
5+
import { IFeature } from '../feature';
6+
7+
export namespace ScriptFileMarkersRequest {
8+
export const type: RequestType<any, any, void> = { get method(): string { return "powerShell/getScriptFileMarkers"; } };
9+
}
10+
11+
// TODO move some of the common interface to a separate file?
12+
interface ScriptFileMarkersRequestParams {
13+
filePath: string;
14+
rules: string[];
15+
}
16+
17+
interface ScriptFileMarkersRequestResultParams {
18+
markers: ScriptFileMarker[];
19+
}
20+
21+
interface ScriptFileMarker {
22+
message: string;
23+
level: ScriptFileMarkerLevel;
24+
scriptRegion: ScriptRegion;
25+
correction: MarkerCorrection;
26+
}
27+
28+
enum ScriptFileMarkerLevel {
29+
Information = 0,
30+
Warning,
31+
Error
32+
}
33+
34+
interface ScriptRegion {
35+
file: string;
36+
text: string;
37+
startLineNumber: number;
38+
startColumnNumber: number;
39+
startOffset: number;
40+
endLineNumber: number;
41+
endColumnNumber: number;
42+
endOffset: number;
43+
}
44+
45+
interface MarkerCorrection {
46+
name: string;
47+
edits: ScriptRegion[]
48+
}
49+
50+
class PSDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
51+
private languageClient: LanguageClient;
52+
private readonly ruleOrder: string[] = [
53+
"PSPlaceCloseBrace",
54+
"PSPlaceOpenBrace"];
55+
56+
provideDocumentFormattingEdits(
57+
document: TextDocument,
58+
options: FormattingOptions,
59+
token: CancellationToken): TextEdit[] | Thenable<TextEdit[]> {
60+
61+
// we need to order the edits such that edit i should not invalidate
62+
// the edits in edit j s.t i < j (seems like a hard problem)
63+
// or
64+
// peform edits ourself and return an empty textedit array
65+
return this.applyEditsInOrder(document, options, 0);
66+
}
67+
68+
applyEditsInOrder(
69+
document: TextDocument,
70+
options: FormattingOptions,
71+
index: number): Thenable<TextEdit[]> | TextEdit[] {
72+
if (this.languageClient !== null && index < this.ruleOrder.length) {
73+
return this.languageClient.sendRequest(
74+
ScriptFileMarkersRequest.type,
75+
{
76+
filePath: document.fileName,
77+
rules: [this.ruleOrder[index]]
78+
})
79+
.then((result: ScriptFileMarkersRequestResultParams) => {
80+
81+
// TODO modify undo stops to make sure all the edits
82+
// can be undone and redone in a single step
83+
this.applyEdits(result.markers, 0);
84+
this.applyEditsInOrder(document, options, ++index);
85+
86+
// we do not return a valid array because our text edits
87+
// need to be executed in a particular order and it is
88+
// easier if we perform the edits ourselves
89+
return TextEdit[0];
90+
});
91+
} else {
92+
return TextEdit[0];
93+
}
94+
}
95+
96+
applyEdits(markers: ScriptFileMarker[], index: number): void {
97+
if (index >= markers.length) {
98+
return;
99+
}
100+
101+
let edit: ScriptRegion = markers[index++].correction.edits[0];
102+
Window.activeTextEditor.edit((editBuilder) => {
103+
editBuilder.replace(
104+
new vscode.Range(
105+
edit.startLineNumber - 1,
106+
edit.startColumnNumber - 1,
107+
edit.endLineNumber - 1,
108+
edit.endColumnNumber - 1),
109+
edit.text);
110+
}).then((isEditApplied) => {
111+
this.applyEdits(markers, index);
112+
}); // TODO handle rejection
113+
114+
}
115+
116+
setLanguageClient(languageClient: LanguageClient): void {
117+
this.languageClient = languageClient;
118+
}
119+
}
120+
121+
export class DocumentFormatterFeature implements IFeature {
122+
private disposable: vscode.Disposable;
123+
private languageClient: LanguageClient;
124+
private documentFormattingEditProvider: PSDocumentFormattingEditProvider;
125+
126+
constructor() {
127+
this.documentFormattingEditProvider = new PSDocumentFormattingEditProvider();
128+
this.disposable = vscode.languages.registerDocumentFormattingEditProvider(
129+
"powershell",
130+
this.documentFormattingEditProvider);
131+
}
132+
133+
public setLanguageClient(languageclient: LanguageClient): void {
134+
this.languageClient = languageclient;
135+
this.documentFormattingEditProvider.setLanguageClient(languageclient);
136+
}
137+
138+
public dispose(): any {
139+
this.disposable.dispose();
140+
}
141+
}

src/main.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { FindModuleFeature } from './features/PowerShellFindModule';
1818
import { ExtensionCommandsFeature } from './features/ExtensionCommands';
1919
import { SelectPSSARulesFeature } from './features/SelectPSSARules';
2020
import { CodeActionsFeature } from './features/CodeActions';
21+
import { DocumentFormatterFeature } from './features/DocumentFormatter';
2122

2223
// NOTE: We will need to find a better way to deal with the required
2324
// PS Editor Services version...
@@ -95,7 +96,8 @@ export function activate(context: vscode.ExtensionContext): void {
9596
new ExtensionCommandsFeature(),
9697
new SelectPSSARulesFeature(),
9798
new CodeActionsFeature(),
98-
new NewFileOrProjectFeature()
99+
new NewFileOrProjectFeature(),
100+
new DocumentFormatterFeature(),
99101
];
100102

101103
sessionManager =

0 commit comments

Comments
 (0)