Skip to content

Commit 78e11f2

Browse files
committed
commands: executeFormatDocumentProvider, executeFormatRangeProvider
1 parent a7f719e commit 78e11f2

4 files changed

Lines changed: 163 additions & 31 deletions

File tree

src/vs/editor/contrib/format/common/format.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,67 @@
55

66
'use strict';
77

8-
import {IFormattingSupport} from 'vs/editor/common/modes';
8+
import {IFormattingSupport, IFormattingOptions} from 'vs/editor/common/modes';
99
import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry';
10+
import {onUnexpectedError, illegalArgument} from 'vs/base/common/errors';
11+
import URI from 'vs/base/common/uri';
12+
import {IAction, Action} from 'vs/base/common/actions';
13+
import {IModelService} from 'vs/editor/common/services/modelService';
14+
import {TPromise} from 'vs/base/common/winjs.base';
15+
import {IModel, IRange, IPosition, ISingleEditOperation} from 'vs/editor/common/editorCommon';
16+
import {Range} from 'vs/editor/common/core/range';
17+
import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions';
1018

1119
export const FormatRegistry = new LanguageFeatureRegistry<IFormattingSupport>('formattingSupport');
1220
export const FormatOnTypeRegistry = new LanguageFeatureRegistry<IFormattingSupport>('formattingSupport');
1321

14-
export {IFormattingSupport};
22+
export {IFormattingSupport};
23+
24+
export function formatRange(model: IModel, range: IRange, options: IFormattingOptions): TPromise<ISingleEditOperation[]> {
25+
const support = FormatRegistry.ordered(model)[0];
26+
if (!support) {
27+
return;
28+
}
29+
return support.formatRange(model.getAssociatedResource(), range, options);
30+
}
31+
32+
export function formatDocument(model: IModel, options: IFormattingOptions): TPromise<ISingleEditOperation[]> {
33+
const support = FormatRegistry.ordered(model)[0];
34+
if (!support) {
35+
return;
36+
}
37+
if (typeof support.formatDocument !== 'function') {
38+
if (typeof support.formatRange === 'function') {
39+
return formatRange(model, model.getFullModelRange(), options);
40+
} else {
41+
return;
42+
}
43+
}
44+
45+
return support.formatDocument(model.getAssociatedResource(), options);
46+
}
47+
48+
CommonEditorRegistry.registerLanguageCommand('_executeFormatRangeProvider', function(accessor, args) {
49+
const {resource, range, options} = args;
50+
if (!URI.isURI(resource) || !Range.isIRange(range)) {
51+
throw illegalArgument();
52+
}
53+
const model = accessor.get(IModelService).getModel(resource);
54+
if (!model) {
55+
throw illegalArgument('resource');
56+
}
57+
return formatRange(model, range, options);
58+
});
59+
60+
CommonEditorRegistry.registerLanguageCommand('_executeFormatDocumentProvider', function(accessor, args) {
61+
const {resource, options} = args;
62+
if (!URI.isURI(resource)) {
63+
throw illegalArgument('resource');
64+
}
65+
const model = accessor.get(IModelService).getModel(resource);
66+
if (!model) {
67+
throw illegalArgument('resource');
68+
}
69+
70+
return formatDocument(model, options)
71+
});

src/vs/editor/contrib/format/common/formatActions.ts

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import formatCommand = require('./formatCommand');
1515
import {Range} from 'vs/editor/common/core/range';
1616
import {INullService} from 'vs/platform/instantiation/common/instantiation';
1717
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
18-
import {FormatOnTypeRegistry, FormatRegistry, IFormattingSupport} from '../common/format';
18+
import {FormatOnTypeRegistry, FormatRegistry, IFormattingSupport, formatRange, formatDocument} from '../common/format';
1919

2020
interface IFormatOnTypeResult {
2121
range: EditorCommon.IEditorRange;
@@ -170,31 +170,19 @@ export class FormatAction extends EditorAction {
170170

171171
public run(): TPromise<boolean> {
172172

173-
var model = this.editor.getModel(),
174-
formattingSupport = FormatRegistry.ordered(model)[0],
175-
canFormatRange = typeof formattingSupport.formatRange === 'function',
176-
canFormatDocument = typeof formattingSupport.formatDocument === 'function',
177-
editorSelection = this.editor.getSelection();
178-
179-
var options = this.editor.getIndentationOptions(),
180-
formattingPromise: TPromise<EditorCommon.ISingleEditOperation[]>;
181-
182-
if(canFormatRange) {
183-
// format a selection/range
184-
var formatRange: EditorCommon.IEditorRange = editorSelection;
185-
if(!formatRange.isEmpty()) {
186-
// Fix the selection to include the entire line to improve formatting results
187-
formatRange.startColumn = 1;
188-
} else {
189-
formatRange = model.getFullModelRange();
190-
}
191-
formattingPromise = formattingSupport.formatRange(model.getAssociatedResource(), formatRange, options);
173+
const model = this.editor.getModel(),
174+
editorSelection = this.editor.getSelection(),
175+
options = this.editor.getIndentationOptions();
176+
177+
let formattingPromise: TPromise<EditorCommon.ISingleEditOperation[]>;
192178

193-
} else if(canFormatDocument) {
194-
// format the whole document
195-
formattingPromise = formattingSupport.formatDocument(model.getAssociatedResource(), options);
179+
if (editorSelection.isEmpty()) {
180+
formattingPromise = formatDocument(model, options);
196181
} else {
197-
// broken support?
182+
formattingPromise = formatRange(model, editorSelection, options);
183+
}
184+
185+
if (!formattingPromise) {
198186
return TPromise.as(false);
199187
}
200188

src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ import {ICodeLensData} from 'vs/editor/contrib/codelens/common/codelens';
4848
// vscode.executeCompletionItemProvider
4949
// vscode.executeCodeActionProvider
5050
// vscode.executeCodeLensProvider
51-
5251
// vscode.executeFormatDocumentProvider
5352
// vscode.executeFormatRangeProvider
53+
5454
// vscode.executeFormatOnTypeProvider
5555

5656
export class ExtHostLanguageFeatureCommands {
@@ -72,6 +72,8 @@ export class ExtHostLanguageFeatureCommands {
7272
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider);
7373
this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider);
7474
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider);
75+
this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider);
76+
this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider);
7577
}
7678

7779
private _register(id: string, callback: (...args: any[]) => any): void {
@@ -217,10 +219,7 @@ export class ExtHostLanguageFeatureCommands {
217219
}
218220

219221
private _executeCodeLensProvider(resource: URI): Thenable<vscode.CodeLens[]>{
220-
const args = {
221-
resource
222-
};
223-
222+
const args = { resource };
224223
return this._commands.executeCommand<ICodeLensData[]>('_executeCodeLensProvider', args).then(value => {
225224
if (Array.isArray(value)) {
226225
return value.map(item => {
@@ -230,4 +229,29 @@ export class ExtHostLanguageFeatureCommands {
230229
}
231230
});
232231
}
232+
233+
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
234+
const args = {
235+
resource,
236+
options
237+
};
238+
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatDocumentProvider', args).then(value => {
239+
if (Array.isArray(value)) {
240+
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
241+
}
242+
});
243+
}
244+
245+
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
246+
const args = {
247+
resource,
248+
range: typeConverters.fromRange(range),
249+
options
250+
};
251+
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatRangeProvider', args).then(value => {
252+
if (Array.isArray(value)) {
253+
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
254+
}
255+
});
256+
}
233257
}

src/vs/workbench/test/common/api/extHostLanguageFeatures.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {getNavigateToItems} from 'vs/workbench/parts/search/common/search';
3737
import {rename} from 'vs/editor/contrib/rename/common/rename';
3838
import {getParameterHints} from 'vs/editor/contrib/parameterHints/common/parameterHints';
3939
import {suggest} from 'vs/editor/contrib/suggest/common/suggest';
40+
import {formatDocument, formatRange} from 'vs/editor/contrib/format/common/format';
4041

4142
const defaultSelector = { scheme: 'far' };
4243
const model: EditorCommon.IModel = new EditorModel(
@@ -907,4 +908,66 @@ suite('ExtHostLanguageFeatures', function() {
907908
});
908909
});
909910
});
911+
912+
// --- format
913+
914+
test('Format Doc, data conversion', function(done) {
915+
disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, <vscode.DocumentFormattingEditProvider>{
916+
provideDocumentFormattingEdits(): any {
917+
return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'testing')];
918+
}
919+
}));
920+
921+
threadService.sync().then(() => {
922+
formatDocument(model, { insertSpaces: true, tabSize: 4 }).then(value => {
923+
assert.equal(value.length, 1);
924+
let [first] = value;
925+
assert.equal(first.text, 'testing');
926+
assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 });
927+
done();
928+
});
929+
});
930+
});
931+
932+
test('Format Doc, evil provider', function(done) {
933+
disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, <vscode.DocumentFormattingEditProvider>{
934+
provideDocumentFormattingEdits(): any {
935+
throw new Error('evil');
936+
}
937+
}));
938+
939+
threadService.sync().then(() => {
940+
formatDocument(model, { insertSpaces: true, tabSize: 4 }).then(undefined, err => done());
941+
});
942+
});
943+
944+
test('Format Range, data conversion', function(done) {
945+
disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, <vscode.DocumentRangeFormattingEditProvider>{
946+
provideDocumentRangeFormattingEdits(): any {
947+
return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'testing')];
948+
}
949+
}));
950+
951+
threadService.sync().then(() => {
952+
formatRange(model, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }, { insertSpaces: true, tabSize: 4 }).then(value => {
953+
assert.equal(value.length, 1);
954+
let [first] = value;
955+
assert.equal(first.text, 'testing');
956+
assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 });
957+
done();
958+
});
959+
});
960+
})
961+
962+
test('Format Range, evil provider', function(done) {
963+
disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, <vscode.DocumentRangeFormattingEditProvider>{
964+
provideDocumentRangeFormattingEdits(): any {
965+
throw new Error('evil');
966+
}
967+
}));
968+
969+
threadService.sync().then(() => {
970+
formatRange(model, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }, { insertSpaces: true, tabSize: 4 }).then(undefined, err => done());
971+
});
972+
})
910973
});

0 commit comments

Comments
 (0)