mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Provided way to reformat context around selected ranges, in order to make code look consistent. During "Wrapping Blocks" stage we collect all alignments from blocks which will be reformatted, during processing stage if we encounter read only block with such an alignment we drop it's read only status, so it could be realigned also.
This commit is contained in:
+9
-1
@@ -35,6 +35,7 @@ import com.intellij.psi.codeStyle.*;
|
||||
import com.intellij.psi.codeStyle.autodetect.DetectableIndentOptionsProvider;
|
||||
import com.intellij.testFramework.LightIdeaTestCase;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.LineReader;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -83,7 +84,7 @@ public abstract class AbstractJavaFormatterTest extends LightIdeaTestCase {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
protected enum Action {REFORMAT, INDENT}
|
||||
protected enum Action {REFORMAT, INDENT, REFORMAT_WITH_CONTEXT}
|
||||
|
||||
public static JavaCodeStyleSettings getJavaSettings() {
|
||||
return getSettings().getRootSettings().getCustomSettings(JavaCodeStyleSettings.class);
|
||||
@@ -107,6 +108,13 @@ public abstract class AbstractJavaFormatterTest extends LightIdeaTestCase {
|
||||
CodeStyleManager.getInstance(getProject()).adjustLineIndent(psiFile, startOffset);
|
||||
}
|
||||
});
|
||||
ACTIONS.put(Action.REFORMAT_WITH_CONTEXT, new TestFormatAction() {
|
||||
@Override
|
||||
public void run(PsiFile psiFile, int startOffset, int endOffset) {
|
||||
List<TextRange> ranges = ContainerUtil.newArrayList(new TextRange(startOffset, endOffset));
|
||||
CodeStyleManager.getInstance(getProject()).reformatTextWithContext(psiFile, ranges);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final String BASE_PATH = JavaTestUtil.getJavaTestDataPath() + "/psi/formatter/java";
|
||||
|
||||
+64
@@ -16,6 +16,7 @@
|
||||
package com.intellij.psi.formatter.java;
|
||||
|
||||
import com.intellij.openapi.fileTypes.StdFileTypes;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
|
||||
@@ -593,4 +594,67 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest {
|
||||
" String superString = \"\";\n" +
|
||||
"}");
|
||||
}
|
||||
|
||||
public void test_Shift_All_AlignedParameters() {
|
||||
myLineRange = new TextRange(2, 2);
|
||||
getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
|
||||
doTextTest(
|
||||
Action.REFORMAT_WITH_CONTEXT,
|
||||
"public class Test {\n" +
|
||||
" \n" +
|
||||
" public void fooooo(String foo,\n" +
|
||||
" String booo,\n" +
|
||||
" String kakadoo) {\n" +
|
||||
"\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
|
||||
"public class Test {\n" +
|
||||
"\n" +
|
||||
" public void fooooo(String foo,\n" +
|
||||
" String booo,\n" +
|
||||
" String kakadoo) {\n" +
|
||||
"\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
public void test_Align_UnselectedField_IfNeeded() {
|
||||
myLineRange = new TextRange(2, 2);
|
||||
getSettings().ALIGN_GROUP_FIELD_DECLARATIONS = true;
|
||||
doTextTest(
|
||||
Action.REFORMAT_WITH_CONTEXT,
|
||||
"public class Test {\n" +
|
||||
" public int i = 1;\n" +
|
||||
" public String iiiiiiiiii = 2;\n" +
|
||||
"}",
|
||||
"public class Test {\n" +
|
||||
" public int i = 1;\n" +
|
||||
" public String iiiiiiiiii = 2;\n" +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
public void test_Align_UnselectedVariable_IfNeeded() {
|
||||
myLineRange = new TextRange(3, 3);
|
||||
getSettings().ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS = true;
|
||||
doTextTest(
|
||||
Action.REFORMAT_WITH_CONTEXT,
|
||||
"public class Test {\n" +
|
||||
" public void test() {\n" +
|
||||
" int s = 2;\n" +
|
||||
" String sssss = 3;\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
"public class Test {\n" +
|
||||
" public void test() {\n" +
|
||||
" int s = 2;\n" +
|
||||
" String sssss = 3;\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,15 @@ public abstract class CodeStyleManager {
|
||||
*/
|
||||
public abstract void reformatText(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) throws IncorrectOperationException;
|
||||
|
||||
|
||||
/**
|
||||
* Works as #reformatText, but reformats not only specified ranges, but also some context around to make code look consistent
|
||||
* @param file
|
||||
* @param ranges
|
||||
* @throws IncorrectOperationException
|
||||
*/
|
||||
public abstract void reformatTextWithContext(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) throws IncorrectOperationException;
|
||||
|
||||
/**
|
||||
* Re-formats the specified range of a file, modifying only line indents and leaving
|
||||
* all other whitespace intact.
|
||||
|
||||
@@ -59,6 +59,8 @@ class FormatProcessor {
|
||||
private static final int BULK_REPLACE_OPTIMIZATION_CRITERIA = 3000;
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.formatting.FormatProcessor");
|
||||
private Set<Alignment> myAlignmentsInsideRangesToModify = null;
|
||||
private boolean myReformatContext;
|
||||
|
||||
private LeafBlockWrapper myCurrentBlock;
|
||||
|
||||
@@ -164,23 +166,21 @@ class FormatProcessor {
|
||||
@Nullable FormatTextRanges affectedRanges,
|
||||
@NotNull FormattingProgressCallback progressCallback)
|
||||
{
|
||||
this(docModel, rootBlock, settings, indentOptions, affectedRanges, -1, progressCallback);
|
||||
this(docModel, rootBlock, new FormatOptions(settings, indentOptions, affectedRanges, false), progressCallback);
|
||||
}
|
||||
|
||||
public FormatProcessor(final FormattingDocumentModel docModel,
|
||||
Block rootBlock,
|
||||
CodeStyleSettings settings,
|
||||
CommonCodeStyleSettings.IndentOptions indentOptions,
|
||||
@Nullable FormatTextRanges affectedRanges,
|
||||
int interestingOffset,
|
||||
@NotNull FormattingProgressCallback progressCallback)
|
||||
public FormatProcessor(FormattingDocumentModel model,
|
||||
Block block,
|
||||
FormatOptions options,
|
||||
@NotNull FormattingProgressCallback callback)
|
||||
{
|
||||
myProgressCallback = progressCallback;
|
||||
myDefaultIndentOption = indentOptions;
|
||||
mySettings = settings;
|
||||
myDocument = docModel.getDocument();
|
||||
myCurrentState = new WrapBlocksState(rootBlock, docModel, affectedRanges, interestingOffset);
|
||||
myRightMargin = getRightMargin(rootBlock);
|
||||
myProgressCallback = callback;
|
||||
myDefaultIndentOption = options.myIndentOptions;
|
||||
mySettings = options.mySettings;
|
||||
myDocument = model.getDocument();
|
||||
myReformatContext = options.myReformatContext;
|
||||
myCurrentState = new WrapBlocksState(block, model, options.myAffectedRanges, options.myInterestingOffset);
|
||||
myRightMargin = getRightMargin(block);
|
||||
}
|
||||
|
||||
private int getRightMargin(Block rootBlock) {
|
||||
@@ -474,6 +474,12 @@ class FormatProcessor {
|
||||
final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty();
|
||||
final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace();
|
||||
|
||||
if (isReformatSelectedRangesContext()) {
|
||||
if (isCurrentBlockAlignmentUsedInRangesToModify() && whiteSpace.isReadOnly()) {
|
||||
whiteSpace.setReadOnly(false);
|
||||
}
|
||||
}
|
||||
|
||||
whiteSpace.arrangeLineFeeds(spaceProperty, this);
|
||||
|
||||
if (!whiteSpace.containsLineFeeds()) {
|
||||
@@ -517,6 +523,27 @@ class FormatProcessor {
|
||||
myCurrentBlock = myCurrentBlock.getNextBlock();
|
||||
}
|
||||
|
||||
private boolean isReformatSelectedRangesContext() {
|
||||
return myReformatContext && myAlignmentsInsideRangesToModify != null;
|
||||
}
|
||||
|
||||
private boolean isCurrentBlockAlignmentUsedInRangesToModify() {
|
||||
AbstractBlockWrapper block = myCurrentBlock;
|
||||
AlignmentImpl alignment = myCurrentBlock.getAlignment();
|
||||
|
||||
while (alignment == null
|
||||
&& block != null
|
||||
&& block.getStartOffset() == myCurrentBlock.getStartOffset())
|
||||
{
|
||||
block = block.getParent();
|
||||
if (block != null) {
|
||||
alignment = block.getAlignment();
|
||||
}
|
||||
}
|
||||
|
||||
return myAlignmentsInsideRangesToModify.contains(alignment);
|
||||
}
|
||||
|
||||
private boolean shouldReformatPreviouslyLocatedDependentSpacing(WhiteSpace space) {
|
||||
final TextRange changed = space.getTextRange();
|
||||
final SortedMap<TextRange, DependantSpacingImpl> sortedHeadMap = myPreviousDependencies.tailMap(changed);
|
||||
@@ -1339,6 +1366,7 @@ class FormatProcessor {
|
||||
myWrapper = InitialInfoBuilder.prepareToBuildBlocksSequentially(
|
||||
root, model, affectedRanges, mySettings, myDefaultIndentOption, interestingOffset, myProgressCallback
|
||||
);
|
||||
myWrapper.setCollectAlignmentsInsideFormattingRange(myReformatContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1364,6 +1392,7 @@ class FormatProcessor {
|
||||
myTextRangeToWrapper = buildTextRangeToInfoMap(myFirstTokenBlock);
|
||||
myLastWhiteSpace = new WhiteSpace(getLastBlock().getEndOffset(), false);
|
||||
myLastWhiteSpace.append(myModel.getTextLength(), myModel, myDefaultIndentOption);
|
||||
myAlignmentsInsideRangesToModify = myWrapper.getAlignmentsInsideRangeToModify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1530,4 +1559,34 @@ class FormatProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class FormatOptions {
|
||||
private CodeStyleSettings mySettings;
|
||||
private CommonCodeStyleSettings.IndentOptions myIndentOptions;
|
||||
|
||||
private FormatTextRanges myAffectedRanges;
|
||||
private boolean myReformatContext;
|
||||
|
||||
private int myInterestingOffset;
|
||||
|
||||
public FormatOptions(CodeStyleSettings settings,
|
||||
CommonCodeStyleSettings.IndentOptions options,
|
||||
FormatTextRanges ranges,
|
||||
boolean reformatContext) {
|
||||
this(settings, options, ranges, reformatContext, -1);
|
||||
}
|
||||
|
||||
public FormatOptions(CodeStyleSettings settings,
|
||||
CommonCodeStyleSettings.IndentOptions options,
|
||||
FormatTextRanges ranges,
|
||||
boolean reformatContext,
|
||||
int interestingOffset) {
|
||||
mySettings = settings;
|
||||
myIndentOptions = options;
|
||||
myAffectedRanges = ranges;
|
||||
myReformatContext = reformatContext;
|
||||
myInterestingOffset = interestingOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,12 @@ public abstract class FormatterEx{
|
||||
CommonCodeStyleSettings.IndentOptions javaIndentOptions,
|
||||
FormatTextRanges affectedRanges) throws IncorrectOperationException;
|
||||
|
||||
public abstract void format(final FormattingModel model,
|
||||
final CodeStyleSettings settings,
|
||||
final CommonCodeStyleSettings.IndentOptions indentOptions,
|
||||
final FormatTextRanges affectedRanges,
|
||||
final boolean formatContextAroundRanges) throws IncorrectOperationException;
|
||||
|
||||
|
||||
public abstract IndentInfo getWhiteSpaceBefore(final FormattingDocumentModel psiBasedFormattingModel,
|
||||
final Block block,
|
||||
|
||||
@@ -47,6 +47,8 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.intellij.formatting.FormatProcessor.FormatOptions;
|
||||
|
||||
public class FormatterImpl extends FormatterEx
|
||||
implements IndentFactory,
|
||||
WrapFactory,
|
||||
@@ -255,14 +257,23 @@ public class FormatterImpl extends FormatterEx
|
||||
final CodeStyleSettings settings,
|
||||
final CommonCodeStyleSettings.IndentOptions indentOptions,
|
||||
final FormatTextRanges affectedRanges) throws IncorrectOperationException {
|
||||
format(model, settings, indentOptions, affectedRanges, false);
|
||||
}
|
||||
|
||||
public void format(final FormattingModel model,
|
||||
final CodeStyleSettings settings,
|
||||
final CommonCodeStyleSettings.IndentOptions indentOptions,
|
||||
final FormatTextRanges affectedRanges,
|
||||
final boolean formatContextAroundRanges) throws IncorrectOperationException {
|
||||
try {
|
||||
validateModel(model);
|
||||
SequentialTask task = new MyFormattingTask() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected FormatProcessor buildProcessor() {
|
||||
FormatOptions options = new FormatOptions(settings, indentOptions, affectedRanges, formatContextAroundRanges);
|
||||
FormatProcessor processor = new FormatProcessor(
|
||||
model.getDocumentModel(), model.getRootBlock(), settings, indentOptions, affectedRanges, getProgressCallback()
|
||||
model.getDocumentModel(), model.getRootBlock(), options, getProgressCallback()
|
||||
);
|
||||
processor.format(model, true);
|
||||
return processor;
|
||||
@@ -522,8 +533,9 @@ public class FormatterImpl extends FormatterEx
|
||||
@Nullable FormatTextRanges affectedRanges,
|
||||
int interestingOffset)
|
||||
{
|
||||
FormatOptions options = new FormatOptions(settings, indentOptions, affectedRanges, false, interestingOffset);
|
||||
FormatProcessor processor = new FormatProcessor(
|
||||
docModel, rootBlock, settings, indentOptions, affectedRanges, interestingOffset, FormattingProgressCallback.EMPTY
|
||||
docModel, rootBlock, options, FormattingProgressCallback.EMPTY
|
||||
);
|
||||
while (!processor.iteration()) ;
|
||||
return processor;
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.formatter.FormattingDocumentModelImpl;
|
||||
import com.intellij.psi.formatter.ReadOnlyBlockInformationProvider;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -35,6 +36,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Allows to build {@link AbstractBlockWrapper formatting block wrappers} for the target {@link Block formatting blocks}.
|
||||
@@ -66,6 +68,9 @@ class InitialInfoBuilder {
|
||||
|
||||
private static final boolean INLINE_TABS_ENABLED = "true".equalsIgnoreCase(System.getProperty("inline.tabs.enabled"));
|
||||
|
||||
private Set<Alignment> myAlignmentsInsideRangeToModify = ContainerUtil.newHashSet();
|
||||
private boolean myCollectAlignmentsInsideFormattingRange = false;
|
||||
|
||||
private InitialInfoBuilder(final FormattingDocumentModel model,
|
||||
@Nullable final FormatTextRanges affectedRanges,
|
||||
@NotNull CodeStyleSettings settings,
|
||||
@@ -163,16 +168,17 @@ class InitialInfoBuilder {
|
||||
}
|
||||
|
||||
myCurrentWhiteSpace.append(blockStartOffset, myModel, myOptions);
|
||||
|
||||
boolean isReadOnly = isReadOnly(rootBlock, rootBlockIsRightBlock);
|
||||
if (myCollectAlignmentsInsideFormattingRange && !isReadOnly && rootBlock.getAlignment() != null) {
|
||||
myAlignmentsInsideRangeToModify.add(rootBlock.getAlignment());
|
||||
}
|
||||
|
||||
ReadOnlyBlockInformationProvider previousProvider = myReadOnlyBlockInformationProvider;
|
||||
try {
|
||||
if (rootBlock instanceof ReadOnlyBlockInformationProvider) {
|
||||
myReadOnlyBlockInformationProvider = (ReadOnlyBlockInformationProvider)rootBlock;
|
||||
}
|
||||
if (isReadOnly) {
|
||||
return processSimpleBlock(rootBlock, parent, true, index, parentBlock);
|
||||
}
|
||||
|
||||
final List<Block> subBlocks = rootBlock.getSubBlocks();
|
||||
if (subBlocks.isEmpty() || myReadOnlyBlockInformationProvider != null
|
||||
@@ -432,6 +438,14 @@ class InitialInfoBuilder {
|
||||
return langThrowable;
|
||||
}
|
||||
|
||||
public Set<Alignment> getAlignmentsInsideRangeToModify() {
|
||||
return myAlignmentsInsideRangeToModify;
|
||||
}
|
||||
|
||||
public void setCollectAlignmentsInsideFormattingRange(boolean value) {
|
||||
myCollectAlignmentsInsideFormattingRange = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to wrap {@link Block code blocks} sequentially, hence, need to store a processing state and continue from the point
|
||||
* where we stopped the processing last time.
|
||||
|
||||
+6
-1
@@ -79,6 +79,7 @@ public class CodeFormatterFacade {
|
||||
private final CodeStyleSettings mySettings;
|
||||
private final FormatterTagHandler myTagHandler;
|
||||
private final int myRightMargin;
|
||||
private boolean myReformatContext;
|
||||
|
||||
public CodeFormatterFacade(CodeStyleSettings settings, @Nullable Language language) {
|
||||
mySettings = settings;
|
||||
@@ -86,6 +87,10 @@ public class CodeFormatterFacade {
|
||||
myRightMargin = mySettings.getRightMargin(language);
|
||||
}
|
||||
|
||||
public void setReformatContext(boolean value) {
|
||||
myReformatContext = value;
|
||||
}
|
||||
|
||||
public ASTNode processElement(ASTNode element) {
|
||||
TextRange range = element.getTextRange();
|
||||
return processRange(element, range.getStartOffset(), range.getEndOffset());
|
||||
@@ -240,7 +245,7 @@ public class CodeFormatterFacade {
|
||||
CommonCodeStyleSettings.IndentOptions indentOptions =
|
||||
mySettings.getIndentOptionsByFile(file, textRanges.size() == 1 ? textRanges.get(0).getTextRange() : null);
|
||||
|
||||
formatter.format(model, mySettings, indentOptions, ranges);
|
||||
formatter.format(model, mySettings, indentOptions, ranges, myReformatContext);
|
||||
for (FormatTextRanges.FormatTextRange range : textRanges) {
|
||||
TextRange textRange = range.getTextRange();
|
||||
wrapLongLinesIfNecessary(file, document, textRange.getStartOffset(), textRange.getEndOffset());
|
||||
|
||||
+11
@@ -164,7 +164,16 @@ public class CodeStyleManagerImpl extends CodeStyleManager {
|
||||
reformatText(file, ranges, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reformatTextWithContext(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) throws IncorrectOperationException {
|
||||
reformatText(file, ranges, null, true);
|
||||
}
|
||||
|
||||
public void reformatText(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges, @Nullable Editor editor) throws IncorrectOperationException {
|
||||
reformatText(file, ranges, editor, false);
|
||||
}
|
||||
|
||||
public void reformatText(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges, @Nullable Editor editor, boolean reformatContext) throws IncorrectOperationException {
|
||||
if (ranges.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -181,6 +190,8 @@ public class CodeStyleManagerImpl extends CodeStyleManager {
|
||||
transformAllChildren(treeElement);
|
||||
|
||||
final CodeFormatterFacade codeFormatter = new CodeFormatterFacade(getSettings(), file.getLanguage());
|
||||
codeFormatter.setReformatContext(reformatContext);
|
||||
|
||||
LOG.assertTrue(file.isValid(), "File name: " + file.getName() + " , class: " + file.getClass().getSimpleName());
|
||||
|
||||
if (editor == null) {
|
||||
|
||||
+5
@@ -71,6 +71,11 @@ public class MockCodeStyleManager extends CodeStyleManager {
|
||||
myFormattedLinesForFile.put(file, formattedLines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reformatTextWithContext(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) throws IncorrectOperationException {
|
||||
throw new UnsupportedOperationException("com.intellij.codeInsight.actions.MockCodeStyleManager.reformatTextWithContext(...)");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Project getProject() {
|
||||
|
||||
Reference in New Issue
Block a user