Index: tools/clang-format-vs/ClangFormat/ClangFormat.csproj
===================================================================
--- tools/clang-format-vs/ClangFormat/ClangFormat.csproj
+++ tools/clang-format-vs/ClangFormat/ClangFormat.csproj
@@ -14,7 +14,7 @@
true
Key.snk
v4.0
- 15.0
+ 14.0
@@ -202,18 +202,17 @@
Menus.ctmenu
+ Designer
-
-
-
true
true
+
@@ -251,11 +250,11 @@
if not exist $(ProjectDir)Key.snk ("$(FrameworkSDKDir)Bin\NETFX 4.6 Tools\sn.exe" -k $(ProjectDir)Key.snk)
-
-
+
\ No newline at end of file
Index: tools/clang-format-vs/ClangFormat/ClangFormat.vsct
===================================================================
--- tools/clang-format-vs/ClangFormat/ClangFormat.vsct
+++ tools/clang-format-vs/ClangFormat/ClangFormat.vsct
@@ -20,12 +20,10 @@
-
-
-
+
-
+
+
+
+
-
-
-
+
+
+
-
-
@@ -61,19 +62,45 @@
DynamicVisibility
If you do not want an image next to your command, remove the Icon node /> -->
-
@@ -95,32 +121,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
Index: tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
+++ tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
@@ -12,6 +12,8 @@
//
//===----------------------------------------------------------------------===//
+#define USE_ODS
+
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
@@ -19,6 +21,8 @@
using Microsoft.VisualStudio.Text.Editor;
using System;
using System.Collections;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.IO;
@@ -36,10 +40,13 @@
private string fallbackStyle = "LLVM";
private bool sortIncludes = false;
private string style = "file";
+ private string altStyle = "file";
private bool formatOnSave = false;
- private string formatOnSaveFileExtensions =
+ private bool altFormatOnOpen = false;
+ private string formatOnFileExtensions =
".c;.cpp;.cxx;.cc;.tli;.tlh;.h;.hh;.hpp;.hxx;.hh;.inl" +
".java;.js;.ts;.m;.mm;.proto;.protodevel;.td";
+ private string formatOnPathFilter = "";
public OptionPageGrid Clone()
{
@@ -91,6 +98,7 @@
" - Predefined styles ('LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit').\n" +
" - 'file' to search for a YAML .clang-format or _clang-format\n" +
" configuration file.\n" +
+ " - 'file:' to explicitly specify the configuration file.\n" +
" - A YAML configuration snippet.\n\n" +
"'File':\n" +
" Searches for a .clang-format or _clang-format configuration file\n" +
@@ -106,6 +114,16 @@
set { style = value; }
}
+ [Category("Format Options")]
+ [DisplayName("Style (Alternate)")]
+ [Description("Alternate Coding style, supports same options as Style.\n")]
+ [TypeConverter(typeof(StyleConverter))]
+ public string AltStyle
+ {
+ get { return altStyle; }
+ set { altStyle = value; }
+ }
+
public sealed class FilenameConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
@@ -136,7 +154,7 @@
[DisplayName("Assume Filename")]
[Description("When reading from stdin, clang-format assumes this " +
"filename to look for a style config file (with 'file' style) " +
- "and to determine the language.")]
+ "and to determine the language. Leave blank to use current document's name.")]
[TypeConverter(typeof(FilenameConverter))]
public string AssumeFilename
{
@@ -166,8 +184,8 @@
}
[Category("Format Options")]
- [DisplayName("Sort includes")]
- [Description("Sort touched include lines.\n\n" +
+ [DisplayName("Sort Includes")]
+ [Description("Sort touched #include lines.\n\n" +
"See also: http://clang.llvm.org/docs/ClangFormat.html.")]
public bool SortIncludes
{
@@ -175,8 +193,8 @@
set { sortIncludes = value; }
}
- [Category("Format On Save")]
- [DisplayName("Enable")]
+ [Category("Format On...")]
+ [DisplayName("Format On Save")]
[Description("Enable running clang-format when modified files are saved. " +
"Will only format if Style is found (ignores Fallback Style)."
)]
@@ -186,14 +204,35 @@
set { formatOnSave = value; }
}
- [Category("Format On Save")]
- [DisplayName("File extensions")]
- [Description("When formatting on save, clang-format will be applied only to " +
+ [Category("Format On...")]
+ [DisplayName("Format On Open (Alternate)")]
+ [Description("Enable running clang-format when files are opened. " +
+ "Will only format if Style (Alternate) is found (ignores Fallback Style)."
+ )]
+ public bool AltFormatOnOpen
+ {
+ get { return altFormatOnOpen; }
+ set { altFormatOnOpen = value; }
+ }
+
+ [Category("Format On...")]
+ [DisplayName("File Extensions")]
+ [Description("When formatting on open/save, clang-format will be applied only to " +
"files with these extensions.")]
- public string FormatOnSaveFileExtensions
+ public string FormatOnFileExtensions
+ {
+ get { return formatOnFileExtensions; }
+ set { formatOnFileExtensions = value; }
+ }
+
+ [Category("Format On...")]
+ [DisplayName("Path Filter")]
+ [Description("When formatting on open/save, clang-format will be applied only to " +
+ "paths matching this regular expression.")]
+ public string FormatOnPathFilter
{
- get { return formatOnSaveFileExtensions; }
- set { formatOnSaveFileExtensions = value; }
+ get { return formatOnPathFilter; }
+ set { formatOnPathFilter = value; }
}
}
@@ -201,44 +240,69 @@
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists)] // Load package on solution load
- [Guid(GuidList.guidClangFormatPkgString)]
+ [Guid(GuidList.guidClangFormatSubmenuString)]
[ProvideOptionPage(typeof(OptionPageGrid), "LLVM/Clang", "ClangFormat", 0, 0, true)]
public sealed class ClangFormatPackage : Package
{
- #region Package Members
-
RunningDocTableEventsDispatcher _runningDocTableEventsDispatcher;
+#if DEBUG
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
+ public static extern void OutputDebugString(string message);
+ public void DebugLog(string s)
+ {
+ OutputDebugString("clang-format-vs: " + s + "\n");
+ }
+#else
+ public void DebugLog(string s) {}
+#endif
+
protected override void Initialize()
{
+ DebugLog("Initialize");
base.Initialize();
_runningDocTableEventsDispatcher = new RunningDocTableEventsDispatcher(this);
_runningDocTableEventsDispatcher.BeforeSave += OnBeforeSave;
+ _runningDocTableEventsDispatcher.BeforeShow += OnBeforeShow;
+ int[] ids =
+ {
+ PkgCmdIDList.cmdidClangFormatSelection,
+ PkgCmdIDList.cmdidClangAltFormatSelection,
+ PkgCmdIDList.cmdidClangFormatDocument,
+ PkgCmdIDList.cmdidClangAltFormatDocument,
+ PkgCmdIDList.cmdidClangFormatOpenDocuments,
+ PkgCmdIDList.cmdidClangAltFormatOpenDocuments
+ };
+
+ DebugLog("Menus");
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
+ foreach (var id in ids)
{
- 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 menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, id);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);
}
}
+
+ DebugLog("Initialized!");
}
- #endregion
OptionPageGrid GetUserOptions()
{
return (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
}
+ OptionPageGrid GetAltUserOptions()
+ {
+ OptionPageGrid copy = GetUserOptions().Clone();
+ copy.Style = copy.AltStyle;
+ return copy;
+ }
+
private void MenuItemCallback(object sender, EventArgs args)
{
var mc = sender as System.ComponentModel.Design.MenuCommand;
@@ -247,20 +311,49 @@
switch (mc.CommandID.ID)
{
- case (int)PkgCmdIDList.cmdidClangFormatSelection:
- FormatSelection(GetUserOptions());
- break;
-
- case (int)PkgCmdIDList.cmdidClangFormatDocument:
- FormatDocument(GetUserOptions());
- break;
+ case PkgCmdIDList.cmdidClangFormatSelection: FormatSelection(GetUserOptions()); break;
+ case PkgCmdIDList.cmdidClangAltFormatSelection: FormatSelection(GetAltUserOptions()); break;
+ case PkgCmdIDList.cmdidClangFormatDocument: FormatDocument(GetUserOptions()); break;
+ case PkgCmdIDList.cmdidClangAltFormatDocument: FormatDocument(GetAltUserOptions()); break;
+ case PkgCmdIDList.cmdidClangFormatOpenDocuments: FormatOpenDocuments(); break;
+ case PkgCmdIDList.cmdidClangAltFormatOpenDocuments: AltFormatOpenDocuments(); break;
}
}
- private static bool FileHasExtension(string filePath, string fileExtensions)
+ private bool FileMatchesFilter(string filePath, string fileExtensions, string pathFilter)
{
- var extensions = fileExtensions.ToLower().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
- return extensions.Contains(Path.GetExtension(filePath).ToLower());
+ if (fileExtensions.Length > 0)
+ {
+ var extensions = fileExtensions.ToLower().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+ if (!extensions.Contains(Path.GetExtension(filePath).ToLower()))
+ return false;
+ }
+ if (pathFilter.Length > 0)
+ {
+ try
+ {
+ Regex filter = new Regex(pathFilter, RegexOptions.IgnoreCase);
+ if (!filter.IsMatch(filePath))
+ return false;
+ }
+ catch (Exception e)
+ {
+ var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
+ var id = Guid.Empty;
+ int result;
+ uiShell.ShowMessageBox(
+ 0, ref id,
+ "Error in path filter regex:",
+ e.Message,
+ string.Empty, 0,
+ OLEMSGBUTTON.OLEMSGBUTTON_OK,
+ OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
+ OLEMSGICON.OLEMSGICON_INFO,
+ 0, out result);
+ return false;
+ }
+ }
+ return true;
}
private void OnBeforeSave(object sender, Document document)
@@ -270,15 +363,48 @@
if (!options.FormatOnSave)
return;
- if (!FileHasExtension(document.FullName, options.FormatOnSaveFileExtensions))
+ if (!FileMatchesFilter(document.FullName, options.FormatOnFileExtensions, options.FormatOnPathFilter))
return;
- if (!Vsix.IsDocumentDirty(document))
- return;
+ DebugLog("OnBeforeSave " + document.FullName);
+
+ bool wasDirty = Vsix.IsDocumentDirty(document);
+ DateTime lastModified = Vsix.LastContentModifiedTime(document);
- var optionsWithNoFallbackStyle = GetUserOptions().Clone();
+ var optionsWithNoFallbackStyle = options.Clone();
optionsWithNoFallbackStyle.FallbackStyle = "none";
FormatDocument(document, optionsWithNoFallbackStyle);
+
+ if (!wasDirty)
+ {
+ Vsix.CleanDocument(document, lastModified);
+ }
+ }
+
+ private void OnBeforeShow(object sender, Document document)
+ {
+ var options = GetUserOptions();
+
+ if (!options.AltFormatOnOpen)
+ return;
+
+ if (!FileMatchesFilter(document.FullName, options.FormatOnFileExtensions, options.FormatOnPathFilter))
+ return;
+
+ DebugLog("OnBeforeShow " + document.FullName);
+
+ bool wasDirty = Vsix.IsDocumentDirty(document);
+ DateTime lastModified = Vsix.LastContentModifiedTime(document);
+
+ var altOptionsWithNoFallbackStyle = options.Clone();
+ altOptionsWithNoFallbackStyle.Style = altOptionsWithNoFallbackStyle.AltStyle;
+ altOptionsWithNoFallbackStyle.FallbackStyle = "none";
+ FormatDocument(document, altOptionsWithNoFallbackStyle);
+
+ if (!wasDirty)
+ {
+ Vsix.CleanDocument(document, lastModified);
+ }
}
///
@@ -287,8 +413,8 @@
private void FormatSelection(OptionPageGrid options)
{
IWpfTextView view = Vsix.GetCurrentView();
+ // Are we in a text view?
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;
@@ -318,6 +444,22 @@
FormatView(Vsix.GetDocumentView(document), options);
}
+ ///
+ /// Runs "On Save" clang-format on all open documents
+ ///
+ private void FormatOpenDocuments()
+ {
+ _runningDocTableEventsDispatcher.FormatOpenDocuments();
+ }
+
+ ///
+ /// Runs "On Open" alternate clang-format on all open documents
+ ///
+ private void AltFormatOpenDocuments()
+ {
+ _runningDocTableEventsDispatcher.AltFormatOpenDocuments();
+ }
+
private void FormatView(IWpfTextView view, OptionPageGrid options)
{
if (view == null)
Index: tools/clang-format-vs/ClangFormat/Guids.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/Guids.cs
+++ tools/clang-format-vs/ClangFormat/Guids.cs
@@ -4,7 +4,7 @@
{
static class GuidList
{
- public const string guidClangFormatPkgString = "c5286038-25d3-4f65-83a8-51fa2df4a146";
+ public const string guidClangFormatSubmenuString = "c5286038-25d3-4f65-83a8-51fa2df4a146";
public const string guidClangFormatCmdSetString = "e39cbab1-0f96-4022-a2bc-da5a9db7eb78";
public static readonly Guid guidClangFormatCmdSet = new Guid(guidClangFormatCmdSetString);
Index: tools/clang-format-vs/ClangFormat/PkgCmdID.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/PkgCmdID.cs
+++ tools/clang-format-vs/ClangFormat/PkgCmdID.cs
@@ -2,7 +2,11 @@
{
static class PkgCmdIDList
{
- public const uint cmdidClangFormatSelection = 0x100;
- public const uint cmdidClangFormatDocument = 0x101;
+ public const int cmdidClangFormatSelection = 0x101;
+ public const int cmdidClangAltFormatSelection = 0x102;
+ public const int cmdidClangFormatDocument = 0x103;
+ public const int cmdidClangAltFormatDocument = 0x104;
+ public const int cmdidClangFormatOpenDocuments = 0x105;
+ public const int cmdidClangAltFormatOpenDocuments = 0x106;
};
}
\ No newline at end of file
Index: tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs
+++ tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs
@@ -3,7 +3,11 @@
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System.Linq;
-
+using System.Collections.Generic;
+#if DEBUG
+using System.Runtime.InteropServices;
+#endif
+
namespace LLVM.ClangFormat
{
// Exposes event sources for IVsRunningDocTableEvents3 events.
@@ -11,15 +15,109 @@
{
private RunningDocumentTable _runningDocumentTable;
private DTE _dte;
+ private SolutionEvents _solutionEvents;
+ private bool _isOpened = false;
+ private bool _isClosing = false;
+ private List _docCookiesShownBeforeOpened = new List();
+
+#if DEBUG
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
+ public static extern void OutputDebugString(string message);
+ public void DebugLog(string s)
+ {
+ OutputDebugString("clang-format-vs (RDT): " + s + "\n");
+ }
+#else
+ public void DebugLog(string s) {}
+#endif
+
+ public delegate void DocumentHander(object sender, Document document);
+ public event DocumentHander BeforeSave;
+ public event DocumentHander BeforeShow;
+
+ private void SolutionIsClosing()
+ {
+ _isOpened = false;
+ _isClosing = true;
+ DebugLog("SolutionIsClosing");
+ }
+
+ private void SolutionOpened()
+ {
+ _isOpened = true;
+ _isClosing = false;
+ DebugLog("SolutionOpened");
+ foreach (var docCookie in _docCookiesShownBeforeOpened)
+ {
+ DebugLog("delay-formatting " + docCookie);
+ OnAfterSave(docCookie);
+ }
+ _docCookiesShownBeforeOpened.Clear();
+ }
+
+ private Document FindDocumentByCookie(uint docCookie)
+ {
+ var documentInfo = _runningDocumentTable.GetDocumentInfo(docCookie);
+ return _dte.Documents.Cast().FirstOrDefault(doc => doc.FullName == documentInfo.Moniker);
+ }
- public delegate void OnBeforeSaveHander(object sender, Document document);
- public event OnBeforeSaveHander BeforeSave;
+ private void SavingDocument(uint docCookie)
+ {
+ if (BeforeSave != null)
+ {
+ var document = FindDocumentByCookie(docCookie);
+ if (document != null) // Not sure why this happens sometimes
+ {
+ BeforeSave(this, document);
+ }
+ }
+ }
+
+ private void ShowingDocument(uint docCookie)
+ {
+ if (BeforeShow != null)
+ {
+ if (!_isOpened)
+ {
+ DebugLog("delaying formatting for " + docCookie);
+ _docCookiesShownBeforeOpened.Add(docCookie);
+ }
+ else
+ {
+ var document = FindDocumentByCookie(docCookie);
+ if (document != null) // Not sure why this happens sometimes
+ {
+ BeforeShow(this, document);
+ }
+ }
+ }
+ }
public RunningDocTableEventsDispatcher(Package package)
{
+ DebugLog("RunningDocTableEventsDispatcher ctor");
_runningDocumentTable = new RunningDocumentTable(package);
_runningDocumentTable.Advise(this);
_dte = (DTE)Package.GetGlobalService(typeof(DTE));
+ _solutionEvents = _dte.Events.SolutionEvents;
+ _solutionEvents.Opened += SolutionOpened;
+ _solutionEvents.BeforeClosing += SolutionIsClosing;
+ }
+
+ public void FormatOpenDocuments()
+ {
+ foreach (var docInfo in _runningDocumentTable)
+ {
+ SavingDocument(docInfo.DocCookie);
+ }
+ }
+
+ public void AltFormatOpenDocuments()
+ {
+ foreach (var docInfo in _runningDocumentTable)
+ {
+ ShowingDocument(docInfo.DocCookie);
+ }
}
public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
@@ -42,13 +140,12 @@
return VSConstants.S_OK;
}
- public int OnAfterSave(uint docCookie)
- {
- return VSConstants.S_OK;
- }
-
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
+ if (fFirstShow != 0)
+ {
+ ShowingDocument(docCookie);
+ }
return VSConstants.S_OK;
}
@@ -59,21 +156,19 @@
public int OnBeforeSave(uint docCookie)
{
- if (BeforeSave != null)
- {
- var document = FindDocumentByCookie(docCookie);
- if (document != null) // Not sure why this happens sometimes
- {
- BeforeSave(this, FindDocumentByCookie(docCookie));
- }
- }
+ SavingDocument(docCookie);
return VSConstants.S_OK;
}
- private Document FindDocumentByCookie(uint docCookie)
+ public int OnAfterSave(uint docCookie)
{
- var documentInfo = _runningDocumentTable.GetDocumentInfo(docCookie);
- return _dte.Documents.Cast().FirstOrDefault(doc => doc.FullName == documentInfo.Moniker);
+ // If we're not closing the solution, re-run the open formatter
+ // (the idea is to always see your alt format if both format on open/save are enabled)
+ if (!_isClosing)
+ {
+ ShowingDocument(docCookie);
+ }
+ return VSConstants.S_OK;
}
}
}
Index: tools/clang-format-vs/ClangFormat/Vsix.cs
===================================================================
--- tools/clang-format-vs/ClangFormat/Vsix.cs
+++ tools/clang-format-vs/ClangFormat/Vsix.cs
@@ -34,6 +34,28 @@
return textDocument?.IsDirty == true;
}
+ public static DateTime LastContentModifiedTime(Document document)
+ {
+ DateTime when = DateTime.MinValue;
+ var textView = GetDocumentView(document);
+ var textDocument = GetTextDocument(textView);
+ if (textDocument != null)
+ {
+ when = textDocument.LastContentModifiedTime;
+ }
+ return when;
+ }
+
+ public static void CleanDocument(Document document, DateTime lastContentModifiedTime)
+ {
+ var textView = GetDocumentView(document);
+ var textDocument = GetTextDocument(textView);
+ if (textDocument != null)
+ {
+ textDocument.UpdateDirtyState(false, lastContentModifiedTime);
+ }
+ }
+
public static IWpfTextView GetDocumentView(Document document)
{
var textView = GetVsTextViewFrompPath(document.FullName);