JavaLikeLangIndentProvider refactoring

This commit is contained in:
Rustam Vishnyakov
2016-06-02 19:54:42 +03:00
parent 09adbb03cc
commit c13453de88
3 changed files with 174 additions and 131 deletions
@@ -123,7 +123,20 @@ public abstract class SemanticEditorPosition {
}
return false;
}
public CharSequence getChars() {
return myChars;
}
public int findStartOf(@NotNull SyntaxElement element) {
while (!myIterator.atEnd()) {
if (element.equals(map(myIterator.getTokenType()))) return myIterator.getStart();
myIterator.retreat();
}
return -1;
}
@Nullable
public SyntaxElement getCurrElement() {
return !myIterator.atEnd() ? map(myIterator.getTokenType()) : null;
@@ -0,0 +1,107 @@
/*
* 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.lineIndent;
import com.intellij.formatting.Indent;
import com.intellij.formatting.IndentInfo;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.source.codeStyle.SemanticEditorPosition;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.formatting.Indent.Type.CONTINUATION;
import static com.intellij.formatting.Indent.Type.NORMAL;
class IndentCalculator {
private @NotNull final Project myProject;
private @NotNull final Editor myEditor;
private @NotNull BaseLineOffsetCalculator myBaseLineOffsetCalculator;
private @NotNull final Indent.Type myIndentType;
public IndentCalculator(@NotNull Project project,
@NotNull Editor editor,
@NotNull BaseLineOffsetCalculator baseLineOffsetCalculator,
@NotNull Indent.Type type) {
myProject = project;
myEditor = editor;
myBaseLineOffsetCalculator = baseLineOffsetCalculator;
myIndentType = type;
}
public final static BaseLineOffsetCalculator LINE_BEFORE = new BaseLineOffsetCalculator() {
@Override
public int getOffsetInBaseIndentLine(@NotNull SemanticEditorPosition currPosition) {
return CharArrayUtil.shiftBackward(currPosition.getChars(), currPosition.getStartOffset(), " \t\n\r");
}
};
@Nullable
String getIndentString(@NotNull SemanticEditorPosition currPosition) {
Document document = myEditor.getDocument();
String baseIndent = getBaseIndent(currPosition);
PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
if (file != null) {
CommonCodeStyleSettings.IndentOptions options = CodeStyleSettingsManager.getSettings(myProject).getIndentOptionsByFile(file);
return
baseIndent + new IndentInfo(0, indentTypeToSize(myIndentType, options), 0, false).generateNewWhiteSpace(options);
}
return null;
}
@NotNull
private String getBaseIndent(@NotNull SemanticEditorPosition currPosition) {
CharSequence docChars = myEditor.getDocument().getCharsSequence();
int offset = currPosition.getStartOffset();
if (offset > 0) {
int indentLineOffset = myBaseLineOffsetCalculator.getOffsetInBaseIndentLine(currPosition);
if (indentLineOffset > 0) {
int indentStart = CharArrayUtil.shiftBackwardUntil(docChars, indentLineOffset, "\n") + 1;
if (indentStart >= 0) {
int indentEnd = CharArrayUtil.shiftForward(docChars, indentStart, " \t");
if (indentEnd > indentStart) {
return docChars.subSequence(indentStart, indentEnd).toString();
}
}
}
}
return "";
}
private static int indentTypeToSize(@NotNull Indent.Type indentType, @NotNull CommonCodeStyleSettings.IndentOptions options) {
if (indentType == NORMAL) {
return options.INDENT_SIZE;
}
else if (indentType == CONTINUATION) {
return options.CONTINUATION_INDENT_SIZE;
}
return 0;
}
public interface BaseLineOffsetCalculator {
int getOffsetInBaseIndentLine(@NotNull SemanticEditorPosition position);
}
}
@@ -15,21 +15,16 @@
*/
package com.intellij.psi.impl.source.codeStyle.lineIndent;
import com.intellij.formatting.IndentInfo;
import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.source.codeStyle.SemanticEditorPosition;
import com.intellij.psi.impl.source.codeStyle.SemanticEditorPosition.SyntaxElement;
import com.intellij.psi.impl.source.codeStyle.lineIndent.IndentCalculator.BaseLineOffsetCalculator;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -62,65 +57,62 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI
DocBlockEnd
}
@Nullable
@Override
public String getLineIndent(@NotNull Project project, @NotNull Editor editor, Language language, int offset) {
Pair<Type,SyntaxElement> indentData = getIndent(project, editor, language, offset);
if (indentData != null){
return getIndentString(project, editor, offset, indentData);
IndentCalculator indentCalculator = getIndent(project, editor, language, offset);
if (indentCalculator != null){
return indentCalculator.getIndentString(getPosition(editor, offset));
}
return super.getLineIndent(project, editor, language, offset);
}
@Nullable
protected Pair<Type,SyntaxElement> getIndent(@NotNull Project project, @NotNull Editor editor, @Nullable Language language, int offset) {
protected IndentCalculator getIndent(@NotNull Project project, @NotNull Editor editor, @Nullable Language language, int offset) {
if (offset > 0) {
offset--;
if (getPosition(editor, offset).matchesRule(
position -> position.isAt(Whitespace) &&
position.isAtMultiline())) {
IndentCalculatorFactory myFactory = new IndentCalculatorFactory(project, editor);
if (getPosition(editor, offset).matchesRule(
position -> position
.before()
.beforeOptional(Semicolon)
.beforeOptional(Whitespace)
.isAt(BlockClosingBrace))) {
return createIndentData(getBlockIndentType(project, language), BlockClosingBrace);
return myFactory.createIndentCalculator(getBlockIndentType(project, language), IndentCalculator.LINE_BEFORE);
}
else if (getPosition(editor, offset).matchesRule(
position -> position.before().isAt(ArrayOpeningBracket)
position -> position.before().isAt(ArrayOpeningBracket) || position.isAt(LeftParenthesis)
)) {
return createIndentData(CONTINUATION, ArrayOpeningBracket);
}
else if (getPosition(editor, offset).matchesRule(
position -> position.before().isAt(LeftParenthesis)
)) {
return createIndentData(CONTINUATION, LeftParenthesis);
return myFactory.createIndentCalculator(CONTINUATION, IndentCalculator.LINE_BEFORE);
}
else if (getPosition(editor, offset).matchesRule(
position -> position.before().isAt(BlockOpeningBrace)
)) {
return createIndentData(getIndentTypeInBlock(project, language), BlockOpeningBrace);
return myFactory.createIndentCalculator(getIndentTypeInBlock(project, language), position -> {
position.before().beforeOptional(Whitespace);
return getOffsetBeforeOptionalParentheses(position);
});
}
else if (getPosition(editor, offset).matchesRule(
position -> position.before().isAt(Colon) && position.isAfterOnSameLine(SwitchCase, SwitchDefault)
)) {
return createIndentData(NORMAL, SwitchCase);
}
else if (getPosition(editor, offset).matchesRule(
) || getPosition(editor, offset).matchesRule(
position -> position.before().isAt(ElseKeyword)
)) {
return createIndentData(NORMAL, ElseKeyword);
return myFactory.createIndentCalculator(NORMAL, IndentCalculator.LINE_BEFORE);
}
else if (getPosition(editor, offset).matchesRule(
position -> position.before().isAt(BlockComment)
)) {
return createIndentData(NONE, BlockComment);
return myFactory.createIndentCalculator(NONE, position -> position.findStartOf(BlockComment));
}
else if (getPosition(editor, offset).matchesRule(
position -> position.before().isAt(DocBlockEnd)
)) {
return createIndentData(NONE, DocBlockEnd);
return myFactory.createIndentCalculator(NONE, position -> position.findStartOf(DocBlockStart));
}
else {
SemanticEditorPosition position = getPosition(editor, offset);
@@ -132,8 +124,9 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI
if (position.isAt(IfKeyword) || position.isAt(ForKeyword)) {
SyntaxElement element = position.getCurrElement();
assert element != null;
final int controlKeywordOffset = position.getStartOffset();
Type indentType = getPosition(editor, offsetAfterParen).afterOptional(Whitespace).isAt(BlockOpeningBrace) ? NONE : NORMAL;
return createIndentData(indentType, element);
return myFactory.createIndentCalculator(indentType, baseLineOffset -> controlKeywordOffset);
}
}
}
@@ -142,6 +135,22 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI
}
return null;
}
private static int getOffsetBeforeOptionalParentheses(@NotNull SemanticEditorPosition position) {
position.beforeOptional(Whitespace);
if (position.isAt(RightParenthesis)) {
position.beforeParentheses(LeftParenthesis, RightParenthesis);
}
while (!position.isAtEnd()) {
if (position.isAt(Whitespace) && position.isAtMultiline()) {
return position.after().getStartOffset();
}
position.before();
}
return -1;
}
protected SemanticEditorPosition getPosition(@NotNull Editor editor, int offset) {
return new SemanticEditorPosition((EditorEx)editor, offset) {
@@ -154,90 +163,7 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI
@Nullable
protected abstract SyntaxElement mapType(@NotNull IElementType tokenType);
@Nullable
private String getIndentString(@NotNull Project project,
@NotNull Editor editor,
int offset,
@NotNull Pair<Type, SyntaxElement> indentData) {
Document document = editor.getDocument();
String baseIndent = getBaseIndent(editor, indentData.second, offset);
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
if (file != null) {
CommonCodeStyleSettings.IndentOptions options = CodeStyleSettingsManager.getSettings(project).getIndentOptionsByFile(file);
return
baseIndent + new IndentInfo(0, indentTypeToSize(indentData.first, options), 0, false).generateNewWhiteSpace(options);
}
return null;
}
private static int indentTypeToSize(@NotNull Type indentType, @NotNull CommonCodeStyleSettings.IndentOptions options) {
if (indentType == NORMAL) {
return options.INDENT_SIZE;
}
else if (indentType == CONTINUATION) {
return options.CONTINUATION_INDENT_SIZE;
}
return 0;
}
@NotNull
private String getBaseIndent(@NotNull Editor editor, @NotNull SyntaxElement afterElement, int offset) {
CharSequence docChars = editor.getDocument().getCharsSequence();
if (offset > 0) {
int indentLineOffset = getOffsetInBaseIndentLine(editor, docChars, afterElement, offset - 1);
if (indentLineOffset > 0) {
int indentStart = CharArrayUtil.shiftBackwardUntil(docChars, indentLineOffset, "\n") + 1;
if (indentStart >= 0) {
int indentEnd = CharArrayUtil.shiftForward(docChars, indentStart, " \t");
if (indentEnd > indentStart) {
return docChars.subSequence(indentStart, indentEnd).toString();
}
}
}
}
return "";
}
private int getOffsetInBaseIndentLine(@NotNull Editor editor,
@NotNull CharSequence docChars,
@NotNull SyntaxElement afterElement,
int offset) {
if (BlockOpeningBrace.equals(afterElement) && !isOnSeparateLine(editor, afterElement, offset)) {
return findStatementStart(editor, afterElement, offset);
}
else if (IfKeyword.equals(afterElement) || ForKeyword.equals(afterElement)) {
return findStatementStart(editor, null, offset);
}
else if (BlockComment.equals(afterElement)) {
return findStartOfBlockComment(editor, offset);
}
else if (DocBlockEnd.equals(afterElement)) {
return findStartOfDocComment(editor, offset);
}
return CharArrayUtil.shiftBackward(docChars, offset, " \t\n\r");
}
private int findStartOfDocComment(Editor editor, int offset) {
SemanticEditorPosition position = getPosition(editor, offset);
while (!position.isAtEnd()) {
if (position.isAt(DocBlockStart)) return position.getStartOffset();
position.before();
}
return -1;
}
private int findStartOfBlockComment(@NotNull Editor editor, int offset) {
SemanticEditorPosition position = getPosition(editor, offset);
while (!position.isAtEnd()) {
if (position.isAt(BlockComment)) return position.getStartOffset();
position.before();
}
return -1;
}
private boolean isOnSeparateLine(@NotNull Editor editor, @NotNull SyntaxElement element, int offset) {
SemanticEditorPosition position = getPosition(editor, offset);
@@ -250,24 +176,6 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI
}
private int findStatementStart(@NotNull Editor editor, @Nullable SyntaxElement afterElement, int offset) {
SemanticEditorPosition position = getPosition(editor, offset);
position.beforeOptional(Whitespace);
if (afterElement != null) {
position.beforeOptional(afterElement).beforeOptional(Whitespace);
}
if (position.isAt(RightParenthesis)) {
position.beforeParentheses(LeftParenthesis, RightParenthesis);
}
while (!position.isAtEnd()) {
if (position.isAt(Whitespace) && position.isAtMultiline()) {
return position.after().getStartOffset();
}
position.before();
}
return -1;
}
@Nullable
private static Type getIndentTypeInBlock(@NotNull Project project, @Nullable Language language) {
if (language != null) {
@@ -290,9 +198,24 @@ public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineI
return null;
}
@Nullable
protected static Pair<Type,SyntaxElement> createIndentData(@Nullable Type type, @NotNull SyntaxElement element) {
return type != null ? Pair.create(type, element) : null;
public static class IndentCalculatorFactory {
private Project myProject;
private Editor myEditor;
public IndentCalculatorFactory(Project project, Editor editor) {
myProject = project;
myEditor = editor;
}
@Nullable
public IndentCalculator createIndentCalculator(@Nullable Type indentType, @Nullable BaseLineOffsetCalculator baseLineOffsetCalculator) {
return indentType != null ?
new IndentCalculator(myProject, myEditor,
baseLineOffsetCalculator != null ? baseLineOffsetCalculator : IndentCalculator.LINE_BEFORE, indentType)
: null;
}
}
@Override