mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Fix problems with editing multi-line commands (PY-21206)
This commit is contained in:
@@ -22,6 +22,7 @@ import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
@@ -40,11 +41,19 @@ import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl
|
||||
class PyConsoleEnterHandler {
|
||||
fun handleEnterPressed(editor: EditorEx): Boolean {
|
||||
val project = editor.project ?: throw IllegalArgumentException()
|
||||
if (editor.document.lineCount > 0) { // move to end of line
|
||||
val lineCount = editor.document.lineCount
|
||||
if (lineCount > 0) { // move to end of line
|
||||
editor.selectionModel.removeSelection()
|
||||
val caretPosition = editor.caretModel.logicalPosition
|
||||
val lineEndOffset = editor.document.getLineEndOffset(caretPosition.line)
|
||||
editor.caretModel.moveToOffset(lineEndOffset)
|
||||
if (caretPosition.line == lineCount - 1) {
|
||||
// we can move caret if only it's on the last line of command
|
||||
val lineEndOffset = editor.document.getLineEndOffset(caretPosition.line)
|
||||
editor.caretModel.moveToOffset(lineEndOffset)
|
||||
} else {
|
||||
// otherwise just process enter action
|
||||
executeEnterHandler(project, editor)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
@@ -54,11 +63,28 @@ class PyConsoleEnterHandler {
|
||||
|
||||
val caretOffset = editor.expectedCaretOffset
|
||||
val atElement = findFirstNoneSpaceElement(psiMgr.getPsiFile(editor.document)!!, caretOffset)
|
||||
var insideDocString = false
|
||||
atElement?.let {
|
||||
insideDocString = isElementInsideDocString(atElement, caretOffset)
|
||||
if (atElement == null) {
|
||||
executeEnterHandler(project, editor)
|
||||
return false
|
||||
}
|
||||
|
||||
val firstLine = getLineAtOffset(editor.document, DocumentUtil.getFirstNonSpaceCharOffset(editor.document, 0))
|
||||
val isCellMagic = firstLine.trim().startsWith("%%") && !firstLine.trimEnd().endsWith("?")
|
||||
val prevLine = getLineAtOffset(editor.document, caretOffset)
|
||||
|
||||
val isLineContinuation = prevLine.trim().endsWith('\\')
|
||||
val insideDocString = isElementInsideDocString(atElement, caretOffset)
|
||||
val isMultiLineCommand = PsiTreeUtil.getParentOfType(atElement, PyStatementListContainer::class.java) != null || isCellMagic
|
||||
val isAtTheEndOfCommand = editor.document.getLineNumber(caretOffset) == editor.document.lineCount - 1
|
||||
|
||||
val hasCompleteStatement = !insideDocString && !isLineContinuation && checkComplete(atElement)
|
||||
|
||||
executeEnterHandler(project, editor)
|
||||
|
||||
return isAtTheEndOfCommand && hasCompleteStatement && ((isMultiLineCommand && prevLine.isBlank()) || (!isMultiLineCommand))
|
||||
}
|
||||
|
||||
private fun executeEnterHandler(project: Project, editor:EditorEx) {
|
||||
val enterHandler = EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_ENTER)
|
||||
object : WriteCommandAction<Nothing>(project) {
|
||||
@Throws(Throwable::class)
|
||||
@@ -66,24 +92,17 @@ class PyConsoleEnterHandler {
|
||||
enterHandler.execute(editor, null, DataManager.getInstance().getDataContext(editor.component))
|
||||
}
|
||||
}.execute()
|
||||
|
||||
val firstLine = getLineAtOffset(editor.document, DocumentUtil.getFirstNonSpaceCharOffset(editor.document, 0))
|
||||
val isCellMagic = firstLine.trim().startsWith("%%") && !firstLine.trimEnd().endsWith("?")
|
||||
val isMultiLineCommand = PsiTreeUtil.getParentOfType(atElement, PyStatementListContainer::class.java) != null || isCellMagic
|
||||
|
||||
val hasCompleteStatement = atElement != null && !insideDocString && checkComplete(atElement)
|
||||
val prevLine = getLineAtOffset(editor.document, caretOffset)
|
||||
|
||||
return hasCompleteStatement && ((isMultiLineCommand && prevLine.isBlank()) || (!isMultiLineCommand))
|
||||
}
|
||||
|
||||
private fun isElementInsideDocString(atElement: PsiElement, caretOffset: Int): Boolean {
|
||||
return atElement.context is PyStringLiteralExpression && PyTokenTypes.TRIPLE_NODES.contains(atElement.node.elementType)
|
||||
&& (atElement.textRange.endOffset > caretOffset || !isCompleteDocString(atElement.text))
|
||||
return atElement.context is PyStringLiteralExpression &&
|
||||
(PyTokenTypes.TRIPLE_NODES.contains(atElement.node.elementType) ||
|
||||
atElement.node.elementType === PyTokenTypes.DOCSTRING) &&
|
||||
isMultilineString(atElement.text) &&
|
||||
(atElement.textRange.endOffset > caretOffset || !isCompleteDocString(atElement.text))
|
||||
}
|
||||
|
||||
private fun checkComplete(el: PsiElement): Boolean {
|
||||
if (!el.isValid) return false
|
||||
val compoundStatement = PsiTreeUtil.getParentOfType(el, PyStatementListContainer::class.java)
|
||||
if (compoundStatement != null && compoundStatement !is PyTryPart) {
|
||||
return compoundStatement.statementList.statements.size != 0
|
||||
@@ -109,6 +128,11 @@ class PyConsoleEnterHandler {
|
||||
return doc.getText(TextRange(start, end))
|
||||
}
|
||||
|
||||
private fun isMultilineString(str: String): Boolean {
|
||||
val text = str.substring(PyStringLiteralExpressionImpl.getPrefixLength(str))
|
||||
return text.startsWith("\"\"\"") || text.startsWith("'''")
|
||||
}
|
||||
|
||||
private fun isCompleteDocString(str: String): Boolean {
|
||||
val prefixLen = PyStringLiteralExpressionImpl.getPrefixLength(str)
|
||||
val text = str.substring(prefixLen)
|
||||
|
||||
@@ -55,6 +55,8 @@ class PyConsoleEnterHandlerTest : PyTestCase() {
|
||||
fun testSingleQuote() {
|
||||
assertTrue(push("'a'"))
|
||||
assertTrue(push("a = 'abc'"))
|
||||
assertTrue(push("'abc"))
|
||||
assertTrue(push("a = 'st"))
|
||||
}
|
||||
|
||||
fun testSimpleSingleLine() {
|
||||
@@ -108,6 +110,7 @@ class PyConsoleEnterHandlerTest : PyTestCase() {
|
||||
fun testCellMagic() {
|
||||
assertFalse(push("%%cellm firstline"))
|
||||
assertFalse(push(" line2"))
|
||||
assertFalse(push(" line3"))
|
||||
assertTrue(push(""))
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user