mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
enter handler that inserts \ at line break position
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyDumbAwareAnnotator"/>
|
||||
<backspaceHandlerDelegate implementation="com.jetbrains.python.editor.PythonBackspaceHandler"/>
|
||||
<quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
|
||||
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PythonEnterHandler"/>
|
||||
<sdkType implementation="com.jetbrains.python.sdk.PythonSdkType"/>
|
||||
<gotoClassContributor implementation="com.jetbrains.python.PyGotoClassContributor"/>
|
||||
<gotoSymbolContributor implementation="com.jetbrains.python.PyGotoSymbolContributor"/>
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.jetbrains.python.editor;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TypedHandler;
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PythonEnterHandler implements EnterHandlerDelegate {
|
||||
private static final Class[] IMPLICIT_WRAP_CLASSES = new Class[]{
|
||||
PsiComment.class,
|
||||
PyStringLiteralExpression.class,
|
||||
PyParenthesizedExpression.class,
|
||||
PyListCompExpression.class,
|
||||
PyDictCompExpression.class,
|
||||
PySetCompExpression.class,
|
||||
PyDictLiteralExpression.class,
|
||||
PySetLiteralExpression.class,
|
||||
PyListLiteralExpression.class,
|
||||
PyArgumentList.class,
|
||||
PyParameterList.class};
|
||||
|
||||
@Override
|
||||
public Result preprocessEnter(PsiFile file,
|
||||
Editor editor,
|
||||
Ref<Integer> caretOffset,
|
||||
Ref<Integer> caretAdvance,
|
||||
DataContext dataContext,
|
||||
EditorActionHandler originalHandler) {
|
||||
if (!(file instanceof PyFile)) {
|
||||
return Result.Continue;
|
||||
}
|
||||
Document doc = editor.getDocument();
|
||||
PsiDocumentManager.getInstance(file.getProject()).commitDocument(doc);
|
||||
final int offset = caretOffset.get();
|
||||
final PsiElement element = file.findElementAt(offset);
|
||||
if (element == null) {
|
||||
return Result.Continue;
|
||||
}
|
||||
PsiElement statementBefore = findStatementBeforeCaret(file, offset);
|
||||
PsiElement statementAfter = findStatementAfterCaret(file, offset);
|
||||
if (statementBefore != statementAfter) { // Enter pressed at statement break
|
||||
return Result.Continue;
|
||||
}
|
||||
|
||||
if (statementBefore != null && PsiTreeUtil.hasErrorElements(statementBefore)) {
|
||||
final Boolean autoWrapping = DataManager.getInstance().loadFromDataContext(dataContext, TypedHandler.AUTO_WRAP_LINE_IN_PROGRESS_KEY);
|
||||
if (autoWrapping == null) {
|
||||
// code is already bad, don't mess it up even further
|
||||
return Result.Continue;
|
||||
}
|
||||
// if we're in middle of typing, it's expected that we will have error elements
|
||||
}
|
||||
|
||||
PsiElement wrappableBefore = findBeforeCaret(file, offset, IMPLICIT_WRAP_CLASSES);
|
||||
PsiElement wrappableAfter = findAfterCaret(file, offset, IMPLICIT_WRAP_CLASSES);
|
||||
while (wrappableBefore != null) {
|
||||
PsiElement next = PsiTreeUtil.getParentOfType(wrappableBefore, IMPLICIT_WRAP_CLASSES);
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
wrappableBefore = next;
|
||||
}
|
||||
while (wrappableAfter != null) {
|
||||
PsiElement next = PsiTreeUtil.getParentOfType(wrappableAfter, IMPLICIT_WRAP_CLASSES);
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
wrappableAfter = next;
|
||||
}
|
||||
if (wrappableAfter == null || wrappableBefore != wrappableAfter) {
|
||||
doc.insertString(offset, "\\");
|
||||
caretOffset.set(offset+1);
|
||||
}
|
||||
return Result.Continue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findStatementBeforeCaret(PsiFile file, int offset) {
|
||||
return findBeforeCaret(file, offset, PyStatement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findStatementAfterCaret(PsiFile file, int offset) {
|
||||
return findAfterCaret(file, offset, PyStatement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findBeforeCaret(PsiFile file, int offset, Class<? extends PsiElement>... classes) {
|
||||
while(offset > 0) {
|
||||
offset--;
|
||||
final PsiElement element = file.findElementAt(offset);
|
||||
if (!(element instanceof PsiWhiteSpace)) {
|
||||
return PsiTreeUtil.getParentOfType(element, classes);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findAfterCaret(PsiFile file, int offset, Class<? extends PsiElement>... classes) {
|
||||
while(offset < file.getTextLength()) {
|
||||
final PsiElement element = file.findElementAt(offset);
|
||||
if (!(element instanceof PsiWhiteSpace)) {
|
||||
return PsiTreeUtil.getParentOfType(element, classes);
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
def copy_location(new_node, old_node):
|
||||
for attr in 'lineno', 'col_offset':
|
||||
if attr in old_node._attributes and attr in new_node._attributes and \
|
||||
hasattr(old_node, attr):
|
||||
@@ -0,0 +1,3 @@
|
||||
def copy_location(new_node, old_node):
|
||||
for attr in 'lineno', 'col_offset':
|
||||
if attr in old_node._attributes and attr in new_node._attributes <caret>
|
||||
@@ -0,0 +1,2 @@
|
||||
# Gallia est omnis divisa in partes tres, quarum unam incolumnt Belgae, aliam
|
||||
# Aquitani
|
||||
@@ -0,0 +1 @@
|
||||
# Gallia est omnis divisa in partes tres, quarum unam incolumnt Belgae, aliam <caret>
|
||||
@@ -97,7 +97,33 @@ public class PyEditingTest extends PyLightFixtureTestCase {
|
||||
}
|
||||
|
||||
public void testEnterInLineComment() { // PY-1739
|
||||
assertEquals("# foo\n# bar", doTestTyping("# foo bar", 5, '\n'));
|
||||
doTestEnter("# foo <caret>bar", "# foo \n# bar");
|
||||
}
|
||||
|
||||
public void testEnterInStatement() {
|
||||
doTestEnter("if a <caret>and b: pass", "if a \\\nand b: pass");
|
||||
}
|
||||
|
||||
public void testEnterBeforeStatement() {
|
||||
doTestEnter("def foo(): <caret>pass", "def foo(): \n pass");
|
||||
}
|
||||
|
||||
public void testEnterInParameterList() {
|
||||
doTestEnter("def foo(a,<caret>b): pass", "def foo(a,\n b): pass");
|
||||
}
|
||||
|
||||
public void testEnterInTuple() {
|
||||
doTestEnter("for x in 'a', <caret>'b': pass", "for x in 'a', \\\n 'b': pass");
|
||||
}
|
||||
|
||||
public void testEnterInCodeWithErrorElements() {
|
||||
doTestEnter("z=1 <caret>2", "z=1 \n2");
|
||||
}
|
||||
|
||||
private void doTestEnter(String before, final String after) {
|
||||
int pos = before.indexOf("<caret>");
|
||||
before = before.replace("<caret>", "");
|
||||
assertEquals(after, doTestTyping(before, pos, '\n'));
|
||||
}
|
||||
|
||||
private String doTestTyping(final String text, final int offset, final char character) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyWrapTest extends PyLightFixtureTestCase {
|
||||
private boolean myOldWrap;
|
||||
private int myOldMargin;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(myFixture.getProject()).getCurrentSettings();
|
||||
myOldWrap = settings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN;
|
||||
myOldMargin = settings.RIGHT_MARGIN;
|
||||
settings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = true;
|
||||
settings.RIGHT_MARGIN = 80;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(myFixture.getProject()).getCurrentSettings();
|
||||
settings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = myOldWrap;
|
||||
settings.RIGHT_MARGIN = myOldMargin;
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testBackslashOnWrap() {
|
||||
doTest("and hasattr(old_node, attr):");
|
||||
}
|
||||
|
||||
public void testWrapInComment() {
|
||||
doTest("Aquitani");
|
||||
}
|
||||
|
||||
private void doTest(final String textToType) {
|
||||
myFixture.configureByFile("wrap/" + getTestName(false) + ".py");
|
||||
myFixture.type(textToType);
|
||||
myFixture.checkResultByFile("wrap/" + getTestName(false) + ".after.py");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public class PythonAllTestsSuite {
|
||||
PythonParsingTest.class,
|
||||
PyStringLiteralTest.class,
|
||||
PyIndentTest.class,
|
||||
PyWrapTest.class,
|
||||
PyStatementPartsTest.class,
|
||||
PythonHighlightingTest.class,
|
||||
PyStubsTest.class,
|
||||
|
||||
Reference in New Issue
Block a user