From 15852fc162eb6f77c28b67c868fdeeffed8c57d4 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Wed, 4 Sep 2013 16:11:46 +0000 Subject: Adds a VSPackage project that builds a VS extension to run clang-format over a selection / the line at the cursor. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189955 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/clang-format-vs/ClangFormat.sln | 20 ++ .../clang-format-vs/ClangFormat/ClangFormat.csproj | 190 +++++++++++++++++++ tools/clang-format-vs/ClangFormat/ClangFormat.vsct | 119 ++++++++++++ .../ClangFormat/ClangFormatPackage.cs | 210 +++++++++++++++++++++ .../ClangFormat/GlobalSuppressions.cs | 11 ++ tools/clang-format-vs/ClangFormat/Guids.cs | 12 ++ tools/clang-format-vs/ClangFormat/PkgCmdID.cs | 7 + .../ClangFormat/Properties/AssemblyInfo.cs | 33 ++++ .../ClangFormat/Resources.Designer.cs | 64 +++++++ tools/clang-format-vs/ClangFormat/Resources.resx | 129 +++++++++++++ .../ClangFormat/Resources/Images.png | Bin 0 -> 989 bytes .../ClangFormat/Resources/Package.ico | Bin 0 -> 2998 bytes tools/clang-format-vs/ClangFormat/VSPackage.resx | 140 ++++++++++++++ .../ClangFormat/source.extension.vsixmanifest | 18 ++ tools/clang-format-vs/README | 6 + 15 files changed, 959 insertions(+) create mode 100644 tools/clang-format-vs/ClangFormat.sln create mode 100644 tools/clang-format-vs/ClangFormat/ClangFormat.csproj create mode 100644 tools/clang-format-vs/ClangFormat/ClangFormat.vsct create mode 100644 tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs create mode 100644 tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs create mode 100644 tools/clang-format-vs/ClangFormat/Guids.cs create mode 100644 tools/clang-format-vs/ClangFormat/PkgCmdID.cs create mode 100644 tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs create mode 100644 tools/clang-format-vs/ClangFormat/Resources.Designer.cs create mode 100644 tools/clang-format-vs/ClangFormat/Resources.resx create mode 100644 tools/clang-format-vs/ClangFormat/Resources/Images.png create mode 100644 tools/clang-format-vs/ClangFormat/Resources/Package.ico create mode 100644 tools/clang-format-vs/ClangFormat/VSPackage.resx create mode 100644 tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest create mode 100644 tools/clang-format-vs/README (limited to 'tools/clang-format-vs') diff --git a/tools/clang-format-vs/ClangFormat.sln b/tools/clang-format-vs/ClangFormat.sln new file mode 100644 index 0000000000..d6b211fe50 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangFormat", "ClangFormat\ClangFormat.csproj", "{7FD1783E-2D31-4D05-BF23-6EBE1B42B608}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/clang-format-vs/ClangFormat/ClangFormat.csproj b/tools/clang-format-vs/ClangFormat/ClangFormat.csproj new file mode 100644 index 0000000000..51e138216d --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/ClangFormat.csproj @@ -0,0 +1,190 @@ + + + + 11.0 + 11.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + false + + + + Debug + AnyCPU + 2.0 + {7FD1783E-2D31-4D05-BF23-6EBE1B42B608} + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + LLVM.ClangFormat + ClangFormat + true + Key.snk + v4.5 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2} + 8 + 0 + 0 + primary + False + False + + + {26AD1324-4B7C-44BC-84F8-B86AED45729F} + 10 + 0 + 0 + primary + False + False + + + {1A31287A-4D7D-413E-8E32-3B374931BD89} + 8 + 0 + 0 + primary + False + False + + + {2CE2370E-D744-4936-A090-3FFFE667B0E1} + 9 + 0 + 0 + primary + False + False + + + {1CBA492E-7263-47BB-87FE-639000619B15} + 8 + 0 + 0 + primary + False + False + + + {00020430-0000-0000-C000-000000000046} + 2 + 0 + 0 + primary + False + False + + + + + + True + True + Resources.resx + + + + Component + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + true + VSPackage + + + + + + Designer + + + + + Menus.ctmenu + + + + + + + + + + true + + + + + if not exist $(ProjectDir)Key.snk ( + "$(FrameworkSDKDir)Bin\NETFX 4.0 Tools\sn.exe" -k $(ProjectDir)Key.snk +) + + + \ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/ClangFormat.vsct b/tools/clang-format-vs/ClangFormat/ClangFormat.vsct new file mode 100644 index 0000000000..9037ecde24 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/ClangFormat.vsct @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs new file mode 100644 index 0000000000..61cc6447c0 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs @@ -0,0 +1,210 @@ +//===-- ClangFormatPackages.cs - VSPackage for clang-format ------*- C# -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class contains a VS extension package that runs clang-format over a +// selection in a VS text editor. +// +//===----------------------------------------------------------------------===// + +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.IO; +using System.Runtime.InteropServices; +using System.Xml.Linq; + +namespace LLVM.ClangFormat +{ + [ClassInterface(ClassInterfaceType.AutoDual)] + [CLSCompliant(false), ComVisible(true)] + public class OptionPageGrid : DialogPage + { + private string style = "File"; + + [Category("LLVM/Clang")] + [DisplayName("Style")] + [Description("Coding style, currently supports:\n" + + " - Predefined styles ('LLVM', 'Google', 'Chromium', 'Mozilla').\n" + + " - 'File' to search for a YAML .clang-format configuration.\n" + + " - A YAML configuration snippet.\n\n" + + "'File':\n" + + " Searches for a .clang-format configuration in the source file's\n" + + " directory and its parents.\n\n" + + "YAML configuration snippet:\n" + + " The content of a .clang-format configuration file, as string.\n" + + " Example: '{BasedOnStyle: \"LLVM\", IndentWidth: 8}'\n\n" + + "See also: http://clang.llvm.org/docs/ClangFormatStyleOptions.html.")] + public string Style + { + get { return style; } + set { style = value; } + } + } + + [PackageRegistration(UseManagedResourcesOnly = true)] + [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] + [ProvideMenuResource("Menus.ctmenu", 1)] + [Guid(GuidList.guidClangFormatPkgString)] + [ProvideOptionPage(typeof(OptionPageGrid), "LLVM/Clang", "ClangFormat", 0, 0, true)] + public sealed class ClangFormatPackage : Package + { + #region Package Members + protected override void Initialize() + { + base.Initialize(); + + 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); + } + } + #endregion + + private void MenuItemCallback(object sender, EventArgs args) + { + IWpfTextView view = GetCurrentView(); + if (view == null) + // We're not in a text view. + return; + string text = view.TextBuffer.CurrentSnapshot.GetText(); + 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); + try + { + var root = XElement.Parse(RunClangFormat(text, start, length, path)); + 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(); + } + catch (Exception e) + { + var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); + var id = Guid.Empty; + int result; + uiShell.ShowMessageBox( + 0, ref id, + "Error while running clang-format:", + e.Message, + string.Empty, 0, + OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, + OLEMSGICON.OLEMSGICON_INFO, + 0, out result); + } + } + + /// + /// Runs the given text through clang-format and returns the replacements as XML. + /// + /// Formats the text range starting at offset of the given length. + /// + private string RunClangFormat(string text, int offset, int length, string path) + { + System.Diagnostics.Process process = new System.Diagnostics.Process(); + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = "clang-format.exe"; + // Poor man's escaping - this will not work when quotes are already escaped + // in the input (but we don't need more). + string style = GetStyle().Replace("\"", "\\\""); + process.StartInfo.Arguments = " -offset " + offset + + " -length " + length + + " -output-replacements-xml " + + " -style \"" + style + "\""; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + if (path != null) + process.StartInfo.WorkingDirectory = path; + // We have to be careful when communicating via standard input / output, + // as writes to the buffers will block until they are read from the other side. + // Thus, we: + // 1. Start the process - clang-format.exe will start to read the input from the + // standard input. + process.Start(); + // 2. We write everything to the standard output - this cannot block, as clang-format + // reads the full standard input before analyzing it without writing anything to the + // standard output. + process.StandardInput.Write(text); + // 3. We notify clang-format that the input is done - after this point clang-format + // will start analyzing the input and eventually write the output. + process.StandardInput.Close(); + // 4. We must read clang-format's output before waiting for it to exit; clang-format + // will close the channel by exiting. + 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. + throw new Exception(process.StandardError.ReadToEnd()); + } + return output; + } + + /// + /// Returns the currently active view if it is a IWpfTextView. + /// + private IWpfTextView GetCurrentView() + { + // The SVsTextManager is a service through which we can get the active view. + var textManager = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager)); + IVsTextView textView; + textManager.GetActiveView(1, null, out textView); + + // Now we have the active view as IVsTextView, but the text interfaces we need + // are in the IWpfTextView. + var userData = (IVsUserData)textView; + if (userData == null) + return null; + Guid guidWpfViewHost = DefGuidList.guidIWpfTextViewHost; + object host; + userData.GetData(ref guidWpfViewHost, out host); + return ((IWpfTextViewHost)host).TextView; + } + + private string GetStyle() + { + var page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid)); + return page.Style; + } + + private string GetDocumentParent(IWpfTextView view) + { + ITextDocument document; + if (view.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document)) + { + return Directory.GetParent(document.FilePath).ToString(); + } + return null; + } + } +} diff --git a/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs b/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs new file mode 100644 index 0000000000..175a74e291 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target +// and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click "In Project +// Suppression File". You do not need to add suppressions to this +// file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] diff --git a/tools/clang-format-vs/ClangFormat/Guids.cs b/tools/clang-format-vs/ClangFormat/Guids.cs new file mode 100644 index 0000000000..c045224cd0 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Guids.cs @@ -0,0 +1,12 @@ +using System; + +namespace LLVM.ClangFormat +{ + static class GuidList + { + public const string guidClangFormatPkgString = "c5286038-25d3-4f65-83a8-51fa2df4a146"; + public const string guidClangFormatCmdSetString = "e39cbab1-0f96-4022-a2bc-da5a9db7eb78"; + + public static readonly Guid guidClangFormatCmdSet = new Guid(guidClangFormatCmdSetString); + }; +} \ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/PkgCmdID.cs b/tools/clang-format-vs/ClangFormat/PkgCmdID.cs new file mode 100644 index 0000000000..bb6b4559a9 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/PkgCmdID.cs @@ -0,0 +1,7 @@ +namespace LLVM.ClangFormat +{ + static class PkgCmdIDList + { + public const uint cmdidClangFormat = 0x100; + }; +} \ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs b/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e6e4de4880 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ClangFormat")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("LLVM")] +[assembly: AssemblyProduct("ClangFormat")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en-US")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tools/clang-format-vs/ClangFormat/Resources.Designer.cs b/tools/clang-format-vs/ClangFormat/Resources.Designer.cs new file mode 100644 index 0000000000..d5d7d3fdbd --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Resources.Designer.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LLVM.ClangFormat { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangFormat.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + } +} diff --git a/tools/clang-format-vs/ClangFormat/Resources.resx b/tools/clang-format-vs/ClangFormat/Resources.resx new file mode 100644 index 0000000000..352987aa07 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Resources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/Resources/Images.png b/tools/clang-format-vs/ClangFormat/Resources/Images.png new file mode 100644 index 0000000000..51fe0d1577 Binary files /dev/null and b/tools/clang-format-vs/ClangFormat/Resources/Images.png differ diff --git a/tools/clang-format-vs/ClangFormat/Resources/Package.ico b/tools/clang-format-vs/ClangFormat/Resources/Package.ico new file mode 100644 index 0000000000..449296f495 Binary files /dev/null and b/tools/clang-format-vs/ClangFormat/Resources/Package.ico differ diff --git a/tools/clang-format-vs/ClangFormat/VSPackage.resx b/tools/clang-format-vs/ClangFormat/VSPackage.resx new file mode 100644 index 0000000000..81102d38a0 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/VSPackage.resx @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ClangFormat + + + Formats code by calling the clang-format executable. + + + Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest b/tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest new file mode 100644 index 0000000000..274c028ed2 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest @@ -0,0 +1,18 @@ + + + + + ClangFormat + Formats code by calling the clang-format executable. + + + + + + + + + + + + diff --git a/tools/clang-format-vs/README b/tools/clang-format-vs/README new file mode 100644 index 0000000000..240821c4af --- /dev/null +++ b/tools/clang-format-vs/README @@ -0,0 +1,6 @@ +This directory contains a VSPackage project to generate a visual studio extension +for clang-format. + +Build prerequisites are: +- Visual Studio 2012 Professional +- Visual Studio SDK (http://www.microsoft.com/en-us/download/details.aspx?id=30668) \ No newline at end of file -- cgit v1.2.3