共用方式為


逐步解說: 顯示對稱的括號

您可以實作語言為基礎的功能,例如括號來定義您要比對,大括的號,以及當插入號位於其中一個大括號,然後將文字標記標籤新增至對稱的括號對稱。 您可以定義大括號內的一種語言,您可以定義您自己的檔案名稱副檔名和內容類型,並將標籤套用到只是該型別,或是您可以將標籤套用到現有的內容類型 (例如,"text")。 下列逐步解說會示範如何套用括號對稱的 「 文字 」 的內容類型的標籤。

必要條件

若要完成這個逐步解說中,您必須安裝Visual Studio 2010 SDK。

注意事項注意事項

如需有關 Visual Studio 的 SDK 的詳細資訊,請參閱擴充 Visual Studio 的概觀。若要了解如何下載 Visual Studio 的 SDK,請參閱Visual Studio 擴充性開發人員中心 MSDN 網站上。

建立受管理的擴充性架構 (MEF) 專案

若要建立 MEF 專案

  1. 建立編輯器類別器的專案。 為方案命名 BraceMatchingTest。

  2. VSIX 資訊清單編輯器中開啟 source.extension.vsixmanifest 檔案。

  3. 請確定Content名包含 MEF 元件的內容類型,並Path設定為 BraceMatchingTest.dll。

  4. 儲存並關閉 Source.extension.vsixmanifest。

  5. 刪除現有的類別檔案。

實作符合 Tagger 的大括號

若要取得效果相似 Visual Studio 中所使用的反白顯示在括號,您可以實作型別的 tagger TextMarkerTag。 下列程式碼會示範如何定義用於任何層級的巢狀的大括號括號 tagger。 在這個範例中,大括號會配對 []。 [],並 {} 定義在 tagger 的建構函式,但在完整的語言實作相關的大括號組應該定義語言規格中。

若要實作的括號對稱 tagger

  1. 將類別檔案,並命名為 BraceMatching。

  2. 匯入下列命名空間。

    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;
    
  3. 定義類別BraceMatchingTagger繼承自ITagger型別的TextMarkerTag

    Friend Class BraceMatchingTagger
        Implements ITagger(Of TextMarkerTag)
    
    internal class BraceMatchingTagger : ITagger<TextMarkerTag>
    
  4. 加入屬性的文字檢視、 來源緩衝區,並在目前的快照集點,以及一組大括號組。

    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;
    
  5. Tagger 建構函式,在設定屬性及訂閱檢視 change 事件PositionChangedLayoutChanged。 在這個範例中,為說明用途,對稱的括號中也有定義建構函式。

    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;
    }
    
  6. 做為一部分的ITagger的實作中,宣告 「 TagsChanged 」 事件。

    Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs) _
        Implements ITagger(Of TextMarkerTag).TagsChanged
    
    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
    
  7. 事件處理常式會更新目前的插入號位置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)));
    }
    
  8. 實作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"));
            }
        }
    }
    
  9. 下列的私用方法找出在任何層級的巢狀結構對稱的括號。 第一種方法可找出符合開啟字元關閉字元:

    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;
    }
    
  10. 下列的 helper 方法會找出開啟比對關閉字元的字元:

    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 提供者。 在此情況下,提供者的內容類型是 「 文字 」。 這表示括號對稱會出現在所有類型的文字檔案,但簡實作會套用比只對特定的內容類型的大括號。

若要實作的大括號對稱 tagger 提供者

  1. 宣告繼承自 tagger 提供者IViewTaggerProvider、 其命名為 BraceMatchingTaggerProvider,並將其與匯出ContentTypeAttribute的 「 文字 」,並TagTypeAttributeTextMarkerTag

    <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
    
  2. 實作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 的解決方案

  1. 建置方案。

  2. 當您執行此專案在偵錯工具時,會執行個體化 Visual Studio 的第二個執行個體。

  3. 建立一個文字檔,並輸入一些文字,其中包括對稱的括號。

    hello {
    goodbye}
    
    {}
    
    {hello}
    
  4. 當您開啟的大括號之前插入號時,該的大括號和相符的右大括弧都應會反白顯示。 當您將游標的位置只會在右大括弧之後時,但是該的大括號,並開啟對稱的大括號應該會醒目提示。

請參閱

工作

逐步解說: 連結至檔案的副檔名的內容類型