IDEA-87639 Add a possibility to specify a dependent LF spacing if a checked range does NOT have line feeds.

1. API changes - DependentSpacingRule allows to define properties to use on dependent change trigger;
2. Corresponding changes have been made to the formatter core;
This commit is contained in:
Denis.Zhdanov
2012-06-28 15:34:01 +04:00
parent 52e7424f0e
commit 657c523636
7 changed files with 216 additions and 37 deletions
@@ -0,0 +1,97 @@
/*
* Copyright 2000-2012 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;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* Holds settings that should be used if
* {@link Spacing#createDependentLFSpacing(int, int, TextRange, boolean, int, DependentSpacingRule) dependent spacing}
* target region changes its 'contains line feeds' status.
*
* @author Denis Zhdanov
* @since 6/28/12 1:08 PM
*/
public class DependentSpacingRule {
enum Anchor {
MIN_LINE_FEEDS, MAX_LINE_FEEDS
}
enum Trigger {
HAS_LINE_FEEDS, DOES_NOT_HAVE_LINE_FEEDS
}
public static final DependentSpacingRule DEFAULT = new DependentSpacingRule(Trigger.HAS_LINE_FEEDS).registerData(Anchor.MIN_LINE_FEEDS, 1);
private final Map<Anchor, Object> myData = new HashMap<Anchor, Object>();
@NotNull private final Trigger myTrigger;
public DependentSpacingRule(@NotNull Trigger trigger) {
myTrigger = trigger;
}
@NotNull
public Trigger getTrigger() {
return myTrigger;
}
/**
* Allows to register given data for the given anchor within the current rule.
*
* @param anchor target anchor
* @param data data to register for the given anchor
* @param <T> data's type
* @see #getData(Anchor)
*/
public <T> DependentSpacingRule registerData(@NotNull Anchor anchor, @NotNull T data) {
myData.put(anchor, data);
return this;
}
/**
* @param anchor target data anchor
* @return <code>true</code> if there is a data registered for the given anchor within the current rule;
* <code>false</code> otherwise
*/
public boolean hasData(@NotNull Anchor anchor) {
return myData.containsKey(anchor);
}
/**
* Allows to retrieve data associated with the given anchor.
*
* @param anchor target anchor
* @param <T> data's type
* @return data associated for the given anchor
* @throws IllegalArgumentException if no data is registered for the given anchor
* (use {@link #hasData(Anchor)} for the preliminary examination)
*/
@SuppressWarnings("unchecked")
public <T> T getData(@NotNull Anchor anchor) throws IllegalArgumentException {
if (!myData.containsKey(anchor)) {
throw new IllegalArgumentException(String.format(
"No data is registered for the dependent spacing rule %s. Registered: %s", anchor, myData
));
}
return (T)myData.get(anchor);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -16,6 +16,7 @@
package com.intellij.formatting;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
/**
* The spacing setting for a formatting model block. Indicates the number of spaces and/or
@@ -100,19 +101,50 @@ public abstract class Spacing {
* to which the spacing setting instance is related, or <code>Integer.MAX_VALUE</code>
* if the number of spaces is not limited. Spaces are deleted if there are
* more than this amount of spaces in the document.
* @param dependance The text range checked for the presense of line breaks.
* @param dependency The text range checked for the presence of line breaks.
* @param keepLineBreaks Whether the existing line breaks between the blocks should be preserved.
* @param keepBlankLines Whether the existing blank lines between the blocks should be preserved.
* @return the spacing setting instance.
*/
public static Spacing createDependentLFSpacing(int minSpaces,
int maxSpaces,
TextRange dependance,
TextRange dependency,
boolean keepLineBreaks,
int keepBlankLines) {
return myFactory.createDependentLFSpacing(minSpaces, maxSpaces, dependance, keepLineBreaks, keepBlankLines);
int keepBlankLines)
{
return createDependentLFSpacing(minSpaces, maxSpaces, dependency, keepLineBreaks, keepBlankLines, DependentSpacingRule.DEFAULT);
}
/**
* Creates a spacing setting instance which uses settings from the given dependent spacing rule if the specified text range changes
* its 'has line feed' status during formatting (new line feed is added and the range hasn't contained them before
* or it contained line feed(s) and it was removed during formatting).
* <p/>
* Used for formatting rules like the "next line if wrapped" brace placement.
*
* @param minSpaces The minimum number of spaces that should be present between the blocks
* to which the spacing setting instance is related. Spaces are inserted
* if there are less than this amount of spaces in the document.
* @param maxSpaces The maximum number of spaces that should be present between the blocks
* to which the spacing setting instance is related, or <code>Integer.MAX_VALUE</code>
* if the number of spaces is not limited. Spaces are deleted if there are
* more than this amount of spaces in the document.
* @param dependencyRange The text range checked for the presence of line breaks.
* @param keepLineBreaks Whether the existing line breaks between the blocks should be preserved.
* @param keepBlankLines Whether the existing blank lines between the blocks should be preserved.
* @param rule settings to use if dependent region changes its 'contains line feed' status during formatting
* @return the spacing setting instance for the given parameters
*/
public static Spacing createDependentLFSpacing(int minSpaces,
int maxSpaces,
@NotNull TextRange dependencyRange,
boolean keepLineBreaks,
int keepBlankLines,
@NotNull DependentSpacingRule rule)
{
return myFactory.createDependentLFSpacing(minSpaces, maxSpaces, dependencyRange, keepLineBreaks, keepBlankLines, rule);
}
/**
* Creates a spacing setting instance which preserves the presence of spaces between the blocks but,
* if spaces are present, may insert or delete the spaces. Used, for example, for HTML formatting
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -16,34 +16,43 @@
package com.intellij.formatting;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
/**
* Internal interface for creating spacing instances.
*/
interface SpacingFactory {
public Spacing createSpacing(int minSpaces,
@NotNull
Spacing createSpacing(int minSpaces,
int maxSpaces,
int minLineFeeds,
boolean keepLineBreaks,
int keepBlankLines);
public Spacing getReadOnlySpacing();
@NotNull
Spacing getReadOnlySpacing();
@NotNull
Spacing createDependentLFSpacing(int minSpaces,
int maxSpaces,
@NotNull TextRange dependencyRange,
boolean keepLineBreaks,
int keepBlankLines,
@NotNull DependentSpacingRule rule);
public Spacing createDependentLFSpacing(int minSpaces,
int maxSpaces,
TextRange dependance,
boolean keepLineBreaks,
int keepBlankLines);
@NotNull
Spacing createSafeSpacing(boolean keepLineBreaks,
int keepBlankLines);
public Spacing createSafeSpacing(boolean keepLineBreaks,
int keepBlankLines);
public Spacing createKeepingFirstColumnSpacing(final int minSpaces,
final int maxSpaces,
final boolean keepLineBreaks,
final int keepBlankLines);
@NotNull
Spacing createKeepingFirstColumnSpacing(final int minSpaces,
final int maxSpaces,
final boolean keepLineBreaks,
final int keepBlankLines);
@NotNull
Spacing createSpacing(final int minSpaces, final int maxSpaces, final int minLineFeeds, final boolean keepLineBreaks,
final int keepBlankLines, final int prefLineFeeds);
}
@@ -17,6 +17,7 @@
package com.intellij.formatting;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
/**
* Extends {@link SpacingImpl} in order to add notion of dependency range.
@@ -24,29 +25,50 @@ import com.intellij.openapi.util.TextRange;
* <code>'Dependency'</code> here affect {@link #getMinLineFeeds() minLineFieeds} property value. See property contract for more details.
*/
public class DependantSpacingImpl extends SpacingImpl {
private final TextRange myDependency;
private static final int DEPENDENCE_CONTAINS_LF_MASK = 0x10;
private static final int DEPENDENCE_CONTAINS_LF_MASK = 0x10;
private static final int DEPENDENT_REGION_LF_CHANGED_MASK = 0x20;
@NotNull private final TextRange myDependency;
@NotNull private final DependentSpacingRule myRule;
public DependantSpacingImpl(final int minSpaces,
final int maxSpaces,
TextRange dependency,
@NotNull TextRange dependency,
final boolean keepLineBreaks,
final int keepBlankLines) {
final int keepBlankLines,
@NotNull DependentSpacingRule rule)
{
super(minSpaces, maxSpaces, 0, false, false, keepLineBreaks, keepBlankLines, false, 0);
myDependency = dependency;
myRule = rule;
}
/**
* @return <code>1</code> if dependency has line feeds; <code>0</code> otherwise
*/
public int getMinLineFeeds() {
if ((myFlags & DEPENDENCE_CONTAINS_LF_MASK) != 0) {
return 1;
if (!isTriggered()) {
return super.getMinLineFeeds();
}
else {
return 0;
if (myRule.hasData(DependentSpacingRule.Anchor.MIN_LINE_FEEDS)) {
return myRule.getData(DependentSpacingRule.Anchor.MIN_LINE_FEEDS);
}
if (myRule.hasData(DependentSpacingRule.Anchor.MAX_LINE_FEEDS)) {
return myRule.getData(DependentSpacingRule.Anchor.MAX_LINE_FEEDS);
}
return super.getMinLineFeeds();
}
@Override
public int getKeepBlankLines() {
if (!isTriggered() || !myRule.hasData(DependentSpacingRule.Anchor.MAX_LINE_FEEDS)) {
return super.getKeepBlankLines();
}
return 0;
}
public void refresh(FormatProcessor formatter) {
@@ -58,14 +80,16 @@ public class DependantSpacingImpl extends SpacingImpl {
else myFlags &= ~DEPENDENCE_CONTAINS_LF_MASK;
}
@NotNull
public TextRange getDependency() {
return myDependency;
}
/**
* Allows to answer whether the target dependent regions has been changed during formatting.
* Allows to answer whether 'contains line feed' status has been changed for the target dependent region during formatting.
*
* @return <code>true</code> if target dependent region has been changed during formatting; <code>false</code> otherwise
* @return <code>true</code> if target 'contains line feed' status has been changed for the target dependent region during formatting;
* <code>false</code> otherwise
*/
public final boolean isDependentRegionChanged() {
return (myFlags & DEPENDENT_REGION_LF_CHANGED_MASK) != 0;
@@ -85,4 +109,9 @@ public class DependantSpacingImpl extends SpacingImpl {
return "<DependantSpacing: minSpaces=" + getMinSpaces() + " maxSpaces=" + getMaxSpaces() + " minLineFeeds=" + getMinLineFeeds() + " dep=" +
myDependency + ">";
}
private boolean isTriggered() {
return myRule.getTrigger() == DependentSpacingRule.Trigger.HAS_LINE_FEEDS
^ (myFlags & DEPENDENCE_CONTAINS_LF_MASK) == 0;
}
}
@@ -108,7 +108,7 @@ class FormatProcessor {
* <pre>
* int[] data = {1, 2, 3};
* </pre>
* We want to keep that in one line with possible but place curly braces on separate lines if the width is not enough:
* We want to keep that in one line if possible but place curly braces on separate lines if the width is not enough:
* <pre>
* int[] data = { | &lt; right margin
* 1, 2, 3 |
@@ -133,6 +133,7 @@ public class FormatterImpl extends FormatterEx
return result;
}
@NotNull
public Spacing createSpacing(int minOffset,
int maxOffset,
int minLineFeeds,
@@ -141,13 +142,21 @@ public class FormatterImpl extends FormatterEx
return getSpacingImpl(minOffset, maxOffset, minLineFeeds, false, false, keepLineBreaks, keepBlankLines,false, 0);
}
@NotNull
public Spacing getReadOnlySpacing() {
return myReadOnlySpacing;
}
public Spacing createDependentLFSpacing(int minOffset, int maxOffset, TextRange dependence, boolean keepLineBreaks,
int keepBlankLines) {
return new DependantSpacingImpl(minOffset, maxOffset, dependence, keepLineBreaks, keepBlankLines);
@NotNull
@Override
public Spacing createDependentLFSpacing(int minSpaces,
int maxSpaces,
@NotNull TextRange dependencyRange,
boolean keepLineBreaks,
int keepBlankLines,
@NotNull DependentSpacingRule rule)
{
return new DependantSpacingImpl(minSpaces, maxSpaces, dependencyRange, keepLineBreaks, keepBlankLines, rule);
}
@NotNull
@@ -669,10 +678,12 @@ public class FormatterImpl extends FormatterEx
return myAbsoluteLabelIndent;
}
@NotNull
public Spacing createSafeSpacing(final boolean shouldKeepLineBreaks, final int keepBlankLines) {
return getSpacingImpl(0, 0, 0, false, true, shouldKeepLineBreaks, keepBlankLines, false, 0);
}
@NotNull
public Spacing createKeepingFirstColumnSpacing(final int minSpace,
final int maxSpace,
final boolean keepLineBreaks,
@@ -680,6 +691,7 @@ public class FormatterImpl extends FormatterEx
return getSpacingImpl(minSpace, maxSpace, -1, false, false, keepLineBreaks, keepBlankLines, true, 0);
}
@NotNull
public Spacing createSpacing(final int minSpaces, final int maxSpaces, final int minLineFeeds, final boolean keepLineBreaks, final int keepBlankLines,
final int prefLineFeeds) {
return getSpacingImpl(minSpaces, maxSpaces, -1, false, false, keepLineBreaks, keepBlankLines, false, prefLineFeeds);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -132,7 +132,7 @@ public final class InjectedLanguageBlockWrapper implements Block {
DependantSpacingImpl hostSpacing = (DependantSpacingImpl)spacing;
return new DependantSpacingImpl(
hostSpacing.getMinSpaces(), hostSpacing.getMaxSpaces(), hostSpacing.getDependency().shiftRight(shift),
hostSpacing.shouldKeepLineFeeds(), hostSpacing.getKeepBlankLines()
hostSpacing.shouldKeepLineFeeds(), hostSpacing.getKeepBlankLines(), DependentSpacingRule.DEFAULT
);
}
return spacing;