Leave JBang directives at the top of a file as written - #91
Merged
Merged
Conversation
JBang reads a script's configuration from line comments with the name right after the slashes, such as //DEPS, //JAVA and //SOURCES, and a shell runs the first line, ///usr/bin/env jbang "$0" "$@" ; exit $?, when the file is executed. The formatter put a space after the slashes of every line comment and wrapped long ones, so JBang silently dropped the dependencies and options, and the file no longer ran from a shell (#24, reported upstream as google/google-java-format#1217). JBang's documentation places directives "in the first comment block of the file (before any code)". There, JavaCommentsHelper now leaves two kinds of line comment exactly as written, with no space and no wrapping: a directive, which is one of the names in JBang's Directives.Names or a name with an integration prefix such as Quarkus's //Q:CONFIG, followed by whitespace or the end of the line; and the first line of the file when its first word is a path, as in ///usr/bin/env, //usr/bin/env or the long self-bootstrapping header. The helper now takes the JavaInput, whose first token marks where the code starts, since comments are numbered along with tokens. After the package declaration, an import or a class, the same text is an ordinary comment again, and //DEPSSS, //deps or //TODO still get their space everywhere. JBang's parser is more lenient than its documentation and reads every line that starts with // at column 0, so a directive after the imports still stops working; that follows the documented rule on purpose. Three goldens cover whole scripts: the one from the issue, one with a package and a license header, and a compact source file. JBangDirectivesTest checks every directive name, the syntax JBang allows after one, and comments that only look like directives. The 15,747 files of the JDK 21 sources format exactly as before; none of them has a directive or a shell line.
abashev
added a commit
to openjavaformat/docs
that referenced
this pull request
Sep 28, 2026
The JBang change of 2.98.0.5 goes on the list as the bug fix it is, although code that palantir-java-format has already formatted keeps its "// DEPS" and does not move: whoever migrates JBang scripts needs to know. The formatter handled a script's header wrongly. The space after the slashes hid //DEPS and the other directives from JBang, and the first line no longer ran from a shell. The item says so, links the JBang page, and says that spaces an earlier run added have to be removed by hand. The corpus paragraph now says the JBang fix changes nothing in the JDK 21 sources, which formatted identically with and without it when the fix was made (openjavaformat/open-java-format#91). The GitHub Action page gives 2.98.0.5 as the default of the version input, as the action's main branch now does (openjavaformat/open-java-format-action@80a52bb).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #24. The same problem is open upstream as google/google-java-format#1217, raised by JBang's author, and for the first line as google/google-java-format#1215 and google/google-java-format#1218. jbangdev/jbang#1194 asked JBang to accept
// DEPSinstead and was closed: a formatter should not add a space where there was none.The bug
JBang reads a script's configuration from line comments with the directive name right after the slashes, and a shell runs the first line when the file is executed. The formatter put a space after the slashes of every line comment and wrapped the long ones:
JBang no longer saw any of it. The dependencies, the Java version and the options were dropped without a word, and a shell tried to run
///, which is a directory.Where directives count
JBang's directives reference says a directive "must be in the first comment block of the file (before any code)". Its parser is more lenient:
Directives.Extended.getAllreads every line of the file that starts with//at column 0. JBang's own tests rely on that: they put//SOURCESbetweenpackageandimportand//RUNTIME_OPTIONSafter the imports.This change follows the documented rule, which keeps the exemption narrow. A directive after the first line of code is formatted like any other comment, as before.
The fix
JavaCommentsHelperleaves two kinds of line comment exactly as written, with no space after the slashes and no wrapping, when they come before the first token of the file:Directives.Names, or a name with an integration prefix such as Quarkus's//Q:CONFIG, followed by whitespace or the end of the line;///usr/bin/env,//usr/bin/envor the long self-bootstrapping header from JBang's docs.The helper now takes the
JavaInput. Comments are numbered along with tokens, so the index of the first token tells which comments come before any code. Header comments always go through the helper, soComment.computeFlat(), the path behind #23, is not involved.What stays as before:
//DEPSSS,//deps,//DEPS:x,//JAVA21+,///DEPSand//TODOget their space everywhere, and JBang ignores them too.// DEPSand// //DEPSare left alone. JBang's own templates switch an optional dependency off with the second form.package, an import or a class,//DEPSbecomes// DEPSand is wrapped like any other comment.Checked
ojf-issue-24-jbang-script: the script from the issue, plus directives between and after the imports, in the class, after a statement and after the class;ojf-issue-24-jbang-package: the bootstrap header, a license comment,//Q:CONFIG, a tab after the name and a directive afterpackage;ojf-issue-24-jbang-compact-source://usr/bin/envand a compact source file, gated at JDK 21 likeCompactSource.JBangDirectivesTestcovers:// comment, a${...}property, no value, and a line longer than 120 columns;./gradlew :open-java-format:teston JDK 21: 1597 tests, all green, 12 skipped (the JDK 23+ module import tests).Version
Only lines that come out broken today, a directive JBang no longer reads or a first line the shell cannot run, format differently. That makes this a bug fix in the 2.x sense, although the output for such files now differs from palantir-java-format 2.98.0.