From 5ab7ac73ecaede16b2ab114a2eb8a8ed24d8dc68 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 20 Jul 2022 17:54:09 -0400 Subject: [PATCH 1/9] fix bug with duplicate keys for certain HelpInfo properties throwing error --- src/code/PSScriptFileInfo.cs | 4 +- src/code/PSScriptHelp.cs | 216 +++++++++++++++++++++++++++++++---- src/code/PSScriptMetadata.cs | 92 ++++++++++++++- src/code/Utils.cs | 68 ----------- 4 files changed, 287 insertions(+), 93 deletions(-) diff --git a/src/code/PSScriptFileInfo.cs b/src/code/PSScriptFileInfo.cs index f3c4f3b5b..495e906f8 100644 --- a/src/code/PSScriptFileInfo.cs +++ b/src/code/PSScriptFileInfo.cs @@ -257,9 +257,9 @@ internal static bool TryPopulateScriptClassesWithParsedContent( currentHelpInfo = new PSScriptHelp(); if (!currentHelpInfo.ParseContentIntoObj( commentLines: helpInfoCommentContent.ToArray(), - out ErrorRecord helpError)) + out ErrorRecord[] helpErrors)) { - errorsList.Add(helpError); + errorsList.AddRange(helpErrors); parsedContentSuccessfully = false; } diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index 9964d2159..2791a2c82 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -25,6 +25,11 @@ public sealed class PSScriptHelp /// public string Synopsis { get; private set; } + /// + /// The parameter(s) for the script. + /// + public string[] Parameter { get; private set; } + /// /// The example(s) relating to the script's usage. /// @@ -74,7 +79,7 @@ public sealed class PSScriptHelp /// public PSScriptHelp (string description) { - this.Description = description; + Description = description; } /// @@ -84,6 +89,7 @@ public PSScriptHelp (string description) public PSScriptHelp ( string description, string synopsis, + string[] parameter, string[] example, string[] inputs, string[] outputs, @@ -93,16 +99,17 @@ public PSScriptHelp ( string[] role, string[] functionality) { - this.Description = description; - this.Synopsis = synopsis; - this.Example = example; - this.Inputs = inputs; - this.Outputs = outputs; - this.Notes = notes; - this.Links = links; - this.Component = component; - this.Role = role; - this.Functionality = functionality; + Description = description; + Synopsis = synopsis; + Parameter = parameter; + Example = example; + Inputs = inputs; + Outputs = outputs; + Notes = notes; + Links = links; + Component = component; + Role = role; + Functionality = functionality; } /// @@ -119,35 +126,200 @@ internal PSScriptHelp() {} /// Parses HelpInfo metadata out of the HelpInfo comment lines found while reading the file /// and populates PSScriptHelp properties from that metadata. /// - internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord error) + internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] errors) { bool successfullyParsed = true; string[] spaceDelimeter = new string[]{" "}; string[] newlineDelimeter = new string[]{Environment.NewLine}; // parse content into a hashtable - Hashtable parsedHelpMetadata = Utils.ParseCommentBlockContent(commentLines); + Hashtable parsedHelpMetadata = ParseHelpContentHelper(commentLines, out errors); + if (errors.Length != 0) + { + return false; + } - if (!ValidateParsedContent(parsedHelpMetadata, out error)) + if (!ValidateParsedContent(parsedHelpMetadata, out ErrorRecord validationError)) { + errors = new ErrorRecord[]{validationError}; return false; } // populate object Description = (string) parsedHelpMetadata["DESCRIPTION"]; Synopsis = (string) parsedHelpMetadata["SYNOPSIS"] ?? String.Empty; - Example = Utils.GetStringArrayFromString(newlineDelimeter, (string) parsedHelpMetadata["EXAMPLE"]); - Inputs = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["INPUT"]); - Outputs = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["OUTPUTS"]); - Notes = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["NOTES"]); - Links = Utils.GetStringArrayFromString(newlineDelimeter, (string) parsedHelpMetadata["LINKS"]); - Component = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["COMPONENT"]); - Role = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["ROLE"]); - Functionality = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["FUNCTIONALITY"]); + + List parameterList = (List)parsedHelpMetadata["PARAMETER"]; + Parameter = parameterList.ToArray(); + + List exampleList = (List)parsedHelpMetadata["EXAMPLE"]; + Example = exampleList.ToArray(); + + List inputList = parsedHelpMetadata.ContainsKey("INPUT") ? (List)parsedHelpMetadata["INPUT"] : new List(); + Inputs = inputList.ToArray(); + + List outputList = parsedHelpMetadata.ContainsKey("OUTPUT") ? (List)parsedHelpMetadata["OUTPUT"] : new List(); + Outputs = outputList.ToArray(); + + List notesList = parsedHelpMetadata.ContainsKey("NOTES") ? (List)parsedHelpMetadata["NOTES"] : new List(); + Notes = notesList.ToArray(); + + List linksList = parsedHelpMetadata.ContainsKey("LINKS") ? (List)parsedHelpMetadata["LINKS"] : new List(); + Links = linksList.ToArray(); + + List componentList = parsedHelpMetadata.ContainsKey("COMPONENT") ? (List)parsedHelpMetadata["COMPONENT"] : new List(); + Component = componentList.ToArray(); + + List roleList = parsedHelpMetadata.ContainsKey("ROLE") ? (List)parsedHelpMetadata["ROLE"] : new List(); + Role = roleList.ToArray(); + + List functionalityList = parsedHelpMetadata.ContainsKey("FUNCTIONALITY") ? (List)parsedHelpMetadata["FUNCTIONALITY"] : new List(); + Functionality = functionalityList.ToArray(); return successfullyParsed; } + /// + /// Parses metadata out of PSScriptCommentInfo comment block's lines (which are passed in) into a hashtable. + /// This comment block cannot have duplicate keys. + /// + public static Hashtable ParseHelpContentHelper(string[] commentLines, out ErrorRecord[] errors) + { + /** + Comment lines can look like this: + + .KEY1 value + + .KEY2 value + + .KEY2 value2 + + .KEY3 + value + + .KEY4 value + value continued + + */ + + errors = Array.Empty(); + List errorsList = new List(); + + Hashtable parsedHelpMetadata = new Hashtable(); + char[] spaceDelimeter = new char[]{' '}; + string keyName = ""; + string value = ""; + + bool keyNeedsToBeAdded = false; + + for (int i = 0; i < commentLines.Length; i++) + { + string line = commentLines[i]; + + // scenario where line is: .KEY VALUE + // this line contains a new metadata property. + if (line.Trim().StartsWith(".")) + { + // check if keyName was previously populated, if so add this key value pair to the metadata hashtable + if (!String.IsNullOrEmpty(keyName)) + { + keyNeedsToBeAdded = false; // we'l end up adding the key,value to hashtable in this code flow + if (parsedHelpMetadata.ContainsKey(keyName)) + { + if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) + { + var message = String.Format("PowerShell script 'HelpInfo' comment block metadata cannot contain duplicate keys (i.e .KEY) for Description or Synopsis"); + var ex = new InvalidOperationException(message); + var psHelpInfoDuplicateKeyError = new ErrorRecord(ex, "psHelpInfoDuplicateKeyError", ErrorCategory.ParserError, null); + errorsList.Add(psHelpInfoDuplicateKeyError); + continue; + } + + List currentValues = (List) parsedHelpMetadata[keyName]; + currentValues.Add(value); + parsedHelpMetadata[keyName] = currentValues; + } + else + { + // adding key for first time + if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) + { + parsedHelpMetadata.Add(keyName, value); + } + else + { + // the other keys will have values of type string[] + List valueList = new List(); + valueList.Add(value); + parsedHelpMetadata.Add(keyName, valueList); + } + } + } + + // setting count to 2 will get 1st separated string (key) into part[0] and the rest (value) into part[1] if any + string[] parts = line.Trim().TrimStart('.').Split(separator: spaceDelimeter, count: 2); + keyName = parts[0]; + value = parts.Length == 2 ? parts[1] : String.Empty; + keyNeedsToBeAdded = true; + } + else if (!String.IsNullOrEmpty(line)) + { + // scenario where line contains text that is a continuation of value from previously recorded key + // this line does not starting with .KEY, and is also not an empty line. + if (value.Equals(String.Empty)) + { + value += line; + } + else + { + value += Environment.NewLine + line; + } + + keyNeedsToBeAdded = true; + } + } + + // this is the case where last key value had multi-line value. + // and we've captured it, but still need to add it to hashtable. + if (!String.IsNullOrEmpty(keyName) && keyNeedsToBeAdded) + { + if (parsedHelpMetadata.ContainsKey(keyName)) + { + if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) + { + var message = String.Format("PowerShell script 'HelpInfo' comment block metadata cannot contain duplicate keys (i.e .KEY) for Description or Synopsis"); + var ex = new InvalidOperationException(message); + var psHelpInfoDuplicateKeyError = new ErrorRecord(ex, "psHelpInfoDuplicateKeyError", ErrorCategory.ParserError, null); + errorsList.Add(psHelpInfoDuplicateKeyError); + } + else + { + List currentValues = (List)parsedHelpMetadata[keyName]; + currentValues.Add(value); + parsedHelpMetadata[keyName] = currentValues; + } + } + else + { + // only add this key value if it hasn't already been added + if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) + { + parsedHelpMetadata.Add(keyName, value); + } + else + { + List valueList = new List(); + valueList.Add(value); + parsedHelpMetadata.Add(keyName, valueList); + } + } + } + + errors = errorsList.ToArray(); + + return parsedHelpMetadata; + } + /// /// Valides parsed help info content from the hashtable to ensure required help metadata (Description) is present /// and does not contain empty values. diff --git a/src/code/PSScriptMetadata.cs b/src/code/PSScriptMetadata.cs index ded8088e7..8a18064a3 100644 --- a/src/code/PSScriptMetadata.cs +++ b/src/code/PSScriptMetadata.cs @@ -150,7 +150,12 @@ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] error List msgsList = new List(); // parse content into a hashtable - Hashtable parsedMetadata = Utils.ParseCommentBlockContent(commentLines); + Hashtable parsedMetadata = ParseMetadataContentHelper(commentLines, out errors); + if (errors.Length != 0) + { + return false; + } + if (parsedMetadata.Count == 0) { @@ -220,6 +225,91 @@ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] error return true; } + /// + /// Parses metadata out of PSScriptCommentInfo comment block's lines (which are passed in) into a hashtable. + /// This comment block cannot have duplicate keys. + /// + public static Hashtable ParseMetadataContentHelper(string[] commentLines, out ErrorRecord[] errors) + { + /** + Comment lines can look like this: + + .KEY1 value + + .KEY2 value + + .KEY3 + value + + .KEY4 value + value continued + + */ + + errors = Array.Empty(); + List errorsList = new List(); + + Hashtable parsedHelpMetadata = new Hashtable(); + char[] spaceDelimeter = new char[]{' '}; + string keyName = ""; + string value = ""; + + for (int i = 1; i < commentLines.Length; i++) + { + string line = commentLines[i]; + + // scenario where line is: .KEY VALUE + // this line contains a new metadata property. + if (line.Trim().StartsWith(".")) + { + // check if keyName was previously populated, if so add this key value pair to the metadata hashtable + if (!String.IsNullOrEmpty(keyName)) + { + if (parsedHelpMetadata.ContainsKey(keyName)) + { + var message = String.Format("PowerShell script '<#PSScriptInfo .. #>' comment block metadata cannot contain duplicate key i.e .KEY"); + var ex = new InvalidOperationException(message); + var psScriptInfoDuplicateKeyError = new ErrorRecord(ex, "psScriptInfoDuplicateKeyError", ErrorCategory.ParserError, null); + errorsList.Add(psScriptInfoDuplicateKeyError); + continue; + } + + parsedHelpMetadata.Add(keyName, value); + } + + // setting count to 2 will get 1st separated string (key) into part[0] and the rest (value) into part[1] if any + string[] parts = line.Trim().TrimStart('.').Split(separator: spaceDelimeter, count: 2); + keyName = parts[0]; + value = parts.Length == 2 ? parts[1] : String.Empty; + } + else if (!String.IsNullOrEmpty(line)) + { + // scenario where line contains text that is a continuation of value from previously recorded key + // this line does not starting with .KEY, and is also not an empty line. + if (value.Equals(String.Empty)) + { + value += line; + } + else + { + value += Environment.NewLine + line; + } + } + } + + // this is the case where last key value had multi-line value. + // and we've captured it, but still need to add it to hashtable. + if (!String.IsNullOrEmpty(keyName) && !parsedHelpMetadata.ContainsKey(keyName)) + { + // only add this key value if it hasn't already been added + parsedHelpMetadata.Add(keyName, value); + } + + errors = errorsList.ToArray(); + + return parsedHelpMetadata; + } + /// /// Valides parsed metadata content from the hashtable to ensure required metadata (Author, Version, Guid) is present /// and does not contain empty values. diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 977d0be8a..32292d87f 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -1037,74 +1037,6 @@ public static bool TryCreateModuleSpecification( return moduleSpecCreatedSuccessfully; } - /// - /// Parses metadata out of a comment block's lines (which are passed in) into a hashtable. - /// - public static Hashtable ParseCommentBlockContent(string[] commentLines) - { - /** - Comment lines can look like this: - - .KEY1 value - - .KEY2 value - - .KEY3 - value - - .KEY4 value - value continued - - */ - - Hashtable parsedHelpMetadata = new Hashtable(); - string keyName = ""; - string value = ""; - - for (int i = 1; i < commentLines.Count(); i++) - { - string line = commentLines[i]; - - // scenario where line is: .KEY VALUE - // this line contains a new metadata property. - if (line.Trim().StartsWith(".")) - { - // check if keyName was previously populated, if so add this key value pair to the metadata hashtable - if (!String.IsNullOrEmpty(keyName)) - { - parsedHelpMetadata.Add(keyName, value); - } - - string[] parts = line.Trim().TrimStart('.').Split(); - keyName = parts[0]; - value = parts.Count() > 1 ? String.Join(" ", parts.Skip(1)) : String.Empty; - } - else if (!String.IsNullOrEmpty(line)) - { - // scenario where line contains text that is a continuation of value from previously recorded key - // this line does not starting with .KEY, and is also not an empty line. - if (value.Equals(String.Empty)) - { - value += line; - } - else - { - value += Environment.NewLine + line; - } - } - } - - // this is the case where last key value had multi-line value. - // and we've captured it, but still need to add it to hashtable. - if (!String.IsNullOrEmpty(keyName) && !parsedHelpMetadata.ContainsKey(keyName)) - { - // only add this key value if it hasn't already been added - parsedHelpMetadata.Add(keyName, value); - } - - return parsedHelpMetadata; - } - #endregion #region Directory and File From 01c1cef4dd6a4e76d828b13d91357a454d8e8ebd Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 20 Jul 2022 18:06:27 -0400 Subject: [PATCH 2/9] fix array out of bounds exception when updating script with signature --- src/code/PSScriptContents.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code/PSScriptContents.cs b/src/code/PSScriptContents.cs index c04f1dc49..eaf11c193 100644 --- a/src/code/PSScriptContents.cs +++ b/src/code/PSScriptContents.cs @@ -105,7 +105,7 @@ private void RemoveSignatureString() { if (ContainsSignature) { - string[] newEndOfFileContents = new string[EndOfFileContents.Length - _signatureStartIndex]; + string[] newEndOfFileContents = new string[_signatureStartIndex]; Array.Copy(EndOfFileContents, newEndOfFileContents, _signatureStartIndex); EndOfFileContents = newEndOfFileContents; From b34b462a386c7fbb2534a43a09d259044ac5721d Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 20 Jul 2022 18:19:49 -0400 Subject: [PATCH 3/9] check for parameter and example in HelpInfo being null after parsing as well --- src/code/PSScriptHelp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index 2791a2c82..57bd5f9eb 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -149,10 +149,10 @@ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] error Description = (string) parsedHelpMetadata["DESCRIPTION"]; Synopsis = (string) parsedHelpMetadata["SYNOPSIS"] ?? String.Empty; - List parameterList = (List)parsedHelpMetadata["PARAMETER"]; + List parameterList = parsedHelpMetadata.ContainsKey("PARAMETER") ? (List)parsedHelpMetadata["PARAMETER"]: new List(); Parameter = parameterList.ToArray(); - List exampleList = (List)parsedHelpMetadata["EXAMPLE"]; + List exampleList = parsedHelpMetadata.ContainsKey("EXAMPLE") ? (List)parsedHelpMetadata["EXAMPLE"] : new List(); Example = exampleList.ToArray(); List inputList = parsedHelpMetadata.ContainsKey("INPUT") ? (List)parsedHelpMetadata["INPUT"] : new List(); From b622633017a3ab83fa7aad566a9941a386ae10c7 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 20 Jul 2022 18:27:10 -0400 Subject: [PATCH 4/9] Parameter property needs to be set to empty string array by default --- src/code/PSScriptHelp.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index 57bd5f9eb..655cdf705 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -28,7 +28,7 @@ public sealed class PSScriptHelp /// /// The parameter(s) for the script. /// - public string[] Parameter { get; private set; } + public string[] Parameter { get; private set; } = Utils.EmptyStrArray; /// /// The example(s) relating to the script's usage. @@ -394,6 +394,12 @@ internal string[] EmitContent() psHelpInfoLines.Add($"{Synopsis}{Environment.NewLine}"); } + foreach (string currentParameter in Parameter) + { + psHelpInfoLines.Add($".PARAMETER"); + psHelpInfoLines.Add($"{currentParameter}{Environment.NewLine}"); + } + foreach (string currentExample in Example) { psHelpInfoLines.Add($".EXAMPLE"); From 602d3fce4ce04d56de57f734efe608464eb41539 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 21 Jul 2022 10:31:23 -0400 Subject: [PATCH 5/9] retain comment block #> end marker and use while parsing for simpler code --- src/code/PSScriptFileInfo.cs | 7 +-- src/code/PSScriptHelp.cs | 83 +++++++++++++++++------------------- src/code/PSScriptMetadata.cs | 17 ++++---- 3 files changed, 52 insertions(+), 55 deletions(-) diff --git a/src/code/PSScriptFileInfo.cs b/src/code/PSScriptFileInfo.cs index 495e906f8..75e1dda9a 100644 --- a/src/code/PSScriptFileInfo.cs +++ b/src/code/PSScriptFileInfo.cs @@ -132,14 +132,15 @@ internal static bool TryParseScriptFileContents( while (j < fileContents.Length) { string blockLine = fileContents[j]; + psScriptInfoCommentContent.Add(blockLine); if (blockLine.StartsWith("#>")) { + reachedPSScriptInfoCommentEnd = true; i = j + 1; break; } - psScriptInfoCommentContent.Add(blockLine); j++; } @@ -159,6 +160,7 @@ internal static bool TryParseScriptFileContents( while (j < fileContents.Length) { string blockLine = fileContents[j]; + helpInfoCommentContent.Add(blockLine); if (blockLine.StartsWith("#>")) { reachedHelpInfoCommentEnd = true; @@ -166,8 +168,7 @@ internal static bool TryParseScriptFileContents( endOfFileContentsStartIndex = i; break; } - - helpInfoCommentContent.Add(blockLine); + j++; } diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index 655cdf705..27099fcff 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -210,20 +210,17 @@ value continued string keyName = ""; string value = ""; - bool keyNeedsToBeAdded = false; - for (int i = 0; i < commentLines.Length; i++) { string line = commentLines[i]; - // scenario where line is: .KEY VALUE + // Scenario where line is: .KEY VALUE // this line contains a new metadata property. if (line.Trim().StartsWith(".")) { // check if keyName was previously populated, if so add this key value pair to the metadata hashtable if (!String.IsNullOrEmpty(keyName)) { - keyNeedsToBeAdded = false; // we'l end up adding the key,value to hashtable in this code flow if (parsedHelpMetadata.ContainsKey(keyName)) { if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) @@ -260,11 +257,47 @@ value continued string[] parts = line.Trim().TrimStart('.').Split(separator: spaceDelimeter, count: 2); keyName = parts[0]; value = parts.Length == 2 ? parts[1] : String.Empty; - keyNeedsToBeAdded = true; + } + else if(line.Trim().StartsWith("#>")) + { + // This line signifies end of comment block, so add last recorded key value pair before the comment block ends. + if (!String.IsNullOrEmpty(keyName)) + { + if (parsedHelpMetadata.ContainsKey(keyName)) + { + if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) + { + var message = String.Format("PowerShell script 'HelpInfo' comment block metadata cannot contain duplicate keys (i.e .KEY) for Description or Synopsis"); + var ex = new InvalidOperationException(message); + var psHelpInfoDuplicateKeyError = new ErrorRecord(ex, "psHelpInfoDuplicateKeyError", ErrorCategory.ParserError, null); + errorsList.Add(psHelpInfoDuplicateKeyError); + } + else + { + List currentValues = (List)parsedHelpMetadata[keyName]; + currentValues.Add(value); + parsedHelpMetadata[keyName] = currentValues; + } + } + else + { + // only add this key value if it hasn't already been added + if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) + { + parsedHelpMetadata.Add(keyName, value); + } + else + { + List valueList = new List(); + valueList.Add(value); + parsedHelpMetadata.Add(keyName, valueList); + } + } + } } else if (!String.IsNullOrEmpty(line)) { - // scenario where line contains text that is a continuation of value from previously recorded key + // Scenario where line contains text that is a continuation of value from previously recorded key // this line does not starting with .KEY, and is also not an empty line. if (value.Equals(String.Empty)) { @@ -275,48 +308,10 @@ value continued value += Environment.NewLine + line; } - keyNeedsToBeAdded = true; - } - } - - // this is the case where last key value had multi-line value. - // and we've captured it, but still need to add it to hashtable. - if (!String.IsNullOrEmpty(keyName) && keyNeedsToBeAdded) - { - if (parsedHelpMetadata.ContainsKey(keyName)) - { - if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) - { - var message = String.Format("PowerShell script 'HelpInfo' comment block metadata cannot contain duplicate keys (i.e .KEY) for Description or Synopsis"); - var ex = new InvalidOperationException(message); - var psHelpInfoDuplicateKeyError = new ErrorRecord(ex, "psHelpInfoDuplicateKeyError", ErrorCategory.ParserError, null); - errorsList.Add(psHelpInfoDuplicateKeyError); - } - else - { - List currentValues = (List)parsedHelpMetadata[keyName]; - currentValues.Add(value); - parsedHelpMetadata[keyName] = currentValues; - } - } - else - { - // only add this key value if it hasn't already been added - if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) - { - parsedHelpMetadata.Add(keyName, value); - } - else - { - List valueList = new List(); - valueList.Add(value); - parsedHelpMetadata.Add(keyName, valueList); - } } } errors = errorsList.ToArray(); - return parsedHelpMetadata; } diff --git a/src/code/PSScriptMetadata.cs b/src/code/PSScriptMetadata.cs index 8a18064a3..f5cadb1ed 100644 --- a/src/code/PSScriptMetadata.cs +++ b/src/code/PSScriptMetadata.cs @@ -282,6 +282,15 @@ value continued keyName = parts[0]; value = parts.Length == 2 ? parts[1] : String.Empty; } + else if (line.Trim().StartsWith("#>")) + { + // This line signifies end of comment block, so add last recorded key value pair before the comment block ends. + if (!String.IsNullOrEmpty(keyName) && !parsedHelpMetadata.ContainsKey(keyName)) + { + // only add this key value if it hasn't already been added + parsedHelpMetadata.Add(keyName, value); + } + } else if (!String.IsNullOrEmpty(line)) { // scenario where line contains text that is a continuation of value from previously recorded key @@ -297,14 +306,6 @@ value continued } } - // this is the case where last key value had multi-line value. - // and we've captured it, but still need to add it to hashtable. - if (!String.IsNullOrEmpty(keyName) && !parsedHelpMetadata.ContainsKey(keyName)) - { - // only add this key value if it hasn't already been added - parsedHelpMetadata.Add(keyName, value); - } - errors = errorsList.ToArray(); return parsedHelpMetadata; From 2127be5637b4937f54ed40dcf5b14edd3f971286 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Fri, 22 Jul 2022 11:25:54 -0400 Subject: [PATCH 6/9] refactor HelpInfo comment block parsing to not modify user created part --- src/code/PSScriptContents.cs | 18 +- src/code/PSScriptFileInfo.cs | 3 +- src/code/PSScriptHelp.cs | 320 ++++++----------------------------- 3 files changed, 65 insertions(+), 276 deletions(-) diff --git a/src/code/PSScriptContents.cs b/src/code/PSScriptContents.cs index eaf11c193..b12f330cf 100644 --- a/src/code/PSScriptContents.cs +++ b/src/code/PSScriptContents.cs @@ -15,7 +15,7 @@ public sealed class PSScriptContents /// /// End of file contents for the .ps1 file. /// - public string[] EndOfFileContents { get; private set; } = Utils.EmptyStrArray; + public string[] ScriptContents { get; private set; } = Utils.EmptyStrArray; /// /// End of file contents for the .ps1 file. @@ -38,7 +38,7 @@ public sealed class PSScriptContents /// public PSScriptContents(string[] endOfFileContents) { - EndOfFileContents = endOfFileContents; + ScriptContents = endOfFileContents; ContainsSignature = CheckForSignature(); } @@ -60,7 +60,7 @@ internal void ParseContent(string[] commentLines) { if (commentLines.Length != 0) { - EndOfFileContents = commentLines; + ScriptContents = commentLines; ContainsSignature = CheckForSignature(); } } @@ -74,7 +74,7 @@ internal void ParseContent(string[] commentLines) internal string[] EmitContent() { RemoveSignatureString(); - return EndOfFileContents; + return ScriptContents; } #endregion @@ -86,9 +86,9 @@ internal string[] EmitContent() /// private bool CheckForSignature() { - for (int i = 0; i < EndOfFileContents.Length; i++) + for (int i = 0; i < ScriptContents.Length; i++) { - if (String.Equals(EndOfFileContents[i], signatureStartString, StringComparison.InvariantCultureIgnoreCase)) + if (String.Equals(ScriptContents[i], signatureStartString, StringComparison.InvariantCultureIgnoreCase)) { _signatureStartIndex = i; } @@ -105,9 +105,9 @@ private void RemoveSignatureString() { if (ContainsSignature) { - string[] newEndOfFileContents = new string[_signatureStartIndex]; - Array.Copy(EndOfFileContents, newEndOfFileContents, _signatureStartIndex); - EndOfFileContents = newEndOfFileContents; + string[] contentsWithoutSignature = new string[_signatureStartIndex]; + Array.Copy(ScriptContents, contentsWithoutSignature, _signatureStartIndex); + ScriptContents = contentsWithoutSignature; ContainsSignature = false; } diff --git a/src/code/PSScriptFileInfo.cs b/src/code/PSScriptFileInfo.cs index 75e1dda9a..c61b25498 100644 --- a/src/code/PSScriptFileInfo.cs +++ b/src/code/PSScriptFileInfo.cs @@ -160,7 +160,7 @@ internal static bool TryParseScriptFileContents( while (j < fileContents.Length) { string blockLine = fileContents[j]; - helpInfoCommentContent.Add(blockLine); + if (blockLine.StartsWith("#>")) { reachedHelpInfoCommentEnd = true; @@ -169,6 +169,7 @@ internal static bool TryParseScriptFileContents( break; } + helpInfoCommentContent.Add(blockLine); j++; } diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index 27099fcff..aebc1e546 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -18,57 +18,12 @@ public sealed class PSScriptHelp /// /// The description of the script. /// - public string Description { get; private set; } + public string Description { get; private set; } = String.Empty; /// - /// The synopsis of the script. + /// This contains all help content aside from Description /// - public string Synopsis { get; private set; } - - /// - /// The parameter(s) for the script. - /// - public string[] Parameter { get; private set; } = Utils.EmptyStrArray; - - /// - /// The example(s) relating to the script's usage. - /// - public string[] Example { get; private set; } = Utils.EmptyStrArray; - - /// - /// The inputs to the script. - /// - public string[] Inputs { get; private set; } = Utils.EmptyStrArray; - - /// - /// The outputs to the script. - /// - public string[] Outputs { get; private set; } = Utils.EmptyStrArray; - - /// - /// The notes for the script. - /// - public string[] Notes { get; private set; } = Utils.EmptyStrArray; - - /// - /// The links for the script. - /// - public string[] Links { get; private set; } = Utils.EmptyStrArray; - - /// - /// The components for the script. - /// - public string[] Component { get; private set; } = Utils.EmptyStrArray; - - /// - /// The roles for the script. - /// - public string[] Role { get; private set; } = Utils.EmptyStrArray; - - /// - /// The functionality components for the script. - /// - public string[] Functionality { get; private set; } = Utils.EmptyStrArray; + public List HelpContent { get; private set; } = new List(); #endregion @@ -82,36 +37,6 @@ public PSScriptHelp (string description) Description = description; } - /// - /// This constructor takes values for description as well as other properties and creates a new PSScriptHelp instance. - /// Currently, the New-PSScriptFileInfo and Update-PSScriptFileInfo cmdlets don't support the user providing these values. - /// - public PSScriptHelp ( - string description, - string synopsis, - string[] parameter, - string[] example, - string[] inputs, - string[] outputs, - string[] notes, - string[] links, - string[] component, - string[] role, - string[] functionality) - { - Description = description; - Synopsis = synopsis; - Parameter = parameter; - Example = example; - Inputs = inputs; - Outputs = outputs; - Notes = notes; - Links = links; - Component = component; - Role = role; - Functionality = functionality; - } - /// /// This constructor is called by internal cmdlet methods and creates a PSScriptHelp with default values /// for the parameters. Calling a method like PSScriptHelp.ParseConentIntoObj() would then populate those properties. @@ -128,16 +53,11 @@ internal PSScriptHelp() {} /// internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] errors) { - bool successfullyParsed = true; string[] spaceDelimeter = new string[]{" "}; string[] newlineDelimeter = new string[]{Environment.NewLine}; // parse content into a hashtable - Hashtable parsedHelpMetadata = ParseHelpContentHelper(commentLines, out errors); - if (errors.Length != 0) - { - return false; - } + Hashtable parsedHelpMetadata = ParseHelpContentHelper(commentLines); if (!ValidateParsedContent(parsedHelpMetadata, out ErrorRecord validationError)) { @@ -146,44 +66,22 @@ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] error } // populate object - Description = (string) parsedHelpMetadata["DESCRIPTION"]; - Synopsis = (string) parsedHelpMetadata["SYNOPSIS"] ?? String.Empty; - - List parameterList = parsedHelpMetadata.ContainsKey("PARAMETER") ? (List)parsedHelpMetadata["PARAMETER"]: new List(); - Parameter = parameterList.ToArray(); - - List exampleList = parsedHelpMetadata.ContainsKey("EXAMPLE") ? (List)parsedHelpMetadata["EXAMPLE"] : new List(); - Example = exampleList.ToArray(); - - List inputList = parsedHelpMetadata.ContainsKey("INPUT") ? (List)parsedHelpMetadata["INPUT"] : new List(); - Inputs = inputList.ToArray(); - - List outputList = parsedHelpMetadata.ContainsKey("OUTPUT") ? (List)parsedHelpMetadata["OUTPUT"] : new List(); - Outputs = outputList.ToArray(); - - List notesList = parsedHelpMetadata.ContainsKey("NOTES") ? (List)parsedHelpMetadata["NOTES"] : new List(); - Notes = notesList.ToArray(); - - List linksList = parsedHelpMetadata.ContainsKey("LINKS") ? (List)parsedHelpMetadata["LINKS"] : new List(); - Links = linksList.ToArray(); - - List componentList = parsedHelpMetadata.ContainsKey("COMPONENT") ? (List)parsedHelpMetadata["COMPONENT"] : new List(); - Component = componentList.ToArray(); - - List roleList = parsedHelpMetadata.ContainsKey("ROLE") ? (List)parsedHelpMetadata["ROLE"] : new List(); - Role = roleList.ToArray(); + List descriptionValue = (List) parsedHelpMetadata["DESCRIPTION"]; + Description = String.Join(Environment.NewLine, descriptionValue); + if (parsedHelpMetadata.ContainsKey("HELPCONTENT")) + { + HelpContent = (List) parsedHelpMetadata["HELPCONTENT"]; + } - List functionalityList = parsedHelpMetadata.ContainsKey("FUNCTIONALITY") ? (List)parsedHelpMetadata["FUNCTIONALITY"] : new List(); - Functionality = functionalityList.ToArray(); - - return successfullyParsed; + errors = Array.Empty(); + return true; } /// /// Parses metadata out of PSScriptCommentInfo comment block's lines (which are passed in) into a hashtable. /// This comment block cannot have duplicate keys. /// - public static Hashtable ParseHelpContentHelper(string[] commentLines, out ErrorRecord[] errors) + public static Hashtable ParseHelpContentHelper(string[] commentLines) { /** Comment lines can look like this: @@ -202,116 +100,50 @@ value continued */ - errors = Array.Empty(); - List errorsList = new List(); + // loop through lines + // if line is .DESCRIPTION say we found descfiption + // otherwise if line just starts with '.' set descriptionFound to false + // else if line is not empty, + // if descriptionFound --> add lines to descriptionValue + // else --> add lines to helpContentValue + // descriptionFound -> rename to onDescripton - Hashtable parsedHelpMetadata = new Hashtable(); - char[] spaceDelimeter = new char[]{' '}; - string keyName = ""; - string value = ""; + List helpContent = new List(); + List descriptionValue = new List(); + bool parsingDescription = false; - for (int i = 0; i < commentLines.Length; i++) + for(int i = 0; i < commentLines.Length; i++) { string line = commentLines[i]; - - // Scenario where line is: .KEY VALUE - // this line contains a new metadata property. - if (line.Trim().StartsWith(".")) + if (line.Trim().StartsWith(".DESCRIPTION")) { - // check if keyName was previously populated, if so add this key value pair to the metadata hashtable - if (!String.IsNullOrEmpty(keyName)) - { - if (parsedHelpMetadata.ContainsKey(keyName)) - { - if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) - { - var message = String.Format("PowerShell script 'HelpInfo' comment block metadata cannot contain duplicate keys (i.e .KEY) for Description or Synopsis"); - var ex = new InvalidOperationException(message); - var psHelpInfoDuplicateKeyError = new ErrorRecord(ex, "psHelpInfoDuplicateKeyError", ErrorCategory.ParserError, null); - errorsList.Add(psHelpInfoDuplicateKeyError); - continue; - } - - List currentValues = (List) parsedHelpMetadata[keyName]; - currentValues.Add(value); - parsedHelpMetadata[keyName] = currentValues; - } - else - { - // adding key for first time - if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) - { - parsedHelpMetadata.Add(keyName, value); - } - else - { - // the other keys will have values of type string[] - List valueList = new List(); - valueList.Add(value); - parsedHelpMetadata.Add(keyName, valueList); - } - } - } - - // setting count to 2 will get 1st separated string (key) into part[0] and the rest (value) into part[1] if any - string[] parts = line.Trim().TrimStart('.').Split(separator: spaceDelimeter, count: 2); - keyName = parts[0]; - value = parts.Length == 2 ? parts[1] : String.Empty; + parsingDescription = true; } - else if(line.Trim().StartsWith("#>")) + else if (line.Trim().StartsWith(".")) { - // This line signifies end of comment block, so add last recorded key value pair before the comment block ends. - if (!String.IsNullOrEmpty(keyName)) - { - if (parsedHelpMetadata.ContainsKey(keyName)) - { - if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) - { - var message = String.Format("PowerShell script 'HelpInfo' comment block metadata cannot contain duplicate keys (i.e .KEY) for Description or Synopsis"); - var ex = new InvalidOperationException(message); - var psHelpInfoDuplicateKeyError = new ErrorRecord(ex, "psHelpInfoDuplicateKeyError", ErrorCategory.ParserError, null); - errorsList.Add(psHelpInfoDuplicateKeyError); - } - else - { - List currentValues = (List)parsedHelpMetadata[keyName]; - currentValues.Add(value); - parsedHelpMetadata[keyName] = currentValues; - } - } - else - { - // only add this key value if it hasn't already been added - if (keyName.Equals("DESCRIPTION") || keyName.Equals("SYNOPSIS")) - { - parsedHelpMetadata.Add(keyName, value); - } - else - { - List valueList = new List(); - valueList.Add(value); - parsedHelpMetadata.Add(keyName, valueList); - } - } - } + parsingDescription = false; + helpContent.Add(line); } else if (!String.IsNullOrEmpty(line)) { - // Scenario where line contains text that is a continuation of value from previously recorded key - // this line does not starting with .KEY, and is also not an empty line. - if (value.Equals(String.Empty)) + if (parsingDescription) { - value += line; + descriptionValue.Add(line); } else { - value += Environment.NewLine + line; + helpContent.Add(line); } - } } - errors = errorsList.ToArray(); + Hashtable parsedHelpMetadata = new Hashtable(); + parsedHelpMetadata.Add("DESCRIPTION", descriptionValue); + if (helpContent.Count != 0) + { + parsedHelpMetadata.Add("HELPCONTENT", helpContent); + } + return parsedHelpMetadata; } @@ -322,7 +154,7 @@ value continued internal bool ValidateParsedContent(Hashtable parsedHelpMetadata, out ErrorRecord error) { error = null; - if (!parsedHelpMetadata.ContainsKey("DESCRIPTION") || String.IsNullOrEmpty((string) parsedHelpMetadata["DESCRIPTION"]) || String.Equals(((string) parsedHelpMetadata["DESCRIPTION"]).Trim(), String.Empty)) + if (!parsedHelpMetadata.ContainsKey("DESCRIPTION")) { var exMessage = "PSScript file must contain value for Description. Ensure value for Description is passed in and try again."; var ex = new ArgumentException(exMessage); @@ -331,7 +163,18 @@ internal bool ValidateParsedContent(Hashtable parsedHelpMetadata, out ErrorRecor return false; } - if (StringContainsComment((string) parsedHelpMetadata["DESCRIPTION"])) + List descriptionValue = (List) parsedHelpMetadata["DESCRIPTION"]; + string descriptionString = String.Join("", descriptionValue); + if (descriptionValue.Count == 0 || (String.IsNullOrEmpty(descriptionString)) || String.IsNullOrWhiteSpace(descriptionString)) + { + var exMessage = "PSScript file value for Description cannot be null, empty or whitespace. Ensure value for Description meets these conditions and try again."; + var ex = new ArgumentException(exMessage); + var PSScriptInfoMissingDescriptionError = new ErrorRecord(ex, "PSScriptInfoMissingDescription", ErrorCategory.InvalidArgument, null); + error = PSScriptInfoMissingDescriptionError; + return false; + } + + if (StringContainsComment(descriptionString)) { var exMessage = "PSScript file's value for Description cannot contain '<#' or '#>'. Pass in a valid value for Description and try again."; var ex = new ArgumentException(exMessage); @@ -383,66 +226,11 @@ internal string[] EmitContent() psHelpInfoLines.Add($".DESCRIPTION"); psHelpInfoLines.Add($"{Description}{Environment.NewLine}"); - if (!String.IsNullOrEmpty(Synopsis)) + if (HelpContent.Count != 0) { - psHelpInfoLines.Add($".SYNOPSIS"); - psHelpInfoLines.Add($"{Synopsis}{Environment.NewLine}"); - } - - foreach (string currentParameter in Parameter) - { - psHelpInfoLines.Add($".PARAMETER"); - psHelpInfoLines.Add($"{currentParameter}{Environment.NewLine}"); - } - - foreach (string currentExample in Example) - { - psHelpInfoLines.Add($".EXAMPLE"); - psHelpInfoLines.Add($"{currentExample}{Environment.NewLine}"); - } - - foreach (string input in Inputs) - { - psHelpInfoLines.Add($".INPUTS"); - psHelpInfoLines.Add($"{input}{Environment.NewLine}"); - } - - foreach (string output in Outputs) - { - psHelpInfoLines.Add($".OUTPUTS"); - psHelpInfoLines.Add($"{output}{Environment.NewLine}"); - } - - if (Notes.Length > 0) - { - psHelpInfoLines.Add($".NOTES"); - psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Notes)}{Environment.NewLine}"); - } - - foreach (string link in Links) - { - psHelpInfoLines.Add($".LINK"); - psHelpInfoLines.Add($"{link}{Environment.NewLine}"); - } - - if (Component.Length > 0) - { - psHelpInfoLines.Add($".COMPONENT"); - psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Component)}{Environment.NewLine}"); + psHelpInfoLines.AddRange(HelpContent); } - if (Role.Length > 0) - { - psHelpInfoLines.Add($".ROLE"); - psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Role)}{Environment.NewLine}"); - } - - if (Functionality.Length > 0) - { - psHelpInfoLines.Add($".FUNCTIONALITY"); - psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Functionality)}{Environment.NewLine}"); - } - psHelpInfoLines.Add("#>"); return psHelpInfoLines.ToArray(); From 7a790948cd49c09182444eec1ced618c5bb825ff Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Fri, 22 Jul 2022 11:32:57 -0400 Subject: [PATCH 7/9] clean up code comments --- src/code/PSScriptHelp.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index aebc1e546..c30395c78 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -79,7 +79,6 @@ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] error /// /// Parses metadata out of PSScriptCommentInfo comment block's lines (which are passed in) into a hashtable. - /// This comment block cannot have duplicate keys. /// public static Hashtable ParseHelpContentHelper(string[] commentLines) { @@ -100,13 +99,7 @@ value continued */ - // loop through lines - // if line is .DESCRIPTION say we found descfiption - // otherwise if line just starts with '.' set descriptionFound to false - // else if line is not empty, - // if descriptionFound --> add lines to descriptionValue - // else --> add lines to helpContentValue - // descriptionFound -> rename to onDescripton + // parse out Description and everything else into a bucket list List helpContent = new List(); List descriptionValue = new List(); From 6c2e0e5c5dac4bf01a21dd26660e020d3d5fd026 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Mon, 25 Jul 2022 18:53:28 -0400 Subject: [PATCH 8/9] code review feedback, need singular error object not array --- src/code/PSScriptContents.cs | 1 + src/code/PSScriptFileInfo.cs | 4 ++-- src/code/PSScriptHelp.cs | 14 ++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/code/PSScriptContents.cs b/src/code/PSScriptContents.cs index b12f330cf..111ec508e 100644 --- a/src/code/PSScriptContents.cs +++ b/src/code/PSScriptContents.cs @@ -91,6 +91,7 @@ private bool CheckForSignature() if (String.Equals(ScriptContents[i], signatureStartString, StringComparison.InvariantCultureIgnoreCase)) { _signatureStartIndex = i; + break; } } diff --git a/src/code/PSScriptFileInfo.cs b/src/code/PSScriptFileInfo.cs index c61b25498..dfeee2121 100644 --- a/src/code/PSScriptFileInfo.cs +++ b/src/code/PSScriptFileInfo.cs @@ -259,9 +259,9 @@ internal static bool TryPopulateScriptClassesWithParsedContent( currentHelpInfo = new PSScriptHelp(); if (!currentHelpInfo.ParseContentIntoObj( commentLines: helpInfoCommentContent.ToArray(), - out ErrorRecord[] helpErrors)) + out ErrorRecord helpError)) { - errorsList.AddRange(helpErrors); + errorsList.Add(helpError); parsedContentSuccessfully = false; } diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs index c30395c78..d6cef24ba 100644 --- a/src/code/PSScriptHelp.cs +++ b/src/code/PSScriptHelp.cs @@ -51,21 +51,20 @@ internal PSScriptHelp() {} /// Parses HelpInfo metadata out of the HelpInfo comment lines found while reading the file /// and populates PSScriptHelp properties from that metadata. /// - internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] errors) + internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord error) { - string[] spaceDelimeter = new string[]{" "}; - string[] newlineDelimeter = new string[]{Environment.NewLine}; + error = null; - // parse content into a hashtable + // Parse content into a hashtable. Hashtable parsedHelpMetadata = ParseHelpContentHelper(commentLines); if (!ValidateParsedContent(parsedHelpMetadata, out ErrorRecord validationError)) { - errors = new ErrorRecord[]{validationError}; + error = validationError; return false; } - // populate object + // Populate object. List descriptionValue = (List) parsedHelpMetadata["DESCRIPTION"]; Description = String.Join(Environment.NewLine, descriptionValue); if (parsedHelpMetadata.ContainsKey("HELPCONTENT")) @@ -73,7 +72,6 @@ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] error HelpContent = (List) parsedHelpMetadata["HELPCONTENT"]; } - errors = Array.Empty(); return true; } @@ -99,7 +97,7 @@ value continued */ - // parse out Description and everything else into a bucket list + // Parse out Description and everything else into a bucket list. List helpContent = new List(); List descriptionValue = new List(); From e038c7160c4114aea93a9e4fff0ccbc9391806a7 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Mon, 25 Jul 2022 18:56:35 -0400 Subject: [PATCH 9/9] Add comment describing removal of signature Co-authored-by: Paul Higinbotham --- src/code/PSScriptContents.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/code/PSScriptContents.cs b/src/code/PSScriptContents.cs index 111ec508e..63f1c4e80 100644 --- a/src/code/PSScriptContents.cs +++ b/src/code/PSScriptContents.cs @@ -106,6 +106,10 @@ private void RemoveSignatureString() { if (ContainsSignature) { + // The script signature comment block always appears at the end of the script file, + // so its start location becomes the end of the content section after the signature + // comment block is removed, and is also the length of the content section minus the + // signature block. string[] contentsWithoutSignature = new string[_signatureStartIndex]; Array.Copy(ScriptContents, contentsWithoutSignature, _signatureStartIndex); ScriptContents = contentsWithoutSignature;