Skip to content

Commit f4b0bc0

Browse files
committed
Format statement around cursor
1 parent c249d61 commit f4b0bc0

File tree

3 files changed

+81
-13
lines changed

3 files changed

+81
-13
lines changed

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
],
2828
"main": "./out/src/extension",
2929
"activationEvents": [
30+
"onCommand:clang-format.formatSelection",
3031
"onLanguage:cpp",
3132
"onLanguage:c",
3233
"onLanguage:objective-c",
@@ -40,6 +41,13 @@
4041
"onLanguage:glsl"
4142
],
4243
"contributes": {
44+
"commands": [
45+
{
46+
"command": "clang-format.formatSelection",
47+
"title": "Format selection or around cursor",
48+
"category": "Clang-Format"
49+
}
50+
],
4351
"configuration": {
4452
"type": "object",
4553
"title": "Clang-Format configuration",

src/clangMode.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ for (let l of ['cpp', 'c', 'objective-c', 'objective-cpp', 'java', 'javascript',
1414
}
1515
}
1616

17+
export const LANGUAGES: string[] = languages;
18+
1719
export const MODES: vscode.DocumentFilter[] = languages.map((language) => ({language, scheme: 'file'}));

src/extension.ts

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@ import * as vscode from 'vscode';
22
import cp = require('child_process');
33
import path = require('path');
44
import {MODES,
5-
ALIAS} from './clangMode';
5+
ALIAS,
6+
LANGUAGES} from './clangMode';
67
import {getBinPath} from './clangPath';
78
import sax = require('sax');
89

910
export let outputChannel = vscode.window.createOutputChannel('Clang-Format');
1011

12+
interface FormatResult {
13+
edits: vscode.TextEdit[];
14+
newCursorPos: vscode.Position;
15+
}
16+
17+
interface EditInfo {
18+
offset: number;
19+
length: number;
20+
}
21+
1122
export class ClangDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider, vscode.DocumentRangeFormattingEditProvider {
1223
private defaultConfigure = {
1324
executable: 'clang-format',
@@ -17,15 +28,46 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
1728
};
1829

1930
public provideDocumentFormattingEdits(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<vscode.TextEdit[]> {
20-
return this.doFormatDocument(document, null, options, token);
31+
return this.doFormatDocument(document, null, options, token).then((result) => {
32+
return result.edits;
33+
});
2134
}
2235

2336
public provideDocumentRangeFormattingEdits(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<vscode.TextEdit[]> {
24-
return this.doFormatDocument(document, range, options, token);
37+
return this.doFormatDocument(document, range, options, token).then((result) => {
38+
return result.edits;
39+
});
40+
}
41+
42+
public formatSelection(): void {
43+
let editor = vscode.window.activeTextEditor;
44+
let document = editor.document;
45+
46+
if (LANGUAGES.indexOf(document.languageId) === -1) {
47+
vscode.commands.executeCommand('editor.action.formatSelection');
48+
return;
49+
}
50+
51+
this.doFormatDocument(editor.document, editor.selection, undefined, undefined).then(
52+
(result) => {
53+
editor.edit((editBuilder) => {
54+
for (let edit of result.edits)
55+
editBuilder.replace(edit.range, edit.newText);
56+
}).then((didEdits) => {
57+
if (!didEdits)
58+
vscode.window.showErrorMessage('Could not apply formatting edits');
59+
else
60+
editor.selection = new vscode.Selection(result.newCursorPos, result.newCursorPos);
61+
});
62+
}
63+
);
2564
}
2665

27-
private getEdits(document: vscode.TextDocument, xml: string, codeContent: string): Thenable<vscode.TextEdit[]> {
66+
private getEdits(document: vscode.TextDocument, xml: string,
67+
codeContent: string): Thenable<FormatResult> {
2868
return new Promise((resolve, reject) => {
69+
let newCursorPosInfo = {offset: 0, length: 0};
70+
2971
let options = {
3072
trim: false,
3173
normalize: false,
@@ -61,6 +103,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
61103
return editInfo;
62104
};
63105

106+
let onNewCursorPos = false;
107+
64108
parser.onerror = (err) => {
65109
reject(err.message);
66110
};
@@ -74,6 +118,10 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
74118
case 'replacements':
75119
return;
76120

121+
case 'cursor':
122+
onNewCursorPos = true;
123+
break;
124+
77125
case 'replacement':
78126
currentEdit = {
79127
length: parseInt(tag.attributes['length'].toString()),
@@ -90,12 +138,18 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
90138
};
91139

92140
parser.ontext = (text) => {
93-
if (!currentEdit) { return; }
94-
95-
currentEdit.text = text;
141+
if (onNewCursorPos) {
142+
newCursorPosInfo.offset = parseInt(text);
143+
byteToOffset(newCursorPosInfo);
144+
} else if (currentEdit)
145+
currentEdit.text = text;
96146
};
97147

98148
parser.onclosetag = (tagName) => {
149+
if (onNewCursorPos) {
150+
onNewCursorPos = false;
151+
return;
152+
}
99153
if (!currentEdit) { return; }
100154

101155
let start = document.positionAt(currentEdit.offset);
@@ -108,7 +162,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
108162
};
109163

110164
parser.onend = () => {
111-
resolve(edits);
165+
resolve({edits, newCursorPos: document.positionAt(newCursorPosInfo.offset)});
112166
};
113167

114168
parser.write(xml);
@@ -174,7 +228,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
174228
return assumedFilename;
175229
}
176230

177-
private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<vscode.TextEdit[]> {
231+
private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<FormatResult> {
178232
return new Promise((resolve, reject) => {
179233
let filename = document.fileName;
180234

@@ -225,6 +279,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
225279
offset = Buffer.byteLength(codeContent.substr(0, offset), 'utf8');
226280

227281
formatArgs.push(`-offset=${offset}`, `-length=${length}`);
282+
if (length === 0)
283+
formatArgs.push(`-cursor=${offset}`);
228284
}
229285

230286
let workingPath = vscode.workspace.rootPath;
@@ -243,10 +299,6 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
243299
}
244300
});
245301
}
246-
247-
public formatDocument(document: vscode.TextDocument): Thenable<vscode.TextEdit[]> {
248-
return this.doFormatDocument(document, null, null, null);
249-
}
250302
}
251303

252304
let diagnosticCollection: vscode.DiagnosticCollection;
@@ -261,4 +313,10 @@ export function activate(ctx: vscode.ExtensionContext): void {
261313
ctx.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(mode, formatter));
262314
availableLanguages[mode.language] = true;
263315
});
316+
317+
ctx.subscriptions.push(
318+
vscode.commands.registerCommand('clang-format.formatSelection',
319+
() => {
320+
formatter.formatSelection();
321+
}));
264322
}

0 commit comments

Comments
 (0)