diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EnterHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EnterHandler.java index 4383ad7bcb5e..2de4acb6d6fe 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EnterHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EnterHandler.java @@ -484,7 +484,7 @@ public class EnterHandler extends BaseEnterHandler { private int adjustLineIndent(CharSequence docChars) { int indentStart = CharArrayUtil.shiftBackwardUntil(docChars, myOffset - 1, "\n") + 1; int indentEnd = CharArrayUtil.shiftForward(docChars, indentStart, " \t"); - String newIndent = CodeStyleFacade.getInstance(getProject()).getLineIndent(myDocument, myOffset); + String newIndent = CodeStyleFacade.getInstance(getProject()).getLineIndent(myEditor, myDocument, myOffset); if (newIndent == null) return myOffset; int delta = newIndent.length() - (indentEnd - indentStart); myDocument.replaceString(indentStart, indentEnd, newIndent); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleFacadeImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleFacadeImpl.java index 6aadf95a1ae2..8aa339af522a 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleFacadeImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleFacadeImpl.java @@ -22,10 +22,12 @@ package com.intellij.psi.impl.source.codeStyle; import com.intellij.codeStyle.CodeStyleFacade; import com.intellij.lang.Language; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import org.jetbrains.annotations.NotNull; @@ -49,12 +51,22 @@ public class CodeStyleFacadeImpl extends CodeStyleFacade { @Override @Nullable + @Deprecated public String getLineIndent(@NotNull final Document document, int offset) { if (myProject == null) return null; PsiDocumentManager.getInstance(myProject).commitDocument(document); return CodeStyleManager.getInstance(myProject).getLineIndent(document, offset); } + @Override + public String getLineIndent(@NotNull Editor editor, @NotNull Document document, int offset) { + if (myProject == null) return null; + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); + PsiFile file = documentManager.getPsiFile(document); + LineIndentProvider lineIndentProvider = LineIndentProviderEP.findLineIndentProvider(file); + return lineIndentProvider != null ? lineIndentProvider.getLineIndent(myProject, editor, document, offset) : null; + } + @Override public String getLineSeparator() { return CodeStyleSettingsManager.getSettings(myProject).getLineSeparator(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/FormatterBasedLineIndentProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/FormatterBasedLineIndentProvider.java new file mode 100644 index 000000000000..cb4ab7469a80 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/FormatterBasedLineIndentProvider.java @@ -0,0 +1,42 @@ +/* + * 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.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.CodeStyleManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Formatter-based line indent provider which calculates indent using formatting model. + */ +public class FormatterBasedLineIndentProvider implements LineIndentProvider { + @Nullable + @Override + public String getLineIndent(@NotNull Project project, @NotNull Editor editor, @NotNull Document document, int offset) { + PsiDocumentManager.getInstance(project).commitDocument(document); + return CodeStyleManager.getInstance(project).getLineIndent(document, offset); + } + + @Override + public boolean isSuitableFor(@Nullable PsiFile file) { + return true; + } +} 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 new file mode 100644 index 000000000000..98d9a2e818e6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/JavaLikeLangLineIndentProvider.java @@ -0,0 +1,91 @@ +/* + * 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.formatting.Indent; +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.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; + +/** + * A base class Java-like language line indent provider. If JavaLikeLangLineIndentProvider is unable to calculate + * the indentation, it forwards the request to FormatterBasedLineIndentProvider. + */ +public abstract class JavaLikeLangLineIndentProvider extends FormatterBasedLineIndentProvider { + @Nullable + @Override + public String getLineIndent(@NotNull Project project, @NotNull Editor editor, @NotNull Document document, int offset) { + Indent.Type indent = getIndent(editor, document, offset); + if (indent == Indent.Type.NONE) return null; + return super.getLineIndent(project, editor, document, offset); + } + + @Override + public final boolean isSuitableFor(@Nullable PsiFile file) { + if (file != null) { + return isSuitableForLanguage(file.getLanguage()); + } + return false; + } + + protected abstract boolean isSuitableForLanguage(@NotNull Language language); + + @Nullable + protected Indent.Type getIndent(@NotNull Editor editor, @NotNull Document document, int offset) { + CharSequence docChars = document.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; + } + } + } + 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()); + } + } + return false; + } + + protected abstract boolean isSemicolon(@NotNull IElementType tokenType); + + protected abstract boolean isBlockClosingBrace(@NotNull IElementType tokenType); +} diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/LineIndentProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/LineIndentProvider.java new file mode 100644 index 000000000000..0ca97d7d40f9 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/LineIndentProvider.java @@ -0,0 +1,45 @@ +/* + * 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.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * An interface for indentation calculation. Used in editor actions like Enter handling. + */ +public interface LineIndentProvider { + + /** + * Calculates the indent that should be used for the line at specified offset in the specified + * document. + * + * @param project The current project. + * @param editor The current editor. + * @param document The document for which the indent should be calculated. + * @param offset The caret offset in the editor. + * @return The indent string (possibly consisting of tabs and/or white spaces), or null if + * LineIndentProvider can't calculate the indent (in this case no indent adjustment will be made). + */ + @Nullable + String getLineIndent(@NotNull Project project, @NotNull Editor editor, @NotNull Document document, int offset); + + boolean isSuitableFor(@Nullable PsiFile file); +} diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/LineIndentProviderEP.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/LineIndentProviderEP.java new file mode 100644 index 000000000000..646b98526753 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/LineIndentProviderEP.java @@ -0,0 +1,38 @@ +/* + * 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.extensions.ExtensionPointName; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.Nullable; + +/** + * Line indent provider extension point + */ +public class LineIndentProviderEP { + public final static ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.lineIndentProvider"); + + @Nullable + public static LineIndentProvider findLineIndentProvider(@Nullable PsiFile psiFile) { + LineIndentProvider foundProvider = null; + for (LineIndentProvider provider : EP_NAME.getExtensions()) { + if (foundProvider == null || provider.isSuitableFor(psiFile) && foundProvider.getClass().isInstance(provider)) { + foundProvider = provider; + } + } + return foundProvider; + } +} diff --git a/platform/platform-api/src/com/intellij/codeStyle/CodeStyleFacade.java b/platform/platform-api/src/com/intellij/codeStyle/CodeStyleFacade.java index c3c4c36ac0ad..dd9680f5bfe9 100644 --- a/platform/platform-api/src/com/intellij/codeStyle/CodeStyleFacade.java +++ b/platform/platform-api/src/com/intellij/codeStyle/CodeStyleFacade.java @@ -22,6 +22,7 @@ package com.intellij.codeStyle; import com.intellij.lang.Language; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; @@ -46,9 +47,26 @@ public abstract class CodeStyleFacade { * @param offset the caret offset in the editor. * @return the indent string (containing of tabs and/or white spaces), or null if it * was not possible to calculate the indent. + * @deprecated Use {@link #getLineIndent(Editor, Document, int)} instead. */ @Nullable + @Deprecated public abstract String getLineIndent(@NotNull Document document, int offset); + + /** + * Calculates the indent that should be used for the line at specified offset in the specified + * document. + * + * @param editor the editor for which the indent must be returned. + * @param document the document for which the indent should be calculated. + * @param offset the caret offset in the editor. + * @return the indent string (containing of tabs and/or white spaces), or null if it + * was not possible to calculate the indent. + */ + public String getLineIndent(@NotNull Editor editor, @NotNull Document document, int offset) { + //noinspection deprecation + return getLineIndent(document, offset); + } public abstract int getIndentSize(FileType fileType); diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index a8a20cbb94c3..b1d266303395 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -289,6 +289,7 @@ + diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index eb839b34b1e2..d447bc0a7764 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -953,6 +953,7 @@ +