This is an archive of the discontinued LLVM Phabricator instance.

[clang-format-vs] Add an option to reformat source code when file is saved to disk
Needs ReviewPublic

Authored by berenm on Aug 27 2015, 8:38 AM.
Tokens
"Like" token, awarded by jsmouret."Like" token, awarded by prados.

Details

Summary

This was only tested with Visual Studio 2015 Community, but from what I could see in MSDN, the interfaces in use are also available in previous versions of VS.

Diff Detail

Event Timeline

berenm updated this revision to Diff 33331.Aug 27 2015, 8:38 AM
berenm retitled this revision from to [clang-format-vs] Add an option to reformat source code when saved to disk.
berenm updated this object.
berenm changed the visibility from "Public (No Login Required)" to "berenm (Beren Minor)".
berenm retitled this revision from [clang-format-vs] Add an option to reformat source code when saved to disk to [clang-format-vs] Add an option to reformat source code when file is saved to disk.Aug 27 2015, 8:42 AM
berenm updated this object.
berenm added a reviewer: djasper.
berenm changed the visibility from "berenm (Beren Minor)" to "Public (No Login Required)".
berenm added a subscriber: cfe-commits.
djasper edited reviewers, added: hans, klimek; removed: djasper.Aug 27 2015, 9:37 AM
djasper added a subscriber: djasper.

I know nothing of Visual Studio, so I have added better reviewers.

This is definitely a nice to have, it would be nice in the future to have an option that only reformatted the changed lines only, nice idea though

hans edited edge metadata.Aug 31 2015, 9:25 AM

I also don't know anything about how this plugin works, I just build it :) I'm hoping Manuel can take a look.

klimek edited edge metadata.Aug 31 2015, 9:51 AM
klimek added a subscriber: klimek.

I'll need to try to build / install it. I'll take a couple of days, I'm
currently swamped. Hope that's ok.

Sure, no worries.

Ping? Just to be sure it's not going to fall to oblivion. :)

It's not falling into oblivion, but it's not going to happen before next
week, unless somebody else picks the review up.

I tried downloaded the diff and tried this, I met a couple of issues with applying the patch due to line endings in .cs file and csproj (but that could be my fault) - (I was using 2013 to build clang library and clang format)

I also had to up the version because I had previously installed a 3.8.0 version of the extension from the trunk, but I suspect that an uninstall/reinstall would have solved that.

my csproj needed me to re-add the ComponentModel reference (which I think requires the VS 2010 SDK which I'm not sure was required before)

So my line in the CS proj ended up being a little different (Version Public Key etc..)

<Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />

But it seem to work well in the version I tried (namely VS2013 Premium and VS2015 Enterprise)

but VS2010 did not work for me, it installed ok, but initially it was disabled by default, (not sure why), going to the extension manager and enabling and restarting devenv didn't make it work.

Very nice feature for forcing the change across a file.

Actually the 2010 failure may be my fault, I only have Visual C# installed for VS2010 and not the C++ compilers, as such it doesn't seem to understand C/C++ as the content type

if (!textBuffer.ContentType.IsOfType("C/C++"))

is failing because the ContentType is "plaintext", my guess is unless you install the C++ parts of VS it doesn't add the "File Type" or C/C++ and hence it can't detect that a .h file is anything other than plaintext, let me recheck VS2010 support after adding in the C++ support

Having installed the VS2010 C++ compilers the format on save does work BUT... you have to disable and enable the setting in tools->options->LLVM/Clang first, its as if the setting is NOT read on startup

Well, the format on save setting is disabled by default, do you mean you had to enable it twice? Or you had it enabled without the C++ development tools, and after the installation you had to disable and enable it again?

I went back and retested VS2010, VS2013 and VS2015. so in all cases I

  1. start Visual studio
  2. open a .h file (with incorrect style) - (via the recent file menu)
  3. make a minor edit of whitespace
  4. hit save file

In allcases it does NOT reformat the file

Now do

Tools->Options->LLVM/CLang
The setting is currently True for reformat on save
Turn it to False press OK
Tools->Options->LLVM/CLang
The setting is now False
Turn it to True press ok

now return to the file and press save

The file will now be reformatted.

After this point any subsequent edits and save will reformat the page, perhaps others can try.

After a little digging, the problem is based on the fact that the extension doesn't load until we use it, so the nBeforeSave won't fire, so after doing Ctrl-R,Ctrl-F once it will work

This exact same problem is mentioned here

http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2

Quote:

So, we implement all of the event functionality and try out the extension. The first time we save a file, no arranging happens. If we run the arrange command from the menu item, it runs fine and then subsequent file saves also get arranged. That seems weird. It turns out that Visual Studio only loads the extensions as needed. We have to add another one of our provide attributes, ProvideAutoLoadAttribute, to the VSPackage class to get the extension to load sooner.
[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]
I found the GUID to pass to this from a blog post I found to get the extension to load as soon as possible. After further research on MSDN, it turns out to be the same as the UIContextGuids80.NoSolution field. The remarks seem to indicate that it would only become active when a solution is closed or Visual Studio is started with the "Show Empty Environment" option set in startup; however, it looks like it happens when Visual Studio is started no matter the value of the startup setting. There are other useful contexts in the UIContextGuids80 Class. The attribute can be rewritten as shown.

berenm updated this revision to Diff 35946.Sep 29 2015, 2:07 AM
berenm edited edge metadata.

Update the diff with the ProvideAutoLoad attribute.

Thanks @MyDeveloperDay for the review and tips!

+aaron for windows specific knowledge
+rnk to see whether we can get a reviewer with more MS VS experience (perhaps somebody from MS :)

My main concern is that this adds a lot of things that I have no idea whether they are needed or whether there are simpler variants.

aaron.ballman edited edge metadata.Oct 13 2015, 7:33 AM

LGTM, but someone who understands VS plugins better should give the final sign-off.

tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
441

Cute. :-P

Ping. rnk, do you have a moment to look this over? My C#-fu is a bit rusty, but this seems reasonable to me.

berenm added inline comments.Nov 6 2015, 7:40 AM
tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
195

I did more tests on my side, and apparently this line does not work on VS2012, componentModel is null. I don't know at all why and how to fix it, and it works fine starting with VS2013.

aaron.ballman added inline comments.Nov 6 2015, 7:43 AM
tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
195

Our minimum supported MSVC version for development is 2013. Do we document supported versions for clang-format? Do we want to support versions older than the development version we're on?

hans added inline comments.Nov 6 2015, 8:36 AM
tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
195

The manifest claims support for 2010 and later. I usually test with 2012 when I build the weekly snapshot.

Our clang-cl VS integration also tries to support 2010 and later, so I think it would be nice if the clang-format plugin does too.

aaron.ballman added inline comments.Nov 6 2015, 8:41 AM
tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
195

Thank you for the information! That sounds good to me. Then I think berenm's issue should be resolved if possible.

kimgr added a subscriber: kimgr.Nov 7 2015, 1:17 AM

Add debugging ideas.

tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
195

I haven't had time to dig into this too much, but here's an idea:

You're only getting the IComponentModel to get the IVsEditorAdaptersFactoryService, which in turn is only used to get at the document's text buffer in OnBeforeSave.

It seems to me there *should* be a more straightforward way to do that without involving COM interop. Unfortunately, I don't know what it is :-)

Google around, and this article describes something similar:
http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2

I don't know if maybe you have the wrong RunningDocumentTable service, it seems very COM-styled, compared to the one used in the article.

berenm updated this revision to Diff 42718.EditedDec 14 2015, 7:32 AM
berenm edited edge metadata.
  • Patch rebased on trunk
  • Small cleanups in the RunningDocumentTable usage
  • Successfully tested on VS >= 2012
berenm added inline comments.Dec 14 2015, 7:39 AM
tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
195

In this blog post, they go through another interface to modify the file contents.

I wanted to re-use the FormatTextBuffer method, that was operating on an ITextBuffer, and getting this ITextBuffer from a IDontKnowWhatDocument requires to go through this ComponentModel thing.

I did some small changes and some new quick testing and I think everything now works on VS2012 and later. I haven't tested the plugin on VS2010 though.

441

Sorry I dropped this part :'(

Ping. What's the state of this?

I think we'll want somebody to find out whether there are simpler ways to
implement this.

prados added a subscriber: prados.Dec 22 2015, 1:02 AM
berenm marked 8 inline comments as done.Dec 22 2015, 5:55 AM

For information, I just tested the current diff under Visual Studio 2010 SP1 and everything looks fine as well.

Actually, the main issue that I see is the difficulty to build the plugin itself. The project does not build out-of-the-box on VS2013, and I had to change the assembly references by hand.

I have a patch to get the required assemblies in a more portable way using nuget packages, but I didn't want to include that in this revision. I'll open a separate one if that's useful.

jsmouret added a subscriber: jsmouret.

Just in case someone is interested. I figured out a hack with VCMD that actually does the job.

using EnvDTE;
using EnvDTE80;

public class E : VisualCommanderExt.IExtension
{
  public void SetSite(EnvDTE80.DTE2 DTE_, Microsoft.VisualStudio.Shell.Package package)
  {
    DTE = DTE_;
    events = DTE.Events;
    documentEvents = events.DocumentEvents;
    documentEvents.DocumentSaved += OnDocumentSaved;  
  }

  public void Close()
  {
    documentEvents.DocumentSaved -= OnDocumentSaved;
  }

  private void OnDocumentSaved(EnvDTE.Document doc)
  {
    if(doc.Language == "C/C++")
    {       
       DTE.ExecuteCommand("Edit.SelectAll");
       DTE.ExecuteCommand("Tools.ClangFormat");
       DTE.ExecuteCommand("View.NavigateBackward");
    }
  }

  private EnvDTE80.DTE2 DTE;
  private EnvDTE.Events events;
  private EnvDTE.DocumentEvents documentEvents;
}

This will invoke clang format whenever a c++ related file is saved

jsmouret added a comment.EditedAug 28 2016, 4:38 AM

A slightly improved version of @jasjuang code.
Restores current selection and really saves if modified.
Also works when saving all documents.

using EnvDTE;
using EnvDTE80;

public class E : VisualCommanderExt.IExtension
{
  public void SetSite(EnvDTE80.DTE2 DTE_, Microsoft.VisualStudio.Shell.Package package)
  {
    DTE = DTE_;
    events = DTE.Events;
    documentEvents = events.DocumentEvents;
    documentEvents.DocumentSaved += OnDocumentSaved;  
  }

  public void Close()
  {
    documentEvents.DocumentSaved -= OnDocumentSaved;
  }

  private void OnDocumentSaved(EnvDTE.Document doc)
  {
    if (doc.Language == "C/C++")
    {
	Document current = DTE.ActiveDocument;
	doc.Activate();

	TextSelection selection = doc.Selection as TextSelection;
	int begin = selection.TopPoint.AbsoluteCharOffset;
	int end = selection.BottomPoint.AbsoluteCharOffset;

	selection.SelectAll();
	DTE.ExecuteCommand("Tools.ClangFormatSelection");

	if (!doc.Saved)
	{
	  doc.Save();
	}

	// As the old selection may not be selectable, be cautious.
	try { selection.MoveToAbsoluteOffset(begin, false); } catch {}
	try { selection.MoveToAbsoluteOffset(end, true); } catch {}

	current.Activate();
    }
  }

  private EnvDTE80.DTE2 DTE;
  private EnvDTE.Events events;
  private EnvDTE.DocumentEvents documentEvents;
}

@jsmouret Bravo for the improved solution!

aaron.ballman resigned from this revision.Jun 1 2017, 11:01 AM
mteodor added a subscriber: mteodor.Jul 7 2017, 2:00 AM

I think we'll want somebody to find out whether there are simpler ways to
implement this.

Format on save was implemented by D29221.

What's the new functionality in the reformat implementation?
Always trigger a (re)format even if there are no changes? Probably not a good idea.