Reputation: 16933
Is it possible to configure Visual Studio 2008 to automatically remove whitespace characters at the end of each line when saving a file? There doesn't seem to be a built-in option, so are there any extensions available to do this?
Upvotes: 150
Views: 108623
Reputation: 151
VS 2022 now has a built-in way to do this, called Code Cleanup. See Tools > Options > Text Editor > Code Cleanup to turn it on. There is also a link to the configuration options there. You may want to review them since this feature covers more than simply whitespace removal.
Upvotes: 3
Reputation:
You can create a macro that executes after a save to do this for you.
Add the following into the EnvironmentEvents Module for your macros.
Private saved As Boolean = False
Private Sub DocumentEvents_DocumentSaved(ByVal document As EnvDTE.Document) _
Handles DocumentEvents.DocumentSaved
If Not saved Then
Try
DTE.Find.FindReplace(vsFindAction.vsFindActionReplaceAll, _
"\t", _
vsFindOptions.vsFindOptionsRegularExpression, _
" ", _
vsFindTarget.vsFindTargetCurrentDocument, , , _
vsFindResultsLocation.vsFindResultsNone)
' Remove all the trailing whitespaces.
DTE.Find.FindReplace(vsFindAction.vsFindActionReplaceAll, _
":Zs+$", _
vsFindOptions.vsFindOptionsRegularExpression, _
String.Empty, _
vsFindTarget.vsFindTargetCurrentDocument, , , _
vsFindResultsLocation.vsFindResultsNone)
saved = True
document.Save()
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.OkOnly, "Trim White Space exception")
End Try
Else
saved = False
End If
End Sub
I've been using this for some time now without any problems. I didn't create the macro, but modified it from the one in ace_guidelines.vsmacros which can be found with a quick google search.
Upvotes: 31
Reputation: 6148
CodeMaid is a very popular Visual Studio extension and does this automatically along with other useful cleanups.
I set it to clean up a file on save, which I believe is the default.
Upvotes: 80
Reputation: 1334
I personally love the Trailing Whitespace Visualizer Visual Studio extension which has support back through Visual Studio 2012.
Upvotes: 7
Reputation: 5459
Before saving you may be able to use the auto-format shortcut CTRL+K+D.
Upvotes: 26
Reputation: 37319
I am using VWD 2010 Express where macros are not supported, unfortunately. So I just do copy/paste into Notepad++ top left menu Edit
> Blank Operations
> Trim Trailing Space
there are other related operations available too. Then copy/paste back into Visual Studio.
One can also use NetBeans instead of Notepad++, which has "Remove trailing spaces" under the "Source" menu.
Upvotes: 3
Reputation: 15905
Building on Dyaus's answer and a regular expression from a connect report, here's a macro that handles save all, doesn't replace tabs with spaces, and doesn't require a static variable. Its possible downside? It seems a little slow, perhaps due to multiple calls to FindReplace
.
Private Sub DocumentEvents_DocumentSaved(ByVal document As EnvDTE.Document) _
Handles DocumentEvents.DocumentSaved
Try
' Remove all the trailing whitespaces.
If vsFindResult.vsFindResultReplaced = DTE.Find.FindReplace(vsFindAction.vsFindActionReplaceAll, _
"{:b}+$", _
vsFindOptions.vsFindOptionsRegularExpression, _
String.Empty, _
vsFindTarget.vsFindTargetFiles, _
document.FullName, , _
vsFindResultsLocation.vsFindResultsNone) Then
document.Save()
End If
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.OkOnly, "Trim White Space exception")
End Try
End Sub
For anyone else trying to use this in a Visual Studio 2012 add-in, the regular expression I ended up using is [ \t]+(?=\r?$)
(don't forget to escape the backslashes if necessary). I arrived here after several futile attempts to fix the problems with a raw conversion of {:b}+$
failing to match the carriage return.
Upvotes: 0
Reputation: 161
You can do this easily with these three actions:
Ctrl + A (select all text)
Edit -> Advanced -> Delete Horizontal Whitespace
Edit -> Advanced -> Format Selection
Wait a few seconds and done.
It's Ctrl + Z'able in case something went wrong.
Upvotes: 12
Reputation: 29
I think that the Jeff Muir version could be a little improved if it only trims source code files (in my case C#, but is easy to add more extensions). Also I added a check to ensure that the document window is visible because some situations without that check show me strange errors (LINQ to SQL files '*.dbml', for example).
Private Sub DocumentEvents_DocumentSaved(ByVal document As EnvDTE.Document) Handles DocumentEvents.DocumentSaved
Dim result As vsFindResult
Try
If (document.ActiveWindow Is Nothing) Then
Return
End If
If (document.Name.ToLower().EndsWith(".cs")) Then
document.Activate()
result = DTE.Find.FindReplace(vsFindAction.vsFindActionReplaceAll, ":Zs+$", vsFindOptions.vsFindOptionsRegularExpression, String.Empty, vsFindTarget.vsFindTargetCurrentDocument, , , vsFindResultsLocation.vsFindResultsNone)
If result = vsFindResult.vsFindResultReplaced Then
document.Save()
End If
End If
Catch ex As Exception
MsgBox(ex.Message & Chr(13) & "Document: " & document.FullName, MsgBoxStyle.OkOnly, "Trim White Space exception")
End Try
End Sub
Upvotes: 2
Reputation: 69505
I use ArtisticStyle (C++) to do this and also reformat my code. However, I had to add this as an external tool and you need to trigger it yourself so you might not like it.
However, I find it excellent that I can reformat code in more custom way (for example, multiline function parameters) that I can pay the price of running it manually. The tool is free.
Upvotes: 0
Reputation: 97839
You can use a macro like described in Removing whitespace and rewriting comments, using regex searches
Upvotes: 4
Reputation: 11
I think I have a version of this macro that won't crash VS2010 on refactor, and also won't hang the IDE when saving non-text files. Try this:
Private Sub DocumentEvents_DocumentSaved( _
ByVal document As EnvDTE.Document) _
Handles DocumentEvents.DocumentSaved
' See if we're saving a text file
Dim textDocument As EnvDTE.TextDocument = _
TryCast(document.Object(), EnvDTE.TextDocument)
If textDocument IsNot Nothing Then
' Perform search/replace on the text document directly
' Convert tabs to spaces
Dim convertedTabs = textDocument.ReplacePattern("\t", " ", _
vsFindOptions.vsFindOptionsRegularExpression)
' Remove trailing whitespace from each line
Dim removedTrailingWS = textDocument.ReplacePattern(":Zs+$", "", _
vsFindOptions.vsFindOptionsRegularExpression)
' Re-save the document if either replace was successful
' (NOTE: Should recurse only once; the searches will fail next time)
If convertedTabs Or removedTrailingWS Then
document.Save()
End If
End If
End Sub
Upvotes: 0
Reputation: 16933
Taking elements from all the answers already given, here's the code I ended up with. (I mainly write C++ code, but it's easy to check for different file extensions, as needed.)
Thanks to everyone who contributed!
Private Sub DocumentEvents_DocumentSaved(ByVal document As EnvDTE.Document) _
Handles DocumentEvents.DocumentSaved
Dim fileName As String
Dim result As vsFindResult
Try
fileName = document.Name.ToLower()
If fileName.EndsWith(".cs") _
Or fileName.EndsWith(".cpp") _
Or fileName.EndsWith(".c") _
Or fileName.EndsWith(".h") Then
' Remove trailing whitespace
result = DTE.Find.FindReplace( _
vsFindAction.vsFindActionReplaceAll, _
"{:b}+$", _
vsFindOptions.vsFindOptionsRegularExpression, _
String.Empty, _
vsFindTarget.vsFindTargetFiles, _
document.FullName, _
"", _
vsFindResultsLocation.vsFindResultsNone)
If result = vsFindResult.vsFindResultReplaced Then
' Triggers DocumentEvents_DocumentSaved event again
document.Save()
End If
End If
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.OkOnly, "Trim White Space exception")
End Try
End Sub
Upvotes: 9
Reputation: 1
A simple addition is to remove carriage returns during the save.
' Remove all the carriage returns.
result = DTE.Find.FindReplace(vsFindAction.vsFindActionReplaceAll, _
"\x000d\x000a", _
vsFindOptions.vsFindOptionsRegularExpression, _
"\x000a", _
vsFindTarget.vsFindTargetCurrentDocument, , , _
vsFindResultsLocation.vsFindResultsNone)
The key to this working is changing \x000d\x000a to \x000a. The \x prefix indicates a Unicode pattern. This will automate the process of getting source files ready for Linux systems.
Upvotes: -1
Reputation: 1
This is a really good example of how to remove trailing whitespace. There are a few things that I would change based on what I discovered using this macro. First of all, the macro automatically converts tabs to spaces. This is not always desirable and could lead to making things worse for people that love tabs (typically Linux-based). The tab problem is not really the same as the extra whitespace problem anyways. Secondly, the macro assumes only one file is being saved at once. If you save multiple files at once, it will not correctly remove the whitespace. The reason is simple. The current document is considered the document you can see. Third, it does no error checking on the find results. These results can give better intelligence about what to do next. For example, if no whitespace is found and replaced, there is no need to save the file again. In general, I did not like the need for the global flag for being saved or not. It tends to ask for trouble based on unknown states. I suspect the flag had been added solely to prevent an infinite loop.
Private Sub DocumentEvents_DocumentSaved(ByVal document As EnvDTE.Document) _
Handles DocumentEvents.DocumentSaved
Dim result As vsFindResult
'Dim nameresult As String
Try
document.Activate()
' Remove all the trailing whitespaces.
result = DTE.Find.FindReplace(vsFindAction.vsFindActionReplaceAll, _
":Zs+$", _
vsFindOptions.vsFindOptionsRegularExpression, _
String.Empty, _
vsFindTarget.vsFindTargetCurrentDocument, , , _
vsFindResultsLocation.vsFindResultsNone)
'nameresult = document.Name & " " & Str$(result)
'MsgBox(nameresult, , "Filename and result")
If result = vsFindResult.vsFindResultReplaced Then
'MsgBox("Document Saved", MsgBoxStyle.OkOnly, "Saved Macro")
document.Save()
Else
'MsgBox("Document Not Saved", MsgBoxStyle.OkOnly, "Saved Macro")
End If
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.OkOnly, "Trim White Space exception")
End Try
End Sub
I added debug message boxes to help see what was going on. It made it very clear that multiple file save was not working. If you want to play with them, uncomment those lines.
The key difference is using document.Activate() to force the document into the foreground active current document. If the result is 4, that means that the text was replaced. Zero means nothing happened. You will see two saves for every file. The first will replace and the second will do nothing. Potentially there could be trouble if the save cannot write the file but hopefully this event will not get called if that happens.
Before the original script, I was unaware of how the scripting worked in Visual Studio. It is slightly surprising that it uses Visual Basic as the main interface but it works just fine for what it needs to do.
Upvotes: -1
Reputation: 6655
Find/Replacing using Regular Expressions
In the Find and Replace dialog, expand Find Options, check Use, choose Regular expressions
Find What: ":Zs#$
"
Replace with: ""
click Replace All
In other editors (a normal Regular Expression parser) ":Zs#$
" would be "\s*$
".
Upvotes: 80
Reputation: 8734
Unless this is a one-person project, don't do it. It's got to be trivial to diff your local files against your source code repository, and clearing whitespace would change lines you don't need to change. I totally understand; I love to get my whitespace all uniform – but this is something you should give up for the sake of cleaner collaboration.
Upvotes: 2