First step to ingest template engine
Please do not merge yet @piotrp @seancpeters @livarocc
This commit is contained in:
parent
441277ccfa
commit
cfa0e5cbd4
6 changed files with 1483 additions and 0 deletions
|
@ -16,6 +16,7 @@ using Microsoft.DotNet.Tools.List;
|
|||
using Microsoft.DotNet.Tools.Migrate;
|
||||
using Microsoft.DotNet.Tools.MSBuild;
|
||||
using Microsoft.DotNet.Tools.New;
|
||||
using Microsoft.DotNet.Tools.New3;
|
||||
using Microsoft.DotNet.Tools.NuGet;
|
||||
using Microsoft.DotNet.Tools.Pack;
|
||||
using Microsoft.DotNet.Tools.Publish;
|
||||
|
@ -41,6 +42,7 @@ namespace Microsoft.DotNet.Cli
|
|||
["migrate"] = MigrateCommand.Run,
|
||||
["msbuild"] = MSBuildCommand.Run,
|
||||
["new"] = NewCommand.Run,
|
||||
["new3"] = New3Command.Run,
|
||||
["nuget"] = NuGetCommand.Run,
|
||||
["pack"] = PackCommand.Run,
|
||||
["publish"] = PublishCommand.Run,
|
||||
|
|
111
src/dotnet/commands/dotnet-new3/AppExtensions.cs
Normal file
111
src/dotnet/commands/dotnet-new3/AppExtensions.cs
Normal file
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.New3
|
||||
{
|
||||
internal static class AppExtensions
|
||||
{
|
||||
public static CommandOption Help(this CommandLineApplication app)
|
||||
{
|
||||
return app.Option("-h|--help", "Displays help for this command.", CommandOptionType.NoValue);
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<string, IList<string>> ParseExtraArgs(this CommandLineApplication app, IList<string> extraArgFileNames)
|
||||
{
|
||||
Dictionary<string, IList<string>> parameters = new Dictionary<string, IList<string>>();
|
||||
|
||||
// Note: If the same param is specified multiple times across the files, last-in-wins
|
||||
// TODO: consider another course of action.
|
||||
if (extraArgFileNames.Count > 0)
|
||||
{
|
||||
foreach (string argFile in extraArgFileNames)
|
||||
{
|
||||
using (Stream s = File.OpenRead(argFile))
|
||||
using (TextReader r = new StreamReader(s, Encoding.UTF8, true, 4096, true))
|
||||
using (JsonTextReader reader = new JsonTextReader(r))
|
||||
{
|
||||
JObject obj = JObject.Load(reader);
|
||||
|
||||
foreach (JProperty property in obj.Properties())
|
||||
{
|
||||
if(property.Value.Type == JTokenType.String)
|
||||
{
|
||||
IList<string> values = new List<string>();
|
||||
values.Add(property.Value.ToString());
|
||||
// adding 2 dashes to the file-based params
|
||||
// won't work right if there's a param that should have 1 dash
|
||||
//
|
||||
// TOOD: come up with a better way to deal with this
|
||||
parameters["--" + property.Name] = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < app.RemainingArguments.Count; ++i)
|
||||
{
|
||||
string key = app.RemainingArguments[i];
|
||||
CommandOption arg = app.Options.FirstOrDefault(x => x.Template.Split('|').Any(y => string.Equals(y, key, StringComparison.OrdinalIgnoreCase)));
|
||||
bool handled = false;
|
||||
|
||||
if (arg != null)
|
||||
{
|
||||
if (arg.OptionType != CommandOptionType.NoValue)
|
||||
{
|
||||
handled = arg.TryParse(app.RemainingArguments[i + 1]);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
handled = arg.TryParse(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!key.StartsWith("-", StringComparison.Ordinal))
|
||||
{
|
||||
throw new Exception("Parameter names must start with -- or -");
|
||||
}
|
||||
|
||||
// Check the next value. If it doesn't start with a '-' then it's a value for the current param.
|
||||
// Otherwise it's its own param.
|
||||
string value = null;
|
||||
if (app.RemainingArguments.Count > i + 1)
|
||||
{
|
||||
value = app.RemainingArguments[i + 1];
|
||||
|
||||
if (value.StartsWith("-", StringComparison.Ordinal))
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
IList<string> valueList;
|
||||
if (!parameters.TryGetValue(key, out valueList))
|
||||
{
|
||||
valueList = new List<string>();
|
||||
parameters.Add(key, valueList);
|
||||
}
|
||||
|
||||
valueList.Add(value);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
}
|
413
src/dotnet/commands/dotnet-new3/ExtendedCommandParser.cs
Normal file
413
src/dotnet/commands/dotnet-new3/ExtendedCommandParser.cs
Normal file
|
@ -0,0 +1,413 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.TemplateEngine.Abstractions;
|
||||
using Microsoft.TemplateEngine.Edge.Settings;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.New3
|
||||
{
|
||||
internal class ExtendedCommandParser
|
||||
{
|
||||
private CommandLineApplication _app;
|
||||
|
||||
// Hidden & default options. Listed here to avoid clashes with on-the-fly params from individual templates.
|
||||
private HashSet<string> _defaultCommandOptions;
|
||||
// key is the variant, value is the canonical version
|
||||
private IDictionary<string, string> _hiddenCommandCanonicalMapping;
|
||||
// key is the canonical version
|
||||
IDictionary<string, CommandOptionType> _hiddenCommandOptions;
|
||||
|
||||
// maps the template param variants to the canonical forms
|
||||
private IDictionary<string, string> _templateParamCanonicalMapping;
|
||||
// Canonical form -> data type
|
||||
private IDictionary<string, string> _templateParamDataTypeMapping;
|
||||
|
||||
// stores the parsed values
|
||||
private IDictionary<string, string> _parsedTemplateParams;
|
||||
private IDictionary<string, IList<string>> _parsedInternalParams;
|
||||
private IDictionary<string, IList<string>> _parsedRemainingParams;
|
||||
|
||||
// stores the options & arguments that are NOT hidden
|
||||
// this is used exclusively to show the help.
|
||||
// it's a bit of a hack.
|
||||
CommandLineApplication _helpDisplayer;
|
||||
|
||||
public ExtendedCommandParser()
|
||||
{
|
||||
_app = new CommandLineApplication(false);
|
||||
_defaultCommandOptions = new HashSet<string>();
|
||||
_hiddenCommandOptions = new Dictionary<string, CommandOptionType>();
|
||||
_hiddenCommandCanonicalMapping = new Dictionary<string, string>();
|
||||
_templateParamCanonicalMapping = new Dictionary<string, string>();
|
||||
_templateParamDataTypeMapping = new Dictionary<string, string>();
|
||||
|
||||
_parsedTemplateParams = new Dictionary<string, string>();
|
||||
_parsedInternalParams = new Dictionary<string, IList<string>>();
|
||||
_parsedRemainingParams = new Dictionary<string, IList<string>>();
|
||||
|
||||
_helpDisplayer = new CommandLineApplication(false);
|
||||
}
|
||||
|
||||
// TODO: consider optionally showing help for things not handled by the CommandLineApplication instance
|
||||
public void ShowHelp()
|
||||
{
|
||||
_helpDisplayer.ShowHelp();
|
||||
}
|
||||
|
||||
public void RemoveOption(CommandOption option)
|
||||
{
|
||||
_app.Options.Remove(option);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool IsParameterNameTaken(string testName)
|
||||
{
|
||||
return _defaultCommandOptions.Contains(testName)
|
||||
|| _hiddenCommandCanonicalMapping.ContainsKey(testName)
|
||||
|| _templateParamCanonicalMapping.ContainsKey(testName);
|
||||
}
|
||||
|
||||
public int Execute(params string[] args)
|
||||
{
|
||||
return _app.Execute(args);
|
||||
}
|
||||
|
||||
public void OnExecute(Func<Task<int>> invoke)
|
||||
{
|
||||
_app.OnExecute(invoke);
|
||||
}
|
||||
|
||||
// Returns the "standard" args that were input - the ones handled by the CommandLineApplication
|
||||
public List<string> RemainingArguments
|
||||
{
|
||||
get { return _app.RemainingArguments; }
|
||||
}
|
||||
|
||||
public CommandArgument Argument(string parameter, string description)
|
||||
{
|
||||
if (IsParameterNameTaken(parameter))
|
||||
{
|
||||
throw new Exception($"Parameter name {parameter} cannot be used for multiple purposes");
|
||||
}
|
||||
|
||||
_defaultCommandOptions.Add(parameter);
|
||||
|
||||
// its not hidden, add it to the help
|
||||
_helpDisplayer.Argument(parameter, description);
|
||||
|
||||
return _app.Argument(parameter, description);
|
||||
}
|
||||
|
||||
public void InternalOption(string parameterVariants, string canonical, string description, CommandOptionType optionType)
|
||||
{
|
||||
_helpDisplayer.Option(parameterVariants, description, optionType);
|
||||
HiddenInternalOption(parameterVariants, canonical, optionType);
|
||||
}
|
||||
|
||||
// NOTE: the exceptions here should never happen, this is strictly called by the program
|
||||
// Once testing is done, we can probably remove them.
|
||||
public void HiddenInternalOption(string parameterVariants, string canonical, CommandOptionType optionType)
|
||||
{
|
||||
string[] parameters = parameterVariants.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (IsParameterNameTaken(parameters[i]))
|
||||
{
|
||||
throw new Exception($"Parameter name {parameters[i]} cannot be used for multiple purposes");
|
||||
}
|
||||
|
||||
_hiddenCommandCanonicalMapping.Add(parameters[i], canonical);
|
||||
}
|
||||
|
||||
_hiddenCommandOptions.Add(canonical, optionType);
|
||||
}
|
||||
|
||||
public bool TemplateParamHasValue(string paramName)
|
||||
{
|
||||
return _parsedTemplateParams.ContainsKey(paramName);
|
||||
}
|
||||
|
||||
public string TemplateParamValue(string paramName)
|
||||
{
|
||||
_parsedTemplateParams.TryGetValue(paramName, out string value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// returns a copy of the template params
|
||||
public IReadOnlyDictionary<string, string> AllTemplateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Dictionary<string, string>(_parsedTemplateParams);
|
||||
}
|
||||
}
|
||||
|
||||
public bool InternalParamHasValue(string paramName)
|
||||
{
|
||||
return _parsedInternalParams.ContainsKey(paramName);
|
||||
}
|
||||
|
||||
public string InternalParamValue(string paramName)
|
||||
{
|
||||
if (_parsedInternalParams.TryGetValue(paramName, out IList<string> values))
|
||||
{
|
||||
return values.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<string> InternalParamValueList(string paramName)
|
||||
{
|
||||
_parsedInternalParams.TryGetValue(paramName, out IList<string> values);
|
||||
return values;
|
||||
}
|
||||
|
||||
public IDictionary<string, IList<string>> RemainingParameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return _parsedRemainingParams;
|
||||
}
|
||||
}
|
||||
|
||||
// Parses all command line args, and any input arg files.
|
||||
// NOTE: any previously parsed values are lost - this resets the parsed values.
|
||||
public void ParseArgs(IList<string> extraArgFileNames = null)
|
||||
{
|
||||
_parsedTemplateParams = new Dictionary<string, string>();
|
||||
_parsedInternalParams = new Dictionary<string, IList<string>>();
|
||||
_parsedRemainingParams = new Dictionary<string, IList<string>>();
|
||||
|
||||
if (extraArgFileNames == null)
|
||||
{
|
||||
extraArgFileNames = new List<string>();
|
||||
}
|
||||
IReadOnlyDictionary<string, IList<string>> allParameters = _app.ParseExtraArgs(extraArgFileNames);
|
||||
|
||||
foreach (KeyValuePair<string, IList<string>> param in allParameters)
|
||||
{
|
||||
string canonicalName;
|
||||
if (_hiddenCommandCanonicalMapping.TryGetValue(param.Key, out canonicalName))
|
||||
{
|
||||
CommandOptionType optionType = _hiddenCommandOptions[canonicalName];
|
||||
|
||||
if (optionType == CommandOptionType.MultipleValue)
|
||||
{
|
||||
; // nothing to check
|
||||
}
|
||||
else if (optionType == CommandOptionType.SingleValue)
|
||||
{
|
||||
if (param.Value.Count != 1)
|
||||
{
|
||||
throw new Exception($"Multiple values specified for single value parameter: {canonicalName}");
|
||||
}
|
||||
}
|
||||
else // NoValue
|
||||
{
|
||||
if (param.Value.Count != 1 || param.Value[0] != null)
|
||||
{
|
||||
throw new Exception($"Value specified for valueless parameter: {canonicalName}");
|
||||
}
|
||||
}
|
||||
|
||||
_parsedInternalParams.Add(canonicalName, param.Value);
|
||||
}
|
||||
else if (_templateParamCanonicalMapping.TryGetValue(param.Key, out canonicalName))
|
||||
{
|
||||
if (_parsedTemplateParams.ContainsKey(canonicalName))
|
||||
{
|
||||
// error, the same param was specified twice
|
||||
throw new Exception($"Parameter [{canonicalName}] was specified multiple times, including with the flag [{param.Key}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((param.Value[0] == null) && (_templateParamDataTypeMapping[canonicalName] != "bool"))
|
||||
{
|
||||
throw new Exception($"Parameter [{param.Key}] ({canonicalName}) must be given a value");
|
||||
}
|
||||
|
||||
// TODO: allow for multi-valued params
|
||||
_parsedTemplateParams[canonicalName] = param.Value[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a known internal or template param.
|
||||
_parsedRemainingParams[param.Key] = param.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Canonical is the template param name without any dashes. The things mapped to it all have dashes, including the param name itself.
|
||||
public void SetupTemplateParameters(IParameterSet allParams, IReadOnlyDictionary<string, string> parameterNameMap)
|
||||
{
|
||||
HashSet<string> invalidParams = new HashSet<string>();
|
||||
|
||||
foreach (ITemplateParameter parameter in allParams.ParameterDefinitions.Where(x => x.Priority != TemplateParameterPriority.Implicit).OrderBy(x => x.Name))
|
||||
{
|
||||
if (parameter.Name.IndexOf(':') >= 0)
|
||||
{ // Colon is reserved, template param names cannot have any.
|
||||
invalidParams.Add(parameter.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
string flagFullText;
|
||||
if (parameterNameMap == null || !parameterNameMap.TryGetValue(parameter.Name, out flagFullText))
|
||||
{
|
||||
flagFullText = parameter.Name;
|
||||
}
|
||||
|
||||
bool longNameFound = false;
|
||||
bool shortNameFound = false;
|
||||
|
||||
// always unless taken
|
||||
string nameAsParameter = "--" + flagFullText;
|
||||
if (!IsParameterNameTaken(nameAsParameter))
|
||||
{
|
||||
MapTemplateParamToCanonical(nameAsParameter, parameter.Name);
|
||||
longNameFound = true;
|
||||
}
|
||||
|
||||
// only as fallback
|
||||
string qualifiedName = "--param:" + flagFullText;
|
||||
if (!longNameFound && !IsParameterNameTaken(qualifiedName))
|
||||
{
|
||||
MapTemplateParamToCanonical(qualifiedName, parameter.Name);
|
||||
longNameFound = true;
|
||||
}
|
||||
|
||||
// always unless taken
|
||||
string shortName = "-" + PosixNameToShortName(flagFullText);
|
||||
if (!IsParameterNameTaken(shortName))
|
||||
{
|
||||
MapTemplateParamToCanonical(shortName, parameter.Name);
|
||||
shortNameFound = true;
|
||||
}
|
||||
|
||||
// only as fallback
|
||||
string singleLetterName = "-" + flagFullText.Substring(0, 1);
|
||||
if (!shortNameFound && !IsParameterNameTaken(singleLetterName))
|
||||
{
|
||||
MapTemplateParamToCanonical(singleLetterName, parameter.Name);
|
||||
shortNameFound = true;
|
||||
}
|
||||
|
||||
// only as fallback
|
||||
string qualifiedShortName = "-p:" + PosixNameToShortName(flagFullText);
|
||||
if (!shortNameFound && !IsParameterNameTaken(qualifiedShortName))
|
||||
{
|
||||
MapTemplateParamToCanonical(qualifiedShortName, parameter.Name);
|
||||
shortNameFound = true;
|
||||
}
|
||||
|
||||
// only as fallback
|
||||
string qualifiedSingleLetterName = "-p:" + flagFullText.Substring(0, 1);
|
||||
if (!shortNameFound && !IsParameterNameTaken(qualifiedSingleLetterName))
|
||||
{
|
||||
MapTemplateParamToCanonical(qualifiedSingleLetterName, parameter.Name);
|
||||
shortNameFound = true;
|
||||
}
|
||||
|
||||
if (!shortNameFound && !longNameFound)
|
||||
{
|
||||
invalidParams.Add(flagFullText);
|
||||
}
|
||||
else
|
||||
{
|
||||
_templateParamDataTypeMapping[parameter.Name] = parameter.DataType;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidParams.Count > 0)
|
||||
{
|
||||
string unusableDisplayList = string.Join(", ", invalidParams);
|
||||
throw new Exception($"Template is malformed. The following parameter names are invalid: {unusableDisplayList}");
|
||||
}
|
||||
}
|
||||
|
||||
private void MapTemplateParamToCanonical(string variant, string canonical)
|
||||
{
|
||||
if (_templateParamCanonicalMapping.TryGetValue(variant, out string existingCanonical))
|
||||
{
|
||||
throw new Exception($"Option variant {variant} for canonical {canonical} was already defined for canonical {existingCanonical}");
|
||||
}
|
||||
|
||||
_templateParamCanonicalMapping[variant] = canonical;
|
||||
}
|
||||
|
||||
// Concats the first letter of dash separated word.
|
||||
private static string PosixNameToShortName(string name)
|
||||
{
|
||||
IList<string> wordsInName = name.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
IList<string> firstLetters = new List<string>();
|
||||
|
||||
foreach (string word in wordsInName)
|
||||
{
|
||||
firstLetters.Add(word.Substring(0, 1));
|
||||
}
|
||||
|
||||
return string.Join("", firstLetters);
|
||||
}
|
||||
|
||||
private IDictionary<string, IList<string>> _canonicalToVariantsTemplateParamMap;
|
||||
|
||||
public IDictionary<string, IList<string>> CanonicalToVariantsTemplateParamMap
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_canonicalToVariantsTemplateParamMap == null)
|
||||
{
|
||||
_canonicalToVariantsTemplateParamMap = new Dictionary<string, IList<string>>();
|
||||
|
||||
foreach (KeyValuePair<string, string> variantToCanonical in _templateParamCanonicalMapping)
|
||||
{
|
||||
string variant = variantToCanonical.Key;
|
||||
string canonical = variantToCanonical.Value;
|
||||
|
||||
IList<string> variantList;
|
||||
if (!_canonicalToVariantsTemplateParamMap.TryGetValue(canonical, out variantList))
|
||||
{
|
||||
variantList = new List<string>();
|
||||
_canonicalToVariantsTemplateParamMap.Add(canonical, variantList);
|
||||
}
|
||||
|
||||
variantList.Add(variant);
|
||||
}
|
||||
}
|
||||
|
||||
return _canonicalToVariantsTemplateParamMap;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _app.Name;
|
||||
}
|
||||
set
|
||||
{
|
||||
_app.Name = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _app.FullName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_app.FullName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
228
src/dotnet/commands/dotnet-new3/HelpFormatter.cs
Normal file
228
src/dotnet/commands/dotnet-new3/HelpFormatter.cs
Normal file
|
@ -0,0 +1,228 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.New3
|
||||
{
|
||||
public class HelpFormatter
|
||||
{
|
||||
public static HelpFormatter<T> For<T>(IEnumerable<T> items, int columnPadding, char? headerSeparator = null, bool blankLineBetweenRows = false)
|
||||
{
|
||||
return new HelpFormatter<T>(items, columnPadding, headerSeparator, blankLineBetweenRows);
|
||||
}
|
||||
}
|
||||
|
||||
public class HelpFormatter<T>
|
||||
{
|
||||
private readonly bool _blankLineBetweenRows;
|
||||
private readonly int _columnPadding;
|
||||
private readonly List<ColumnDefinition> _columns = new List<ColumnDefinition>();
|
||||
private readonly char? _headerSeparator;
|
||||
private readonly IEnumerable<T> _items;
|
||||
|
||||
public HelpFormatter(IEnumerable<T> items, int columnPadding, char? headerSeparator, bool blankLineBetweenRows)
|
||||
{
|
||||
_items = items;
|
||||
_columnPadding = columnPadding;
|
||||
_headerSeparator = headerSeparator;
|
||||
_blankLineBetweenRows = blankLineBetweenRows;
|
||||
}
|
||||
|
||||
public HelpFormatter<T> DefineColumn(Func<T, string> binder, string header = null, int maxWidth = 0, bool alwaysMaximizeWidth = false)
|
||||
{
|
||||
_columns.Add(new ColumnDefinition(header, binder, maxWidth, alwaysMaximizeWidth));
|
||||
return this;
|
||||
}
|
||||
|
||||
public string Layout()
|
||||
{
|
||||
Dictionary<int, int> widthLookup = new Dictionary<int, int>();
|
||||
Dictionary<int, int> lineCountLookup = new Dictionary<int, int>();
|
||||
List<TextWrapper[]> textByRow = new List<TextWrapper[]>();
|
||||
|
||||
TextWrapper[] header = new TextWrapper[_columns.Count];
|
||||
int headerLines = 0;
|
||||
for (int i = 0; i < _columns.Count; ++i)
|
||||
{
|
||||
header[i] = new TextWrapper(_columns[i].Header, _columns[i].MaxWidth, _columns[i].AlwaysMaximizeWidth);
|
||||
headerLines = Math.Max(headerLines, header[i].LineCount);
|
||||
widthLookup[i] = header[i].MaxWidth;
|
||||
}
|
||||
|
||||
int lineNumber = 0;
|
||||
|
||||
foreach (T item in _items)
|
||||
{
|
||||
TextWrapper[] line = new TextWrapper[_columns.Count];
|
||||
int maxLineCount = 0;
|
||||
|
||||
for (int i = 0; i < _columns.Count; ++i)
|
||||
{
|
||||
line[i] = _columns[i].GetCell(item);
|
||||
widthLookup[i] = Math.Max(widthLookup[i], line[i].MaxWidth);
|
||||
maxLineCount = Math.Max(maxLineCount, line[i].LineCount);
|
||||
}
|
||||
|
||||
lineCountLookup[lineNumber++] = maxLineCount;
|
||||
textByRow.Add(line);
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
if (_columns.Any(x => !string.IsNullOrEmpty(x.Header)))
|
||||
{
|
||||
for (int j = 0; j < headerLines; ++j)
|
||||
{
|
||||
for (int i = 0; i < _columns.Count - 1; ++i)
|
||||
{
|
||||
b.Append(header[i][j, widthLookup[i]]);
|
||||
b.Append("".PadRight(_columnPadding));
|
||||
}
|
||||
|
||||
b.AppendLine(header[_columns.Count - 1][j, widthLookup[_columns.Count - 1]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (_headerSeparator.HasValue)
|
||||
{
|
||||
int totalWidth = _columnPadding * (_columns.Count - 1);
|
||||
|
||||
for (int i = 0; i < _columns.Count; ++i)
|
||||
{
|
||||
totalWidth += Math.Max(header[i].MaxWidth, widthLookup[i]);
|
||||
}
|
||||
|
||||
b.AppendLine("".PadRight(totalWidth, _headerSeparator.Value));
|
||||
}
|
||||
|
||||
int currentLine = 0;
|
||||
foreach (TextWrapper[] line in textByRow)
|
||||
{
|
||||
for (int j = 0; j < lineCountLookup[currentLine]; ++j)
|
||||
{
|
||||
for (int i = 0; i < _columns.Count - 1; ++i)
|
||||
{
|
||||
b.Append(line[i][j, widthLookup[i]]);
|
||||
b.Append("".PadRight(_columnPadding));
|
||||
}
|
||||
|
||||
b.AppendLine(line[_columns.Count - 1][j, widthLookup[_columns.Count - 1]]);
|
||||
}
|
||||
|
||||
if (_blankLineBetweenRows)
|
||||
{
|
||||
b.AppendLine();
|
||||
}
|
||||
|
||||
++currentLine;
|
||||
}
|
||||
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
private class ColumnDefinition
|
||||
{
|
||||
private readonly int _maxWidth;
|
||||
private readonly string _header;
|
||||
private readonly Func<T, string> _binder;
|
||||
private readonly bool _alwaysMaximizeWidth;
|
||||
|
||||
public ColumnDefinition(string header, Func<T, string> binder, int maxWidth = -1, bool alwaysMaximizeWidth = false)
|
||||
{
|
||||
_header = header;
|
||||
_maxWidth = maxWidth > 0 ? maxWidth : int.MaxValue;
|
||||
_binder = binder;
|
||||
_alwaysMaximizeWidth = alwaysMaximizeWidth && maxWidth > 0;
|
||||
}
|
||||
|
||||
public string Header => _header;
|
||||
|
||||
public bool AlwaysMaximizeWidth => _alwaysMaximizeWidth;
|
||||
|
||||
public int MaxWidth => _maxWidth;
|
||||
|
||||
public TextWrapper GetCell(T value)
|
||||
{
|
||||
return new TextWrapper(_binder(value), _maxWidth, _alwaysMaximizeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private class TextWrapper
|
||||
{
|
||||
private readonly IReadOnlyList<string> _lines;
|
||||
|
||||
public TextWrapper(string text, int maxWidth, bool alwaysMax)
|
||||
{
|
||||
List<string> lines = new List<string>();
|
||||
int position = 0;
|
||||
int realMaxWidth = alwaysMax ? maxWidth : 0;
|
||||
|
||||
while (position < text.Length)
|
||||
{
|
||||
int newline = text.IndexOf(Environment.NewLine, position, StringComparison.Ordinal);
|
||||
|
||||
if (newline > -1)
|
||||
{
|
||||
if (newline - position <= maxWidth)
|
||||
{
|
||||
lines.Add(text.Substring(position, newline - position).TrimEnd());
|
||||
position = newline + Environment.NewLine.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetLineText(text, lines, maxWidth, newline, ref position);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetLineText(text, lines, maxWidth, text.Length - 1, ref position);
|
||||
}
|
||||
|
||||
realMaxWidth = Math.Max(realMaxWidth, lines[lines.Count - 1].Length);
|
||||
}
|
||||
|
||||
_lines = lines;
|
||||
MaxWidth = realMaxWidth;
|
||||
}
|
||||
|
||||
public int LineCount => _lines.Count;
|
||||
|
||||
public int MaxWidth { get; }
|
||||
|
||||
public string this[int index, int padTo = 0]
|
||||
{
|
||||
get { return (_lines.Count > index ? _lines[index] : string.Empty).PadRight(MaxWidth).PadRight(padTo > MaxWidth ? padTo : MaxWidth); }
|
||||
}
|
||||
|
||||
private static void GetLineText(string text, List<string> lines, int maxLength, int end, ref int position)
|
||||
{
|
||||
if (text.Length - position < maxLength)
|
||||
{
|
||||
lines.Add(text.Substring(position));
|
||||
position = text.Length;
|
||||
return;
|
||||
}
|
||||
|
||||
int lastBreak = text.LastIndexOfAny(new[] { ' ', '-' }, end, end - position);
|
||||
while (lastBreak > 0 && lastBreak - position > maxLength)
|
||||
{
|
||||
--lastBreak;
|
||||
lastBreak = text.LastIndexOfAny(new[] { ' ', '-' }, lastBreak, lastBreak - position);
|
||||
}
|
||||
|
||||
if (lastBreak > 0)
|
||||
{
|
||||
lines.Add(text.Substring(position, lastBreak - position + 1).TrimEnd());
|
||||
position = lastBreak + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int properMax = Math.Min(maxLength - 1, text.Length - position);
|
||||
lines.Add(text.Substring(position, properMax) + '-');
|
||||
position += maxLength - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
716
src/dotnet/commands/dotnet-new3/Program.cs
Normal file
716
src/dotnet/commands/dotnet-new3/Program.cs
Normal file
|
@ -0,0 +1,716 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.TemplateEngine.Abstractions;
|
||||
using Microsoft.TemplateEngine.Abstractions.Mount;
|
||||
using Microsoft.TemplateEngine.Edge;
|
||||
using Microsoft.TemplateEngine.Edge.Settings;
|
||||
using Microsoft.TemplateEngine.Edge.Template;
|
||||
using Microsoft.TemplateEngine.Utils;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.New3
|
||||
{
|
||||
public class New3Command
|
||||
{
|
||||
private static readonly string HostIdentifier = "dotnetcli";
|
||||
private static readonly Version HostVersion = typeof(Program).GetTypeInfo().Assembly.GetName().Version;
|
||||
private static DefaultTemplateEngineHost Host;
|
||||
|
||||
private static void SetupInternalCommands(ExtendedCommandParser appExt)
|
||||
{
|
||||
// visible
|
||||
appExt.InternalOption("-l|--list", "--list", "List templates containing the specified name.", CommandOptionType.NoValue);
|
||||
appExt.InternalOption("-n|--name", "--name", "The name for the output being created. If no name is specified, the name of the current directory is used.", CommandOptionType.SingleValue);
|
||||
appExt.InternalOption("-h|--help", "--help", "Display help for the indicated template's parameters.", CommandOptionType.NoValue);
|
||||
|
||||
// hidden
|
||||
appExt.HiddenInternalOption("-d|--dir", "--dir", CommandOptionType.NoValue);
|
||||
appExt.HiddenInternalOption("-a|--alias", "--alias", CommandOptionType.SingleValue);
|
||||
appExt.HiddenInternalOption("-x|--extra-args", "--extra-args", CommandOptionType.MultipleValue);
|
||||
appExt.HiddenInternalOption("--locale", "--locale", CommandOptionType.SingleValue);
|
||||
appExt.HiddenInternalOption("--quiet", "--quiet", CommandOptionType.NoValue);
|
||||
appExt.HiddenInternalOption("-i|--install", "--install", CommandOptionType.MultipleValue);
|
||||
|
||||
// reserved but not currently used
|
||||
appExt.HiddenInternalOption("-up|--update", "--update", CommandOptionType.MultipleValue);
|
||||
appExt.HiddenInternalOption("-u|--uninstall", "--uninstall", CommandOptionType.MultipleValue);
|
||||
appExt.HiddenInternalOption("--skip-update-check", "--skip-update-check", CommandOptionType.NoValue);
|
||||
|
||||
// Preserve these for now - they've got the help text, in case we want it back.
|
||||
// (they'll need to get converted to extended option calls)
|
||||
//
|
||||
//CommandOption dirOption = app.Option("-d|--dir", "Indicates whether to create a directory for the generated content.", CommandOptionType.NoValue);
|
||||
//CommandOption aliasOption = app.Option("-a|--alias", "Creates an alias for the specified template.", CommandOptionType.SingleValue);
|
||||
//CommandOption parametersFilesOption = app.Option("-x|--extra-args", "Specifies a file containing additional parameters.", CommandOptionType.MultipleValue);
|
||||
//CommandOption localeOption = app.Option("--locale", "The locale to use", CommandOptionType.SingleValue);
|
||||
//CommandOption quietOption = app.Option("--quiet", "Doesn't output any status information.", CommandOptionType.NoValue);
|
||||
//CommandOption installOption = app.Option("-i|--install", "Installs a source or a template pack.", CommandOptionType.MultipleValue);
|
||||
|
||||
//CommandOption update = app.Option("--update", "Update matching templates.", CommandOptionType.NoValue);
|
||||
}
|
||||
|
||||
public static int Run(string[] args)
|
||||
{
|
||||
// Initial host setup has the current locale. May need to be changed based on inputs.
|
||||
Host = new DefaultTemplateEngineHost(HostIdentifier, HostVersion, CultureInfo.CurrentCulture.Name);
|
||||
EngineEnvironmentSettings.Host = Host;
|
||||
|
||||
ExtendedCommandParser app = new ExtendedCommandParser()
|
||||
{
|
||||
Name = "dotnet new3",
|
||||
FullName = "Template Instantiation Commands for .NET Core CLI."
|
||||
};
|
||||
SetupInternalCommands(app);
|
||||
CommandArgument templateNames = app.Argument("template", "The template to instantiate.");
|
||||
|
||||
app.OnExecute(async () =>
|
||||
{
|
||||
app.ParseArgs();
|
||||
if (app.InternalParamHasValue("--extra-args"))
|
||||
{
|
||||
app.ParseArgs(app.InternalParamValueList("--extra-args"));
|
||||
}
|
||||
|
||||
if (app.RemainingParameters.ContainsKey("--debug:attach"))
|
||||
{
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
if (app.InternalParamHasValue("--locale"))
|
||||
{
|
||||
string newLocale = app.InternalParamValue("--locale");
|
||||
if (!ValidateLocaleFormat(newLocale))
|
||||
{
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("Invalid format for input locale: [{0}]. Example valid formats: [en] [en-US]", newLocale));
|
||||
return -1;
|
||||
}
|
||||
|
||||
Host.UpdateLocale(newLocale);
|
||||
}
|
||||
|
||||
int resultCode = InitializationAndDebugging(app, out bool shouldExit);
|
||||
if (shouldExit)
|
||||
{
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
resultCode = ParseTemplateArgs(app, templateNames.Value, out shouldExit);
|
||||
if (shouldExit)
|
||||
{
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
resultCode = MaintenanceAndInfo(app, templateNames.Value, out shouldExit);
|
||||
if (shouldExit)
|
||||
{
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
return await CreateTemplate(app, templateNames.Value);
|
||||
});
|
||||
|
||||
int result;
|
||||
try
|
||||
{
|
||||
using (Timing.Over("Execute"))
|
||||
{
|
||||
result = app.Execute(args);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AggregateException ax = ex as AggregateException;
|
||||
|
||||
while (ax != null && ax.InnerExceptions.Count == 1)
|
||||
{
|
||||
ex = ax.InnerException;
|
||||
ax = ex as AggregateException;
|
||||
}
|
||||
|
||||
Reporter.Error.WriteLine(ex.Message.Bold().Red());
|
||||
|
||||
while (ex.InnerException != null)
|
||||
{
|
||||
ex = ex.InnerException;
|
||||
ax = ex as AggregateException;
|
||||
|
||||
while (ax != null && ax.InnerExceptions.Count == 1)
|
||||
{
|
||||
ex = ax.InnerException;
|
||||
ax = ex as AggregateException;
|
||||
}
|
||||
|
||||
Reporter.Error.WriteLine(ex.Message.Bold().Red());
|
||||
}
|
||||
|
||||
Reporter.Error.WriteLine(ex.StackTrace.Bold().Red());
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<int> CreateTemplate(ExtendedCommandParser app, string templateName)
|
||||
{
|
||||
string nameValue = app.InternalParamValue("--name");
|
||||
string fallbackName = new DirectoryInfo(Directory.GetCurrentDirectory()).Name;
|
||||
bool dirValue = app.InternalParamHasValue("--dir");
|
||||
string aliasName = app.InternalParamValue("--alias");
|
||||
bool skipUpdateCheckValue = app.InternalParamHasValue("--skip-update-check");
|
||||
|
||||
// TODO: refactor alias creation out of InstantiateAsync()
|
||||
TemplateCreationResult instantiateResult = await TemplateCreator.InstantiateAsync(templateName ?? "", nameValue, fallbackName, dirValue, aliasName, app.AllTemplateParams, skipUpdateCheckValue);
|
||||
|
||||
string resultTemplateName = string.IsNullOrEmpty(instantiateResult.TemplateFullName) ? templateName : instantiateResult.TemplateFullName;
|
||||
|
||||
switch (instantiateResult.Status)
|
||||
{
|
||||
case CreationResultStatus.AliasSucceeded:
|
||||
// TODO: get this localized - in the mean time just list the templates, showing the alias
|
||||
//EngineEnvironmentSettings.Host.LogMessage("Alias creation successful");
|
||||
ListTemplates(templateName);
|
||||
break;
|
||||
case CreationResultStatus.AliasFailed:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("Specified alias {0} already exists. Please specify a different alias.", aliasName));
|
||||
ListTemplates(templateName);
|
||||
break;
|
||||
case CreationResultStatus.CreateSucceeded:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("The template {0} created successfully. Please run \"dotnet restore\" to get started!", resultTemplateName));
|
||||
break;
|
||||
case CreationResultStatus.CreateFailed:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("Template {0} could not be created. Error returned was: {1}", resultTemplateName, instantiateResult.Message));
|
||||
ListTemplates(templateName);
|
||||
break;
|
||||
case CreationResultStatus.InstallSucceeded:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("The template {0} installed successfully. You can use \"dotnet new {0}\" to get started with the new template.", resultTemplateName));
|
||||
break;
|
||||
case CreationResultStatus.InstallFailed:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("Template {0} could not be created. Error returned was: {1}.", resultTemplateName, instantiateResult.Message));
|
||||
break;
|
||||
case CreationResultStatus.MissingMandatoryParam:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("Mandatory parameter {0} missing for template {1}.", instantiateResult.Message, resultTemplateName));
|
||||
break;
|
||||
case CreationResultStatus.TemplateNotFound:
|
||||
EngineEnvironmentSettings.Host.LogMessage(string.Format("Template {0} could not be created. Error returned was: {1}.", resultTemplateName, instantiateResult.Message));
|
||||
ListTemplates(templateName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return instantiateResult.ResultCode;
|
||||
}
|
||||
|
||||
private static int InitializationAndDebugging(ExtendedCommandParser app, out bool shouldExit)
|
||||
{
|
||||
bool reinitFlag = app.RemainingArguments.Any(x => x == "--debug:reinit");
|
||||
if (reinitFlag)
|
||||
{
|
||||
Paths.User.FirstRunCookie.Delete();
|
||||
}
|
||||
|
||||
// Note: this leaves things in a weird state. Might be related to the localized caches.
|
||||
// not sure, need to look into it.
|
||||
if (reinitFlag || app.RemainingArguments.Any(x => x == "--debug:reset-config"))
|
||||
{
|
||||
Paths.User.AliasesFile.Delete();
|
||||
Paths.User.SettingsFile.Delete();
|
||||
TemplateCache.DeleteAllLocaleCacheFiles();
|
||||
shouldExit = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!Paths.User.BaseDir.Exists() || !Paths.User.FirstRunCookie.Exists())
|
||||
{
|
||||
if (!app.InternalParamHasValue("--quiet"))
|
||||
{
|
||||
Reporter.Output.WriteLine("Getting things ready for first use...");
|
||||
}
|
||||
|
||||
ConfigureEnvironment();
|
||||
Paths.User.FirstRunCookie.WriteAllText("");
|
||||
}
|
||||
|
||||
if (app.RemainingArguments.Any(x => x == "--debug:showconfig"))
|
||||
{
|
||||
ShowConfig();
|
||||
shouldExit = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
shouldExit = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ParseTemplateArgs(ExtendedCommandParser app, string templateName, out bool shouldExit)
|
||||
{
|
||||
try
|
||||
{
|
||||
IReadOnlyCollection<ITemplateInfo> templates = TemplateCreator.List(templateName);
|
||||
if (templates.Count == 1)
|
||||
{
|
||||
ITemplateInfo templateInfo = templates.First();
|
||||
|
||||
ITemplate template = SettingsLoader.LoadTemplate(templateInfo);
|
||||
IParameterSet allParams = template.Generator.GetParametersForTemplate(template);
|
||||
IReadOnlyDictionary<string, string> parameterNameMap = template.Generator.ParameterMapForTemplate(template);
|
||||
app.SetupTemplateParameters(allParams, parameterNameMap);
|
||||
}
|
||||
|
||||
// re-parse after setting up the template params
|
||||
app.ParseArgs(app.InternalParamValueList("--extra-args"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Reporter.Error.WriteLine(ex.Message.Red().Bold());
|
||||
app.ShowHelp();
|
||||
shouldExit = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (app.RemainingParameters.Any(x => !x.Key.StartsWith("--debug:")))
|
||||
{
|
||||
EngineEnvironmentSettings.Host.LogMessage("Invalid input switch:");
|
||||
foreach (string flag in app.RemainingParameters.Keys)
|
||||
{
|
||||
EngineEnvironmentSettings.Host.LogMessage($"\t{flag}");
|
||||
}
|
||||
|
||||
shouldExit = true;
|
||||
return DisplayHelp(templateName, app, app.AllTemplateParams);
|
||||
}
|
||||
|
||||
shouldExit = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int MaintenanceAndInfo(ExtendedCommandParser app, string templateName, out bool shouldExit)
|
||||
{
|
||||
if (app.InternalParamHasValue("--list"))
|
||||
{
|
||||
ListTemplates(templateName);
|
||||
shouldExit = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (app.InternalParamHasValue("--help"))
|
||||
{
|
||||
shouldExit = true;
|
||||
return DisplayHelp(templateName, app, app.AllTemplateParams);
|
||||
}
|
||||
|
||||
if (app.InternalParamHasValue("--install"))
|
||||
{
|
||||
InstallPackages(app.InternalParamValueList("--install").ToList(), app.InternalParamHasValue("--quiet"));
|
||||
shouldExit = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//if (update.HasValue())
|
||||
//{
|
||||
// return PerformUpdateAsync(templateName, quiet, source);
|
||||
//}
|
||||
|
||||
if (string.IsNullOrEmpty(templateName))
|
||||
{
|
||||
ListTemplates(string.Empty);
|
||||
shouldExit = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
shouldExit = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Regex _localeFormatRegex = new Regex(@"
|
||||
^
|
||||
[a-z]{2}
|
||||
(?:-[A-Z]{2})?
|
||||
$"
|
||||
, RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static bool ValidateLocaleFormat(string localeToCheck)
|
||||
{
|
||||
return _localeFormatRegex.IsMatch(localeToCheck);
|
||||
}
|
||||
|
||||
//private static async Task<int> PerformUpdateAsync(string name, bool quiet, CommandOption source)
|
||||
//{
|
||||
// HashSet<IConfiguredTemplateSource> allSources = new HashSet<IConfiguredTemplateSource>();
|
||||
// HashSet<string> toInstall = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// foreach (ITemplate template in TemplateCreator.List(name, source))
|
||||
// {
|
||||
// allSources.Add(template.Source);
|
||||
// }
|
||||
|
||||
// foreach (IConfiguredTemplateSource src in allSources)
|
||||
// {
|
||||
// if (!quiet)
|
||||
// {
|
||||
// Reporter.Output.WriteLine($"Checking for updates for {src.Alias}...");
|
||||
// }
|
||||
|
||||
// bool updatesReady;
|
||||
|
||||
// if (src.ParentSource != null)
|
||||
// {
|
||||
// updatesReady = await src.Source.CheckForUpdatesAsync(src.ParentSource, src.Location);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// updatesReady = await src.Source.CheckForUpdatesAsync(src.Location);
|
||||
// }
|
||||
|
||||
// if (updatesReady)
|
||||
// {
|
||||
// if (!quiet)
|
||||
// {
|
||||
// Reporter.Output.WriteLine($"An update for {src.Alias} is available...");
|
||||
// }
|
||||
|
||||
// string packageId = src.ParentSource != null
|
||||
// ? src.Source.GetInstallPackageId(src.ParentSource, src.Location)
|
||||
// : src.Source.GetInstallPackageId(src.Location);
|
||||
|
||||
// toInstall.Add(packageId);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if(toInstall.Count == 0)
|
||||
// {
|
||||
// if (!quiet)
|
||||
// {
|
||||
// Reporter.Output.WriteLine("No updates were found.");
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// if (!quiet)
|
||||
// {
|
||||
// Reporter.Output.WriteLine("Installing updates...");
|
||||
// }
|
||||
|
||||
// List<string> installCommands = new List<string>();
|
||||
// List<string> uninstallCommands = new List<string>();
|
||||
|
||||
// foreach (string packageId in toInstall)
|
||||
// {
|
||||
// installCommands.Add("-i");
|
||||
// installCommands.Add(packageId);
|
||||
|
||||
// uninstallCommands.Add("-i");
|
||||
// uninstallCommands.Add(packageId);
|
||||
// }
|
||||
|
||||
// installCommands.Add("--quiet");
|
||||
// uninstallCommands.Add("--quiet");
|
||||
|
||||
// Command.CreateDotNet("new3", uninstallCommands).ForwardStdOut().ForwardStdErr().Execute();
|
||||
// Command.CreateDotNet("new3", installCommands).ForwardStdOut().ForwardStdErr().Execute();
|
||||
// Broker.ComponentRegistry.ForceReinitialize();
|
||||
|
||||
// if (!quiet)
|
||||
// {
|
||||
// Reporter.Output.WriteLine("Done.");
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
private static void ConfigureEnvironment()
|
||||
{
|
||||
string userNuGetConfig = $@"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key=""dotnet new3 builtins"" value = ""{Paths.Global.BuiltInsFeed}""/>
|
||||
</packageSources>
|
||||
</configuration>";
|
||||
Paths.User.NuGetConfig.WriteAllText(userNuGetConfig);
|
||||
|
||||
string[] packageList = Paths.Global.DefaultInstallPackageList.ReadAllText().Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (packageList.Length > 0)
|
||||
{
|
||||
InstallPackages(packageList, true);
|
||||
}
|
||||
|
||||
packageList = Paths.Global.DefaultInstallTemplateList.ReadAllText().Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (packageList.Length > 0)
|
||||
{
|
||||
InstallPackages(packageList, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InstallPackages(IReadOnlyList<string> packageNames, bool quiet = false)
|
||||
{
|
||||
List<string> toInstall = new List<string>();
|
||||
|
||||
foreach (string package in packageNames)
|
||||
{
|
||||
string pkg = package.Trim();
|
||||
pkg = Environment.ExpandEnvironmentVariables(pkg);
|
||||
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(pkg) || File.Exists(pkg))
|
||||
{
|
||||
string packageLocation = new DirectoryInfo(pkg).FullName;
|
||||
TemplateCache.Scan(packageLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
string directory = Path.GetDirectoryName(pkg);
|
||||
string fileGlob = Path.GetFileName(pkg);
|
||||
string fullDirectory = new DirectoryInfo(directory).FullName;
|
||||
string fullPathGlob = Path.Combine(fullDirectory, fileGlob);
|
||||
TemplateCache.Scan(fullPathGlob);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
EngineEnvironmentSettings.Host.OnNonCriticalError("InvalidPackageSpecification", string.Format($"Package [{pkg}] is not a valid package specification"), null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TemplateCache.WriteTemplateCaches();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
ListTemplates(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ListTemplates(string templateNames)
|
||||
{
|
||||
IEnumerable<ITemplateInfo> results = TemplateCreator.List(templateNames);
|
||||
HelpFormatter<ITemplateInfo> formatter = new HelpFormatter<ITemplateInfo>(results, 6, '-', false);
|
||||
formatter.DefineColumn(delegate (ITemplateInfo t) { return t.Name; }, "Templates");
|
||||
formatter.DefineColumn(delegate (ITemplateInfo t) { return $"[{t.ShortName}]"; }, "Short Name");
|
||||
formatter.DefineColumn(delegate (ITemplateInfo t) { return AliasRegistry.GetAliasForTemplate(t) ?? ""; }, "Alias");
|
||||
Reporter.Output.WriteLine(formatter.Layout());
|
||||
}
|
||||
|
||||
private static void ShowConfig()
|
||||
{
|
||||
Reporter.Output.WriteLine("dotnet new3 current configuration:");
|
||||
Reporter.Output.WriteLine(" ");
|
||||
TableFormatter.Print(SettingsLoader.MountPoints, "(No Items)", " ", '-', new Dictionary<string, Func<MountPointInfo, object>>
|
||||
{
|
||||
{"Mount Points", x => x.Place},
|
||||
{"Id", x => x.MountPointId},
|
||||
{"Parent", x => x.ParentMountPointId},
|
||||
{"Factory", x => x.MountPointFactoryId}
|
||||
});
|
||||
|
||||
TableFormatter.Print(SettingsLoader.Components.OfType<IMountPointFactory>(), "(No Items)", " ", '-', new Dictionary<string, Func<IMountPointFactory, object>>
|
||||
{
|
||||
{"Mount Point Factories", x => x.Id},
|
||||
{"Type", x => x.GetType().FullName},
|
||||
{"Assembly", x => x.GetType().GetTypeInfo().Assembly.FullName}
|
||||
});
|
||||
|
||||
TableFormatter.Print(SettingsLoader.Components.OfType<IGenerator>(), "(No Items)", " ", '-', new Dictionary<string, Func<IGenerator, object>>
|
||||
{
|
||||
{"Generators", x => x.Id},
|
||||
{"Type", x => x.GetType().FullName},
|
||||
{"Assembly", x => x.GetType().GetTypeInfo().Assembly.FullName}
|
||||
});
|
||||
}
|
||||
|
||||
private static int DisplayHelp(string templateNames, ExtendedCommandParser app, IReadOnlyDictionary<string, string> userParameters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(templateNames))
|
||||
{ // no template specified
|
||||
app.ShowHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
IReadOnlyCollection<ITemplateInfo> templates = TemplateCreator.List(templateNames);
|
||||
|
||||
if (templates.Count > 1)
|
||||
{
|
||||
ListTemplates(templateNames);
|
||||
return -1;
|
||||
}
|
||||
else if (templates.Count == 1)
|
||||
{
|
||||
ITemplateInfo templateInfo = templates.First();
|
||||
return TemplateHelp(templateInfo, app, userParameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add a message indicating no templates matched the pattern. Requires LOC coordination
|
||||
ListTemplates(string.Empty);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int TemplateHelp(ITemplateInfo templateInfo, ExtendedCommandParser app, IReadOnlyDictionary<string, string> userParameters)
|
||||
{
|
||||
Reporter.Output.WriteLine(templateInfo.Name);
|
||||
if (!string.IsNullOrWhiteSpace(templateInfo.Author))
|
||||
{
|
||||
Reporter.Output.WriteLine($"Author: {templateInfo.Author}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(templateInfo.Description))
|
||||
{
|
||||
Reporter.Output.WriteLine($"Description: {templateInfo.Description}");
|
||||
}
|
||||
|
||||
ITemplate template = SettingsLoader.LoadTemplate(templateInfo);
|
||||
IParameterSet allParams = TemplateCreator.SetupDefaultParamValuesFromTemplateAndHost(template, template.DefaultName);
|
||||
TemplateCreator.ResolveUserParameters(template, allParams, userParameters);
|
||||
ParameterHelp(allParams, app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void ParameterHelp(IParameterSet allParams, ExtendedCommandParser app)
|
||||
{
|
||||
IEnumerable<ITemplateParameter> filteredParams = allParams.ParameterDefinitions.Where(x => x.Priority != TemplateParameterPriority.Implicit);
|
||||
|
||||
if (filteredParams.Any())
|
||||
{
|
||||
HelpFormatter<ITemplateParameter> formatter = new HelpFormatter<ITemplateParameter>(filteredParams, 2, null, true);
|
||||
|
||||
formatter.DefineColumn(delegate (ITemplateParameter param)
|
||||
{
|
||||
// the key is guaranteed to exist
|
||||
IList<string> variants = app.CanonicalToVariantsTemplateParamMap[param.Name];
|
||||
string options = string.Join("|", variants.Reverse());
|
||||
return " " + options;
|
||||
},
|
||||
"Options:"
|
||||
);
|
||||
|
||||
formatter.DefineColumn(delegate (ITemplateParameter param)
|
||||
{
|
||||
StringBuilder displayValue = new StringBuilder(255);
|
||||
displayValue.AppendLine(param.Documentation);
|
||||
|
||||
if (string.Equals(param.DataType, "choice", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
displayValue.AppendLine(string.Join(", ", param.Choices));
|
||||
}
|
||||
else
|
||||
{
|
||||
displayValue.Append(param.DataType ?? "string");
|
||||
displayValue.AppendLine(" - " + param.Priority.ToString());
|
||||
}
|
||||
|
||||
if (allParams.ResolvedValues.TryGetValue(param, out object resolvedValueObject))
|
||||
{
|
||||
string resolvedValue = resolvedValueObject as string;
|
||||
|
||||
if (!string.IsNullOrEmpty(resolvedValue)
|
||||
&& !string.IsNullOrEmpty(param.DefaultValue)
|
||||
&& !string.Equals(param.DefaultValue, resolvedValue))
|
||||
{
|
||||
displayValue.AppendLine("Configured Value: " + resolvedValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(param.DefaultValue))
|
||||
{
|
||||
displayValue.AppendLine("Default: " + param.DefaultValue);
|
||||
}
|
||||
|
||||
return displayValue.ToString();
|
||||
},
|
||||
string.Empty
|
||||
);
|
||||
|
||||
Reporter.Output.WriteLine(formatter.Layout());
|
||||
}
|
||||
else
|
||||
{
|
||||
Reporter.Output.WriteLine(" (No Parameters)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TableFormatter
|
||||
{
|
||||
public static void Print<T>(IEnumerable<T> items, string noItemsMessage, string columnPad, char header, Dictionary<string, Func<T, object>> dictionary)
|
||||
{
|
||||
List<string>[] columns = new List<string>[dictionary.Count];
|
||||
|
||||
for (int i = 0; i < dictionary.Count; ++i)
|
||||
{
|
||||
columns[i] = new List<string>();
|
||||
}
|
||||
|
||||
string[] headers = new string[dictionary.Count];
|
||||
int[] columnWidths = new int[dictionary.Count];
|
||||
int valueCount = 0;
|
||||
|
||||
foreach (T item in items)
|
||||
{
|
||||
int index = 0;
|
||||
foreach (KeyValuePair<string, Func<T, object>> act in dictionary)
|
||||
{
|
||||
headers[index] = act.Key;
|
||||
columns[index++].Add(act.Value(item)?.ToString() ?? "(null)");
|
||||
}
|
||||
++valueCount;
|
||||
}
|
||||
|
||||
if (valueCount > 0)
|
||||
{
|
||||
for (int i = 0; i < columns.Length; ++i)
|
||||
{
|
||||
columnWidths[i] = Math.Max(columns[i].Max(x => x.Length), headers[i].Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = 0;
|
||||
foreach (KeyValuePair<string, Func<T, object>> act in dictionary)
|
||||
{
|
||||
headers[index] = act.Key;
|
||||
columnWidths[index++] = act.Key.Length;
|
||||
}
|
||||
}
|
||||
|
||||
int headerWidth = columnWidths.Sum() + columnPad.Length * (dictionary.Count - 1);
|
||||
|
||||
for (int i = 0; i < headers.Length - 1; ++i)
|
||||
{
|
||||
Reporter.Output.Write(headers[i].PadRight(columnWidths[i]));
|
||||
Reporter.Output.Write(columnPad);
|
||||
}
|
||||
|
||||
Reporter.Output.WriteLine(headers[headers.Length - 1]);
|
||||
Reporter.Output.WriteLine("".PadRight(headerWidth, header));
|
||||
|
||||
for (int i = 0; i < valueCount; ++i)
|
||||
{
|
||||
for (int j = 0; j < columns.Length - 1; ++j)
|
||||
{
|
||||
Reporter.Output.Write(columns[j][i].PadRight(columnWidths[j]));
|
||||
Reporter.Output.Write(columnPad);
|
||||
}
|
||||
|
||||
Reporter.Output.WriteLine(columns[headers.Length - 1][i]);
|
||||
}
|
||||
|
||||
if (valueCount == 0)
|
||||
{
|
||||
Reporter.Output.WriteLine(noItemsMessage);
|
||||
}
|
||||
|
||||
Reporter.Output.WriteLine(" ");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,6 +80,19 @@
|
|||
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions">
|
||||
<Version>1.0.1-beta-000933</Version>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="Microsoft.TemplateEngine.Utils">
|
||||
<Version>1.0.0-beta1-20161216-12</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.TemplateEngine.Abstractions">
|
||||
<Version>1.0.0-beta1-20161216-12</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.TemplateEngine.Edge">
|
||||
<Version>1.0.0-beta1-20161216-12</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.TemplateEngine.Orchestrator.RunnableProjects">
|
||||
<Version>1.0.0-beta1-20161216-12</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
|
||||
|
|
Loading…
Reference in a new issue