연습: 개요
텍스트 영역을 확장 하거나 축소 하려면의 종류를 정의 하 여 개요와 같은 언어를 기반으로 기능을 구현할 수 있습니다. 언어 서비스의 컨텍스트 내에서 영역을 정의할 수 있습니다 또는 직접 파일 이름 확장명 및 콘텐츠 형식을 정의 하 고 영역 정의 해당 형식에만 적용 수 또는 영역 정의가 기존 콘텐츠 형식 (예: "text")에 적용할 수 있습니다. 이 연습에서는 정의 및 개요 영역을 표시 하는 방법을 보여 줍니다.
사전 요구 사항
이 연습을 완료 하려면 설치 해야 해당 Visual Studio 2010 SDK.
참고
Visual Studio SDK에 대 한 자세한 내용은 참조 하십시오. Visual Studio 개요를 확장합니다..Visual Studio SDK를 다운로드 하는 방법를 참조 하십시오. Visual Studio 확장성 개발자 센터 MSDN 웹 사이트에서.
관리 되는 확장성 프레임 워크 (MEF) 프로젝트 만들기
MEF 프로젝트를 만들려면
편집기 분류자 프로젝트를 만듭니다. 솔루션의 이름을 OutlineRegionTest.
VSIX 매니페스트 편집기에서 source.extension.vsixmanifest 파일을 엽니다.
Content MEF 구성 요소 콘텐츠 형식 및 해당 제목 포함은 Path Outlineregiontest.dll으로 설정 됩니다.
저장 하 고 source.extension.vsixmanifest를 닫습니다.
기존 클래스 파일을 삭제 합니다.
개요는 Tagger 구현
개요 영역에서 일종의 태그 표시 됩니다 (OutliningRegionTag). 이 태그는 표준 동작 개요를 제공 합니다. 윤곽이 설정 된 영역을 확장 하거나 축소할 수 있습니다. 확장 된 경우 확장 된 영역에 세로 줄 빈입니다 개요 영역이 축소 되어 있으면 더하기 기호 또는 빼기 기호로 표시 되어 있습니다.
다음 단계는 구분 되는 모든 영역에 대 한 개요 영역을 만듭니다를 tagger 정의 하는 방법을 보여줍니다 "[" 및 "]".
개요는 tagger 구현 하기
클래스 파일을 추가 하 고 이름을 OutliningTagger.
다음 네임 스페이스를 가져옵니다.
Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.ComponentModel.Composition Imports Microsoft.VisualStudio.Text.Outlining Imports Microsoft.VisualStudio.Text.Tagging Imports Microsoft.VisualStudio.Utilities Imports Microsoft.VisualStudio.Text
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text.Outlining; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Text;
라는 클래스 만들기 OutliningTagger, 하 여 구현 ITagger:
Friend NotInheritable Class OutliningTagger Implements ITagger(Of IOutliningRegionTag)
internal sealed class OutliningTagger : ITagger<IOutliningRegionTag>
일부 필드 텍스트 버퍼 및 스냅샷을 추적할 수 및 집합 영역 개요로 태그를 지정 해야 하는 줄을 계속 추가 합니다. 이 코드는 개체의 목록을 나타내는 개요 영역 (정의 나중에) 지역 포함 됩니다.
'the characters that start the outlining region Private startHide As String = "[" 'the characters that end the outlining region Private endHide As String = "]" 'the characters that are displayed when the region is collapsed Private ellipsis As String = "..." 'the contents of the tooltip for the collapsed span Private hoverText As String = "hover text" Private buffer As ITextBuffer Private snapshot As ITextSnapshot Private regions As List(Of Region)
string startHide = "["; //the characters that start the outlining region string endHide = "]"; //the characters that end the outlining region string ellipsis = "..."; //the characters that are displayed when the region is collapsed string hoverText = "hover text"; //the contents of the tooltip for the collapsed span ITextBuffer buffer; ITextSnapshot snapshot; List<Region> regions;
필드를 초기화 하는 tagger 생성자를 추가 버퍼를 구문 분석 하 고 이벤트 처리기를 추가 Changed 이벤트입니다.
Public Sub New(ByVal buffer As ITextBuffer) Me.buffer = buffer Me.snapshot = buffer.CurrentSnapshot Me.regions = New List(Of Region)() Me.ReParse() AddHandler Me.buffer.Changed, AddressOf BufferChanged End Sub
public OutliningTagger(ITextBuffer buffer) { this.buffer = buffer; this.snapshot = buffer.CurrentSnapshot; this.regions = new List<Region>(); this.ReParse(); this.buffer.Changed += BufferChanged; }
구현에 GetTags 태그를 인스턴스화하여 메서드를 포함 합니다. 범위는 NormalizedSpanCollection 메서드에 전달 된이 경우 항상 될 수 있지만 연속적입니다. 이 메서드는 새 태그 범위를 개요 영역 각각에 대해 인스턴스화합니다.
Public Function GetTags(ByVal spans As NormalizedSnapshotSpanCollection) As IEnumerable(Of ITagSpan(Of IOutliningRegionTag)) Implements ITagger(Of Microsoft.VisualStudio.Text.Tagging.IOutliningRegionTag).GetTags If spans.Count = 0 Then Return Nothing Exit Function End If Dim currentRegions As List(Of Region) = Me.regions Dim currentSnapshot As ITextSnapshot = Me.snapshot Dim entire As SnapshotSpan = New SnapshotSpan(spans(0).Start, spans(spans.Count - 1).[End]).TranslateTo(currentSnapshot, SpanTrackingMode.EdgeExclusive) Dim startLineNumber As Integer = entire.Start.GetContainingLine().LineNumber Dim endLineNumber As Integer = entire.[End].GetContainingLine().LineNumber Dim list As List(Of ITagSpan(Of IOutliningRegionTag)) list = New List(Of ITagSpan(Of IOutliningRegionTag))() For Each region In currentRegions If region.StartLine <= endLineNumber AndAlso region.EndLine >= startLineNumber Then Dim startLine = currentSnapshot.GetLineFromLineNumber(region.StartLine) Dim endLine = currentSnapshot.GetLineFromLineNumber(region.EndLine) 'the region starts at the beginning of the "[", and goes until the *end* of the line that contains the "]". list.Add(New TagSpan(Of IOutliningRegionTag)(New SnapshotSpan(startLine.Start + region.StartOffset, endLine.End), New OutliningRegionTag(False, False, ellipsis, hoverText))) End If Next Return list End Function
public IEnumerable<ITagSpan<IOutliningRegionTag>> GetTags(NormalizedSnapshotSpanCollection spans) { if (spans.Count == 0) yield break; List<Region> currentRegions = this.regions; ITextSnapshot currentSnapshot = this.snapshot; SnapshotSpan entire = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End).TranslateTo(currentSnapshot, SpanTrackingMode.EdgeExclusive); int startLineNumber = entire.Start.GetContainingLine().LineNumber; int endLineNumber = entire.End.GetContainingLine().LineNumber; foreach (var region in currentRegions) { if (region.StartLine <= endLineNumber && region.EndLine >= startLineNumber) { var startLine = currentSnapshot.GetLineFromLineNumber(region.StartLine); var endLine = currentSnapshot.GetLineFromLineNumber(region.EndLine); //the region starts at the beginning of the "[", and goes until the *end* of the line that contains the "]". yield return new TagSpan<IOutliningRegionTag>( new SnapshotSpan(startLine.Start + region.StartOffset, endLine.End), new OutliningRegionTag(false, false, ellipsis, hoverText)); } } }
선언 된 TagsChanged 이벤트 처리기입니다.
Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs) Implements ITagger(Of IOutliningRegionTag).TagsChanged
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
추가 된 BufferChanged 에 응답 하는 이벤트 처리기 Changed 이벤트 버퍼가 텍스트를 구문 분석 하 여.
Private Sub BufferChanged(ByVal sender As Object, ByVal e As TextContentChangedEventArgs) ' If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll eventually get another change event). If e.After IsNot buffer.CurrentSnapshot Then Exit Sub End If Me.ReParse() End Sub
void BufferChanged(object sender, TextContentChangedEventArgs e) { // If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll eventually get another change event). if (e.After != buffer.CurrentSnapshot) return; this.ReParse(); }
버퍼 구문 분석을 수행 하는 메서드를 추가 합니다. 여기에 제공 된 예는 설명을 위한 것입니다. 동기적으로 중첩 된 개요 영역 버퍼 구문 분석합니다.
Private Sub ReParse() Dim newSnapshot As ITextSnapshot = buffer.CurrentSnapshot Dim newRegions As New List(Of Region)() 'keep the current (deepest) partial region, which will have ' references to any parent partial regions. Dim currentRegion As PartialRegion = Nothing For Each line In newSnapshot.Lines Dim regionStart As Integer = -1 Dim text As String = line.GetText() 'lines that contain a "[" denote the start of a new region. If text.IndexOf(startHide, StringComparison.Ordinal) <> -1 Then regionStart = text.IndexOf(startHide, StringComparison.Ordinal) Dim currentLevel As Integer = If((currentRegion IsNot Nothing), currentRegion.Level, 1) Dim newLevel As Integer If Not TryGetLevel(text, regionStart, newLevel) Then newLevel = currentLevel + 1 End If 'levels are the same and we have an existing region; 'end the current region and start the next If currentLevel = newLevel AndAlso currentRegion IsNot Nothing Then Dim newRegion = New Region() newRegion.Level = currentRegion.Level newRegion.StartLine = currentRegion.StartLine newRegion.StartOffset = currentRegion.StartOffset newRegion.EndLine = line.LineNumber newRegions.Add(newRegion) currentRegion = New PartialRegion() currentRegion.Level = newLevel currentRegion.StartLine = line.LineNumber currentRegion.StartOffset = regionStart currentRegion.PartialParent = currentRegion.PartialParent Else 'this is a new (sub)region currentRegion = New PartialRegion() currentRegion.Level = newLevel currentRegion.StartLine = line.LineNumber currentRegion.StartOffset = regionStart currentRegion.PartialParent = currentRegion End If 'lines that contain "]" denote the end of a region ElseIf (text.IndexOf(endHide, StringComparison.Ordinal)) <> -1 Then regionStart = text.IndexOf(endHide, StringComparison.Ordinal) Dim currentLevel As Integer = If((currentRegion IsNot Nothing), currentRegion.Level, 1) Dim closingLevel As Integer If Not TryGetLevel(text, regionStart, closingLevel) Then closingLevel = currentLevel End If 'the regions match If currentRegion IsNot Nothing AndAlso currentLevel = closingLevel Then Dim newRegion As Region newRegion = New Region() newRegion.Level = currentLevel newRegion.StartLine = currentRegion.StartLine newRegion.StartOffset = currentRegion.StartOffset newRegion.EndLine = line.LineNumber newRegions.Add(newRegion) currentRegion = currentRegion.PartialParent End If End If Next 'determine the changed span, and send a changed event with the new spans Dim oldSpans As New List(Of Span)(Me.regions.[Select](Function(r) AsSnapshotSpan(r, Me.snapshot).TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive).Span)) Dim newSpans As New List(Of Span)(newRegions.[Select](Function(r) AsSnapshotSpan(r, newSnapshot).Span)) Dim oldSpanCollection As New NormalizedSpanCollection(oldSpans) Dim newSpanCollection As New NormalizedSpanCollection(newSpans) 'the changed regions are regions that appear in one set or the other, but not both. Dim removed As NormalizedSpanCollection = NormalizedSpanCollection.Difference(oldSpanCollection, newSpanCollection) Dim changeStart As Integer = Integer.MaxValue Dim changeEnd As Integer = -1 If removed.Count > 0 Then changeStart = removed(0).Start changeEnd = removed(removed.Count - 1).[End] End If If newSpans.Count > 0 Then changeStart = Math.Min(changeStart, newSpans(0).Start) changeEnd = Math.Max(changeEnd, newSpans(newSpans.Count - 1).[End]) End If Me.snapshot = newSnapshot Me.regions = newRegions If changeStart <= changeEnd Then Dim snap As ITextSnapshot = Me.snapshot RaiseEvent TagsChanged(Me, New SnapshotSpanEventArgs(New SnapshotSpan(Me.snapshot, Span.FromBounds(changeStart, changeEnd)))) End If End Sub
void ReParse() { ITextSnapshot newSnapshot = buffer.CurrentSnapshot; List<Region> newRegions = new List<Region>(); //keep the current (deepest) partial region, which will have // references to any parent partial regions. PartialRegion currentRegion = null; foreach (var line in newSnapshot.Lines) { int regionStart = -1; string text = line.GetText(); //lines that contain a "[" denote the start of a new region. if ((regionStart = text.IndexOf(startHide, StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int newLevel; if (!TryGetLevel(text, regionStart, out newLevel)) newLevel = currentLevel + 1; //levels are the same and we have an existing region; //end the current region and start the next if (currentLevel == newLevel && currentRegion != null) { newRegions.Add(new Region() { Level = currentRegion.Level, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, EndLine = line.LineNumber }); currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion.PartialParent }; } //this is a new (sub)region else { currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion }; } } //lines that contain "]" denote the end of a region else if ((regionStart = text.IndexOf(endHide, StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int closingLevel; if (!TryGetLevel(text, regionStart, out closingLevel)) closingLevel = currentLevel; //the regions match if (currentRegion != null && currentLevel == closingLevel) { newRegions.Add(new Region() { Level = currentLevel, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, EndLine = line.LineNumber }); currentRegion = currentRegion.PartialParent; } } } //determine the changed span, and send a changed event with the new spans List<Span> oldSpans = new List<Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive) .Span)); List<Span> newSpans = new List<Span>(newRegions.Select(r => AsSnapshotSpan(r, newSnapshot).Span)); NormalizedSpanCollection oldSpanCollection = new NormalizedSpanCollection(oldSpans); NormalizedSpanCollection newSpanCollection = new NormalizedSpanCollection(newSpans); //the changed regions are regions that appear in one set or the other, but not both. NormalizedSpanCollection removed = NormalizedSpanCollection.Difference(oldSpanCollection, newSpanCollection); int changeStart = int.MaxValue; int changeEnd = -1; if (removed.Count > 0) { changeStart = removed[0].Start; changeEnd = removed[removed.Count - 1].End; } if (newSpans.Count > 0) { changeStart = Math.Min(changeStart, newSpans[0].Start); changeEnd = Math.Max(changeEnd, newSpans[newSpans.Count - 1].End); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; if (this.TagsChanged != null) this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } }
다음 도우미 메서드 1 왼쪽에 있는 중괄호 쌍이 되도록의 개요 수준을 나타내는 정수를 가져옵니다.
Private Shared Function TryGetLevel(ByVal text As String, ByVal startIndex As Integer, ByRef level As Integer) As Boolean level = -1 If text.Length > startIndex + 3 Then If Integer.TryParse(text.Substring(startIndex + 1), level) Then Return True End If End If Return False End Function
static bool TryGetLevel(string text, int startIndex, out int level) { level = -1; if (text.Length > startIndex + 3) { if (int.TryParse(text.Substring(startIndex + 1), out level)) return true; } return false; }
다음 도우미 메서드 (이 단원의 뒷부분에 정의 된) 영역을 Snapshotspan로 변환 합니다.
Private Shared Function AsSnapshotSpan(ByVal region As Region, ByVal snapshot As ITextSnapshot) As SnapshotSpan Dim startLine = snapshot.GetLineFromLineNumber(region.StartLine) Dim endLine = If((region.StartLine = region.EndLine), startLine, snapshot.GetLineFromLineNumber(region.EndLine)) Return New SnapshotSpan(startLine.Start + region.StartOffset, endLine.[End]) End Function
static SnapshotSpan AsSnapshotSpan(Region region, ITextSnapshot snapshot) { var startLine = snapshot.GetLineFromLineNumber(region.StartLine); var endLine = (region.StartLine == region.EndLine) ? startLine : snapshot.GetLineFromLineNumber(region.EndLine); return new SnapshotSpan(startLine.Start + region.StartOffset, endLine.End); }
다음 코드는 설명을 위한 것입니다. 줄 번호와 오프셋의 시작 부분에 개요 표시/숨기기 영역 및 부모 영역 (있을 경우)에 대 한 참조를 포함 하는 PartialRegion 클래스를 정의 합니다. 이 개요 영역은 중첩 된 파서를 설정할 수 있습니다. 파생된 영역 클래스의 개요는 영역의 끝 줄 번호에 대 한 참조가 포함 되어 있습니다.
Private Class PartialRegion Private _StartLine As Integer Public Property StartLine() As Integer Get Return _StartLine End Get Set(ByVal value As Integer) _StartLine = value End Set End Property Private _StartOffset As Integer Public Property StartOffset() As Integer Get Return _StartOffset End Get Set(ByVal value As Integer) _StartOffset = value End Set End Property Private _Level As Integer Public Property Level() As Integer Get Return _Level End Get Set(ByVal value As Integer) _Level = value End Set End Property Private _PartialParent As PartialRegion Public Property PartialParent() As PartialRegion Get Return _PartialParent End Get Set(ByVal value As PartialRegion) _PartialParent = value End Set End Property End Class Private Class Region Inherits PartialRegion Private _EndLine As Integer Public Property EndLine() As Integer Get Return _EndLine End Get Set(ByVal value As Integer) _EndLine = value End Set End Property End Class
class PartialRegion { public int StartLine { get; set; } public int StartOffset { get; set; } public int Level { get; set; } public PartialRegion PartialParent { get; set; } } class Region : PartialRegion { public int EndLine { get; set; } }
Tagger 공급자 구현
사용자에 대 한 tagger tagger 공급자를 내보내야 합니다. Tagger 공급자를 만듭니다는 OutliningTagger 의 내용 형식을 "text" 또는 다른 반환 버퍼에 대 한 있는 OutliningTagger 하나를 버퍼에 이미 있는 경우.
Tagger 공급자를 구현 하려면
라는 클래스를 만드는 OutliningTaggerProvider 는 구현 ITaggerProvider, TagType 및 ContentType 특성을 내보냅니다.
<Export(GetType(ITaggerProvider))> _ <TagType(GetType(IOutliningRegionTag))> _ <ContentType("text")> _ Friend NotInheritable Class OutliningTaggerProvider Implements ITaggerProvider
[Export(typeof(ITaggerProvider))] [TagType(typeof(IOutliningRegionTag))] [ContentType("text")] internal sealed class OutliningTaggerProvider : ITaggerProvider
구현에서 CreateTagger``1 메서드를 추가 하 여는 OutliningTagger 버퍼의 속성.
Public Function CreateTagger(Of T As ITag)(ByVal buffer As ITextBuffer) As ITagger(Of T) Implements ITaggerProvider.CreateTagger 'create a single tagger for each buffer. Dim sc As Func(Of ITagger(Of T)) = Function() TryCast(New OutliningTagger(buffer), ITagger(Of T)) Return buffer.Properties.GetOrCreateSingletonProperty(Of ITagger(Of T))(sc) End Function
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag { //create a single tagger for each buffer. Func<ITagger<T>> sc = delegate() { return new OutliningTagger(buffer) as ITagger<T>; }; return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc); }
빌드 및 코드 테스트
이 코드를 테스트 하려면 OutlineRegionTest 솔루션을 빌드하고 실험 인스턴스를 실행 합니다.
빌드 및 OutlineRegionTest 솔루션을 테스트 하려면
솔루션을 빌드합니다.
이 프로젝트를 디버거에서 실행할 때 Visual Studio 두 번째 인스턴스를 인스턴스화합니다.
텍스트 파일을 만듭니다. 여는 중괄호와 닫는 중괄호에 포함 된 일부 텍스트를 입력 합니다.
[ Hello ]
양쪽 중괄호를 포함 하는 개요 영역이 있어야 합니다. 개요 표시/숨기기 영역을 축소 하려면 빼기 기호 왼쪽에 여는 중괄호를 누릅니다 수 있습니다. 때 지역입니다 축소, 줄임표 기호 (...) 축소 된 영역 및 텍스트를 포함 하는 팝업의 왼쪽에 나타납니다 호버 텍스트 줄임표 위로 포인터를 이동 하면 나타납니다.