mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2026-10-17 05:33:52 +02:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a086b239c
|
||
|
|
9521d016d2
|
||
|
|
49eac08f9a
|
||
|
|
d05c3fe940
|
||
|
|
b76106c44f
|
||
|
|
8b61b15334
|
||
|
|
ed38927954
|
||
|
|
2657d4fb38
|
||
|
|
a7b8cc8a8d
|
||
|
|
fdab9c5565
|
||
|
|
0f3c12653d
|
||
|
|
6b96be5e07
|
||
|
|
9f601d7442
|
||
|
|
1c51cf3457
|
+1
-1
@@ -18,7 +18,7 @@ ideaVersion=2026.1
|
|||||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||||
ideaType=IU
|
ideaType=IU
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=chylex-1
|
version=9999.59-chylex
|
||||||
javaVersion=21
|
javaVersion=21
|
||||||
remoteRobotVersion=0.11.23
|
remoteRobotVersion=0.11.23
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
package com.maddyhome.idea.vim.action.macro
|
||||||
|
|
||||||
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
|
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||||
|
import com.intellij.openapi.command.impl.FinishMarkAction
|
||||||
|
import com.intellij.openapi.command.impl.StartMarkAction
|
||||||
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
|
import com.intellij.vim.annotations.CommandOrMotion
|
||||||
|
import com.intellij.vim.annotations.Mode
|
||||||
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
|
import com.maddyhome.idea.vim.command.Command
|
||||||
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
|
||||||
|
@CommandOrMotion(keys = ["z@"], modes = [Mode.NORMAL])
|
||||||
|
class PlaybackRegisterInOpenFilesAction : VimActionHandler.SingleExecution() {
|
||||||
|
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||||
|
|
||||||
|
override val argumentType: Argument.Type = Argument.Type.CHARACTER
|
||||||
|
|
||||||
|
private val playbackRegisterAction = PlaybackRegisterAction()
|
||||||
|
|
||||||
|
override fun execute(
|
||||||
|
editor: VimEditor,
|
||||||
|
context: ExecutionContext,
|
||||||
|
cmd: Command,
|
||||||
|
operatorArguments: OperatorArguments,
|
||||||
|
): Boolean {
|
||||||
|
val argument = cmd.argument as? Argument.Character ?: return false
|
||||||
|
|
||||||
|
val project = editor.ij.project ?: return false
|
||||||
|
val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return false
|
||||||
|
|
||||||
|
val register = argument.character.let { if (it == '@') injector.macro.lastRegister else it }
|
||||||
|
val commandName = "Execute Macro '$register' in All Open Files"
|
||||||
|
|
||||||
|
val action = Runnable {
|
||||||
|
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
|
||||||
|
|
||||||
|
for (textEditor in fileEditorManager.allEditors.filterIsInstance<TextEditor>()) {
|
||||||
|
fileEditorManager.openFile(textEditor.file, true)
|
||||||
|
|
||||||
|
val editor = textEditor.editor
|
||||||
|
val vimEditor = editor.vim
|
||||||
|
|
||||||
|
vimEditor.mode = com.maddyhome.idea.vim.state.mode.Mode.NORMAL()
|
||||||
|
KeyHandler.Companion.getInstance().reset(vimEditor)
|
||||||
|
|
||||||
|
val startMarkAction = StartMarkAction.start(editor, project, commandName)
|
||||||
|
playbackRegisterAction.execute(vimEditor, context, cmd, operatorArguments)
|
||||||
|
FinishMarkAction.finish(project, editor, startMarkAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandProcessor.getInstance()
|
||||||
|
.executeCommand(project, action, commandName, null, UndoConfirmationPolicy.REQUEST_CONFIRMATION)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,12 +24,11 @@ import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
|||||||
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
||||||
|
import com.maddyhome.idea.vim.api.VirtualBufferKind
|
||||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
||||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||||
import com.maddyhome.idea.vim.api.getVisualLineCount
|
import com.maddyhome.idea.vim.api.getVisualLineCount
|
||||||
import com.maddyhome.idea.vim.api.VirtualBufferKind
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.helper.CmdwinKeys
|
|
||||||
import com.maddyhome.idea.vim.api.lineLength
|
import com.maddyhome.idea.vim.api.lineLength
|
||||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||||
import com.maddyhome.idea.vim.api.normalizeVisualLine
|
import com.maddyhome.idea.vim.api.normalizeVisualLine
|
||||||
@@ -44,6 +43,7 @@ import com.maddyhome.idea.vim.handler.Motion
|
|||||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
||||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
|
import com.maddyhome.idea.vim.helper.CmdwinKeys
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
||||||
@@ -126,6 +126,9 @@ class MotionGroup : VimMotionGroupBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||||
|
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
|
||||||
|
return AbsoluteOffset(caret.ij.visualLineStart)
|
||||||
|
}
|
||||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||||
return moveCaretToColumn(editor, caret, col, false)
|
return moveCaretToColumn(editor, caret, col, false)
|
||||||
}
|
}
|
||||||
@@ -134,6 +137,15 @@ class MotionGroup : VimMotionGroupBase() {
|
|||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
caret: ImmutableVimCaret,
|
caret: ImmutableVimCaret,
|
||||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||||
|
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
|
||||||
|
val offset = caret.ij.visualLineStart
|
||||||
|
val line = editor.offsetToBufferPosition(offset).line
|
||||||
|
return if (offset == editor.getLineStartOffset(line)) {
|
||||||
|
editor.getLeadingCharacterOffset(line, 0)
|
||||||
|
} else {
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
}
|
||||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||||
val bufferLine = caret.getLine()
|
val bufferLine = caret.getLine()
|
||||||
return editor.getLeadingCharacterOffset(bufferLine, col)
|
return editor.getLeadingCharacterOffset(bufferLine, col)
|
||||||
@@ -144,6 +156,9 @@ class MotionGroup : VimMotionGroupBase() {
|
|||||||
caret: ImmutableVimCaret,
|
caret: ImmutableVimCaret,
|
||||||
allowEnd: Boolean,
|
allowEnd: Boolean,
|
||||||
): Motion {
|
): Motion {
|
||||||
|
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
|
||||||
|
return AbsoluteOffset(caret.ij.visualLineEnd - 1)
|
||||||
|
}
|
||||||
val col = EditorHelper.getVisualColumnAtRightOfDisplay(editor.ij, caret.getVisualPosition().line)
|
val col = EditorHelper.getVisualColumnAtRightOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||||
return moveCaretToColumn(editor, caret, col, allowEnd)
|
return moveCaretToColumn(editor, caret, col, allowEnd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
package com.maddyhome.idea.vim.helper
|
package com.maddyhome.idea.vim.helper
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import com.intellij.openapi.util.SystemInfoRt
|
||||||
|
import com.intellij.openapi.util.registry.Registry
|
||||||
import com.intellij.ui.KeyStrokeAdapter
|
import com.intellij.ui.KeyStrokeAdapter
|
||||||
|
import java.awt.event.InputEvent
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
@@ -24,12 +27,30 @@ internal class ActionEventKeyStrokeExtractor {
|
|||||||
val defaultKeyStroke = KeyStrokeAdapter.getDefaultKeyStroke(inputEvent)
|
val defaultKeyStroke = KeyStrokeAdapter.getDefaultKeyStroke(inputEvent)
|
||||||
val strokeCache = keyStrokeCache
|
val strokeCache = keyStrokeCache
|
||||||
if (defaultKeyStroke != null) {
|
if (defaultKeyStroke != null) {
|
||||||
keyStrokeCache = inputEvent.`when` to defaultKeyStroke
|
val fixedKeyStroke = fixKeyStroke(defaultKeyStroke)
|
||||||
return defaultKeyStroke
|
keyStrokeCache = inputEvent.`when` to fixedKeyStroke
|
||||||
|
return fixedKeyStroke
|
||||||
} else if (strokeCache.first == inputEvent.`when`) {
|
} else if (strokeCache.first == inputEvent.`when`) {
|
||||||
keyStrokeCache = null to null
|
keyStrokeCache = null to null
|
||||||
return strokeCache.second
|
return strokeCache.second
|
||||||
}
|
}
|
||||||
return KeyStroke.getKeyStrokeForEvent(inputEvent)
|
return KeyStroke.getKeyStrokeForEvent(inputEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fixKeyStroke(key: KeyStroke): KeyStroke {
|
||||||
|
return if (
|
||||||
|
key.modifiers and CTRL_ALT_MASK != 0 &&
|
||||||
|
key.isOnKeyRelease &&
|
||||||
|
SystemInfoRt.isWindows &&
|
||||||
|
Registry.`is`("actionSystem.fix.alt.gr", true)
|
||||||
|
) {
|
||||||
|
KeyStroke.getKeyStroke(key.keyCode, key.modifiers)
|
||||||
|
} else {
|
||||||
|
key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val CTRL_ALT_MASK = InputEvent.CTRL_DOWN_MASK or InputEvent.ALT_DOWN_MASK
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.editor.EditorKind
|
import com.intellij.openapi.editor.EditorKind
|
||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
|
import com.intellij.openapi.ui.popup.util.PopupUtil
|
||||||
import com.intellij.util.ui.table.JBTableRowEditor
|
import com.intellij.util.ui.table.JBTableRowEditor
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.StringListOptionValue
|
import com.maddyhome.idea.vim.api.StringListOptionValue
|
||||||
@@ -71,6 +72,10 @@ internal val Editor.isIdeaVimDisabledHere: Boolean
|
|||||||
* Enter and arrow keys (see KeyGroup and ToolWindowNavEverywhere).
|
* Enter and arrow keys (see KeyGroup and ToolWindowNavEverywhere).
|
||||||
*/
|
*/
|
||||||
private fun Editor.isAllowedFileEditor(): Boolean {
|
private fun Editor.isAllowedFileEditor(): Boolean {
|
||||||
|
if (EditorHelper.getVirtualFile(this)?.name?.contains("Dummy.txt") == true) {
|
||||||
|
return PopupUtil.getPopupContainerFor(component) == null
|
||||||
|
}
|
||||||
|
|
||||||
return EditorHelper.isCommitWindowEditor(this)
|
return EditorHelper.isCommitWindowEditor(this)
|
||||||
|| EditorHelper.isKotlinClassDecompiledToJavaFile(this)
|
|| EditorHelper.isKotlinClassDecompiledToJavaFile(this)
|
||||||
|| EditorHelper.isDiffEditor(this)
|
|| EditorHelper.isDiffEditor(this)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.intellij.openapi.diagnostic.thisLogger
|
|||||||
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
|
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
|
||||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
|
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
|
||||||
import com.intellij.openapi.util.NlsContexts
|
import com.intellij.openapi.util.NlsContexts
|
||||||
|
import com.intellij.refactoring.actions.BaseRefactoringAction
|
||||||
import com.maddyhome.idea.vim.RegisterActions
|
import com.maddyhome.idea.vim.RegisterActions
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.NativeAction
|
import com.maddyhome.idea.vim.api.NativeAction
|
||||||
@@ -35,6 +36,8 @@ import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
|||||||
import com.maddyhome.idea.vim.newapi.IjNativeAction
|
import com.maddyhome.idea.vim.newapi.IjNativeAction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimDocument
|
import com.maddyhome.idea.vim.newapi.IjVimDocument
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
@@ -70,15 +73,35 @@ class IjActionExecutor : VimActionExecutor {
|
|||||||
thisLogger().error("Actions cannot be updated when write-action is running or pending")
|
thisLogger().error("Actions cannot be updated when write-action is running or pending")
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeAction((action as IjNativeAction).action, editor?.ij?.contentComponent)
|
return executeAction(editor, (action as IjNativeAction).action, editor?.ij?.contentComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeAction(ijAction: AnAction, contextComponent: Component?): Boolean {
|
private fun executeAction(editor: VimEditor?, ijAction: AnAction, contextComponent: Component?): Boolean {
|
||||||
|
val startVisualModeType = (editor?.mode as? Mode.VISUAL)?.selectionType
|
||||||
|
val startVisualCaretSelection = if (editor != null && startVisualModeType != null && ijAction !is BaseRefactoringAction)
|
||||||
|
editor.primaryCaret().let { Triple(it.offset, it.selectionStart, it.selectionEnd) }
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isRunningActionFromVim = true
|
isRunningActionFromVim = true
|
||||||
val place = ijAction.choosePlace()
|
val place = ijAction.choosePlace()
|
||||||
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, place, true)
|
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, place, true)
|
||||||
res.waitFor(5_000)
|
res.waitFor(5_000)
|
||||||
|
|
||||||
|
if (startVisualModeType != null && startVisualCaretSelection != null) {
|
||||||
|
val primaryCaret = editor.primaryCaret()
|
||||||
|
val endVisualCaretOffset = primaryCaret.offset
|
||||||
|
if (startVisualCaretSelection.first != endVisualCaretOffset) {
|
||||||
|
if (!editor.inVisualMode || (editor.mode as Mode.VISUAL).selectionType != startVisualModeType) {
|
||||||
|
injector.visualMotionGroup.toggleVisual(editor, 1, 0, startVisualModeType)
|
||||||
|
}
|
||||||
|
primaryCaret.moveToOffset(startVisualCaretSelection.first)
|
||||||
|
primaryCaret.setSelection(startVisualCaretSelection.second, startVisualCaretSelection.third)
|
||||||
|
primaryCaret.moveToOffset(endVisualCaretOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res.isDone
|
return res.isDone
|
||||||
} finally {
|
} finally {
|
||||||
isRunningActionFromVim = false
|
isRunningActionFromVim = false
|
||||||
@@ -88,7 +111,7 @@ class IjActionExecutor : VimActionExecutor {
|
|||||||
override fun executeAction(editor: VimEditor?, action: NativeAction, context: ExecutionContext): Boolean {
|
override fun executeAction(editor: VimEditor?, action: NativeAction, context: ExecutionContext): Boolean {
|
||||||
if (editor != null && editor === injector.fallbackWindow) {
|
if (editor != null && editor === injector.fallbackWindow) {
|
||||||
val contextComponent = context.ij.getData(PlatformDataKeys.CONTEXT_COMPONENT)
|
val contextComponent = context.ij.getData(PlatformDataKeys.CONTEXT_COMPONENT)
|
||||||
return executeAction((action as IjNativeAction).action, contextComponent)
|
return executeAction(editor, (action as IjNativeAction).action, contextComponent)
|
||||||
}
|
}
|
||||||
return executeAction(editor, action)
|
return executeAction(editor, action)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,11 @@ private fun updateSearchHighlights(
|
|||||||
currentMatchOffset,
|
currentMatchOffset,
|
||||||
editor
|
editor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!isIncsearch) {
|
||||||
|
removeHighlightsEditors.add(editor)
|
||||||
|
removeHighlightsTimer.restart()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentEditorCurrentMatchOffset
|
return currentEditorCurrentMatchOffset
|
||||||
@@ -512,3 +517,9 @@ private fun shouldAddCurrentMatchSearchHighlight(pattern: String?, hlSearch: Boo
|
|||||||
*/
|
*/
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
private fun isIncrementalSearchHighlights(initialOffset: Int) = initialOffset != -1
|
private fun isIncrementalSearchHighlights(initialOffset: Int) = initialOffset != -1
|
||||||
|
|
||||||
|
val removeHighlightsEditors = mutableListOf<Editor>()
|
||||||
|
val removeHighlightsTimer = javax.swing.Timer(450) {
|
||||||
|
removeHighlightsEditors.forEach(::removeSearchHighlights)
|
||||||
|
removeHighlightsEditors.clear()
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.intellij.openapi.editor.VisualPosition
|
|||||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.UserDataHolder
|
import com.intellij.openapi.util.UserDataHolder
|
||||||
|
import com.intellij.openapi.util.UserDataHolderBase
|
||||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
|
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
|
||||||
import com.maddyhome.idea.vim.api.LocalMarkStorage
|
import com.maddyhome.idea.vim.api.LocalMarkStorage
|
||||||
import com.maddyhome.idea.vim.api.SelectionInfo
|
import com.maddyhome.idea.vim.api.SelectionInfo
|
||||||
@@ -97,7 +98,7 @@ var Caret.vimInsertStart: RangeMarker by userDataOr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Data could be lost during visual block motion
|
// TODO: Data could be lost during visual block motion
|
||||||
var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor()
|
var Caret.registerStorage: CaretRegisterStorageBase? by GlobalCaretData.userData()
|
||||||
var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
|
var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
|
||||||
var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
|
var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
|
||||||
|
|
||||||
@@ -144,6 +145,39 @@ fun <T> userData(): ReadWriteProperty<UserDataHolder, T?> =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object GlobalCaretData {
|
||||||
|
private val holders = mutableListOf<UserDataHolderBase>()
|
||||||
|
|
||||||
|
private fun getHolder(caretIndex: Int): UserDataHolderBase {
|
||||||
|
while (holders.lastIndex < caretIndex) {
|
||||||
|
holders.add(UserDataHolderBase())
|
||||||
|
}
|
||||||
|
return holders[caretIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> userData(): ReadWriteProperty<Caret, T?> = object : UserDataReadWriteProperty<Caret, T?>() {
|
||||||
|
override fun getValue(thisRef: Caret, property: KProperty<*>): T? {
|
||||||
|
return getCaretData(thisRef).getUserData(getKey(property))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Caret, property: KProperty<*>, value: T?) {
|
||||||
|
getCaretData(thisRef).putUserData(getKey(property), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCaretData(caret: Caret): UserDataHolder {
|
||||||
|
if (caret == caret.caretModel.primaryCaret) {
|
||||||
|
return caret.editor
|
||||||
|
}
|
||||||
|
|
||||||
|
val caretIndex = caret.caretModel.allCarets.indexOf(caret)
|
||||||
|
return if (caretIndex == -1)
|
||||||
|
caret.editor
|
||||||
|
else
|
||||||
|
getHolder(caretIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function for delegated properties.
|
* Function for delegated properties.
|
||||||
* The property will be saved to caret if this caret is not primary
|
* The property will be saved to caret if this caret is not primary
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.template.TemplateManagerListener
|
|||||||
import com.intellij.codeInsight.template.impl.TemplateImpl
|
import com.intellij.codeInsight.template.impl.TemplateImpl
|
||||||
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
|
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
|
||||||
import com.intellij.codeInsight.template.impl.TemplateState
|
import com.intellij.codeInsight.template.impl.TemplateState
|
||||||
|
import com.intellij.codeInsight.template.impl.actions.NextVariableAction
|
||||||
import com.intellij.find.FindModelListener
|
import com.intellij.find.FindModelListener
|
||||||
import com.intellij.ide.actions.ApplyIntentionAction
|
import com.intellij.ide.actions.ApplyIntentionAction
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
@@ -52,7 +53,6 @@ import com.maddyhome.idea.vim.group.NotificationService
|
|||||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
|
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
|
||||||
import com.maddyhome.idea.vim.helper.exitSelectMode
|
import com.maddyhome.idea.vim.helper.exitSelectMode
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.hasVisualSelection
|
|
||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||||
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
|
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
|
||||||
import com.maddyhome.idea.vim.helper.vimInitialised
|
import com.maddyhome.idea.vim.helper.vimInitialised
|
||||||
@@ -211,6 +211,10 @@ internal object IdeaSpecifics {
|
|||||||
KeyHandler.getInstance().reset(it.vim)
|
KeyHandler.getInstance().reset(it.vim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (action is NextVariableAction && TemplateManagerImpl.getTemplateState(editor) == null) {
|
||||||
|
editor.vim.exitInsertMode(event.dataContext.vim)
|
||||||
|
KeyHandler.getInstance().reset(editor.vim)
|
||||||
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
if (caretOffset != -1 && caretOffset != editor.caretModel.offset) {
|
if (caretOffset != -1 && caretOffset != editor.caretModel.offset) {
|
||||||
@@ -413,23 +417,6 @@ internal object IdeaSpecifics {
|
|||||||
vimEditor.exitMode()
|
vimEditor.exitMode()
|
||||||
vimEditor.mode = Mode.NORMAL()
|
vimEditor.mode = Mode.NORMAL()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// IdeaSelectionControl will not be called if we're moving to a new variable with no change in selection.
|
|
||||||
// And if we're moving to the end of the template, the change in selection will reset us to Normal because
|
|
||||||
// IdeaSelectionControl will be called when the template is no longer active.
|
|
||||||
if ((!editor.selectionModel.hasSelection() && !vimEditor.mode.hasVisualSelection) || newIndex == -1) {
|
|
||||||
if (vimEditor.isIdeaRefactorModeSelect) {
|
|
||||||
if (vimEditor.mode !is Mode.INSERT) {
|
|
||||||
vimEditor.exitMode()
|
|
||||||
injector.application.runReadAction {
|
|
||||||
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
|
||||||
VimPlugin.getChange().insertBeforeCaret(editor.vim, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vimEditor.mode = Mode.NORMAL()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -405,16 +405,18 @@ object VimListenerManager {
|
|||||||
editorGroup?.editorDeinit(editor.vim)
|
editorGroup?.editorDeinit(editor.vim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
if (vimDisabled(editor)) {
|
||||||
|
remove(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(editor: Editor) {
|
fun remove(editor: Editor) {
|
||||||
val editorDisposable = editor.removeUserData(editorListenersDisposableKey)
|
val editorDisposable = editor.removeUserData(editorListenersDisposableKey)
|
||||||
if (editorDisposable != null) {
|
if (editorDisposable != null) {
|
||||||
Disposer.dispose(editorDisposable)
|
Disposer.dispose(editorDisposable)
|
||||||
} else if (!EditorHelper.isCommandHistoryWindow(editor)) {
|
|
||||||
// Cmdwin editors (q:, q/, q?) may bypass `add` in some test paths; that's expected.
|
|
||||||
// We definitely do not expect this to happen for any other editor.
|
|
||||||
StrictMode.fail("Editor doesn't have disposable attached. $editor")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -529,7 +531,7 @@ object VimListenerManager {
|
|||||||
|
|
||||||
MotionGroup.fileEditorManagerSelectionChangedCallback(event)
|
MotionGroup.fileEditorManagerSelectionChangedCallback(event)
|
||||||
FileGroupHelper.fileEditorManagerSelectionChangedCallback(event)
|
FileGroupHelper.fileEditorManagerSelectionChangedCallback(event)
|
||||||
VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event)
|
// VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event)
|
||||||
IjVimRedrawService.fileEditorManagerSelectionChangedCallback(event)
|
IjVimRedrawService.fileEditorManagerSelectionChangedCallback(event)
|
||||||
VimLastSelectedEditorTracker.setLastSelectedEditor(event.newEditor)
|
VimLastSelectedEditorTracker.setLastSelectedEditor(event.newEditor)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -657,6 +657,10 @@ class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase() {
|
|||||||
get() = ijFoldRegion.endOffset
|
get() = ijFoldRegion.endOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSoftWrapStartAtOffset(offset: Int): Int? {
|
||||||
|
return editor.softWrapModel.getSoftWrap(offset)?.start
|
||||||
|
}
|
||||||
|
|
||||||
override fun <T : ImmutableVimCaret> findLastVersionOfCaret(caret: T): T {
|
override fun <T : ImmutableVimCaret> findLastVersionOfCaret(caret: T): T {
|
||||||
return caret
|
return caret
|
||||||
}
|
}
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.maddyhome.idea.vim.vimscript.model.functions.handlers
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
|
import com.intellij.openapi.actionSystem.impl.PresentationFactory
|
||||||
|
import com.intellij.openapi.actionSystem.impl.Utils
|
||||||
|
import com.intellij.openapi.keymap.impl.ActionProcessor
|
||||||
|
import com.intellij.vim.annotations.VimscriptFunction
|
||||||
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
||||||
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
|
||||||
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.asVimInt
|
||||||
|
import com.maddyhome.idea.vim.vimscript.model.functions.BuiltinFunctionHandler
|
||||||
|
import java.awt.event.KeyEvent
|
||||||
|
|
||||||
|
@VimscriptFunction(name = "isactionenabled")
|
||||||
|
internal class IsActionEnabled : BuiltinFunctionHandler<VimInt>() {
|
||||||
|
override fun doFunction(
|
||||||
|
arguments: Arguments,
|
||||||
|
editor: VimEditor,
|
||||||
|
context: ExecutionContext,
|
||||||
|
vimContext: VimLContext,
|
||||||
|
): VimInt {
|
||||||
|
val action = ActionManager.getInstance().getAction(arguments.getString(0).value)
|
||||||
|
if (action == null) {
|
||||||
|
return false.asVimInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
val presentationFactory = PresentationFactory()
|
||||||
|
val wrappedContext = Utils.createAsyncDataContext(context.ij)
|
||||||
|
val actionProcessor = object : ActionProcessor() {}
|
||||||
|
val inputEventAdjusted = KeyEvent(editor.ij.contentComponent, KeyEvent.KEY_PRESSED, 0L, 0, KeyEvent.VK_UNDEFINED, '\u0000')
|
||||||
|
|
||||||
|
val updateEvent = Utils.runUpdateSessionForInputEvent(listOf(action), inputEventAdjusted, wrappedContext, "IdeaVim", actionProcessor, presentationFactory) { _, updater, events ->
|
||||||
|
val presentation = updater(action)
|
||||||
|
events[presentation]
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = updateEvent != null && updateEvent.presentation.isEnabled
|
||||||
|
return result.asVimInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,5 +118,10 @@
|
|||||||
"keys": "gJ",
|
"keys": "gJ",
|
||||||
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction",
|
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction",
|
||||||
"modes": "X"
|
"modes": "X"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": "z@",
|
||||||
|
"class": "com.maddyhome.idea.vim.action.macro.PlaybackRegisterInOpenFilesAction",
|
||||||
|
"modes": "N"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler",
|
"has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler",
|
||||||
|
"isactionenabled": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.IsActionEnabled",
|
||||||
"pumvisible": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.PopupMenuVisibleFunctionHandler"
|
"pumvisible": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.PopupMenuVisibleFunctionHandler"
|
||||||
}
|
}
|
||||||
+1
-2
@@ -115,8 +115,7 @@ class ProcessSearchEntryAction(private val parentAction: ProcessExEntryAction) :
|
|||||||
val offsetAndMotion = injector.searchGroup.processSearchCommand(
|
val offsetAndMotion = injector.searchGroup.processSearchCommand(
|
||||||
editor, argument.string, caret.offset, count, direction
|
editor, argument.string, caret.offset, count, direction
|
||||||
)
|
)
|
||||||
// Vim doesn't treat not finding something as an error, although it might report either an error or warning message
|
if (offsetAndMotion == null) return Motion.Error
|
||||||
if (offsetAndMotion == null) return Motion.NoMotion
|
|
||||||
parentAction.motionType = offsetAndMotion.second
|
parentAction.motionType = offsetAndMotion.second
|
||||||
return offsetAndMotion.first.toMotionOrError()
|
return offsetAndMotion.first.toMotionOrError()
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -31,6 +31,12 @@ class MotionCamelLeftAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
|
if (caret.hasSelection() && caret.offset > caret.vimSelectionStart) {
|
||||||
|
val target = injector.searchHelper.findPreviousCamelEnd(editor.text(), caret.offset, operatorArguments.count1)
|
||||||
|
if (target != null && target > caret.vimSelectionStart) {
|
||||||
|
return target.toMotionOrError()
|
||||||
|
}
|
||||||
|
}
|
||||||
return injector.searchHelper.findPreviousCamelStart(editor.text(), caret.offset, operatorArguments.count1)
|
return injector.searchHelper.findPreviousCamelStart(editor.text(), caret.offset, operatorArguments.count1)
|
||||||
?.toMotionOrError() ?: Motion.Error
|
?.toMotionOrError() ?: Motion.Error
|
||||||
}
|
}
|
||||||
@@ -47,6 +53,10 @@ class MotionCamelRightAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
|
if (caret.hasSelection() && caret.offset >= caret.vimSelectionStart) {
|
||||||
|
return injector.searchHelper.findNextCamelEnd(editor.text(), caret.offset + 1, operatorArguments.count1)
|
||||||
|
?.toMotionOrError() ?: Motion.Error
|
||||||
|
}
|
||||||
return injector.searchHelper.findNextCamelStart(editor.text(), caret.offset + 1, operatorArguments.count1)
|
return injector.searchHelper.findNextCamelStart(editor.text(), caret.offset + 1, operatorArguments.count1)
|
||||||
?.toMotionOrError() ?: Motion.Error
|
?.toMotionOrError() ?: Motion.Error
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -70,6 +70,6 @@ class MotionDownNotLineWiseAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
return injector.motion.getVerticalMotionOffset(editor, caret, operatorArguments.count1)
|
return injector.motion.getVerticalMotionOffset(editor, caret, operatorArguments.count1, bufferLines = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -70,6 +70,6 @@ class MotionUpNotLineWiseAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
return injector.motion.getVerticalMotionOffset(editor, caret, -operatorArguments.count1)
|
return injector.motion.getVerticalMotionOffset(editor, caret, -operatorArguments.count1, bufferLines = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.maddyhome.idea.vim.register.Register
|
|||||||
import com.maddyhome.idea.vim.register.RegisterConstants
|
import com.maddyhome.idea.vim.register.RegisterConstants
|
||||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase
|
import com.maddyhome.idea.vim.register.VimRegisterGroupBase
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
import com.maddyhome.idea.vim.yank.MultiCaretJoinedText
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
abstract class VimCaretBase : VimCaret
|
abstract class VimCaretBase : VimCaret
|
||||||
@@ -67,6 +68,11 @@ open class CaretRegisterStorageBase(override var caret: ImmutableVimCaret) : Car
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register? {
|
override fun getRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register? {
|
||||||
|
val multiCaretJoinRegister = injector.registerGroup.getRegister(editor, context, RegisterConstants.MULTICARET_JOIN_REGISTER)
|
||||||
|
val multiCaretJoinedText = multiCaretJoinRegister?.copiedText as? MultiCaretJoinedText
|
||||||
|
if (multiCaretJoinedText != null && multiCaretJoinedText.carets.let { it > 0 && it != editor.carets().size }) {
|
||||||
|
return multiCaretJoinRegister
|
||||||
|
}
|
||||||
if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
|
if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
|
||||||
return injector.registerGroup.getRegister(editor, context, r)
|
return injector.registerGroup.getRegister(editor, context, r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ interface VimEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createIndentBySize(size: Int): String
|
fun createIndentBySize(size: Int): String
|
||||||
|
fun getSoftWrapStartAtOffset(offset: Int): Int?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the collapsed fold region at the specified offset, if any.
|
* Returns the collapsed fold region at the specified offset, if any.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ interface VimMotionGroup {
|
|||||||
allowWrap: Boolean = false,
|
allowWrap: Boolean = false,
|
||||||
): Motion
|
): Motion
|
||||||
|
|
||||||
fun getVerticalMotionOffset(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Motion
|
fun getVerticalMotionOffset(editor: VimEditor, caret: ImmutableVimCaret, count: Int, bufferLines: Boolean = false): Motion
|
||||||
|
|
||||||
// TODO: Consider naming. These don't move the caret, but calculate offsets. Also consider returning Motion
|
// TODO: Consider naming. These don't move the caret, but calculate offsets. Also consider returning Motion
|
||||||
|
|
||||||
|
|||||||
@@ -38,14 +38,18 @@ abstract class VimMotionGroupBase : VimMotionGroup {
|
|||||||
override var lastFTCmd: TillCharacterMotionType = TillCharacterMotionType.LAST_SMALL_T
|
override var lastFTCmd: TillCharacterMotionType = TillCharacterMotionType.LAST_SMALL_T
|
||||||
override var lastFTChar: Char = ' '
|
override var lastFTChar: Char = ' '
|
||||||
|
|
||||||
override fun getVerticalMotionOffset(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Motion {
|
override fun getVerticalMotionOffset(editor: VimEditor, caret: ImmutableVimCaret, count: Int, bufferLines: Boolean): Motion {
|
||||||
val pos = caret.getVisualPosition()
|
val pos = caret.getVisualPosition()
|
||||||
if ((pos.line == 0 && count < 0) || (pos.line >= editor.getVisualLineCount() - 1 && count > 0)) {
|
if ((pos.line == 0 && count < 0) || (pos.line >= editor.getVisualLineCount() - 1 && count > 0)) {
|
||||||
return Motion.Error
|
return Motion.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
val intendedColumn = caret.vimLastColumn
|
val intendedColumn = caret.vimLastColumn
|
||||||
val line = editor.normalizeVisualLine(pos.line + count)
|
val line = if (bufferLines)
|
||||||
|
// TODO Does not work with folds, but I don't use those.
|
||||||
|
editor.normalizeVisualLine(editor.bufferLineToVisualLine(editor.visualLineToBufferLine(pos.line) + count))
|
||||||
|
else
|
||||||
|
editor.normalizeVisualLine(pos.line + count)
|
||||||
|
|
||||||
if (intendedColumn == LAST_COLUMN) {
|
if (intendedColumn == LAST_COLUMN) {
|
||||||
val normalisedColumn = injector.engineEditorHelper.normalizeVisualColumn(
|
val normalisedColumn = injector.engineEditorHelper.normalizeVisualColumn(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
|||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimCaretListener
|
import com.maddyhome.idea.vim.api.VimCaretListener
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
@@ -247,7 +248,15 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
|
|||||||
StrictMode.assert(caret.isPrimary, "Block selection mode must only operate on primary caret")
|
StrictMode.assert(caret.isPrimary, "Block selection mode must only operate on primary caret")
|
||||||
}
|
}
|
||||||
|
|
||||||
val normalisedOffset = prepareMoveToAbsoluteOffset(editor, cmd, offset)
|
val normalisedOffset = prepareMoveToAbsoluteOffset(editor, cmd, offset).let {
|
||||||
|
if (offset.intendedColumn == VimMotionGroupBase.LAST_COLUMN) {
|
||||||
|
val softWrapStart = editor.getSoftWrapStartAtOffset(it)
|
||||||
|
if (softWrapStart != null) softWrapStart - 1 else it
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
StrictMode.assert(normalisedOffset == offset.offset, "Adjusted offset should be normalised by action")
|
StrictMode.assert(normalisedOffset == offset.offset, "Adjusted offset should be normalised by action")
|
||||||
|
|
||||||
// Set before moving, so it can be applied during move, especially important for LAST_COLUMN and visual block mode
|
// Set before moving, so it can be applied during move, especially important for LAST_COLUMN and visual block mode
|
||||||
@@ -295,7 +304,7 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
|
|||||||
foldRegion.isExpanded = true
|
foldRegion.isExpanded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultOffset
|
return resultOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -792,6 +792,10 @@ class VimRegex(pattern: String) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSoftWrapStartAtOffset(offset: Int): Int? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAllFoldRegions(): List<VimFoldRegion> {
|
override fun getAllFoldRegions(): List<VimFoldRegion> {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ object RegisterConstants {
|
|||||||
const val CLIPBOARD_REGISTERS: String = CLIPBOARD_REGISTER.toString() + PRIMARY_REGISTER
|
const val CLIPBOARD_REGISTERS: String = CLIPBOARD_REGISTER.toString() + PRIMARY_REGISTER
|
||||||
|
|
||||||
const val NUMBERED_REGISTERS: String = "0123456789"
|
const val NUMBERED_REGISTERS: String = "0123456789"
|
||||||
const val NAMED_REGISTERS: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
const val MULTICARET_JOIN_REGISTER: Char = '\u0000'
|
||||||
|
const val NAMED_REGISTERS: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + MULTICARET_JOIN_REGISTER
|
||||||
|
|
||||||
const val WRITABLE_REGISTERS: String = (
|
const val WRITABLE_REGISTERS: String = (
|
||||||
NUMBERED_REGISTERS + NAMED_REGISTERS + CLIPBOARD_REGISTERS +
|
NUMBERED_REGISTERS + NAMED_REGISTERS + CLIPBOARD_REGISTERS +
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.maddyhome.idea.vim.yank
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.common.VimCopiedText
|
||||||
|
|
||||||
|
data class MultiCaretJoinedText(val carets: Int, override val text: String): VimCopiedText {
|
||||||
|
companion object {
|
||||||
|
val EMPTY = MultiCaretJoinedText(0, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateText(newText: String): VimCopiedText {
|
||||||
|
return MultiCaretJoinedText(carets, newText)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,12 +16,15 @@ import com.maddyhome.idea.vim.api.VimEditor
|
|||||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
||||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
||||||
|
import com.maddyhome.idea.vim.api.getText
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
import com.maddyhome.idea.vim.command.MotionType
|
import com.maddyhome.idea.vim.command.MotionType
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
||||||
|
import com.maddyhome.idea.vim.register.Register
|
||||||
|
import com.maddyhome.idea.vim.register.RegisterConstants
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@@ -42,6 +45,20 @@ open class YankGroupBase : VimYankGroup {
|
|||||||
for ((caret, myRange) in caretToRange) {
|
for ((caret, myRange) in caretToRange) {
|
||||||
result = caret.registerStorage.storeText(editor, context, myRange.first, myRange.second, false) && result
|
result = caret.registerStorage.storeText(editor, context, myRange.first, myRange.second, false) && result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val selectionType = caretToRange.values.first().second
|
||||||
|
val multiCaretJoinedText: MultiCaretJoinedText
|
||||||
|
if (result && caretToRange.size > 1) {
|
||||||
|
val joinedText = caretToRange.entries.sortedBy { it.key.offset }.joinToString("\n") { editor.getText(it.value.first).trimEnd('\n') }
|
||||||
|
multiCaretJoinedText = MultiCaretJoinedText(caretToRange.size, joinedText)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
multiCaretJoinedText = MultiCaretJoinedText.EMPTY
|
||||||
|
}
|
||||||
|
injector.registerGroup.saveRegister(editor, context, RegisterConstants.MULTICARET_JOIN_REGISTER, Register(
|
||||||
|
RegisterConstants.MULTICARET_JOIN_REGISTER, multiCaretJoinedText, selectionType)
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user