Skip to content

Instantly share code, notes, and snippets.

@george-polevoy
Created May 31, 2016 14:05
Show Gist options
  • Save george-polevoy/d1921e0b7ed45918057c8b44cf662e17 to your computer and use it in GitHub Desktop.
Save george-polevoy/d1921e0b7ed45918057c8b44cf662e17 to your computer and use it in GitHub Desktop.
Code formatting inspections for line endings and unicode local alphabet for msbuild.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TasksDllPath Condition="'$(MSBuildToolsVersion)' == '14'">$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll</TasksDllPath>
<TasksDllPath Condition="'$(MSBuildToolsVersion)' != '14'">$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll</TasksDllPath>
</PropertyGroup>
<UsingTask TaskName="InspectSourceCode"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(TasksDllPath)">
<ParameterGroup>
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.IO" />
<Using Namespace="System.Text.RegularExpressions" />
<Using Namespace="System.Linq" />
<Using Namespace="Microsoft.Build.Framework" />
<Code Type="Fragment"
Language="cs">
<![CDATA[
foreach(var item in Items)
{
string fileName = item.GetMetadata("FullPath");
if (!File.Exists(fileName))
continue;
if (fileName.ToLower().Contains(".generated."))
continue;
CheckLineEndings(fileName);
CheckEncodingForRussianLanguage(fileName);
//CheckEmptySpace(fileName);
}
// Hack https://badcorporatelogo.wordpress.com/2013/09/15/code-injection-with-msbuild-inline-tasks/
return true;
}
private void CheckEmptySpace(string fileName)
{
foreach(var item in File.ReadLines(fileName).Select((line, index) => new {line, index}))
{
if (item.line.TrimEnd() != item.line)
Log.LogWarning(
subcategory : "Whitespace",
warningCode : "EX",
helpKeyword : "helpKeywordNone",
file : fileName,
lineNumber : item.index + 1,
columnNumber : 1,
endLineNumber : item.index + 1,
endColumnNumber : item.line.Length,
message : "Excess whitespace");
}
}
private void CheckEncodingForRussianLanguage(string fileName)
{
var byteOrderMark = new byte[]{0xEF, 0xBB, 0xBF};
var russianCharsLower = "абвгдеёжзиклмнопрстуфхцчшщъьэюя";
var russianAlphabet = new HashSet<char>(string.Concat(russianCharsLower, russianCharsLower.ToUpper()));
if (byteOrderMark.SequenceEqual(ReadBytes(fileName).Take(byteOrderMark.Length))) // Skip unicode files.
return;
var asWindows = File.ReadAllText(fileName, Encoding.GetEncoding(1251));
if (!asWindows.Any(c => russianAlphabet.Contains(c)))
return;
var asUnicode = File.ReadAllText(fileName);
if (asWindows == asUnicode)
return;
Log.LogWarning(
subcategory : "Code Encoding",
warningCode : "EX",
helpKeyword : "helpKeywordNone",
file : fileName,
lineNumber : 1,
columnNumber : 1,
endLineNumber : 1,
endColumnNumber : 1,
message : "Inconsistent encoding found for russian alphabet");
}
IEnumerable<byte> ReadBytes(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var buffer = new byte[4096];
var actuallyRead = 0;
while ((actuallyRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
for (var i = 0; i < actuallyRead; i++)
yield return buffer[i];
}
}
}
private void CheckLineEndings(string fileName)
{
var soleNewLine = new Regex(@"([^\r]{1}\n)|(^\n)", RegexOptions.Multiline | RegexOptions.Compiled);
if(!File.Exists(fileName))
{
Log.LogCriticalMessage(
subcategory:"Code Formatting",
code:"IX",
helpKeyword:"MISSING",
file:fileName,
lineNumber:0,
columnNumber:0,
endLineNumber:0,
endColumnNumber:0,
message:"File is missing");
return;
}
string content = File.ReadAllText(fileName);
if (soleNewLine.IsMatch(content))
{
Log.LogWarning(
subcategory : "Code Formatting",
warningCode : "WX",
helpKeyword : "helpKeywordNone",
file : fileName,
lineNumber : 1,
columnNumber : 1,
endLineNumber : 1,
endColumnNumber : 1,
message : "Inconsistent line endings found");
}
}
// Hack https://badcorporatelogo.wordpress.com/2013/09/15/code-injection-with-msbuild-inline-tasks/
private bool IgnoreThisInjectedFunction() {
// msbuild will complete this with:
// return _Success;
// }
]]>
</Code>
</Task>
</UsingTask>
<Target Name="RunSourceCodeInspections">
<InspectSourceCode Items="@(Compile)" />
</Target>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment