연습: 일치 하는 중괄호를 표시
중괄호 일치 중괄호 일치 시킬 정의 하 고 캐럿 중괄호 중 하나에 있을 때 다음 텍스트 마커 태그 일치 하는 중괄호를 추가 같은 언어를 기반으로 기능을 구현할 수 있습니다. 중괄호는 언어 측면에서 정의할 수 있습니다 또는 고유한 파일 이름 확장명 및 콘텐츠 형식 정의 하 고 해당 형식으로 태그를 적용할 수 있습니다 하거나 기존 콘텐츠 형식 (예: "text")에 태그를 적용할 수 있습니다. 다음 연습에서는 중괄호 일치 하는 콘텐츠 형식이 "text"에 태그를 적용 하는 방법을 보여 줍니다.
사전 요구 사항
이 연습을 완료 하려면 설치 해야 해당 Visual Studio 2010 SDK.
참고
Visual Studio SDK에 대 한 자세한 내용은 참조 하십시오. Visual Studio 개요를 확장합니다..Visual Studio SDK를 다운로드 하는 방법를 참조 하십시오. Visual Studio 확장성 개발자 센터 MSDN 웹 사이트에서.
관리 되는 확장성 프레임 워크 (MEF) 프로젝트 만들기
MEF 프로젝트를 만들려면
편집기 분류자 프로젝트를 만듭니다. 솔루션의 이름을 BraceMatchingTest.
VSIX 매니페스트 편집기에서 source.extension.vsixmanifest 파일을 엽니다.
Content MEF 구성 요소 콘텐츠 형식 및 해당 제목 포함은 Path Bracematchingtest.dll으로 설정 됩니다.
저장 하 고 source.extension.vsixmanifest를 닫습니다.
기존 클래스 파일을 삭제 합니다.
Tagger 일치 하는 중괄호를 구현 합니다.
중괄호 강조 Visual Studio 사용 되는 것과 유사한 효과 얻을 수 있는 tagger 형식 구현 TextMarkerTag. 다음 코드는 tagger 중괄호 쌍 중첩의 모든 수준에 대해 정의 하는 방법을 보여 줍니다. 이 예에서는 중괄호 쌍입니다. {} tagger 생성자에서 정의 되 고 전체 언어 구현에 관련 된 중괄호 쌍 언어 사양에 정의 됩니다.
Tagger 일치 하는 중괄호를 구현 하려면
클래스 파일을 추가 하 고 이름을 중괄호 일치 합니다.
다음 네임 스페이스를 가져옵니다.
Imports System.ComponentModel.Composition Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Tagging Imports Microsoft.VisualStudio.Utilities
using System; using System.Linq; using System.Collections.Generic; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities;
클래스를 정의 BraceMatchingTagger 는 상속에서 ITagger 형식 TextMarkerTag.
Friend Class BraceMatchingTagger Implements ITagger(Of TextMarkerTag)
internal class BraceMatchingTagger : ITagger<TextMarkerTag>
텍스트 보기, 소스 버퍼 및 현재 스냅샷 지점 및 또한 중괄호 쌍의 집합에 대 한 속성을 추가 합니다.
Private _View As ITextView Private Property View() As ITextView Get Return _View End Get Set(ByVal value As ITextView) _View = value End Set End Property Private _SourceBuffer As ITextBuffer Private Property SourceBuffer() As ITextBuffer Get Return _SourceBuffer End Get Set(ByVal value As ITextBuffer) _SourceBuffer = value End Set End Property Private _CurrentChar As System.Nullable(Of SnapshotPoint) Private Property CurrentChar() As System.Nullable(Of SnapshotPoint) Get Return _CurrentChar End Get Set(ByVal value As System.Nullable(Of SnapshotPoint)) _CurrentChar = value End Set End Property Private m_braceList As Dictionary(Of Char, Char)
ITextView View { get; set; } ITextBuffer SourceBuffer { get; set; } SnapshotPoint? CurrentChar { get; set; } private Dictionary<char, char> m_braceList;
Tagger 생성자에서 속성을 설정 하 고 뷰 변경 이벤트를 등록 PositionChanged 및 LayoutChanged. 이 예제에서는 설명을 돕기 위해, 일치 하는 쌍도 생성자에서 정의 됩니다.
Friend Sub New(ByVal view As ITextView, ByVal sourceBuffer As ITextBuffer) 'here the keys are the open braces, and the values are the close braces m_braceList = New Dictionary(Of Char, Char)() m_braceList.Add("{"c, "}"c) m_braceList.Add("["c, "]"c) m_braceList.Add("("c, ")"c) Me.View = view Me.SourceBuffer = sourceBuffer Me.CurrentChar = Nothing AddHandler Me.View.Caret.PositionChanged, AddressOf Me.CaretPositionChanged AddHandler Me.View.LayoutChanged, AddressOf Me.ViewLayoutChanged End Sub
internal BraceMatchingTagger(ITextView view, ITextBuffer sourceBuffer) { //here the keys are the open braces, and the values are the close braces m_braceList = new Dictionary<char, char>(); m_braceList.Add('{', '}'); m_braceList.Add('[', ']'); m_braceList.Add('(', ')'); this.View = view; this.SourceBuffer = sourceBuffer; this.CurrentChar = null; this.View.Caret.PositionChanged += CaretPositionChanged; this.View.LayoutChanged += ViewLayoutChanged; }
일환으로는 ITagger 구현에서 TagsChanged 이벤트를 선언 합니다.
Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs) _ Implements ITagger(Of TextMarkerTag).TagsChanged
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
현재 캐럿 위치를 업데이트 하는 이벤트 처리기는 CurrentChar 속성과 TagsChanged 이벤트가 발생 합니다.
Private Sub ViewLayoutChanged(ByVal sender As Object, ByVal e As TextViewLayoutChangedEventArgs) If e.NewSnapshot IsNot e.OldSnapshot Then 'make sure that there has really been a change UpdateAtCaretPosition(View.Caret.Position) End If End Sub Private Sub CaretPositionChanged(ByVal sender As Object, ByVal e As CaretPositionChangedEventArgs) UpdateAtCaretPosition(e.NewPosition) End Sub Private Sub UpdateAtCaretPosition(ByVal caretPosition As CaretPosition) CurrentChar = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity) If Not CurrentChar.HasValue Then Exit Sub End If RaiseEvent TagsChanged(Me, New SnapshotSpanEventArgs(New SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length))) End Sub
void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { if (e.NewSnapshot != e.OldSnapshot) //make sure that there has really been a change { UpdateAtCaretPosition(View.Caret.Position); } } void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { UpdateAtCaretPosition(e.NewPosition); } void UpdateAtCaretPosition(CaretPosition caretPosition) { CurrentChar = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity); if (!CurrentChar.HasValue) return; var tempEvent = TagsChanged; if (tempEvent != null) tempEvent(this, new SnapshotSpanEventArgs(new SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length))); }
구현에서 GetTags 일치 하는 메서드가 중괄호가 하나 현재 문자는 여는 중괄호 또는 이전 문자 Visual Studio와 같이 닫는 중괄호를 되 면 경우. 일치 항목이 발견 되 면이 메서드는 두 태그는 여는 중괄호와 닫는 중괄호를 인스턴스화합니다.
Public Function GetTags(ByVal spans As NormalizedSnapshotSpanCollection) As IEnumerable(Of ITagSpan(Of TextMarkerTag)) Implements ITagger(Of Microsoft.VisualStudio.Text.Tagging.TextMarkerTag).GetTags If spans.Count = 0 Then 'there is no content in the buffer Exit Function End If 'don't do anything if the current SnapshotPoint is not initialized or at the end of the buffer If Not CurrentChar.HasValue OrElse CurrentChar.Value.Position >= CurrentChar.Value.Snapshot.Length Then Exit Function End If 'hold on to a snapshot of the current character Dim currentChar__1 As SnapshotPoint = CurrentChar.Value 'if the requested snapshot isn't the same as the one the brace is on, translate our spans to the expected snapshot If spans(0).Snapshot IsNot currentChar__1.Snapshot Then currentChar__1 = currentChar__1.TranslateTo(spans(0).Snapshot, PointTrackingMode.Positive) End If 'get the current char and the previous char Dim currentText As Char = currentChar__1.GetChar() Dim lastChar As SnapshotPoint = If(CInt(currentChar__1) = 0, currentChar__1, currentChar__1 - 1) 'if currentChar is 0 (beginning of buffer), don't move it back Dim lastText As Char = lastChar.GetChar() Dim pairSpan As New SnapshotSpan() If m_braceList.ContainsKey(currentText) Then 'the key is the open brace Dim closeChar As Char m_braceList.TryGetValue(currentText, closeChar) If BraceMatchingTagger.FindMatchingCloseChar(currentChar__1, currentText, closeChar, View.TextViewLines.Count, pairSpan) = True Then Exit Function End If ElseIf m_braceList.ContainsValue(lastText) Then 'the value is the close brace, which is the *previous* character Dim open = From n In m_braceList _ Where n.Value.Equals(lastText) _ Select n.Key If BraceMatchingTagger.FindMatchingOpenChar(lastChar, CChar(open.ElementAt(0)), lastText, View.TextViewLines.Count, pairSpan) = True Then Exit Function End If End If End Function
public IEnumerable<ITagSpan<TextMarkerTag>> GetTags(NormalizedSnapshotSpanCollection spans) { if (spans.Count == 0) //there is no content in the buffer yield break; //don't do anything if the current SnapshotPoint is not initialized or at the end of the buffer if (!CurrentChar.HasValue || CurrentChar.Value.Position >= CurrentChar.Value.Snapshot.Length) yield break; //hold on to a snapshot of the current character SnapshotPoint currentChar = CurrentChar.Value; //if the requested snapshot isn't the same as the one the brace is on, translate our spans to the expected snapshot if (spans[0].Snapshot != currentChar.Snapshot) { currentChar = currentChar.TranslateTo(spans[0].Snapshot, PointTrackingMode.Positive); } //get the current char and the previous char char currentText = currentChar.GetChar(); SnapshotPoint lastChar = currentChar == 0 ? currentChar : currentChar - 1; //if currentChar is 0 (beginning of buffer), don't move it back char lastText = lastChar.GetChar(); SnapshotSpan pairSpan = new SnapshotSpan(); if (m_braceList.ContainsKey(currentText)) //the key is the open brace { char closeChar; m_braceList.TryGetValue(currentText, out closeChar); if (BraceMatchingTagger.FindMatchingCloseChar(currentChar, currentText, closeChar, View.TextViewLines.Count, out pairSpan) == true) { yield return new TagSpan<TextMarkerTag>(new SnapshotSpan(currentChar, 1), new TextMarkerTag("blue")); yield return new TagSpan<TextMarkerTag>(pairSpan, new TextMarkerTag("blue")); } } else if (m_braceList.ContainsValue(lastText)) //the value is the close brace, which is the *previous* character { var open = from n in m_braceList where n.Value.Equals(lastText) select n.Key; if (BraceMatchingTagger.FindMatchingOpenChar(lastChar, (char)open.ElementAt<char>(0), lastText, View.TextViewLines.Count, out pairSpan) == true) { yield return new TagSpan<TextMarkerTag>(new SnapshotSpan(lastChar, 1), new TextMarkerTag("blue")); yield return new TagSpan<TextMarkerTag>(pairSpan, new TextMarkerTag("blue")); } } }
다음 개인 메서드 모든 중첩 수준에서 일치 하는 중괄호 찾기. 첫 번째 방법은 열린 문자와 일치 하는 닫기 문자를 찾습니다.
Private Shared Function FindMatchingCloseChar(ByVal startPoint As SnapshotPoint, ByVal open As Char, ByVal close As Char, ByVal maxLines As Integer, ByRef pairSpan As SnapshotSpan) As Boolean pairSpan = New SnapshotSpan(startPoint.Snapshot, 1, 1) Dim line As ITextSnapshotLine = startPoint.GetContainingLine() Dim lineText As String = line.GetText() Dim lineNumber As Integer = line.LineNumber Dim offset As Integer = startPoint.Position - line.Start.Position + 1 Dim stopLineNumber As Integer = startPoint.Snapshot.LineCount - 1 If maxLines > 0 Then stopLineNumber = Math.Min(stopLineNumber, lineNumber + maxLines) End If Dim openCount As Integer = 0 While True 'walk the entire line While offset < line.Length Dim currentChar As Char = lineText(offset) If currentChar = close Then 'found the close character If openCount > 0 Then openCount -= 1 Else 'found the matching close pairSpan = New SnapshotSpan(startPoint.Snapshot, line.Start + offset, 1) Return True End If ElseIf currentChar = open Then ' this is another open openCount += 1 End If offset += 1 End While 'move on to the next line If System.Threading.Interlocked.Increment(lineNumber) > stopLineNumber Then Exit While End If line = line.Snapshot.GetLineFromLineNumber(lineNumber) lineText = line.GetText() offset = 0 End While Return False End Function
private static bool FindMatchingCloseChar(SnapshotPoint startPoint, char open, char close, int maxLines, out SnapshotSpan pairSpan) { pairSpan = new SnapshotSpan(startPoint.Snapshot, 1, 1); ITextSnapshotLine line = startPoint.GetContainingLine(); string lineText = line.GetText(); int lineNumber = line.LineNumber; int offset = startPoint.Position - line.Start.Position + 1; int stopLineNumber = startPoint.Snapshot.LineCount - 1; if (maxLines > 0) stopLineNumber = Math.Min(stopLineNumber, lineNumber + maxLines); int openCount = 0; while (true) { //walk the entire line while (offset < line.Length) { char currentChar = lineText[offset]; if (currentChar == close) //found the close character { if (openCount > 0) { openCount--; } else //found the matching close { pairSpan = new SnapshotSpan(startPoint.Snapshot, line.Start + offset, 1); return true; } } else if (currentChar == open) // this is another open { openCount++; } offset++; } //move on to the next line if (++lineNumber > stopLineNumber) break; line = line.Snapshot.GetLineFromLineNumber(lineNumber); lineText = line.GetText(); offset = 0; } return false; }
다음 도우미 메서드 닫기 문자와 일치 하는 열린 문자를 찾습니다.
Private Shared Function FindMatchingOpenChar(ByVal startPoint As SnapshotPoint, ByVal open As Char, ByVal close As Char, ByVal maxLines As Integer, ByRef pairSpan As SnapshotSpan) As Boolean pairSpan = New SnapshotSpan(startPoint, startPoint) Dim line As ITextSnapshotLine = startPoint.GetContainingLine() Dim lineNumber As Integer = line.LineNumber Dim offset As Integer = startPoint - line.Start - 1 'move the offset to the character before this one 'if the offset is negative, move to the previous line If offset < 0 Then line = line.Snapshot.GetLineFromLineNumber(System.Threading.Interlocked.Decrement(lineNumber)) offset = line.Length - 1 End If Dim lineText As String = line.GetText() Dim stopLineNumber As Integer = 0 If maxLines > 0 Then stopLineNumber = Math.Max(stopLineNumber, lineNumber - maxLines) End If Dim closeCount As Integer = 0 While True ' Walk the entire line While offset >= 0 Dim currentChar As Char = lineText(offset) If currentChar = open Then If closeCount > 0 Then closeCount -= 1 Else ' We've found the open character pairSpan = New SnapshotSpan(line.Start + offset, 1) 'we just want the character itself Return True End If ElseIf currentChar = close Then closeCount += 1 End If offset -= 1 End While ' Move to the previous line If System.Threading.Interlocked.Decrement(lineNumber) < stopLineNumber Then Exit While End If line = line.Snapshot.GetLineFromLineNumber(lineNumber) lineText = line.GetText() offset = line.Length - 1 End While Return False End Function
private static bool FindMatchingOpenChar(SnapshotPoint startPoint, char open, char close, int maxLines, out SnapshotSpan pairSpan) { pairSpan = new SnapshotSpan(startPoint, startPoint); ITextSnapshotLine line = startPoint.GetContainingLine(); int lineNumber = line.LineNumber; int offset = startPoint - line.Start - 1; //move the offset to the character before this one //if the offset is negative, move to the previous line if (offset < 0) { line = line.Snapshot.GetLineFromLineNumber(--lineNumber); offset = line.Length - 1; } string lineText = line.GetText(); int stopLineNumber = 0; if (maxLines > 0) stopLineNumber = Math.Max(stopLineNumber, lineNumber - maxLines); int closeCount = 0; while (true) { // Walk the entire line while (offset >= 0) { char currentChar = lineText[offset]; if (currentChar == open) { if (closeCount > 0) { closeCount--; } else // We've found the open character { pairSpan = new SnapshotSpan(line.Start + offset, 1); //we just want the character itself return true; } } else if (currentChar == close) { closeCount++; } offset--; } // Move to the previous line if (--lineNumber < stopLineNumber) break; line = line.Snapshot.GetLineFromLineNumber(lineNumber); lineText = line.GetText(); offset = line.Length - 1; } return false; }
중괄호 일치 Tagger 공급자 구현
Tagger를 구현 하는 외에 구현 하 고 tagger 공급자 내보내기 합니다. 이 경우 공급자의 내용 형식을 "text"입니다. 즉, 중괄호 일치 하는 모든 텍스트 파일의 형식에 나타납니다 있지만 보다 자세한 구현 중괄호 일치는 특정 콘텐츠 형식에만 적용 됩니다.
중괄호 일치 tagger 공급자를 구현 하려면
상속 tagger 공급자를 선언 IViewTaggerProvider, BraceMatchingTaggerProvider, 이름을 지정 하 고 내보내기를 ContentTypeAttribute "텍스트"와 a TagTypeAttribute 의 TextMarkerTag.
<Export(GetType(IViewTaggerProvider))> _ <ContentType("text")> _ <TagType(GetType(TextMarkerTag))> _ Friend Class BraceMatchingTaggerProvider Implements IViewTaggerProvider
[Export(typeof(IViewTaggerProvider))] [ContentType("text")] [TagType(typeof(TextMarkerTag))] internal class BraceMatchingTaggerProvider : IViewTaggerProvider
구현에서 CreateTagger``1 메서드는 Bracematchingtagger를 인스턴스화할 수 있습니다.
Public Function CreateTagger(Of T As ITag)(ByVal textView As ITextView, ByVal buffer As ITextBuffer) As ITagger(Of T) Implements IViewTaggerProvider.CreateTagger If textView Is Nothing Then Return Nothing End If 'provide highlighting only on the top-level buffer If textView.TextBuffer IsNot buffer Then Return Nothing End If Return TryCast(New BraceMatchingTagger(textView, buffer), ITagger(Of T)) End Function
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag { if (textView == null) return null; //provide highlighting only on the top-level buffer if (textView.TextBuffer != buffer) return null; return new BraceMatchingTagger(textView, buffer) as ITagger<T>; }
빌드 및 코드 테스트
이 코드를 테스트 하려면 BraceMatchingTest 솔루션을 빌드하고 실험 인스턴스를 실행 합니다.
빌드 및 BraceMatchingTest 솔루션을 테스트 하려면
솔루션을 빌드합니다.
이 프로젝트를 디버거에서 실행할 때 Visual Studio 두 번째 인스턴스를 인스턴스화합니다.
일치 하는 중괄호를 포함 하는 텍스트를 입력 하 고 텍스트 파일을 만듭니다.
hello { goodbye} {} {hello}
해당 중괄호와 일치 하는 닫는 중괄호는 여는 중괄호 앞에 캐럿을 배치할 때 강조 표시 합니다. 닫기 괄호 바로 뒤에 커서를 배치 하면 해당 중괄호와 일치 하는 중괄호를 모두 강조 표시 합니다.