mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
move python formatter extensions to python.psi.impl module
GitOrigin-RevId: 452346cbd12e9c21c636dc8dbcab9a29813bc67b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
77da7682df
commit
68f080743c
@@ -9,5 +9,7 @@
|
||||
serviceImplementation="com.intellij.util.download.impl.DownloadableFileServiceImpl"/>
|
||||
<applicationService serviceInterface="com.intellij.notification.NotificationGroupManager"
|
||||
serviceImplementation="com.intellij.notification.impl.NotificationGroupManagerImpl"/>
|
||||
<applicationService serviceImplementation="com.intellij.ide.GeneralSettings" preload="notHeadless"/>
|
||||
<applicationService serviceImplementation="com.intellij.openapi.editor.ex.EditorSettingsExternalizable"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide;
|
||||
|
||||
import com.intellij.ide.ui.UINumericRange;
|
||||
@@ -8,6 +8,7 @@ import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
@@ -42,7 +43,7 @@ public final class GeneralSettings implements PersistentStateComponent<GeneralSe
|
||||
|
||||
private static final String SHOW_TIPS_ON_STARTUP_DEFAULT_VALUE_PROPERTY = "ide.show.tips.on.startup.default.value";
|
||||
|
||||
private String myBrowserPath = BrowserUtil.getDefaultAlternativeBrowserPath();
|
||||
private String myBrowserPath = getDefaultAlternativeBrowserPath();
|
||||
private boolean myShowTipsOnStartup = Boolean.parseBoolean(System.getProperty(SHOW_TIPS_ON_STARTUP_DEFAULT_VALUE_PROPERTY, "true"));
|
||||
private boolean myReopenLastProject = true;
|
||||
private boolean mySupportScreenReaders = ObjectUtils.chooseNotNull(SUPPORT_SCREEN_READERS_OVERRIDDEN, false);
|
||||
@@ -281,4 +282,19 @@ public final class GeneralSettings implements PersistentStateComponent<GeneralSe
|
||||
public void setDefaultProjectDirectory(@SystemDependent String defaultProjectDirectory) {
|
||||
myDefaultProjectDirectory = defaultProjectDirectory;
|
||||
}
|
||||
|
||||
private static @NotNull String getDefaultAlternativeBrowserPath() {
|
||||
if (SystemInfo.isWindows) {
|
||||
return "C:\\Program Files\\Internet Explorer\\IExplore.exe";
|
||||
}
|
||||
else if (SystemInfo.isMac) {
|
||||
return "open";
|
||||
}
|
||||
else if (SystemInfo.isUnix) {
|
||||
return "/usr/bin/firefox";
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,21 +126,6 @@ public final class BrowserUtil {
|
||||
return command;
|
||||
}
|
||||
|
||||
public static @NotNull String getDefaultAlternativeBrowserPath() {
|
||||
if (SystemInfo.isWindows) {
|
||||
return "C:\\Program Files\\Internet Explorer\\IExplore.exe";
|
||||
}
|
||||
else if (SystemInfo.isMac) {
|
||||
return "open";
|
||||
}
|
||||
else if (SystemInfo.isUnix) {
|
||||
return "/usr/bin/firefox";
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold desc="Deprecated stuff.">
|
||||
/** @deprecated the result is a pain to deal with; please use {@link #getOpenBrowserCommand(String, String, List, boolean)} instead */
|
||||
@Deprecated(forRemoval = true)
|
||||
|
||||
@@ -160,8 +160,6 @@
|
||||
<virtualFileSystem implementationClass="com.intellij.openapi.vfs.impl.jar.JarFileSystemImpl" key="jar" physical="true"/>
|
||||
<virtualFileSystem implementationClass="com.intellij.openapi.vfs.ex.temp.TempFileSystem" key="temp" physical="true"/>
|
||||
|
||||
<applicationService serviceImplementation="com.intellij.ide.GeneralSettings" preload="notHeadless"/>
|
||||
|
||||
<applicationService serviceImplementation="com.intellij.ui.jcef.JBCefStartup" preload="notHeadless" os="mac"/>
|
||||
<cachesInvalidator implementation="com.intellij.ui.jcef.JBCefAppCacheInvalidator" order="last" />
|
||||
|
||||
@@ -638,7 +636,6 @@
|
||||
<applicationService serviceInterface="com.intellij.openapi.actionSystem.AbbreviationManager"
|
||||
serviceImplementation="com.intellij.openapi.actionSystem.impl.AbbreviationManagerImpl"/>
|
||||
|
||||
<applicationService serviceImplementation="com.intellij.openapi.editor.ex.EditorSettingsExternalizable"/>
|
||||
<applicationService serviceImplementation="com.intellij.openapi.editor.ex.EditorSettingsExternalizable$OsSpecificState"/>
|
||||
<applicationConfigurable
|
||||
groupId="editor"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.formatting.WrapType;
|
||||
@@ -7,15 +7,16 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.fixture.PythonCommonTestCase;
|
||||
import com.jetbrains.python.formatter.PyCodeStyleSettings;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyElementGenerator;
|
||||
import com.jetbrains.python.psi.PyStatement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class PythonCommonFormatterTest extends PythonCommonTestCase {
|
||||
protected static final LanguageLevel LANGUAGE_LEVEL = LanguageLevel.getLatest();
|
||||
|
||||
public class PyFormatterTest extends PyTestCase {
|
||||
@NotNull
|
||||
private PyCodeStyleSettings getPythonCodeStyleSettings() {
|
||||
return getCodeStyleSettings().getCustomSettings(PyCodeStyleSettings.class);
|
||||
@@ -522,7 +523,7 @@ public class PyFormatterTest extends PyTestCase {
|
||||
public void testAlignmentOfClosingBraceInDictLiteralWhenNoHangingIndent() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testNoAlignmentClosingBraceInDictLiteralWhenOpeningBraceIsForcedOnNewLine() {
|
||||
getPythonCodeStyleSettings().DICT_NEW_LINE_AFTER_LEFT_BRACE = true;
|
||||
doTest();
|
||||
@@ -671,7 +672,7 @@ public class PyFormatterTest extends PyTestCase {
|
||||
getPythonCodeStyleSettings().DICT_NEW_LINE_BEFORE_RIGHT_BRACE = true;
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testForceNewLineAfterLeftParenInMethodParameters() { // PY-33060
|
||||
getCommonCodeStyleSettings().METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = true;
|
||||
doTest();
|
||||
@@ -714,7 +715,7 @@ public class PyFormatterTest extends PyTestCase {
|
||||
public void testHangingIndentDetectionIgnoresComments() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
// PY-15530
|
||||
public void testAlignmentInArgumentListWhereFirstArgumentIsEmptyCall() {
|
||||
doTest();
|
||||
@@ -814,7 +815,7 @@ public class PyFormatterTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-10182
|
||||
// PY-10182
|
||||
public void testHangClosingParenthesisInFunctionDefinition() {
|
||||
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
|
||||
doTest();
|
||||
@@ -831,7 +832,7 @@ public class PyFormatterTest extends PyTestCase {
|
||||
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = false;
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
// PY-15874
|
||||
public void testHangClosingOnComprehensionsAndGeneratorExpressions() {
|
||||
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
|
||||
@@ -853,7 +854,7 @@ public class PyFormatterTest extends PyTestCase {
|
||||
getCodeStyleSettings().setRightMargin(PythonLanguage.INSTANCE, 35);
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
// PY-20633
|
||||
public void testFromImportParenthesesPlacementHangClosingParenthesis() {
|
||||
getPythonCodeStyleSettings().FROM_IMPORT_NEW_LINE_AFTER_LEFT_PARENTHESIS = true;
|
||||
@@ -1157,17 +1158,17 @@ public class PyFormatterTest extends PyTestCase {
|
||||
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
// PY-48009
|
||||
public void testItemAlignmentInMappingPatterns() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
// PY-48009
|
||||
public void testItemIndentInMappingPatterns() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
// PY-48009
|
||||
public void testHangingClosingBracketInMappingPatterns() {
|
||||
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
|
||||
@@ -26,6 +26,8 @@ interface PythonCommonCodeInsightTestFixture {
|
||||
|
||||
val testRootDisposable: Disposable
|
||||
|
||||
val caretOffset: Int
|
||||
|
||||
fun setUp() {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python.fixture;
|
||||
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkModificator;
|
||||
@@ -8,6 +9,9 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.documentation.PyDocumentationSettings;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringFormat;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
@@ -90,6 +94,21 @@ public abstract class PythonCommonTestCase extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected CommonCodeStyleSettings getCommonCodeStyleSettings() {
|
||||
return getCodeStyleSettings().getCommonSettings(PythonLanguage.getInstance());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected CodeStyleSettings getCodeStyleSettings() {
|
||||
return CodeStyle.getSettings(myFixture.getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected CommonCodeStyleSettings.IndentOptions getIndentOptions() {
|
||||
return getCommonCodeStyleSettings().getIndentOptions();
|
||||
}
|
||||
|
||||
protected void assertProjectFilesNotParsed(@NotNull PsiFile currentFile) {
|
||||
assertRootNotParsed(currentFile, myFixture.getTempDirRoot(), null);
|
||||
}
|
||||
|
||||
@@ -18,5 +18,7 @@
|
||||
<orderEntry type="library" scope="TEST" name="Guava" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="StreamEx" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.analysis.impl" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.codeStyle" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.editor" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -51,6 +51,14 @@
|
||||
implementationClass="com.jetbrains.python.psi.impl.references.PyKeywordPatternManipulator"/>
|
||||
<lang.parserDefinition language="Python" implementationClass="com.jetbrains.python.PythonParserDefinition"/>
|
||||
|
||||
<lang.formatter language="Python" implementationClass="com.jetbrains.python.formatter.PythonFormattingModelBuilder"/>
|
||||
<preFormatProcessor implementation="com.jetbrains.python.formatter.PyPreFormatProcessor"/>
|
||||
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyTrailingBlankLinesPostFormatProcessor"/>
|
||||
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyFromImportPostFormatProcessor"/>
|
||||
<lang.whiteSpaceFormattingStrategy language="Python"
|
||||
implementationClass="com.jetbrains.python.formatter.PyWhiteSpaceFormattingStrategy"/>
|
||||
<lang.lineWrapStrategy language="Python" implementationClass="com.jetbrains.python.formatter.PyLineWrapPositionStrategy"/>
|
||||
|
||||
<!-- PyFunctionTypeAnnotation -->
|
||||
<lang.parserDefinition language="PyFunctionTypeComment"
|
||||
implementationClass="com.jetbrains.python.codeInsight.functionTypeComments.PyFunctionTypeAnnotationParserDefinition"/>
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.jetbrains.python.editor;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PyWhiteSpaceUtil {
|
||||
public static boolean needInsertBackslash(PsiFile file, int offset, boolean autoWrapInProgress) {
|
||||
if (offset > 0) {
|
||||
final PsiElement beforeCaret = file.findElementAt(offset - 1);
|
||||
if (beforeCaret instanceof PsiWhiteSpace && beforeCaret.getText().indexOf('\\') >= 0) {
|
||||
// we've got a backslash at EOL already, don't need another one
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PsiElement atCaret = file.findElementAt(offset);
|
||||
if (atCaret == null) {
|
||||
return false;
|
||||
}
|
||||
ASTNode nodeAtCaret = atCaret.getNode();
|
||||
return needInsertBackslash(nodeAtCaret, autoWrapInProgress);
|
||||
}
|
||||
|
||||
public static boolean needInsertBackslash(ASTNode nodeAtCaret, boolean autoWrapInProgress) {
|
||||
if (PsiTreeUtil.getParentOfType(nodeAtCaret.getPsi(), PyFStringFragment.class) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiElement statementBefore = findStatementBeforeCaret(nodeAtCaret);
|
||||
PsiElement statementAfter = findStatementAfterCaret(nodeAtCaret);
|
||||
if (statementBefore != statementAfter) { // Enter pressed at statement break
|
||||
return false;
|
||||
}
|
||||
if (statementBefore == null) { // empty file
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PsiTreeUtil.hasErrorElements(statementBefore)) {
|
||||
if (!autoWrapInProgress) {
|
||||
// code is already bad, don't mess it up even further
|
||||
return false;
|
||||
}
|
||||
// if we're in middle of typing, it's expected that we will have error elements
|
||||
}
|
||||
|
||||
final int offset = nodeAtCaret.getTextRange().getStartOffset();
|
||||
if (inFromImportParentheses(statementBefore, offset) || inWithItemsParentheses(statementBefore, offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiElement wrappableBefore = findWrappable(nodeAtCaret, true);
|
||||
PsiElement wrappableAfter = findWrappable(nodeAtCaret, false);
|
||||
if (!(wrappableBefore instanceof PsiComment)) {
|
||||
while (wrappableBefore != null) {
|
||||
PsiElement next = PsiTreeUtil.getParentOfType(wrappableBefore, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
wrappableBefore = next;
|
||||
}
|
||||
}
|
||||
if (!(wrappableAfter instanceof PsiComment)) {
|
||||
while (wrappableAfter != null) {
|
||||
PsiElement next = PsiTreeUtil.getParentOfType(wrappableAfter, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
wrappableAfter = next;
|
||||
}
|
||||
}
|
||||
if (wrappableBefore instanceof PsiComment || wrappableAfter instanceof PsiComment) {
|
||||
return false;
|
||||
}
|
||||
if (wrappableAfter == null) {
|
||||
return !(wrappableBefore instanceof PyDecoratorList);
|
||||
}
|
||||
return wrappableBefore != wrappableAfter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findWrappable(ASTNode nodeAtCaret, boolean before) {
|
||||
PsiElement wrappable = before
|
||||
? findBeforeCaret(nodeAtCaret, PyEditorHandlerConfig.WRAPPABLE_CLASSES)
|
||||
: findAfterCaret(nodeAtCaret, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
|
||||
if (wrappable == null) {
|
||||
PsiElement emptyTuple = before
|
||||
? findBeforeCaret(nodeAtCaret, PyTupleExpression.class)
|
||||
: findAfterCaret(nodeAtCaret, PyTupleExpression.class);
|
||||
if (emptyTuple != null && emptyTuple.getNode().getFirstChildNode().getElementType() == PyTokenTypes.LPAR) {
|
||||
wrappable = emptyTuple;
|
||||
}
|
||||
}
|
||||
return wrappable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findStatementBeforeCaret(ASTNode node) {
|
||||
return findBeforeCaret(node, PyStatement.class, PyStatementPart.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findStatementAfterCaret(ASTNode node) {
|
||||
return findAfterCaret(node, PyStatement.class, PyStatementPart.class);
|
||||
}
|
||||
|
||||
private static PsiElement findBeforeCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
|
||||
while (atCaret != null) {
|
||||
atCaret = TreeUtil.prevLeaf(atCaret);
|
||||
if (atCaret != null && atCaret.getElementType() != TokenType.WHITE_SPACE) {
|
||||
return getNonStrictParentOfType(atCaret.getPsi(), classes);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiElement findAfterCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
|
||||
while (atCaret != null) {
|
||||
if (atCaret.getElementType() != TokenType.WHITE_SPACE) {
|
||||
return getNonStrictParentOfType(atCaret.getPsi(), classes);
|
||||
}
|
||||
atCaret = TreeUtil.nextLeaf(atCaret);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T extends PsiElement> T getNonStrictParentOfType(@NotNull PsiElement element, Class<? extends T> @NotNull ... classes) {
|
||||
PsiElement run = element;
|
||||
while (run != null) {
|
||||
for (Class<? extends T> aClass : classes) {
|
||||
if (aClass.isInstance(run)) return (T)run;
|
||||
}
|
||||
if (run instanceof PsiFile || run instanceof PyStatementList) break;
|
||||
run = run.getParent();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean inFromImportParentheses(PsiElement statement, int offset) {
|
||||
if (!(statement instanceof PyFromImportStatement)) {
|
||||
return false;
|
||||
}
|
||||
PyFromImportStatement fromImportStatement = (PyFromImportStatement)statement;
|
||||
PsiElement leftParen = fromImportStatement.getLeftParen();
|
||||
if (leftParen != null && offset >= leftParen.getTextRange().getEndOffset()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean inWithItemsParentheses(@NotNull PsiElement statement, int offset) {
|
||||
if (!(statement instanceof PyWithStatement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiElement leftParen = PyPsiUtils.getFirstChildOfType(statement, PyTokenTypes.LPAR);
|
||||
return leftParen != null && offset >= leftParen.getTextRange().getEndOffset();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import com.intellij.openapi.util.text.Strings;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.formatter.StaticSymbolWhiteSpaceDefinitionStrategy;
|
||||
import com.jetbrains.python.editor.PythonEnterHandler;
|
||||
import com.jetbrains.python.editor.PyWhiteSpaceUtil;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntIterator;
|
||||
@@ -25,7 +25,7 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
|
||||
CodeStyleSettings codeStyleSettings) {
|
||||
CharSequence whiteSpace = super.adjustWhiteSpaceIfNecessary(whiteSpaceText, startElement, startOffset, endOffset, codeStyleSettings);
|
||||
if (whiteSpace.length() > 0 && whiteSpace.charAt(0) == '\n' && !Strings.contains(whiteSpace, 0, whiteSpace.length(), '\\') &&
|
||||
PythonEnterHandler.needInsertBackslash(startElement.getContainingFile(), startOffset, false)) {
|
||||
PyWhiteSpaceUtil.needInsertBackslash(startElement.getContainingFile(), startOffset, false)) {
|
||||
return addBackslashPrefix(whiteSpace, codeStyleSettings);
|
||||
}
|
||||
return whiteSpace;
|
||||
@@ -60,7 +60,7 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
|
||||
Int2IntMap initialBackSlashes = countBackSlashes(text, startOffset, endOffset);
|
||||
if (initialBackSlashes.isEmpty()) {
|
||||
if (nodeAfter != null && whiteSpaceText.length() > 0 && whiteSpaceText.charAt(0) == '\n' &&
|
||||
PythonEnterHandler.needInsertBackslash(nodeAfter, false)) {
|
||||
PyWhiteSpaceUtil.needInsertBackslash(nodeAfter, false)) {
|
||||
return addBackslashPrefix(whiteSpaceText, codeStyleSettings);
|
||||
}
|
||||
return whiteSpaceText;
|
||||
@@ -46,13 +46,6 @@
|
||||
<lang.commenter language="Python" implementationClass="com.jetbrains.python.PythonCommenter"/>
|
||||
<lang.foldingBuilder language="Python" implementationClass="com.jetbrains.python.PythonFoldingBuilder"/>
|
||||
<lang.findUsagesProvider language="Python" implementationClass="com.jetbrains.python.findUsages.PythonFindUsagesProvider"/>
|
||||
<lang.formatter language="Python" implementationClass="com.jetbrains.python.formatter.PythonFormattingModelBuilder"/>
|
||||
<preFormatProcessor implementation="com.jetbrains.python.formatter.PyPreFormatProcessor"/>
|
||||
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyTrailingBlankLinesPostFormatProcessor"/>
|
||||
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyFromImportPostFormatProcessor"/>
|
||||
<lang.whiteSpaceFormattingStrategy language="Python"
|
||||
implementationClass="com.jetbrains.python.formatter.PyWhiteSpaceFormattingStrategy"/>
|
||||
<lang.lineWrapStrategy language="Python" implementationClass="com.jetbrains.python.formatter.PyLineWrapPositionStrategy"/>
|
||||
<lang.namesValidator language="Python" implementationClass="com.jetbrains.python.refactoring.rename.PythonNamesValidator"/>
|
||||
<lang.surroundDescriptor language="Python"
|
||||
implementationClass="com.jetbrains.python.refactoring.surround.PyStatementSurroundDescriptor"/>
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.LineTokenizer;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -25,9 +24,7 @@ import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.documentation.docstrings.*;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
@@ -145,85 +142,13 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
Document doc) {
|
||||
boolean autoWrapInProgress = DataManager.getInstance().loadFromDataContext(dataContext,
|
||||
AutoHardWrapHandler.AUTO_WRAP_LINE_IN_PROGRESS_KEY) != null;
|
||||
if (needInsertBackslash(file, offset, autoWrapInProgress)) {
|
||||
if (PyWhiteSpaceUtil.needInsertBackslash(file, offset, autoWrapInProgress)) {
|
||||
doc.insertString(offset, "\\");
|
||||
caretOffset.set(caretOffset.get() + 1);
|
||||
}
|
||||
return Result.Continue;
|
||||
}
|
||||
|
||||
public static boolean needInsertBackslash(PsiFile file, int offset, boolean autoWrapInProgress) {
|
||||
if (offset > 0) {
|
||||
final PsiElement beforeCaret = file.findElementAt(offset - 1);
|
||||
if (beforeCaret instanceof PsiWhiteSpace && beforeCaret.getText().indexOf('\\') >= 0) {
|
||||
// we've got a backslash at EOL already, don't need another one
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PsiElement atCaret = file.findElementAt(offset);
|
||||
if (atCaret == null) {
|
||||
return false;
|
||||
}
|
||||
ASTNode nodeAtCaret = atCaret.getNode();
|
||||
return needInsertBackslash(nodeAtCaret, autoWrapInProgress);
|
||||
}
|
||||
|
||||
public static boolean needInsertBackslash(ASTNode nodeAtCaret, boolean autoWrapInProgress) {
|
||||
if (PsiTreeUtil.getParentOfType(nodeAtCaret.getPsi(), PyFStringFragment.class) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiElement statementBefore = findStatementBeforeCaret(nodeAtCaret);
|
||||
PsiElement statementAfter = findStatementAfterCaret(nodeAtCaret);
|
||||
if (statementBefore != statementAfter) { // Enter pressed at statement break
|
||||
return false;
|
||||
}
|
||||
if (statementBefore == null) { // empty file
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PsiTreeUtil.hasErrorElements(statementBefore)) {
|
||||
if (!autoWrapInProgress) {
|
||||
// code is already bad, don't mess it up even further
|
||||
return false;
|
||||
}
|
||||
// if we're in middle of typing, it's expected that we will have error elements
|
||||
}
|
||||
|
||||
final int offset = nodeAtCaret.getTextRange().getStartOffset();
|
||||
if (inFromImportParentheses(statementBefore, offset) || inWithItemsParentheses(statementBefore, offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiElement wrappableBefore = findWrappable(nodeAtCaret, true);
|
||||
PsiElement wrappableAfter = findWrappable(nodeAtCaret, false);
|
||||
if (!(wrappableBefore instanceof PsiComment)) {
|
||||
while (wrappableBefore != null) {
|
||||
PsiElement next = PsiTreeUtil.getParentOfType(wrappableBefore, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
wrappableBefore = next;
|
||||
}
|
||||
}
|
||||
if (!(wrappableAfter instanceof PsiComment)) {
|
||||
while (wrappableAfter != null) {
|
||||
PsiElement next = PsiTreeUtil.getParentOfType(wrappableAfter, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
wrappableAfter = next;
|
||||
}
|
||||
}
|
||||
if (wrappableBefore instanceof PsiComment || wrappableAfter instanceof PsiComment) {
|
||||
return false;
|
||||
}
|
||||
if (wrappableAfter == null) {
|
||||
return !(wrappableBefore instanceof PyDecoratorList);
|
||||
}
|
||||
return wrappableBefore != wrappableAfter;
|
||||
}
|
||||
|
||||
private static void insertDocStringStub(Editor editor, PsiElement element, DocstringState state) {
|
||||
PyDocStringOwner docOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class);
|
||||
if (docOwner != null) {
|
||||
@@ -244,87 +169,6 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findWrappable(ASTNode nodeAtCaret, boolean before) {
|
||||
PsiElement wrappable = before
|
||||
? findBeforeCaret(nodeAtCaret, PyEditorHandlerConfig.WRAPPABLE_CLASSES)
|
||||
: findAfterCaret(nodeAtCaret, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
|
||||
if (wrappable == null) {
|
||||
PsiElement emptyTuple = before
|
||||
? findBeforeCaret(nodeAtCaret, PyTupleExpression.class)
|
||||
: findAfterCaret(nodeAtCaret, PyTupleExpression.class);
|
||||
if (emptyTuple != null && emptyTuple.getNode().getFirstChildNode().getElementType() == PyTokenTypes.LPAR) {
|
||||
wrappable = emptyTuple;
|
||||
}
|
||||
}
|
||||
return wrappable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findStatementBeforeCaret(ASTNode node) {
|
||||
return findBeforeCaret(node, PyStatement.class, PyStatementPart.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findStatementAfterCaret(ASTNode node) {
|
||||
return findAfterCaret(node, PyStatement.class, PyStatementPart.class);
|
||||
}
|
||||
|
||||
private static PsiElement findBeforeCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
|
||||
while (atCaret != null) {
|
||||
atCaret = TreeUtil.prevLeaf(atCaret);
|
||||
if (atCaret != null && atCaret.getElementType() != TokenType.WHITE_SPACE) {
|
||||
return getNonStrictParentOfType(atCaret.getPsi(), classes);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiElement findAfterCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
|
||||
while (atCaret != null) {
|
||||
if (atCaret.getElementType() != TokenType.WHITE_SPACE) {
|
||||
return getNonStrictParentOfType(atCaret.getPsi(), classes);
|
||||
}
|
||||
atCaret = TreeUtil.nextLeaf(atCaret);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T extends PsiElement> T getNonStrictParentOfType(@NotNull PsiElement element, Class<? extends T> @NotNull ... classes) {
|
||||
PsiElement run = element;
|
||||
while (run != null) {
|
||||
for (Class<? extends T> aClass : classes) {
|
||||
if (aClass.isInstance(run)) return (T)run;
|
||||
}
|
||||
if (run instanceof PsiFile || run instanceof PyStatementList) break;
|
||||
run = run.getParent();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean inFromImportParentheses(PsiElement statement, int offset) {
|
||||
if (!(statement instanceof PyFromImportStatement)) {
|
||||
return false;
|
||||
}
|
||||
PyFromImportStatement fromImportStatement = (PyFromImportStatement)statement;
|
||||
PsiElement leftParen = fromImportStatement.getLeftParen();
|
||||
if (leftParen != null && offset >= leftParen.getTextRange().getEndOffset()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean inWithItemsParentheses(@NotNull PsiElement statement, int offset) {
|
||||
if (!(statement instanceof PyWithStatement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiElement leftParen = PyPsiUtils.getFirstChildOfType(statement, PyTokenTypes.LPAR);
|
||||
return leftParen != null && offset >= leftParen.getTextRange().getEndOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result postProcessEnter(@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
|
||||
13
python/testSrc/com/jetbrains/python/PythonFormatterTest.java
Normal file
13
python/testSrc/com/jetbrains/python/PythonFormatterTest.java
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.jetbrains.python.fixture.PythonCommonCodeInsightTestFixture;
|
||||
import com.jetbrains.python.fixtures.PythonPlatformCodeInsightTestFixture;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
|
||||
public class PythonFormatterTest extends PythonCommonFormatterTest {
|
||||
@Override
|
||||
protected PythonCommonCodeInsightTestFixture getFixture() {
|
||||
return new PythonPlatformCodeInsightTestFixture(LANGUAGE_LEVEL);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,8 @@ class PythonPlatformCodeInsightTestFixture(languageLevel: LanguageLevel) : Pytho
|
||||
get() = requireNotNull(myDelegateFixture.tempDirFixture.getFile("."))
|
||||
override val testRootDisposable: Disposable
|
||||
get() = myDelegateFixture.testRootDisposable
|
||||
override val caretOffset: Int
|
||||
get() = myDelegateFixture.caretOffset
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
Reference in New Issue
Block a user