{"id":2031,"date":"2013-10-30T08:23:00","date_gmt":"2013-10-30T08:23:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2013\/10\/30\/using-abstract-syntax-trees-asts-with-ise-to-make-scripting-more-productive\/"},"modified":"2019-02-18T13:05:30","modified_gmt":"2019-02-18T20:05:30","slug":"using-abstract-syntax-trees-asts-with-ise-to-make-scripting-more-productive","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/using-abstract-syntax-trees-asts-with-ise-to-make-scripting-more-productive\/","title":{"rendered":"Using abstract syntax trees (ASTs) with ISE to make scripting more productive"},"content":{"rendered":"<div class=\"WordSection1\">\n<p class=\"MsoNormal\">One thing I really like about Windows PowerShell ISE is its ability to expose its underlying script object model, to allow users to customize the scripting experience to suit their style and need.<\/p>\n<p>At the heart of customizing ISE is the $psISE object. The $psISE object allows you to control the various functional aspects of ISE. You can get a good description of the hierarchical object model for $psISE, and an exhaustive list of available features associated with this object <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819500.aspx\">here<\/a>.<\/p>\n<p>This post discusses how you can leverage the power of PowerShell&#8217;s publicly-available parser APIs, and combine that power with the ISE object model to create tools for script analysis and easy navigation.<\/p>\n<p>Imagine you have to analyze a relatively large PowerShell script. This might either have been written by somebody else, or you might be reading your own script a few months down the line. PowerShell ISE does a great job of providing a scripting environment. You can increase its usefulness by adding your own custom Add-ons to make your scripting experience better and more productive. Starting in Windows PowerShell 3.0, the abstract syntax tree (AST) of the script can be conveniently obtained by using the parser API&rsquo;s. The following line will get the AST of the script currently open in ISE:<\/p>\n<p><span style=\"color: orangered;line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\">$AbstractSyntaxTree<\/span><span style=\"line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\"> <span style=\"color: darkgray\">=<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Management.Automation.Language.Parser<\/span><span style=\"color: darkgray\">]::<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParseInput(<span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>Text<span style=\"color: darkgray\">,<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">ref<\/span><span style=\"color: darkgray\">]<\/span><span style=\"color: orangered\">$null<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">ref<\/span><span style=\"color: darkgray\">]<\/span><span style=\"color: orangered\">$null<\/span>) <\/p>\n<p><\/span><\/p>\n<p>Next, let us find all functions in the script:<\/p>\n<p><span style=\"color: orangered;line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\">$functionsInFile<\/span><span style=\"line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\"> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$AbstractSyntaxTree<\/span><span style=\"color: darkgray\">.<\/span>FindAll({<span style=\"color: orangered\">$args<\/span><span style=\"color: darkgray\">[<\/span><span style=\"color: purple\">0<\/span><span style=\"color: darkgray\">]<\/span> <span style=\"color: darkgray\">-is<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Management.Automation.Language.FunctionDefinitionAst<\/span><span style=\"color: darkgray\">]<\/span>}<span style=\"color: darkgray\">,<\/span> <span style=\"color: orangered\">$true<\/span>)<\/span><\/p>\n<p>Apart from navigating to the function definition, it would be nice if we could also come back to the place where we left. Implementing this is pretty easy. All we need to do is store the line numbers to which we want to navigate, and then traverse them in the reverse order. (Did someone just say the word, &#8216;stack&#8217;?)<\/p>\n<p>The following script block shows the implementation of the <strong>Go-To Definition<\/strong> functionality:<\/p>\n<p><span style=\"color: darkgreen;line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\">#Define some useful global variables<\/span><span style=\"line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\"> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOncurrLine<\/span><span style=\"color: darkgray\">=<\/span><span style=\"color: purple\">1<\/span> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOncurrcol<\/span><span style=\"color: darkgray\">=<\/span><span style=\"color: purple\">1<\/span> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOnlineToGoTo<\/span><span style=\"color: darkgray\">=<\/span><span style=\"color: purple\">1<\/span> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOncolToGoTo<\/span><span style=\"color: darkgray\">=<\/span><span style=\"color: purple\">1<\/span> <br \/><span style=\"color: darkgreen\">#We need two stacks &#8211; one each for line and column<\/span> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfLine<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: blue\">New-Object<\/span> <span style=\"color: blueviolet\">System.Collections.Stack<\/span> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfCol<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: blue\">New-Object<\/span> <span style=\"color: blueviolet\">System.Collections.Stack<\/span> <\/p>\n<p><span style=\"color: darkgreen\">#This script block has the logic for the implementation of the Go-To definition functionality<\/span> <br \/><span style=\"color: orangered\">$global:__ISEGoToAddOnscriptBlockGoTo<\/span> <span style=\"color: darkgray\">=<\/span> <br \/> { <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$AbstractSyntaxTree<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Management.Automation.Language.Parser<\/span><span style=\"color: darkgray\">]::<\/span>ParseInput(<span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>Text<span style=\"color: darkgray\">,<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">ref<\/span><span style=\"color: darkgray\">]<\/span><span style=\"color: orangered\">$null<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">ref<\/span><span style=\"color: darkgray\">]<\/span><span style=\"color: orangered\">$null<\/span>) <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$functionsInFile<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$AbstractSyntaxTree<\/span><span style=\"color: darkgray\">.<\/span>FindAll( <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span style=\"color: orangered\">$args<\/span><span style=\"color: darkgray\">[<\/span><span style=\"color: purple\">0<\/span><span style=\"color: darkgray\">]<\/span> <span style=\"color: darkgray\">-is<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Management.Automation.Language.FunctionDefinitionAst<\/span><span style=\"color: darkgray\">]<\/span>}<span style=\"color: darkgray\">,<\/span> <span style=\"color: orangered\">$true<\/span>) <\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#Get the text of the line where we have the cursor<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$str<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>CaretLineText <\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#Store them on the stack for later use<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfLine<\/span><span style=\"color: darkgray\">.<\/span>Push(<span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>CaretLine) <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfCol<\/span><span style=\"color: darkgray\">.<\/span>Push(<span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>CaretColumn) <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOncurrLine<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfLine<\/span><span style=\"color: darkgray\">.<\/span>Peek() <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOncurrcol<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfCol<\/span><span style=\"color: darkgray\">.<\/span>Peek() <\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#Get the selected text so that it can be used for searching existing functions<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$selectedFunction<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>SelectedText <\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#Ensure that the cursor is somewhere between the word boundaries of the function<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$functionsInFile<\/span> <span style=\"color: darkgray\">|<\/span> <span style=\"color: blue\">%<\/span>{<span style=\"color: darkblue\">if<\/span>((<span style=\"color: orangered\">$str<\/span><span style=\"color: darkgray\">.<\/span>Contains(<span style=\"color: orangered\">$_<\/span><span style=\"color: darkgray\">.<\/span>name)) ` <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkgray\">&ndash;and<\/span> (<span style=\"color: orangered\">$global:__ISEGoToAddOncurrcol<\/span> <span style=\"color: darkgray\">-ge<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$str<\/span><span style=\"color: darkgray\">.<\/span>IndexOf(<span style=\"color: orangered\">$_<\/span><span style=\"color: darkgray\">.<\/span>name)) ` <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkgray\">-and<\/span> (<span style=\"color: orangered\">$global:__ISEGoToAddOncurrcol<\/span> <span style=\"color: darkgray\">-le<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (<span style=\"color: orangered\">$str<\/span><span style=\"color: darkgray\">.<\/span>IndexOf(<span style=\"color: orangered\">$_<\/span><span style=\"color: darkgray\">.<\/span>name)<span style=\"color: darkgray\">+<\/span><span style=\"color: orangered\">$_<\/span><span style=\"color: darkgray\">.<\/span>name<span style=\"color: darkgray\">.<\/span>length)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span style=\"color: orangered\">$selectedFunction<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$_<\/span><span style=\"color: darkgray\">.<\/span>name} <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">if<\/span>(<span style=\"color: orangered\">$selectedFunction<\/span> <span style=\"color: darkgray\">-ne<\/span> <span style=\"color: darkred\">&#8220;&#8221;<\/span>) <br \/>&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#See if the selected function exists in the current open file<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$functionToGoTo<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$functionsInFile<\/span> <span style=\"color: darkgray\">|<\/span> <span style=\"color: blue\">?<\/span>{<span style=\"color: orangered\">$_<\/span><span style=\"color: darkgray\">.<\/span>name <span style=\"color: darkgray\">-eq<\/span> <span style=\"color: darkred\">&#8220;<\/span><span style=\"color: orangered\">$selectedFunction<\/span><span style=\"color: darkred\">&#8220;<\/span>} <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOnlineToGoTo<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$functionToGoTo<\/span><span style=\"color: darkgray\">.<\/span>Extent<span style=\"color: darkgray\">.<\/span>StartLineNumber <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOncolToGoTo<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: orangered\">$functionToGoTo<\/span><span style=\"color: darkgray\">.<\/span>Extent<span style=\"color: darkgray\">.<\/span>StartColumnNumber <\/p>\n<p>&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">if<\/span>(<span style=\"color: orangered\">$functionToGoTo<\/span> <span style=\"color: darkgray\">-eq<\/span> <span style=\"color: orangered\">$null<\/span>) <br \/>&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">try<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$comm<\/span> <span style=\"color: darkgray\">=<\/span> <span style=\"color: blue\">Get-Command<\/span> <span style=\"color: navy\">-Name<\/span> <span style=\"color: darkred\">&#8220;<\/span><span style=\"color: orangered\">$selectedFunction<\/span><span style=\"color: darkred\">&#8220;<\/span> <span style=\"color: navy\">-ErrorAction<\/span> <span style=\"color: blueviolet\">SilentlyContinue<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$comm<\/span><span style=\"color: darkgray\">.<\/span>Definition <span style=\"color: darkgray\">|<\/span> <span style=\"color: blue\">Out-GridView<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">catch<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Exception<\/span><span style=\"color: darkgray\">]<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">else<\/span> <br \/>&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#Select the function definition, assuming the function name immediately follows the keyword &#8216;function&#8217;<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">try<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$psise<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>Select(<span style=\"color: orangered\">$global:__ISEGoToAddOnlineToGoTo<\/span><span style=\"color: darkgray\">,<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (<span style=\"color: orangered\">$global:__ISEGoToAddOncolToGoTo<\/span><span style=\"color: darkgray\">+<\/span><span style=\"color: purple\">9<\/span>)<span style=\"color: darkgray\">,<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOnlineToGoTo<\/span><span style=\"color: darkgray\">,<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (<span style=\"color: orangered\">$global:__ISEGoToAddOncolToGoTo<\/span><span style=\"color: darkgray\">+<\/span><span style=\"color: purple\">8<\/span><span style=\"color: darkgray\">+<\/span><span style=\"color: orangered\">$selectedFunction<\/span><span style=\"color: darkgray\">.<\/span>length<span style=\"color: darkgray\">+<\/span><span style=\"color: purple\">1<\/span>)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">catch<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Exception<\/span><span style=\"color: darkgray\">]<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; } <br \/> } <\/p>\n<p><\/span><\/p>\n<p>In addition to the <strong>Go-To Definition<\/strong> functionality, the above script block also shows the definition of the selected text if it exists in the current PowerShell session. (The above script block is just a simple example, and assumes that the keyword &#8216;function&#8217; and its name appear in the same line in the script. This is not required by PowerShell, so if your scripting style is different, you might need to tweak the logic a little.)<\/p>\n<p>The next step is to add this script block as the result of selecting a command on the Add-on menu. The following two lines do just that:<\/p>\n<p><span style=\"color: orangered;line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\">$global:__ISEGoToAddOnsb1<\/span><span style=\"line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\"> <span style=\"color: darkgray\">=<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span style=\"color: darkgray\">&amp;<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOnscriptBlockGoTo<\/span> <span style=\"color: darkgray\">|<\/span> <span style=\"color: blue\">Out-Null<\/span>} <br \/><span style=\"color: orangered\">$null<\/span><span style=\"color: darkgray\">=<\/span><span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentPowerShellTab<span style=\"color: darkgray\">.<\/span>AddOnsMenu<span style=\"color: darkgray\">.<\/span>Submenus<span style=\"color: darkgray\">.<\/span>Add( <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkred\">&#8220;Go do definition&#8221;<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOnsb1<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: darkred\">&#8220;F12&#8221;<\/span>) <\/p>\n<p><\/span><\/p>\n<p>Now, let us see how we can implement the <strong>Go-Back<\/strong> functionality, in just a few lines of code that leverages our global stack:<\/p>\n<p><span style=\"color: orangered;line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\">$global:__ISEGoToAddOnscriptBlockGoBack<\/span><span style=\"line-height: 107%;font-family: 'Lucida Console';font-size: 9.5pt\"> <span style=\"color: darkgray\">=<\/span> <br \/> { <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">try<\/span> <br \/>&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: darkgreen\">#Pop the line and column numbers from the stack to do a reverse traversal <\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOncurrLine<\/span> <span style=\"color: darkgray\">=<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfLine<\/span><span style=\"color: darkgray\">.<\/span>Pop() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOncurrcol<\/span> <span style=\"color: darkgray\">=<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOnstackOfCol<\/span><span style=\"color: darkgray\">.<\/span>Pop() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>SetCaretPosition( <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$global:__ISEGoToAddOncurrLine<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOncurrcol<\/span>) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentFile<span style=\"color: darkgray\">.<\/span>Editor<span style=\"color: darkgray\">.<\/span>SelectCaretLine(); <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; <span style=\"color: darkblue\">catch<\/span> <span style=\"color: darkgray\">[<\/span><span style=\"color: teal\">System.Exception<\/span><span style=\"color: darkgray\">]<\/span> <br \/>&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp; } <br \/> } <\/p>\n<p><span style=\"color: orangered\">$global:__ISEGoToAddOnsb2<\/span> <span style=\"color: darkgray\">=<\/span> {<span style=\"color: darkgray\">&amp;<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOnscriptBlockGoBack<\/span> <span style=\"color: darkgray\">|<\/span> <span style=\"color: blue\">Out-Null<\/span>} <br \/><span style=\"color: orangered\">$null<\/span><span style=\"color: darkgray\">=<\/span><span style=\"color: orangered\">$psISE<\/span><span style=\"color: darkgray\">.<\/span>CurrentPowerShellTab<span style=\"color: darkgray\">.<\/span>AddOnsMenu<span style=\"color: darkgray\">.<\/span>Submenus<span style=\"color: darkgray\">.<\/span>Add(<span style=\"color: darkred\">&#8220;Go Back&#8221;<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: orangered\">$global:__ISEGoToAddOnsb2<\/span><span style=\"color: darkgray\">,<\/span> <span style=\"color: darkred\">&#8220;Shift+F12&#8221;<\/span>) <\/p>\n<p><\/span><\/p>\n<p>That&#8217;s it! With just a few lines of PowerShell code, we have implemented Visual Studio functionalities, <strong>Go-To Definition<\/strong> and <strong>Go-Back<\/strong>.<\/p>\n<p>You can extend this script further to include tasks such as viewing all the functions in the script, and then navigating to the function you want by clicking a GUI button. As an encouragement to add further capabilities, let me give you a peek at what my ISE Add-ons menu currently looks like:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/30\/2018\/10\/6567.addontoolsmenu.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/30\/2018\/10\/6567.addontoolsmenu.png\" alt=\"\" border=\"0\" \/><\/a><\/p>\n<p><span style=\"font-family: 'Segoe UI','sans-serif'\"> <\/p>\n<p> Thanks, <br \/> Abhik Chatterjee <br \/> Windows PowerShell Developer <br \/> Microsoft Corporation <\/p>\n<p><\/span><\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>One thing I really like about Windows PowerShell ISE is its ability to expose its underlying script object model, to allow users to customize the scripting experience to suit their style and need. At the heart of customizing ISE is the $psISE object. The $psISE object allows you to control the various functional aspects of [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[87,95,186,187,205],"class_list":["post-2031","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-psise-object","tag-add-ons","tag-go-back","tag-go-to-definition","tag-ise"],"acf":[],"blog_post_summary":"<p>One thing I really like about Windows PowerShell ISE is its ability to expose its underlying script object model, to allow users to customize the scripting experience to suit their style and need. At the heart of customizing ISE is the $psISE object. The $psISE object allows you to control the various functional aspects of [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/2031","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=2031"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/2031\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=2031"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=2031"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=2031"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}