A formatter for the Ceylon programming language.

Command line usage

Note: if the ceylon format plugin wasn’t installed by default in your distribution, you can add it by running:

ceylon plugin install ceylon.formatter/1.3.1

To format all Ceylon code in the source and test-source directories:

ceylon format source test-source

To format all Ceylon code in the source directory into the source-formatted directory:

ceylon format source --to source-formatted

To format all Ceylon code in the source and test-source directories into the source-formatted directory:

ceylon format source --and test-source --to source-formatted

(This results in two subdirectories source and test-source of source-formatted.)

You can specify an arbitrary amount of these formatting commands:

ceylon format \
        source --to source-formatted \
        test-source --to test-source-formatted

(The line breaks are only included for clarity and not a part of the command line syntax.)

If no formatting commands are present, the formatter operates in “pipe mode”, reading code from standard input and writing to standard output.

Options

You can specify formatting options using the following syntax:

--optionName=optionValue
# or
--optionName optionValue

For available option names, see FormattingOptions. The syntax of optionValue is:

  • for Boolean or Integer values, use a Ceylon-style literal (1, true)
  • for Range values, use a Ceylon-style range operator x..y
  • for IndentMode values, see the documentation of parseIndentMode
  • for Iterable values, list the individual elements, separated by spaces
  • for LineBreakStrategy, the only valid value is default
  • for enumerated types, use the name of one of the object cases (lf, all)

Library usage

Use the format() function to format any AST node. This can be a compilation unit (simply speaking, a complete file) or any other node.

If the node was parsed from an existing file, don’t forget to pass the token stream to format() – without the token stream, the formatter can’t obtain the comments, so they won’t be present in the formatted file.

To construct FormattingOptions, usage of named arguments is highly recommended:

FormattingOptions {
    indentMode = Spaces(8);
    maxLineLength = 80;
}

You can also use SparseFormattingOptions and combinedOptions to compose several sets of options, like this:

combinedOptions {
    baseOptions = companyWideOptions;
    SparseFormattingOptions {
        indentMode = Spaces(1); // our department has very small screens :-(
    }
}
Platform: Java
By: Lucas Werkmeister
License: https://www.apache.org/licenses/LICENSE-2.0.html
Packages
ceylon.formatter

A formatter for the Ceylon programming language.

ceylon.formatter.options

Options for the Ceylon formatter.

Dependencies
ceylon.collection1.3.1
ceylon.file1.3.1
ceylon.interop.java1.3.1
com.redhat.ceylon.cli1.3.1
com.redhat.ceylon.common1.3.1
com.redhat.ceylon.typechecker1.3.1
java.base7

A formatter for the Ceylon programming language.

The main class of this package is FormattingVisitor, which visits an AST Node (typically a CompilationUnit) and writes it out to a Writer. See the ceylon.formatter.options package on how to influence the format of the written code.

Values
alwaysshared always always

Indicates that an indentation should always be stacked.

ifAppliedshared ifApplied ifApplied

Indicates that an indentation should be stacked if and only if it was applied, that is:

  • for the indentation before a token: if that token is the first of its line;
  • for the indentation after a token: if that token is the last of its line.
maxDesireshared Integer maxDesiree = runtime.maxIntegerValue /

The maximum value that is safe to use as FormattingWriter.writeToken()’s space[Before|After] argument.

Using a greater value risks inverting the intended result due to overflow.

minDesireshared Integer minDesireire = runtime.minIntegerValue/2

The minimum value that is safe to use as FormattingWriter.writeToken()’s space[Before|After] argument.

Using a smaller value risks inverting the intended result due to overflow.

nevershared never never

Indicates that an indentation should never be stacked.

noLineBreakshared Range<Integer> noLineBreak
Functions
commonRootshared Path commonRoot([Path+] paths)

Determines the common root of several paths. For example, the common root of a/b/c and a/b/d is a/b, the common root of /a/b/c and /a/d/e is /a and the common root of a and b is the empty path.

Parameters:
  • paths

    The paths. Must be either all absolute or all relative.

desireshared Integer desire(Boolean|Integer desire)

Parses a Boolean or Integer value into a desire in range minDesire..maxDesire.

If desire is Integer, it is clamped to that range; if it’s Boolean, the returned value is minDesire for false and maxDesire for true.

formatshared void format(Node node, FormattingOptions options = ..., Writer output = ..., TokenStream? tokens = ..., Integer initialIndentation = ...)

Format the given CompilationUnit and write it to the given Writer.

Parameters:
  • node

    A node that you want to format, e. g. a CompilationUnit from the Ceylon compiler.

  • options = ions = FormattingOp

    The options for the formatter. These dictate the line breaking strategy, the bracing style, the maximum line length, and much more.

    The default options are modeled after the ceylon.language module and the Ceylon SDK. You can adapt them through the named arguments syntax, keeping the default values of the options that you don’t want to change.

    You can also adapt any other FormattingOptions by using SparseFormattingOptions and combinedOptions(), like this:

    value myOptions = combinedOptions(bossOptions,
        SparseFormattingOptions {
            indentMode = Spaces(4); // I don’t care what the boss says, spaces rule
        });
    
  • output = tput = stdou

    The Writer to which the formatted node is written.

    Defaults to standard output.

  • tokens = kens

    An ANTLR Token Stream from which the CompilationUnit was parsed. This only makes sense if you got node from the Ceylon compiler, otherwise you should keep the default value null.

    Note that you probably do not want a CommonTokenStream (which is what you normally give to the compiler), because that skips comments. Use BufferedTokenStream instead.

  • initialIndentation = e

    The initial indentation to use.

    You probably shouldn’t use this when node is a CompilationUnit, but it’s useful when formatting other nodes.

helpshared String? help(String? topic)

Returns a help string for the given topic, or null if no help is available for this topic.

Parameters:
  • topic

    The requested help topic, or null for general help.

    Currently available:

parseTranslationsshared <String[]->String>[] parseTranslations(String[] arguments)

Parses translations from the arguments.

For example, the arguments

a/b/c --and a/b/d --to x/a/b   d/e   f/g --to m/n/f/g

correspond to the translations

[
  [a/b/c, a/b/d] -> x/a/b,
  d/e -> d/e,
  f/g -> m/n/f/g
]

The special argument --pipe corresponds to a translation [/dev/stdin] -> /dev/stdout.

runshared void run(String[] arguments = ...)

Run the module ceylon.formatter.

Parameters:
  • arguments = guments = process
Classes
CeylonFormatToolshared CeylonFormatTool
FormattingVisitorshared FormattingVisitor

A Visitor that writes a formatted version of the element (typically a Tree.CompilationUnit) to a Writer.

FormattingWritershared FormattingWriter

Writes tokens to an underlying FormattingWriter.writer, respecting certain formatting settings and a maximum line width.

The FormattingWriter manages the following aspects:

  • Indentation
  • Line Breaking
  • Spacing

Additionally, it also writes comments if a token stream is given.

Indentation

Two indentation levels are associated with each token: one before the token, and one after it. Each token also introduces a context which tracks these indentation levels if they stack. In this case, their indentation is applied to all subsequent lines until the context is closed.

By default, the indentation before the token never stacks, and the indentation after the token always does. However, either indentation may also stack in the other case, or only if it is actually applied, that is, only if that token is the first/last of its line.

When the token is written, its context (instance of FormattingWriter.FormattingContext) is pushed onto a context stack. The indentation of each line is then the sum of all indentation currently on the stack, plus the indentation before the current token and after the last token (if they’re not already on the stack). When a context is closed, the context and all contexts on top of it are removed from the context stack.

A context can be closed in two ways:

  1. By associating it with a token. For example, you would say that a closing brace } closes the context of the corresponding opening brace {: The block has ended, and subsequent lines should no longer be indented as if they were still part of the block. Tokens that close another token’s context may not introduce indentation themselves, since they don’t get a context of their own.
  2. By calling FormattingWriter.closeContext().

You can also obtain a context not associated with any token by calling FormattingWriter.openContext(), which introduces some undirectional indentation that always stacks. This is mostly useful if you have a closing token with no designated opening token: for example, a statement’s closing semicolon ; should close some context, but there is no corresponding token which opens that context.

Examples:

  • An opening brace { has an indentAfter of 1, which always stacks. The resulting context is closed by the associated closing brace }.
  • A member operator . has an indentBefore of 1, which never stacks. If it stacked, you would get this:
    value someValue = something
      .foo(thing)
          .bar(otherThing)
              .baz(whyIsThisSoIndented);
    
  • A refinement operator => has an indentBefore of 2, which stacks only if it is applied. Thus, you get both of the following results:
    Integer i => function(
      longArgument // only indented by one level, from the (
    );
    Integer f(String param1, String param2)
          => let (thing = param1.length, thing2 = param2.uppercased)
              thing + thing2.string; // indented from both => and let
    

Line Breaking

Two Integer ranges are associated with each token. One indicates how many line breaks may occur before the token, and the other indicates how many may occur after the token. Additionally, one may call FormattingWriter.requireAtLeastLineBreaks() to further restrict how many line breaks may occur between two tokens.

The intersection of these ranges for the border between two tokens is then used to determine how many line breaks should be written before the token.

  • If FormattingWriter.tokens exists, then each time a token is written, the token stream is fast-forwarded until the token is met (if a token with a different text is met, an exception is thrown). In fast-forwarding, the amount of line breaks is counted. After fast-forwarding has finished, the number of line breaks that were counted is clamped into the line break range, and this many line breaks are written.
  • If FormattingWriter.tokens doesn’t exist, then the first element of the range is used (usually the lowest, unless the range is decreasing).

(Internally, the FormattingWriter also keeps track if a line break range came from FormattingWriter.writeToken(), FormattingWriter.requireAtLeastLineBreaks(), or was added internally when dealing with comments; an empty intersection of two ranges is usually a bug, unless one of the ranges comes from a comment, in which case we just use that range instead.)

Additionally, the FormattingWriter also breaks lines according to a maximum line length and a LineBreakStrategy, as determined by FormattingWriter.options. To achieve this, tokens are not directly written to the underlying writer; instead, they are added to a token queue (not to be confused with the token stack, which is used for indentation). Each time a token is added, the FormattingWriter checks if there are enough tokens on the queue for the line break strategy to decide where a line break should be placed. Line breaks are allowed between tokens if their respecive ranges included at least one value greater than zero (in other words, to disallow a line breaks between two tokens, pass a range of 0..0 to either of them). When a line break location is known, that line is written and its tokens removed from the queue (their contexts are then added to the token stack).

Spacing

If you’ve made it this far, relax, this is the easiest section :)

Two Integers are associated with each token. One indicates the token’s desire to have a space before it, the other indicates the desire to have a space after it. When the two tokens are written, these integers are added, and if the sum is >= 0, then a space is written.

To avoid inverting the intended result by numerical overflow, don’t use values outside the range minDesire..maxDesire. You can also give false and true to FormattingWriter.writeToken(), which are convenient and readable syntax sugar for these two values (spaceBefore = true).

Comments

The fast-forwarding of the token stream (if given) was already mentioned in the “Line Breaking” section. If comment tokens are encountered during the fast-forwarding, they are written out like tokens with

StackConditionshared abstract StackCondition

A condition that dictates when a token’s indentation stacks.

alwaysshared always

Indicates that an indentation should always be stacked.

ifAppliedshared ifApplied

Indicates that an indentation should be stacked if and only if it was applied, that is:

  • for the indentation before a token: if that token is the first of its line;
  • for the indentation after a token: if that token is the last of its line.
nevershared never

Indicates that an indentation should never be stacked.