Skip to content

Commit 7703ddd

Browse files
committed
clang-format-vsix: add command to format document
Bound to Ctrl+R, Ctrl+D by default. Also added section on how to debug the extension to the Readme. Differential Revision: https://reviews.llvm.org/D27501 llvm-svn: 289910
1 parent 5ccb65f commit 7703ddd

File tree

5 files changed

+119
-19
lines changed

5 files changed

+119
-19
lines changed

clang/tools/clang-format-vs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Visual Studio files
22
.vs/
3+
*.user
34
/packages/
45
/ClangFormat/obj/
56
/ClangFormat/bin/

clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,21 @@
6161
<CommandFlag>DynamicVisibility</CommandFlag>
6262
If you do not want an image next to your command, remove the Icon node /> -->
6363

64-
<Button guid="guidClangFormatCmdSet" id="cmdidClangFormat" priority="0x0100" type="Button">
64+
<Button guid="guidClangFormatCmdSet" id="cmdidClangFormatSelection" priority="0x0100" type="Button">
6565
<Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
6666
<Icon guid="guidImages" id="bmpPic1" />
6767
<Strings>
68-
<ButtonText>ClangFormat</ButtonText>
68+
<ButtonText>Clang Format Selection</ButtonText>
6969
</Strings>
7070
</Button>
7171

72-
72+
<Button guid="guidClangFormatCmdSet" id="cmdidClangFormatDocument" priority="0x0101" type="Button">
73+
<Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
74+
<Icon guid="guidImages" id="bmpPic2" />
75+
<Strings>
76+
<ButtonText>Clang Format Document</ButtonText>
77+
</Strings>
78+
</Button>
7379

7480
</Buttons>
7581

@@ -88,7 +94,8 @@
8894

8995

9096
<KeyBindings>
91-
<KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormat" editor="guidTextEditor" key1="R" mod1="Control" key2="F" mod2="Control"/>
97+
<KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormatSelection" editor="guidTextEditor" key1="R" mod1="Control" key2="F" mod2="Control"/>
98+
<KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormatDocument" editor="guidTextEditor" key1="R" mod1="Control" key2="D" mod2="Control"/>
9299
</KeyBindings>
93100

94101

@@ -101,7 +108,8 @@
101108
<GuidSymbol name="guidClangFormatCmdSet" value="{e39cbab1-0f96-4022-a2bc-da5a9db7eb78}">
102109

103110
<IDSymbol name="MyMenuGroup" value="0x1020" />
104-
<IDSymbol name="cmdidClangFormat" value="0x0100" />
111+
<IDSymbol name="cmdidClangFormatSelection" value="0x0100" />
112+
<IDSymbol name="cmdidClangFormatDocument" value="0x0101" />
105113
</GuidSymbol>
106114

107115
<GuidSymbol name="guidTextEditor" value="{8B382828-6202-11d1-8870-0000F87579D2}" />

clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,43 @@ protected override void Initialize()
180180
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
181181
if (commandService != null)
182182
{
183-
var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormat);
184-
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
185-
commandService.AddCommand(menuItem);
183+
{
184+
var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormatSelection);
185+
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
186+
commandService.AddCommand(menuItem);
187+
}
188+
189+
{
190+
var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormatDocument);
191+
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
192+
commandService.AddCommand(menuItem);
193+
}
186194
}
187195
}
188196
#endregion
189197

190198
private void MenuItemCallback(object sender, EventArgs args)
199+
{
200+
var mc = sender as System.ComponentModel.Design.MenuCommand;
201+
if (mc == null)
202+
return;
203+
204+
switch (mc.CommandID.ID)
205+
{
206+
case (int)PkgCmdIDList.cmdidClangFormatSelection:
207+
FormatSelection();
208+
break;
209+
210+
case (int)PkgCmdIDList.cmdidClangFormatDocument:
211+
FormatDocument();
212+
break;
213+
}
214+
}
215+
216+
/// <summary>
217+
/// Runs clang-format on the current selection
218+
/// </summary>
219+
private void FormatSelection()
191220
{
192221
IWpfTextView view = GetCurrentView();
193222
if (view == null)
@@ -197,24 +226,40 @@ private void MenuItemCallback(object sender, EventArgs args)
197226
int start = view.Selection.Start.Position.GetContainingLine().Start.Position;
198227
int end = view.Selection.End.Position.GetContainingLine().End.Position;
199228
int length = end - start;
229+
200230
// clang-format doesn't support formatting a range that starts at the end
201231
// of the file.
202232
if (start >= text.Length && text.Length > 0)
203233
start = text.Length - 1;
204234
string path = GetDocumentParent(view);
205235
string filePath = GetDocumentPath(view);
236+
237+
RunClangFormatAndApplyReplacements(text, start, length, path, filePath, view);
238+
}
239+
240+
/// <summary>
241+
/// Runs clang-format on the current document
242+
/// </summary>
243+
private void FormatDocument()
244+
{
245+
IWpfTextView view = GetCurrentView();
246+
if (view == null)
247+
// We're not in a text view.
248+
return;
249+
250+
string filePath = GetDocumentPath(view);
251+
var path = Path.GetDirectoryName(filePath);
252+
string text = view.TextBuffer.CurrentSnapshot.GetText();
253+
254+
RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, view);
255+
}
256+
257+
private void RunClangFormatAndApplyReplacements(string text, int offset, int length, string path, string filePath, IWpfTextView view)
258+
{
206259
try
207260
{
208-
var root = XElement.Parse(RunClangFormat(text, start, length, path, filePath));
209-
var edit = view.TextBuffer.CreateEdit();
210-
foreach (XElement replacement in root.Descendants("replacement"))
211-
{
212-
var span = new Span(
213-
int.Parse(replacement.Attribute("offset").Value),
214-
int.Parse(replacement.Attribute("length").Value));
215-
edit.Replace(span, replacement.Value);
216-
}
217-
edit.Apply();
261+
string replacements = RunClangFormat(text, offset, length, path, filePath);
262+
ApplyClangFormatReplacements(replacements, view);
218263
}
219264
catch (Exception e)
220265
{
@@ -304,6 +349,27 @@ private string RunClangFormat(string text, int offset, int length, string path,
304349
return output;
305350
}
306351

352+
/// <summary>
353+
/// Applies the clang-format replacements (xml) to the current view
354+
/// </summary>
355+
private void ApplyClangFormatReplacements(string replacements, IWpfTextView view)
356+
{
357+
// clang-format returns no replacements if input text is empty
358+
if (replacements.Length == 0)
359+
return;
360+
361+
var root = XElement.Parse(replacements);
362+
var edit = view.TextBuffer.CreateEdit();
363+
foreach (XElement replacement in root.Descendants("replacement"))
364+
{
365+
var span = new Span(
366+
int.Parse(replacement.Attribute("offset").Value),
367+
int.Parse(replacement.Attribute("length").Value));
368+
edit.Replace(span, replacement.Value);
369+
}
370+
edit.Apply();
371+
}
372+
307373
/// <summary>
308374
/// Returns the currently active view if it is a IWpfTextView.
309375
/// </summary>

clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
static class PkgCmdIDList
44
{
5-
public const uint cmdidClangFormat = 0x100;
5+
public const uint cmdidClangFormatSelection = 0x100;
6+
public const uint cmdidClangFormatDocument = 0x101;
67
};
78
}

clang/tools/clang-format-vs/README.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,27 @@ directory so they can be bundled with the plug-in, as well as creating
2525
ClangFormat/source.extension.vsixmanifest. Once the plug-in has been built with
2626
CMake once, it can be built manually from the ClangFormat.sln solution in Visual
2727
Studio.
28+
29+
===========
30+
Debugging
31+
===========
32+
33+
Once you've built the clang_format_vsix project from LLVM.sln at least once,
34+
open ClangFormat.sln in Visual Studio, then:
35+
36+
- Make sure the "Debug" target is selected
37+
- Open the ClangFormat project properties
38+
- Select the Debug tab
39+
- Set "Start external program:" to where your devenv.exe is installed. Typically
40+
it's "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe"
41+
- Set "Command line arguments" to: /rootsuffix Exp
42+
- You can now set breakpoints if you like
43+
- Press F5 to build and run with debugger
44+
45+
If all goes well, a new instance of Visual Studio will be launched in a special
46+
mode where it uses the experimental hive instead of the normal configuration hive.
47+
By default, when you build a VSIX project in Visual Studio, it auto-registers the
48+
extension in the experimental hive, allowing you to test it. In the new Visual Studio
49+
instance, open or create a C++ solution, and you should now see the Clang Format
50+
entries in the Tool menu. You can test it out, and any breakpoints you set will be
51+
hit where you can debug as usual.

0 commit comments

Comments
 (0)