/// <summary> /// Creates a command:parameter element /// </summary> /// <param name="param">The parsed parameter object</param> /// <param name="isSyntax">Render using the format for syntax blocks where we do not render [switch]</param> /// <returns></returns> private static XElement CreateParameter(MamlParameter param, bool isSyntax = false) { string mamlType = ConvertPSTypeToMamlType(param); bool isSwitchParameter = mamlType == "SwitchParameter" || mamlType == "System.Management.Automation.SwitchParameter"; return(new XElement(commandNS + "parameter", new XAttribute("required", param.Required), new XAttribute("variableLength", param.VariableLength), new XAttribute("globbing", param.Globbing), new XAttribute("pipelineInput", param.PipelineInput), new XAttribute("position", param.Position.ToLower()), new XAttribute("aliases", param.Aliases.Any() ? string.Join(", ", param.Aliases) : "none"), new XElement(mamlNS + "name", param.Name), new XElement(mamlNS + "Description", GenerateParagraphs(param.Description)), isSyntax && param.ParameterValueGroup.Any() ? new XElement(commandNS + "parameterValueGroup", param.ParameterValueGroup.Select(pvg => CreateParameterValueGroup(pvg))) : null, // we don't add [switch] info to make it appear good !isSyntax || !isSwitchParameter ? new XElement(commandNS + "parameterValue", new XAttribute("required", param.ValueRequired), new XAttribute("variableLength", param.ValueVariableLength), mamlType) : null, new XElement(devNS + "type", new XElement(mamlNS + "name", mamlType), new XElement(mamlNS + "uri")), new XElement(devNS + "defaultValue", isSwitchParameter && (string.IsNullOrEmpty(param.DefaultValue) || param.DefaultValue == "None") ? "False" : param.DefaultValue ?? "None"))); }
public void ReturnsSyntaxString() { var command = new MamlCommand() { Name = "Get-Foo", SupportCommonParameters = true }; var param1 = new MamlParameter() { Name = "Bar", Type = "BarObject" }; var syntax = new MamlSyntax() { IsDefault = true }; syntax.Parameters.Add(param1); string syntaxString = MarkdownV2Renderer.GetSyntaxString(command, syntax); Assert.Equal("Get-Foo [-Bar <BarObject>] [<CommonParameters>]", syntaxString); }
private void FillUpSyntax(MamlSyntax syntax, string name) { var parametersList = new List <MamlParameter>(); foreach (var pair in _parameterName2ParameterSetMap) { MamlParameter param = null; if (pair.Item2.ContainsKey(name)) { param = pair.Item2[name]; } else { if (pair.Item2.Count == 1 && pair.Item2.First().Key == ALL_PARAM_SETS_MONIKER) { param = pair.Item2.First().Value; } } if (param != null) { parametersList.Add(param); } } // order parameters based on position // User OrderBy instead of Sort for stable sort syntax.Parameters.AddRange(parametersList.OrderBy(x => x.Position)); }
private MamlCommand GetModel2() { MamlCommand command = new MamlCommand() { Name = "Get-Foo", Synopsis = new SectionBody("This is the synopsis"), Description = new SectionBody("This is a long description.\r\nWith two paragraphs."), Notes = new SectionBody("This is a multiline note.\r\nSecond line.\r\nSecond Command") }; command.Links.Add(new MamlLink(true) { LinkName = "[foo]()\r\n[bar]()", }); command.Examples.Add(new MamlExample() { Title = "Example 1", Code = new[] { new MamlCodeBlock("PS:> Get-Help -YouNeedIt") }, Remarks = "This does stuff!" } ); command.Inputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Foo" } ); var parameterName = new MamlParameter() { Type = "String", Name = "Name", Required = true, Description = "Parameter Description.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "1", DefaultValue = "trololo", Aliases = new string[] { "GF", "Foos", "Do" }, }; command.Parameters.Add(parameterName); var syntax1 = new MamlSyntax() { ParameterSetName = "ByName", }; syntax1.Parameters.Add(parameterName); command.Syntax.Add(syntax1); return(command); }
private MamlCommand GetModel1() { MamlCommand command = new MamlCommand() { Name = "Get-Foo", Synopsis = new SectionBody("This is the synopsis"), Description = new SectionBody("This is a long description.\r\nWith two paragraphs."), Notes = new SectionBody("This is a multiline note.\r\nSecond line.\r\nFirst Command") }; command.Links.Add(new MamlLink(true) { LinkName = "[foo]()\r\n", }); command.Inputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Foo" } ); command.Outputs.Add(new MamlInputOutput() { TypeName = "String", Description = null } ); var parameterName = new MamlParameter() { Type = "String", Name = "Name", Required = true, Description = "Parameter Description.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "1", DefaultValue = "trololo", Aliases = new string[] { "GF", "Foos", "Do" }, }; command.Parameters.Add(parameterName); var syntax1 = new MamlSyntax() { ParameterSetName = "ByName", }; syntax1.Parameters.Add(parameterName); command.Syntax.Add(syntax1); return(command); }
private MamlCommand GetRegenerated() { MamlCommand metadataCommand = new MamlCommand() { Name = "Get-Foo", Description = "This is a long description.\r\nWith two paragraphs.", Synopsis = "This is a old synopsis.", Notes = "These are old notes" }; var parameterName1 = new MamlParameter() { Type = "String[]", // DIFF!! Name = "Name", Description = "Old Description", Required = true, VariableLength = true, Globbing = false, // DIFF!! PipelineInput = "True (ByValue)", Position = "Named", // DIFF!! Aliases = new string[] { "GF", "Foo", "Bar" } }; var parameterNew = new MamlParameter() { Type = "String", Name = "NewParam", Description = "Old Param Description" }; metadataCommand.Parameters.Add(parameterName1); metadataCommand.Parameters.Add(parameterNew); var syntax3 = new MamlSyntax() { ParameterSetName = "AnotherName" }; syntax3.Parameters.Add(parameterName1); syntax3.Parameters.Add(parameterNew); metadataCommand.Syntax.Add(syntax3); var syntax4 = new MamlSyntax() { ParameterSetName = "SetNewName", IsDefault = true }; syntax4.Parameters.Add(parameterName1); metadataCommand.Syntax.Add(syntax4); return(metadataCommand); }
private void AddParameter(MamlParameter parameter, MamlCommand command) { var extraNewLine = ShouldBreak(parameter.FormatOption); AddHeader(ModelTransformerBase.PARAMETERSET_NAME_HEADING_LEVEL, '-' + parameter.Name, extraNewLine: extraNewLine); // for some reason, in the update mode parameters produces extra newline. AddParagraphs(parameter.Description, this._mode == ParserMode.FormattingPreserve); var sets = SimplifyParamSets(GetParamSetDictionary(parameter.Name, command.Syntax)); foreach (var set in sets) { _stringBuilder.AppendFormat("```yaml{0}", NewLine); AppendYamlKeyValue(MarkdownStrings.Type, parameter.Type); string parameterSetsString; if (command.Syntax.Count == 1 || set.Item1.Count == command.Syntax.Count) { // ignore, if there is just one parameter set // or this parameter belongs to All parameter sets, use (All) parameterSetsString = ModelTransformerVersion2.ALL_PARAM_SETS_MONIKER; } else { parameterSetsString = JoinWithComma(set.Item1); } AppendYamlKeyValue(MarkdownStrings.Parameter_Sets, parameterSetsString); AppendYamlKeyValue(MarkdownStrings.Aliases, JoinWithComma(parameter.Aliases)); if (parameter.ParameterValueGroup.Count > 0) { AppendYamlKeyValue(MarkdownStrings.Accepted_values, JoinWithComma(parameter.ParameterValueGroup)); } if (parameter.Applicable != null) { AppendYamlKeyValue(MarkdownStrings.Applicable, JoinWithComma(parameter.Applicable)); } _stringBuilder.AppendLine(); AppendYamlKeyValue(MarkdownStrings.Required, set.Item2.Required.ToString()); AppendYamlKeyValue(MarkdownStrings.Position, set.Item2.IsNamed() ? "Named" : set.Item2.Position); AppendYamlKeyValue(MarkdownStrings.Default_value, string.IsNullOrWhiteSpace(parameter.DefaultValue) ? "None" : parameter.DefaultValue); AppendYamlKeyValue(MarkdownStrings.Accept_pipeline_input, parameter.PipelineInput); AppendYamlKeyValue(MarkdownStrings.Accept_wildcard_characters, parameter.Globbing.ToString()); _stringBuilder.AppendFormat("```{0}{0}", NewLine); } }
public void RendererLineBreakAfterParameter() { var renderer = new MarkdownV2Renderer(ParserMode.Full); MamlCommand command = new MamlCommand() { Name = "Test-LineBreak", Synopsis = new SectionBody("This is the synopsis"), Description = new SectionBody("This is a long description"), Notes = new SectionBody("This is a note") }; var parameter1 = new MamlParameter() { Type = "String", Name = "Name", Required = true, Description = "Name description.", Globbing = true }; var parameter2 = new MamlParameter() { Type = "String", Name = "Path", FormatOption = SectionFormatOption.LineBreakAfterHeader, Required = true, Description = "Path description.", Globbing = true }; command.Parameters.Add(parameter1); command.Parameters.Add(parameter2); var syntax1 = new MamlSyntax() { ParameterSetName = "ByName" }; syntax1.Parameters.Add(parameter1); syntax1.Parameters.Add(parameter2); command.Syntax.Add(syntax1); string markdown = renderer.MamlModelToString(command, null); // Does not use line break and should not be added Assert.Contains("### -Name\r\nName description.", markdown); // Uses line break and should be preserved Assert.Contains("### -Path\r\n\r\nPath description.", markdown); }
private static string ConvertPSTypeToMamlType(MamlParameter parameter) { if (parameter.Type == null) { return(string.Empty); } if (parameter.IsSwitchParameter()) { return("SwitchParameter"); } return(parameter.Type); }
private static void FillUpParameterFromPSObject(MamlParameter parameter, PSObject parameterDetails) { // TODO: What about if null? // parameter.Type = (string)((PSObject)parameterDetails.Properties["type"].Value).Properties["name"].Value; parameter.Position = (string)parameterDetails.Properties["position"].Value; parameter.Required = ((string)parameterDetails.Properties["required"].Value).Equals("true"); string pipelineInput = (string)parameterDetails.Properties["pipelineInput"].Value; if (pipelineInput.StartsWith("t")) { // for some reason convention is: // false // True (ByValue) // True (ByPropertyName) pipelineInput = 'T' + pipelineInput.Substring(1); } parameter.PipelineInput = pipelineInput; // TODO: Still need to determine how to get these //parameter.VariableLength = ((string)parameterDetails.Properties["variableLength"].Value).Equals("true"); //parameter.ValueVariableLength = false; // it turns to work very well on all real-world examples, but in theory it can be orbitrary parameter.ValueRequired = parameter.Type == "switch" ? false : true; var parameterValueGroup = parameterDetails.Properties["parameterValueGroup"]; if (parameterValueGroup != null) { var validateSet = (parameterValueGroup.Value as PSObject).Properties["parameterValue"].Value as object[]; parameter.ParameterValueGroup.AddRange(validateSet.Select(x => x.ToString())); } // $h.Syntax.syntaxItem[0].parameter[0].parameterValueGroup.parameterValue // The 'aliases' property will contain either 'None' or a // comma-separated list of aliases. string aliasesString = ((string)parameterDetails.Properties["aliases"].Value); if (!string.Equals(aliasesString, "None")) { parameter.Aliases = aliasesString.Split( new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries); } }
private static YamlParameter CreateParameter(MamlParameter mamlParameter) { return(new YamlParameter { Name = mamlParameter.Name, AcceptWildcardCharacters = mamlParameter.Globbing, Description = mamlParameter.Description, Aliases = mamlParameter.Aliases.ToList(), Type = mamlParameter.Type, ParameterValueGroup = mamlParameter.ParameterValueGroup, Position = mamlParameter.Position, PipelineInput = mamlParameter.PipelineInput, DefaultValue = mamlParameter.DefaultValue }); }
public void RendererProduceSyntaxAndParameter() { MamlRenderer renderer = new MamlRenderer(); MamlCommand command = new MamlCommand() { Name = "Get-Foo", }; var param1 = new MamlParameter() { Type = "String", Name = "Param1", Position = "" }; var param2 = new MamlParameter() { Type = "System.Int32", Name = "Param2", Position = "Named" }; command.Parameters.Add(param1); command.Parameters.Add(param2); var syntax = new MamlSyntax(); syntax.Parameters.Add(param1); syntax.Parameters.Add(param2); command.Syntax.Add(syntax); string maml = renderer.MamlModelToString(new[] { command }); string[] syntaxItemName = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:syntax/command:syntaxItem/maml:name"); Assert.Single(syntaxItemName); Assert.Equal("Get-Foo", syntaxItemName[0]); string[] nameSyntax = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:syntax/command:syntaxItem/command:parameter/maml:name"); Assert.Equal(2, nameSyntax.Length); Assert.Equal("Param1", nameSyntax[0]); Assert.Equal("Param2", nameSyntax[1]); string[] nameParam = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:parameters/command:parameter/maml:name"); Assert.Equal(2, nameParam.Length); Assert.Equal("Param1", nameParam[0]); Assert.Equal("Param2", nameParam[1]); }
private void AddParameter(MamlParameter parameter, MamlCommand command) { AddHeader(ModelTransformerBase.PARAMETERSET_NAME_HEADING_LEVEL, '-' + parameter.Name, extraNewLine: false); // for some reason, in the update mode parameters produces extra newline. AddParagraphs(parameter.Description, /*noNewline*/ true); var sets = SimplifyParamSets(GetParamSetDictionary(parameter.Name, command.Syntax)); foreach (var set in sets) { _stringBuilder.AppendFormat("```yaml{0}", Environment.NewLine); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Type, parameter.Type, Environment.NewLine); string parameterSetsString; if (command.Syntax.Count == 1 || set.Item1.Count == command.Syntax.Count) { // ignore, if there is just one parameter set // or this parameter belongs to All parameter sets, use (All) parameterSetsString = ModelTransformerVersion2.ALL_PARAM_SETS_MONIKER; } else { parameterSetsString = JoinWithComma(set.Item1); } _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Parameter_Sets, parameterSetsString, Environment.NewLine); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Aliases, JoinWithComma(parameter.Aliases), Environment.NewLine); if (parameter.ParameterValueGroup.Count > 0) { _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Accepted_values, JoinWithComma(parameter.ParameterValueGroup), Environment.NewLine); } _stringBuilder.AppendLine(); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Required, set.Item2.Required, Environment.NewLine); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Position, set.Item2.IsNamed() ? "Named" : set.Item2.Position, Environment.NewLine); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Default_value, parameter.DefaultValue, Environment.NewLine); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Accept_pipeline_input, parameter.PipelineInput, Environment.NewLine); _stringBuilder.AppendFormat("{0}: {1}{2}", MarkdownStrings.Accept_wildcard_characters, parameter.Globbing, Environment.NewLine); _stringBuilder.AppendFormat("```{0}{0}", Environment.NewLine); } }
private MamlCommand GetModel2() { MamlCommand command = new MamlCommand() { Name = "Get-Foo", Synopsis = new SectionBody("This is the synopsis"), Description = new SectionBody("This is a long description.\r\nWith two paragraphs."), Notes = new SectionBody("This is a multiline note.\r\nSecond line.\r\nSecond Command") }; var parameterName = new MamlParameter() { Type = "String", Name = "Name2", Required = true, Description = "Parameter Description.\r\n", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "1", DefaultValue = "trololo", Aliases = new string[] { "GF", "Foos", "Do" }, }; command.Parameters.Add(parameterName); var syntax1 = new MamlSyntax() { IsDefault = true }; syntax1.Parameters.Add(parameterName); command.Syntax.Add(syntax1); return(command); }
public void RenderesAllParameterSetMoniker() { var renderer = new MarkdownV2Renderer(ParserMode.Full); MamlCommand command = new MamlCommand() { Name = "Get-Foo", }; var commonParam = new MamlParameter() { Type = "String", Name = "Common", Required = true }; var parameter1 = new MamlParameter() { Type = "String", Name = "First", Required = true }; var parameter2 = new MamlParameter() { Type = "String", Name = "Second", Required = true }; command.Parameters.Add(commonParam); command.Parameters.Add(parameter1); command.Parameters.Add(parameter2); var syntax1 = new MamlSyntax() { ParameterSetName = "FirstSyntax" }; syntax1.Parameters.Add(commonParam); syntax1.Parameters.Add(parameter1); var syntax2 = new MamlSyntax() { ParameterSetName = "SecondSyntax", IsDefault = true }; syntax2.Parameters.Add(commonParam); syntax2.Parameters.Add(parameter2); command.Syntax.Add(syntax1); command.Syntax.Add(syntax2); string markdown = renderer.MamlModelToString(command, null); Assert.Equal(@"--- schema: 2.0.0 --- # Get-Foo ## SYNOPSIS ## SYNTAX ### FirstSyntax ``` Get-Foo -Common <String> -First <String> [<CommonParameters>] ``` ### SecondSyntax (Default) ``` Get-Foo -Common <String> -Second <String> [<CommonParameters>] ``` ## DESCRIPTION ## EXAMPLES ## PARAMETERS ### -Common ```yaml Type: String Parameter Sets: (All) Aliases: Required: True Position: Named Default value: Accept pipeline input: false Accept wildcard characters: False ``` ### -First ```yaml Type: String Parameter Sets: FirstSyntax Aliases: Required: True Position: Named Default value: Accept pipeline input: false Accept wildcard characters: False ``` ### -Second ```yaml Type: String Parameter Sets: SecondSyntax Aliases: Required: True Position: Named Default value: Accept pipeline input: false Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ## OUTPUTS ## NOTES ## RELATED LINKS ", markdown); }
public void RendererProduceMarkdownV2Output() { var renderer = new MarkdownV2Renderer(ParserMode.Full); MamlCommand command = new MamlCommand() { Name = "Get-Foo", Synopsis = "This is the synopsis", Description = "This is a long description.\r\nWith two paragraphs. And the second one contains of few line! They should be auto-wrapped. Because, why not? We can do that kind of the things, no problem.\r\n\r\n-- Foo. Bar.\r\n-- Don't break. The list.\r\n-- Into. Pieces", Notes = "This is a multiline note.\r\nSecond line." }; var parameter = new MamlParameter() { Type = "String", Name = "Name", Required = true, Description = "Parameter Description.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "1", DefaultValue = "trololo", Aliases = new string[] { "GF", "Foos", "Do" }, }; parameter.ParameterValueGroup.AddRange(new string[] { "Value1", "Value2" }); command.Parameters.Add(parameter); var syntax1 = new MamlSyntax() { ParameterSetName = "ByName" }; syntax1.Parameters.Add(parameter); command.Syntax.Add(syntax1); command.Inputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Input <Description> goes here!" } ); command.Outputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Output Description goes here!" } ); command.Examples.Add(new MamlExample() { Title = "Example 1", Code = "PS:> Get-Help -YouNeedIt", Remarks = "This does stuff!" } ); command.Links.Add(new MamlLink() { LinkName = "PowerShell made by Microsoft Hackathon", LinkUri = "www.microsoft.com" }); command.Links.Add(new MamlLink() { LinkName = "", // if name is empty, it would be populated with uri LinkUri = "http://foo.com" }); // Note that the metadata should end up getting alphabetized. var metadata = new Hashtable(); metadata["foo"] = "bar"; metadata["null"] = null; string markdown = renderer.MamlModelToString(command, metadata); Assert.Equal(@"--- foo: bar null: schema: 2.0.0 --- # Get-Foo ## SYNOPSIS This is the synopsis ## SYNTAX ``` Get-Foo [-Name] <String> [<CommonParameters>] ``` ## DESCRIPTION This is a long description. With two paragraphs. And the second one contains of few line! They should be auto-wrapped. Because, why not? We can do that kind of the things, no problem. -- Foo. Bar. -- Don't break. The list. -- Into. Pieces ## EXAMPLES ### Example 1 ``` PS:> Get-Help -YouNeedIt ``` This does stuff! ## PARAMETERS ### -Name Parameter Description. ```yaml Type: String Parameter Sets: (All) Aliases: GF, Foos, Do Accepted values: Value1, Value2 Required: True Position: 1 Default value: trololo Accept pipeline input: True (ByValue) Accept wildcard characters: True ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### String Input \<Description\> goes here! ## OUTPUTS ### String Output Description goes here! ## NOTES This is a multiline note. Second line. ## RELATED LINKS [PowerShell made by Microsoft Hackathon](www.microsoft.com) [http://foo.com](http://foo.com) ", markdown); }
private MamlCommand GetOriginal() { MamlCommand originalCommand = new MamlCommand() { Name = "Get-Foo", Synopsis = new SectionBody("This is the synopsis"), Description = new SectionBody("This is a long description.\r\nWith two paragraphs."), Notes = new SectionBody("This is a multiline note.\r\nSecond line.") }; var parameterName = new MamlParameter() { Type = "String", Name = "Name", Required = true, Description = "Parameter Description.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "1", DefaultValue = "trololo", Aliases = new string[] { "GF", "Foos", "Do" }, }; var removedParameterName = new MamlParameter() { Type = "int", Name = "Remove", Required = true, Description = "Parameter Description 2.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "2", DefaultValue = "dodododo", Aliases = new string[] { "Pa1", "RemovedParam", "Gone" }, }; var parameterPath = new MamlParameter() { Type = "String", Name = "Path", Required = true, Description = "Parameter path description.", FormatOption = SectionFormatOption.LineBreakAfterHeader, Globbing = true }; originalCommand.Parameters.Add(parameterName); originalCommand.Parameters.Add(removedParameterName); originalCommand.Parameters.Add(parameterPath); var syntax1 = new MamlSyntax() { ParameterSetName = "ByName", }; syntax1.Parameters.Add(parameterName); syntax1.Parameters.Add(removedParameterName); syntax1.Parameters.Add(parameterPath); originalCommand.Syntax.Add(syntax1); var syntax2 = new MamlSyntax() { ParameterSetName = "SetOldName", IsDefault = true }; syntax2.Parameters.Add(parameterName); syntax2.Parameters.Add(parameterPath); originalCommand.Syntax.Add(syntax2); originalCommand.Inputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Input <Description> goes here!" } ); originalCommand.Outputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Output Description goes here!" } ); originalCommand.Examples.Add(new MamlExample() { Title = "Example 1", Code = new[] { new MamlCodeBlock("PS:> Get-Help -YouNeedIt") }, Remarks = "This does stuff!" } ); originalCommand.Links.Add(new MamlLink() { LinkName = "PowerShell made by Microsoft Hackathon", LinkUri = "www.microsoft.com" }); originalCommand.Links.Add(new MamlLink() { LinkName = "", // if name is empty, it would be populated with uri LinkUri = "http://foo.com" }); return(originalCommand); }
private static MamlCommand CreateModel() { var command = new MamlCommand { Name = "Test-Unit", Description = "A test cmdlet", Synopsis = "A cmdlet to test", Notes = "This is just a test", ModuleName = "TestModule" }; command.Examples.Add(new MamlExample { Remarks = "Example 1 remarks", Code = "Example 1 code", Introduction = "Example 1 intro", Title = "Example 1 title" }); command.Inputs.Add(new MamlInputOutput { Description = "Input 1", TypeName = "System.String" }); command.Links.Add(new MamlLink { LinkUri = "https://docs.microsoft.com", LinkName = "Docs" }); var mamlOptionalParameter = new MamlParameter { Required = false, Name = "-TestParam", Globbing = true, Aliases = new [] { "-Alias1" }, DefaultValue = "Test", Description = "Test Parameter", PipelineInput = "false", Position = "1", Type = "System.String" }; mamlOptionalParameter.ParameterValueGroup.Add("TestGroup"); var mamlRequiredParameter = new MamlParameter { Required = true, Name = "-TestParam2", Globbing = false, Aliases = new[] { "-Alias2" }, DefaultValue = "Test2", Description = "Test Parameter 2", PipelineInput = "true", Position = "2", Type = "System.Boolean" }; mamlRequiredParameter.ParameterValueGroup.Add("TestGroup2"); command.Parameters.AddRange(new [] { mamlRequiredParameter, mamlOptionalParameter }); command.Outputs.Add(new MamlInputOutput { Description = "Output 1", TypeName = "System.String" }); var mamlSyntax = new MamlSyntax { ParameterSetName = "TestGroup", IsDefault = true }; mamlSyntax.Parameters.Add(mamlOptionalParameter); command.Syntax.Add(mamlSyntax); return(command); }
private void GatherParameterDetails(MamlCommand command) { const string parameterFormatString = @" {0} ${1}"; const string docFunctionFormatString = @" function Get-AttributeDocFunction {{ param( {0} ) }} $h = Get-Help Get-AttributeDocFunction $h.parameters.parameter "; // Create the Runspace on demand if (this.runspace == null) { this.runspace = RunspaceFactory.CreateRunspace(); this.runspace.Open(); } using (PowerShell powerShell = PowerShell.Create()) { var parameterBlocks = command .Parameters .Select(p => string.Format(parameterFormatString, p.AttributesText, ShiftName(p.Name))); var functionScript = string.Format( docFunctionFormatString, string.Join(",\r\n", parameterBlocks)); // TODO: There could be some security concerns with executing arbitrary // text here, need to investigate safer ways to do it. JEA? powerShell.Runspace = this.runspace; powerShell.AddScript(functionScript); var parameterDetailses = powerShell.Invoke <PSObject>(); if (powerShell.Streams.Error.Any()) { throw new HelpSchemaException(command.Extent, "Errors when processing command " + command.Name + ":\n" + string.Join(";\n", powerShell.Streams.Error)); } foreach (PSObject parameterDetailsPsObject in parameterDetailses) { var parameter = command.Parameters.FirstOrDefault( p => string.Equals(p.Name, UndoShiftName((string)parameterDetailsPsObject.Properties["name"].Value))); FillUpParameterFromPSObject(parameter, parameterDetailsPsObject); } powerShell.Commands.Clear(); powerShell.Commands.AddScript("$h.Syntax.syntaxItem"); var syntaxDetailses = powerShell.Invoke <PSObject>(); if (powerShell.Streams.Error.Any()) { throw new HelpSchemaException(command.Extent, "Errors when processing command " + command.Name + ":\n" + string.Join(";\n", powerShell.Streams.Error)); } var sortedSyntaxItems = syntaxDetailses.ToList(); sortedSyntaxItems.Sort((si1, si2) => String.CompareOrdinal(GetParameterSetNameFromSyntaxItem(si1), GetParameterSetNameFromSyntaxItem(si2))); foreach (var syntaxDetails in sortedSyntaxItems) { MamlSyntax syntax = new MamlSyntax(); var syntaxParams = (object[])syntaxDetails.Properties["parameter"].Value; foreach (PSObject syntaxParamPsObject in syntaxParams.OfType <PSObject>()) { string paramName = UndoShiftName((string)syntaxParamPsObject.Properties["name"].Value); MamlParameter parametersParameter = command.Parameters.FirstOrDefault(p => string.Equals(p.Name, paramName)); if (parametersParameter == null) { throw new HelpSchemaException(command.Extent, "Cannot find corresponding parameter for syntax item " + paramName); } MamlParameter syntaxParameter = new MamlParameter() { Name = parametersParameter.Name, Type = parametersParameter.Type }; FillUpParameterFromPSObject(syntaxParameter, syntaxParamPsObject); syntax.Parameters.Add(syntaxParameter); } command.Syntax.Add(syntax); } } }
private void AddParameter(MamlCommand command, MamlParameter parameter, bool inSyntax) { var attributes = "required=\"" + parameter.Required.ToString().ToLower() + "\" " + "variableLength=\"" + parameter.VariableLength.ToString().ToLower() + "\" " + "globbing=\"" + parameter.Globbing.ToString().ToLower() + "\" " + "pipelineInput=\"" + parameter.PipelineInput + "\" " + "position=\"" + parameter.Position.ToLower() + "\" " + "aliases=\""; attributes += parameter.Aliases.Length > 0 ? string.Join(", ", parameter.Aliases) : "none"; attributes += "\""; PushTag("command:parameter", attributes); PushTag("maml:name"); _stringBuilder.Append(XmlEscape(parameter.Name)); PopTag("maml:name"); PushTag("maml:Description"); AddParas(parameter.Description); PopTag("maml:Description"); if (inSyntax && parameter.ParameterValueGroup.Count > 0) { AddParameterValueGroup(parameter.ParameterValueGroup); } attributes = "required=\"" + parameter.ValueRequired.ToString().ToLower() + "\" " + "variableLength=\"" + parameter.ValueVariableLength.ToString().ToLower(); attributes += "\""; string mamlType = ConvertPSTypeToMamlType(parameter); // this is weired quirk inside <syntax>: // we don't add [switch] info to make it appear good. if (!inSyntax || mamlType != "SwitchParameter") { PushTag("command:parameterValue", attributes); _stringBuilder.Append(XmlEscape(mamlType)); PopTag("command:parameterValue"); } PushTag("dev:type"); PushTag("maml:name"); _stringBuilder.Append(XmlEscape(mamlType)); PopTag("maml:name"); _stringBuilder.Append("<maml:uri />"); PopTag("dev:type"); // Region defaultValue PushTag("dev:defaultValue"); if (mamlType == "SwitchParameter" && (string.IsNullOrEmpty(parameter.DefaultValue) || (parameter.DefaultValue == "None"))) { _stringBuilder.Append("False"); } else { if (parameter.DefaultValue != null) { _stringBuilder.Append(XmlEscape(parameter.DefaultValue)); } else { // sometimes default is none, but empty makes more sense. _stringBuilder.Append("None"); } } PopTag("dev:defaultValue"); // closing tag PopTag("command:parameter"); }
private void FillUpParameterFromKeyValuePairs(Dictionary <string, string> pairs, MamlParameter parameter) { // for all null keys, we should ignore the value in this context var newPairs = new Dictionary <string, string>(pairs.Comparer); foreach (var pair in pairs) { if (pair.Value != null) { newPairs[pair.Key] = pair.Value; } } pairs = newPairs; string value; parameter.Type = pairs.TryGetValue(MarkdownStrings.Type, out value) ? value : null; parameter.Aliases = pairs.TryGetValue(MarkdownStrings.Aliases, out value) ? SplitByCommaAndTrim(value) : new string [0]; parameter.ParameterValueGroup.AddRange(pairs.TryGetValue(MarkdownStrings.Accepted_values, out value) ? SplitByCommaAndTrim(value) : new string[0]); parameter.Required = pairs.TryGetValue(MarkdownStrings.Required, out value) ? StringComparer.OrdinalIgnoreCase.Equals("true", value) : false; parameter.Position = pairs.TryGetValue(MarkdownStrings.Position, out value) ? value : "named"; parameter.DefaultValue = pairs.TryGetValue(MarkdownStrings.Default_value, out value) ? value : null; parameter.PipelineInput = pairs.TryGetValue(MarkdownStrings.Accept_pipeline_input, out value) ? value : "false"; parameter.Globbing = pairs.TryGetValue(MarkdownStrings.Accept_wildcard_characters, out value) ? StringComparer.OrdinalIgnoreCase.Equals("true", value) : false; // having Applicable for the whole parameter is a little bit sloppy: ideally it should be per yaml entry. // but that will make the code super ugly and it's unlikely that these two features would need to be used together. parameter.Applicable = pairs.TryGetValue(MarkdownStrings.Applicable, out value) ? SplitByCommaAndTrim(value) : null; }
/// <summary> /// /// </summary> /// <param name="command"></param> /// <returns>true if Parameter was found</returns> private bool ParameterRule(MamlCommand command) { // grammar: // #### Name [TypeName] - mandatory // ```powershell - optional // [Parameter(...)] // ``` // Description - optional var node = GetNextNode(); var headingNode = GetHeadingWithExpectedLevel(node, PARAMETER_NAME_HEADING_LEVEL); if (headingNode == null) { return(false); } var name = headingNode.Text.Split()[0]; MamlParameter parameter = new MamlParameter() { Name = name, Extent = headingNode.SourceExtent }; int equalIndex = headingNode.Text.IndexOf('='); string headingNodeText; if (equalIndex >= 0) { parameter.DefaultValue = headingNode.Text.Substring(equalIndex + 1).Trim(); // trim it for this case from PSReadLine: // #### WordDelimiters [Int32] = ;:,.[]{}()/\|^&*-=+ // We need to make sure that closing ] corresponds to [Int32], so it's the last ] before first = sign. headingNodeText = headingNode.Text.Substring(0, equalIndex); } else { headingNodeText = headingNode.Text; } int typeBeginIndex = headingNodeText.IndexOf('['); int typeEndIndex = headingNodeText.LastIndexOf(']'); if (typeBeginIndex >= 0 && typeEndIndex > 0) { parameter.Type = headingNodeText.Substring(typeBeginIndex + 1, typeEndIndex - typeBeginIndex - 1); } node = GetNextNode(); ParagraphNode descriptionNode = null; CodeBlockNode attributesNode = null; // it can be the end if (node != null) { switch (node.NodeType) { case MarkdownNodeType.Unknown: break; case MarkdownNodeType.Document: break; case MarkdownNodeType.Paragraph: descriptionNode = node as ParagraphNode; break; case MarkdownNodeType.Heading: // next parameter started UngetNode(node); break; case MarkdownNodeType.CodeBlock: attributesNode = node as CodeBlockNode; break; default: throw new ArgumentOutOfRangeException(); } if (descriptionNode == null) { descriptionNode = ParagraphNodeRule(); } } parameter.Description = GetTextFromParagraphNode(descriptionNode); parameter.AttributesText = attributesNode != null ? attributesNode.Text : string.Empty; if (parameter.AttributesText.Contains(@"[SupportsWildCards()]")) { parameter.Globbing = true; } command.Parameters.Add(parameter); return(true); }
private void GatherParameterDetails(MamlCommand command) { const string parameterFormatString = @" {0} ${1}"; const string docFunctionFormatString = @" function Get-AttributeDocFunction {{ param( {0} ) }} # $h = Get-Help Get-AttributeDocFunction {1} $h.parameters.parameter "; // this is a workaround for https://github.com/PowerShell/platyPS/issues/27 const string getHelpString = @" $isAdmin = (New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) $prev = Get-ItemProperty -Name DisablePromptToUpdateHelp -path 'HKLM:\SOFTWARE\Microsoft\PowerShell' -ErrorAction SilentlyContinue try { if ($isAdmin) { Set-ItemProperty -Name DisablePromptToUpdateHelp -Value 1 -path 'HKLM:\SOFTWARE\Microsoft\PowerShell' } # this is an importent line that populates object $h = Get-Help Get-AttributeDocFunction } finally { if ($isAdmin) { if ($prev) { if ($prev.DisablePromptToUpdateHelp -ne 1) { Set-ItemProperty -Name DisablePromptToUpdateHelp -Value ($prev.DisablePromptToUpdateHelp) -path 'HKLM:\SOFTWARE\Microsoft\PowerShell' } } else { Remove-ItemProperty -Name DisablePromptToUpdateHelp -path 'HKLM:\SOFTWARE\Microsoft\PowerShell' } } } "; // Create the Runspace on demand if (this.runspace == null) { this.runspace = RunspaceFactory.CreateRunspace(); this.runspace.Open(); } using (PowerShell powerShell = PowerShell.Create()) { var parameterBlocks = command .Parameters .Select(p => string.Format(parameterFormatString, p.AttributesMetadata, ShiftName(p.Name))); var functionScript = string.Format( docFunctionFormatString, string.Join(",\r\n", parameterBlocks), getHelpString); // TODO: There could be some security concerns with executing arbitrary // text here, need to investigate safer ways to do it. JEA? powerShell.Runspace = this.runspace; powerShell.AddScript(functionScript); var parameterDetailses = powerShell.Invoke <PSObject>(); if (powerShell.Streams.Error.Any()) { throw new HelpSchemaException(command.Extent, "Errors when processing command " + command.Name + ":\n" + string.Join(";\n", powerShell.Streams.Error)); } foreach (PSObject parameterDetailsPsObject in parameterDetailses) { var parameter = command.Parameters.FirstOrDefault( p => string.Equals(p.Name, UndoShiftName((string)parameterDetailsPsObject.Properties["name"].Value))); FillUpParameterFromPSObject(parameter, parameterDetailsPsObject); } powerShell.Commands.Clear(); powerShell.Commands.AddScript("$h.Syntax.syntaxItem"); var syntaxDetailses = powerShell.Invoke <PSObject>(); if (powerShell.Streams.Error.Any()) { throw new HelpSchemaException(command.Extent, "Errors when processing command " + command.Name + ":\n" + string.Join(";\n", powerShell.Streams.Error)); } var sortedSyntaxItems = syntaxDetailses.ToList(); sortedSyntaxItems.Sort((si1, si2) => String.CompareOrdinal(GetParameterSetNameFromSyntaxItem(si1), GetParameterSetNameFromSyntaxItem(si2))); int count = 1; foreach (var syntaxDetails in sortedSyntaxItems) { MamlSyntax syntax = new MamlSyntax() { // HACK: we need a better solution ParameterSetName = "Set " + (count++) }; var syntaxParams = (object[])syntaxDetails.Properties["parameter"].Value; foreach (PSObject syntaxParamPsObject in syntaxParams.OfType <PSObject>()) { string paramName = UndoShiftName((string)syntaxParamPsObject.Properties["name"].Value); MamlParameter parametersParameter = command.Parameters.FirstOrDefault(p => string.Equals(p.Name, paramName)); if (parametersParameter == null) { throw new HelpSchemaException(command.Extent, "Cannot find corresponding parameter for syntax item " + paramName); } MamlParameter syntaxParameter = new MamlParameter() { Name = parametersParameter.Name, Type = parametersParameter.Type }; FillUpParameterFromPSObject(syntaxParameter, syntaxParamPsObject); syntax.Parameters.Add(syntaxParameter); } command.Syntax.Add(syntax); } } }
private void MergeParameters(MamlCommand result, Dictionary <string, MamlCommand> applicableTag2Model) { // First handle syntax. // To avoid dealing with complicated applicableTag + ParameterSetName pairs. // They already serve very similar purpose. // // We simply merge syntaxes of the same name. That way all possible parameter names from all applicable tags // apearing in the syntax. This is not ideal, but alternative would be introducing "applicable" for syntax. // That would make it even more complicated. var tagsModel = applicableTag2Model.ToList(); var syntaxes = tagsModel.SelectMany(pair => pair.Value.Syntax); var syntaxNames = syntaxes.Select(syntax => syntax.ParameterSetName).Distinct(StringComparer.OrdinalIgnoreCase); var defaultSyntaxNames = syntaxes.Where(syntax => syntax.IsDefault).Select(syntax => syntax.ParameterSetName).Distinct(StringComparer.OrdinalIgnoreCase); if (defaultSyntaxNames.Count() > 1) { // reporting warning and continue Report("Found conflicting default ParameterSets ({0}) in applicableTags ({1})", string.Join(", ", defaultSyntaxNames), string.Join(", ", applicableTag2Model.Keys)); } string defaultSyntaxName = defaultSyntaxNames.FirstOrDefault(); foreach (string syntaxName in syntaxNames) { var newSyntax = new MamlSyntax() { ParameterSetName = syntaxName, IsDefault = StringComparer.OrdinalIgnoreCase.Equals(defaultSyntaxName, syntaxName), }; var paramSetsToMerge = syntaxes.Where(syntax => StringComparer.OrdinalIgnoreCase.Equals(syntax.ParameterSetName, syntaxName)); newSyntax.Parameters.AddRange(MergeParameterList(paramSetsToMerge.Select(syntax => syntax.Parameters))); result.Syntax.Add(newSyntax); } // Then merge individual parameters var allNames = tagsModel.SelectMany(pair => pair.Value.Parameters).Select(p => p.Name).Distinct().ToList(); foreach (string name in allNames) { Dictionary <string, MamlParameter> paramsToMerge = new Dictionary <string, MamlParameter>(); foreach (var pair in tagsModel) { MamlParameter candidate = FindParameterByName(name, pair.Value.Parameters); if (candidate != null) { paramsToMerge[pair.Key] = candidate; } } string newDescription = MergeText(paramsToMerge.ToDictionary(pair => pair.Key, pair => pair.Value.Description)); var newParameter = paramsToMerge.First().Value.Clone(); newParameter.Description = newDescription; if (paramsToMerge.Count != applicableTag2Model.Count || !this._ignoreTagsIfAllApplicable) { // we should not update applicable tags, if it's applicable to everything and not explicitly specified newParameter.Applicable = paramsToMerge.Select(p => p.Key).ToArray(); } result.Parameters.Add(newParameter); } }
private void FillUpParameterFromKeyValuePairs(Dictionary <string, string> pairs, MamlParameter parameter) { // for all null keys, we should ignore the value in this context var newPairs = new Dictionary <string, string>(pairs.Comparer); foreach (var pair in pairs) { if (pair.Value != null) { newPairs[pair.Key] = pair.Value; } } pairs = newPairs; string value; parameter.Type = pairs.TryGetValue(MarkdownStrings.Type, out value) ? value : null; parameter.Aliases = pairs.TryGetValue(MarkdownStrings.Aliases, out value) ? SplitByCommaAndTrim(value) : new string [0]; parameter.ParameterValueGroup.AddRange(pairs.TryGetValue(MarkdownStrings.Accepted_values, out value) ? SplitByCommaAndTrim(value) : new string[0]); parameter.Required = pairs.TryGetValue(MarkdownStrings.Required, out value) ? StringComparer.OrdinalIgnoreCase.Equals("true", value) : false; parameter.Position = pairs.TryGetValue(MarkdownStrings.Position, out value) ? value : "named"; parameter.DefaultValue = pairs.TryGetValue(MarkdownStrings.Default_value, out value) ? value : null; parameter.PipelineInput = pairs.TryGetValue(MarkdownStrings.Accept_pipeline_input, out value) ? value : "false"; parameter.Globbing = pairs.TryGetValue(MarkdownStrings.Accept_wildcard_characters, out value) ? StringComparer.OrdinalIgnoreCase.Equals("true", value) : false; }
private bool ParameterRule(MamlCommand commmand) { // grammar: // #### -Name // Description - optional, there also could be codesnippets in the description // but no yaml codesnippets // // ```yaml - one entry for every unique parameter metadata set // ... // ``` var node = GetNextNode(); var headingNode = GetHeadingWithExpectedLevel(node, PARAMETER_NAME_HEADING_LEVEL); if (headingNode == null) { return(false); } var name = headingNode.Text; if (name.Length > 0 && name[0] == '-') { name = name.Substring(1); } MamlParameter parameter = new MamlParameter() { Name = name, Extent = headingNode.SourceExtent }; parameter.Description = ParagraphOrCodeBlockNodeRule("yaml"); parameter.FormatOption = headingNode.FormatOption; if (StringComparer.OrdinalIgnoreCase.Equals(parameter.Name, MarkdownStrings.CommonParametersToken)) { // ignore text body commmand.SupportCommonParameters = true; return(true); } if (StringComparer.OrdinalIgnoreCase.Equals(parameter.Name, MarkdownStrings.WorkflowParametersToken)) { // ignore text body commmand.IsWorkflow = true; return(true); } // we are filling up two pieces here: Syntax and Parameters // we are adding this parameter object to the parameters and later modifying it // in the rare case, when there are multiply yaml snippets, // the first one should be present in the resulted maml in the Parameters section // (all of them would be present in Syntax entry) var parameterSetMap = new Dictionary <string, MamlParameter>(StringComparer.OrdinalIgnoreCase); CodeBlockNode codeBlock; // fill up couple other things, even if there are no codeBlocks // if there are, we will fill it up inside parameter.ValueRequired = true; // First parameter is what should be used in the Parameters section MamlParameter firstParameter = null; bool isAtLeastOneYaml = false; while ((codeBlock = CodeBlockRule()) != null) { isAtLeastOneYaml = true; var yaml = ParseYamlKeyValuePairs(codeBlock); FillUpParameterFromKeyValuePairs(yaml, parameter); parameter.ValueRequired = parameter.IsSwitchParameter() ? false : true; // handle applicable tag if (parameter.IsApplicable(this._applicableTag)) { if (firstParameter == null) { firstParameter = parameter; } // handle parameter sets if (yaml.ContainsKey(MarkdownStrings.Parameter_Sets)) { foreach (string parameterSetName in SplitByCommaAndTrim(yaml[MarkdownStrings.Parameter_Sets])) { if (string.IsNullOrEmpty(parameterSetName)) { continue; } parameterSetMap[parameterSetName] = parameter; } } else { parameterSetMap[ALL_PARAM_SETS_MONIKER] = parameter; } } // in the rare case, when there are multiply yaml snippets parameter = parameter.Clone(); } if (!isAtLeastOneYaml) { // if no yaml are present it's a special case and we leave it as is firstParameter = parameter; } // capture these two piece of information if (firstParameter != null) { commmand.Parameters.Add(firstParameter); _parameterName2ParameterSetMap.Add(Tuple.Create(name, parameterSetMap)); } return(true); }
private MamlCommand GetModel3() { MamlCommand command = new MamlCommand() { Name = "Get-Foo", Synopsis = "This is the synopsis 3", Description = "This is a long description.\r\nWith two paragraphs.", Notes = "This is a multiline note.\r\nSecond line.\r\nThird Command" }; command.Links.Add(new MamlLink(true) { LinkName = "[bar]()", }); command.Examples.Add(new MamlExample() { Title = "Example 1", Code = "PS:> Get-Help -YouNeedIt", Remarks = "This does stuff too!" } ); command.Inputs.Add(new MamlInputOutput() { TypeName = "String", Description = "Foo 2" } ); command.Outputs.Add(new MamlInputOutput() { TypeName = "String", Description = null } ); var parameterName = new MamlParameter() { Type = "String", Name = "Name", Required = true, Description = "Parameter Description.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "1", DefaultValue = "trololo", Aliases = new string[] { "GF", "Foos", "Do" }, }; var parameterRemoved = new MamlParameter() { Type = "int", Name = "Remove", Required = true, Description = "Parameter Description 2.", VariableLength = true, Globbing = true, PipelineInput = "True (ByValue)", Position = "2", DefaultValue = "dodododo", Aliases = new string[] { "Pa1", "RemovedParam", "Gone" }, }; command.Parameters.Add(parameterName); command.Parameters.Add(parameterRemoved); var syntax1 = new MamlSyntax() { ParameterSetName = "ByName", }; syntax1.Parameters.Add(parameterName); syntax1.Parameters.Add(parameterRemoved); command.Syntax.Add(syntax1); var syntax2 = new MamlSyntax() { ParameterSetName = "BySomethingElse", IsDefault = true }; syntax2.Parameters.Add(parameterName); command.Syntax.Add(syntax2); return(command); }