mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +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() {
|
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
|
* @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
|
* @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);
|
return getTextBlockIndent(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the lines of text inside the quotes of a text block. No further processing is performed.
|
* 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.
|
* 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) {
|
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 prefix = Integer.MAX_VALUE;
|
||||||
|
int position = -1;
|
||||||
for (int i = 0; i < lines.length && prefix != 0; i++) {
|
for (int i = 0; i < lines.length && prefix != 0; i++) {
|
||||||
String line = lines[i];
|
String line = lines[i];
|
||||||
int indent = 0;
|
int indent = 0;
|
||||||
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
|
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
|
||||||
if (indent == line.length() && (i < lines.length - 1 || ignoreLastLine)) {
|
if (indent == line.length() && (i < lines.length - 1 || ignoreLastLine)) {
|
||||||
if (!preserveContent) lines[i] = "";
|
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.
|
// 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
|
package com.intellij.psi.formatter.java
|
||||||
|
|
||||||
import com.intellij.openapi.fileTypes.FileType
|
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings
|
import com.intellij.psi.codeStyle.CodeStyleSettings
|
||||||
@@ -12,15 +11,15 @@ import com.intellij.util.text.CharArrayUtil
|
|||||||
|
|
||||||
class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
||||||
override fun processElement(source: PsiElement, settings: CodeStyleSettings): PsiElement {
|
override fun processElement(source: PsiElement, settings: CodeStyleSettings): PsiElement {
|
||||||
processFile(source.containingFile, source.textRange, settings)
|
processFile(source.containingFile, source.textRange)
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processText(source: PsiFile, rangeToReformat: TextRange, settings: CodeStyleSettings): TextRange {
|
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 documentManager = PsiDocumentManager.getInstance(source.project)
|
||||||
val document = documentManager.getDocument(source) ?: return rangeToReformat
|
val document = documentManager.getDocument(source) ?: return rangeToReformat
|
||||||
|
|
||||||
@@ -30,12 +29,11 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
|||||||
var deltaLength = 0
|
var deltaLength = 0
|
||||||
textBlockVisitor.whiteSpaceRangesWithIndentList.sortedByDescending {
|
textBlockVisitor.whiteSpaceRangesWithIndentList.sortedByDescending {
|
||||||
it.whiteSpaceRange.startOffset
|
it.whiteSpaceRange.startOffset
|
||||||
}.forEach { (range, indent) ->
|
}.forEach { (range, indentString) ->
|
||||||
val newSpaces = buildWhitespaceString(settings, source.fileType, indent)
|
deltaLength += indentString.length
|
||||||
deltaLength += newSpaces.length
|
|
||||||
deltaLength -= range.length
|
deltaLength -= range.length
|
||||||
document.deleteString(range.startOffset, range.endOffset)
|
document.deleteString(range.startOffset, range.endOffset)
|
||||||
document.insertString(range.startOffset, newSpaces)
|
document.insertString(range.startOffset, indentString)
|
||||||
}
|
}
|
||||||
|
|
||||||
documentManager.commitDocument(document)
|
documentManager.commitDocument(document)
|
||||||
@@ -44,11 +42,6 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
|||||||
|
|
||||||
override fun isWhitespaceOnly(): Boolean = true
|
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() {
|
private class TextBlockVisitor(private val rangeToReformat: TextRange) : JavaRecursiveElementVisitor() {
|
||||||
val whiteSpaceRangesWithIndentList: List<WhiteSpaceRangeWithIndent>
|
val whiteSpaceRangesWithIndentList: List<WhiteSpaceRangeWithIndent>
|
||||||
get() = _whiteSpaceRangesWithIndentList
|
get() = _whiteSpaceRangesWithIndentList
|
||||||
@@ -60,13 +53,13 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val indent = PsiLiteralUtil.getTextBlockIndent(literal)
|
val indentString = PsiLiteralUtil.getTextBlockIndentString(literal)
|
||||||
if (indent == -1) {
|
if (indentString == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val text = literal.text
|
val text = literal.text
|
||||||
|
|
||||||
JavaFormatterUtil.extractTextRangesFromLiteralText(text, indent, true)
|
JavaFormatterUtil.extractTextRangesFromLiteralText(text, indentString.length, true)
|
||||||
.filter { textRange ->
|
.filter { textRange ->
|
||||||
rangeToReformat.contains(textRange.shiftRight(literal.startOffset)) &&
|
rangeToReformat.contains(textRange.shiftRight(literal.startOffset)) &&
|
||||||
CharArrayUtil.isEmptyOrSpaces(literal.text, textRange.startOffset, textRange.endOffset)
|
CharArrayUtil.isEmptyOrSpaces(literal.text, textRange.startOffset, textRange.endOffset)
|
||||||
@@ -74,11 +67,11 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessor : PostFormatProcessor {
|
|||||||
.map { it.shiftRight(literal.startOffset) }
|
.map { it.shiftRight(literal.startOffset) }
|
||||||
.reversed()
|
.reversed()
|
||||||
.forEach {
|
.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);
|
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)
|
* @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()
|
doTest()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testNonIntegerNumberOfTabs() {
|
||||||
|
getCommonSettings().indentOptions?.USE_TAB_CHARACTER = true
|
||||||
|
getJavaSettings().ALIGN_MULTILINE_TEXT_BLOCKS = true
|
||||||
|
doTest()
|
||||||
|
}
|
||||||
|
|
||||||
fun testAlignTextBlockWhitespacesLessThanAlignment() {
|
fun testAlignTextBlockWhitespacesLessThanAlignment() {
|
||||||
getJavaSettings().ALIGN_MULTILINE_TEXT_BLOCKS = true
|
getJavaSettings().ALIGN_MULTILINE_TEXT_BLOCKS = true
|
||||||
|
|||||||
Reference in New Issue
Block a user