Procédure pas à pas : afficher la saisie semi-automatique des instructions
Vous pouvez implémenter l’achèvement de l’instruction basée sur la langue en définissant les identificateurs pour lesquels vous souhaitez fournir l’achèvement, puis en déclenchant une session d’achèvement. Vous pouvez définir la saisie semi-automatique des instructions dans le contexte d’un service de langage, définir votre propre extension de nom de fichier et le type de contenu, puis afficher la saisie semi-automatique pour ce type. Vous pouvez également déclencher la saisie semi-automatique pour un type de contenu existant, par exemple, « texte en clair ». Cette procédure pas à pas montre comment déclencher la saisie semi-automatique de l’instruction pour le type de contenu « texte en clair », qui est le type de contenu des fichiers texte. Le type de contenu « text » est l’ancêtre de tous les autres types de contenu, y compris le code et les fichiers XML.
La saisie semi-automatique de l’instruction est généralement déclenchée en tapant certains caractères, par exemple en tapant le début d’un identificateur tel que « using ». Il est généralement ignoré en appuyant sur la barre d’espace, l’onglet ou la touche Entrée pour valider une sélection. Vous pouvez implémenter les fonctionnalités IntelliSense qui se déclenchent lors de la saisie d’un caractère à l’aide d’un gestionnaire de commandes pour les séquences de touches (l’interface IOleCommandTarget ) et un fournisseur de gestionnaires qui implémente l’interface IVsTextViewCreationListener . Pour créer la source d’achèvement, qui est la liste des identificateurs qui participent à l’achèvement, implémentez l’interface ICompletionSource et un fournisseur de source d’achèvement (l’interface ICompletionSourceProvider ). Les fournisseurs sont des composants MEF (Managed Extensibility Framework). Ils sont responsables de l’exportation des classes source et du contrôleur et de l’importation de services et de répartiteurs( par exemple, le ITextStructureNavigatorSelectorService, qui permet la navigation dans la mémoire tampon de texte et le ICompletionBroker, qui déclenche la session d’achèvement.
Cette procédure pas à pas montre comment implémenter la saisie semi-automatique des instructions pour un ensemble codé en dur d’identificateurs. Dans les implémentations complètes, le service linguistique et la documentation linguistique sont chargés de fournir ce contenu.
Créer un projet MEF
Pour créer un projet MEF
Créez un projet VSIX C#. (Dans le Boîte de dialogue Nouveau projet , sélectionnez Visual C# / Extensibilité, puis VSIX Project.) Nommez la solution
CompletionTest
.Ajoutez un modèle d’élément Classifieur d’éditeur au projet. Pour plus d’informations, consultez Créer une extension avec un modèle d’élément d’éditeur.
Supprimez les fichiers de classe existants.
Ajoutez les références suivantes au projet et vérifiez que CopyLocal est défini sur
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
Implémenter la source d’achèvement
La source d’achèvement est chargée de collecter l’ensemble d’identificateurs et d’ajouter le contenu à la fenêtre d’achèvement lorsqu’un utilisateur tape un déclencheur d’achèvement, par exemple les premières lettres d’un identificateur. Dans cet exemple, les identificateurs et leurs descriptions sont codés en dur dans la AugmentCompletionSession méthode. Dans la plupart des utilisations réelles, vous utiliseriez l’analyseur de votre langue pour obtenir les jetons pour remplir la liste de saisie semi-automatique.
Pour implémenter la source d’achèvement
Ajoutez un fichier de classe et nommez-le
TestCompletionSource
.Ajoutez ces importations :
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;
Modifiez la déclaration de classe pour
TestCompletionSource
qu’elle implémente ICompletionSource:Ajoutez des champs privés pour le fournisseur source, la mémoire tampon de texte et une liste d’objets Completion (qui correspondent aux identificateurs qui participeront à la session d’achèvement) :
Ajoutez un constructeur qui définit le fournisseur source et la mémoire tampon. La
TestCompletionSourceProvider
classe est définie dans les étapes ultérieures :Implémentez la AugmentCompletionSession méthode en ajoutant un jeu d’achèvement qui contient les achèvements que vous souhaitez fournir dans le contexte. Chaque jeu d’achèvements contient un ensemble d’achèvements Completion et correspond à un onglet de la fenêtre d’achèvement. (Dans les projets Visual Basic, les onglets de la fenêtre d’achèvement sont nommés Common and All.) La
FindTokenSpanAtPosition
méthode est définie à l’étape suivante.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)); }
La méthode suivante permet de rechercher le mot actuel à partir de la position du curseur :
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); }
Implémentez la
Dispose()
méthode :
Implémenter le fournisseur source d’achèvement
Le fournisseur de source d’achèvement est la partie du composant MEF qui instancie la source d’achèvement.
Pour implémenter le fournisseur source d’achèvement
Ajoutez une classe nommée
TestCompletionSourceProvider
qui implémente ICompletionSourceProvider. Exportez cette classe avec un ContentTypeAttribute « texte en clair » et un NameAttribute « test d’achèvement ».Importez un ITextStructureNavigatorSelectorServicemot actif dans la source de saisie semi-automatique.
Implémentez la TryCreateCompletionSource méthode pour instancier la source d’achèvement.
Implémenter le fournisseur de gestionnaires de commandes d’achèvement
Le fournisseur de gestionnaires de commandes de saisie semi-automatique est dérivé d’un IVsTextViewCreationListenerévénement de création d’affichage de texte et convertit l’affichage à partir d’un IVsTextView( qui permet l’ajout de la commande à la chaîne de commandes de l’interpréteur de commandes Visual Studio) en un ITextView. Étant donné que cette classe est une exportation MEF, vous pouvez également l’utiliser pour importer les services requis par le gestionnaire de commandes lui-même.
Pour implémenter le fournisseur de gestionnaires de commandes d’achèvement
Ajoutez un fichier nommé
TestCompletionCommandHandler
.Ajoutez ces directives 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;
Ajoutez une classe nommée
TestCompletionHandlerProvider
qui implémente IVsTextViewCreationListener. Exportez cette classe avec un NameAttribute « gestionnaire de saisie semi-automatique de jeton », un ContentTypeAttribute « texte en clair » et un TextViewRoleAttribute de Editable.Importez le IVsEditorAdaptersFactoryService, qui permet la conversion d’un IVsTextView vers un ITextView, un ICompletionBroker, et un SVsServiceProvider qui permet l’accès aux services Visual Studio standard.
Implémentez la VsTextViewCreated méthode pour instancier le gestionnaire de commandes.
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); }
Implémenter le gestionnaire de commandes d’achèvement
Étant donné que la saisie semi-automatique des instructions est déclenchée par des séquences de touches, vous devez implémenter l’interface IOleCommandTarget pour recevoir et traiter les séquences de touches qui déclenchent, valident et ignorent la session d’achèvement.
Pour implémenter le gestionnaire de commandes d’achèvement
Ajoutez une classe nommée
TestCompletionCommandHandler
qui implémente IOleCommandTarget:Ajoutez des champs privés pour le gestionnaire de commandes suivant (auquel vous passez la commande), l’affichage texte, le fournisseur de gestionnaires de commandes (qui permet l’accès à différents services) et une session d’achèvement :
Ajoutez un constructeur qui définit la vue de texte et les champs du fournisseur, puis ajoute la commande à la chaîne de commandes :
Implémentez la QueryStatus méthode en transmettant la commande :
Implémentez la méthode Exec. Lorsque cette méthode reçoit une séquence de touches, elle doit effectuer l’une des opérations suivantes :
Autorisez l’écriture du caractère dans la mémoire tampon, puis déclenchez ou filtrez l’achèvement. (Les caractères d’impression effectuent cette opération.)
Validez l’achèvement, mais n’autorisez pas l’écriture du caractère dans la mémoire tampon. (Espace blanc, Onglet et Entrée effectuez cette opération lorsqu’une session de saisie semi-automatique est affichée.)
Autoriser l’envoi de la commande au gestionnaire suivant. (Toutes les autres commandes.)
Étant donné que cette méthode peut afficher l’interface utilisateur, appelez IsInAutomationFunction pour vous assurer qu’elle n’est pas appelée dans un contexte d’automatisation :
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; }
Ce code est une méthode privée qui déclenche la session d’achèvement :
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’exemple suivant est une méthode privée qui se désabonne de l’événement Dismissed :
Générer et tester le code
Pour tester ce code, générez la solution CompletionTest et exécutez-la dans l’instance expérimentale.
Pour générer et tester la solution CompletionTest
Générez la solution.
Lorsque vous exécutez ce projet dans le débogueur, une deuxième instance de Visual Studio est démarrée.
Créez un fichier texte et tapez du texte incluant le mot « ajouter ».
Lorsque vous tapez d’abord « a », puis « d », une liste qui contient « addition » et « adaptation » doit apparaître. Notez que l’ajout est sélectionné. Lorsque vous tapez un autre « d », la liste doit contenir uniquement « addition », qui est maintenant sélectionnée. Vous pouvez valider « addition » en appuyant sur La barre d’espace, l’onglet ou la touche Entrée , ou ignorer la liste en tapant Échap ou toute autre touche.