[java formatter] c style comments formatting supported. Note, only single line comments or comments with each line starting with asterisks will be formatted, since by using /* and */ you

This commit is contained in:
yarik
2017-06-16 11:08:53 +03:00
parent c4cfcf98b6
commit ced545552a
34 changed files with 272 additions and 87 deletions

View File

@@ -17,6 +17,7 @@ package com.intellij.psi.formatter.java;
import com.intellij.formatting.*;
import com.intellij.formatting.alignment.AlignmentStrategy;
import com.intellij.formatting.blocks.CStyleCommentBlock;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
@@ -204,6 +205,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
return new CommentWithInjectionBlock(child, wrap, alignment, indent, settings, javaSettings);
}
if (child instanceof LeafElement || childPsi instanceof PsiJavaModuleReferenceElement) {
if (child.getElementType() == JavaTokenType.C_STYLE_COMMENT) {
return new CStyleCommentBlock(child, indent);
}
final LeafBlock block = new LeafBlock(child, wrap, alignment, actualIndent);
block.setStartOffset(startOffset);
return block;
@@ -355,7 +359,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
@Nullable
@Override
public Spacing getSpacing(Block child1, @NotNull Block child2) {
return JavaSpacePropertyProcessor.getSpacing(getTreeNode(child2), mySettings, myJavaSettings);
return JavaSpacePropertyProcessor.getSpacing(child2, mySettings, myJavaSettings);
}
@Override
@@ -1126,12 +1130,15 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
}
@Nullable
protected static ASTNode getTreeNode(final Block child2) {
if (child2 instanceof JavaBlock) {
return ((JavaBlock)child2).getFirstTreeNode();
protected static ASTNode getTreeNode(final Block block) {
if (block instanceof JavaBlock) {
return ((JavaBlock)block).getFirstTreeNode();
}
if (child2 instanceof LeafBlock) {
return ((LeafBlock)child2).getTreeNode();
if (block instanceof LeafBlock) {
return ((LeafBlock)block).getTreeNode();
}
if (block instanceof CStyleCommentBlock) {
return ((CStyleCommentBlock)block).getNode();
}
return null;
}

View File

@@ -15,7 +15,10 @@
*/
package com.intellij.psi.formatter.java;
import com.intellij.formatting.Block;
import com.intellij.formatting.Spacing;
import com.intellij.formatting.blocks.CStyleCommentBlock;
import com.intellij.formatting.blocks.TextLineBlock;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.lang.java.JavaParserDefinition;
@@ -76,7 +79,8 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor {
private static final ThreadLocal<JavaSpacePropertyProcessor> mySharedProcessorAllocator = new ThreadLocal<>();
private void doInit(ASTNode child, CommonCodeStyleSettings settings, JavaCodeStyleSettings javaSettings) {
private void doInit(Block block, CommonCodeStyleSettings settings, JavaCodeStyleSettings javaSettings) {
ASTNode child = AbstractJavaBlock.getTreeNode(block);
if (isErrorElement(child)) {
myResult = Spacing.getReadOnlySpacing();
return;
@@ -101,11 +105,17 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor {
return;
}
if (block instanceof TextLineBlock) {
myResult = ((TextLineBlock)block).getSpacing();
return;
}
if (block instanceof CStyleCommentBlock) {
myResult = ((CStyleCommentBlock)block).getSpacing();
return;
}
if (myChild2 != null && StdTokenSets.COMMENT_BIT_SET.contains(myChild2.getElementType())) {
if (myChild2.getElementType() == JavaTokenType.C_STYLE_COMMENT) {
myResult = Spacing.getReadOnlySpacing();
}
else if (mySettings.KEEP_FIRST_COLUMN_COMMENT) {
if (mySettings.KEEP_FIRST_COLUMN_COMMENT) {
myResult = Spacing.createKeepingFirstColumnSpacing(0, Integer.MAX_VALUE, true, mySettings.KEEP_BLANK_LINES_IN_CODE);
}
else {
@@ -1758,7 +1768,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor {
}
@SuppressWarnings({"ConstantConditions"})
public static Spacing getSpacing(ASTNode node, CommonCodeStyleSettings settings, JavaCodeStyleSettings javaSettings) {
public static Spacing getSpacing(Block node, CommonCodeStyleSettings settings, JavaCodeStyleSettings javaSettings) {
JavaSpacePropertyProcessor spacePropertyProcessor = mySharedProcessorAllocator.get();
try {
if (spacePropertyProcessor == null) {

View File

@@ -95,7 +95,7 @@ public class SyntheticCodeBlock implements Block, JavaBlock{
@Override
public Spacing getSpacing(Block child1, @NotNull Block child2) {
return JavaSpacePropertyProcessor.getSpacing(AbstractJavaBlock.getTreeNode(child2), mySettings, myJavaSettings);
return JavaSpacePropertyProcessor.getSpacing(child2, mySettings, myJavaSettings);
}
public String toString() {

View File

@@ -12,7 +12,7 @@ public class Main {
BinaryOperator<Integer> min = Math::min;
String foo = "xyz";
/*check*/
/*check*/
boolean b = selector.andThen(Collections.singleton(/* "xyz" here */ "xyz")::contains).apply(/* foo here */ foo);
}
}

View File

@@ -3,8 +3,8 @@ import java.util.List;
public class Main {
public static void test(List<CharSequence> list) {
/*before dot*/
/*after dot*/
/*before dot*/
/*after dot*/
list.stream()
/*before dot2*/./*after dot2*/map(cs -> /*length!!!*/ cs.subSequence(/*subsequence*/1, 5).length()).forEach(System.out::println);
}

View File

@@ -3,7 +3,7 @@ import java.util.List;
public class Main {
public static void test(List<CharSequence> list) {
/*out of body*/
/*out of body*/
list.stream().map(cs -> cs/*in body*/.subSequence(1, 5).length()).forEach(System.out::println);
}
}

View File

@@ -2,10 +2,10 @@
import java.util.stream.Stream;
class Test {
void foo(Stream<String> stringStream ) {
stringStream.filter(name -> name.startsWith("A") && name.//comment2
length() > 1//comment
/*comment1*/
).findAny();
}
void foo(Stream<String> stringStream ) {
stringStream.filter(name -> name.startsWith("A") && name.//comment2
length() > 1//comment
/*comment1*/
).findAny();
}
}

View File

@@ -2,9 +2,9 @@
import java.util.stream.Stream;
class Test {
void foo(Stream<String> stringStream ) {
stringStream.filt<caret>er(name -> name.startsWith("A"))//comment
.filter(a -> a.//comment2
length() > 1 /*comment1*/).findAny();
}
void foo(Stream<String> stringStream ) {
stringStream.filt<caret>er(name -> name.startsWith("A"))//comment
.filter(a -> a.//comment2
length() > 1 /*comment1*/).findAny();
}
}

View File

@@ -11,10 +11,10 @@ public class Main {
}
public Number testOptionalComments(Optional<MyList> strList) {
/* optional is present */
/*return something */
/* optional is absent */
/* return null*/
/* optional is present */
/*return something */
/* optional is absent */
/* return null*/
return strList.map(myList -> myList.size() > /*too big*/ 1 ? myList.get(1) : 1.0).orElse(null);
}
}

View File

@@ -8,11 +8,11 @@ class T {
}
else if (s.startsWith("@")) {
return s.substring(1); // return comment
/* inline 1 *//* inline 2 */
/* inline 1 *//* inline 2 */
}
else if (s.startsWith("#")) {
return "#"; // return comment
/* inline */
/* inline */
}
return s; // return comment
}

View File

@@ -21,7 +21,7 @@ public class Main {
}
public static List<String> testUseName() {
/*limit*/
/*limit*/
List<String> list = new ArrayList<>();
long limit = 20;
for (String x = ""; ; x = x /* add "a" */ + "a") {

View File

@@ -10,7 +10,7 @@ public class Test {
// comment2
System.out.println("hello");
/*in return */
/*in return */
String s = "foo" + //inline
"bar";
}

View File

@@ -4,8 +4,8 @@ import java.util.function.Function;
public class Test {
public static void main(String[] args) {
/* bar */
/* who-hoo */
/* bar */
/* who-hoo */
String s = "foo";
}
}

View File

@@ -4,7 +4,7 @@ import java.util.function.Function;
public class Test {
public static void main(String[] args) {
/* bar */
/* bar */
String s = ("a" +/* who-hoo */ "x") + "foo";
}
}

View File

@@ -2,7 +2,7 @@
class Test {
public int test(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
/*otherwise bigger*/
/*otherwise bigger*/
}
public int test2(String s1, String s2) {

View File

@@ -3,9 +3,9 @@ public class Test {
public void test(String s1, String s2) {
System.out.println(Integer.compare(s1.length(), s2.length()));
System.out.println(Integer.compare(s2.length(), s1.length()));
/*greater!*/
/*less!*/
/*equal!*/
/*greater!*/
/*less!*/
/*equal!*/
System.out.println(Integer.compare(s1.length(), s2.length()));
System.out.println(Integer.compare(s2.length(), s1.length()));
System.out.println(Integer.compare(s2.length(), s1.length()));

View File

@@ -4,7 +4,7 @@ import java.util.Arrays;
class Test {
long cnt() {
/*count*/
/*count*/
return (long) Arrays.asList('d', 'e', 'f')./*stream*/size()/*after*/;
}
}

View File

@@ -4,7 +4,7 @@ import java.util.Arrays;
class Test {
int cnt() {
/*inside*/
/*inside*/
return Arrays.asList('d', 'e', 'f').size();
}
}

View File

@@ -3,6 +3,6 @@
import java.util.Arrays;
class Test {
/*count*/
/*count*/
long cnt = (long) Arrays.asList('d', 'e', 'f')./*stream*/size()/*after*/;
}

View File

@@ -7,7 +7,7 @@ public class Main {
private String str;
public void testGetOrDefault(Map<String, String> map, String key, Main other) {
/* output none */
/* output none */
System.out.println(/* output map value */ map.getOrDefault("k", NONE));
}
}

View File

@@ -11,7 +11,7 @@ public class Test {
"d", "1", "e", /* this is also 1*/ "1", "f", "1", "g", "1", // G is important!
"h", "1", "i", "1",
/* Finally J */
/* why not putting comment inside the call expression? */"j", "1");
/* Finally J */
/* why not putting comment inside the call expression? */"j", "1");
}
}

View File

@@ -3,7 +3,7 @@ import java.util.stream.Stream;
public class Test {
public void test() {
/*redundant*/
/*redundant*/
System.out.println(Stream.of(/*just one number*/123).count());
}
}

View File

@@ -36,8 +36,8 @@ public class Main {
public boolean ternaryFnMrGenericComment(List<String> list, Function<String, Boolean> fn, boolean b) {
return list.stream().allMatch(b ? // select
/* comment */ String::isEmpty :
/* comment2 */fn::apply);
/* comment */ String::isEmpty :
/* comment2 */fn::apply);
}
public <T extends Boolean> boolean doubleTernaryMr(List<String> list, boolean b, boolean b2) {
@@ -49,7 +49,7 @@ public class Main {
}
public boolean anyMatchBooleanValue(List<String> list) {
/* ditto boolean!*/
/* ditto boolean!*/
return list.stream().anyMatch(String::isEmpty);
}

View File

@@ -5,7 +5,7 @@ import java.util.List;
public class Main {
void test(List<String> list) {
// hello
/* in return */
/* in return */
long count = list.stream()
.peek(System.out::println)
.count();

View File

@@ -3,17 +3,17 @@
import java.util.List;
public class Main {
void test(List<String> list) {
long count = list.stream()
.peek(e -> {
if(e.isEmpty()) {
System.out.println("Empty line passed!");
throw new IllegalArgumentException();
}
// hello
/* in return */
})
.count();
System.out.println(count);
}
void test(List<String> list) {
long count = list.stream()
.peek(e -> {
if(e.isEmpty()) {
System.out.println("Empty line passed!");
throw new IllegalArgumentException();
}
// hello
/* in return */
})
.count();
System.out.println(count);
}
}

View File

@@ -4,7 +4,7 @@ import java.util.*;
class Test {
public void testToArray(List<String[]> data) {
/*generate array*/
/*generate array*/
String[][] array = data.subList(0, /*max number*/ 10).toArray(new String[0][]);
}
}

View File

@@ -3,17 +3,17 @@
import java.util.List;
public class Main {
void test(List<String> list) {
long count = list.stream()
.ma<caret>p(e -> {
if(e.isEmpty()) {
System.out.println("Empty line passed!");
throw new IllegalArgumentException();
}
// hello
return /* in return */ e;
})
.count();
System.out.println(count);
}
void test(List<String> list) {
long count = list.stream()
.ma<caret>p(e -> {
if(e.isEmpty()) {
System.out.println("Empty line passed!");
throw new IllegalArgumentException();
}
// hello
return /* in return */ e;
})
.count();
System.out.println(count);
}
}

View File

@@ -1,5 +1,5 @@
class Test {
/*
*/
/*
*/
}

View File

@@ -1,5 +1,5 @@
class Test {
/*and comment here*///comment
/*and comment here*///comment
public static final String xxx = "";
{

View File

@@ -80,7 +80,7 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest {
" AAAAAA.b()\n" +
" .c()\n" +
" .d()\n" +
" /* simple block comment */\n" +
" /* simple block comment */\n" +
" .e();\n" +
" }\n" +
"}");

View File

@@ -603,7 +603,7 @@ public class JavaFormatterIndentationTest extends AbstractJavaFormatterTest {
String expected =
"/*\n" +
"\t* comment\n" +
" * comment\n" +
" */\n" +
"class Test {\n" +
"}";

View File

@@ -3388,5 +3388,20 @@ public void testSCR260() throws Exception {
"}"
);
}
public void testFormatCStyleCommentWithAsterisks() {
doMethodTest(
" for (Object o : new Object[]{}) {\n" +
"/*\n" +
" *\n" +
" \t\t\t\t\t */\n" +
" }\n",
"for (Object o : new Object[]{}) {\n" +
" /*\n" +
" *\n" +
" */\n" +
"}\n"
);
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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.
*/
package com.intellij.formatting.blocks
import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.TokenType
import com.intellij.psi.formatter.common.AbstractBlock
fun ASTNode.prev(): ASTNode? {
var prev = treePrev
while (prev != null && prev.elementType == TokenType.WHITE_SPACE) {
prev = prev.treePrev
}
if (prev != null) return prev
return if (treeParent != null) treeParent.prev() else null
}
class CStyleCommentBlock(comment: ASTNode, private val indent: Indent?): AbstractBlock(comment, null, null) {
private val lines by lazy { lineBlocks() }
val isCommentFormattable by lazy {
lines.drop(1).all { it.text.startsWith("*") }
}
val spacing: Spacing?
get() = if (isCommentFormattable) null else Spacing.getReadOnlySpacing()
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
val isLicenseComment = child1 == null && node.prev() == null
if (isLicenseComment) {
return Spacing.getReadOnlySpacing()
}
return child2.getSpacing(null, this)
}
override fun getIndent() = indent
override fun buildChildren(): List<Block> {
if (!isCommentFormattable) return emptyList()
return lines.map {
val text = it.text
val indent = when {
!isCommentFormattable -> null
text.startsWith("/*") -> Indent.getNoneIndent()
else -> Indent.getSpaceIndent(1)
}
TextLineBlock(text, it.textRange, null, indent, null)
}
}
private fun lineBlocks(): List<LineInfo> {
return node.text
.mapIndexed { index, char -> index to char }
.split { it.second == '\n' }
.mapNotNull {
val block = it.dropWhile { Character.isWhitespace(it.second) }
if (block.isEmpty()) return@mapNotNull null
val text = block.map { it.second }.joinToString("").trimEnd()
val startOffset = node.startOffset + block.first().first
val range = TextRange(startOffset, startOffset + text.length)
LineInfo(text, range)
}
}
override fun isLeaf() = !isCommentFormattable
}
private class LineInfo(val text: String, val textRange: TextRange)
class TextLineBlock(
val text: String,
private val textRange: TextRange,
private val alignment: Alignment?,
private val indent: Indent?,
val spacing: Spacing?
) : Block {
override fun getTextRange(): TextRange {
return textRange
}
override fun getSubBlocks(): List<Block> = emptyList()
override fun getWrap() = null
override fun getIndent() = indent
override fun getAlignment() = alignment
override fun getSpacing(child1: Block?, child2: Block) = spacing
override fun getChildAttributes(newChildIndex: Int): ChildAttributes {
throw UnsupportedOperationException("Should not be called")
}
override fun isIncomplete() = false
override fun isLeaf() = true
override fun toString(): String {
return "TextLineBlock(text='$text', textRange=$textRange)"
}
}
fun <T> List<T>.split(predicate: (T) -> Boolean): List<List<T>> {
if (indices.isEmpty()) return listOf()
val result = mutableListOf<List<T>>()
val current = mutableListOf<T>()
for (e in this) {
if (predicate(e)) {
result.add(current.toList())
current.clear()
}
else {
current.add(e)
}
}
if (current.isNotEmpty()) {
result.add(current)
}
return result
}

View File

@@ -1,6 +1,6 @@
class Comment3 {{
int i = 8;
/*giant*/
/*giant*/
String t = "killer" +/*robots*/"with laser eyes" + //coming
i + //to
"\n" + //destroy