Index: tools/clang-format-vs/.gitignore
===================================================================
--- tools/clang-format-vs/.gitignore
+++ tools/clang-format-vs/.gitignore
@@ -1,5 +1,6 @@
# Visual Studio files
.vs/
+*.user
/packages/
/ClangFormat/obj/
/ClangFormat/bin/
Index: tools/clang-format-vs/ClangFormat/ClangFormat.vsct
===================================================================
--- tools/clang-format-vs/ClangFormat/ClangFormat.vsct
+++ tools/clang-format-vs/ClangFormat/ClangFormat.vsct
@@ -61,15 +61,21 @@
DynamicVisibility
If you do not want an image next to your command, remove the Icon node /> -->
-
+
- ClangFormat
+ Clang Format Selection
-
+
+
+
+
+ Clang Format Document
+
+
@@ -88,7 +94,8 @@
-
+
+
@@ -101,7 +108,8 @@
-
+
+
Index: tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
+++ tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
@@ -180,15 +180,44 @@
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;
+ }
+ }
+
+ ///
+ /// Runs clang-format on the current selection
+ ///
+ private void FormatSelection()
+ {
IWpfTextView view = GetCurrentView();
if (view == null)
// We're not in a text view.
@@ -197,24 +226,40 @@
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);
+ }
+
+ ///
+ /// Runs clang-format on the current document
+ ///
+ 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)
{
@@ -295,16 +340,43 @@
string output = process.StandardOutput.ReadToEnd();
// 5. clang-format is done, wait until it is fully shut down.
process.WaitForExit();
- if (process.ExitCode != 0)
+
+ // FIXME: If clang-format writes enough to the standard error stream to block,
+ // we will never reach this point; instead, read the standard error asynchronously.
+ string stdErr = process.StandardError.ReadToEnd();
+
+ if (process.ExitCode != 0 || stdErr.Length > 0)
{
- // FIXME: If clang-format writes enough to the standard error stream to block,
- // we will never reach this point; instead, read the standard error asynchronously.
- throw new Exception(process.StandardError.ReadToEnd());
+ throw new Exception(
+ (stdErr.Length > 0 ? stdErr + "\n\n" : "") +
+ "(Exit Code: " + process.ExitCode + ")");
}
+
return output;
}
///
+ /// Applies the clang-format replacements (xml) to the current view
+ ///
+ 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();
+ }
+
+ ///
/// Returns the currently active view if it is a IWpfTextView.
///
private IWpfTextView GetCurrentView()
Index: tools/clang-format-vs/ClangFormat/PkgCmdID.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/PkgCmdID.cs
+++ tools/clang-format-vs/ClangFormat/PkgCmdID.cs
@@ -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
Index: tools/clang-format-vs/README.txt
===================================================================
--- tools/clang-format-vs/README.txt
+++ tools/clang-format-vs/README.txt
@@ -25,3 +25,27 @@
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.