Compartir a través de


Creación de un predictor de línea de comandos

PSReadLine 2.1.0 introdujo el concepto de predicción de línea de comandos inteligente mediante la implementación de la característica IntelliSense predictivo. PSReadLine 2.2.2 expandió esa característica agregando un modelo de complemento que le permite crear sus propios predictores de línea de comandos.

IntelliSense predictivo mejora la finalización con tabulación al proporcionar sugerencias, en la línea de comandos, a medida que escribe. La sugerencia de predicción aparece como texto coloreado después del cursor. Esto le permite detectar, editar y ejecutar comandos completos en función de las predicciones correspondientes del historial de comandos o los complementos adicionales específicos del dominio.

Requisitos del sistema

Para crear y usar un predictor de complemento, debe usar las siguientes versiones de software:

  • PowerShell 7.2 (o superior): proporciona las API necesarias para crear un predictor de comandos.
  • PSReadLine 2.2.2 (o superior): permite configurar PSReadLine para usar el complemento.

Información general de un predictor

Un predictor es un módulo binario de PowerShell. Este objeto debe implementar la interfaz System.Management.Automation.Subsystem.Prediction.ICommandPredictor. Esta interfaz declara los métodos usados para consultar los resultados de la predicción y proporcionar comentarios.

Un módulo de predicción debe registrar un subsistema CommandPredictor con SubsystemManager de PowerShell cuando se carga y anular el registro cuando se descarga.

En el siguiente diagrama, se muestra la arquitectura de un predictor en PowerShell.

Architecture

Creación del código

Para crear un predictor, debe tener instalado el SDK de .NET 6 para la plataforma. Para más información sobre el SDK, consulte Descarga de .NET 6.0.

Siga estos pasos para crear un nuevo proyecto de módulo de PowerShell:

  1. Use la herramienta de línea de comandos dotnet para crear un proyecto de biblioteca de clases de inicio.

    dotnet new classlib --name SamplePredictor
    
  2. EditeSamplePredictor.csproj para que contenga la información siguiente:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.0" />
      </ItemGroup>
    
    </Project>
    
  3. Elimine el archivo Class1.cs predeterminado creado por dotnet y copie el código siguiente en un archivo SamplePredictorClass.cs de la carpeta del proyecto.

    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));
            }
        }
    }
    

    El código de ejemplo siguiente devuelve la cadena "HELLO WORLD" para el resultado de la predicción para todas las entradas del usuario. Como el predictor de ejemplo no procesa ningún comentario, el código no implementa los métodos de comentarios desde la interfaz. Cambie el código de predicción y comentarios para satisfacer las necesidades del predictor.

    Nota

    La vista de lista de PSReadLine no admite sugerencias de varias líneas. Cada sugerencia debe ser una sola línea. Si el código tiene una sugerencia de varias líneas, debe dividir las líneas en sugerencias independientes o combinar las líneas con un punto y coma (;).

  4. Ejecute dotnet build para generar el ensamblado. Puede encontrar el ensamblado compilado en la ubicación bin/Debug/net6.0 de la carpeta del proyecto.

    Nota:

    Para garantizar una experiencia de usuario dinámica, la interfaz ICommandPredictor tiene un tiempo de espera de 20 ms para las respuestas de los predictores. El código del predictor debe devolver resultados en menos de 20 ms para que se muestren.

Uso del complemento predictor

Para probar el nuevo predictor, abra una nueva sesión de PowerShell 7.2 y ejecute los siguientes comandos:

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

Con el ensamblado cargado en la sesión, verá que el texto "HELLO WORLD" aparece a medida que escribe en el terminal. Puede presionar F2 para cambiar entre la vista Inline y la vista List.

Para más información sobre las opciones de PSReadLine, consulte Set-PSReadLineOption.

Puede obtener una lista de los predictores instalados con el siguiente comando:

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

Nota

Get-PSSubsystem es un cmdlet experimental que se introdujo en PowerShell 7.1. Debe habilitar la característica experimental PSSubsystemPluginModel para usar este cmdlet. Para obtener más información, vea Uso de las características experimentales.