Procedura dettagliata: Visualizzare il completamento istruzioni
È possibile implementare il completamento dell'istruzione basata sul linguaggio definendo gli identificatori per i quali si vuole fornire il completamento e quindi attivando una sessione di completamento. È possibile definire il completamento delle istruzioni nel contesto di un servizio linguistico, definire l'estensione del nome file e il tipo di contenuto e quindi visualizzare il completamento solo per quel tipo. In alternativa, è possibile attivare il completamento per un tipo di contenuto esistente, ad esempio "testo non crittografato". Questa procedura dettagliata illustra come attivare il completamento dell'istruzione per il tipo di contenuto "testo non crittografato", ovvero il tipo di contenuto dei file di testo. Il tipo di contenuto "text" è il predecessore di tutti gli altri tipi di contenuto, inclusi il codice e i file XML.
Il completamento dell'istruzione viene in genere attivato digitando determinati caratteri, ad esempio digitando l'inizio di un identificatore, ad esempio "using". In genere viene ignorato premendo la barra spaziatrice, tabulazione o invio per eseguire il commit di una selezione. È possibile implementare le funzionalità di IntelliSense che si attivano quando si digita un carattere usando un gestore comandi per le sequenze di tasti (l'interfaccia IOleCommandTarget ) e un provider di gestori che implementa l'interfaccia IVsTextViewCreationListener . Per creare l'origine di completamento, ovvero l'elenco di identificatori che partecipano al completamento, implementare l'interfaccia ICompletionSource e un provider di origine di completamento (l'interfaccia ICompletionSourceProvider ). I provider sono parti del componente MEF (Managed Extensibility Framework). Sono responsabili dell'esportazione delle classi di origine e controller e dell'importazione di servizi e broker, ad esempio , ITextStructureNavigatorSelectorServiceche abilita la navigazione nel buffer di testo e , ICompletionBrokerche attiva la sessione di completamento.
Questa procedura dettagliata illustra come implementare il completamento dell'istruzione per un set hardcoded di identificatori. Nelle implementazioni complete, il servizio di linguaggio e la documentazione del linguaggio sono responsabili della fornitura di tale contenuto.
Creare un progetto MEF
Per creare un progetto MEF
Creare un progetto VSIX C#. (In Finestra di dialogo Nuovo progetto , selezionare Visual C# / Estendibilità e quindi progetto VSIX. Assegnare alla soluzione
CompletionTest
il nome .Aggiungere un modello di elemento classificatore dell'editor al progetto. Per altre informazioni, vedere Creare un'estensione con un modello di elemento dell'editor.
Eliminare i file di classe esistenti.
Aggiungere i riferimenti seguenti al progetto e assicurarsi che CopyLocal sia impostato su
false
:Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.15.0
Microsoft.VisualStudio.Shell.Immutable.10.0
Microsoft.VisualStudio.TextManager.Interop
Implementare l'origine di completamento
L'origine di completamento è responsabile della raccolta del set di identificatori e dell'aggiunta del contenuto alla finestra di completamento quando un utente digita un trigger di completamento, ad esempio le prime lettere di un identificatore. In questo esempio, gli identificatori e le relative descrizioni sono hardcoded nel AugmentCompletionSession metodo . Nella maggior parte degli usi reali si userà il parser della lingua per ottenere i token per popolare l'elenco di completamento.
Per implementare l'origine di completamento
Aggiungere un file di classe e assegnargli il nome
TestCompletionSource
.Aggiungere queste importazioni:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities;
Modificare la dichiarazione di classe per in
TestCompletionSource
modo che implementi ICompletionSource:Aggiungere campi privati per il provider di origine, il buffer di testo e un elenco di Completion oggetti (che corrispondono agli identificatori che faranno parte della sessione di completamento):
Aggiungere un costruttore che imposta il provider di origine e il buffer. La
TestCompletionSourceProvider
classe è definita nei passaggi successivi:Implementare il AugmentCompletionSession metodo aggiungendo un set di completamento contenente i completamenti che si desidera fornire nel contesto. Ogni set di completamento contiene un set di Completion completamenti e corrisponde a una scheda della finestra di completamento. Nei progetti Visual Basic le schede della finestra di completamento sono denominate Common and All.) Il
FindTokenSpanAtPosition
metodo viene definito nel passaggio successivo.void ICompletionSource.AugmentCompletionSession(ICompletionSession session, IList<CompletionSet> completionSets) { List<string> strList = new List<string>(); strList.Add("addition"); strList.Add("adaptation"); strList.Add("subtraction"); strList.Add("summation"); m_compList = new List<Completion>(); foreach (string str in strList) m_compList.Add(new Completion(str, str, str, null, null)); completionSets.Add(new CompletionSet( "Tokens", //the non-localized title of the tab "Tokens", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); }
Il metodo seguente viene usato per trovare la parola corrente dalla posizione del cursore:
private ITrackingSpan FindTokenSpanAtPosition(ITrackingPoint point, ICompletionSession session) { SnapshotPoint currentPoint = (session.TextView.Caret.Position.BufferPosition) - 1; ITextStructureNavigator navigator = m_sourceProvider.NavigatorService.GetTextStructureNavigator(m_textBuffer); TextExtent extent = navigator.GetExtentOfWord(currentPoint); return currentPoint.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive); }
Implementare il
Dispose()
metodo :
Implementare il provider di origine di completamento
Il provider di origine di completamento è la parte del componente MEF che crea un'istanza dell'origine di completamento.
Per implementare il provider di origine di completamento
Aggiungere una classe denominata
TestCompletionSourceProvider
che implementa ICompletionSourceProvider. Esportare questa classe con un ContentTypeAttribute "testo non crittografato" e un NameAttribute "completamento del test".Importare un ITextStructureNavigatorSelectorServiceoggetto , che trova la parola corrente nell'origine di completamento.
Implementare il TryCreateCompletionSource metodo per creare un'istanza dell'origine di completamento.
Implementare il provider del gestore dei comandi di completamento
Il provider del gestore dei comandi di completamento è derivato da un IVsTextViewCreationListeneroggetto , che rimane in ascolto di un evento di creazione della visualizzazione testo e converte la visualizzazione da un IVsTextViewoggetto , che consente l'aggiunta del comando alla catena di comandi della shell di Visual Studio, in un oggetto ITextView. Poiché questa classe è un'esportazione MEF, è anche possibile usarla per importare i servizi richiesti dal gestore dei comandi stesso.
Per implementare il provider del gestore dei comandi di completamento
Aggiungere un file denominato
TestCompletionCommandHandler
.Aggiungere queste direttive using:
using System; using System.ComponentModel.Composition; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities;
Aggiungere una classe denominata
TestCompletionHandlerProvider
che implementa IVsTextViewCreationListener. Esportare questa classe con un NameAttribute "gestore di completamento del token", un ContentTypeAttribute "testo non crittografato" e un TextViewRoleAttribute di Editable.Importare , che consente la IVsEditorAdaptersFactoryServiceconversione da un IVsTextView oggetto a un ITextViewoggetto , un ICompletionBrokere un oggetto SVsServiceProvider che consente l'accesso ai servizi standard di Visual Studio.
Implementare il VsTextViewCreated metodo per creare un'istanza del gestore dei comandi.
public void VsTextViewCreated(IVsTextView textViewAdapter) { ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); if (textView == null) return; Func<TestCompletionCommandHandler> createCommandHandler = delegate() { return new TestCompletionCommandHandler(textViewAdapter, textView, this); }; textView.Properties.GetOrCreateSingletonProperty(createCommandHandler); }
Implementare il gestore dei comandi di completamento
Poiché il completamento delle istruzioni viene attivato dalle sequenze di tasti, è necessario implementare l'interfaccia IOleCommandTarget per ricevere ed elaborare le sequenze di tasti che attivano, eseguono il commit e ignorano la sessione di completamento.
Per implementare il gestore dei comandi di completamento
Aggiungere una classe denominata
TestCompletionCommandHandler
che implementa IOleCommandTarget:Aggiungere campi privati per il gestore dei comandi successivo (a cui si passa il comando), la visualizzazione testo, il provider del gestore comandi (che consente l'accesso a vari servizi) e una sessione di completamento:
Aggiungere un costruttore che imposta la visualizzazione testo e i campi del provider e aggiunge il comando alla catena di comandi:
Implementare il QueryStatus metodo passando il comando lungo:
Implementa il metodo Exec. Quando questo metodo riceve una sequenza di tasti, deve eseguire una di queste operazioni:
Consentire la scrittura del carattere nel buffer e quindi attivare o filtrare il completamento. I caratteri di stampa eseguono questa operazione.
Eseguire il commit del completamento, ma non consentire la scrittura del carattere nel buffer. (Spazi vuoti, Tabulazione e Invio eseguire questa operazione quando viene visualizzata una sessione di completamento.
Consente di passare il comando al gestore successivo. (Tutti gli altri comandi.
Poiché questo metodo può visualizzare l'interfaccia utente, chiamare IsInAutomationFunction per assicurarsi che non venga chiamato in un contesto di automazione:
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); } //make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; //make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } //check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB || (char.IsWhiteSpace(typedChar) || char.IsPunctuation(typedChar))) { //check for a selection if (m_session != null && !m_session.IsDismissed) { //if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { m_session.Commit(); //also, don't add the character to the buffer return VSConstants.S_OK; } else { //if there is no selection, dismiss the session m_session.Dismiss(); } } } //pass along the command so the char is added to the buffer int retVal = m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); bool handled = false; if (!typedChar.Equals(char.MinValue) && char.IsLetterOrDigit(typedChar)) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { this.TriggerCompletion(); m_session.Filter(); } else //the completion session is already active, so just filter { m_session.Filter(); } handled = true; } else if (commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE //redo the filter if there is a deletion || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE) { if (m_session != null && !m_session.IsDismissed) m_session.Filter(); handled = true; } if (handled) return VSConstants.S_OK; return retVal; }
Questo codice è un metodo privato che attiva la sessione di completamento:
private bool TriggerCompletion() { //the caret must be in a non-projection location SnapshotPoint? caretPoint = m_textView.Caret.Position.Point.GetPoint( textBuffer => (!textBuffer.ContentType.IsOfType("projection")), PositionAffinity.Predecessor); if (!caretPoint.HasValue) { return false; } m_session = m_provider.CompletionBroker.CreateCompletionSession (m_textView, caretPoint.Value.Snapshot.CreateTrackingPoint(caretPoint.Value.Position, PointTrackingMode.Positive), true); //subscribe to the Dismissed event on the session m_session.Dismissed += this.OnSessionDismissed; m_session.Start(); return true; }
L'esempio seguente è un metodo privato che annulla la sottoscrizione all'evento Dismissed :
Compilare e testare il codice
Per testare questo codice, compilare la soluzione CompletionTest ed eseguirla nell'istanza sperimentale.
Per compilare e testare la soluzione CompletionTest
Compilare la soluzione.
Quando si esegue questo progetto nel debugger, viene avviata una seconda istanza di Visual Studio.
Creare un file di testo e digitare un testo che includa la parola "add".
Quando si digita prima "a" e quindi "d", dovrebbe essere visualizzato un elenco che contiene "addizione" e "adattamento". Si noti che l'aggiunta è selezionata. Quando si digita un altro "d", l'elenco deve contenere solo "addizione", che è ora selezionato. È possibile eseguire il commit di "addizione" premendo la barra spaziatrice, tabulazione o invio oppure ignorando l'elenco digitando ESC o qualsiasi altro tasto.