StyleCop offers to check for consistent use of spaces, but sadly lacks the opposite idea: Force source code to use tabs. Is there some way to add this functionality? It does not have to be StyleCop, other tools are welcome as well.
Tabs Debate. Pro-spaces programmers argue that using the space bar makes the layout more flexible. However, pro-tabs users rebut saying tabs makes code more readable and aesthetically pleasing.
Conclusion. So, at the end of the day, tabs versus spaces is truly a matter of preference, however the tab is still the character specifically designed for indentation, and using one tab character per indentation level instead of 2 or 4 spaces will use less disk space / memory / compiler resources and the like.
In general, tabs take fewer key presses and use less computer storage, but are more imprecise. Spaces, on the other hand, offer more clarity for coders, but practice takes up more space.
As nobody mentioned it, the Google HTML/CSS Style Guide and the W3School HTML(5) Style Guide recommend 2 spaces. This article also brings an analysis of the effect of tabs vs spaces in the resulting file size. Show activity on this post. Do not use tabs; use two spaces.
I'm a tabs-not-spaces person, too, though there are plenty of reasons to use either one and there are other places to get into why you think one is better than the other. :)
I actually wanted the same thing - a rule to check for tab indents - so I wrote it based on the SpacingRules source from StyleCop. It seems to work reasonably well, though I've only used it on a few projects so far. It could probably be optimized or whatever... but it works.
using System;
using System.Text.RegularExpressions;
using Microsoft.StyleCop;
using Microsoft.StyleCop.CSharp;
namespace CustomRules.StyleCop.CSharp
{
[SourceAnalyzer(typeof(CsParser))]
public class SpacingRules : SourceAnalyzer
{
public SpacingRules()
{
}
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument csdocument = (CsDocument)document;
if (csdocument.RootElement != null && !csdocument.RootElement.Generated)
{
this.CheckSpacing(csdocument.Tokens);
}
}
private void CheckSpacing(MasterList<CsToken> tokens)
{
Param.AssertNotNull(tokens, "tokens");
foreach (var token in tokens)
{
if (this.Cancel)
{
break;
}
if (token.Generated)
{
continue;
}
switch (token.CsTokenType)
{
case CsTokenType.WhiteSpace:
this.CheckWhitespace(token as Whitespace);
break;
case CsTokenType.XmlHeader:
XmlHeader header = (XmlHeader)token;
foreach (var xmlChild in header.ChildTokens)
{
this.CheckTabsInComment(xmlChild);
}
break;
case CsTokenType.SingleLineComment:
case CsTokenType.MultiLineComment:
this.CheckTabsInComment(token);
break;
}
switch (token.CsTokenClass)
{
case CsTokenClass.ConstructorConstraint:
this.CheckSpacing(((ConstructorConstraint)token).ChildTokens);
break;
case CsTokenClass.GenericType:
this.CheckGenericSpacing((GenericType)token);
this.CheckSpacing(((TypeToken)token).ChildTokens);
break;
case CsTokenClass.Type:
this.CheckSpacing(((TypeToken)token).ChildTokens);
break;
}
}
}
private void CheckGenericSpacing(GenericType generic)
{
Param.AssertNotNull(generic, "generic");
if (generic.ChildTokens.Count == 0)
{
return;
}
foreach (var token in generic.ChildTokens)
{
if (this.Cancel)
{
break;
}
if (token.CsTokenClass == CsTokenClass.GenericType)
{
this.CheckGenericSpacing(token as GenericType);
}
if (!token.Generated && token.CsTokenType == CsTokenType.WhiteSpace)
{
this.CheckWhitespace(token as Whitespace);
}
}
}
private void CheckWhitespace(Whitespace whitespace)
{
Param.AssertNotNull(whitespace, "whitespace");
if (whitespace.Location.StartPoint.IndexOnLine == 0 && Regex.IsMatch(whitespace.Text, "^ +"))
{
this.AddViolation(whitespace.FindParentElement(), whitespace.LineNumber, "TabsMustBeUsed");
}
}
private void CheckTabsInComment(CsToken comment)
{
Param.AssertNotNull(comment, "comment");
var lines = comment.Text.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
if (Regex.IsMatch(lines[i], "^ +"))
{
this.AddViolation(comment.FindParentElement(), comment.LineNumber + i, "TabsMustBeUsed");
}
}
}
}
}
Note that you also have to have the embedded XML file "SpacingRules.xml" in the assembly alongside this thing. (Read the StyleCop SDK doc for more on that.)
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Spacing Rules">
<Description>
Rules which verify the spacing placed between keywords and symbols in the code.
</Description>
<Rules>
<Rule Name="TabsMustBeUsed" CheckId="MY1027">
<Context>Spaces are not allowed. Use tabs instead.</Context>
<Description>Verifies that the code does not contain spaces.</Description>
</Rule>
</Rules>
</SourceAnalyzer>
You can use StyleCop+ plugin to enforce usage of tabs.
After downloading StyleCopPlus.dll
place it in Custom Rules
folder inside the main StyleCop folder C:\Program Files (x86)\StyleCop 4.7\Custom Rules
or directly in the main folder.
Now, when opening a Settings.StyleCop
with StyleCopSettingsEditor
you will be able to set rule SP2001: CheckAllowedIndentationCharacters
.
This rule can be found under the StyleCop+
tab, under the More Custom Rules
subtab, under the Formatting
heading:
One thing you could do, assuming you are using Visual Studio as your IDE, and that your team-mates buy-in to this idea, would be to set VS to use tabs instead of spaces, export and share the settings file.
The setting can be found under Tools > Options > Text Editor > All Languages (or the language you wish to use) > Tabs and then on the right hand side you can pick to 'Insert Spaces' or 'Keep Tabs'.
To export the settings from your visual studio: Tools > Import and Export Settings > Export selected environment settings > select the 'Options'
Just a thought - but to be honest the real problem seems to be the buy-in from your team-mates. They can always revert back to their settings otherwise. Alternatively, upon check-in, as Sam suggested, you can do some automated re-formatting.
HTH
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With