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
This commit is contained in:
Antonio Maiorano 2016-12-16 01:51:43 +00:00
parent 5ccb65f77d
commit 7703ddd48e
5 changed files with 119 additions and 19 deletions

View File

@ -1,5 +1,6 @@
# Visual Studio files
.vs/
*.user
/packages/
/ClangFormat/obj/
/ClangFormat/bin/

View File

@ -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}" />

View File

@ -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>

View File

@ -2,6 +2,7 @@
{
static class PkgCmdIDList
{
public const uint cmdidClangFormat = 0x100;
public const uint cmdidClangFormatSelection = 0x100;
public const uint cmdidClangFormatDocument = 0x101;
};
}

View 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.