Skip to content

Source Code Viewer

SourceCodeViewer is the component that renders a Java source file alongside the demo. It is wired in automatically by TabbedDemo when a demo view is annotated with @DemoSource.

Rendering pipeline

A source file is rendered through five steps:

  1. Source code retrieval
  2. License abbreviation
  3. Cleanup — conditional code, boilerplate removal, synthetic-source rewriting
  4. Formatting — provided by prism.js
  5. Post-processing — fragment highlighting

Source code retrieval

The URL on @DemoSource (see Tabbed Demo) is used to fetch sources at runtime. In development mode, sources are pulled from the local src directory if available.

By default, URLs pointing to github.com are rewritten to raw.githubusercontent.com. To override this behavior, register a SourceUrlResolver:

@Component
public final class SourceUrlResolverImpl implements SourceUrlResolver, VaadinServiceInitListener {

    @Override
    public void serviceInit(ServiceInitEvent event) {
        TabbedDemo.configureSourceUrlResolver(this);
    }

    @Override
    public Optional<String> resolveURL(TabbedDemo demo, Class<?> annotatedClass, DemoSource annotation) {
        return ...;
    }

}

License abbreviation

A license comment block at the beginning of the file is replaced with a short two-line comment (author, year, and link to the license terms), provided that:

  • The license comment is placed before the package declaration.
  • The license comment is formatted by license-maven-plugin with default delimiters.
  • The license is a well-known license supported by CommonsDemo.

Supported licenses:

This feature cannot be disabled.

Example

A source starting with:

/*-
 * #%L
 * Example
 * %%
 * Copyright (C) 2019 - 2023 Flowing Code
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

Is rendered as:

//Copyright (C) 2019 - 2023 Flowing Code
//Licensed under the Apache License, Version 2.0

Boilerplate removal

Commonly used boilerplate is automatically hidden from the rendered source:

  • The package declaration.
  • All lines with a // hide-source comment.
  • The following annotations and their imports:
    • @com.vaadin.flow.router.Route
    • @com.vaadin.flow.router.PageTitle
    • @com.flowingcode.vaadin.addons.demo.DemoSource
    • @java.lang.SuppressWarning
    • @org.junit.Ignore
  • Calls to SourceCodeViewer.highlight, SourceCodeViewer.highlightOnHover, and SourceCodeViewer.highlightOnClick.

This feature cannot be disabled.

Boilerplate-cleaned source rendering

Synthetic source

A // show-source comment lets a line of plain text be rendered as if it were code. Combined with // hide-source, this is useful when the real code uses reflection or other workarounds that would obscure the example's intent.

Source:

// show-source foo.bar();
Method m = Foo.class.getMethod("bar"); // hide-source
m.invoke(foo);                         // hide-source

Rendered as:

foo.bar();

Conditional code

#if / #elif / #else / #endif directives control code-block rendering depending on the framework version. For example, the following block renders as foo(); on Vaadin 23+, bar(); on Vaadin 22, and baz(); on any other version:

// #if vaadin ge 23
// show-source foo();
// #elif vaadin eq 22
// show-source bar();
// #else
// show-source baz();
// #endif

Directives: #if, #elif, #else, #endif. Nested conditionals are supported.

Syntax: #if <variable> <operator> <value>

  • Variables: vaadin, flow (note: Vaadin 14 uses Flow 1.x).
  • Operators: lt, le, ne, eq, gt, ge.
  • Value: a one-, two-, or three-digit version number (x, x.y, or x.y.z).

The SourceCodeViewer constructor accepts a map of arbitrary variables defined by the caller; TabbedDemo defines the vaadin and flow variables. Implementation details: CommonsDemo PR #44.

Conditional source tabs

The @DemoSource annotation supports a condition attribute that controls the visibility of the source tab based on the environment. The same syntax and operators as the directives apply:

@DemoSource(value = "/src/test/resources/META-INF/resources/frontend/example.css", condition = "vaadin ge 24")
public class MyDemo extends Div {
    // ...
}

Fragment highlighting

A source fragment can be highlighted to emphasize a specific part of the code; the highlighted fragment is scrolled into view automatically.

A fragment is highlighted by calling SourceCodeViewer.highlight(filenameAndId), or when a component is clicked/hovered if it was configured with SourceCodeViewer.highlightOnClick or highlightOnHover. filenameAndId is the name of the fragment; if the component lives in an additional source file, use the format filename#id. If no # is present, the identifier is assumed to refer to a block in the first source panel. SourceCodeViewer.highlight(null) turns highlighting off.

In the source, a fragment is delimited by // begin-block <id> and // end-block comments. Nested fragments are not supported. The delimiter comments are removed after post-processing.

// begin-block first
Div first = new Div(new Text("Highlight block in first panel"));
SourceCodeViewer.highlightOnHover(first, "first");
add(first);
// end-block

Div other = new Div(new Text("Highlight additional source"));
SourceCodeViewer.highlightOnHover(other, "AdditionalSource.java#other");
add(other);

Fragment highlighting example

Multiple sources

If a demo view has several @DemoSource annotations, the layout includes a tab sheet for navigating between them. The first @DemoSource is shown by default. The annotation accepts:

  • A custom caption for the source tab (defaults to the file name).
  • A language override for prism.js formatting (otherwise inferred from the file extension).

Multiple source tabs in a demo