mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-58743 Formatter: Add ability to configure formatter's 'white space symbols'
Dedicated extension is added - com.intellij.formatting.WhiteSpaceFormattingStrategy
This commit is contained in:
@@ -54,4 +54,23 @@ public interface FormattingDocumentModel {
|
||||
int getTextLength();
|
||||
|
||||
Document getDocument();
|
||||
|
||||
/**
|
||||
* Allows to answer if all document symbols from <code>[startOffset; endOffset)</code> region are treated as white spaces by formatter.
|
||||
*
|
||||
* @param startOffset target start document offset (inclusive)
|
||||
* @param endOffset target end document offset (exclusive)
|
||||
* @return <code>true</code> if all document symbols from <code>[startOffset; endOffset)</code> region are treated
|
||||
* as white spaces by formatter; <code>false</code> otherwise
|
||||
*/
|
||||
boolean containsWhiteSpaceSymbolsOnly(int startOffset, int endOffset);
|
||||
|
||||
/**
|
||||
* Allows to answer if given symbol is treated by the current model as white space symbol during formatting.
|
||||
*
|
||||
* @param symbol symbols to check
|
||||
* @return <code>true</code> if given symbol is treated by the current model as white space symbol during formatting;
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
boolean isWhiteSpaceSymbol(char symbol);
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.lang.LanguageExtension;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Exposes pre-configured {@link WhiteSpaceFormattingStrategy} objects to use in a per-language manner.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 20, 2010 7:41:55 PM
|
||||
*/
|
||||
public class LanguageWhiteSpaceFormattingStrategy extends LanguageExtension<Collection<WhiteSpaceFormattingStrategy>> {
|
||||
|
||||
public static final String EP_NAME = "lang.whiteSpaceFormattingStrategy";
|
||||
public static final LanguageWhiteSpaceFormattingStrategy INSTANCE = new LanguageWhiteSpaceFormattingStrategy();
|
||||
|
||||
private LanguageWhiteSpaceFormattingStrategy() {
|
||||
super(EP_NAME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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 org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Defines common contract for strategy that determines if particular symbol or sequence of symbols may be treated as
|
||||
* white space during formatting.
|
||||
* <p/>
|
||||
* <code>'Treated as white space'</code> here means that formatter is free to remove any number of such symbols or replace them
|
||||
* by 'pure' white spaces.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 20, 2010 5:05:08 PM
|
||||
*/
|
||||
public interface WhiteSpaceFormattingStrategy {
|
||||
|
||||
/**
|
||||
* Checks if given sub-sequence of the given text contains symbols that may be treated as white spaces.
|
||||
*
|
||||
* @param text text to check
|
||||
* @param start start offset to use with the given text (inclusive)
|
||||
* @param end end offset to use with the given text (exclusive)
|
||||
* @return offset of the first symbol that belongs to <code>[startOffset; endOffset)</code> range
|
||||
* and is not treated as white space by the current strategy <b>or</b> value that is greater
|
||||
* or equal to the given <code>'end'</code> parameter if all target sub-sequence symbols
|
||||
* can be treated as white spaces
|
||||
*/
|
||||
int check(@NotNull CharSequence text, int start, int end);
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.formatter.FormattingDocumentModelImpl;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -66,8 +65,6 @@ class WhiteSpace {
|
||||
private static final byte CONTAINS_SPACES_INITIALLY = 0x40;
|
||||
private static final int LF_COUNT_SHIFT = 7;
|
||||
private static final int MAX_LF_COUNT = 1 << 24;
|
||||
@NonNls private static final String CDATA_START = "<![CDATA[";
|
||||
@NonNls private static final String CDATA_END = "]]>";
|
||||
|
||||
/**
|
||||
* Creates new <code>WhiteSpace</code> object with the given start offset and a flag that shows if current white space is
|
||||
@@ -158,19 +155,13 @@ class WhiteSpace {
|
||||
*/
|
||||
private boolean coveredByBlock(final FormattingDocumentModel model) {
|
||||
if (myInitial == null) return true;
|
||||
String s = myInitial.toString().trim();
|
||||
if (s.length() == 0) return true;
|
||||
if (model.containsWhiteSpaceSymbolsOnly(myStart, myEnd)) return true;
|
||||
|
||||
if (!(model instanceof FormattingDocumentModelImpl)) return false;
|
||||
PsiFile psiFile = ((FormattingDocumentModelImpl)model).getFile();
|
||||
if (psiFile == null) return false;
|
||||
PsiElement start = psiFile.findElementAt(myStart);
|
||||
PsiElement end = psiFile.findElementAt(myEnd-1);
|
||||
|
||||
// CDATA usage at common white space stuff smells because it makes no sense in context, for example, white space for pure java block.
|
||||
if (s.startsWith(CDATA_START)) s = s.substring(CDATA_START.length());
|
||||
if (s.endsWith(CDATA_END)) s = s.substring(0, s.length() - CDATA_END.length());
|
||||
s = s.trim();
|
||||
if (s.length() == 0) return true;
|
||||
return start == end && start instanceof PsiWhiteSpace; // there maybe non-white text inside CDATA-encoded injected elements
|
||||
}
|
||||
|
||||
@@ -183,12 +174,10 @@ class WhiteSpace {
|
||||
mySpaces = 0;
|
||||
myIndentSpaces = 0;
|
||||
break;
|
||||
case ' ':
|
||||
mySpaces++;
|
||||
break;
|
||||
case '\t':
|
||||
myIndentSpaces += tabSize;
|
||||
break;
|
||||
default: mySpaces++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.psi.formatter;
|
||||
|
||||
import com.intellij.formatting.WhiteSpaceFormattingStrategy;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* {@link WhiteSpaceFormattingStrategy} implementation that considers to be white spaces all symbols from
|
||||
* standard XML CDATA section ('{@code <![CDATA[...]]>}').
|
||||
* <p/>
|
||||
* Thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 20, 2010 5:39:50 PM
|
||||
*/
|
||||
public class CdataWhiteSpaceDefinitionStrategy implements WhiteSpaceFormattingStrategy {
|
||||
|
||||
@NonNls public static final String CDATA_START = "<![CDATA[";
|
||||
@NonNls public static final String CDATA_END = "]]>";
|
||||
|
||||
@Override
|
||||
public int check(@NotNull CharSequence text, int start, int end) {
|
||||
if (CharArrayUtil.indexOf(text, CDATA_START, start, end) != start) {
|
||||
return start;
|
||||
}
|
||||
|
||||
int i = CharArrayUtil.indexOf(text, CDATA_END, start, end);
|
||||
if (i < 0) {
|
||||
return start;
|
||||
}
|
||||
|
||||
int result = i + CDATA_END.length();
|
||||
return result > end ? start : result;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@
|
||||
package com.intellij.psi.formatter;
|
||||
|
||||
import com.intellij.formatting.FormattingDocumentModel;
|
||||
import com.intellij.formatting.LanguageWhiteSpaceFormattingStrategy;
|
||||
import com.intellij.formatting.WhiteSpaceFormattingStrategy;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
@@ -28,15 +31,31 @@ import com.intellij.psi.impl.PsiDocumentManagerImpl;
|
||||
import com.intellij.psi.impl.PsiToDocumentSynchronizer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.*;
|
||||
|
||||
public class FormattingDocumentModelImpl implements FormattingDocumentModel{
|
||||
|
||||
private static final List<WhiteSpaceFormattingStrategy> SHARED_STRATEGIES = Arrays.asList(
|
||||
new StaticWhiteSpaceDefinitionStrategy(' ', '\t'), new CdataWhiteSpaceDefinitionStrategy()
|
||||
);
|
||||
|
||||
private final Set<WhiteSpaceFormattingStrategy> myWhiteSpaceStrategies = new HashSet<WhiteSpaceFormattingStrategy>(SHARED_STRATEGIES);
|
||||
private final Document myDocument;
|
||||
private final PsiFile myFile;
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.FormattingDocumentModelImpl");
|
||||
|
||||
public FormattingDocumentModelImpl(final Document document, PsiFile file) {
|
||||
myDocument = document;
|
||||
myFile = file;
|
||||
if (file != null) {
|
||||
Language language = file.getLanguage();
|
||||
Collection<WhiteSpaceFormattingStrategy> strategies = LanguageWhiteSpaceFormattingStrategy.INSTANCE.forLanguage(language);
|
||||
if (strategies != null) {
|
||||
myWhiteSpaceStrategies.addAll(strategies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FormattingDocumentModelImpl createOn(PsiFile file) {
|
||||
@@ -68,23 +87,28 @@ public class FormattingDocumentModelImpl implements FormattingDocumentModel{
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineNumber(int offset) {
|
||||
LOG.assertTrue (offset <= myDocument.getTextLength());
|
||||
return myDocument.getLineNumber(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineStartOffset(int line) {
|
||||
return myDocument.getLineStartOffset(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getText(final TextRange textRange) {
|
||||
return myDocument.getCharsSequence().subSequence(textRange.getStartOffset(), textRange.getEndOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextLength() {
|
||||
return myDocument.getTextLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Document getDocument() {
|
||||
return myDocument;
|
||||
}
|
||||
@@ -93,6 +117,33 @@ public class FormattingDocumentModelImpl implements FormattingDocumentModel{
|
||||
return myFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsWhiteSpaceSymbolsOnly(int startOffset, int endOffset) {
|
||||
return containsWhiteSpaceSymbolsOnly(myDocument.getCharsSequence(), startOffset, endOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhiteSpaceSymbol(char symbol) {
|
||||
return containsWhiteSpaceSymbolsOnly(CharBuffer.wrap(new char[] {symbol}), 0, 1);
|
||||
}
|
||||
|
||||
private boolean containsWhiteSpaceSymbolsOnly(CharSequence text, int startOffset, int endOffset) {
|
||||
int offset = startOffset;
|
||||
while (offset < endOffset) {
|
||||
int oldOffset = offset;
|
||||
for (WhiteSpaceFormattingStrategy strategy : myWhiteSpaceStrategies) {
|
||||
offset = strategy.check(text, offset, endOffset);
|
||||
if (offset > oldOffset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (offset == oldOffset) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return offset >= endOffset;
|
||||
}
|
||||
|
||||
public static boolean canUseDocumentModel(@NotNull Document document,@NotNull PsiFile file) {
|
||||
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject());
|
||||
return !psiDocumentManager.isUncommited(document) &&
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.psi.formatter;
|
||||
|
||||
import com.intellij.formatting.WhiteSpaceFormattingStrategy;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* {@link WhiteSpaceFormattingStrategy} implementation that is pre-configured with the set of symbols that may
|
||||
* be treated as white spaces.
|
||||
* <p/>
|
||||
* Thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 20, 2010 5:11:49 PM
|
||||
*/
|
||||
public class StaticWhiteSpaceDefinitionStrategy implements WhiteSpaceFormattingStrategy {
|
||||
|
||||
private final TIntHashSet myWhiteSpaceSymbols = new TIntHashSet();
|
||||
|
||||
/**
|
||||
* Creates new <code>StaticWhiteSpaceDefinitionStrategy</code> object with the symbols that should be treated as white spaces.
|
||||
*
|
||||
* @param whiteSpaceSymbols symbols that should be treated as white spaces by the current strategy
|
||||
*/
|
||||
public StaticWhiteSpaceDefinitionStrategy(char ... whiteSpaceSymbols) {
|
||||
for (char symbol : whiteSpaceSymbols) {
|
||||
myWhiteSpaceSymbols.add(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int check(@NotNull CharSequence text, int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = text.charAt(i);
|
||||
if (!myWhiteSpaceSymbols.contains(c)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.psi.formatter;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import com.intellij.psi.formatter.CdataWhiteSpaceDefinitionStrategy;
|
||||
import org.junit.Test;
|
||||
import org.junit.Before;
|
||||
import org.junit.After;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 09/20/2010
|
||||
*/
|
||||
public class CdataWhiteSpaceDefinitionStrategyTest {
|
||||
|
||||
private CdataWhiteSpaceDefinitionStrategy myStrategy;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
myStrategy = new CdataWhiteSpaceDefinitionStrategy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withoutClosingSection() {
|
||||
String text = "<![CDATA[xxx]>"; // Doesn't contain double ']'
|
||||
assertSame(0, myStrategy.check(text, 0, text.length()));
|
||||
|
||||
text = "<![CDATA[xxx]]>";
|
||||
assertSame(0, myStrategy.check(text, 0, text.length() - 1)); // Last symbol is out of scope
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cdataNotAtFirstOffset() {
|
||||
String text = " <![CDATA[xxx]]>";
|
||||
assertSame(0, myStrategy.check(text, 0, text.length()));
|
||||
assertSame(1, myStrategy.check(text, 1, text.length()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialMatch() {
|
||||
String text = "<![CDATA[xxx]]> ";
|
||||
int expectedOffset = text.indexOf(">") + 1;
|
||||
assertSame(expectedOffset, myStrategy.check(text, 0, text.length()));
|
||||
assertSame(expectedOffset + 1, myStrategy.check(" " + text, 1, text.length()));
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.intellij.psi.formatter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 09/20/2010
|
||||
*/
|
||||
public class StaticWhiteSpaceDefinitionStrategyTest {
|
||||
|
||||
private StaticWhiteSpaceDefinitionStrategy myStrategy;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
myStrategy = new StaticWhiteSpaceDefinitionStrategy('a', 'b', 'c');
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failOnTheFirstSymbol() {
|
||||
assertSame(0, myStrategy.check("def", 0, 2));
|
||||
assertSame(1, myStrategy.check("defghi", 1, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failInTheMiddle() {
|
||||
assertSame(1, myStrategy.check("adef", 0, 3));
|
||||
assertSame(2, myStrategy.check("daefghi", 1, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failOnTheLastSymbol() {
|
||||
assertSame(2, myStrategy.check("abe", 0, 3));
|
||||
assertSame(3, myStrategy.check("dabefghi", 1, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulMatch() {
|
||||
assertSame(3, myStrategy.check("abc", 0, 3));
|
||||
assertSame(4, myStrategy.check("dabcefg", 1, 4));
|
||||
}
|
||||
}
|
||||
@@ -280,6 +280,8 @@
|
||||
beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.lineWrapStrategy"
|
||||
beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.whiteSpaceFormattingStrategy"
|
||||
beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
|
||||
<extensionPoint name="gotoDeclarationHandler"
|
||||
interface="com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler"/>
|
||||
|
||||
Reference in New Issue
Block a user