Skip to content

Commit fec59ef

Browse files
authored
Merge pull request #116 from openjavaformat/gjf-1475-hyphen-join
Join hyphenated words across Javadoc line breaks; no blank lines before a leading block tag
2 parents 444ffbc + fefb09b commit fec59ef

3 files changed

Lines changed: 120 additions & 7 deletions

File tree

‎open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,16 @@ private static ImmutableList<Token> joinAdjacentLiteralsAndAdjacentWhitespace(Li
388388
continue;
389389
}
390390

391+
if (seenWhitespace.indexOf("\n") >= 0
392+
&& !hasMultipleNewlines(seenWhitespace.toString())
393+
&& isHyphenatedWordEnd(accumulated)
394+
&& tokens.peek().getType() == LITERAL
395+
&& isWordStart(tokens.peek().getValue())
396+
&& !isSuspendedHyphenConjunction(tokens.peek().getValue())) {
397+
accumulated.append(tokens.next().getValue());
398+
continue;
399+
}
400+
391401
output.add(new Token(LITERAL, accumulated.toString()));
392402
accumulated.setLength(0);
393403

@@ -399,13 +409,26 @@ private static ImmutableList<Token> joinAdjacentLiteralsAndAdjacentWhitespace(Li
399409
// We have another token coming, possibly of type OTHER. Leave it for the next iteration.
400410
}
401411

402-
/*
403-
* TODO(cpovirk): Another case where we could try to join tokens is if a line ends with
404-
* /[^ -]-/, as in "non-\nblocking."
405-
*/
406412
return output.build();
407413
}
408414

415+
private static boolean isHyphenatedWordEnd(CharSequence cs) {
416+
int length = cs.length();
417+
return length >= 2 && cs.charAt(length - 1) == '-' && isWordChar(cs.charAt(length - 2));
418+
}
419+
420+
private static boolean isWordChar(char c) {
421+
return Character.isLetterOrDigit(c) || c == '_';
422+
}
423+
424+
private static boolean isWordStart(String s) {
425+
return !s.isEmpty() && isWordChar(s.charAt(0));
426+
}
427+
428+
private static boolean isSuspendedHyphenConjunction(String s) {
429+
return s.equals("and") || s.equals("or");
430+
}
431+
409432
/**
410433
* Where the input has two consecutive line breaks between literals, insert a {@code <p>} tag between the literals.
411434
*

‎open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocWriter.java‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,7 @@ void writeFooterJavadocTagStart(Token token) {
114114
*/
115115
postWriteModifiedContinuingListCount.reset();
116116

117-
if (!wroteAnythingSignificant) {
118-
// Javadoc consists solely of tags. This is frowned upon in general but OK for @Overrides.
119-
} else if (!continuingFooterTag) {
117+
if (!continuingFooterTag) {
120118
// First footer tag after a body tag.
121119
requestBlankLine();
122120
} else {
@@ -328,6 +326,12 @@ private void writeToken(Token token) {
328326
requestNewline();
329327
}
330328

329+
if (!wroteAnythingSignificant) {
330+
// Nothing precedes the first token but the opening ∕✱✱ and its newline, so no requested
331+
// whitespace (e.g., the blank line a <pre> or <ul> asks for) belongs before it.
332+
requestedWhitespace = NONE;
333+
}
334+
331335
if (requestedWhitespace == BLANK_LINE
332336
&& (postWriteModifiedContinuingListCount.isPositive() || continuingFooterTag)) {
333337
/*

‎open-java-format/src/test/java/com/palantir/javaformat/java/JavadocFormattingTest.java‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,4 +1625,90 @@ void preCodeMixedStarAndBareLinesKeepRelativeIndent() {
16251625
};
16261626
doFormatTest(input, expected);
16271627
}
1628+
1629+
@Test
1630+
void hyphenatedLineBreakJoined() {
1631+
String[] input = {
1632+
"/**", //
1633+
" * This calculation requires an infinite-",
1634+
" * precision number.",
1635+
" */",
1636+
"class Test {}",
1637+
};
1638+
String[] expected = {
1639+
"/** This calculation requires an infinite-precision number. */", //
1640+
"class Test {}",
1641+
};
1642+
doFormatTest(input, expected);
1643+
}
1644+
1645+
@Test
1646+
void suspendedHyphenPreserved() {
1647+
String[] input = {
1648+
"/**", //
1649+
" * Both pre-",
1650+
" * and post-processing steps.",
1651+
" */",
1652+
"class Test {}",
1653+
};
1654+
String[] expected = {
1655+
"/** Both pre- and post-processing steps. */", //
1656+
"class Test {}",
1657+
};
1658+
doFormatTest(input, expected);
1659+
}
1660+
1661+
@Test
1662+
void hyphenBeforeParagraphBreakNotJoined() {
1663+
String[] input = {
1664+
"/**", //
1665+
" * Ends with a non-",
1666+
" *",
1667+
" * blocking paragraph.",
1668+
" */",
1669+
"class Test {}",
1670+
};
1671+
String[] expected = {
1672+
"/**", //
1673+
" * Ends with a non-",
1674+
" *",
1675+
" * <p>blocking paragraph.",
1676+
" */",
1677+
"class Test {}",
1678+
};
1679+
doFormatTest(input, expected);
1680+
}
1681+
1682+
@Test
1683+
void preBlockAsFirstContentGetsNoBlankLines() {
1684+
String[] input = {
1685+
"/**", //
1686+
" * <pre>{@code",
1687+
" * class Demo {",
1688+
" * int x;",
1689+
" * }",
1690+
" * }</pre>",
1691+
" */",
1692+
"class Test {}",
1693+
};
1694+
doFormatTest(input, input);
1695+
}
1696+
1697+
@Test
1698+
void listAsFirstContentGetsNoBlankLines() {
1699+
String[] input = {
1700+
"/** <ul><li>one <li>two</ul> */", //
1701+
"class Test {}",
1702+
};
1703+
String[] expected = {
1704+
"/**", //
1705+
" * <ul>",
1706+
" * <li>one",
1707+
" * <li>two",
1708+
" * </ul>",
1709+
" */",
1710+
"class Test {}",
1711+
};
1712+
doFormatTest(input, expected);
1713+
}
16281714
}

0 commit comments

Comments
 (0)