QuickInfo는 사용자가 메서드 이름 위로 포인터를 이동할 때 메서드 서명 및 설명을 표시하는 IntelliSense 기능입니다. QuickInfo 설명을 제공할 식별자를 정의한 다음 콘텐츠를 표시할 도구 설명을 만들어 QuickInfo와 같은 언어 기반 기능을 구현할 수 있습니다. 언어 서비스의 컨텍스트에서 QuickInfo를 정의하거나 사용자 고유의 파일 이름 확장명 및 콘텐츠 형식을 정의하고 해당 형식에 대한 QuickInfo를 표시하거나 기존 콘텐츠 형식(예: “텍스트”)에 대해 QuickInfo를 표시할 수 있습니다. 이 연습에서는 “텍스트” 콘텐츠 형식에 대해 QuickInfo를 표시하는 방법을 보여 줍니다.
이 연습의 QuickInfo 예제는 사용자가 메서드 이름 위로 포인터를 이동할 때 도구 설명을 표시합니다. 이 디자인을 사용하려면 다음 네 가지 인터페이스를 구현해야 합니다.
원본 인터페이스
원본 공급자 인터페이스
컨트롤러 인터페이스
컨트롤러 공급자 인터페이스
소스 및 컨트롤러 공급자는 MEF(Managed Extensibility Framework) 구성 요소 부분이며 소스 및 컨트롤러 클래스를 내보내고 도구 설명 텍스트 버퍼를 생성하는 ITextBufferFactoryService 및 QuickInfo 세션을 트리거하는 IQuickInfoBroker와 같은 서비스 및 브로커 가져오기를 담당합니다.
이 예제에서 QuickInfo 원본은 하드 코드된 메서드 이름 및 설명 목록을 사용하지만 전체 구현에서는 언어 서비스 및 언어 설명서에서 해당 콘텐츠를 제공해야 합니다.
MEF 프로젝트 만들기
MEF 프로젝트를 만들려면
C# VSIX 프로젝트를 만듭니다. (새 프로젝트 대화 상자에서 Visual C#/확장성, VSIX 프로젝트를 차례로 선택합니다.) 솔루션 이름을 QuickInfoTest로 지정합니다.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
public TestQuickInfoSource(TestQuickInfoSourceProvider provider, ITextBuffer subjectBuffer)
{
m_provider = provider;
m_subjectBuffer = subjectBuffer;
//these are the method names and their descriptions
m_dictionary = new Dictionary<string, string>();
m_dictionary.Add("add", "int add(int firstInt, int secondInt)\nAdds one integer to another.");
m_dictionary.Add("subtract", "int subtract(int firstInt, int secondInt)\nSubtracts one integer from another.");
m_dictionary.Add("multiply", "int multiply(int firstInt, int secondInt)\nMultiplies one integer by another.");
m_dictionary.Add("divide", "int divide(int firstInt, int secondInt)\nDivides one integer by another.");
}
Public Sub New(ByVal provider As TestQuickInfoSourceProvider, ByVal subjectBuffer As ITextBuffer)
m_provider = provider
m_subjectBuffer = subjectBuffer
'these are the method names and their descriptions
m_dictionary = New Dictionary(Of String, String)()
m_dictionary.Add("add", "int add(int firstInt, int secondInt)" & vbLf & "Adds one integer to another.")
m_dictionary.Add("subtract", "int subtract(int firstInt, int secondInt)" & vbLf & "Subtracts one integer from another.")
m_dictionary.Add("multiply", "int multiply(int firstInt, int secondInt)" & vbLf & "Multiplies one integer by another.")
m_dictionary.Add("divide", "int divide(int firstInt, int secondInt)" & vbLf & "Divides one integer by another.")
End Sub
AugmentQuickInfoSession 메서드를 구현합니다. 이 예제에서 메서드는 현재 단어를 찾거나 커서가 줄 또는 텍스트 버퍼 끝에 있는 경우 이전 단어를 찾습니다. 단어가 메서드 이름 중 하나인 경우 해당 메서드 이름에 대한 설명이 QuickInfo 콘텐츠에 추가됩니다.
public void AugmentQuickInfoSession(IQuickInfoSession session, IList<object> qiContent, out ITrackingSpan applicableToSpan)
{
// Map the trigger point down to our buffer.
SnapshotPoint? subjectTriggerPoint = session.GetTriggerPoint(m_subjectBuffer.CurrentSnapshot);
if (!subjectTriggerPoint.HasValue)
{
applicableToSpan = null;
return;
}
ITextSnapshot currentSnapshot = subjectTriggerPoint.Value.Snapshot;
SnapshotSpan querySpan = new SnapshotSpan(subjectTriggerPoint.Value, 0);
//look for occurrences of our QuickInfo words in the span
ITextStructureNavigator navigator = m_provider.NavigatorService.GetTextStructureNavigator(m_subjectBuffer);
TextExtent extent = navigator.GetExtentOfWord(subjectTriggerPoint.Value);
string searchText = extent.Span.GetText();
foreach (string key in m_dictionary.Keys)
{
int foundIndex = searchText.IndexOf(key, StringComparison.CurrentCultureIgnoreCase);
if (foundIndex > -1)
{
applicableToSpan = currentSnapshot.CreateTrackingSpan
(
//querySpan.Start.Add(foundIndex).Position, 9, SpanTrackingMode.EdgeInclusive
extent.Span.Start + foundIndex, key.Length, SpanTrackingMode.EdgeInclusive
);
string value;
m_dictionary.TryGetValue(key, out value);
if (value != null)
qiContent.Add(value);
else
qiContent.Add("");
return;
}
}
applicableToSpan = null;
}
Public Sub AugmentQuickInfoSession(ByVal session As IQuickInfoSession, ByVal qiContent As IList(Of Object), ByRef applicableToSpan As ITrackingSpan) Implements IQuickInfoSource.AugmentQuickInfoSession
' Map the trigger point down to our buffer.
Dim subjectTriggerPoint As System.Nullable(Of SnapshotPoint) = session.GetTriggerPoint(m_subjectBuffer.CurrentSnapshot)
If Not subjectTriggerPoint.HasValue Then
applicableToSpan = Nothing
Exit Sub
End If
Dim currentSnapshot As ITextSnapshot = subjectTriggerPoint.Value.Snapshot
Dim querySpan As New SnapshotSpan(subjectTriggerPoint.Value, 0)
'look for occurrences of our QuickInfo words in the span
Dim navigator As ITextStructureNavigator = m_provider.NavigatorService.GetTextStructureNavigator(m_subjectBuffer)
Dim extent As TextExtent = navigator.GetExtentOfWord(subjectTriggerPoint.Value)
Dim searchText As String = extent.Span.GetText()
For Each key As String In m_dictionary.Keys
Dim foundIndex As Integer = searchText.IndexOf(key, StringComparison.CurrentCultureIgnoreCase)
If foundIndex > -1 Then
'applicableToSpan = currentSnapshot.CreateTrackingSpan(querySpan.Start.Add(foundIndex).Position, 9, SpanTrackingMode.EdgeInclusive)
applicableToSpan = currentSnapshot.CreateTrackingSpan(extent.Span.Start + foundIndex, key.Length, SpanTrackingMode.EdgeInclusive)
Dim value As String = ""
m_dictionary.TryGetValue(key, value)
If value IsNot Nothing Then
qiContent.Add(value)
Else
qiContent.Add("")
End If
Exit Sub
End If
Next
applicableToSpan = Nothing
End Sub
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
QuickInfo 원본 공급자 구현
QuickInfo 원본 공급자는 주로 MEF 구성 요소 부분으로 자신을 내보내고 QuickInfo 소스를 인스턴스화하는 역할을 합니다. MEF 구성 요소 부분이기 때문에 다른 MEF 구성 요소 부분을 가져올 수 있습니다.
Private _NavigatorService As ITextStructureNavigatorSelectorService
<Import()> _
Friend Property NavigatorService() As ITextStructureNavigatorSelectorService
Get
Return _NavigatorService
End Get
Set(ByVal value As ITextStructureNavigatorSelectorService)
_NavigatorService = value
End Set
End Property
Private _TextBufferFactoryService As ITextBufferFactoryService
<Import()> _
Friend Property TextBufferFactoryService() As ITextBufferFactoryService
Get
Return _TextBufferFactoryService
End Get
Set(ByVal value As ITextBufferFactoryService)
_TextBufferFactoryService = value
End Set
End Property
public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer)
{
return new TestQuickInfoSource(this, textBuffer);
}
Public Function TryCreateQuickInfoSource(ByVal textBuffer As ITextBuffer) As IQuickInfoSource Implements IQuickInfoSourceProvider.TryCreateQuickInfoSource
Return New TestQuickInfoSource(Me, textBuffer)
End Function
QuickInfo 컨트롤러 구현
QuickInfo 컨트롤러는 QuickInfo가 표시되는 시기를 결정합니다. 이 예제에서는 메서드 이름 중 하나에 해당하는 단어 위에 포인터가 있을 때 QuickInfo가 나타납니다. QuickInfo 컨트롤러는 QuickInfo 세션을 트리거하는 마우스 가리키기 이벤트 처리기를 구현합니다.
Private m_textView As ITextView
Private m_subjectBuffers As IList(Of ITextBuffer)
Private m_provider As TestQuickInfoControllerProvider
Private m_session As IQuickInfoSession
Friend Sub New(ByVal textView As ITextView, ByVal subjectBuffers As IList(Of ITextBuffer), ByVal provider As TestQuickInfoControllerProvider)
m_textView = textView
m_subjectBuffers = subjectBuffers
m_provider = provider
AddHandler m_textView.MouseHover, AddressOf Me.OnTextViewMouseHover
End Sub
private void OnTextViewMouseHover(object sender, MouseHoverEventArgs e)
{
//find the mouse position by mapping down to the subject buffer
SnapshotPoint? point = m_textView.BufferGraph.MapDownToFirstMatch
(new SnapshotPoint(m_textView.TextSnapshot, e.Position),
PointTrackingMode.Positive,
snapshot => m_subjectBuffers.Contains(snapshot.TextBuffer),
PositionAffinity.Predecessor);
if (point != null)
{
ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position,
PointTrackingMode.Positive);
if (!m_provider.QuickInfoBroker.IsQuickInfoActive(m_textView))
{
m_session = m_provider.QuickInfoBroker.TriggerQuickInfo(m_textView, triggerPoint, true);
}
}
}
Private Sub OnTextViewMouseHover(ByVal sender As Object, ByVal e As MouseHoverEventArgs)
'find the mouse position by mapping down to the subject buffer
Dim point As System.Nullable(Of SnapshotPoint) = m_textView.BufferGraph.MapDownToFirstMatch(New SnapshotPoint(m_textView.TextSnapshot, e.Position), PointTrackingMode.Positive, Function(snapshot) m_subjectBuffers.Contains(snapshot.TextBuffer), PositionAffinity.Predecessor)
If point IsNot Nothing Then
Dim triggerPoint As ITrackingPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position, PointTrackingMode.Positive)
If Not m_provider.QuickInfoBroker.IsQuickInfoActive(m_textView) Then
m_session = m_provider.QuickInfoBroker.TriggerQuickInfo(m_textView, triggerPoint, True)
End If
End If
End Sub
컨트롤러가 텍스트 보기에서 분리될 때 마우스 가리키기 이벤트 처리기를 제거하도록 Detach 메서드를 구현합니다.
public void Detach(ITextView textView)
{
if (m_textView == textView)
{
m_textView.MouseHover -= this.OnTextViewMouseHover;
m_textView = null;
}
}
Public Sub Detach(ByVal textView As ITextView) Implements IIntellisenseController.Detach
If m_textView Is textView Then
AddHandler m_textView.MouseHover, AddressOf Me.OnTextViewMouseHover
m_textView = Nothing
End If
End Sub
public void ConnectSubjectBuffer(ITextBuffer subjectBuffer)
{
}
public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer)
{
}
Public Sub ConnectSubjectBuffer(ByVal subjectBuffer As ITextBuffer) Implements IIntellisenseController.ConnectSubjectBuffer
End Sub
Public Sub DisconnectSubjectBuffer(ByVal subjectBuffer As ITextBuffer) Implements IIntellisenseController.DisconnectSubjectBuffer
End Sub
QuickInfo 컨트롤러 공급자 구현
QuickInfo 컨트롤러는 주로 MEF 구성 요소 부분으로 자신을 내보내고 QuickInfo 컨트롤러를 인스턴스화하는 역할을 합니다. MEF 구성 요소 부분이기 때문에 다른 MEF 구성 요소 부분을 가져올 수 있습니다.
Private _QuickInfoBroker As IQuickInfoBroker
<Import()> _
Friend Property QuickInfoBroker() As IQuickInfoBroker
Get
Return _QuickInfoBroker
End Get
Set(ByVal value As IQuickInfoBroker)
_QuickInfoBroker = value
End Set
End Property
public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList<ITextBuffer> subjectBuffers)
{
return new TestQuickInfoController(textView, subjectBuffers, this);
}
Public Function TryCreateIntellisenseController(ByVal textView As ITextView, ByVal subjectBuffers As IList(Of ITextBuffer)) As IIntellisenseController Implements IIntellisenseControllerProvider.TryCreateIntellisenseController
Return New TestQuickInfoController(textView, subjectBuffers, Me)
End Function
코드 빌드 및 테스트
이 코드를 테스트하려면 QuickInfoTest 솔루션을 빌드하고 실험적 인스턴스에서 실행합니다.
QuickInfoTest 솔루션을 빌드하고 테스트하려면
솔루션을 빌드합니다.
디버거에서 이 프로젝트를 실행하면 Visual Studio의 두 번째 인스턴스가 시작됩니다.
텍스트 파일을 만들고 “추가” 및 “빼기”라는 단어가 포함된 텍스트를 입력합니다.
포인터를 “추가” 발생 중 하나로 이동합니다. add 메서드의 서명과 설명이 표시되어야 합니다.