mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
[Java. Code Formatting] Add better whitespace detection for AdjustWhitespaceLineTextBlockReformatPostProcessor
IDEA-271085 GitOrigin-RevId: 9b77c8be1713797b705fe5860b826a9d36dcb2d9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6920302df3
commit
4daf0e509c
@@ -8,6 +8,19 @@ public final class BasicLiteralUtil {
|
||||
private BasicLiteralUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indent string for a text block expression. The indent string is calculated based on the indentation
|
||||
* of the text block lines.
|
||||
*
|
||||
* @param expression the text block expression to calculate indent for
|
||||
* @return The indent string of the text block, or null if it is impossible to determine indent string.
|
||||
*/
|
||||
public static @Nullable String getTextBlockIndentString(@NotNull PsiElement expression) {
|
||||
String[] lines = getTextBlockLines(expression.getText(), true);
|
||||
if (lines == null) return null;
|
||||
return getTextBlockIndentString(lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression text block expression to calculate indent for
|
||||
* @return the indent of text block lines; may return -1 if text block is heavily malformed
|
||||
@@ -18,7 +31,6 @@ public final class BasicLiteralUtil {
|
||||
return getTextBlockIndent(lines);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the lines of text inside the quotes of a text block. No further processing is performed.
|
||||
* Any escaped characters will remain escaped. Indent is not stripped.
|
||||
@@ -65,17 +77,48 @@ public final class BasicLiteralUtil {
|
||||
}
|
||||
|
||||
public static int getTextBlockIndent(String @NotNull [] lines, boolean preserveContent, boolean ignoreLastLine) {
|
||||
return getTextBlockIndentInner(lines, preserveContent, ignoreLastLine).indentSize;
|
||||
}
|
||||
|
||||
private static @NotNull IndentResult getTextBlockIndentInner(String @NotNull[] lines, boolean preserveContent, boolean ignoreLastLine) {
|
||||
int prefix = Integer.MAX_VALUE;
|
||||
int position = -1;
|
||||
for (int i = 0; i < lines.length && prefix != 0; i++) {
|
||||
String line = lines[i];
|
||||
int indent = 0;
|
||||
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
|
||||
if (indent == line.length() && (i < lines.length - 1 || ignoreLastLine)) {
|
||||
if (!preserveContent) lines[i] = "";
|
||||
if (lines.length == 1) prefix = indent;
|
||||
if (lines.length == 1) {
|
||||
prefix = indent;
|
||||
position = i;
|
||||
}
|
||||
}
|
||||
else if (indent < prefix) {
|
||||
prefix = indent;
|
||||
position = i;
|
||||
}
|
||||
else if (indent < prefix) prefix = indent;
|
||||
}
|
||||
return prefix;
|
||||
return new IndentResult(prefix, position);
|
||||
}
|
||||
|
||||
private static @NotNull String getTextBlockIndentString(String @NotNull [] lines) {
|
||||
IndentResult result = getTextBlockIndentInner(lines, true, false);
|
||||
String restorationLine = lines[result.indentLineNumber];
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < result.indentSize; ++i) {
|
||||
builder.append(restorationLine.charAt(i));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static class IndentResult {
|
||||
private final int indentSize;
|
||||
private final int indentLineNumber;
|
||||
|
||||
private IndentResult(int indentSize, int indentLineNumber) {
|
||||
this.indentSize = indentSize;
|
||||
this.indentLineNumber = indentLineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.formatter.java
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings
|
||||
@@ -12,15 +11,15 @@ import com.intellij.util.text.CharArrayUtil
|
||||
|
||||
class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
||||
override fun processElement(source: PsiElement, settings: CodeStyleSettings): PsiElement {
|
||||
processFile(source.containingFile, source.textRange, settings)
|
||||
processFile(source.containingFile, source.textRange)
|
||||
return source
|
||||
}
|
||||
|
||||
override fun processText(source: PsiFile, rangeToReformat: TextRange, settings: CodeStyleSettings): TextRange {
|
||||
return processFile(source, rangeToReformat, settings)
|
||||
return processFile(source, rangeToReformat)
|
||||
}
|
||||
|
||||
private fun processFile(source: PsiFile, rangeToReformat: TextRange, settings: CodeStyleSettings): TextRange {
|
||||
private fun processFile(source: PsiFile, rangeToReformat: TextRange): TextRange {
|
||||
val documentManager = PsiDocumentManager.getInstance(source.project)
|
||||
val document = documentManager.getDocument(source) ?: return rangeToReformat
|
||||
|
||||
@@ -30,12 +29,11 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
||||
var deltaLength = 0
|
||||
textBlockVisitor.whiteSpaceRangesWithIndentList.sortedByDescending {
|
||||
it.whiteSpaceRange.startOffset
|
||||
}.forEach { (range, indent) ->
|
||||
val newSpaces = buildWhitespaceString(settings, source.fileType, indent)
|
||||
deltaLength += newSpaces.length
|
||||
}.forEach { (range, indentString) ->
|
||||
deltaLength += indentString.length
|
||||
deltaLength -= range.length
|
||||
document.deleteString(range.startOffset, range.endOffset)
|
||||
document.insertString(range.startOffset, newSpaces)
|
||||
document.insertString(range.startOffset, indentString)
|
||||
}
|
||||
|
||||
documentManager.commitDocument(document)
|
||||
@@ -44,11 +42,6 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
||||
|
||||
override fun isWhitespaceOnly(): Boolean = true
|
||||
|
||||
private fun buildWhitespaceString(settings: CodeStyleSettings, fileType: FileType, indent: Int): String {
|
||||
val whitespaceSymbol = if (settings.useTabCharacter(fileType)) "\t" else " "
|
||||
return whitespaceSymbol.repeat(indent)
|
||||
}
|
||||
|
||||
private class TextBlockVisitor(private val rangeToReformat: TextRange) : JavaRecursiveElementVisitor() {
|
||||
val whiteSpaceRangesWithIndentList: List<WhiteSpaceRangeWithIndent>
|
||||
get() = _whiteSpaceRangesWithIndentList
|
||||
@@ -60,13 +53,13 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
val indent = PsiLiteralUtil.getTextBlockIndent(literal)
|
||||
if (indent == -1) {
|
||||
val indentString = PsiLiteralUtil.getTextBlockIndentString(literal)
|
||||
if (indentString == null) {
|
||||
return
|
||||
}
|
||||
val text = literal.text
|
||||
|
||||
JavaFormatterUtil.extractTextRangesFromLiteralText(text, indent, true)
|
||||
JavaFormatterUtil.extractTextRangesFromLiteralText(text, indentString.length, true)
|
||||
.filter { textRange ->
|
||||
rangeToReformat.contains(textRange.shiftRight(literal.startOffset)) &&
|
||||
CharArrayUtil.isEmptyOrSpaces(literal.text, textRange.startOffset, textRange.endOffset)
|
||||
@@ -74,11 +67,11 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
||||
.map { it.shiftRight(literal.startOffset) }
|
||||
.reversed()
|
||||
.forEach {
|
||||
_whiteSpaceRangesWithIndentList.add(WhiteSpaceRangeWithIndent(it, indent))
|
||||
_whiteSpaceRangesWithIndentList.add(WhiteSpaceRangeWithIndent(it, indentString))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private data class WhiteSpaceRangeWithIndent(val whiteSpaceRange: TextRange, val indent: Int)
|
||||
private data class WhiteSpaceRangeWithIndent(val whiteSpaceRange: TextRange, val indentString: String)
|
||||
}
|
||||
@@ -418,6 +418,16 @@ public final class PsiLiteralUtil {
|
||||
return BasicLiteralUtil.getTextBlockIndent(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the indent string of a text block literal expression.
|
||||
* @param expression The text block literal expression.
|
||||
* @return The indent string of the text block, or null if it is impossible to determine indent string.
|
||||
* @see #getTextBlockIndent(PsiLiteralExpression)
|
||||
*/
|
||||
public static @Nullable String getTextBlockIndentString(@NotNull PsiLiteralExpression expression) {
|
||||
return BasicLiteralUtil.getTextBlockIndentString(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getTextBlockIndent(PsiLiteralExpression)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
public class Formatter {
|
||||
void foo() {
|
||||
String sss = """
|
||||
|
||||
|
||||
foo""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
public class Formatter {
|
||||
void foo() {
|
||||
String sss = """
|
||||
|
||||
|
||||
foo""";
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,11 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessorTest : LightPlatformCode
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNonIntegerNumberOfTabs() {
|
||||
getCommonSettings().indentOptions?.USE_TAB_CHARACTER = true
|
||||
getJavaSettings().ALIGN_MULTILINE_TEXT_BLOCKS = true
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testAlignTextBlockWhitespacesLessThanAlignment() {
|
||||
getJavaSettings().ALIGN_MULTILINE_TEXT_BLOCKS = true
|
||||
|
||||
Reference in New Issue
Block a user