Przewodnik: implementowanie fragmentów kodu
Możesz utworzyć fragmenty kodu i dołączyć je do rozszerzenia edytora, aby użytkownicy rozszerzenia mogli dodać je do własnego kodu.
Fragment kodu jest fragmentem kodu lub innego tekstu, który można włączyć do pliku. Aby wyświetlić wszystkie fragmenty kodu zarejestrowane dla określonych języków programowania, w menu Narzędzia kliknij pozycję Menedżer fragmentów kodu. Aby wstawić fragment kodu w pliku, kliknij prawym przyciskiem myszy miejsce, w którym chcesz umieścić fragment kodu, kliknij polecenie Wstaw fragment kodu lub Umieść za pomocą, znajdź odpowiedni fragment kodu, a następnie kliknij go dwukrotnie. Naciśnij klawisz Tab lub Klawisz Shift+, aby zmodyfikować odpowiednie części fragmentu kodu, a następnie naciśnij klawisz Enter lub Esc, aby go zaakceptować. Aby uzyskać więcej informacji, zobacz Fragmenty kodu.
Fragment kodu znajduje się w pliku XML zawierającym rozszerzenie nazwy pliku .snippet*. Fragment kodu może zawierać pola wyróżnione po wstawieniu fragmentu kodu, aby użytkownik mógł je znaleźć i zmienić. Plik fragmentu kodu zawiera również informacje dotyczące Menedżera fragmentów kodu, dzięki czemu może wyświetlać nazwę fragmentu kodu w odpowiedniej kategorii. Aby uzyskać informacje o schemacie fragmentu kodu, zobacz Dokumentacja schematu fragmentów kodu.
W tym przewodniku przedstawiono sposób wykonywania tych zadań:
Tworzenie i rejestrowanie fragmentów kodu dla określonego języka.
Dodaj polecenie Wstaw fragment kodu do menu skrótów.
Zaimplementuj rozszerzenie fragmentu kodu.
Ten przewodnik jest oparty na przewodniku: uzupełnianie instrukcji wyświetlania.
Tworzenie i rejestrowanie fragmentów kodu
Zazwyczaj fragmenty kodu są skojarzone z zarejestrowaną usługą językową. Nie trzeba jednak implementować elementu w celu rejestrowania LanguageService fragmentów kodu. Zamiast tego wystarczy określić identyfikator GUID w pliku indeksu fragmentu kodu, a następnie użyć tego samego identyfikatora GUID w ProvideLanguageCodeExpansionAttribute pliku dodanym do projektu.
W poniższych krokach pokazano, jak utworzyć fragmenty kodu i skojarzyć je z określonym identyfikatorem GUID.
Utwórz następującą strukturę katalogów:
%InstallDir%\TestSnippets\Snippets\1033\
gdzie %InstallDir% to folder instalacyjny programu Visual Studio. (Chociaż ta ścieżka jest zwykle używana do instalowania fragmentów kodu, można określić dowolną ścieżkę).
W folderze \1033\ utwórz plik XML i nadaj mu nazwę TestSnippets.xml. (Chociaż ta nazwa jest zwykle używana dla pliku indeksu fragmentu kodu, można określić dowolną nazwę, o ile ma rozszerzenie nazwy pliku XML ). Dodaj następujący tekst, a następnie usuń identyfikator GUID symbolu zastępczego i dodaj własny.
<?xml version="1.0" encoding="utf-8" ?> <SnippetCollection> <Language Lang="TestSnippets" Guid="{00000000-0000-0000-0000-000000000000}"> <SnippetDir> <OnOff>On</OnOff> <Installed>true</Installed> <Locale>1033</Locale> <DirPath>%InstallRoot%\TestSnippets\Snippets\%LCID%\</DirPath> <LocalizedName>Snippets</LocalizedName> </SnippetDir> </Language> </SnippetCollection>
Utwórz plik w folderze fragmentu kodu, nadaj mu nazwę test
.snippet
, a następnie dodaj następujący tekst:<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Test replacement fields</Title> <Shortcut>test</Shortcut> <Description>Code snippet for testing replacement fields</Description> <Author>MSIT</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>param1</ID> <ToolTip>First field</ToolTip> <Default>first</Default> </Literal> <Literal> <ID>param2</ID> <ToolTip>Second field</ToolTip> <Default>second</Default> </Literal> </Declarations> <References> <Reference> <Assembly>System.Windows.Forms.dll</Assembly> </Reference> </References> <Code Language="TestSnippets"> <![CDATA[MessageBox.Show("$param1$"); MessageBox.Show("$param2$");]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>
W poniższych krokach pokazano, jak zarejestrować fragmenty kodu.
Aby zarejestrować fragmenty kodu dla określonego identyfikatora GUID
Otwórz projekt CompletionTest. Aby uzyskać informacje o sposobie tworzenia tego projektu, zobacz Wskazówki: uzupełnianie instrukcji wyświetlania.
W projekcie dodaj odwołania do następujących zestawów:
Microsoft.VisualStudio.TextManager.Interop
Microsoft.VisualStudio.TextManager.Interop.8.0
microsoft.msxml
W projekcie otwórz plik source.extension.vsixmanifest .
Upewnij się, że karta Zasoby zawiera typ zawartości VsPackage i że program Project jest ustawiony na nazwę projektu.
Wybierz projekt CompletionTest i w okno Właściwości ustaw wartość Generate Pkgdef File (Generuj plik Pkgdef) na true. Zapisz projekt.
Dodaj klasę statyczną
SnippetUtilities
do projektu.W klasie SnippetUtilities zdefiniuj identyfikator GUID i nadaj mu wartość użytą w pliku SnippetsIndex.xml .
Dodaj element ProvideLanguageCodeExpansionAttribute do
TestCompletionHandler
klasy . Ten atrybut można dodać do dowolnej publicznej lub wewnętrznej (niestacjonanej) klasy w projekcie. (Może być konieczne dodanieusing
dyrektywy dla przestrzeni nazw Microsoft.VisualStudio.Shell).[ProvideLanguageCodeExpansion( SnippetUtilities.LanguageServiceGuidStr, "TestSnippets", //the language name 0, //the resource id of the language "TestSnippets", //the language ID used in the .snippet files @"%InstallRoot%\TestSnippets\Snippets\%LCID%\TestSnippets.xml", //the path of the index file SearchPaths = @"%InstallRoot%\TestSnippets\Snippets\%LCID%\", ForceCreateDirs = @"%InstallRoot%\TestSnippets\Snippets\%LCID%\")] internal class TestCompletionCommandHandler : IOleCommandTarget
Skompiluj i uruchom projekt. W eksperymentalnym wystąpieniu programu Visual Studio, które rozpoczyna się po uruchomieniu projektu, właśnie zarejestrowany fragment kodu powinien być wyświetlany w Menedżerze fragmentów kodu w języku TestSnippets.
Dodaj polecenie Wstaw fragment kodu do menu skrótów
Polecenie Wstaw fragment kodu nie jest uwzględniane w menu skrótów dla pliku tekstowego. W związku z tym należy włączyć polecenie .
Aby dodać polecenie Wstaw fragment kodu do menu skrótów
TestCompletionCommandHandler
Otwórz plik klasy.Ponieważ ta klasa implementuje IOleCommandTargetmetodę , możesz aktywować polecenie Wstaw fragment kodu w metodzie QueryStatus . Przed włączeniem polecenia sprawdź, czy ta metoda nie jest wywoływana wewnątrz funkcji automatyzacji, ponieważ po kliknięciu polecenia Wstaw fragment kodu zostanie wyświetlony interfejs użytkownika selektora fragmentów kodu.
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { if (!VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { if (pguidCmdGroup == VSConstants.VSStd2K && cCmds > 0) { // make the Insert Snippet command appear on the context menu if ((uint)prgCmds[0].cmdID == (uint)VSConstants.VSStd2KCmdID.INSERTSNIPPET) { prgCmds[0].cmdf = (int)Constants.MSOCMDF_ENABLED | (int)Constants.MSOCMDF_SUPPORTED; return VSConstants.S_OK; } } } return m_nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); }
Skompiluj i uruchom projekt. W wystąpieniu eksperymentalnym otwórz plik z rozszerzeniem nazwy pliku zzz , a następnie kliknij prawym przyciskiem myszy w dowolnym miejscu w nim. Polecenie Wstaw fragment kodu powinno pojawić się w menu skrótów.
Implementowanie rozszerzenia fragmentu kodu w interfejsie użytkownika selektora fragmentów kodu
W tej sekcji pokazano, jak zaimplementować rozszerzenie fragmentu kodu, aby interfejs użytkownika selektora fragmentów kodu był wyświetlany po kliknięciu wstawka fragmentu kodu w menu skrótów. Fragment kodu jest również rozszerzany, gdy użytkownik wpisze skrót fragmentu kodu, a następnie naciska klawisz Tab.
Aby wyświetlić interfejs użytkownika selektora fragmentów kodu i włączyć akceptację fragmentu kodu nawigacji i po wstawieniu, użyj Exec metody . Sama wstawienie jest obsługiwane przez metodę OnItemChosen .
Implementacja rozszerzenia fragmentu kodu używa starszych Microsoft.VisualStudio.TextManager.Interop interfejsów. Podczas tłumaczenia z bieżących klas edytora na starszy kod należy pamiętać, że starsze interfejsy używają kombinacji numerów wierszy i liczb kolumn do określania lokalizacji w buforze tekstowym, ale bieżące klasy używają jednego indeksu. W związku z tym, jeśli bufor ma trzy wiersze, z których każda ma 10 znaków (plus nowy wiersz, który liczy się jako jeden znak), czwarty znak w trzecim wierszu znajduje się na pozycji 27 w bieżącej implementacji, ale znajduje się w wierszu 2, pozycja 3 w starej implementacji.
Aby zaimplementować rozszerzenie fragmentu kodu
Do pliku zawierającego klasę
TestCompletionCommandHandler
dodaj następująceusing
dyrektywy.Ustaw klasę
TestCompletionCommandHandler
na implementację interfejsu IVsExpansionClient .W klasie zaimportuj
TestCompletionCommandHandlerProvider
element ITextStructureNavigatorSelectorService.Dodaj kilka pól prywatnych dla interfejsów rozszerzenia kodu i IVsTextView.
W konstruktorze
TestCompletionCommandHandler
klasy ustaw następujące pola.internal TestCompletionCommandHandler(IVsTextView textViewAdapter, ITextView textView, TestCompletionHandlerProvider provider) { this.m_textView = textView; m_vsTextView = textViewAdapter; m_provider = provider; //get the text manager from the service provider IVsTextManager2 textManager = (IVsTextManager2)m_provider.ServiceProvider.GetService(typeof(SVsTextManager)); textManager.GetExpansionManager(out m_exManager); m_exSession = null; //add the command to the command chain textViewAdapter.AddCommandFilter(this, out m_nextCommandHandler); }
Aby wyświetlić selektor fragmentów kodu, gdy użytkownik kliknie polecenie Wstaw fragment kodu, dodaj następujący kod do Exec metody . (Aby to wyjaśnienie było bardziej czytelne,
Exec()
kod używany do uzupełniania instrukcji nie jest wyświetlany; zamiast tego bloki kodu są dodawane do istniejącej metody). Dodaj następujący blok kodu po kodzie, który wyszukuje znak.//code previously written for Exec if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } //the snippet picker code starts here if (nCmdID == (uint)VSConstants.VSStd2KCmdID.INSERTSNIPPET) { IVsTextManager2 textManager = (IVsTextManager2)m_provider.ServiceProvider.GetService(typeof(SVsTextManager)); textManager.GetExpansionManager(out m_exManager); m_exManager.InvokeInsertionUI( m_vsTextView, this, //the expansion client new Guid(SnippetUtilities.LanguageServiceGuidStr), null, //use all snippet types 0, //number of types (0 for all) 0, //ignored if iCountTypes == 0 null, //use all snippet kinds 0, //use all snippet kinds 0, //ignored if iCountTypes == 0 "TestSnippets", //the text to show in the prompt string.Empty); //only the ENTER key causes insert return VSConstants.S_OK; }
Jeśli fragment kodu zawiera pola, które można nawigować, sesja rozszerzenia jest otwarta do momentu jawnego zaakceptowania rozszerzenia; Jeśli fragment kodu nie zawiera żadnych pól, sesja jest zamknięta i jest zwracana zgodnie
null
z InvokeInsertionUI metodą . W metodzie Exec po kodzie interfejsu użytkownika selektora fragmentów kodu, który został dodany w poprzednim kroku, dodaj następujący kod, aby obsłużyć nawigację fragmentu kodu (gdy użytkownik naciska klawisz Tab lub Shift+Tab po wstawieniu fragmentu kodu).//the expansion insertion is handled in OnItemChosen //if the expansion session is still active, handle tab/backtab/return/cancel if (m_exSession != null) { if (nCmdID == (uint)VSConstants.VSStd2KCmdID.BACKTAB) { m_exSession.GoToPreviousExpansionField(); return VSConstants.S_OK; } else if (nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB) { m_exSession.GoToNextExpansionField(0); //false to support cycling through all the fields return VSConstants.S_OK; } else if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.CANCEL) { if (m_exSession.EndCurrentExpansion(0) == VSConstants.S_OK) { m_exSession = null; return VSConstants.S_OK; } } }
Aby wstawić fragment kodu, gdy użytkownik wpisze odpowiedni skrót, a następnie naciśnie klawisz Tab, dodaj kod do Exec metody . Metoda prywatna, która wstawia fragment kodu, zostanie wyświetlona w późniejszym kroku. Dodaj następujący kod po kodzie nawigacji dodanym w poprzednim kroku.
//neither an expansion session nor a completion session is open, but we got a tab, so check whether the last word typed is a snippet shortcut if (m_session == null && m_exSession == null && nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB) { //get the word that was just added CaretPosition pos = m_textView.Caret.Position; TextExtent word = m_provider.NavigatorService.GetTextStructureNavigator(m_textView.TextBuffer).GetExtentOfWord(pos.BufferPosition - 1); //use the position 1 space back string textString = word.Span.GetText(); //the word that was just added //if it is a code snippet, insert it, otherwise carry on if (InsertAnyExpansion(textString, null, null)) return VSConstants.S_OK; }
Zaimplementuj metody interfejsu IVsExpansionClient . W tej implementacji jedynymi metodami zainteresowania są EndExpansion i OnItemChosen. Inne metody powinny po prostu zwrócić wartość S_OK.
public int EndExpansion() { m_exSession = null; return VSConstants.S_OK; } public int FormatSpan(IVsTextLines pBuffer, TextSpan[] ts) { return VSConstants.S_OK; } public int GetExpansionFunction(IXMLDOMNode xmlFunctionNode, string bstrFieldName, out IVsExpansionFunction pFunc) { pFunc = null; return VSConstants.S_OK; } public int IsValidKind(IVsTextLines pBuffer, TextSpan[] ts, string bstrKind, out int pfIsValidKind) { pfIsValidKind = 1; return VSConstants.S_OK; } public int IsValidType(IVsTextLines pBuffer, TextSpan[] ts, string[] rgTypes, int iCountTypes, out int pfIsValidType) { pfIsValidType = 1; return VSConstants.S_OK; } public int OnAfterInsertion(IVsExpansionSession pSession) { return VSConstants.S_OK; } public int OnBeforeInsertion(IVsExpansionSession pSession) { return VSConstants.S_OK; } public int PositionCaretForEditing(IVsTextLines pBuffer, TextSpan[] ts) { return VSConstants.S_OK; }
Zaimplementuj metodę OnItemChosen . Metoda pomocnika, która faktycznie wstawia rozszerzenia, jest omówiona w późniejszym kroku. Zawiera TextSpan informacje o wierszach i kolumnach, które można uzyskać z .IVsTextView
Poniższa metoda prywatna wstawia fragment kodu na podstawie skrótu lub tytułu i ścieżki. Następnie wywołuje metodę InsertNamedExpansion za pomocą fragmentu kodu.
private bool InsertAnyExpansion(string shortcut, string title, string path) { //first get the location of the caret, and set up a TextSpan int endColumn, startLine; //get the column number from the IVsTextView, not the ITextView m_vsTextView.GetCaretPos(out startLine, out endColumn); TextSpan addSpan = new TextSpan(); addSpan.iStartIndex = endColumn; addSpan.iEndIndex = endColumn; addSpan.iStartLine = startLine; addSpan.iEndLine = startLine; if (shortcut != null) //get the expansion from the shortcut { //reset the TextSpan to the width of the shortcut, //because we're going to replace the shortcut with the expansion addSpan.iStartIndex = addSpan.iEndIndex - shortcut.Length; m_exManager.GetExpansionByShortcut( this, new Guid(SnippetUtilities.LanguageServiceGuidStr), shortcut, m_vsTextView, new TextSpan[] { addSpan }, 0, out path, out title); } if (title != null && path != null) { IVsTextLines textLines; m_vsTextView.GetBuffer(out textLines); IVsExpansion bufferExpansion = (IVsExpansion)textLines; if (bufferExpansion != null) { int hr = bufferExpansion.InsertNamedExpansion( title, path, addSpan, this, new Guid(SnippetUtilities.LanguageServiceGuidStr), 0, out m_exSession); if (VSConstants.S_OK == hr) { return true; } } } return false; }
Kompilowanie i testowanie rozszerzenia fragmentu kodu
Możesz sprawdzić, czy rozszerzenie fragmentu kodu działa w projekcie.
Stwórz rozwiązanie. Po uruchomieniu tego projektu w debugerze zostanie uruchomione drugie wystąpienie programu Visual Studio.
Otwórz plik tekstowy i wpisz tekst.
Kliknij prawym przyciskiem myszy gdzieś w tekście, a następnie kliknij polecenie Wstaw fragment kodu.
Interfejs użytkownika selektora fragmentów powinien pojawić się z wyskakującym okienkiem z wyświetlonym komunikatem Testowanie pól zastępczych. Kliknij dwukrotnie wyskakujące okienko.
Należy wstawić poniższy fragment kodu.
MessageBox.Show("first"); MessageBox.Show("second");
Nie naciskaj klawisza Enter ani Esc.
Naciśnij klawisze Tab i Shift+Tab, aby przełączać się między "pierwszym" i "drugim".
Zaakceptuj wstawienie, naciskając klawisz Enter lub Esc.
W innej części tekstu wpisz "test", a następnie naciśnij klawisz Tab. Ponieważ "test" to skrót fragmentu kodu, fragment kodu powinien zostać wstawiony ponownie.