From c13453de88c1ea559679b4f3d58fbf7e09def2e1 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Thu, 2 Jun 2016 16:19:36 +0300 Subject: [PATCH] JavaLikeLangIndentProvider refactoring --- .../codeStyle/SemanticEditorPosition.java | 13 ++ .../lineIndent/IndentCalculator.java | 107 ++++++++++ .../JavaLikeLangLineIndentProvider.java | 185 +++++------------- 3 files changed, 174 insertions(+), 131 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java 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 index 647a3bf85868..319ed818c5bd 100644 --- 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 @@ -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; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java new file mode 100644 index 000000000000..b088aa90f5cf --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java @@ -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); + } +} diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java index 50207a2bed98..e3636db5f469 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java @@ -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 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 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 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 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