Skip to content

Introduce editor command extensibility model #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
daviwil opened this issue Dec 17, 2015 · 5 comments
Closed

Introduce editor command extensibility model #81

daviwil opened this issue Dec 17, 2015 · 5 comments
Labels
Issue-Discussion Let's talk about it. Issue-Enhancement A feature request (enhancement).

Comments

@daviwil
Copy link
Contributor

daviwil commented Dec 17, 2015

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

@daviwil daviwil added Issue-Enhancement A feature request (enhancement). Issue-Discussion Let's talk about it. labels Dec 17, 2015
@ChrisWarwick
Copy link

Great idea!

Nothing adds more to an editor environment than full programmability. I would strongly support this suggestion and would contribute enthusiastically :-)

One of the major omissions, in my opinion, from Windows systems is a proper programmable editor. There are some good editors about, and some ports of other well-known editor from other operating systems (VIM et al), but native Windows editors all lack programmability – which is a real shame – and something this proposal could address in the VSCode editor. C# is good. PowerShell would be much better.

ISE includes an object model (in the $PsISE variable) – but is very limited in terms of actually controlling the editor. IseSteroids has some very basic programmability but this is limited to some very specific use cases and is far from being a general purpose facility.

Adding programmability to VsCode would ideally allow arbitrary scripts/macros to operate on the editor or its contents. While I agree that Action/Analyzer/Formatter type operations are a good start I would hope that the interface would be entirely general (i.e. having specific cmdlets for these particular operations may be inherently limiting?)

I’d strongly suggest you spend a while looking at what’s gone before – as this illustrates what can be achieved. Here are a few thoughts:

Emacs and Vi are macro editors – in the sense that a scripting language can be seamlessly used to control not only the content of the file being edited, but also the appearance and operation of the editor itself. It’s been many, many years since I was competent with Vi (and I never learned much Emacs), so I won’t dwell on these…

Xedit was the IBM VM/CMS editor (and, I believe is still the system editor on z/VM). This editor is programmable with Rexx (scripting language). The editor exposes almost all its internal state to the Rexx interface (see references/links below). Xedit spawned many third-party clones – for example, Kedit (www.kedit.com) for PCs. On the Amiga (age alert!) there was a great editor called CygnusEd (http://www.vesalia.de/e_cygnused.htm) that was programmable via Rexx (in this case ARexx) in the same way.

There’s an open-source Windows version of Xedit/Kedit, called THE (http://hessling-editor.sourceforge.net/) with a lot of documentation that gives a good idea of what’s possible (http://hessling-editor.sourceforge.net/doc/index.html).

Although this stuff is old it still illustrates the benefits that programmability can offer. Publishing macros on the PS Gallery is another great idea. A similar old-fashioned resource is available for THE here: http://www.gut-wirtz.de/THE/contrib/index.html. Some good example macros/scripts (in Rexx) would be:
http://www.gut-wirtz.de/THE/contrib/wirtz/allx.the
http://www.gut-wirtz.de/THE/contrib/ringhofer/formate.the
http://www.gut-wirtz.de/THE/contrib/deschner/flow.the

Let me know if I can help or if you’d like more musings!

Regards,
Chris

@daviwil
Copy link
Contributor Author

daviwil commented Dec 17, 2015

Thanks for the great feedback Chris! I am a long time Vim and Emacs user so I definitely understand the value of full programmability :) The current proposal only addresses the types of extensibility that are fairly easy to provide across different editors. My ultimate goal is to provide a more robust automation model.

The next phase would be providing more cmdlets that will allow you to automate tasks in the editor, much like what you're suggesting here. One obvious feature would be enumerating and executing the editor's own commands. Another would be manipulating the open buffers and frames in the editor's window. Some automation points may only be available in certain editors, so I was going to provide some form of "capabilities" model where certain parts of the API were turned on/off based on what the user's editor provides.

Are there particular automation points in the editors that youv'e used which have been particularly helpful?

@Jaykul
Copy link

Jaykul commented Dec 17, 2015

Does the PowerShellEditorServices module ship with the host/library or do we use
a module installed via the gallery?

I don't think how the module ships matters to Pester testing (except in the sense that the module will be harder to find if it's in a random location). If I install PSES in multiple editors, do I end up with multiple copies (potentially at different versions, depending on the maintainer)? That would indicate you need to keep the module with the library.

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?

I'm not sure this matters very much. It's unlikely people will write extensions for PSES that are valuable in other languages (I think), since all of the editors you're tackling are inherently extensible already, so I wouldn't worry about that aspect. The only real downside of having it run in the language host would be polluting the name/variable space there during development, or crashing the whole thing...

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.

How is this question different than the previous? Are you contemplating running analyzer extensions separate from others because they'll potentially run in "real time"? Would you spin up a runspace for each analyzer plugin?

Will it be difficult for extension authors if we target PowerShell v3 as a baseline
but require support for v4 and v5 as well?

Currently supporting 3-5 isn't too hard. It just means that you have to work at the v3 language level. I wouldn't worry about this -- document that the core works to v3, and suggest extensions should too, you can't force the issue.

However, it's completely impossible to test backwards compatibility without a v3 machine, since there's no -version 3 switch for PowerShell, and no SxS support. (queue rant 😠).

Is there any benefit to writing the API cmdlets as a C#-based module rather than
in pure PowerShell code (or vide versa)?

There are still lots of things C# can do that PowerShell can't, and still more that are incredibly hard or complex to do in PowerShell (especially in a way that's backwards compatible to v3). Threading, events, classes, generic methods ...

@daviwil
Copy link
Contributor Author

daviwil commented Dec 17, 2015

I don't think how the module ships matters to Pester testing (except in the sense that the module will be harder to find if it's in a random location).

It matters if an extension author wants to be able to run tests on a CI server without installing one of the editors and its PowerShell extension. Though I don't know how many people would end up writing CI tests for their interface to the extension API itself. Maybe this doesn't matter so much until people start asking about it.

If I install PSES in multiple editors, do I end up with multiple copies (potentially at different versions, depending on the maintainer)?

That's true, different editor integrations could be using different versions of PSES. I believe PSGallery can install multiple versions of the same module though, so shipping from the Gallery could still be an option in this case.

I'm not sure this matters very much. It's unlikely people will write extensions for PSES that are valuable in other languages (I think) ... The only real downside of having it run in the language host would be polluting the name/variable space there during development, or crashing the whole thing...

For the first point, I agree when it comes to the currently proposed extensibility types. However, the PowerShell-based extensibility model becomes more generally useful when I add more commands for automating things inside of the host editor. I'm not exactly sure what this entails yet, but I'm trying to plan ahead a bit incase we can do more cool stuff.

The second point is one of the reasons why I'm considering having a separate host. The extensions shouldn't affect the name/variable space of the user's session. I'm also trying to make sure that the editor experience stays quick for things like IntelliSense so keeping extension commands and analyzers in a separate runspace (at least) should allow the mainline features to continue to run smoothly.

Currently supporting 3-5 isn't too hard. It just means that you have to work at the v3 language level. I wouldn't worry about this -- document that the core works to v3, and suggest extensions should too, you can't force the issue.

Agreed.

However, it's completely impossible to test backwards compatibility without a v3 machine

Yep, I'm going to set up v3/v4 build servers at some point :)

There are still lots of things C# can do that PowerShell can't, and still more that are incredibly hard or complex to do in PowerShell

I'm leaning more toward writing the core extension API code as C# cmdlets to make it easier for the extension service to hook into it.

Thanks for the feedback!

@daviwil daviwil modified the milestone: Backlog Dec 31, 2015
@daviwil
Copy link
Contributor Author

daviwil commented May 18, 2016

This was shipped in the recent 0.6.0 with some modifications from the original design discussed here. For more information check out the documentation page:

http://powershell.github.io/PowerShellEditorServices/guide/extensions.html

@daviwil daviwil closed this as completed May 18, 2016
@daviwil daviwil modified the milestone: Backlog May 24, 2017
TylerLeonhardt pushed a commit to TylerLeonhardt/PowerShellEditorServices that referenced this issue Feb 26, 2019
…l-snippets

More work on issue PowerShell#63, snippet improvements.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Discussion Let's talk about it. Issue-Enhancement A feature request (enhancement).
Projects
None yet
Development

No branches or pull requests

3 participants