Skip navigation links
org.netbeans.modules.editor.bracesmatching/0 1.41.1

Editor Braces Matching
Under Development

See: Description

Editor Braces Matching 
Package Description
org.netbeans.spi.editor.bracesmatching
The Braces Matching SPI for implementing BracesMatchers that can search through a document and find matching areas of text.
org.netbeans.spi.editor.bracesmatching.support
The support package provides several useful implementations of the BracesMatcher interface.

The Editor Braces Matching module provides the BracesMatchingSPI for registering services capable of finding matching areas of text in a document. These areas can be pretty much anything and the rules for finding them are solely up to the implementors. A typical usecase and the main motivation for this SPI is to be able to implement language specific matchers for finding pairs of braces, highlighting them and allowing users to move a caret (navigate) from one to the other. The matching areas can also be for example XML, HTML or JSP tags.

The Braces Matching SPI is a replacement for the old SPI in org.netbeans.editor.ExtSyntaxSupport.

The infrastructure overview

The following paragraphs give an overview of the infrastructure used for braces matching and explain the essential terminology and ideas behind it.

Original and matching areas

The whole task of finding matching areas can be split in two parts. First we need to determine if the caret is positioned over a character or an area that could possibly have another matching areas. And if so, then we need to find those areas. For the purpose of this SPI we call the area at the caret the original area and the other areas are simply the matching areas.

    for(int i = 0; i < 10; i++) {
        System.out.println(i);
    }|

    | - the caret
      - original area
      - matching area
  

There is always only one original area, if there is any at all. On the other hand the matchers are free to report as many matching areas as they like. In most cases, however, the matchers will only report none or one matching are as well.

Caret Bias

Normally when typing or moving a caret in the editor one of the characters is always considered to lay 'under' a caret. The decision if it is the character on the left hand side or right hand side of a caret depends on the shape of the caret. Although the visual appearance of a caret can vary, there are generaly only two shapes to consider. The I-beam caret and the block caret.

The I-beam caret normally looks like a vertical line placed on a position between characters in text. However, when typing it is generally accepted that the left hand side character is the one under the I-beam caret. The I-beam caret is used in Netbeans editor when editing text in the normal mode.

The block caret on the other hand may look like a little square or vertical line placed beneath or under a character. This type of caret makes it visually very clear what character is 'under' the caret. It is the right hand side character. In Netbeans editor the block caret is used for the overwrite mode.

When searching for the original area the search has always to start at the character 'under' a caret - the important character. In order not to tie the braces matching infrastructure to a praticular implementation of a caret we use the parameter caret bias to describe on which side of a caret the important character lies. Obviously the caret bias can only have two values - backward or forward - designating positions on the left or right hand side of the caret.

Because the search for the original area always starts with the important character the result of this search is directly influenced by the caret bias. The search could result in two different original areas depending on the value of the caret bias, even if the actual position of the caret was not changed.

The caret bias is not only used for searching for the original area, but it is also used when navigating (jumping) between matching areas. The navigation is always done in a way to make the matching area where we are navigating to to appear on the same side of the caret as is the side of the caret bias. This makes sure that the matching area will be recognized as the next original area. (Strictly speaking this also depends on the particular matcher implementation that was used for finding the areas.)

Search direction

In general when looking for the original area we not only want to look right next to a caret, but we also want to search a little bit further. Looking a little bit further makes it easier for users to see the matching areas and navigate between them. They don't have to position the caret right at a brace in order to see the matching one or navigate to it. The scope of this search should, however, never leave the line with the caret and in some situations it might be restricted even more.

Although there are only two directions to look in - backward or forward - in a real situation looking in both directions gives more practical results. That is why the infrastructure supports the following two search directions.

Maximum lookahead

The maximum distance, measured in characters from the position of a caret, that the search should be attempted in is called maximum lookahead. The infrastructure uses independent values for both search directions and calls them maximum backward lookahead and maximum forward lookahead. The lookahead is normally limited by the beginning and end of a line with the caret, but can be further limited by those two parameters. The absolute lookahead maximum in each direction enforced by the infrastructure is 256 characters

There is an important relationship between maximum lookaheads and the caret bias. The maximum lookaheads can never obscure the important character. In other words the maximum lookahead in the direction of the caret bias will not prevent the infrastructure from searching through the important character (ie. the character right next to the caret).

Controlling the parameters

There is no API for controlling caret bias, search direction and maximum lookaheads on the global level. It is expected that the module implementing the braces matching infrastructure will expose its own GUI for user to control those parameters. This GUI is yet to be designed. By default the infrastructure will set the parameters to mimic the behavior of the existing (old) implementation of braces matching done using org.netbeans.editor.ExtSyntaxSupport.

On the other hand the parameters can be controlled on per-component basis by setting appropriate client properties. This is to allow interested modules such as the jVi plugin to overwrite global settings and implement their own searching policies. The following properties are supported.

Key parts of the SPI

Modules implementing the SPI are shielded from most of the complexity of the braces matching infrastructure as it was described earlier. Thier job is as simple as being able to search in one direction through the requested number of characters. The SPI provides all the neccessary information to perform such search as well as mechanisms for registering implemented matchers.

The main part of the SPI is the BracesMatcher interface, which needs to be implemented by anybody who wishes to provide a text matching services for a particular type of documents. Implementations of the BracesMatcher interface can be plugged in to the system by registering BracesMatcherFactory in the MIME lookup.

The braces matching infrastructure will use registered factories to create matchers for highlighting matching areas in documents. Each type of a document (ie. mime type or more precisely each mime path) can have at most one BracesMatcher registered. If there is more different implementations registered for the same mime path the infrastructure will only use the first one.

When asking a BracesMatcherFactory to create an instance of BracesMatcher the infrastructure passes the factory an instance of MatcherContext, which provides information about a document and a position in that document, where the search should start.

BracesMatcher registration

The registration of BracesMatchers has to be done through an instance of the BracesMatcherFactory class. The factory should be registered in MimeLookup under the mime-type of documents, which the BracesMatcher should be used for. For example, if a module wants to provide BracesMatcher for text/x-something documents it should implement its own BracesMatcherFactory (e.g. org.some.module.BMFactory class) and register it in MimeLookup using its XML layer as it is shown on the example below.

<folder name="Editors">
  <folder name="text">
    <folder name="x-something">
        <folder name="BracesMatchers">
            <file name="org-some-module-BMFactory.instance" />
        </folder>
    </folder>
  </folder>
</folder>
  

The BMFactory class will simply return a new instance of the module's implementation of the BracesMatcher interface from its createMatcher method.

Languages embedding

The registration mechanism described earlier and the general concept of MIME lookup allows to provide special BracesMatchers for embedded languages. The braces matching infrastructure will always try to find the inner-most language at a position in a document, where the search should start. It will then create the BracesMatcher according to the mime path of that inner-most language.

As per the general inheritance rules defined by MIME lookup this may result in using a BracesMatcher that is registered for a top level language (ie. mime type) different from the main language of the scanned document. Here is an example of a java snippet in a JSP page, the caret's position is indicated by the '|' pipe character. Assuming that there are only two BracesMatcher implementations registered - one for text/x-jsp and the other one for text/x-java, the infrastructure's attempt to find a matcher for the text/x-jsp/text/x-java mime path is going to result in finding the matcher registered for text/x-java.

<% for(int i = 0; i < 10; i++) { %>
<th><%=i%>. column</th>
<% }| %>;
  

Since the support for languages embedding comes with the new Lexer API, the above example is naturally only going to work for document types that provide a Lexer implementation. This is the case for the most document types supported in Netbeans 6. In case that a document does not have a Lexer the infrastructure will fall back to using the document's main mime type ignoring any other languages that may potentially be embedded in the document.

Threading

While finding the original brace is relatively simle task, because it's always done in a limited and quite small area around a caret, finding the matching brace can take much more time and in generall it can involve searching through a whole document. The search is normally done in response to a moving caret, which means that there can be a lot of search requests generated when a user moves the caret quickly over some text. It is not very important to show all the results as the caret moves, but it is important to show the last result when the caret stops moving.

In order not to impact responsiveness of the editor and its caret the braces matching infrastructure performs all searches in a background thread outside of the Swing's event thread. If needed a running task is cancelled and rescheduled with new parameters reflecting the latest position of the caret. The BracesMatchers implemented by modules are required to react accordingly when a search task is cancelled. This is described in detail in the javadoc for BracesMatcher.

Constraints and limitations

The current design does not cover searching for areas in right-to-left documents. This usecase has not been even analyzed and may prove itself unsolvable withough major changes in the infrastructure and the SPI.

What is New (see all changes)?

Use Cases

Although the SPI can generally by used for highlighting areas in a document that have something in common, the usecases below are demonstrated on a simple braces matching example. This example is used for its simplicity and clarity, but it could be substituted by more complex examples.

The usecases listed here were heavily inspired by comments in issues 95126 and 66037.

Usecase 1. - Highlighting results

Probably the main reason why this SPI exists is to allow Netbeans editor to highlight matching braces in a document. The highlighting itself is done by the infrastructure and is not of a concern for BracesMatcher implementors. It should be possible to highlight independently (ie. in a different color) both the original brace and the matching brace. It should also be possible to highlight the original brace in a special color when its matching brace can't be found. The colors obviously have to be customizable by users.

Usecase 2. - Navigating between results

If the original brace is detected and its matching brace is found Netbeans editor needs to allow an easy navigation between those two positions (ie. jumping from the original brace to the matching one and back).

In general if there is more than one matching area users should be allowed to cycle through all of them. Since there is only one editor action (shortcut) for navigating between matching areas the cycling is only done in one direction (backward). In order for cycling to work properly the BracesMatcher implementation has to report matching areas in a consistent way. That is the matching areas should always be sorted by their position in a document, starting with the one at the lowest offset.

Usecase 3. - Different search scenarios

The users are likely to have different preferences for the way how braces matching works therefore its behavior should be customizable. We have listed below several possible scenarios that can be used simply by setting different values for the search parameters described before. They all differ in the way how the original area is detected.

The shortcuts for the parameters have the following meaning - MBL ... max backward lookahead, MFL ... max forward lookahead, SD ... search direction, CB ... caret bias. The values are B ... backward or backward preferred and F ... forward or forward preferred. The question mark ? means that the value of this parameter has no effect for the search.

  • MBL = 0, MFL = 0, SD = ?, CB = F : Check only the right hand side character of the caret. This is the default for Netbeans overwrite mode (ie. the block shaped caret).
  • MBL = 1, MFL = 1, SD = ?, CB = B : Check characters right next to the caret on both its sides. This is the default for Netbeans normal mode (ie. the I-beam shaped caret).
  • MBL = 0, MFL = 256, SD = F, CB = B : Check the character on the left hand side of the caret, otherwise search forward. This is the default for jVi insert mode.
  • MBL = 0, MFL = 256, SD = F, CB = F : Search forward from the caret. This is the default for jVi command mode.
  • MBL = 256, MFL = 256, SD = F, CB = B : Check the left hand side character, otherwise search forward first and then backward. Suitable for I-beam carets that detect the original area anywhere on the line with the caret giving preferrence to the area positioned forward from the caret. The preferrence for the backward positioned area can simply be achieved by changing the search direction (ie. SD = B).
  • MBL = 256, MFL = 256, SD = F, CB = F : The same as option E, but for the block carets.
Usecase 4. - Auto-switching the search scenario

The editor in Netbeans can operate in two modes - normal and overwrite. They both use different shape of a caret and thus need a different caret bias. The braces matching infrastructure should detect what mode the editor is in and change the parameters used for searching for the original area accordingly. The caret bias is the most important parameter, but other parameters may need to be changed too.

This feature should of course be only active for text components where the parameters have not been overwritten by some other module.

By default the two search scenarios used for the normal and overwrite modes in Netbeans editor are the scenario B for the normal mode and scenario A for the overwrite mode. In general scenarios from both editor modes should be customizable by users.

Exported Interfaces

This table lists all of the module exported APIs with defined stability classifications. It is generated based on answers to questions about the architecture of the module. Read them all...
Group of java interfaces
Interface NameIn/OutStabilitySpecified in What Document?
BracesMatchingSPIExportedUnder Development .../overview-summary.html

Group of property interfaces
Interface NameIn/OutStabilitySpecified in What Document?
nbeditor-bracesMatching-caretBiasExportedUnder Development

Controls where the important character lies in relation to a caret. The important character is a character right next to a caret and depending on the value of this property it can be the character on either the left hand side or the right hand side of the caret. Please see the infrastructure overview for more details.

nbeditor-bracesMatching-searchDirectionExportedUnder Development

Controls a direction in which the search for the original area is performed. Please see the infrastructure overview for more details.

nbeditor-bracesMatching-maxBackwardLookaheadExportedUnder Development

Controls the maximum distance measured in characters from the position of a caret, where matchers are allowed to search for the original area. Please see the infrastructure overview for more details.

nbeditor-bracesMatching-maxForwardLookaheadExportedUnder Development

Controls the maximum distance measured in characters from the position of a caret, where matchers are allowed to search for the original area. Please see the overveiw for more details.

Implementation Details

Where are the sources for the module?

The sources for the module are in the NetBeans Mercurial repositories.

What do other modules need to do to declare a dependency on this one, in addition to or instead of a plain module dependency?

Just normal module dependency.

Read more about the implementation in the answers to architecture questions.

Skip navigation links
org.netbeans.modules.editor.bracesmatching/0 1.41.1

Built on June 4 2024.  |   Copyright © 2017-2024 Apache Software Foundation. All Rights Reserved.