diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/JavaLikeLangLineIndentProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/JavaLikeLangLineIndentProvider.java index 1e6679c677c6..399271b16c6e 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/JavaLikeLangLineIndentProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/JavaLikeLangLineIndentProvider.java @@ -19,12 +19,9 @@ import com.intellij.formatting.Indent; import com.intellij.lang.Language; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; -import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,6 +30,14 @@ import org.jetbrains.annotations.Nullable; * the indentation, it forwards the request to FormatterBasedLineIndentProvider. */ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineIndentProvider { + + protected enum JavaLikeElement implements SemanticEditorPosition.SyntaxElement { + Whitespace, + Semicolon, + BlockClosingBrace + } + + @Nullable @Override public String getLineIndent(@NotNull Project project, @NotNull Editor editor, int offset) { @@ -54,39 +59,34 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI @Nullable protected Indent.Type getIndent(@NotNull Editor editor, int offset) { if (offset > 0) { - CharSequence docChars = editor.getDocument().getCharsSequence(); - EditorHighlighter highlighter = ((EditorEx)editor).getHighlighter(); - HighlighterIterator iterator = highlighter.createIterator(offset - 1); - if (isWhitespace(iterator.getTokenType())) { - if (containsLineBreaks(iterator, docChars)) { - iterator.retreat(); - if (!iterator.atEnd()) { - if (isEndOfCodeBlock(iterator)) return Indent.Type.NONE; - } - } + if (matchesRule(editor, offset - 1, + position -> position.isAt(JavaLikeElement.Whitespace) && + position.isAtMultiline() + && position + .before() + .beforeOptional(JavaLikeElement.Semicolon) + .beforeOptional(JavaLikeElement.Whitespace) + .isAt(JavaLikeElement.BlockClosingBrace) + )) { + return Indent.Type.NONE; } } return null; } - - protected abstract boolean isWhitespace(@NotNull IElementType tokenType); - - private static boolean containsLineBreaks(@NotNull HighlighterIterator iterator, @NotNull CharSequence chars) { - return CharArrayUtil.containLineBreaks(chars, iterator.getStart(), iterator.getEnd()); - } - - protected boolean isEndOfCodeBlock(@NotNull HighlighterIterator iterator) { - if (isSemicolon(iterator.getTokenType())) iterator.retreat(); - if (!iterator.atEnd()) { - if (isWhitespace(iterator.getTokenType())) iterator.retreat(); - if (!iterator.atEnd()) { - return isBlockClosingBrace(iterator.getTokenType()); + + private boolean matchesRule(@NotNull Editor editor, int offset, @NotNull Rule rule) { + SemanticEditorPosition editorPosition = new SemanticEditorPosition((EditorEx)editor, offset) { + @Override + public SyntaxElement map(@NotNull IElementType elementType) { + return mapType(elementType); } - } - return false; + }; + return rule.check(editorPosition); } - protected abstract boolean isSemicolon(@NotNull IElementType tokenType); + protected abstract SemanticEditorPosition.SyntaxElement mapType(@NotNull IElementType tokenType); - protected abstract boolean isBlockClosingBrace(@NotNull IElementType tokenType); + private interface Rule { + boolean check(SemanticEditorPosition wrapper); + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java new file mode 100644 index 000000000000..76e23aa03bd4 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2016 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.impl.source.codeStyle; + +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Rustam Vishnyakov + */ +public abstract class SemanticEditorPosition { + public interface SyntaxElement {} + + private final HighlighterIterator myIterator; + private final CharSequence myChars; + + public SemanticEditorPosition(@NotNull EditorEx editor, int offset) { + myChars = editor.getDocument().getCharsSequence(); + myIterator = editor.getHighlighter().createIterator(offset); + } + + public SemanticEditorPosition beforeOptional(@NotNull SyntaxElement syntaxElement) { + if (!myIterator.atEnd()) { + if (syntaxElement.equals(map(myIterator.getTokenType()))) myIterator.retreat(); + } + return this; + } + + public boolean isAtMultiline() { + if (!myIterator.atEnd()) { + return CharArrayUtil.containLineBreaks(myChars, myIterator.getStart(), myIterator.getEnd()); + } + return false; + } + + public SemanticEditorPosition before() { + if (!myIterator.atEnd()) { + myIterator.retreat(); + } + return this; + } + + public boolean isAt(@NotNull SyntaxElement syntaxElement) { + return !myIterator.atEnd() && syntaxElement.equals(map(myIterator.getTokenType())); + } + + public abstract SyntaxElement map(@NotNull IElementType elementType); +}