Skip to content
Snippets Groups Projects
Commit f6a2fba0 authored by Antonio Maiorano's avatar Antonio Maiorano
Browse files

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@289910 91177308-0d34-0410-b5e6-96231b3b80d8
parent 0ecff4a7
No related branches found
No related tags found
No related merge requests found
# Visual Studio files
.vs/
*.user
/packages/
/ClangFormat/obj/
/ClangFormat/bin/
......
......@@ -61,15 +61,21 @@
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidClangFormatCmdSet" id="cmdidClangFormat" priority="0x0100" type="Button">
<Button guid="guidClangFormatCmdSet" id="cmdidClangFormatSelection" priority="0x0100" type="Button">
<Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
<Icon guid="guidImages" id="bmpPic1" />
<Strings>
<ButtonText>ClangFormat</ButtonText>
<ButtonText>Clang Format Selection</ButtonText>
</Strings>
</Button>
<Button guid="guidClangFormatCmdSet" id="cmdidClangFormatDocument" priority="0x0101" type="Button">
<Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
<Icon guid="guidImages" id="bmpPic2" />
<Strings>
<ButtonText>Clang Format Document</ButtonText>
</Strings>
</Button>
</Buttons>
......@@ -88,7 +94,8 @@
<KeyBindings>
<KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormat" editor="guidTextEditor" key1="R" mod1="Control" key2="F" mod2="Control"/>
<KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormatSelection" editor="guidTextEditor" key1="R" mod1="Control" key2="F" mod2="Control"/>
<KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormatDocument" editor="guidTextEditor" key1="R" mod1="Control" key2="D" mod2="Control"/>
</KeyBindings>
......@@ -101,7 +108,8 @@
<GuidSymbol name="guidClangFormatCmdSet" value="{e39cbab1-0f96-4022-a2bc-da5a9db7eb78}">
<IDSymbol name="MyMenuGroup" value="0x1020" />
<IDSymbol name="cmdidClangFormat" value="0x0100" />
<IDSymbol name="cmdidClangFormatSelection" value="0x0100" />
<IDSymbol name="cmdidClangFormatDocument" value="0x0101" />
</GuidSymbol>
<GuidSymbol name="guidTextEditor" value="{8B382828-6202-11d1-8870-0000F87579D2}" />
......
......@@ -180,14 +180,43 @@ namespace LLVM.ClangFormat
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormat);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);
{
var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormatSelection);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);
}
{
var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormatDocument);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);
}
}
}
#endregion
private void MenuItemCallback(object sender, EventArgs args)
{
var mc = sender as System.ComponentModel.Design.MenuCommand;
if (mc == null)
return;
switch (mc.CommandID.ID)
{
case (int)PkgCmdIDList.cmdidClangFormatSelection:
FormatSelection();
break;
case (int)PkgCmdIDList.cmdidClangFormatDocument:
FormatDocument();
break;
}
}
/// <summary>
/// Runs clang-format on the current selection
/// </summary>
private void FormatSelection()
{
IWpfTextView view = GetCurrentView();
if (view == null)
......@@ -197,24 +226,40 @@ namespace LLVM.ClangFormat
int start = view.Selection.Start.Position.GetContainingLine().Start.Position;
int end = view.Selection.End.Position.GetContainingLine().End.Position;
int length = end - start;
// clang-format doesn't support formatting a range that starts at the end
// of the file.
if (start >= text.Length && text.Length > 0)
start = text.Length - 1;
string path = GetDocumentParent(view);
string filePath = GetDocumentPath(view);
RunClangFormatAndApplyReplacements(text, start, length, path, filePath, view);
}
/// <summary>
/// Runs clang-format on the current document
/// </summary>
private void FormatDocument()
{
IWpfTextView view = GetCurrentView();
if (view == null)
// We're not in a text view.
return;
string filePath = GetDocumentPath(view);
var path = Path.GetDirectoryName(filePath);
string text = view.TextBuffer.CurrentSnapshot.GetText();
RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, view);
}
private void RunClangFormatAndApplyReplacements(string text, int offset, int length, string path, string filePath, IWpfTextView view)
{
try
{
var root = XElement.Parse(RunClangFormat(text, start, length, path, filePath));
var edit = view.TextBuffer.CreateEdit();
foreach (XElement replacement in root.Descendants("replacement"))
{
var span = new Span(
int.Parse(replacement.Attribute("offset").Value),
int.Parse(replacement.Attribute("length").Value));
edit.Replace(span, replacement.Value);
}
edit.Apply();
string replacements = RunClangFormat(text, offset, length, path, filePath);
ApplyClangFormatReplacements(replacements, view);
}
catch (Exception e)
{
......@@ -304,6 +349,27 @@ namespace LLVM.ClangFormat
return output;
}
/// <summary>
/// Applies the clang-format replacements (xml) to the current view
/// </summary>
private void ApplyClangFormatReplacements(string replacements, IWpfTextView view)
{
// clang-format returns no replacements if input text is empty
if (replacements.Length == 0)
return;
var root = XElement.Parse(replacements);
var edit = view.TextBuffer.CreateEdit();
foreach (XElement replacement in root.Descendants("replacement"))
{
var span = new Span(
int.Parse(replacement.Attribute("offset").Value),
int.Parse(replacement.Attribute("length").Value));
edit.Replace(span, replacement.Value);
}
edit.Apply();
}
/// <summary>
/// Returns the currently active view if it is a IWpfTextView.
/// </summary>
......
......@@ -2,6 +2,7 @@
{
static class PkgCmdIDList
{
public const uint cmdidClangFormat = 0x100;
public const uint cmdidClangFormatSelection = 0x100;
public const uint cmdidClangFormatDocument = 0x101;
};
}
\ No newline at end of file
......@@ -25,3 +25,27 @@ directory so they can be bundled with the plug-in, as well as creating
ClangFormat/source.extension.vsixmanifest. Once the plug-in has been built with
CMake once, it can be built manually from the ClangFormat.sln solution in Visual
Studio.
===========
Debugging
===========
Once you've built the clang_format_vsix project from LLVM.sln at least once,
open ClangFormat.sln in Visual Studio, then:
- Make sure the "Debug" target is selected
- Open the ClangFormat project properties
- Select the Debug tab
- Set "Start external program:" to where your devenv.exe is installed. Typically
it's "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe"
- Set "Command line arguments" to: /rootsuffix Exp
- You can now set breakpoints if you like
- Press F5 to build and run with debugger
If all goes well, a new instance of Visual Studio will be launched in a special
mode where it uses the experimental hive instead of the normal configuration hive.
By default, when you build a VSIX project in Visual Studio, it auto-registers the
extension in the experimental hive, allowing you to test it. In the new Visual Studio
instance, open or create a C++ solution, and you should now see the Clang Format
entries in the Tool menu. You can test it out, and any breakpoints you set will be
hit where you can debug as usual.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment