Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 27 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "clang-format",
"displayName": "Clang-Format",
"description": "Use Clang-Format in Visual Studio Code",
"version": "1.9.0",
"version": "99.9.0",
"publisher": "xaver",
"engines": {
"vscode": "^1.1.0"
Expand All @@ -12,7 +12,8 @@
"theme": "dark"
},
"dependencies": {
"sax": "^1.2.1"
"sax": "^1.2.1",
"shlex": "^2.0.1"
},
"categories": [
"Formatters"
Expand All @@ -28,6 +29,7 @@
],
"main": "./out/src/extension",
"activationEvents": [
"onCommand:clang-format.formatSelection",
"onLanguage:cpp",
"onLanguage:c",
"onLanguage:objective-c",
Expand All @@ -42,6 +44,18 @@
"onLanguage:cuda"
],
"contributes": {
"commands": [
{
"command": "clang-format.formatSelection",
"title": "Format selection or around cursor",
"category": "Clang-Format"
},
{
"command": "clang-format.formatSelectionAlternate",
"title": "Format selection or around cursor (alternate)",
"category": "Clang-Format"
}
],
"configuration": {
"type": "object",
"title": "Clang-Format configuration",
Expand Down Expand Up @@ -71,10 +85,15 @@
"default": "",
"description": "clang-format fallback style for C++, left empty to use clang-format.style"
},
"clang-format.language.cpp.alternate.style": {
"type": "string",
"default": "",
"description": "clang-format fallback style for C++, left empty to use clang-format.style"
},
"clang-format.language.cpp.fallbackStyle": {
"type": "string",
"default": "",
"description": "clang-format fallback style for C++, left empty to use clang-format.fallbackStyle"
"description": "clang-format alternate style for C++"
},
"clang-format.language.c.enable": {
"type": "boolean",
Expand All @@ -86,6 +105,11 @@
"default": "",
"description": "clang-format fallback style for C, left empty to use clang-format.style"
},
"clang-format.language.c.alternate.style": {
"type": "string",
"default": "",
"description": "clang-format alternate style for C"
},
"clang-format.language.c.fallbackStyle": {
"type": "string",
"default": "",
Expand Down
2 changes: 2 additions & 0 deletions src/clangMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ for (let l of ['cpp', 'c', 'objective-c', 'objective-cpp', 'java', 'javascript',
}
}

export const LANGUAGES: string[] = languages;

export const MODES: vscode.DocumentFilter[] = languages.map((language) => ({language, scheme: 'file'}));
105 changes: 86 additions & 19 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@ import * as vscode from 'vscode';
import cp = require('child_process');
import path = require('path');
import {MODES,
ALIAS} from './clangMode';
ALIAS,
LANGUAGES} from './clangMode';
import {getBinPath} from './clangPath';
import sax = require('sax');
import * as shlex from 'shlex';

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

interface FormatResult {
edits: vscode.TextEdit[];
newCursorPos: vscode.Position;
}

interface EditInfo {
offset: number;
length: number;
}

export class ClangDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider, vscode.DocumentRangeFormattingEditProvider {
private defaultConfigure = {
executable: 'clang-format',
Expand All @@ -17,15 +29,46 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
};

public provideDocumentFormattingEdits(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<vscode.TextEdit[]> {
return this.doFormatDocument(document, null, options, token);
return this.doFormatDocument(document, null, options, token).then((result) => {
return result.edits;
});
}

public provideDocumentRangeFormattingEdits(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<vscode.TextEdit[]> {
return this.doFormatDocument(document, range, options, token);
return this.doFormatDocument(document, range, options, token).then((result) => {
return result.edits;
});
}

public formatSelection(alternate = false): void {
let editor = vscode.window.activeTextEditor;
let document = editor.document;

if (LANGUAGES.indexOf(document.languageId) === -1) {
vscode.commands.executeCommand('editor.action.formatSelection');
return;
}

this.doFormatDocument(editor.document, editor.selection, undefined, undefined, alternate).then(
(result) => {
editor.edit((editBuilder) => {
for (let edit of result.edits)
editBuilder.replace(edit.range, edit.newText);
}).then((didEdits) => {
if (!didEdits)
vscode.window.showErrorMessage('Could not apply formatting edits');
else
editor.selection = new vscode.Selection(result.newCursorPos, result.newCursorPos);
});
}
);
}

private getEdits(document: vscode.TextDocument, xml: string, codeContent: string): Thenable<vscode.TextEdit[]> {
private getEdits(document: vscode.TextDocument, xml: string,
codeContent: string): Thenable<FormatResult> {
return new Promise((resolve, reject) => {
let newCursorPosInfo = {offset: 0, length: 0};

let options = {
trim: false,
normalize: false,
Expand Down Expand Up @@ -61,6 +104,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
return editInfo;
};

let onNewCursorPos = false;

parser.onerror = (err) => {
reject(err.message);
};
Expand All @@ -74,6 +119,10 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
case 'replacements':
return;

case 'cursor':
onNewCursorPos = true;
break;

case 'replacement':
currentEdit = {
length: parseInt(tag.attributes['length'].toString()),
Expand All @@ -90,12 +139,18 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
};

parser.ontext = (text) => {
if (!currentEdit) { return; }

currentEdit.text = text;
if (onNewCursorPos) {
newCursorPosInfo.offset = parseInt(text);
byteToOffset(newCursorPosInfo);
} else if (currentEdit)
currentEdit.text = text;
};

parser.onclosetag = (tagName) => {
if (onNewCursorPos) {
onNewCursorPos = false;
return;
}
if (!currentEdit) { return; }

let start = document.positionAt(currentEdit.offset);
Expand All @@ -108,7 +163,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
};

parser.onend = () => {
resolve(edits);
resolve({edits, newCursorPos: document.positionAt(newCursorPosInfo.offset)});
};

parser.write(xml);
Expand Down Expand Up @@ -138,8 +193,9 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
return ALIAS[document.languageId] || document.languageId;
}

private getStyle(document: vscode.TextDocument) {
let ret = vscode.workspace.getConfiguration('clang-format').get<string>(`language.${this.getLanguage(document)}.style`);
private getStyle(document: vscode.TextDocument, alternate = false) {
const styleOpt = alternate ? 'alternate.style' : 'style';
let ret = vscode.workspace.getConfiguration('clang-format').get<string>(`language.${this.getLanguage(document)}.${styleOpt}`);
if (ret.trim()) {
return ret.trim();
}
Expand Down Expand Up @@ -174,7 +230,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
return assumedFilename;
}

private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable<vscode.TextEdit[]> {
private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken, alternate = false): Thenable<FormatResult> {
return new Promise((resolve, reject) => {
let filename = document.fileName;

Expand All @@ -183,7 +239,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma

let formatArgs = [
'-output-replacements-xml',
`-style=${this.getStyle(document)}`,
`-style=${this.getStyle(document, alternate)}`,
`-fallback-style=${this.getFallbackStyle(document)}`,
`-assume-filename=${this.getAssumedFilename(document)}`
];
Expand All @@ -198,6 +254,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
offset = Buffer.byteLength(codeContent.substr(0, offset), 'utf8');

formatArgs.push(`-offset=${offset}`, `-length=${length}`);
if (length === 0)
formatArgs.push(`-cursor=${offset}`);
}

let workingPath = vscode.workspace.rootPath;
Expand All @@ -207,6 +265,9 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma

let stdout = '';
let stderr = '';
const argsString = formatArgs.map(shlex.quote).join(' ');
outputChannel.clear();
outputChannel.appendLine(`Calling with arguments: ${argsString}`)
let child = cp.spawn(formatCommandBinPath, formatArgs, { cwd: workingPath });
child.stdin.end(codeContent);
child.stdout.on('data', chunk => stdout += chunk);
Expand All @@ -221,13 +282,12 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
child.on('close', code => {
try {
if (stderr.length != 0) {
outputChannel.show();
outputChannel.clear();
outputChannel.appendLine(stderr);
return reject('Cannot format due to syntax errors.');
}

if (code != 0) {
outputChannel.show();
outputChannel.appendLine(`Process exited with code ${code}`);
return reject();
}

Expand All @@ -245,10 +305,6 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma
}
});
}

public formatDocument(document: vscode.TextDocument): Thenable<vscode.TextEdit[]> {
return this.doFormatDocument(document, null, null, null);
}
}

let diagnosticCollection: vscode.DiagnosticCollection;
Expand All @@ -263,4 +319,15 @@ export function activate(ctx: vscode.ExtensionContext): void {
ctx.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(mode, formatter));
availableLanguages[mode.language] = true;
});

ctx.subscriptions.push(
vscode.commands.registerCommand('clang-format.formatSelection',
() => {
formatter.formatSelection();
}));
ctx.subscriptions.push(
vscode.commands.registerCommand('clang-format.formatSelectionAlternate',
() => {
formatter.formatSelection(true /* alternate */);
}));
}