More readable rule language for indent position calculation

This commit is contained in:
Rustam Vishnyakov
2016-05-20 16:46:18 +03:00
parent 8e52a6ba25
commit 463adf8de2
2 changed files with 94 additions and 30 deletions
@@ -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);
}
}
@@ -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);
}