Revision as of 13:58, 21 February 2013 by Sdedic (Talk | contribs)


Separation from Editor

For legacy reasons, part of the Code Folding (sidebar, bindings to caret) was placed in the editor.lib module. This complicates dependencies, and puts an add-on feature into the base editor module. Instead of moving the parts of folding code to editor.lib2, they will be merged with the data layer in editor.fold.

Several legacy classes are moved and deprecated:

  • CodeFoldingSidebar
  • CustomFoldManager

Classes are moved between modules, with editor.lib injecting dependency on editor.fold module, so existing clients continue to compile and run.


The class resides in a public package, but exposes a lot of its internals. It's typically used just to create an instance of SideBar for the editor. Potential other use could be a different presentation of folding signs, but that is severely impaired by design of the class, where useful methods are private, leading to copy-paste or reflection programming.

I've decided to provide factories for the sidebar, both for code and XML layer usage, and deprecate the whole class. It could be redesigned in the future and published in a codefolding API package.

The moved class (in the original package) is left public and unchanged for backwards compatibility.


The CustomFoldManager handles in-comments user-defined folds, such as those used by Form module:

// <editor-fold collapsed=true">

it has to be instantiated for every MIME type, which want to support user comments. The use-cases require only the creation to be public, not the entire class. XML layer - usable creation API was added into FoldingSupport utility class.

Additional hooks between editor.lib and editor.fold

Two hooks between editor.lib and editor.fold are needed to integrate folding with the caret operations. New method

public void refresh(boolean retainInView)

is introduced, so fold support can refresh caret position on the screen.

The 2nd hook is used by BaseCaret handling of mouse clicks, which needs an immediate info on whether the current visual position falls into a folded view, since the behaviour is then different. Since the mouse support is quite mixed with BaseCaret with no points for extension, I decided to use a pseudo-API: register a Callable<Boolean> as org.netbeans.api.fold.expander property on the text component. Base caret calls the Callable to obtain the necessary information. The pseudo-API is documented in arch document of editor.lib module.

Incompatible changes

The BaseCaret used to implement FoldHierarchyListener directly on the BaseCaret class. BaseCaret attached itself to the FoldHierarchy and the listener implementation served its internal purposes, not purposes of the BaseCaret clients.

The BaseCaret dependency on the Fold API was the last dependency of the editor.lib module on editor.fold. There were 2 options: 1. define a separate editor.fold.ui, which depends on editor.lib (for BaseDocument, BaseCaret etc) which in turn depends on editor.fold (for FoldHierarchyListener in the BaseCaret). Note that the dependency editor.lib > editor.fold has no function, just to preserve formal compatibility.

2. break the compatibility a little at - I assess - no cost for BaseCaret clients. Using the FoldHierarchyListener (e.g. attach it twice to the Fold hierarchy, or calling the listener method) could lead to issues anyway.

Option #2 was selected as a deliberate breach with the benefit of less dependencies and cleaner codebase.

New features


FoldTypes were extended to act like extensible enum. Each FoldType has a code, a String which can be used to lookup the FoldType, or represent it (mainly in settings/preferences). In 7.3 and earlier, anyone could introduce a new fold type. However, the infrastructure was not able to compute the set of applicable fold types for a given file type, so it could offer only a limited assistance and default implementations (i.e. UI for options).

To further support modularization, the extension was enabled not only for 1 provider per language (MIME type), but multiple parties can define FoldTypes for a language. This allows to build a base support, usually tied to syntax structure of the language, and possibly enhance with an additional module e.g. for more advanced constructs.

Since FoldTypes can now be defined in an uncontrolled way (each language support can define whatever it needs), a single-parent hierarchy can be defined among FoldTypes. For example, large field initializers and methods are both members; so an action "Collapse all members" will work on all of them.

The hierarchy also allows individual new FoldTypes to be preconfigured from the general settings: if the user configures auto-folding for NESTED' type folds, inner classes (in java), classes (in PHP) and other derived fold types will become autofolded, unless they have a specific override in the target language.

FoldTypes are contributed by FoldProviders registered in the layer or by @MimeRegistration annotation.

Dynamic fold content - ContentReader

If the number of folds (potential foldable areas) is high, the FoldProvider need not to copy 'description' to be displayed to the fold information. Instead, a ContentReader can be registered in the layer, to read the fold content. This is useful when the fold is collapsed only occasionally, as the characters are not duplicated in advance. The ContentReader may also use information in the Fold's extraInfo to synthesize the content.

Folding options

Support for folding options was centralized. It is no longer needed to assign initial collapsed state to a Fold, when it is created. For special cases and backward compatibility, the assignment can be still made by the FoldManager, but in the case FoldManager does not set collapsed or expanded state, the infrastructure is capable to determine the auto-folding option status.

UI for folding options was extended to display specific options and values for each language. The infrastructure could support completely custom additional per-language UI in the future, but that feature was not requested (yet).

Fold update operation

All known clients used the same pattern on update 1. remember previous folds 2. create new folding info 3. compare remembered & new info, determine areas that are no longer foldable, new foldable areas 4. delete obsolete Folds, using Folding API 5. create new Folds, using Folding API This pattern has been adopted to the Folding API itself. The FoldManager can use new methods

public Iterator<Fold>  foldIterator();

    public Map<FoldInfo, Fold> update(
            Collection<FoldInfo> infos, 
            Collection<Fold> removed, 
            Collection<FoldInfo> created) throws BadLocationException;

The provider does not need to do bookkeeping, since all its folds - including the blocked ones - can be now enumerated. The former FoldHierarchy operation only provide unblocked folds.

The other method performs the algorithm described above; in addition, it can optionally report to the caller the exact Folds (FoldInfos) that were removed or used to produce new Folds. This supplementary result can be useful, if the FoldManager needs to update its additional data.

Shifting folds

The update() operation covers the case of a Fold which extends back or forward in between updates, as a result of added content to the document, e.g. a newly typed import statement in Java. If such a fold is sent to the update operation, the corresponding existing fold is not removed, but rather updated.

This may simplify bookkeeping in Java or other providers, which try to maintain fold's state unchanged once it exists.

The description or other properties, except type can be updated on existing folds. Change events for these situations existed already, but corresponding operations were not available.


Existing code owners are kindly asked to migrate their code to the new infrastructure in exchange for a simpler API and consistent operation.

The following migration steps should be done:

Provide FoldTypes

Think well what fold types your language supports. Either the standard FoldTypes (defined as FoldType.* constants) can be used, or new one can be derived. See FoldType javadoc for discussion.

Define FoldTypes, if extension of the language core is anticipated, declare them as public constants. Define a FoldTypeProvider implementation and register it using @MimeRegistration.

Update your code to use the newly defined FoldTypes, discard the old definitions.

Simplify FoldManager

If your FoldManager does a diff between old and new folds, this part of code can be completely removed. Instead of Folds, construct FoldInfos and put them to FoldOperation.update(). Delete all your diffing code.

Possibly delete also code, which tries to keep fold state of imports and initial comments; these should be gracefully handled by the infrastructure itself.

Delete code that reads preferences for auto-folding. They are handled by the infrastructure, if you pass null in FoldInfo.collapse, or null as initial state into FoldOperation.addToHierarchy.

Delete CodeFoldingSidebar

You no longer need to register the Sidebar. The standard implementation is guarded against multiple registrations, so keeping the old registration in the layer does no harm, but should be cleaned up. Delete subclasses of Sidebar which do nothing but inherit the standard one. File RFEs if you need to extend/enhance the sidebar.

Allow for user folds

If not, add FoldingSupport.userFoldManagerFactory registration to the layer. You may configure the lexer token the factory uses.

Preview in placeholder

Add ContentReader registration for your MIME type. There's a regexp-based ContentReader ready in FoldingSupport.contentReaderFactory for layer registration. The generic reader suits well java, php and even XML.

Impact on CSL-based modules

The CSL FoldManager acts as an adapter, so CSL-based languages do not register directly with folding (yet). This will be cleaned in a next step. I did not want to make basic Fold API dependent on Parsing (this is the code of what CSL folding support does after this stage of refactoring).

CSL modules should use the existing SPI (StructureScanner.fold()) to return ranges for individual folds.

CSL modules may however register FoldTypes (that includes a FoldTemplate for each type, which describes how the placeholder is displayed), so a CSL module may customize:

  • what fold types it uses (registered FoldTypes). Folding options UI will be displayed for the MIME type automatically in Tools > Options.
  • how a fold looks like (/** ... */, # ... - in a FoldTemplate)
  • dynamic content of the folded placeholder, if the module registers a ContentReader in the XML layer.

In the future an additional API will be provided that connects Parsing API with folding, with a new SPI that will enable better access to the Fold API for SPI implementors. This API should be also suitable for non-CSL languages (XML, Java).

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo