Code Monkey home page Code Monkey logo

powershelleditorservices's Introduction

PowerShell Editor Services

CI Tests Discord Join the chat at https://gitter.im/PowerShell/PowerShellEditorServices

PowerShell Editor Services is a PowerShell module that provides common functionality needed to enable a consistent and robust PowerShell development experience in almost any editor or integrated development environment (IDE).

Language Server Protocol clients using PowerShell Editor Services:

The functionality in PowerShell Editor Services is available in the following editor extensions:

Please note that other than PowerShell for Visual Studio Code, these clients are community maintained and may be very out of date. It is recommended that you simply use an LSP plugin for your editor and configure it as demonstrated below.

Features

  • The Language Service provides common editor features for the PowerShell language:
    • Code navigation actions (find references, go to definition)
    • Statement completions (IntelliSense)
    • Real-time semantic analysis of scripts using PowerShell Script Analyzer
  • The Debugging Service simplifies interaction with the PowerShell debugger (breakpoints, variables, call stack, etc.)
  • The $psEditor API enables scripting of the host editor
  • A full, Extension Terminal experience for interactive development and debugging

Usage

If you're looking to integrate PowerShell Editor Services into your Language Server Protocol compliant editor or client, we support two ways of connecting.

Named Pipes / Unix Domain Sockets

If you're looking for a more feature-rich experience, named pipes (AKA sockets) are the way to go. They give you all the benefits of the Language Server Protocol with extra capabilities that you can take advantage of:

The typical command to start PowerShell Editor Services using named pipes / sockets is as follows:

pwsh -NoLogo -NoProfile -Command "./PowerShellEditorServices/Start-EditorServices.ps1 -SessionDetailsPath ./session.json"

The start script, Start-EditorServices.ps1, is found in the PowerShellEditorServices folder instead the PowerShellEditorServices.zip downloaded from the GitHub releases.

The session details (which named pipes were created) will be written to the given session details path, and the client needs to point to these in order to connect.

The Visual Studio Code, Vim, Neovim, and IntelliJ extensions use named pipes.

Standard Input and Output

Alternatively, the -SessionDetailsPath ./session.json argument can be replaced with just -Stdio. The use of stdio is the simplest way to connect with most LSP clients, but will limit some features, such as the debugger and Extension Terminal. This is because because these two features require their own IO streams and stdio only provides a single pair of streams.

Please see the emacs-simple-test.el, emacs-test.el, vim-simple-test.vim and vim-test.vim for examples of end-to-end tested configurations. They use eglot for Emacs and LanguageClient-neovim.

Advanced Usage

If you are trying to automate the service in PowerShell, you can also run it under Start-Process to prevent hanging your script. It also gives you access to process automation features like $process.Close() or $process.Kill(). The Start-EditorServices.ps1 script takes many more optional arguments, but they no longer need to be specified.

$command = @(
    "$PSES_BUNDLE_PATH/PowerShellEditorServices/Start-EditorServices.ps1",
        "-BundledModulesPath $PSES_BUNDLE_PATH",
        "-LogPath ./logs",
        "-SessionDetailsPath ./session.json",
        "-FeatureFlags @()",
        "-AdditionalModules @()",
        "-HostName 'My Client'",
        "-HostProfileId 'myclient'",
        "-HostVersion 1.0.0",
        "-LogLevel Diagnostic"
) -join " "

$pwsh_arguments = "-NoLogo -NoProfile -Command $command"
$process = Start-Process pwsh -ArgumentList $arguments -PassThru
...
$process.Close(); #$process.Kill();

Once the command is run, PowerShell Editor Services will wait until the client connects to the named pipe. The session.json will contain the paths of the named pipes that you will connect to. There will be one you immediately connect to for Language Server Protocol messages, and once you connect to when you launch the debugger for Debug Adapter Protocol messages.

PowerShell Extension Terminal

image

The PowerShell Extension Terminal uses the host process' stdio streams for console input and output. Please note that this is mutually exclusive from using stdio for the Language Server Protocol messages.

If you want to take advantage of the PowerShell Extension Terminal, you must include the -EnableConsoleRepl switch when calling Start-EditorServices.ps1.

This is typically used if your client can create arbitrary terminals in the editor like below:

Extension Terminal in VS Code

The Visual Studio Code, Vim, and IntelliJ extensions currently use the PowerShell Extension Terminal.

Debugging

Debugging support is also exposed with PowerShell Editor Services. It is handled within the same process as the Language Server Protocol. This provides a more integrated experience for end users but is something to note as not many other language servers work in the same way. If you want to take advantage of debugging, your client must support the Debug Adapter Protocol. Your client should use the path to the debug named pipe found in the session.json file talked about above.

The debugging functionality in PowerShell Editor Services is available in the following editor extensions:

API Usage

Please note that we only consider the following as stable APIs that can be relied on:

  • Language Server Protocol connection
  • Debug Adapter Protocol connection
  • Start-up mechanism

The types of PowerShell Editor Services can change at any moment and should not be linked against in a production environment.

Development

NOTE: The easiest way to manually test changes you've made in PowerShellEditorServices is to follow the vscode-powershell development doc to get a local build of the VS Code extension to use your local build of PowerShellEditorServices.

1. Install PowerShell 7+

Install PowerShell 7+ with these instructions.

2. Clone the GitHub repository

git clone https://github.com/PowerShell/PowerShellEditorServices.git

3. Install Invoke-Build

Install-Module InvokeBuild -Scope CurrentUser
Install-Module platyPS -Scope CurrentUser

4. Delete NuGet.Config

Our NuGet configuration points to a private feed necessary for secure builds, and it must be committed to the repo as it is. The easiest way to build without access to that private feed is to delete the file:

Remove-Item NuGet.Config

Please be careful not to commit this change in a PR.

Now you're ready to build the code. You can do so in one of two ways:

Building the code from PowerShell

PS C:\path\to\PowerShellEditorServices> Invoke-Build Build

Building the code from Visual Studio Code

Open the PowerShellEditorServices folder that you cloned locally and press Ctrl+Shift+B (or Cmd+Shift+B on macOS).

Code of Conduct

Please see our Code of Conduct before participating in this project.

Contributions Welcome

We would love to incorporate community contributions into this project. If you would like to contribute code, documentation, tests, or bug reports, please read our Contribution Guide to learn more.

Security Note

For any security issues, please see here.

Maintainers

Emeriti

License

This project is licensed under the MIT License. Please see the third-party notices file for details on the third-party binaries that we include with releases of this project.

powershelleditorservices's People

Contributors

adamdriscoll avatar alexandair avatar andyleejordan avatar bergmeister avatar corbob avatar daviwil avatar dee-see avatar dependabot-preview[bot] avatar dependabot[bot] avatar dfinke avatar dkattan avatar fflaten avatar glennsarti avatar justingrote avatar justinytchen avatar kamilkosek avatar krishna-vutukuri avatar martingc94 avatar martinsgill avatar microsoft-github-policy-service[bot] avatar nohwnd avatar pinuke avatar powerschill avatar rjmholt avatar rkeithhill avatar seeminglyscience avatar stevel-msft avatar sydneyhsmith avatar tylerleonhardt avatar vors avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

powershelleditorservices's Issues

Variables in the debugger are not displayed if type is PSObject

I see this when debubbing a filter definition like:

filter foo {
    "`$_ is $_"
}

In the Variables window $_ shows up with no value. I believe the problem is that the code that checks to see if the variable is expandable returns true on primitives wrapped in a PSObject. I have a fix I'll submit in a few.

Variables display isn't showing contents for generic-based dictionaries

Specifically the $PSBoundParameters variable in Local scope of a function that specifies parameters. I've modified the code in VariableDetails.GetChildren like so:

else if (dictionary != null)
{
    var res1 = dictionary.OfType<object>().ToList();
    var res2 = dictionary.OfType<KeyValuePair<string,object>>().ToList();
    foreach (var item in dictionary)
    {
        if (item.GetType() != typeof(DictionaryEntry))
        {
            break;
        }

        var entry = (DictionaryEntry)item;
        childVariables.Add(
            new VariableDetails(
                entry.Key.ToString(),
                entry.Value));
    }
    //childVariables.AddRange(
    //    dictionary
    //        .OfType<DictionaryEntry>()
    //        .Select(e => new VariableDetails(e.Key.ToString(), e.Value)));
}

The commented out code at the bottom is the original code. The LINQ operator OfType returns nothing for PSBoundParametersDictionary because it derives from the generic type Dictionary<string, object>. I can see in res1 that the type of object enumerated using the OfType<> method is KeyValuePair<string,object>. However, if I just do the enumeration via a foreach loop, the item type works out to DictionaryEntry even for PSBoundParametersDictionary.

It is late and I'm not quite grokking how this is working and what the best way is to handle both IDictionary and IDictionary<TKey,TValue>. If anybody has any suggestions, let me know. At the very least, the code above (sans the res1/res2 experiment) is better than what we have right now.

Proposal: Symbol categories to surface for "Go to Symbol" feature

Visual Studio Code has two related features that this proposal addresses: Go to Symbol and Go to Symbol by Category accessed with Ctrl+Shift+O or Ctrl+P, @ and Ctrl+P, @:. For a JavaScript file, the follow symbols are shown:

image

And by category:

image

PowerShell Symbol Categories

For PowerShell, I propose the following categories:

  • functions (both simple and advanced as well as filters)
  • workflows
  • configurations
  • classes
  • properties (properties within classes)
  • methods (methods within classes)
  • enums
  • variables (script-scope only)

Filters could be a category but they are not significantly different from functions and with the advent of advanced functions, I don't believe filters are widely used anymore.

The JavaScript support only shows global variables - not function-scoped variables. I suggest that we only show script-scope variables as well (assuming this can be determined from the AST). If scope can't be determined from the AST then I propose not showing variables.

Add Result<T> pattern to wrap API responses

Instead of returning null from core API methods, a Result type should be returned which communicates the success or failure of the operation. If succeeded, the result value should be provided. Otherwise, error details should be provided.

Long-running requests should automatically cancel

A user may want to cancel a request if it is taking too long. The editor service should handle for that.

When making a reference request or a definition request, we should be able to cancel the request if another (reference or definition request) is made before the current one has finished. It may make sense to also do this for an occurrences request.

Improve parameter Intellisense sorting

Right now, command-specific parameters are mixed in with common parameters.

vscintellisense

Ideally, I'd love to see VSC support a tabbed Intellisense popup (like I've seen before in VS) where there would be two tabs as shown I the C# popup:

vsintellisense

Where the first tab would show the command-specific parameters and the second tab would show common parameters.

If VSC doesn't support this then I propose that the parameters are listed in the following order:

  • Sorted non-common parameters
  • Line separator (if possible)
  • Optional common parmeters
  • Line separator (if possible)
  • Sort common parameters

For example, this is an example of the above for Stop-Process:

Force
Id
InputObject
Name
PassThru
----
Confirm
WhatIf
----
Debug
ErrorAction
ErrorVariable
InformationAction
InformationVariable
OutBuffer
OutVariable
PipelineVariable
Verbose
WarningAction
WarningVariable

I think this order would be more useful to PowerShell scripters. Personally, I'm almost always more interested in the command-specific parameters.

Signature help request should be able to identify the parameter that the user is typing

In VS Code, the signature help window appears when the user is typing a command to give them hints about parameters.

image

It also has the ability to highlight the current parameter while the user is typing. The internal logic for finding parameter hints at the cursor should be able to determine which parameter the user is typing in and return its index so that the UI knows which parameter to highlight.

Account for function and variable scope in language service features

For the following intellisense features the entire file (or multiple files if dot sourced) is/are being searched through for variable and parameters:

  • Get definition
  • Get references
  • Get occurrences

We need fix this to use the correct scope when searching on variables and parameters.

High CPU usage in MessageReader.ReadNextChunk

Bug details from @rkeithhill:

It seems that the app is stuck in a loop in MessageReader here:

       private async Task<bool> ReadNextChunk()
        {
            // Do we need to resize the buffer?  See if less than 1/4 of the space is left.
            if (((double)(this.messageBuffer.Length - this.bufferEndOffset) / this.messageBuffer.Length) < 0.25)
            {
                // Double the size of the buffer
                Array.Resize(
                    ref this.messageBuffer, 
                    this.messageBuffer.Length * 2);
            }

            // Read the next chunk into the message buffer
            int readLength =
                await this.inputStream.ReadAsync(
                    this.messageBuffer,
                    this.bufferEndOffset,
                    this.messageBuffer.Length - this.bufferEndOffset);

            this.bufferEndOffset += readLength;

            return readLength >= 0;
        }

The ReadNextChunk() method hits the await inputStream.ReadAsync and returns immediately with 0 bytes read. Rinse and repeat and that is why, I think, the CPU usage is high. I wonder if the bool check above should be readLength > 0. If I set a conditional breakpoint on the that (readLength == 0), it only seems to break when I exit VS Code. And the docs on ReadAsync say this:

A task that represents the asynchronous read operation. The value of the TResult parameter contains the total number of bytes read into the buffer. The result value can be less than the number of bytes requested if the number of bytes currently available is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached.

If I change that line to readLength > 0, then VS Code seems to auto-complete cmdlet names just fine and on exit of VSC, the services will exit. However, if I have a debugger attached when VSC closes, I get an uncaught ArgumentNullException. I suspect that is because caller of ReadNextChunk() tries to have messageContent (which is null), parsed e.g.:

            // Return the parsed message
            return this.messageParser.ParseMessage(messageContent);

The last message it was processing I think is:

System.Text.Encoding.ASCII.GetString(this.messageBuffer)
"Content-Length: 158\r\n\r\n{\"seq\":1,\"type\":\"request\",\"command\":\"geterr\",\"arguments\":{\"delay\":750,\"files\":[\"c:/Users/hillr/.vscode/extensions/vscode-powershell/examples/DebugTest.ps1\"]}}58\r\n\r\n{\"seq\":1,\"type\":\"request\",\"command\":\"geterr\",\"arguments\":{\"delay\":750,\"files\":[\"c:/Users/hillr/.vscode/extensions/vscode-powershell/examples/DebugTest.ps1\"]}}\0\0\0\0…

While debugging, VariableDetails.GetChildren should be run on the pipeline thread

Keith uncovered this issue while debugging a script with the following lines:

    $p = Get-Process
    $ps0 = $p[0]

If you stop the debugger after the $ps0 line and hover the mouse cursor over that variable, the debug server will crash with the following stack trace:

    System.Management.Automation.GetValueInvocationException: Exception getting "Path": "There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type. The script block you attempted to invoke was: $this.Mainmodule.FileName" ---> System.Management.Automation.PSInvalidOperationException: There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type. The script block you attempted to invoke was: $this.Mainmodule.FileName
       at System.Management.Automation.ScriptBlock.GetContextFromTLS()
       at System.Management.Automation.ScriptBlock.InvokeWithPipe(Boolean useLocalScope, ErrorHandlingBehavior errorHandlingBehavior, Object dollarUnder, Object input, Object scriptThis, Pipe outputPipe, InvocationInfo invocationInfo, Boolean propagateAllExceptionsToTop, List`1 variablesToDefine, Dictionary`2 functionsToDefine, Object[] args)
       at System.Management.Automation.ScriptBlock.DoInvokeReturnAsIs(Boolean useLocalScope, ErrorHandlingBehavior errorHandlingBehavior, Object dollarUnder, Object input, Object scriptThis, Object[] args)
       at System.Management.Automation.PSScriptProperty.InvokeGetter(Object scriptThis)
       --- End of inner exception stack trace ---
       at System.Management.Automation.PSScriptProperty.InvokeGetter(Object scriptThis)
       at System.Management.Automation.PSScriptProperty.get_Value()
       at Microsoft.PowerShell.EditorServices.VariableDetails.<GetChildren>b__0(PSPropertyInfo p) in c:\dev\PowerShellEditorServices\src\PowerShellEditorServices\Debugging\VariableDetails.cs:line 189
       at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
       at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
       at Microsoft.PowerShell.EditorServices.VariableDetails.GetChildren(Object obj) in c:\dev\PowerShellEditorServices\src\PowerShellEditorServices\Debugging\VariableDetails.cs:line 186
       at Microsoft.PowerShell.EditorServices.VariableDetails.GetChildren() in c:\dev\PowerShellEditorServices\src\PowerShellEditorServices\Debugging\VariableDetails.cs:line 138
       at Microsoft.PowerShell.EditorServices.DebugService.GetVariables(Int32 variableReferenceId) in c:\dev\PowerShellEditorServices\src\PowerShellEditorServices\Debugging\DebugService.cs:line 167
       at Microsoft.PowerShell.EditorServices.Host.DebugAdapter.<HandleVariablesRequest>d__3c.MoveNext() in c:\dev\PowerShellEditorServices\src\PowerShellEditorServices.Host\DebugAdapter.cs:line 313

It seems like VariableDetails.GetChildren needs to be run on the debugger pipeline thread to ensure that any runspace operations are run in the correct context.

VSC always allows intellisense features to be selected in right-click menu

When you right-click in a PowerShell file in VSC a menu comes up with the following intellisense options:

  • Peek Definition
  • Go to Definition
  • Find All References
  • Change All Occurrences

Currently, these are always clickable, which is wrong. We only want these options to be clickable on certain symbols. For example, none of these options should be clickable on function or any whitespace in the following code block:

function MyFunction ($param1, $param2)
{

}

When should the UI window for a parameter hint terminate?

Right now when showing PowerShell signature sets the UI window only shows for a small text region. Specifically, it shows from Cmd-Name t where t is the trigger character. As long as the cursor is in the region of the command to where the trigger character is the UI will display parameter hints. If the cursor is before or after that the window goes away.

This is happening because of what the ApplicableSpan is being set as in SignatureHelpResponse.cs

We need to decide what an appropriate span for parameter hints on signature sets is.

Using IntelliSense on parameter attributes causes the brackets to be removed

From Norberto:

Using IntelliSense to complete the 'hash' attribute to 'hashtable'

function Add-DscEncyptionCertificate 
{
    [CmdletBinding()]
    param (
        [AllowNull()]
        [AllowEmptyString()]
        [Parameter()] 
        [hash] $ConfigurationDataPath,

Gives this

function Add-DscEncyptionCertificate 
{
    [CmdletBinding()]
    param (
        [AllowNull()]
        [AllowEmptyString()]
        [Parameter()]
        hashtable $ConfigurationDataPath,

Shouldn’t remove the [], if possible

Don't need to ship vshost binaries

The following vshost files are in the extension's bin dir:

Microsoft.PowerShell.EditorServices.Host.vshost.exe
Microsoft.PowerShell.EditorServices.Host.vshost.exe.config
Microsoft.PowerShell.EditorServices.Host.vshost.exe.manifest

No need to ship these. Could say the same for the XML (doc comment) files as well.

Investigate intermittent test hangs on AppVeyor

Tests are randomly hanging on AppVeyor. This happens both for library and host tests. I still haven't uncovered the root of the issue yet but you can usually get past the issue by re-running the tests. This isn't a long-term solution so we'll have to uncover the root cause soon.

Variables that have changed since previous debug stop should be displayed with a "dirty" indicator

This is borrowing another page from Visual Studio where it shows the value of a variable in a different color when the variable's value has changed since the previous debug stop e.g.:
localvariabledirty
Doing this requires at least two new capabilities. First we have to keep the previous debug stop's top level set of variables. From what I can tell this feature doesn't need to support change indicator for when a composite variables properties or fields have changed. Second, we need a way to compare objects (or determine that we can't compare objects).

VSCode limits us in what we can display in the Variables tool window. It doesn't let us color text for a specific variable value so we can't do it exactly like Visual Studio. The current proposal is to display a changed variable like so:

Locals
  $i: 1 *

Message payload types should be registered with MessageParser automatically

Right now all message types in Transport.Stdio have to be manually added to a dictionary in the MessageParser class before they can be deserialized correctly. A developer also has to set the right payload type name in their Request/Response/Event class' constructor which is quite easy to forget.

A better solution would be to have an attribute on message classes that defines the payload type name and then use reflection to scan for message types with this attribute. Message types that don't have the attribute should raise an assert in debug builds so that the developer knows that they need to add it. This should solve both problems.

Include a .NET Client Class

I'm finding it cumbersome to consume the message classes in PoshTools. It seems like there is a lot of plumbing that needs to go on to use them correctly.

The LanguageServiceManager in the Host test project is a good example of how to host the process from .NET but it looks like there is a lot of work to do actually serialize the messages correctly and wait on them etc. I've been primarily looking at ScenarioTest for examples.

I think it would be really neat to expose the same model found in the PowerShellEditorServices namespace through an abstracted namedspace in something like PowerShellEditorServices.Client. It would function similar to a WCF data context. We could then call methods like:

 PowerShellClient.LanguageService.GetCompletionsInFile()

Rather than attempting to format the message classes correctly.

        await this.SendOpenFileEvent("TestFiles\\CompleteFunctionName.ps1");

        CompletionItem[] completions =
            await this.SendRequest(
                CompletionRequest.Type,
                new TextDocumentPosition
                {
                    Uri = "TestFiles\\CompleteFunctionName.ps1",
                    Position = new Position
                    {
                        Line = 5,
                        Character = 8
                    }
                });

I realize that the idea behind the non-.NET interface is so that any editor can interact with it but I figure that there are quite a few .NET editors out there that would benefit from this.

Display variable's type name

Visual Studio uses a three column grid to display a variable's name, value and type name. In VSCode we have a tree view and the tool window it is in is typically narrow. It is common to display variable name followed by its value. However PowerShell quite often has objects (composite variables) where the current variables display shows no value for objects.

I propose is that for expandable objects, we take advantage of the opportunity to display something. One option is the variable's short type name e.g.:

vscodevariabletypenames1

Of course since we've chosen to prefix variables with $ then perhaps we display type names using PowerShell syntax:
vscodevariabletypenames2

Another variation is to test the expandable object's ToString() return and if it is different than the type name, then display that instead. Here's an example of where this is useful. I can see the useful value of $pwd without having to open the node:
vscodevariabletypenames3
In this case, I think seeing the path (ToString() value) is more useful than seeing [PathInfo].

What are your thoughts on this? The implementation is a trivial change to VariableDetails.cs. Personally, I'm leaning towards the last example of using either the type name or ToString() if it is different.

Filter and/or organize variables displayed during debugging

Right now the variables displayed in Global, Script and stack frame local scope are simply every single variable returned by:

Get-Variable -Scope <Global | Script | <stack frame #>>

While this returns variables the user has defined, it also returns many, many built-in variables including predefined constants like $null, $true and $false in addition to SessionStateCapacityVariables like MaximumAliasCount, MaximumDriveCount, etc even in local scope e.g.:

Name                
----                
?                   
args                
ConsoleFileName     
ExecutionContext    
false               
HOME                
Host                
i <== assigned in the function              
input               
itemCount <== assigned in the function       
MaximumAliasCount   
MaximumDriveCount   
MaximumErrorCount   
MaximumFunctionCount
MaximumVariableCount
MyInvocation        
null                
PID                 
PSBoundParameters   
PSCommandPath       
PSCulture           
PSDebugContext      
PSHOME              
PSScriptRoot        
PSUICulture         
PSVersionTable      
ShellId             
true                

So here you have two variables that are very likely to be the most interesting to you in the Local scope but you have to pick through 28 variables. That is not a great user experience.

I want to determine a way to organize this information (set of variables) into a more usable structure. VSCode offers up a tree view to display nodes - whether those are containers like Global, Script, Local or variables where variables can be a primitive variable such as a bool, or a composite variable such as a Process object.

One approach I'm leaning towards is adding a top level folder called Auto that acts like the C#/VS auto debug variables window. This would attempt to show just the two variables that you have assigned locally.

Another approach is to just filter the Local list of variables. This can be done partly by inspecting the type returned by the call to Get-Variable e.g.:
variabletypes
It would be pretty simple to filter out the NullVariable ($null).

Variables also have an Options property with values like ReadOnly, Constant, AllScope.

SessionStateCapacityVariable typed variables are rarely of interest but I wouldn't say never of interest. These are typically set by a profile (if modified at all). One way to handle these is only show them in a scope other than Global when they have a different value than their Global counterpart. Another way is to create a tree node under Local called something like System and "hide" these less interesting variables there.

All of these approaches require that we somehow know how to distinguish between PowerShell-defined and user-defined variables. I can filter somewhat based on type as shown above. Still, there are a lot of PSVariable typed variables that are PowerShell-defined. I hate the idea of maintaining a static list of built-in variable names. Does that list exist somewhere in S.M.A.dll?

I guess we could check if the variable name appears in the Global scope and only display it again if the value is different. However comparing values is tricky because not all objects support equality comparison ($Host comes to mind). Value comparison will also come into play for another proposal - indicating dirty variables (variables whose values have changed since the last debug stop).

Sorry for the stream of consciousness post here but I wanted to get some of these ideas down.

Implement support for data tips while debugging

I see that JavaScript debugging supports data tips (tooltip that pops up when you hover over a variable and shows its value). This would be nice to have for PowerShell debugging at some point.

Add new 'context' parameter to EvaluateRequest

The VS Code team has added a new 'context' parameter to the EvaluateRequest which allows us to determine whether the request comes from the watch window or the REPL. This makes it easier to know whether a request should be evaluated against the current variables list or executed against the runspace.

microsoft/vscode#252

We'll need to add this parameter at some point before the next VS Code release.

Async unit tests don't rethrow Asserts correctly

For some reason when I use xUnit Assert methods in async helper methods (see DebugServiceTests.AssertStateChange for instance), the exceptions thrown by the asserts aren't bubbled back up to xUnit so the tests just hang. For now these assert statements have been commented out. Need to figure out how to make them work correctly to validate behavior appropriately.

Clear-Host causes error while debugging

Phil reports that he sees this exception when he has Clear-Host in a script and executes it in the debugger:

Exception setting "CursorPosition": "The method or operation is not 
implemented."

Seems like there's a method I need to implement in PSHostUserInterface or PSRawHostUserInterface.

Add proper variable scoping support in the DebugService

Currently the DebugService only gathers variables from the local scope when the debugger stops. Ideally all available variable scopes should be provided, both the global scope and all dynamic scopes in between that and the local scope. Non-local scopes should not be expanded by default though. The debugger client will ask for a given scope to be expanded when necessary.

Add language feature support for classes

The following features do not work when inside a class:

  • Parameter hints
  • Get Definition
  • Get References
  • Change Occurrences
  • Symbol search

When outside of a class all features work normally.

Add signature help support for .NET methods

C# methods are using the suggestions documentation window to show parameter hints (see below).
csharpbug.

The information contained this suggestion detail needs to be used in parameter hints. Currently the parameter hints UI does not display for c# methods.

Untitled and in-memory PowerShell files should have language features enabled

A user reported a crash of the language service with the following log output:

11/18/2015 11:09:48 AM [VERBOSE] - Method "ReadMessage" at line 111 of c:\projects\powershelleditorservices\src\PowerShellEditorServices.Protocol\MessageProtocol\MessageReader.cs

    READ MESSAGE:

    {
      "jsonrpc": "2.0",
      "method": "textDocument/didOpen",
      "params": {
        "uri": "inmemory://model/3",
        "text": " ... file contents ..."
      }
    }

11/18/2015 11:09:48 AM [VERBOSE] - Method "ResolveFilePath" at line 197 of c:\projects\powershelleditorservices\src\PowerShellEditorServices\Workspace\Workspace.cs

    Resolved path: inmemory:\\model\3

11/18/2015 11:09:48 AM [ERROR] - Method "CurrentDomain_UnhandledException" at line 79 of c:\projects\powershelleditorservices\src\PowerShellEditorServices.Host\Program.cs

    FATAL UNHANDLED EXCEPTION:

    System.NotSupportedException: The given path's format is not supported.
       at System.Security.Permissions.FileIOPermission.QuickDemand(FileIOPermissionAccess access, String fullPath, Boolean checkForDuplicates, Boolean needFullPath)
       at Microsoft.PowerShell.EditorServices.Workspace.ResolveFilePath(String filePath)
       at Microsoft.PowerShell.EditorServices.Workspace.GetFileBuffer(String filePath, String initialBuffer)
       at Microsoft.PowerShell.EditorServices.Host.LanguageServer.HandleDidOpenTextDocumentNotification(DidOpenTextDocumentNotification openParams, EditorSession editorSession, EventContext eventContext)
       at Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.MessageDispatcher`1.<>c__DisplayClass4`1.<AddEventHandler>b__3(Message eventMessage, TSession session, MessageWriter messageWriter)
       at Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.MessageDispatcher`1.<DispatchMessage>d__6.MoveNext()

It appears that VS Code sent an invalid file path to the language service: inmemory://model/3. Not sure what would cause it to send this path but the language service should be able to gracefully reject invalid file paths.

Variables retrieved from the DebugService should use proper '$' notation

This issue causes top-level variables retrieved from the DebugService to not have the dollar sign '$' prefix, causing some confusion when they are seen in a debugger client. The fix will cause the dollar sign prefix to be introduced to top-level variables, i.e. those that are obtained from calling "Get-Variable". Children of those top-level variables do not need this prefix.

Variables should reflect changes made by the user in the debug console

If the user modifies, add or removes a variable in the debug console during a debug stop, that should be reflected after execution of their command.

We will probably need to re-fetch the variables as we can't rely on examining the executed expression for simple variable assignment. The user could be using Add/Clear/Remove/Set-Variable or they may be manipulating the variable drive.

VSC doesn't handle code completion for files and filepaths correctly

The wordDefinition property in VSC includes the "." and "" characters as word separators. This makes the completion suggestions complete incorrectly.

For example, since any suggestion with "" or "." is not a word the following problems will happen:
Type 'notepa' and choose 'notepad.exe' and it will complete as 'notepad.exe'. However if you type 'notepad.e' and choose 'notepad.exe' and it will complete as 'notepad.notepad.exe'.

Or

Type 'C:' and there will be a list of paths to choose from, but if you keep typing '_C:_' the examples go away. If you still continue to type a real path like 'C:\Use' the suggestion 'C:\Users' will come up but if you choose it will complete as C:\C:\Users.

Add prompt string support for REPL

The Editor Services REPL should provide prompt string, much like what you see in the ISE today:

PS C:\Users\daviwil>

This prompt gets updated based on the state of the user's session. For example, when script execution breaks into the debugger, the prompt gets a [DBG] prefix:

[DBG]: PS C:\Users\daviwil>>

The prompt implementation should also support custom user prompts my invoking the prompt function to get the user's prompt string.

Introduce editor command extensibility model

I would like to enable PowerShell modules to provide new commands and behaviors
to any editor that uses PowerShell Editor Services. Instead of learning how to
write an extension for a specific editor, a user should be able to use a simple,
PowerShell-based API to implement their desired feature once and have it be
available anywhere Editor Services is used.

I thought it might be nice to gather some feedback on my current thinking before
I get too deep into the implementation. Please let me know if I'm on the right
track here and feel free to suggest any other ideas you may have. I'll be updating
the text of this issue to capture any ideas or feedback given in the comments
section below.

Overall Design

Extensions will be written either in PowerShell code or as binary modules in
C#. These extensions could even come from simple one-liner code in the
user's profile or be written on the fly in the editor's REPL. The model
should allow extensions to be developed in real-time, easily being
re-registered when the user evaluates their extension code in the editor.

Extensions will be hosted by an "extension service" (similar to the language
and debugging services). For now it is assumed that the extension service
will have its own local runspace which will not follow the session changes
in the user's REPL runspace. This means that all extension commands would
run locally, but in some cases may be able to affect the user's current
session whether it is local or remote.

Generally, extension modules will be installed via the PowerShell Gallery.
If we decide to make the PowerShellEditorServices module installable via
the Gallery this would allow extension modules to take a dependency on the
version of the PowerShellEditorServices module that they support.

In our editor integrations, we will provide commands which will make it easy
to locate and install editor extensions and enable them for use immediately.

Command Types

Here's the proposed list of extension command types. Please feel free to
suggest more!

Action

An Action command represents some operation that can be executed in the user's
local PowerShell session. This would most commonly be some kind of script
that the user has created to boost their productivity or automate some process.
Exposing this type of action as a command could allow the user to bind the action
to a hotkey in their editor for quick execution.

Actions may also need access to the current runspace of the user's REPL session
so that commands can be executed there. There would need to be some way for
the command to choose to execute its action locally or inside of the user's
current session. More use case ideas would be helpful here.

Actions will be registered as individual commands with the editor so that
the user can bind them to hotkeys and launch them on demand.

Analyzer

An Analyzer command provides a function or ScriptBlock which can be invoked to
analyze a ScriptAst and return diagnostic markers that will be displayed in
the user's editor. These diagnostics can also provide "quick fix" actions which
may cause the code to be changed or to run operations in the user's current session.

One obvious use case is PowerShell Script Analyzer. Currently we are bundling
Script Analyzer with PowerShell Editor Services but I'd like to move away from
this. Using the the user's installed Script Analyzer module will allow the them
to freely update Script Analyzer outside of the Editor Services ship cycle. It
would also simplify the direct dependencies and deployment of PowerShell Editor
Services and its various editor integrations.

Another use case would be custom script analysis which doesn't fall under the
umbrella of Script Analyzer. One could write an analyzer extension which identifies
cmdlets that exist in PS Gallery modules that are not yet installed and give the user
a quick fix action to install the necessary modules.

The process of applying registered analyzers will be as follows:

  1. User makes a change in a script file
  2. Language service parses the script into a ScriptAst
  3. Extension service loops through registered analyzers and invokes them,
    aggregating the returned markers into a single list.
  4. The aggregated marker list is returned to the editor to be displayed
  5. If the user starts editing the file again while the analyzers are running,
    cancel the current analyzer run and start the delay for a new one.

Formatter

A Formatter command provides a function or ScriptBlock that can be invoked to
reformat or insert new text into the user's script file. This could be used
for general code style formatting tools or for operations like Doug's "Expand
Aliases" command which is currently shipped as a built-in action.

One use case I can think of here would be to provide smarter snippets. For
example, an extension could provide a "create cmdlet help" formatter which
analyzes the cmdlet's AST and generates a help comment which includes all of
the parameters and a good starting point for the documentation for each.

Formatters will be registered as individual commands with the editor so that
the user can bind them to hotkeys and launch them on demand.

Extension API

Register-EditorCommand

This command allows an extension to register itself so that it can be made
available to the user in their editor. There will be a unique paramset for
each command type which will expose the necessary configuration parameters
for that type.

Here are the currently proposed signatures:

Action

Register-EditorCommand -Name <String> -Action <Object>

The -Action parameter will accept a command, function, or ScriptBlock which
accepts the user's current Runspace. Other contextual information may need
to be provided as parameters, would love feedback on what might be useful.

Analyzer

Register-EditorCommand -Name <String> -Analyzer <Object> -RunAutomatically <SwitchParameter>

The -Analyzer parameter will accept a command, function, or ScriptBlock which
accepts the file path and ScriptAst of the currently edited file. The command
should return a list of diagnostic objects with the following information:

  • Range - The coordinate range in the file where the marker applies
  • Level - The level of the marker, one of "Error", "Warning", "Information", "Hint"
  • Action - The "quick fix" action for this marker

The -RunAutomatically parameter denotes whether the analyzer should be run
automatically when the user changes a file or if it should be invoked manually
by the user. Is this helpful, or should we just simplify it and have every
analyzer be run automatically?

Formatter

Register-EditorCommand -Name <String> -Formatter <Object>

The -Formatter parameter will accept a command, function, or ScriptBlock which
accepts the file path and ScriptAst of the currently edited file and also an
object that contains the coordinates of the user's selection range in the file
if applicable. The Formatter would then process the ScriptAst and return a
new ScriptAst after the formatting changes have been applied. We may also
allow a string to be returned.

Get-EditorCommand

Enumerates registered editor commands. Used primarily by the extension service
to get the list of registered commands after the user's profile has been loaded.

Extension Lifecycle

  1. Editor launches a new session in PowerShell Editor Services and loads the
    PowerShellEditorServices module (which contains the API cmdlets) into the
    local runspace.
  2. The extension service then loads the user's profile which may load editor
    extension modules. (We may want to have an editor-specific profile for this case)
  3. When editor extension modules are loaded, they register their commands by calling
    the Register-EditorCommand method.
  4. Once profile loading is complete, the extension service calls Get-EditorCommand
    to get the list of registered commands.
  5. Once the Editor Services host is fully initialized, the editor will request the
    list of extension commands and then make them available to the user.
  6. During their session, the user may write or install new extensions. When the
    Register-EditorCommand command is invoked, these new extension commands will
    be loaded and a notification will be sent through Editor Services back to the
    editor to make the new command available to the user.

Questions for Feedback

  1. Does the PowerShellEditorServices module ship with the host/library or do we use
    a module installed via the gallery? The former open simplifies distribution and
    ensures that the right module version will be used. However, the latter provides
    the opportunity for editor extension authors to write Pester tests for their
    extension against the real module API. Would love some feedback on this issue.
  2. Where should the extension code run? In the language service host process or in
    a separate instance of the host process that is focused purely on extensions? The
    former approach would be the most straightforward but the latter would allow general
    extensibility of the user's editor through PowerShell. However, in the latter case
    we would only make Action commands available in non-PowerShell files since we don't
    have ScriptAst types for other languages. Thoughts?
  3. Will it be difficult for extension authors if we target PowerShell v3 as a baseline
    but require support for v4 and v5 as well? I would like for Editor Services to work
    on machines that only have v3 or v4 so this requires that extensions either be
    version-specific or version-independent.
  4. Is there any benefit to writing the API cmdlets as a C#-based module rather than
    in pure PowerShell code (or vide versa)? One potential benefit of a C#-based module
    is that there might be a way to expose events to the extension service so that it
    can be notified when new commands are registered.
  5. Should there be a separate runspace in which analyzers will be run? My concern is
    that a greedy analyzer will cause other extension commands to stop executing.

/cc @dfinke @adamdriscoll @Jaykul @rkeithhill

Publish to NuGet

I know this is likely planned for this library but I figure it should be documented!

Would love to see this on the NuGet gallery so I can just pull it into my projects.

Language service should be able to find variable references inside of strings

This affects references/occurrences finding and variable renaming. An example from Kirk:

function Write-Item($itemCount)
{
    $i = 1

    while ($i -le $itemCount)
    {
        $str = "Output ${i}"
        Write-Output $str
        $i = $i + 1
    }
}

If you attempt to rename the variable $i, the instance inside of the output string does not get renamed.

Add go to definition support for dot sourced file paths

When peek definition or go to definition are used on a dot sourced file nothing happens. This is currently the same behavior that happens on built-in commands where the source file can't be found.

Should peek and go to definition bring the user to the very beginning of the dot sourced file when those features are used?

Support for Bitness Selection

The ISE and PoshTools allows you to select the bitness of the PowerShell host. PSES should probably build the host in two different configurations so that this is possible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.