Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CLI styler options overrides #249

Merged
merged 6 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions XamlStyler.Console/LogLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// © Xavalon. All rights reserved.

namespace Xavalon.XamlStyler.Xmagic
{
public enum LogLevel
{
None = 0,
Minimal = 1,
Default = 2,
Verbose = 3,
Debug = 4,
Insanity = 5,
}
}
117 changes: 117 additions & 0 deletions XamlStyler.Console/Options.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// © Xavalon. All rights reserved.

using System.Collections.Generic;
using CommandLine;
using Xavalon.XamlStyler.Core.DocumentManipulation;
using Xavalon.XamlStyler.Core.Options;

namespace Xavalon.XamlStyler.Xmagic
{
// CLI-intrinsic options
public sealed partial class Options
{
[Option('f', "file", Separator = ',', HelpText = "XAML file to process (supports comma-separated list).")]
public IList<string> File { get; set; }

[Option('d', "directory", HelpText = "Directory to process XAML files in.")]
public string Directory { get; set; }

[Option('c', "config", HelpText = "JSON file containing XAML Styler settings configuration.")]
public string Configuration { get; set; }

[Option('i', "ignore", Default = false, HelpText = "Ignore XAML file type check and process all files.")]
public bool Ignore { get; set; }

[Option('r', "recursive", Default = false, HelpText = "Recursively process specified directory.")]
public bool IsRecursive { get; set; }

[Option('l', "loglevel", Default = LogLevel.Default,
HelpText = "Levels in order of increasing detail: None, Minimal, Default, Verbose, Debug")]
public LogLevel LogLevel { get; set; }
}

// Styler overrides
public sealed partial class Options
{
[Option("indent-size", HelpText = "Override: indent size.")]
public int? IndentSize { get; set; }

[Option("indent-tabs", HelpText = "Override: indent with tabs.")]
public bool? IndentWithTabs { get; set; }

[Option("attributes-tolerance", HelpText = "Override: attributes tolerance.")]
public int? AttributesTolerance { get; set; }

[Option("attributes-same-line", HelpText = "Override: keep first attribute on the same line.")]
public bool? KeepFirstAttributeOnSameLine { get; set; }

[Option("attributes-max-chars", HelpText = "Override: max attribute characters per line.")]
public int? MaxAttributeCharactersPerLine { get; set; }

[Option("attributes-max", HelpText = "Override: max attributes per line.")]
public int? MaxAttributesPerLine { get; set; }

[Option("no-newline-elements", HelpText = "Override: no newline elements.")]
public string NoNewLineElements { get; set; }

[Option("attributes-order-groups-newline", HelpText = "Override: put attribute order rule groups on separate lines.")]
public bool? PutAttributeOrderRuleGroupsOnSeparateLines { get; set; }

[Option("attributes-indentation", HelpText = "Override: attribute indentation.")]
public int? AttributeIndentation { get; set; }

[Option("attributes-indentation-style", HelpText = "Override: attribute indentation style.")]
public AttributeIndentationStyle? AttributeIndentationStyle { get; set; }

[Option("remove-design-references", HelpText = "Override: remove design time references.")]
public bool? RemoveDesignTimeReferences { get; set; }

[Option("attributes-reorder", HelpText = "Override: enable attribute reordering.")]
public bool? EnableAttributeReordering { get; set; }

[Option("attributes-first-line", HelpText = "Override: first line attributes.")]
public string FirstLineAttributes { get; set; }

[Option("attributes-order-name", HelpText = "Override: order attributes by name.")]
public bool? OrderAttributesByName { get; set; }

[Option("ending-bracket-newline", HelpText = "Override: put ending bracket on new line.")]
public bool? PutEndingBracketOnNewLine { get; set; }

[Option("remove-empty-ending-tag", HelpText = "Override: remove ending tag of empty element.")]
public bool? RemoveEndingTagOfEmptyElement { get; set; }

[Option("space-before-closing-slash", HelpText = "Override: space before closing slash.")]
public bool? SpaceBeforeClosingSlash { get; set; }

[Option("root-line-break", HelpText = "Override: root element line break rule.")]
public LineBreakRule? RootElementLineBreakRule { get; set; }

[Option("reorder-vsm", HelpText = "Override: reorder visual state manager rule.")]
public VisualStateManagerRule? ReorderVSM { get; set; }

[Option("reorder-grid-children", HelpText = "Override: reorder grid children.")]
public bool? ReorderGridChildren { get; set; }

[Option("reorder-canvas-children", HelpText = "Override: reorder canvas children.")]
public bool? ReorderCanvasChildren { get; set; }

[Option("reorder-setters", HelpText = "Override: reorder setters.")]
public ReorderSettersBy? ReorderSetters { get; set; }

[Option("format-markup-extension", HelpText = "Override: format markup extension.")]
public bool? FormatMarkupExtension { get; set; }

[Option("no-newline-markup-extensions", HelpText = "Override: no newline markup extensions.")]
public string NoNewLineMarkupExtensions { get; set; }

[Option("thickness-style", HelpText = "Override: thickness style.")]
public ThicknessStyle? ThicknessStyle { get; set; }

[Option("thickness-attributes", HelpText = "Override: thickness attributes.")]
public string ThicknessAttributes { get; set; }

[Option("comment-spaces", HelpText = "Override: comment spaces.")]
public int? CommentSpaces { get; set; }
}
}
10 changes: 10 additions & 0 deletions XamlStyler.Console/ProcessType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// © Xavalon. All rights reserved.

namespace Xavalon.XamlStyler.Xmagic
{
public enum ProcessType
{
File,
Directory,
}
}
202 changes: 1 addition & 201 deletions XamlStyler.Console/Program.cs
Original file line number Diff line number Diff line change
@@ -1,176 +1,13 @@
// © Xavalon. All rights reserved.

using CommandLine;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Xavalon.XamlStyler.Core;
using Xavalon.XamlStyler.Core.Options;

namespace Xavalon.XamlStyler.Xmagic
{
public sealed class Program
public sealed partial class Program
{
public sealed class XamlStylerConsole
{
private readonly Options options;
private readonly StylerService stylerService;

public XamlStylerConsole(Options options)
{
this.options = options;

IStylerOptions stylerOptions = new StylerOptions();

if (this.options.Configuration != null)
{
stylerOptions = this.LoadConfiguration(this.options.Configuration);
}

this.stylerService = new StylerService(stylerOptions);
}

public void Process(ProcessType processType)
{
int successCount = 0;

IList<string> files;

switch(processType)
{
case ProcessType.File:
files = this.options.File;
break;
case ProcessType.Directory:
var searchOption = this.options.IsRecursive
? SearchOption.AllDirectories
: SearchOption.TopDirectoryOnly;
files = (File.GetAttributes(this.options.Directory).HasFlag(FileAttributes.Directory))
? Directory.GetFiles(this.options.Directory, "*.xaml", searchOption).ToList()
: new List<string>();
break;
default:
throw new ArgumentException("Invalid ProcessType");
}

foreach (string file in files)
{
if (this.TryProcessFile(file))
{
successCount++;
}
}

this.Log($"Processed {successCount} of {files.Count} files.", LogLevel.Minimal);
}

private bool TryProcessFile(string file)
{
this.Log($"Processing: {file}");

if (!options.Ignore)
{
var extension = Path.GetExtension(file);
this.Log($"Extension: {extension}", LogLevel.Debug);

if (!extension.Equals(".xaml", StringComparison.OrdinalIgnoreCase))
{
this.Log("Skipping... Can only process XAML files. Use the --ignore parameter to override.");
return false;
}
}

var path = Path.GetFullPath(file);
this.Log($"Full Path: {file}", LogLevel.Debug);

// If the options already has a configuration file set, we don't need to go hunting for one
string configurationPath = string.IsNullOrEmpty(this.options.Configuration) ? this.GetConfigurationFromPath(path) : null;

string originalContent = null;
Encoding encoding = Encoding.UTF8; // Visual Studio by default uses UTF8
using (var reader = new StreamReader(path))
{
originalContent = reader.ReadToEnd();
encoding = reader.CurrentEncoding;
this.Log($"\nOriginal Content:\n\n{originalContent}\n", LogLevel.Insanity);
}

var formattedOutput = String.IsNullOrWhiteSpace(configurationPath)
? stylerService.StyleDocument(originalContent)
: new StylerService(this.LoadConfiguration(configurationPath)).StyleDocument(originalContent);

this.Log($"\nFormatted Output:\n\n{formattedOutput}\n", LogLevel.Insanity);

using (var writer = new StreamWriter(path, false, encoding))
{
try
{
writer.Write(formattedOutput);
this.Log($"Finished Processing: {file}", LogLevel.Verbose);
}
catch (Exception e)
{
this.Log("Skipping... Error formatting XAML. Increase log level for more details.");
this.Log($"Exception: {e.Message}", LogLevel.Verbose);
this.Log($"StackTrace: {e.StackTrace}", LogLevel.Debug);
}
}

return true;
}

private IStylerOptions LoadConfiguration(string path)
{
StylerOptions stylerOptions = new StylerOptions(path);
this.Log(JsonConvert.SerializeObject(stylerOptions), LogLevel.Insanity);
this.Log(JsonConvert.SerializeObject(stylerOptions.AttributeOrderingRuleGroups), LogLevel.Debug);
return stylerOptions;
}

private string GetConfigurationFromPath(string path)
{
try
{
if (String.IsNullOrWhiteSpace(path))
{
return null;
}

bool isSolutionRoot = false;

while (!isSolutionRoot && ((path = Path.GetDirectoryName(path)) != null))
{
isSolutionRoot = Directory.Exists(Path.Combine(path, ".vs"));
this.Log($"In solution root: {isSolutionRoot}", LogLevel.Debug);
var configFile = Path.Combine(path, "Settings.XamlStyler");
this.Log($"Looking in: {path}", LogLevel.Debug);

if (File.Exists(configFile))
{
this.Log($"Configuration Found: {configFile}", LogLevel.Verbose);
return configFile;
}
}
}
catch
{
}

return null;
}

private void Log(string value, LogLevel logLevel = LogLevel.Default)
{
if (logLevel <= this.options.LogLevel)
{
Console.WriteLine(value);
}
}
}

public static void Main(string[] args)
{
var writer = new StringWriter();
Expand Down Expand Up @@ -209,42 +46,5 @@ public static void Main(string[] args)
}
});
}

public sealed class Options
{
[Option('f', "file", Separator = ',', HelpText = "XAML file to process (supports comma-separated list).")]
public IList<string> File { get; set; }

[Option('d', "directory", HelpText = "Directory to process XAML files in.")]
public string Directory { get; set; }

[Option('c', "config", HelpText = "JSON file containing XAML Styler settings configuration.")]
public string Configuration { get; set; }

[Option('i', "ignore", Default = false, HelpText = "Ignore XAML file type check and process all files.")]
public bool Ignore { get; set; }

[Option('r', "recursive", Default = false, HelpText = "Recursively process specified directory.")]
public bool IsRecursive { get; set; }

[Option('l', "loglevel", Default = LogLevel.Default, HelpText = "Levels in order of increasing detail: None, Minimal, Default, Verbose, Debug")]
public LogLevel LogLevel { get; set; }
}

public enum LogLevel
{
None = 0,
Minimal = 1,
Default = 2,
Verbose = 3,
Debug = 4,
Insanity = 5,
}

public enum ProcessType
{
File,
Directory,
}
}
}
Loading