Erstellen einer Befehlszeilenvorhersage

In PSReadLine 2.1.0 wurde das Konzept einer intelligenten Befehlszeilenvorhersage eingeführt, indem das Feature Predictive IntelliSense implementiert wurde. Für PSReadLine 2.2.2 wurde dieses Feature um ein Plug-In-Modell erweitert, mit dem Sie eigene Befehlszeilenvorhersagen erstellen können.

Predictive IntelliSense verbessert die Vervollständigung mit der TAB-TASTE, indem während der Eingabe Vorschläge an der Befehlszeile bereitgestellt werden. Die vorhergesagten Vorschläge werden als farbiger Text hinter dem Cursor angezeigt. Auf diese Weise können Sie vollständige Befehle basierend auf übereinstimmenden Vorhersagen aus Ihrem Befehlsverlauf oder zusätzlichen domänenspezifischen Plug-Ins ermitteln, bearbeiten und ausführen.

Systemanforderungen

Zum Erstellen und Verwenden einer Plug-In-basierten Vorhersage sind die folgenden Softwareversionen erforderlich:

  • PowerShell 7.2 (oder höher): stellt die APIs bereit, die zum Erstellen einer Befehlsvorhersage erforderlich sind
  • PSReadLine 2.2.2 (oder höher): ermöglicht Ihnen, PSReadLine für die Verwendung des Plug-Ins zu konfigurieren

Übersicht über Vorhersagen

Eine Vorhersage ist ein binäres PowerShell-Modul. Das Modul muss die System.Management.Automation.Subsystem.Prediction.ICommandPredictor-Schnittstelle implementieren. Diese Schnittstelle deklariert die Methoden, die verwendet werden, um Vorhersageergebnisse abzufragen und Feedback zu geben.

Ein Vorhersagemodul muss beim Laden ein CommandPredictor-Subsystem beim SubsystemManager von PowerShell registrieren und die Registrierung beim Entladen selbst aufheben.

Das folgende Diagramm zeigt die Architektur einer Vorhersage in PowerShell.

Architektur

Erstellen des Codes

Zum Erstellen einer Vorhersage muss das .NET 6 SDK für Ihre Plattform installiert sein. Weitere Informationen zum SDK finden Sie unter Herunterladen von .NET 6.0.

Gehen Sie wie folgt vor, um ein neues PowerShell-Modulprojekt zu erstellen:

  1. Verwenden Sie das dotnet-Befehlszeilentool, um ein Classlib-Starterprojekt zu erstellen.

    dotnet new classlib --name SamplePredictor
    
  2. Bearbeiten Sie die Datei SamplePredictor.csproj so, dass sie die folgenden Informationen enthält:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.0" />
      </ItemGroup>
    
    </Project>
    
  3. Löschen Sie die von dotnet erstellte Standarddatei Class1.cs, und kopieren Sie den folgenden Code in die Datei SamplePredictorClass.cs in Ihrem Projektordner.

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Management.Automation;
    using System.Management.Automation.Subsystem;
    using System.Management.Automation.Subsystem.Prediction;
    
    namespace PowerShell.Sample
    {
        public class SamplePredictor : ICommandPredictor
        {
            private readonly Guid _guid;
    
            internal SamplePredictor(string guid)
            {
                _guid = new Guid(guid);
            }
    
            /// <summary>
            /// Gets the unique identifier for a subsystem implementation.
            /// </summary>
            public Guid Id => _guid;
    
            /// <summary>
            /// Gets the name of a subsystem implementation.
            /// </summary>
            public string Name => "SamplePredictor";
    
            /// <summary>
            /// Gets the description of a subsystem implementation.
            /// </summary>
            public string Description => "A sample predictor";
    
            /// <summary>
            /// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="context">The <see cref="PredictionContext"/> object to be used for prediction.</param>
            /// <param name="cancellationToken">The cancellation token to cancel the prediction.</param>
            /// <returns>An instance of <see cref="SuggestionPackage"/>.</returns>
            public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContext context, CancellationToken cancellationToken)
            {
                string input = context.InputAst.Extent.Text;
                if (string.IsNullOrWhiteSpace(input))
                {
                    return default;
                }
    
                return new SuggestionPackage(new List<PredictiveSuggestion>{
                    new PredictiveSuggestion(string.Concat(input, " HELLO WORLD"))
                });
            }
    
            #region "interface methods for processing feedback"
    
            /// <summary>
            /// Gets a value indicating whether the predictor accepts a specific kind of feedback.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="feedback">A specific type of feedback.</param>
            /// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
            public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback) => false;
    
            /// <summary>
            /// One or more suggestions provided by the predictor were displayed to the user.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="session">The mini-session where the displayed suggestions came from.</param>
            /// <param name="countOrIndex">
            /// When the value is greater than 0, it's the number of displayed suggestions from the list
            /// returned in <paramref name="session"/>, starting from the index 0. When the value is
            /// less than or equal to 0, it means a single suggestion from the list got displayed, and
            /// the index is the absolute value.
            /// </param>
            public void OnSuggestionDisplayed(PredictionClient client, uint session, int countOrIndex) { }
    
            /// <summary>
            /// The suggestion provided by the predictor was accepted.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="session">Represents the mini-session where the accepted suggestion came from.</param>
            /// <param name="acceptedSuggestion">The accepted suggestion text.</param>
            public void OnSuggestionAccepted(PredictionClient client, uint session, string acceptedSuggestion) { }
    
            /// <summary>
            /// A command line was accepted to execute.
            /// The predictor can start processing early as needed with the latest history.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="history">History command lines provided as references for prediction.</param>
            public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history) { }
    
            /// <summary>
            /// A command line was done execution.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="commandLine">The last accepted command line.</param>
            /// <param name="success">Shows whether the execution was successful.</param>
            public void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success) { }
    
            #endregion;
        }
    
        /// <summary>
        /// Register the predictor on module loading and unregister it on module un-loading.
        /// </summary>
        public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
        {
            private const string Identifier = "843b51d0-55c8-4c1a-8116-f0728d419306";
    
            /// <summary>
            /// Gets called when assembly is loaded.
            /// </summary>
            public void OnImport()
            {
                var predictor = new SamplePredictor(Identifier);
                SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, predictor);
            }
    
            /// <summary>
            /// Gets called when the binary module is unloaded.
            /// </summary>
            public void OnRemove(PSModuleInfo psModuleInfo)
            {
                SubsystemManager.UnregisterSubsystem(SubsystemKind.CommandPredictor, new Guid(Identifier));
            }
        }
    }
    

    Der folgende Beispielcode gibt für alle Benutzereingaben die Zeichenfolge „HELLO WORLD“ als Vorhersageergebnis zurück. Da die Beispielvorhersage kein Feedback verarbeitet, implementiert der Code keine der Feedbackmethoden der Schnittstelle. Ändern Sie den Vorhersage- und Feedbackcode gemäß den Anforderungen an Ihre Vorhersage.

    Hinweis

    Die Listenansicht von PSReadLine unterstützt keine mehrzeiligen Vorschläge. Jeder Vorschlag muss eine einzelne Zeile sein. Wenn Ihr Code einen mehrzeiligen Vorschlag enthält, müssen Sie die Zeilen in separate Vorschläge aufteilen oder die Zeilen mit einem Semikolon (;) verbinden.

  4. Führen Sie dotnet build aus, um die Assembly zu generieren. Sie finden die kompilierte Assembly in Ihrem Projektordner unter bin/Debug/net6.0.

    Hinweis

    Um eine reaktionsfähige Benutzererfahrung sicherzustellen, verfügt die ICommandPredictor-Schnittstelle über ein Timeout von 20 ms für Antworten von Predictors. Ihr Vorhersagecode muss Ergebnisse in weniger als 20 ms zurückgeben, um angezeigt zu werden.

Verwenden Ihres Vorhersage-Plug-Ins

Um Ihre neue Vorhersage auszuprobieren, öffnen Sie eine neue PowerShell 7.2-Sitzung und führen die folgenden Befehle aus:

Set-PSReadLineOption -PredictionSource HistoryAndPlugin
Import-Module .\bin\Debug\net6.0\SamplePredictor.dll

Wenn die Assembly in der Sitzung geladen wurde, wird während der Eingabe im Terminal der Text „HELLO WORLD“ angezeigt. Sie können F2 drücken, um zwischen der Inline-Ansicht und der List-Ansicht zu wechseln.

Weitere Informationen zu PSReadLine-Optionen finden Sie unter Set-PSReadLineOption.

Mit dem folgenden Befehl können Sie eine Liste der installierten Vorhersagen abrufen:

Get-PSSubsystem -Kind CommandPredictor
Kind              SubsystemType      IsRegistered Implementations
----              -------------      ------------ ---------------
CommandPredictor  ICommandPredictor          True {SamplePredictor}

Hinweis

Get-PSSubsystem ist ein experimentelles Cmdlet, das in PowerShell 7.1 eingeführt wurde. Sie müssen das experimentelle Feature PSSubsystemPluginModel aktivieren, um dieses Cmdlet zu verwenden. Weitere Informationen finden Sie unter Verwenden von experimentellen Features.