diff --git a/clang/tools/clang-format-vs/.gitignore b/clang/tools/clang-format-vs/.gitignore index 6453dd8ecef2..c9d25d96c8ec 100644 --- a/clang/tools/clang-format-vs/.gitignore +++ b/clang/tools/clang-format-vs/.gitignore @@ -1,5 +1,6 @@ # Visual Studio files .vs/ +*.user /packages/ /ClangFormat/obj/ /ClangFormat/bin/ diff --git a/clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct b/clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct index 3e3e22e67d42..798957740d54 100644 --- a/clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct +++ b/clang/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 /> --> - - + @@ -88,7 +94,8 @@ - + + @@ -101,7 +108,8 @@ - + + diff --git a/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs b/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs index 6af2fd177f0f..c7eac42211ad 100644 --- a/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs +++ b/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs @@ -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; + } + } + + /// + /// Runs clang-format on the current selection + /// + 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); + } + + /// + /// 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) { @@ -304,6 +349,27 @@ namespace LLVM.ClangFormat 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. /// diff --git a/clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs b/clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs index bb6b4559a98d..fcc31ee95b1e 100644 --- a/clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs +++ b/clang/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 diff --git a/clang/tools/clang-format-vs/README.txt b/clang/tools/clang-format-vs/README.txt index edfffed774c1..84e0b451f018 100644 --- a/clang/tools/clang-format-vs/README.txt +++ b/clang/tools/clang-format-vs/README.txt @@ -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.