|
| 1 | +--- |
| 2 | +RFC: RFC |
| 3 | +Author: Dongbo Wang |
| 4 | +Status: Withdrawn |
| 5 | +SupercededBy: N/A |
| 6 | +Version: 1.0 |
| 7 | +Area: Engine |
| 8 | +Comments Due: April 30th, 2018 |
| 9 | +Plan to implement: No |
| 10 | +--- |
| 11 | + |
| 12 | +# Command-line Suggestion Infrastructure |
| 13 | + |
| 14 | +This RFC is to propose a design of command-line suggestion infrastructure in PowerShell engine. |
| 15 | + |
| 16 | +## Motivation |
| 17 | + |
| 18 | +[PowerShell in Azure Cloud Shell](https://docs.microsoft.com/en-us/azure/cloud-shell/overview) targets a very specific PowerShell use scenario -- Azure resource management. |
| 19 | +It is an Azure service, which means it is possible to collect telemetry about how people use the shell. Given these two facts, |
| 20 | +it is interesting to see whether ML can be leveraged to study the telemetry data within the targeted scenario, |
| 21 | +and provide useful command-line suggestions in the cloud shell based on the user input. |
| 22 | + |
| 23 | +It should be pointed out that, the command-line suggestion is different from tab completion in both purpose and its inner working. |
| 24 | +From the perspective of purpose, |
| 25 | +tab completion targets a specific syntactic part of the typed input, such as a parameter name or a member, |
| 26 | +while command-line suggestion targets the whole input string and gives suggestions like how a search engine reacts to the user input. |
| 27 | +From the perspective of inner working, |
| 28 | +tab completion mainly depend on inference within the runtime, |
| 29 | +while command-line suggestion depends on studying the pattern of the history inputs. |
| 30 | + |
| 31 | +Of course, in order for PowerShell cloud shell to do this, basic infrastructure support is required in both the PowerShell engine and host. |
| 32 | +PowerShell itself should make it easy to plug in a suggestion engine via a module, as well as present the suggestion results to the user. |
| 33 | +With this support, the cloud shell can focus on the design of the suggestion engine, |
| 34 | +and enable it in the could environment by simply loading the suggestion engine as a module. |
| 35 | + |
| 36 | +## Specification |
| 37 | + |
| 38 | +### Interface with Suggestion Engine |
| 39 | + |
| 40 | +The proposal is to have a similar infrastructure as the tab completion today. |
| 41 | +A cmdlet named `CommandLineSuggestion` will be used as the interface between PowerShell and a suggestion engine. |
| 42 | +The suggestion engine could be completely PowerShell-agnostic. |
| 43 | +To turn it into a module that works with PowerShell, |
| 44 | +you just need to have the cmdlet `CommandLineSuggestion` as a thin wrapper over its suggestion APIs. |
| 45 | + |
| 46 | +The syntax of `CommandLineSuggestion` looks as follows: |
| 47 | + |
| 48 | +```powershell |
| 49 | +CommandLineSuggestion [-InputScript] <string> [-HistoryInput] <string[]> [-Async] [<CommonParameters>] |
| 50 | +``` |
| 51 | + |
| 52 | +- The parameter `-InputScript` is mandatory, which takes the user input for suggestions. |
| 53 | +- The parameter `-HistoryInput` can be mandatory or optional, which takes the relevant history inputs as a context for the suggestion. |
| 54 | + Given the privacy concerns, only the history from the current local/remote session is passed to the cmdlet by PowerShell, |
| 55 | + which can be retrieved easily with `Get-History`. |
| 56 | + This may seem no much value due to the limitation of the data set, |
| 57 | + but it won't be a problem in cloud shell as the telemetry will provide much richer context for the suggestion engine to work. |
| 58 | + From that perspective, we can see the history context is not necessarily needed by all suggestion engines, |
| 59 | + and therefore, a suggestion engine module can indicate whether or not it needs this information by making `-HistoryInput` mandatory or optional. |
| 60 | + In case `-HistoryInput` is optional, PowerShell won't bother to collect the history inputs. |
| 61 | +- The parameter `Async` is optional, which instructs the cmdlet to run synchronously or asynchronously. |
| 62 | + When running synchronously, the cmdlet returns a collection of strings as the suggestion results. |
| 63 | + When running asynchronously, the cmdlet returns a `Task<Collection<string>>` that represents a already started asynchronous operation. |
| 64 | + |
| 65 | +When running in a remote session, the tab completion calls `TabExpansion2` in the remote Runspace to get the completion results. |
| 66 | +This is necessary for tab completion because inference needs to happen in the right context. |
| 67 | +However, this is not necessary for command-line suggestion. |
| 68 | +`CommandLineSuggestion` will always be invoked from the default local Runspace (`[runspace]::DefaultRunspace`), |
| 69 | +so the task-based asynchronous behavior of the cmdlet is guaranteed to work properly |
| 70 | +(serialization and de-serialization would break the async behavior if invoking the cmdlet in a remote Runspace). |
| 71 | +When `-HistoryInput` is mandatory, PowerShell will collect the history from the default local Runspace and the remote Runspace (if there is one), |
| 72 | +and then pass the history context along when invoking `CommandLineSuggestion`. |
| 73 | + |
| 74 | +### Interface with UI Component |
| 75 | + |
| 76 | +Two static method will be exposed to interact with the UI component. They look as follows: |
| 77 | + |
| 78 | +```c# |
| 79 | +public static Collection<string> SuggestInput(string input, powershell powershell); |
| 80 | +public static Task<Collection<string>> SuggestInput(string input, powershell powershell); |
| 81 | +``` |
| 82 | + |
| 83 | +- The first method is synchronous, where `CommandLineSuggestion` is invoked without `-Async`. |
| 84 | +- The second method is asynchronous, where `CommandLineSuggestion` is invoked with `-Async`. |
| 85 | + PowerShell will make sure the `Task<Collection<string>>` object is started before returning it from this method |
| 86 | + (`CommandLineSuggestion` should have started the task before writing it out). |
| 87 | +- The `powershell` parameter indicates the current active Runspace of the host. |
| 88 | + This parameter is needed to get the history from the current session. |
| 89 | + When the host is in `Enter-PSSession`, `powershell` points to the remote runspace. |
| 90 | + So if it's needed, PowerShell can get the history from the remote Runspace as well as from the default local Runspace. |
| 91 | + |
| 92 | +### Simple Local Experience |
| 93 | + |
| 94 | +A simple local experience needs to be implemented to verify the design and show people what it looks like. |
| 95 | + |
| 96 | +#### Suggestion Engine |
| 97 | + |
| 98 | +To prove the concept, a simple local suggestion engine needs to be implemented. |
| 99 | +It provides suggestions only based on the passed in history context with a string proximity algorithm, such as |
| 100 | +[Levenshtein distance](https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance) or |
| 101 | +[Dice's coefficient](https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Dice%27s_coefficient). |
| 102 | +By using the string proximity algorithm, it can calculate the proximity rate between each history command-line and the current input, |
| 103 | +and return suggestions based on the proximity rates. |
| 104 | + |
| 105 | +#### Update PSReadline |
| 106 | + |
| 107 | +To prove the concept, we need to update `PSReadline` to interact with the `SuggestInput` APIs and render suggestions on the host. |
| 108 | +The rudimentary idea is to borrow and alter the existing UI experience of `Ctrl+Space` in `PSReadline` 2.0, |
| 109 | +where completion options show up as a table and you can move cursor around to choose what you want. |
| 110 | + |
| 111 | +- A custom key binding will be used to trigger the suggestion, |
| 112 | + and returned suggestions will be shown in a single-column table, ordered by the proximity rate of each suggestion. |
| 113 | + Similarly, a user can move the cursor with `Up` and `Down` arrow keys and confirm an option with the `Enter` key. |
| 114 | +- For each returned suggestion string, the words that appear in the input will be rendered with green color, |
| 115 | + so that it's easy for user to see the what matches the input and what doesn't. |
| 116 | +- Once the suggestions are shown up, you can continue typing and the suggestion list will be filtered based on the new input, |
| 117 | + which may result in the rendered suggestions to be reordered or shrunk. |
| 118 | + Note that the filtering doesn't call `SuggestInput` again. |
| 119 | + Instead, it applies a **filtering method** on the previously returned suggestions. |
| 120 | +- After typing more on the host, if you think the filtered suggestions are not relevant anymore, |
| 121 | + you can press the key binding again to trigger another suggestion based on the current input. |
| 122 | + |
| 123 | +The **filtering method** should ideally be returned by the suggestion engine as a delegate along with the suggestion results. |
| 124 | +This is currently not enforced in the interface contacts we defined above. |
| 125 | +However, we can change the interface contracts if the filtering method proves to be valuable. |
| 126 | +For this simple local experience, |
| 127 | +PowerShell will provide a filtering delegate that always depends on the string proximity algorithm to filter the given suggestions. |
| 128 | + |
| 129 | +PowerShell could provide some utility methods to help the UI components, |
| 130 | +such as a method to find all common words between two strings. |
| 131 | + |
| 132 | +### Cloud Shell Experience |
| 133 | + |
| 134 | +The goal of this infrastructure design is to allow a custom suggestion engine to be completely self-contained and PowerShell-agnostic. |
| 135 | +The cloud shell team can design the suggestion engine without having PowerShell in mind. |
| 136 | +All they need is to provide the synchronous and the task-based asynchronous APIs for getting suggestions. |
| 137 | +Then, it can be easily plug in to PowerShell by wrapping their APIs in the cmdlet `CommandLineSuggestion`. |
| 138 | + |
| 139 | +## Alternate Proposals and Considerations |
| 140 | + |
| 141 | +A C# interface between PowerShell and suggestion engine was considered initially. |
| 142 | +It's in the form of a C# interface or abstract class, |
| 143 | +and a suggestion engine needs to implement them to integrate with PowerShell. |
| 144 | + |
| 145 | +Then I found it's hard to register and un-register a suggestion engine when loading and unloading a module. |
| 146 | +Using a cmdlet as the interface makes things easy because the registration and un-registration is already taken care of by PowerShell module. |
0 commit comments