mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-09 09:34:06 +02:00
Remove the root write and read actions
Historically, IdeaVim was starting read or write action based on command type. However, this is incorrect because of several reasons: - The command of different type may perform a write/read action - The write lock with a lot of code inside does cause application freezes - Since a large branching of code happens inside the write action, there is a chance of read action request, resulting in the deadlock Also, now this prevents from implementing a proper execution of VIM-3376.
This commit is contained in:
parent
0511ee277f
commit
437fa9a1a0
src
main/java/com/maddyhome/idea/vim
extension/surround
group
helper
newapi
test/java/org/jetbrains/plugins/ideavim/group
testFixtures/kotlin/org/jetbrains/plugins/ideavim
vim-engine/src/main/kotlin/com/maddyhome/idea/vim
@ -176,7 +176,9 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val currentSurrounding = getCurrentSurrounding(it.caret, pick(charFrom))
|
||||
if (currentSurrounding != null) {
|
||||
it.caret.moveToOffset(currentSurrounding.startOffset)
|
||||
editor.deleteString(currentSurrounding)
|
||||
injector.application.runWriteAction {
|
||||
editor.deleteString(currentSurrounding)
|
||||
}
|
||||
}
|
||||
|
||||
val registerValue = getRegisterForCaret(editor, context, REGISTER, it.caret)
|
||||
|
@ -130,7 +130,9 @@ class ChangeGroup : VimChangeGroupBase() {
|
||||
val project = (editor as IjVimEditor).editor.project ?: return
|
||||
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
|
||||
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
|
||||
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
|
||||
injector.application.runWriteAction {
|
||||
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
|
||||
}
|
||||
}
|
||||
|
||||
override fun autoIndentRange(
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
/**
|
||||
* This provides some helper methods to run code as a command and an application write action
|
||||
*/
|
||||
internal object RunnableHelper {
|
||||
private val logger = logger<RunnableHelper>()
|
||||
|
||||
@JvmStatic
|
||||
fun runReadCommand(project: Project?, cmd: Runnable, name: String?, groupId: Any?) {
|
||||
logger.debug { "Run read command: $name" }
|
||||
CommandProcessor.getInstance()
|
||||
.executeCommand(project, { ApplicationManager.getApplication().runReadAction(cmd) }, name, groupId)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun runWriteCommand(project: Project?, cmd: Runnable, name: String?, groupId: Any?) {
|
||||
logger.debug { "Run write command: $name" }
|
||||
CommandProcessor.getInstance()
|
||||
.executeCommand(project, { ApplicationManager.getApplication().runWriteAction(cmd) }, name, groupId)
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ import com.intellij.util.ExceptionUtil
|
||||
import com.maddyhome.idea.vim.api.VimApplicationBase
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.diagnostic.vimLogger
|
||||
import com.maddyhome.idea.vim.helper.RunnableHelper
|
||||
import java.awt.Component
|
||||
import java.awt.Toolkit
|
||||
import java.awt.Window
|
||||
@ -58,14 +57,6 @@ internal class IjVimApplication : VimApplicationBase() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun runWriteCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
|
||||
RunnableHelper.runWriteCommand((editor as IjVimEditor).editor.project, command, name, groupId)
|
||||
}
|
||||
|
||||
override fun runReadCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
|
||||
RunnableHelper.runReadCommand((editor as IjVimEditor).editor.project, command, name, groupId)
|
||||
}
|
||||
|
||||
override fun <T> runWriteAction(action: () -> T): T {
|
||||
return ApplicationManager.getApplication().runWriteAction(Computable(action))
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import com.intellij.openapi.util.Ref
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
|
||||
import com.maddyhome.idea.vim.common.Direction
|
||||
import com.maddyhome.idea.vim.helper.RunnableHelper
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
@ -913,21 +912,13 @@ class SearchGroupTest : VimTestCase() {
|
||||
private fun search(pattern: String, input: String): Int {
|
||||
configureByText(input)
|
||||
val editor = fixture.editor
|
||||
val project = fixture.project
|
||||
val searchGroup = VimPlugin.getSearch()
|
||||
val ref = Ref.create(-1)
|
||||
ApplicationManager.getApplication().invokeAndWait {
|
||||
ApplicationManager.getApplication().runWriteIntentReadAction<Any, Throwable> {
|
||||
RunnableHelper.runReadCommand(
|
||||
project,
|
||||
{
|
||||
// Does not move the caret!
|
||||
val n = searchGroup.processSearchCommand(editor.vim, pattern, fixture.caretOffset, 1, Direction.FORWARDS)
|
||||
ref.set(n?.first ?: -1)
|
||||
},
|
||||
null,
|
||||
null,
|
||||
)
|
||||
// Does not move the caret!
|
||||
val n = searchGroup.processSearchCommand(editor.vim, pattern, fixture.caretOffset, 1, Direction.FORWARDS)
|
||||
ref.set(n?.first ?: -1)
|
||||
}
|
||||
}
|
||||
return ref.get()
|
||||
|
@ -70,7 +70,6 @@ import com.maddyhome.idea.vim.group.IjOptions
|
||||
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.swingTimer
|
||||
import com.maddyhome.idea.vim.handler.isOctopusEnabled
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.RunnableHelper.runWriteCommand
|
||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||
import com.maddyhome.idea.vim.helper.getGuiCursorMode
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
@ -1063,8 +1062,8 @@ abstract class VimTestCase {
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
val dataContext = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||
TestInputModel.getInstance(editor).setKeyStrokes(keys.filterNotNull())
|
||||
runWriteCommand(
|
||||
project,
|
||||
injector.actionExecutor.executeCommand(
|
||||
editor.vim,
|
||||
Runnable {
|
||||
val inputModel = TestInputModel.getInstance(editor)
|
||||
var key = inputModel.nextKeyStroke()
|
||||
|
@ -245,13 +245,7 @@ class KeyHandler {
|
||||
val action: Runnable = ActionRunner(editor, context, command, keyState, operatorArguments)
|
||||
val cmdAction = command.action
|
||||
val name = cmdAction.id
|
||||
if (type.isWrite) {
|
||||
injector.application.runWriteCommand(editor, name, action, action)
|
||||
} else if (type.isRead) {
|
||||
injector.application.runReadCommand(editor, name, action, action)
|
||||
} else {
|
||||
injector.actionExecutor.executeCommand(editor, action, name, action)
|
||||
}
|
||||
injector.actionExecutor.executeCommand(editor, action, name, action)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,9 @@ private fun insertCharacterAroundCursor(editor: VimEditor, caret: VimCaret, dir:
|
||||
val charsSequence = editor.text()
|
||||
if (offset < charsSequence.length) {
|
||||
val ch = charsSequence[offset]
|
||||
(editor as MutableVimEditor).insertText(caret, caret.offset, ch.toString())
|
||||
injector.application.runWriteAction {
|
||||
(editor as MutableVimEditor).insertText(caret, caret.offset, ch.toString())
|
||||
}
|
||||
caret.moveToMotion(injector.motion.getHorizontalMotion(editor, caret, 1, true))
|
||||
res = true
|
||||
}
|
||||
|
@ -18,9 +18,6 @@ interface VimApplication {
|
||||
fun isInternal(): Boolean
|
||||
fun postKey(stroke: KeyStroke, editor: VimEditor)
|
||||
|
||||
fun runWriteCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable)
|
||||
fun runReadCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable)
|
||||
|
||||
fun <T> runWriteAction(action: () -> T): T
|
||||
fun <T> runReadAction(action: () -> T): T
|
||||
|
||||
|
@ -197,7 +197,9 @@ abstract class VimChangeGroupBase : VimChangeGroup {
|
||||
editor,
|
||||
LineDeleteShift.NL_ON_END
|
||||
) ?: continue
|
||||
editor.deleteString(TextRange(newRange.first, newRange.second))
|
||||
injector.application.runWriteAction {
|
||||
editor.deleteString(TextRange(newRange.first, newRange.second))
|
||||
}
|
||||
}
|
||||
if (type != null) {
|
||||
val start = updatedRange.startOffset
|
||||
@ -215,7 +217,9 @@ abstract class VimChangeGroupBase : VimChangeGroup {
|
||||
* @param str The text to insert
|
||||
*/
|
||||
override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret {
|
||||
(editor as MutableVimEditor).insertText(caret, offset, str)
|
||||
injector.application.runWriteAction {
|
||||
(editor as MutableVimEditor).insertText(caret, offset, str)
|
||||
}
|
||||
val newCaret = caret.moveToInlayAwareOffset(offset + str.length)
|
||||
|
||||
injector.markService.setMark(newCaret, MARK_CHANGE_POS, offset)
|
||||
@ -1003,7 +1007,9 @@ abstract class VimChangeGroupBase : VimChangeGroup {
|
||||
var processedCaret = editor.findLastVersionOfCaret(caret) ?: caret
|
||||
if (removeLastNewLine) {
|
||||
val textLength = editor.fileSize().toInt()
|
||||
editor.deleteString(TextRange(textLength - 1, textLength))
|
||||
injector.application.runWriteAction {
|
||||
editor.deleteString(TextRange(textLength - 1, textLength))
|
||||
}
|
||||
processedCaret = editor.findLastVersionOfCaret(caret) ?: caret
|
||||
}
|
||||
|
||||
@ -1189,7 +1195,9 @@ abstract class VimChangeGroupBase : VimChangeGroup {
|
||||
* @param str The new text
|
||||
*/
|
||||
override fun replaceText(editor: VimEditor, caret: VimCaret, start: Int, end: Int, str: String) {
|
||||
(editor as MutableVimEditor).replaceString(start, end, str)
|
||||
injector.application.runWriteAction {
|
||||
(editor as MutableVimEditor).replaceString(start, end, str)
|
||||
}
|
||||
|
||||
val newEnd = start + str.length
|
||||
injector.markService.setChangeMarks(caret, TextRange(start, newEnd))
|
||||
|
@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.api
|
||||
import com.maddyhome.idea.vim.common.LiveRange
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.common.VimEditorReplaceMask
|
||||
import com.maddyhome.idea.vim.helper.RWLockLabel
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
|
||||
@ -152,6 +153,7 @@ interface VimEditor {
|
||||
}
|
||||
|
||||
fun getVirtualFile(): VirtualFile?
|
||||
@RWLockLabel.Writable
|
||||
fun deleteString(range: TextRange)
|
||||
|
||||
fun getLineText(line: Int): String {
|
||||
@ -241,7 +243,9 @@ interface VimEditor {
|
||||
|
||||
interface MutableVimEditor : VimEditor {
|
||||
fun addLine(atPosition: Int): Int?
|
||||
@RWLockLabel.Writable
|
||||
fun insertText(caret: VimCaret, atPosition: Int, text: CharSequence)
|
||||
@RWLockLabel.Writable
|
||||
fun replaceString(start: Int, end: Int, newString: String)
|
||||
}
|
||||
|
||||
|
@ -903,7 +903,7 @@ abstract class VimSearchGroupBase : VimSearchGroup {
|
||||
}
|
||||
|
||||
override fun executeInput(input: ReplaceConfirmationChoice, editor: VimEditor, context: ExecutionContext) {
|
||||
injector.application.runWriteCommand(editor, "substitute-with-confirmation", modalInput) {
|
||||
injector.actionExecutor.executeCommand(editor, {
|
||||
highlight.remove()
|
||||
injector.jumpService.saveJumpLocation(editor)
|
||||
val matchRange = nextSubstitute.first.range
|
||||
@ -931,7 +931,7 @@ abstract class VimSearchGroupBase : VimSearchGroup {
|
||||
options
|
||||
)
|
||||
closeModalInputPrompt()
|
||||
return@runWriteCommand
|
||||
return@executeCommand
|
||||
}
|
||||
|
||||
ReplaceConfirmationChoice.QUIT -> {
|
||||
@ -965,7 +965,7 @@ abstract class VimSearchGroupBase : VimSearchGroup {
|
||||
column = replaceResult.column
|
||||
|
||||
goToNextIteration()
|
||||
}
|
||||
}, "substitute-with-confirmation", modalInput)
|
||||
}
|
||||
|
||||
private fun goToNextIteration() {
|
||||
|
@ -42,14 +42,6 @@ class VimApplicationStub : VimApplicationBase() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun runWriteCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun runReadCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T> runWriteAction(action: () -> T): T {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
@ -96,12 +96,11 @@ data class Command(
|
||||
MODE_CHANGE,
|
||||
;
|
||||
|
||||
val isRead: Boolean
|
||||
get() = when (this) {
|
||||
MOTION, COPY, OTHER_READONLY, MODE_CHANGE -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated because not only this set of commands can be writable.
|
||||
* A different way of detecting if a command is going to write something is needed.
|
||||
*/
|
||||
@Deprecated("")
|
||||
val isWrite: Boolean
|
||||
get() = when (this) {
|
||||
INSERT, DELETE, CHANGE, PASTE, OTHER_WRITABLE -> true
|
||||
|
@ -93,7 +93,9 @@ data class MoveTextCommand(val range: Range, val modifier: CommandModifier, val
|
||||
val dropNewLineInEnd = (line + linesMoved == editor.lineCount() - 1 && text.last() == '\n') ||
|
||||
(lineRange.endLine == editor.lineCount() - 1)
|
||||
|
||||
editor.deleteString(range)
|
||||
injector.application.runWriteAction {
|
||||
editor.deleteString(range)
|
||||
}
|
||||
val putData = if (line == -1) {
|
||||
// Special case. Move text to below the line before the first line
|
||||
caret.moveToOffset(0)
|
||||
@ -113,7 +115,9 @@ data class MoveTextCommand(val range: Range, val modifier: CommandModifier, val
|
||||
|
||||
if (dropNewLineInEnd) {
|
||||
assert(editor.text().last() == '\n')
|
||||
editor.deleteString(TextRange(editor.text().length - 1, editor.text().length))
|
||||
injector.application.runWriteAction {
|
||||
editor.deleteString(TextRange(editor.text().length - 1, editor.text().length))
|
||||
}
|
||||
}
|
||||
|
||||
globalMarks.forEach { shiftGlobalMark(editor, it, shift) }
|
||||
|
Loading…
Reference in New Issue
Block a user