Tutorial: Mostrar llaves coincidentesWalkthrough: Displaying Matching Braces

Puede implementar características basadas en lenguaje como relleno de llaves definiendo las llaves que desee buscar y, a continuación, agregar una etiqueta de marcador de texto a las llaves cuando el símbolo de intercalación está en una de las llaves.You can implement language-based features such as brace matching by defining the braces you want to match, and then adding a text marker tag to the matching braces when the caret is on one of the braces. Puede definir llaves en el contexto de un idioma, o puede definir su propio tipo de contenido y la extensión de nombre de archivo y las etiquetas se aplican a ese tipo, o puede aplicar las etiquetas a un tipo de contenido existente (por ejemplo, "text").You can define braces in the context of a language, or you can define your own file name extension and content type and apply the tags to just that type, or you can apply the tags to an existing content type (such as "text"). En el siguiente tutorial se muestra cómo aplicar etiquetas para el tipo de contenido "text" la coincidencia de llaves.The following walkthrough shows how to apply brace matching tags to the "text" content type.

Requisitos previosPrerequisites

A partir de Visual Studio 2015, no instale el SDK de Visual Studio desde el centro de descarga.Starting in Visual Studio 2015, you do not install the Visual Studio SDK from the download center. Se incluye como una característica opcional en el programa de instalación de Visual Studio.It is included as an optional feature in Visual Studio setup. También puede instalar el SDK de VS más adelante.You can also install the VS SDK later on. Para obtener más información, consulte instalar el SDK de Visual Studio.For more information, see Installing the Visual Studio SDK.

Crear un proyecto de Managed Extensibility Framework (MEF)Creating a Managed Extensibility Framework (MEF) Project

Para crear un nuevo proyecto de MEFTo create a MEF project

  1. Cree un proyecto de clasificador de editor.Create an Editor Classifier project. Llame a la solución BraceMatchingTest.Name the solution BraceMatchingTest.

  2. Agregar una plantilla de elementos de clasificador de Editor para el proyecto.Add an Editor Classifier item template to the project. Para obtener más información, consulte crear una extensión con una plantilla de elemento de Editor.For more information, see Creating an Extension with an Editor Item Template.

  3. Elimine los archivos de clase existentes.Delete the existing class files.

Implementar un etiquetador de coincidencia de llavesImplementing a Brace Matching Tagger

Para obtener un efecto similar al que se usa en Visual Studio de resaltado de llaves, puede implementar un etiquetador de tipo TextMarkerTag.To get a brace highlighting effect that resembles the one that is used in Visual Studio, you can implement a tagger of type TextMarkerTag. El código siguiente muestra cómo definir el etiquetador para los pares de llave en cualquier nivel de anidamiento.The following code shows how to define the tagger for brace pairs at any level of nesting. En este ejemplo, la llave de pares [].In this example, the brace pairs []. [] y {} se define en el constructor de etiquetador, pero en una implementación de lenguaje completo se definiría los pares de llave relevante en la especificación del lenguaje.[], and {} are defined in the tagger constructor, but in a full language implementation the relevant brace pairs would be defined in the language specification.

Para implementar un etiquetador de coincidencia de llavesTo implement a brace matching tagger

  1. Agregue un archivo de clase y asígnele el intercambio.Add a class file and name it BraceMatching.

  2. Importe los siguientes espacios de nombres.Import the following namespaces.

    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;
    
    Imports System.ComponentModel.Composition
    Imports Microsoft.VisualStudio.Text
    Imports Microsoft.VisualStudio.Text.Editor
    Imports Microsoft.VisualStudio.Text.Tagging
    Imports Microsoft.VisualStudio.Utilities
    
  3. Defina una clase BraceMatchingTagger que herede de ITagger<T> de tipo TextMarkerTag.Define a class BraceMatchingTagger that inherits from ITagger<T> of type TextMarkerTag.

    internal class BraceMatchingTagger : ITagger<TextMarkerTag>
    
    Friend Class BraceMatchingTagger
        Implements ITagger(Of TextMarkerTag)
    
  4. Agregar propiedades de la vista de texto, el búfer de origen y el punto de la instantánea actual y un conjunto de pares de llaves.Add properties for the text view, the source buffer, and the current snapshot point, and also a set of brace pairs.

    ITextView View { get; set; }
    ITextBuffer SourceBuffer { get; set; }
    SnapshotPoint? CurrentChar { get; set; }
    private Dictionary<char, char> m_braceList;
    
    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)
    
  5. En el constructor etiquetador, establezca las propiedades y suscribirse a los eventos de cambio de vista PositionChanged y LayoutChanged.In the tagger constructor, set the properties and subscribe to the view change events PositionChanged and LayoutChanged. En este ejemplo, con fines ilustrativos, también se definen los pares coincidentes en el constructor.In this example, for illustrative purposes, the matching pairs are also defined in the constructor.

    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;
    }
    
    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
    
  6. Como parte de la ITagger<T> implementación, declara un evento TagsChanged.As part of the ITagger<T> implementation, declare a TagsChanged event.

    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
    
    Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs) _
        Implements ITagger(Of TextMarkerTag).TagsChanged
    
  7. Los controladores de eventos actualizan la posición actual del símbolo de intercalación de la CurrentChar propiedad y generar el evento TagsChanged.The event handlers update the current caret position of the CurrentChar property and raise the TagsChanged event.

    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)));
    }
    
    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
    
  8. Implemente el GetTags método para que coincida con las llaves cualquiera cuando el carácter actual es una llave de apertura o cuando el carácter anterior es una llave de cierre, como en Visual Studio.Implement the GetTags method to match braces either when the current character is an open brace or when the previous character is a close brace, as in Visual Studio. Cuando se encuentra la coincidencia, este método crea una instancia de dos etiquetas, una para la llave de apertura y otro para la llave de cierre.When the match is found, this method instantiates two tags, one for the open brace and one for the close brace.

    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"));
            }
        }
    }
    
    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
    
  9. Los siguientes métodos privados encuentra la llave correspondiente en cualquier nivel de anidamiento.The following private methods find the matching brace at any level of nesting. El primer método busca el carácter de cierre que coincida con el carácter abierto:The first method finds the close character that matches the open character:

    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 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
    
  10. El método auxiliar siguiente busca el carácter abierto que coincide con un carácter de cierre:The following helper method finds the open character that matches a close character:

    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;
    }
    
    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
    

Implementar un proveedor del etiquetador de coincidencia de llavesImplementing a Brace Matching Tagger Provider

Además de implementar un etiquetador, también debe implementar y exportar un proveedor del etiquetador.In addition to implementing a tagger, you must also implement and export a tagger provider. En este caso, el tipo de contenido del proveedor es "text".In this case, the content type of the provider is "text". Esto significa que la coincidencia de llaves aparecerá en todos los tipos de archivos de texto, pero una implementación más completa se aplicaría solo a un tipo de contenido específico de la coincidencia de llaves.This means that brace matching will appear in all types of text files, but a fuller implementation would apply brace matching only to a specific content type.

Para implementar un proveedor del etiquetador de coincidencia de llavesTo implement a brace matching tagger provider

  1. Declarar un proveedor del etiquetador que hereda de IViewTaggerProvider, asígnele el nombre BraceMatchingTaggerProvider y exportarlo a un ContentTypeAttribute de "text" y un TagTypeAttribute de TextMarkerTag.Declare a tagger provider that inherits from IViewTaggerProvider, name it BraceMatchingTaggerProvider, and export it with a ContentTypeAttribute of "text" and a TagTypeAttribute of TextMarkerTag.

    [Export(typeof(IViewTaggerProvider))]
    [ContentType("text")]
    [TagType(typeof(TextMarkerTag))]
    internal class BraceMatchingTaggerProvider : IViewTaggerProvider
    
    <Export(GetType(IViewTaggerProvider))> _
    <ContentType("text")> _
    <TagType(GetType(TextMarkerTag))> _
    Friend Class BraceMatchingTaggerProvider
        Implements IViewTaggerProvider
    
  2. Implemente el CreateTagger método para crear instancias de un BraceMatchingTagger.Implement the CreateTagger method to instantiate a BraceMatchingTagger.

    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>;
    }
    
    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
    

Compilar y probar el códigoBuilding and Testing the Code

Para probar este código, compile la solución BraceMatchingTest y ejecútelo en la instancia experimental.To test this code, build the BraceMatchingTest solution and run it in the experimental instance.

Para compilar y probar soluciones de BraceMatchingTestTo build and test BraceMatchingTest solution

  1. Compile la solución.Build the solution.

  2. Al ejecutar este proyecto en el depurador, se crea una segunda instancia de Visual Studio.When you run this project in the debugger, a second instance of Visual Studio is instantiated.

  3. Cree un archivo de texto y escriba algún texto que incluye llaves coincidentes.Create a text file and type some text that includes matching braces.

    hello {  
    goodbye}  
    
    {}  
    
    {hello}  
    
  4. Al colocar el símbolo de intercalación antes de una llave de apertura, esa llave y la llave de cierre correspondiente debe aparecer resaltada.When you position the caret before an open brace, both that brace and the matching close brace should be highlighted. Al colocar el cursor justo después de la llave de cierre, debe aparecer resaltada esa llave y la llave de apertura correspondiente.When you position the cursor just after the close brace, both that brace and the matching open brace should be highlighted.

Vea tambiénSee Also

Tutorial: vinculación de un tipo de contenido con una extensión de nombre de archivoWalkthrough: Linking a Content Type to a File Name Extension