Instantly share code, notes, and snippets.
Forked from praeclarum/CodeShareReport.cs
Last active
August 29, 2015 14:11
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save Cheesebaron/a8faad78ec055eecfcbe to your computer and use it in GitHub Desktop.
LinqPad script counting lines. Yay \o/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// based on https://gist.github.com/praeclarum/1608597 | |
void Main() | |
{ | |
var projects = new List<Solution> { | |
new Solution { | |
Name = "Windows 8", | |
ProjectFiles = new List<string> { | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp.Store\\NoiseSentinelApp.Store.Windows\\NoiseSentinelApp.Store.Windows.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp.Store\\NoiseSentinelApp.Store.Shared\\NoiseSentinelApp.Store.Shared.projitems", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp.Models\\NoiseSentinelApp.Models.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp\\NoiseSentinelApp.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\Custom Views\\MiniChart.WindowsCommon\\MiniChart.WindowsCommon.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\Custom Views\\MiniChart.Core\\MiniChart.Core.csproj", | |
}, | |
}, | |
new Solution { | |
Name = "Android", | |
ProjectFiles = new List<string> { | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp.Droid\\NoiseSentinelApp.Droid.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp.Models\\NoiseSentinelApp.Models.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp\\NoiseSentinelApp.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\Custom Views\\MiniChart.Droid\\MiniChart.Droid.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\Custom Views\\MiniChart.Core\\MiniChart.Core.csproj" | |
}, | |
}, | |
new Solution { | |
Name = "Core", | |
ProjectFiles = new List<string> { | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp.Models\\NoiseSentinelApp.Models.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\NoiseSentinelApp\\NoiseSentinelApp.csproj", | |
"C:\\vcs\\git\\MastersThesis\\NoiseSentinelApp\\Custom Views\\MiniChart.Core\\MiniChart.Core.csproj" | |
}, | |
}, | |
}; | |
var excludedFiles = new[] { "App.xaml", "NoiseSentinelTheme.xaml", "OxyplotStyles.xaml", "Resource.Designer.cs", | |
"AssemblyInfo.cs", }; | |
new CodeShareReport().Run(projects, excludedFiles); | |
} | |
class CodeShareReport | |
{ | |
Dictionary<string, FileInfo> _files = new Dictionary<string, FileInfo>(); | |
void AddCodeRef (string path, Solution sln) | |
{ | |
if (_files.ContainsKey(path)) | |
{ | |
_files[path].Solutions.Add(sln); | |
sln.CodeFiles.Add(_files[path]); | |
} | |
else | |
{ | |
var info = new FileInfo { Path = path, }; | |
info.Solutions.Add(sln); | |
_files[path] = info; | |
sln.CodeFiles.Add(info); | |
} | |
} | |
void AddViewRef (string path, Solution sln) | |
{ | |
if (_files.ContainsKey(path)) | |
{ | |
_files[path].Solutions.Add(sln); | |
sln.ViewFiles.Add(_files[path]); | |
} | |
else | |
{ | |
var info = new FileInfo { Path = path }; | |
info.Solutions.Add(sln); | |
_files[path] = info; | |
sln.ViewFiles.Add(info); | |
} | |
} | |
const string SharedProjectPrefix = "$(MSBuildThisFileDirectory)"; | |
static string[] AndroidViewFileEndings = new[] { ".axml", ".xml" }; | |
static string[] ViewCodeBehindFileEndings = new [] { "Fragment.cs", ".xaml.cs", "View.cs" }; | |
public void Run (List<Solution> solutions, IEnumerable<string> excludedFiles) | |
{ | |
// | |
// Find all the files | |
// | |
foreach (var sln in solutions) | |
{ | |
foreach (var projectFile in sln.ProjectFiles) | |
{ | |
var dir = Path.GetDirectoryName(projectFile); | |
var projectName = Path.GetFileNameWithoutExtension(projectFile); | |
var doc = XDocument.Load(projectFile); | |
var q = GetPaths(doc, "Compile", excludedFiles: excludedFiles); | |
foreach (var inc in q) | |
{ | |
var inc1 = inc; | |
if (inc.StartsWith(SharedProjectPrefix)) { | |
inc1 = inc.Remove(0, SharedProjectPrefix.Length); | |
} | |
var path = Path.GetFullPath(Path.Combine(dir, inc1)); | |
if (ViewCodeBehindFileEndings.Any(x => path.ToLowerInvariant().Contains(x.ToLowerInvariant()))) | |
AddViewRef(path, sln); | |
else | |
AddCodeRef(path, sln); | |
} | |
// xaml (very naive implementation) | |
q = GetPaths(doc, "Page", excludedFiles: excludedFiles); | |
foreach(var inc in q) | |
{ | |
var inc1 = inc; | |
if (inc.StartsWith(SharedProjectPrefix)) { | |
inc1 = inc.Remove(0, SharedProjectPrefix.Length); | |
} | |
var path = Path.GetFullPath(Path.Combine(dir, inc1)); | |
AddViewRef(path, sln); | |
} | |
//axml | |
q = GetPaths(doc, "AndroidResource", | |
allowedFileEndings: AndroidViewFileEndings, excludedFiles: excludedFiles); | |
foreach(var inc in q) | |
{ | |
var path = Path.GetFullPath(Path.Combine(dir, inc)); | |
AddViewRef(path, sln); | |
} | |
} | |
} | |
// | |
// Get the lines of code | |
// | |
foreach (var f in _files.Values) | |
{ | |
try | |
{ | |
f.LinesOfCode = File.ReadAllLines(f.Path).Length; | |
} | |
catch (Exception) { } | |
} | |
// | |
// Output | |
// | |
var table = new ConsoleTable("Sln", "Total [l]", "Unique [l]", "Shared [l]", "View [l]", "Unique [%]", "Shared [%]", "View [%]"); | |
foreach (var sln in solutions) | |
{ | |
table.AddRow( | |
sln.Name, | |
sln.TotalLinesOfCode, | |
sln.UniqueLinesOfCode, | |
sln.SharedLinesOfCode, | |
sln.ViewLinesOfCode, | |
string.Format("{0:p}", sln.UniqueLinesOfCode / (double)sln.TotalLinesOfCode), | |
string.Format("{0:p}", sln.SharedLinesOfCode / (double)sln.TotalLinesOfCode), | |
string.Format("{0:p}", sln.ViewLinesOfCode / (double)sln.TotalLinesOfCode)); | |
} | |
Console.WriteLine(table.ToString()); | |
Console.WriteLine("\tLegend:\r\n\t\tl = lines"); | |
} | |
private IEnumerable<string> GetPaths(XDocument doc, string elementName, string attribute = "Include", IEnumerable<string> allowedFileEndings = null, | |
IEnumerable<string> excludedFiles = null) | |
{ | |
if (allowedFileEndings == null) | |
allowedFileEndings = new[] {".cs", ".xaml", ".xaml.cs", ".axml", ".xml"}; | |
if (excludedFiles == null) | |
excludedFiles = new[] { "App.xaml", "Resource.Designer.cs" }; | |
var items = from x in doc.Descendants() | |
let e = x as XElement | |
where e != null | |
where e.Name.LocalName == elementName | |
where e.Attributes().Any(a => a.Name.LocalName == attribute) | |
where allowedFileEndings.Any(a => e.Attribute(attribute).Value.ToLowerInvariant().Contains(a.ToLowerInvariant())) | |
where !excludedFiles.Any(a => e.Attribute(attribute).Value.ToLowerInvariant().Contains(a.ToLowerInvariant())) | |
select e.Attribute(attribute).Value; | |
return items; | |
} | |
} | |
class FileInfo | |
{ | |
public string Path = ""; | |
public HashSet<Solution> Solutions = new HashSet<Solution>(); | |
public int LinesOfCode = 0; | |
public override string ToString () | |
{ | |
return Path; | |
} | |
} | |
class Solution | |
{ | |
public string Name = ""; | |
public List<string> ProjectFiles = new List<string>(); | |
public List<FileInfo> CodeFiles = new List<FileInfo>(); | |
public List<FileInfo> ViewFiles = new List<FileInfo>(); | |
public override string ToString () | |
{ | |
return Name; | |
} | |
public int UniqueLinesOfCode | |
{ | |
get | |
{ | |
return (from f in CodeFiles | |
where f.Solutions.Count == 1 | |
select f.LinesOfCode).Sum(); | |
} | |
} | |
public int SharedLinesOfCode | |
{ | |
get | |
{ | |
return (from f in CodeFiles | |
where f.Solutions.Count > 1 | |
select f.LinesOfCode).Sum(); | |
} | |
} | |
public int LinesOfCode | |
{ | |
get | |
{ | |
return (from f in CodeFiles | |
select f.LinesOfCode).Sum(); | |
} | |
} | |
public int ViewLinesOfCode | |
{ | |
get | |
{ | |
return (from f in ViewFiles | |
select f.LinesOfCode).Sum(); | |
} | |
} | |
public int TotalLinesOfCode | |
{ | |
get { return LinesOfCode + ViewLinesOfCode; } | |
} | |
} | |
#region ConsoleTable | |
// https://github.com/khalidabuhakmeh/ConsoleTables/blob/master/ConsoleTables.Core/ConsoleTable.cs | |
public class ConsoleTable | |
{ | |
public IList<string> Columns { get; protected set; } | |
public IList<object[]> Rows { get; protected set; } | |
public ConsoleTable(params string[] columns) | |
{ | |
Columns = new List<string>(columns); | |
Rows = new List<object[]>(); | |
} | |
public ConsoleTable AddColumn(string[] names) | |
{ | |
foreach (var name in names) | |
Columns.Add(name); | |
return this; | |
} | |
public ConsoleTable AddRow(params object[] values) | |
{ | |
if (values == null) | |
throw new ArgumentNullException("values"); | |
if (!Columns.Any()) | |
throw new Exception("Please set the columns first"); | |
if (Columns.Count != values.Length) | |
throw new Exception(string.Format("The number columns in the row ({0}) does not match the values ({1}", | |
Columns.Count, values.Length)); | |
Rows.Add(values); | |
return this; | |
} | |
public static ConsoleTable From<T>(IEnumerable<T> values) | |
{ | |
var table = new ConsoleTable(); | |
var columns = typeof(T).GetProperties().Select(x => x.Name).ToArray(); | |
table.AddColumn(columns); | |
foreach (var propertyValues in values.Select(value => columns.Select(column => typeof(T).GetProperty(column).GetValue(value, null)))) | |
table.AddRow(propertyValues.ToArray()); | |
return table; | |
} | |
public override string ToString() | |
{ | |
var builder = new StringBuilder(); | |
// find the longest column by searching each row | |
var columnLengths = Columns | |
.Select((t, i) => Rows.Select(x => x[i]) | |
.Union(Columns) | |
.Where(x => x != null) | |
.Select(x => x.ToString().Length).Max()) | |
.ToList(); | |
// create the string format with padding | |
var format = Enumerable.Range(0, Columns.Count) | |
.Select(i => " | {" + i + ", -" + columnLengths[i] + " }") | |
.Aggregate((s, a) => s + a) + " |"; | |
var longestLine = 0; | |
var results = new List<string>(); | |
// find the longest formatted line | |
foreach (var result in Rows.Select(row => string.Format(format, row))) | |
{ | |
longestLine = Math.Max(longestLine, result.Length); | |
results.Add(result); | |
} | |
// create the divider | |
var line = " " + string.Join("", Enumerable.Repeat("-", longestLine - 1)) + " "; | |
builder.AppendLine(line); | |
builder.AppendLine(string.Format(format, Columns.ToArray())); | |
foreach (var row in results) | |
{ | |
builder.AppendLine(line); | |
builder.AppendLine(row); | |
} | |
builder.AppendLine(line); | |
builder.AppendLine(""); | |
return builder.ToString(); | |
} | |
public void Write() { Console.WriteLine(ToString()); } | |
} | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment