완성을 제공하려는 식별자를 정의하고 완성 세션을 트리거하여, 언어 기반 문 완성을 구현할 수 있습니다. 언어 서비스의 컨텍스트에서 문 완성을 정의하고, 고유한 파일 이름 확장명 및 콘텐츠 형식을 정의한 다음, 해당 형식에 대한 완성만 표시할 수 있습니다. 또는 기존 콘텐츠 형식(예: "일반 텍스트")에 대한 완성을 트리거할 수 있습니다. 이 연습에서는 텍스트 파일의 콘텐츠 형식인 "일반 텍스트" 콘텐츠 형식에 대해 문 완성을 트리거하는 방법을 보여 줍니다. "텍스트" 콘텐츠 형식은 코드 및 XML 파일을 포함한 다른 모든 콘텐츠 형식의 상위 항목입니다.
문 완성은 일반적으로 "using"과 같은 식별자의 시작을 입력하는 것처럼 특정 문자를 입력하여 트리거됩니다. 일반적으로 스페이스바, Tab 또는 Enter 키를 눌러 선택을 커밋하여 해제합니다. 키 입력에 대한 명령 처리기(IOleCommandTarget 인터페이스) 및 IVsTextViewCreationListener 인터페이스를 구현하는 처리기 공급자를 사용하여 문자를 입력할 때 트리거되는 IntelliSense 기능을 구현할 수 있습니다. 완성에 참여하는 식별자 목록인 완성 원본을 만들려면 ICompletionSource 인터페이스 및 완성 원본 공급자(ICompletionSourceProvider 인터페이스)를 구현합니다. 공급자는 MEF(Managed Extensibility Framework) 구성 요소 부분입니다. 원본 및 컨트롤러 클래스의 내보내기와, 서비스 및 브로커(예: 텍스트 버퍼에서 탐색을 사용하도록 설정하는 ITextStructureNavigatorSelectorService 및 완성 세션을 트리거하는 ICompletionBroker)의 가져오기를 담당합니다.
이 연습에서는 하드 코딩된 식별자 집합에 대해 문 완성을 구현하는 방법을 보여 줍니다. 전체 구현에서는 언어 서비스 및 언어 설명서에서 해당 콘텐츠를 제공해야 합니다.
MEF 프로젝트 만들기
MEF 프로젝트를 만들려면
C# VSIX 프로젝트를 만듭니다. (새 프로젝트 대화 상자에서 Visual C#/확장성, VSIX 프로젝트를 차례로 선택합니다.) 솔루션 이름을 CompletionTest
로 지정합니다.
프로젝트에 편집기 분류자 항목 템플릿을 추가합니다. 자세한 내용은 편집기 항목 템플릿을 사용하여 확장 만들기를 참조하세요.
기존 클래스 파일을 삭제합니다.
프로젝트에 다음 참조를 추가하고 CopyLocal이 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
완성 원본 구현
완성 원본은 식별자 집합을 수집하고 사용자가 식별자의 첫 글자와 같은 완료 트리거를 입력할 때 완성 창에 콘텐츠를 추가하는 작업을 담당합니다. 이 예제에서는 식별자와 해당 설명이 AugmentCompletionSession 메서드에 하드 코딩됩니다. 대부분의 실제 사용에서는 언어의 파서를 사용하여 완성 목록을 채우는 토큰을 가져옵니다.
완성 원본을 구현하려면
클래스 파일을 추가하고 이름을 TestCompletionSource
로 지정합니다.
다음 가져오기를 추가합니다.
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;
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.ComponentModel.Composition
Imports Microsoft.VisualStudio.Language.Intellisense
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Operations
Imports Microsoft.VisualStudio.Utilities
ICompletionSource를 구현할 수 있게 TestCompletionSource
에 대한 클래스 선언을 수정합니다.
internal class TestCompletionSource : ICompletionSource
Friend Class TestCompletionSource
Implements ICompletionSource
원본 공급자, 텍스트 버퍼 및 Completion 개체 목록(완성 세션에 참여할 식별자에 해당)에 대한 프라이빗 필드를 추가합니다.
private TestCompletionSourceProvider m_sourceProvider;
private ITextBuffer m_textBuffer;
private List<Completion> m_compList;
Private m_sourceProvider As TestCompletionSourceProvider
Private m_textBuffer As ITextBuffer
Private m_compList As List(Of Completion)
원본 공급자 및 버퍼를 설정하는 생성자를 추가합니다. TestCompletionSourceProvider
클래스는 이후의 단계에서 정의됩니다.
public TestCompletionSource(TestCompletionSourceProvider sourceProvider, ITextBuffer textBuffer)
{
m_sourceProvider = sourceProvider;
m_textBuffer = textBuffer;
}
Public Sub New(ByVal sourceProvider As TestCompletionSourceProvider, ByVal textBuffer As ITextBuffer)
m_sourceProvider = sourceProvider
m_textBuffer = textBuffer
End Sub
컨텍스트에서 제공하려는 완성이 포함된 완성 집합을 추가하여 AugmentCompletionSession 메서드를 구현합니다. 각 완성 집합은 Completion 완성의 세트를 포함하며 완성 창의 탭에 해당합니다. Visual Basic 프로젝트에서 완성 창 탭의 이름은 Common 및 All입니다. FindTokenSpanAtPosition
메서드는 다음 단계에서 정의합니다.
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));
}
Private Sub AugmentCompletionSession(ByVal session As ICompletionSession, ByVal completionSets As IList(Of CompletionSet)) Implements ICompletionSource.AugmentCompletionSession
Dim strList As New List(Of String)()
strList.Add("addition")
strList.Add("adaptation")
strList.Add("subtraction")
strList.Add("summation")
m_compList = New List(Of Completion)()
For Each str As String In strList
m_compList.Add(New Completion(str, str, str, Nothing, Nothing))
Next str
completionSets.Add(New CompletionSet(
"Tokens",
"Tokens",
FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer),
session),
m_compList,
Nothing))
End Sub
다음 메서드는 커서의 위치에서 현재 단어를 찾는 데 사용됩니다.
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);
}
Private Function FindTokenSpanAtPosition(ByVal point As ITrackingPoint, ByVal session As ICompletionSession) As ITrackingSpan
Dim currentPoint As SnapshotPoint = (session.TextView.Caret.Position.BufferPosition) - 1
Dim navigator As ITextStructureNavigator = m_sourceProvider.NavigatorService.GetTextStructureNavigator(m_textBuffer)
Dim extent As TextExtent = navigator.GetExtentOfWord(currentPoint)
Return currentPoint.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive)
End Function
Dispose()
메서드를 구현합니다.
private bool m_isDisposed;
public void Dispose()
{
if (!m_isDisposed)
{
GC.SuppressFinalize(this);
m_isDisposed = true;
}
}
Private m_isDisposed As Boolean
Public Sub Dispose() Implements IDisposable.Dispose
If Not m_isDisposed Then
GC.SuppressFinalize(Me)
m_isDisposed = True
End If
End Sub
완성 원본 공급자 구현
완성 원본 공급자는 완성 원본을 인스턴스화하는 MEF 구성 요소 부분입니다.
완성 원본 공급자를 구현하려면
ICompletionSourceProvider를 구현하는 TestCompletionSourceProvider
라는 클래스를 추가합니다. "일반 텍스트"의 ContentTypeAttribute 및 "테스트 완성"의 NameAttribute를 사용하여 이 클래스를 내보냅니다.
[Export(typeof(ICompletionSourceProvider))]
[ContentType("plaintext")]
[Name("token completion")]
internal class TestCompletionSourceProvider : ICompletionSourceProvider
<Export(GetType(ICompletionSourceProvider)), ContentType("plaintext"), Name("token completion")>
Friend Class TestCompletionSourceProvider
Implements ICompletionSourceProvider
완성 원본에서 현재 단어를 찾는 ITextStructureNavigatorSelectorService를 가져옵니다.
[Import]
internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
<Import()>
Friend Property NavigatorService() As ITextStructureNavigatorSelectorService
완성 원본을 인스턴스화하는 TryCreateCompletionSource 메서드를 구현합니다.
public ICompletionSource TryCreateCompletionSource(ITextBuffer textBuffer)
{
return new TestCompletionSource(this, textBuffer);
}
Public Function TryCreateCompletionSource(ByVal textBuffer As ITextBuffer) As ICompletionSource Implements ICompletionSourceProvider.TryCreateCompletionSource
Return New TestCompletionSource(Me, textBuffer)
End Function
완성 명령 처리기 공급자 구현
완성 명령 처리기 공급자는 텍스트 보기 만들기 이벤트를 수신 대기하고 Visual Studio 셸의 명령 체인에 명령을 추가할 수 있게 IVsTextView에서 뷰를 변환하는 IVsTextViewCreationListener에서 ITextView로 파생됩니다. 이 클래스는 MEF 내보내기이므로 명령 처리기 자체에 필요한 서비스를 가져오는 데 사용할 수도 있습니다.
완성 명령 처리기 공급자를 구현하려면
TestCompletionCommandHandler
라는 파일을 추가합니다.
다음 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;
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.ComponentModel.Composition
Imports System.Runtime.InteropServices
Imports Microsoft.VisualStudio
Imports Microsoft.VisualStudio.Editor
Imports Microsoft.VisualStudio.Language.Intellisense
Imports Microsoft.VisualStudio.OLE.Interop
Imports Microsoft.VisualStudio.Shell
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.TextManager.Interop
Imports Microsoft.VisualStudio.Utilities
IVsTextViewCreationListener를 구현하는 TestCompletionHandlerProvider
라는 클래스를 추가합니다. "토큰 완성 처리기"의 NameAttribute, "일반 텍스트"의 ContentTypeAttribute 및 Editable의 TextViewRoleAttribute를 사용하여 이 클래스를 내보냅니다.
[Export(typeof(IVsTextViewCreationListener))]
[Name("token completion handler")]
[ContentType("plaintext")]
[TextViewRole(PredefinedTextViewRoles.Editable)]
internal class TestCompletionHandlerProvider : IVsTextViewCreationListener
<Export(GetType(IVsTextViewCreationListener))>
<Name("token completion handler")>
<ContentType("plaintext")> <TextViewRole(PredefinedTextViewRoles.Editable)>
Friend Class TestCompletionHandlerProvider
Implements IVsTextViewCreationListener
IVsTextView를 ITextView로 변환할 수 있게 하는 IVsEditorAdaptersFactoryService와, ICompletionBroker 및 표준 Visual Studio 서비스에 액세스할 수 있게 하는 SVsServiceProvider를 가져옵니다.
[Import]
internal IVsEditorAdaptersFactoryService AdapterService = null;
[Import]
internal ICompletionBroker CompletionBroker { get; set; }
[Import]
internal SVsServiceProvider ServiceProvider { get; set; }
<Import()>
Friend AdapterService As IVsEditorAdaptersFactoryService = Nothing
<Import()>
Friend Property CompletionBroker() As ICompletionBroker
<Import()>
Friend Property ServiceProvider() As SVsServiceProvider
명령 처리기를 인스턴스화하는 VsTextViewCreated 메서드를 구현합니다.
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);
}
Public Sub VsTextViewCreated(ByVal textViewAdapter As IVsTextView) Implements IVsTextViewCreationListener.VsTextViewCreated
Dim textView As ITextView = AdapterService.GetWpfTextView(textViewAdapter)
If textView Is Nothing Then
Return
End If
Dim createCommandHandler As Func(Of TestCompletionCommandHandler) = Function() New TestCompletionCommandHandler(textViewAdapter, textView, Me)
textView.Properties.GetOrCreateSingletonProperty(createCommandHandler)
End Sub
완성 명령 처리기 구현
문 완성은 키 입력에 의해 트리거되므로 완성 세션을 트리거, 커밋 및 해제하는 키 입력을 수신 및 처리하는 IOleCommandTarget 인터페이스를 구현해야 합니다.
완성 명령 처리기를 구현하려면
IOleCommandTarget을 구현하는 TestCompletionCommandHandler
라는 클래스를 추가합니다.
internal class TestCompletionCommandHandler : IOleCommandTarget
Friend Class TestCompletionCommandHandler
Implements IOleCommandTarget
명령을 전달하는 다음 명령 처리기, 텍스트 보기, 명령 처리기 공급자(다양한 서비스에 대한 액세스 제공) 및 완성 세션에 대한 프라이빗 필드를 추가합니다.
private IOleCommandTarget m_nextCommandHandler;
private ITextView m_textView;
private TestCompletionHandlerProvider m_provider;
private ICompletionSession m_session;
Private m_nextCommandHandler As IOleCommandTarget
Private m_textView As ITextView
Private m_provider As TestCompletionHandlerProvider
Private m_session As ICompletionSession
텍스트 뷰 및 공급자 필드를 설정하고 명령 체인에 명령을 추가하는 생성자를 추가합니다.
internal TestCompletionCommandHandler(IVsTextView textViewAdapter, ITextView textView, TestCompletionHandlerProvider provider)
{
this.m_textView = textView;
this.m_provider = provider;
//add the command to the command chain
textViewAdapter.AddCommandFilter(this, out m_nextCommandHandler);
}
Friend Sub New(ByVal textViewAdapter As IVsTextView, ByVal textView As ITextView, ByVal provider As TestCompletionHandlerProvider)
Me.m_textView = textView
Me.m_provider = provider
'add the command to the command chain
textViewAdapter.AddCommandFilter(Me, m_nextCommandHandler)
End Sub
다음을 통해 명령을 전달하여 QueryStatus 메서드를 구현합니다.
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
{
return m_nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
Public Function QueryStatus(ByRef pguidCmdGroup As Guid, ByVal cCmds As UInteger, ByVal prgCmds() As OLECMD, ByVal pCmdText As IntPtr) As Integer Implements IOleCommandTarget.QueryStatus
Return m_nextCommandHandler.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText)
End Function
Exec 메서드를 구현합니다. 이 메서드는 키 입력을 받으면 다음 중 하나를 수행해야 합니다.
문자를 버퍼에 쓸 수 있도록 허용한 다음, 완성을 트리거하거나 필터링합니다. (인쇄 문자가 이 작업을 수행합니다.)
완성을 커밋하지만 버퍼에 문자를 쓸 수 없습니다. (완성 세션이 표시되면 공백, 탭 및 Enter에서 이 작업을 수행합니다.)
명령이 다음 처리기에 전달되도록 허용합니다. (다른 모든 명령)
이 메서드는 UI를 표시할 수 있으므로 자동화 컨텍스트에서 호출되지 않도록 IsInAutomationFunction을 호출합니다.
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;
}
Public Function Exec(ByRef pguidCmdGroup As Guid, ByVal nCmdID As UInteger, ByVal nCmdexecopt As UInteger, ByVal pvaIn As IntPtr, ByVal pvaOut As IntPtr) As Integer Implements IOleCommandTarget.Exec
If VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider) Then
Return m_nextCommandHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)
End If
'make a copy of this so we can look at it after forwarding some commands
Dim commandID As UInteger = nCmdID
Dim typedChar As Char = Char.MinValue
'make sure the input is a char before getting it
If pguidCmdGroup = VSConstants.VSStd2K AndAlso nCmdID = CUInt(VSConstants.VSStd2KCmdID.TYPECHAR) Then
typedChar = CChar(ChrW(CUShort(Marshal.GetObjectForNativeVariant(pvaIn))))
End If
'check for a commit character
If nCmdID = CUInt(VSConstants.VSStd2KCmdID.RETURN) OrElse nCmdID = CUInt(VSConstants.VSStd2KCmdID.TAB) OrElse (Char.IsWhiteSpace(typedChar) OrElse Char.IsPunctuation(typedChar)) Then
'check for a selection
If m_session IsNot Nothing AndAlso (Not m_session.IsDismissed) Then
'if the selection is fully selected, commit the current session
If m_session.SelectedCompletionSet.SelectionStatus.IsSelected Then
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()
End If
End If
End If
'pass along the command so the char is added to the buffer
Dim retVal As Integer = m_nextCommandHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)
Dim handled As Boolean = False
If (Not typedChar.Equals(Char.MinValue)) AndAlso Char.IsLetterOrDigit(typedChar) Then
If m_session Is Nothing OrElse m_session.IsDismissed Then ' If there is no active session, bring up completion
Me.TriggerCompletion()
m_session.Filter()
Else 'the completion session is already active, so just filter
m_session.Filter()
End If
handled = True
ElseIf commandID = CUInt(VSConstants.VSStd2KCmdID.BACKSPACE) OrElse commandID = CUInt(VSConstants.VSStd2KCmdID.DELETE) Then 'redo the filter if there is a deletion
If m_session IsNot Nothing AndAlso (Not m_session.IsDismissed) Then
m_session.Filter()
End If
handled = True
End If
If handled Then
Return VSConstants.S_OK
End If
Return retVal
End Function
이 코드는 완성 세션을 트리거하는 프라이빗 메서드입니다.
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;
}
Private Function TriggerCompletion() As Boolean
'the caret must be in a non-projection ___location
Dim caretPoint As SnapshotPoint? = m_textView.Caret.Position.Point.GetPoint(Function(textBuffer) ((Not textBuffer.ContentType.IsOfType("projection"))), PositionAffinity.Predecessor)
If Not caretPoint.HasValue Then
Return False
End If
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
AddHandler m_session.Dismissed, AddressOf OnSessionDismissed
m_session.Start()
Return True
End Function
다음 예제는 Dismissed 이벤트에서 구독을 취소하는 프라이빗 메서드입니다.
private void OnSessionDismissed(object sender, EventArgs e)
{
m_session.Dismissed -= this.OnSessionDismissed;
m_session = null;
}
Private Sub OnSessionDismissed(ByVal sender As Object, ByVal e As EventArgs)
RemoveHandler m_session.Dismissed, AddressOf OnSessionDismissed
m_session = Nothing
End Sub
코드 빌드 및 테스트
이 코드를 테스트하려면 CompletionTest 솔루션을 빌드하고 실험적 인스턴스에서 실행합니다.
CompletionTest 솔루션을 빌드하고 테스트하려면
솔루션을 빌드합니다.
디버거에서 이 프로젝트를 실행하면 Visual Studio의 두 번째 인스턴스가 시작됩니다.
텍스트 파일을 만들고 단어 "add"가 포함된 텍스트를 입력합니다.
먼저 "a"를 입력한 다음, "d"를 입력하면 "addition" 및 "adaptation"이 포함된 목록이 표시됩니다. addition이 선택되어 있습니다. 다른 "d"를 입력하면 목록에 이제 선택된 "addition"만 포함됩니다. 스페이스바, 탭 또는 Enter 키를 눌러 "더하기"를 커밋하거나 Esc 또는 다른 키를 입력하여 목록을 해제할 수 있습니다.
관련 콘텐츠