mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-04 10:40:10 +01:00 
			
		
		
		
	Compare commits
	
		
			10 Commits
		
	
	
		
			customized
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						edb9b194bb
	
				 | 
					
					
						|||
| 
						 | 
					
						
						
							
						
						eae7ed95e2
	
				 | 
					
					
						||
| 
						
						
							
						
						a1e2ae0eb9
	
				 | 
					
					
						|||
| 
						
						
							
						
						eae2e3b6b8
	
				 | 
					
					
						|||
| 
						
						
							
						
						c2d997a520
	
				 | 
					
					
						|||
| 
						
						
							
						
						e2a8a3c21a
	
				 | 
					
					
						|||
| 
						
						
							
						
						9b7fee6163
	
				 | 
					
					
						|||
| 
						
						
							
						
						d0f9d3dc70
	
				 | 
					
					
						|||
| 
						
						
							
						
						8d3a69b338
	
				 | 
					
					
						|||
| 
						
						
							
						
						3c530474a1
	
				 | 
					
					
						
@@ -1,9 +1,9 @@
 | 
			
		||||
# suppress inspection "UnusedProperty" for whole file
 | 
			
		||||
 | 
			
		||||
ideaVersion=LATEST-EAP-SNAPSHOT
 | 
			
		||||
ideaVersion=2022.1.2
 | 
			
		||||
downloadIdeaSources=true
 | 
			
		||||
instrumentPluginCode=true
 | 
			
		||||
version=SNAPSHOT
 | 
			
		||||
version=chylex-12
 | 
			
		||||
javaVersion=11
 | 
			
		||||
remoteRobotVersion=0.11.10
 | 
			
		||||
antlrVersion=4.10.1
 | 
			
		||||
 
 | 
			
		||||
@@ -149,6 +149,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
			
		||||
 | 
			
		||||
      if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false
 | 
			
		||||
      
 | 
			
		||||
      if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT) return false
 | 
			
		||||
      if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) return false
 | 
			
		||||
      if (keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END) return false
 | 
			
		||||
 | 
			
		||||
      if (editor.inInsertMode) {
 | 
			
		||||
        if (keyCode == KeyEvent.VK_TAB) {
 | 
			
		||||
          // TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view
 | 
			
		||||
 
 | 
			
		||||
@@ -233,7 +233,7 @@ private object FileTypePatterns {
 | 
			
		||||
    } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") {
 | 
			
		||||
      this.cMakePatterns
 | 
			
		||||
    } else {
 | 
			
		||||
      return null
 | 
			
		||||
      this.htmlPatterns
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,12 +22,14 @@ import com.intellij.openapi.application.runWriteAction
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimChangeGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.command.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.command.MappingMode
 | 
			
		||||
import com.maddyhome.idea.vim.command.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.common.TextRange
 | 
			
		||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
 | 
			
		||||
@@ -37,9 +39,9 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
 | 
			
		||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
			
		||||
import com.maddyhome.idea.vim.helper.editorMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
 | 
			
		||||
import com.maddyhome.idea.vim.key.OperatorFunction
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
@@ -84,22 +86,20 @@ class VimSurroundExtension : VimExtension {
 | 
			
		||||
    override val isRepeatable = true
 | 
			
		||||
 | 
			
		||||
    override fun execute(editor: VimEditor, context: ExecutionContext) {
 | 
			
		||||
      setOperatorFunction(Operator())
 | 
			
		||||
      setOperatorFunction(Operator(supportsMultipleCursors = false)) // TODO
 | 
			
		||||
      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class VSurroundHandler : ExtensionHandler {
 | 
			
		||||
    override fun execute(editor: VimEditor, context: ExecutionContext) {
 | 
			
		||||
      val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
 | 
			
		||||
      // NB: Operator ignores SelectionType anyway
 | 
			
		||||
      if (!Operator().apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) {
 | 
			
		||||
      if (!Operator(supportsMultipleCursors = true).apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      runWriteAction {
 | 
			
		||||
        // Leave visual mode
 | 
			
		||||
        executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
 | 
			
		||||
        editor.ij.caretModel.moveToOffset(selectionStart)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -120,6 +120,10 @@ class VimSurroundExtension : VimExtension {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
      fun change(editor: Editor, charFrom: Char, newSurround: Pair<String, String>?) {
 | 
			
		||||
        editor.runWithEveryCaretAndRestore { changeAtCaret(editor, charFrom, newSurround) }
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      fun changeAtCaret(editor: Editor, charFrom: Char, newSurround: Pair<String, String>?) {
 | 
			
		||||
        // We take over the " register, so preserve it
 | 
			
		||||
        val oldValue: List<KeyStroke>? = getRegister(REGISTER)
 | 
			
		||||
        // Empty the " register
 | 
			
		||||
@@ -184,26 +188,44 @@ class VimSurroundExtension : VimExtension {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class Operator : OperatorFunction {
 | 
			
		||||
  private class Operator(private val supportsMultipleCursors: Boolean) : OperatorFunction {
 | 
			
		||||
    override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
 | 
			
		||||
      val c = getChar(editor)
 | 
			
		||||
      if (c.code == 0) return true
 | 
			
		||||
 | 
			
		||||
      val pair = getOrInputPair(c, editor) ?: return false
 | 
			
		||||
      // XXX: Will it work with line-wise or block-wise selections?
 | 
			
		||||
      val range = getSurroundRange(editor) ?: return false
 | 
			
		||||
 | 
			
		||||
      runWriteAction {
 | 
			
		||||
        val change = VimPlugin.getChange()
 | 
			
		||||
        val leftSurround = pair.first
 | 
			
		||||
        val primaryCaret = editor.caretModel.primaryCaret
 | 
			
		||||
        change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, leftSurround)
 | 
			
		||||
        change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + leftSurround.length, pair.second)
 | 
			
		||||
        if (supportsMultipleCursors) {
 | 
			
		||||
          editor.runWithEveryCaretAndRestore {
 | 
			
		||||
            applyOnce(editor, change, pair)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          applyOnce(editor, change, pair)
 | 
			
		||||
          // Jump back to start
 | 
			
		||||
          executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>) {
 | 
			
		||||
      // XXX: Will it work with line-wise or block-wise selections?
 | 
			
		||||
      val range = getSurroundRange(editor)
 | 
			
		||||
      if (range != null) {
 | 
			
		||||
        val primaryCaret = editor.caretModel.primaryCaret
 | 
			
		||||
        change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, pair.first)
 | 
			
		||||
        change.insertText(
 | 
			
		||||
          IjVimEditor(editor),
 | 
			
		||||
          IjVimCaret(primaryCaret),
 | 
			
		||||
          range.endOffset + pair.first.length,
 | 
			
		||||
          pair.second
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) {
 | 
			
		||||
      VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim)
 | 
			
		||||
      VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) }
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,10 @@ import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.*;
 | 
			
		||||
import com.maddyhome.idea.vim.key.KeyHandlerKeeper;
 | 
			
		||||
import com.maddyhome.idea.vim.listener.VimInsertListener;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.*;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjExecutionContext;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjExecutionContextKt;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
			
		||||
import com.maddyhome.idea.vim.options.OptionConstants;
 | 
			
		||||
import com.maddyhome.idea.vim.options.OptionScope;
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
 | 
			
		||||
@@ -200,6 +203,61 @@ public class ChangeGroup extends VimChangeGroupBase {
 | 
			
		||||
    return new Pair<>(range, type);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Delete the range of text.
 | 
			
		||||
   *
 | 
			
		||||
   * @param editor   The editor to delete the text from
 | 
			
		||||
   * @param caret    The caret to be moved after deletion
 | 
			
		||||
   * @param range    The range to delete
 | 
			
		||||
   * @param type     The type of deletion
 | 
			
		||||
   * @param isChange Is from a change action
 | 
			
		||||
   * @return true if able to delete the text, false if not
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public boolean deleteRange(@NotNull VimEditor editor,
 | 
			
		||||
                             @NotNull VimCaret caret,
 | 
			
		||||
                             @NotNull TextRange range,
 | 
			
		||||
                             @Nullable SelectionType type,
 | 
			
		||||
                             boolean isChange,
 | 
			
		||||
                             boolean noYank) {
 | 
			
		||||
 | 
			
		||||
    // Update the last column before we delete, or we might be retrieving the data for a line that no longer exists
 | 
			
		||||
    UserDataManager.setVimLastColumn(((IjVimCaret) caret).getCaret(), InlayHelperKt.getInlayAwareVisualColumn(((IjVimCaret) caret).getCaret()));
 | 
			
		||||
 | 
			
		||||
    boolean removeLastNewLine = removeLastNewLine(editor, range, type);
 | 
			
		||||
    final boolean res = deleteText(editor, range, type, noYank);
 | 
			
		||||
    if (removeLastNewLine) {
 | 
			
		||||
      int textLength = ((IjVimEditor) editor).getEditor().getDocument().getTextLength();
 | 
			
		||||
      ((IjVimEditor) editor).getEditor().getDocument().deleteString(textLength - 1, textLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (res) {
 | 
			
		||||
      int pos = EditorHelper.normalizeOffset(((IjVimEditor) editor).getEditor(), range.getStartOffset(), isChange);
 | 
			
		||||
      if (type == SelectionType.LINE_WISE) {
 | 
			
		||||
        pos = VimPlugin.getMotion()
 | 
			
		||||
          .moveCaretToLineWithStartOfLineOption(editor, editor.offsetToLogicalPosition(pos).getLine(),
 | 
			
		||||
                                                caret);
 | 
			
		||||
      }
 | 
			
		||||
      injector.getMotion().moveCaret(editor, caret, pos);
 | 
			
		||||
    }
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private boolean removeLastNewLine(@NotNull VimEditor editor, @NotNull TextRange range, @Nullable SelectionType type) {
 | 
			
		||||
    int endOffset = range.getEndOffset();
 | 
			
		||||
    int fileSize = EditorHelperRt.getFileSize(((IjVimEditor) editor).getEditor());
 | 
			
		||||
    if (endOffset > fileSize) {
 | 
			
		||||
      if (injector.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, OptionConstants.ideastrictmodeName, OptionConstants.ideastrictmodeName)) {
 | 
			
		||||
        throw new IllegalStateException("Incorrect offset. File size: " + fileSize + ", offset: " + endOffset);
 | 
			
		||||
      }
 | 
			
		||||
      endOffset = fileSize;
 | 
			
		||||
    }
 | 
			
		||||
    return type == SelectionType.LINE_WISE &&
 | 
			
		||||
           range.getStartOffset() != 0 &&
 | 
			
		||||
           ((IjVimEditor) editor).getEditor().getDocument().getCharsSequence().charAt(endOffset - 1) != '\n' &&
 | 
			
		||||
           endOffset == fileSize;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void insertLineAround(@NotNull VimEditor editor, @NotNull ExecutionContext context, int shift) {
 | 
			
		||||
    com.maddyhome.idea.vim.newapi.ChangeGroupKt.insertLineAround(editor, context, shift);
 | 
			
		||||
@@ -228,7 +286,8 @@ public class ChangeGroup extends VimChangeGroupBase {
 | 
			
		||||
                              @NotNull VimCaret caret,
 | 
			
		||||
                              @NotNull ExecutionContext context,
 | 
			
		||||
                              @NotNull Argument argument,
 | 
			
		||||
                              @NotNull OperatorArguments operatorArguments) {
 | 
			
		||||
                              @NotNull OperatorArguments operatorArguments,
 | 
			
		||||
                              boolean noYank) {
 | 
			
		||||
    int count0 = operatorArguments.getCount0();
 | 
			
		||||
    // Vim treats cw as ce and cW as cE if cursor is on a non-blank character
 | 
			
		||||
    final Command motion = argument.getMotion();
 | 
			
		||||
@@ -306,7 +365,7 @@ public class ChangeGroup extends VimChangeGroupBase {
 | 
			
		||||
      Pair<TextRange, SelectionType> deleteRangeAndType =
 | 
			
		||||
        getDeleteRangeAndType(editor, caret, context, argument, true, operatorArguments.withCount0(count0));
 | 
			
		||||
      if (deleteRangeAndType == null) return false;
 | 
			
		||||
      return changeRange(editor, caret, deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), context);
 | 
			
		||||
      return changeRange(editor, caret, deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), context, noYank);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -436,7 +495,8 @@ public class ChangeGroup extends VimChangeGroupBase {
 | 
			
		||||
                             @NotNull VimCaret caret,
 | 
			
		||||
                             @NotNull TextRange range,
 | 
			
		||||
                             @NotNull SelectionType type,
 | 
			
		||||
                             ExecutionContext context) {
 | 
			
		||||
                             @Nullable ExecutionContext context,
 | 
			
		||||
                             boolean noYank) {
 | 
			
		||||
    int col = 0;
 | 
			
		||||
    int lines = 0;
 | 
			
		||||
    if (type == SelectionType.BLOCK_WISE) {
 | 
			
		||||
@@ -450,7 +510,7 @@ public class ChangeGroup extends VimChangeGroupBase {
 | 
			
		||||
 | 
			
		||||
    final VimLogicalPosition lp = editor.offsetToLogicalPosition(injector.getMotion().moveCaretToLineStartSkipLeading(editor, caret));
 | 
			
		||||
 | 
			
		||||
    boolean res = deleteRange(editor, caret, range, type, true);
 | 
			
		||||
    boolean res = deleteRange(editor, caret, range, type, true, noYank);
 | 
			
		||||
    if (res) {
 | 
			
		||||
      if (type == SelectionType.LINE_WISE) {
 | 
			
		||||
        // Please don't use `getDocument().getText().isEmpty()` because it converts CharSequence into String
 | 
			
		||||
@@ -675,7 +735,7 @@ public class ChangeGroup extends VimChangeGroupBase {
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            if (pos > wsoff) {
 | 
			
		||||
              deleteText(editor, new TextRange(wsoff, pos), null);
 | 
			
		||||
              deleteText(editor, new TextRange(wsoff, pos), null, false);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,7 @@ public class ProcessGroup extends VimProcessGroupBase {
 | 
			
		||||
    String initText = getRange(((IjVimEditor) editor).getEditor(), cmd);
 | 
			
		||||
    VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE);
 | 
			
		||||
    ExEntryPanel panel = ExEntryPanel.getInstance();
 | 
			
		||||
    panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, 1);
 | 
			
		||||
    panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, cmd.getCount());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
@@ -123,7 +123,7 @@ public class ProcessGroup extends VimProcessGroupBase {
 | 
			
		||||
 | 
			
		||||
      logger.debug("processing command");
 | 
			
		||||
 | 
			
		||||
      final String text = panel.getText();
 | 
			
		||||
      String text = panel.getText();
 | 
			
		||||
 | 
			
		||||
      if (!panel.getLabel().equals(":")) {
 | 
			
		||||
        // Search is handled via Argument.Type.EX_STRING. Although ProcessExEntryAction is registered as the handler for
 | 
			
		||||
@@ -134,8 +134,16 @@ public class ProcessGroup extends VimProcessGroupBase {
 | 
			
		||||
 | 
			
		||||
      if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread());
 | 
			
		||||
 | 
			
		||||
      int repeat = 1;
 | 
			
		||||
      if (text.contains("raction ")) {
 | 
			
		||||
        text = text.replace("raction ", "action ");
 | 
			
		||||
        repeat = panel.getCount();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (int i = 0; i < repeat; i++) {
 | 
			
		||||
        VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    catch (ExException e) {
 | 
			
		||||
      VimPlugin.showMessage(e.getMessage());
 | 
			
		||||
      VimPlugin.indicateError();
 | 
			
		||||
 
 | 
			
		||||
@@ -97,8 +97,23 @@ class PutGroup : VimPutBase() {
 | 
			
		||||
      EditorHelper.getOrderedCaretsList(editor.ij).map { IjVimCaret(it) }
 | 
			
		||||
    }
 | 
			
		||||
    injector.application.runWriteAction {
 | 
			
		||||
      val singleCaret = myCarets.singleOrNull()
 | 
			
		||||
      if (singleCaret != null) {
 | 
			
		||||
        putForCaret(editor, singleCaret, data, additionalData, context, text)
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        val lines = text.text.split('\n')
 | 
			
		||||
        if (lines.size != myCarets.size) {
 | 
			
		||||
          myCarets.forEach { caret -> putForCaret(editor, caret, data, additionalData, context, text) }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          myCarets.asReversed().forEachIndexed { index, caret ->
 | 
			
		||||
            val line = ProcessedTextData(lines[index], text.typeInRegister, text.transferableData)
 | 
			
		||||
            putForCaret(editor, caret, data, additionalData, context, line)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun putForCaret(
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ package com.maddyhome.idea.vim.helper
 | 
			
		||||
 | 
			
		||||
import com.intellij.codeWithMe.ClientId
 | 
			
		||||
import com.intellij.openapi.editor.Caret
 | 
			
		||||
import com.intellij.openapi.editor.CaretState
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
			
		||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
			
		||||
@@ -106,3 +107,41 @@ val Caret.vimLine: Int
 | 
			
		||||
 */
 | 
			
		||||
val Editor.vimLine: Int
 | 
			
		||||
  get() = this.caretModel.currentCaret.vimLine
 | 
			
		||||
 | 
			
		||||
inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) {
 | 
			
		||||
  val caretModel = this.caretModel
 | 
			
		||||
  val carets = if (this.inBlockSubMode) null else caretModel.allCarets
 | 
			
		||||
  if (carets == null || carets.size == 1) {
 | 
			
		||||
    action()
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    var initialDocumentSize = this.document.textLength
 | 
			
		||||
    var documentSizeDifference = 0
 | 
			
		||||
 | 
			
		||||
    val caretOffsets = carets.map { it.selectionStart to it.selectionEnd }
 | 
			
		||||
    val restoredCarets = mutableListOf<CaretState>()
 | 
			
		||||
 | 
			
		||||
    caretModel.removeSecondaryCarets()
 | 
			
		||||
    
 | 
			
		||||
    for ((selectionStart, selectionEnd) in caretOffsets) {
 | 
			
		||||
      if (selectionStart == selectionEnd) {
 | 
			
		||||
        caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference)
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        caretModel.primaryCaret.setSelection(
 | 
			
		||||
          selectionStart + documentSizeDifference,
 | 
			
		||||
          selectionEnd + documentSizeDifference
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      action()
 | 
			
		||||
      restoredCarets.add(caretModel.caretsAndSelections.single())
 | 
			
		||||
 | 
			
		||||
      val documentLength = this.document.textLength
 | 
			
		||||
      documentSizeDifference += documentLength - initialDocumentSize
 | 
			
		||||
      initialDocumentSize = documentLength
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    caretModel.caretsAndSelections = restoredCarets
 | 
			
		||||
  } 
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,9 +25,7 @@ import com.intellij.openapi.command.undo.UndoManager
 | 
			
		||||
import com.intellij.openapi.components.Service
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.common.ChangesListener
 | 
			
		||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.options.OptionScope
 | 
			
		||||
@@ -49,7 +47,7 @@ class UndoRedoHelper : UndoRedoBase() {
 | 
			
		||||
        SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
 | 
			
		||||
      } else {
 | 
			
		||||
        val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
 | 
			
		||||
        performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) })
 | 
			
		||||
        undoManager.undo(fileEditor)
 | 
			
		||||
        editor?.carets()?.forEach {
 | 
			
		||||
          val ijCaret = it.ij
 | 
			
		||||
          val hasSelection = ijCaret.hasSelection()
 | 
			
		||||
@@ -75,30 +73,14 @@ class UndoRedoHelper : UndoRedoBase() {
 | 
			
		||||
        SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
 | 
			
		||||
      } else {
 | 
			
		||||
        val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
 | 
			
		||||
        performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) })
 | 
			
		||||
        undoManager.redo(fileEditor)
 | 
			
		||||
        if (editor?.primaryCaret()?.ij?.hasSelection() == true) {
 | 
			
		||||
          undoManager.redo(fileEditor)
 | 
			
		||||
        }
 | 
			
		||||
        editor?.carets()?.forEach { it.ij.removeSelection() }
 | 
			
		||||
      }
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun performUntilFileChanges(editor: IjVimEditor?, check: () -> Boolean, action: Runnable) {
 | 
			
		||||
    if (editor == null) return
 | 
			
		||||
    val vimDocument = editor.document
 | 
			
		||||
 | 
			
		||||
    val changeListener = object : ChangesListener {
 | 
			
		||||
      var hasChanged = false
 | 
			
		||||
 | 
			
		||||
      override fun documentChanged(change: ChangesListener.Change) {
 | 
			
		||||
        hasChanged = true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vimDocument.addChangeListener(changeListener)
 | 
			
		||||
    while (check() && !changeListener.hasChanged) {
 | 
			
		||||
      action.run()
 | 
			
		||||
    }
 | 
			
		||||
    vimDocument.removeChangeListener(changeListener)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,10 @@
 | 
			
		||||
package com.maddyhome.idea.vim.listener
 | 
			
		||||
 | 
			
		||||
import com.intellij.codeInsight.lookup.Lookup
 | 
			
		||||
import com.intellij.codeInsight.lookup.LookupManager
 | 
			
		||||
import com.intellij.codeInsight.lookup.LookupManagerListener
 | 
			
		||||
import com.intellij.codeInsight.lookup.impl.LookupImpl
 | 
			
		||||
import com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction
 | 
			
		||||
import com.intellij.codeInsight.template.Template
 | 
			
		||||
import com.intellij.codeInsight.template.TemplateEditingAdapter
 | 
			
		||||
import com.intellij.codeInsight.template.TemplateManagerListener
 | 
			
		||||
@@ -35,10 +37,10 @@ import com.intellij.openapi.actionSystem.ex.AnActionListener
 | 
			
		||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.project.DumbAwareToggleAction
 | 
			
		||||
import com.intellij.openapi.util.TextRange
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.group.NotificationService
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorDataContext
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.helper.inNormalMode
 | 
			
		||||
@@ -49,6 +51,8 @@ import com.maddyhome.idea.vim.options.OptionScope
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
 | 
			
		||||
import org.jetbrains.annotations.NonNls
 | 
			
		||||
import java.awt.event.KeyEvent
 | 
			
		||||
import javax.swing.KeyStroke
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Alex Plate
 | 
			
		||||
@@ -60,6 +64,8 @@ object IdeaSpecifics {
 | 
			
		||||
    private val surrounderAction =
 | 
			
		||||
      "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
 | 
			
		||||
    private var editor: Editor? = null
 | 
			
		||||
    private var completionPrevDocumentLength: Int? = null
 | 
			
		||||
    private var completionPrevDocumentOffset: Int? = null
 | 
			
		||||
    override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
 | 
			
		||||
      if (!VimPlugin.isEnabled()) return
 | 
			
		||||
 | 
			
		||||
@@ -68,19 +74,52 @@ object IdeaSpecifics {
 | 
			
		||||
        editor = hostEditor
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      //region Track action id
 | 
			
		||||
      if (VimPlugin.getOptionService().isSet(OptionScope.GLOBAL, OptionConstants.trackactionidsName)) {
 | 
			
		||||
        if (action !is NotificationService.ActionIdNotifier.CopyActionId && action !is NotificationService.ActionIdNotifier.StopTracking) {
 | 
			
		||||
        val id: String? = ActionManager.getInstance().getId(action) ?: (action.shortcutSet as? ProxyShortcutSet)?.actionId
 | 
			
		||||
        VimPlugin.getNotifications(dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (hostEditor != null && action is ChooseItemAction && hostEditor.vimStateMachine?.isRecording == true) {
 | 
			
		||||
        val lookup = LookupManager.getActiveLookup(hostEditor)
 | 
			
		||||
        if (lookup != null) {
 | 
			
		||||
          val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart
 | 
			
		||||
 | 
			
		||||
          val register = VimPlugin.getRegister()
 | 
			
		||||
          val backSpace = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)
 | 
			
		||||
          repeat(charsToRemove) {
 | 
			
		||||
            register.recordKeyStroke(backSpace)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          completionPrevDocumentLength = hostEditor.document.textLength - charsToRemove
 | 
			
		||||
          completionPrevDocumentOffset = lookup.lookupStart
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      //endregion
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
 | 
			
		||||
      if (!VimPlugin.isEnabled()) return
 | 
			
		||||
 | 
			
		||||
      val editor = editor
 | 
			
		||||
      if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
 | 
			
		||||
        val prevDocumentLength = completionPrevDocumentLength
 | 
			
		||||
        val prevDocumentOffset = completionPrevDocumentOffset
 | 
			
		||||
 | 
			
		||||
        if (prevDocumentLength != null && prevDocumentOffset != null) {
 | 
			
		||||
          val register = VimPlugin.getRegister()
 | 
			
		||||
          val addedTextLength = editor.document.textLength - prevDocumentLength
 | 
			
		||||
          val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset)
 | 
			
		||||
          val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)
 | 
			
		||||
 | 
			
		||||
          register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength)))
 | 
			
		||||
          repeat(caretShift.coerceAtLeast(0)) {
 | 
			
		||||
            register.recordKeyStroke(leftArrow)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.completionPrevDocumentLength = null
 | 
			
		||||
        this.completionPrevDocumentOffset = null
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      //region Enter insert mode after surround with if
 | 
			
		||||
      if (surrounderAction == action.javaClass.name && surrounderItems.any {
 | 
			
		||||
        action.templatePresentation.text.endsWith(
 | 
			
		||||
@@ -99,7 +138,7 @@ object IdeaSpecifics {
 | 
			
		||||
      }
 | 
			
		||||
      //endregion
 | 
			
		||||
 | 
			
		||||
      editor = null
 | 
			
		||||
      this.editor = null
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
/*
 | 
			
		||||
 * IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
 | 
			
		||||
 * Copyright (C) 2003-2021 The IdeaVim authors
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim.vimscript.model.functions.handlers
 | 
			
		||||
 | 
			
		||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring
 | 
			
		||||
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.VimDataType
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
 | 
			
		||||
 | 
			
		||||
object RenamingFunctionHandler : FunctionHandler() {
 | 
			
		||||
 | 
			
		||||
  override val name = "renaming"
 | 
			
		||||
  override val minimumNumberOfArguments = 0
 | 
			
		||||
  override val maximumNumberOfArguments = 0
 | 
			
		||||
  
 | 
			
		||||
  override fun doFunction(
 | 
			
		||||
    argumentValues: List<Expression>,
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    vimContext: VimLContext,
 | 
			
		||||
  ): VimDataType {
 | 
			
		||||
    return if (InplaceRefactoring.getActiveInplaceRenamer(editor.ij) == null)
 | 
			
		||||
      VimInt.ZERO
 | 
			
		||||
    else
 | 
			
		||||
      VimInt.ONE
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -14,5 +14,6 @@
 | 
			
		||||
    <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.TolowerFunctionHandler" name="tolower"/>
 | 
			
		||||
    <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ToupperFunctionHandler" name="toupper"/>
 | 
			
		||||
    <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.JoinFunctionHandler" name="join"/>
 | 
			
		||||
    <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.RenamingFunctionHandler" name="renaming"/>
 | 
			
		||||
  </extensions>
 | 
			
		||||
</idea-plugin>
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude">
 | 
			
		||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
 | 
			
		||||
  <name>IdeaVim</name>
 | 
			
		||||
  <id>IdeaVIM</id>
 | 
			
		||||
  <change-notes><![CDATA[
 | 
			
		||||
@@ -65,7 +65,7 @@
 | 
			
		||||
        <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    ]]></description>
 | 
			
		||||
  <version>SNAPSHOT</version>
 | 
			
		||||
  <version>chylex</version>
 | 
			
		||||
  <vendor>JetBrains</vendor>
 | 
			
		||||
 | 
			
		||||
  <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
 | 
			
		||||
 
 | 
			
		||||
@@ -75,28 +75,6 @@ class UndoActionTest : VimTestCase() {
 | 
			
		||||
    assertFalse(hasSelection())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun `test cursor movements do not require additional undo`() {
 | 
			
		||||
    val keys = listOf("a1<Esc>ea2<Esc>ea3<Esc>", "uu")
 | 
			
		||||
    val before = """
 | 
			
		||||
                A Discovery
 | 
			
		||||
 | 
			
		||||
                ${c}I found it in a legendary land
 | 
			
		||||
                all rocks and lavender and tufted grass,
 | 
			
		||||
                where it was settled on some sodden sand
 | 
			
		||||
                hard by the torrent of a mountain pass.
 | 
			
		||||
    """.trimIndent()
 | 
			
		||||
    val after = """
 | 
			
		||||
                A Discovery
 | 
			
		||||
 | 
			
		||||
                I1 found$c it in a legendary land
 | 
			
		||||
                all rocks and lavender and tufted grass,
 | 
			
		||||
                where it was settled on some sodden sand
 | 
			
		||||
                hard by the torrent of a mountain pass.
 | 
			
		||||
    """.trimIndent()
 | 
			
		||||
    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
 | 
			
		||||
    assertFalse(hasSelection())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun hasSelection(): Boolean {
 | 
			
		||||
    val editor = myFixture.editor
 | 
			
		||||
    return editor.caretModel.primaryCaret.hasSelection()
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package com.maddyhome.idea.vim.action.change.change
 | 
			
		||||
 | 
			
		||||
import com.maddyhome.idea.vim.action.copy.YankMotionAction
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
@@ -34,6 +35,19 @@ class ChangeMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
 | 
			
		||||
 | 
			
		||||
  override val duplicateWith: Char = 'c'
 | 
			
		||||
 | 
			
		||||
  private var isMultiCaret = false
 | 
			
		||||
 | 
			
		||||
  override fun baseExecute(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    operatorArguments: OperatorArguments
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    isMultiCaret = YankMotionAction().yankIfMultiCaret(cmd, editor, context, operatorArguments)
 | 
			
		||||
    return super.baseExecute(editor, caret, context, cmd, operatorArguments)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun execute(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
@@ -46,7 +60,8 @@ class ChangeMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
 | 
			
		||||
      caret,
 | 
			
		||||
      context,
 | 
			
		||||
      argument,
 | 
			
		||||
      operatorArguments
 | 
			
		||||
      operatorArguments,
 | 
			
		||||
      noYank = isMultiCaret
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package com.maddyhome.idea.vim.action.change.change
 | 
			
		||||
 | 
			
		||||
import com.maddyhome.idea.vim.action.copy.YankVisualAction
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
@@ -37,6 +38,18 @@ class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
 | 
			
		||||
 | 
			
		||||
  override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MULTIKEY_UNDO, CommandFlags.FLAG_EXIT_VISUAL)
 | 
			
		||||
 | 
			
		||||
  private var isMultiCaret = false
 | 
			
		||||
 | 
			
		||||
  override fun beforeExecution(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    caretsAndSelections: Map<VimCaret, VimSelection>
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    isMultiCaret = YankVisualAction().yankIfMultiCaret(editor, caretsAndSelections)
 | 
			
		||||
    return super.beforeExecution(editor, context, cmd, caretsAndSelections)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun executeAction(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
@@ -50,7 +63,8 @@ class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
 | 
			
		||||
      caret,
 | 
			
		||||
      range.toVimTextRange(false),
 | 
			
		||||
      range.type,
 | 
			
		||||
      context
 | 
			
		||||
      context,
 | 
			
		||||
      noYank = isMultiCaret
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package com.maddyhome.idea.vim.action.change.delete
 | 
			
		||||
 | 
			
		||||
import com.maddyhome.idea.vim.action.copy.YankMotionAction
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
@@ -36,6 +37,19 @@ class DeleteMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
 | 
			
		||||
 | 
			
		||||
  override val duplicateWith: Char = 'd'
 | 
			
		||||
 | 
			
		||||
  private var isMultiCaret = false
 | 
			
		||||
 | 
			
		||||
  override fun baseExecute(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    operatorArguments: OperatorArguments
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    isMultiCaret = YankMotionAction().yankIfMultiCaret(cmd, editor, context, operatorArguments)
 | 
			
		||||
    return super.baseExecute(editor, caret, context, cmd, operatorArguments)
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  override fun execute(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
@@ -53,7 +67,7 @@ class DeleteMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
 | 
			
		||||
      val (first, second) = injector.changeGroup
 | 
			
		||||
        .getDeleteRangeAndType(editor, caret, context, argument, false, operatorArguments)
 | 
			
		||||
        ?: return false
 | 
			
		||||
      return injector.changeGroup.deleteRange(editor, caret, first, second, false)
 | 
			
		||||
      return injector.changeGroup.deleteRange(editor, caret, first, second, false, noYank = isMultiCaret)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package com.maddyhome.idea.vim.action.change.delete
 | 
			
		||||
 | 
			
		||||
import com.maddyhome.idea.vim.action.copy.YankVisualAction
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
@@ -37,6 +38,18 @@ class DeleteVisualAction : VisualOperatorActionHandler.ForEachCaret() {
 | 
			
		||||
 | 
			
		||||
  override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
 | 
			
		||||
 | 
			
		||||
  private var isMultiCaret = false
 | 
			
		||||
 | 
			
		||||
  override fun beforeExecution(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    caretsAndSelections: Map<VimCaret, VimSelection>,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    isMultiCaret = YankVisualAction().yankIfMultiCaret(editor, caretsAndSelections)
 | 
			
		||||
    return super.beforeExecution(editor, context, cmd, caretsAndSelections)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun executeAction(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
@@ -45,7 +58,13 @@ class DeleteVisualAction : VisualOperatorActionHandler.ForEachCaret() {
 | 
			
		||||
    range: VimSelection,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val selectionType = range.type
 | 
			
		||||
    return injector.changeGroup.deleteRange(editor, caret, range.toVimTextRange(false), selectionType, false)
 | 
			
		||||
    return injector.changeGroup.deleteRange(
 | 
			
		||||
      editor,
 | 
			
		||||
      caret,
 | 
			
		||||
      range.toVimTextRange(false),
 | 
			
		||||
      range.type,
 | 
			
		||||
      false,
 | 
			
		||||
      noYank = isMultiCaret
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,8 +38,27 @@ class YankMotionAction : VimActionHandler.SingleExecution(), DuplicableOperatorA
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    return executeYank(cmd, editor, context, operatorArguments)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun executeYank(
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val argument = cmd.argument ?: return false
 | 
			
		||||
    return injector.yank.yankMotion(editor, context, argument, operatorArguments)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun yankIfMultiCaret(cmd: Command, editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments): Boolean {
 | 
			
		||||
    if (editor.nativeCarets().size > 1) {
 | 
			
		||||
      executeYank(cmd, editor, context, operatorArguments)
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,13 @@ class YankVisualAction : VisualOperatorActionHandler.SingleExecution() {
 | 
			
		||||
    cmd: Command,
 | 
			
		||||
    caretsAndSelections: Map<VimCaret, VimSelection>,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    return executeYank(editor, caretsAndSelections)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun executeYank(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caretsAndSelections: Map<VimCaret, VimSelection>,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val selections = caretsAndSelections.values
 | 
			
		||||
    val starts: MutableList<Int> = ArrayList()
 | 
			
		||||
@@ -58,4 +65,14 @@ class YankVisualAction : VisualOperatorActionHandler.SingleExecution() {
 | 
			
		||||
    val endsArray = ends.toIntArray()
 | 
			
		||||
    return injector.yank.yankRange(editor, TextRange(startsArray, endsArray), vimSelection.type, true)
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  fun yankIfMultiCaret(editor: VimEditor, caretsAndSelections: Map<VimCaret, VimSelection>): Boolean {
 | 
			
		||||
    if (caretsAndSelections.size > 1) {
 | 
			
		||||
      executeYank(editor, caretsAndSelections)
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -83,14 +83,14 @@ interface VimChangeGroup {
 | 
			
		||||
 | 
			
		||||
  fun getDeleteRangeAndType2(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, isChange: Boolean, operatorArguments: OperatorArguments): Pair<TextRange, SelectionType>?
 | 
			
		||||
 | 
			
		||||
  fun deleteRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType?, isChange: Boolean): Boolean
 | 
			
		||||
  fun deleteRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType?, isChange: Boolean, noYank: Boolean = false): Boolean
 | 
			
		||||
  fun deleteRange2(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType): Boolean
 | 
			
		||||
 | 
			
		||||
  fun changeCharacters(editor: VimEditor, caret: VimCaret, count: Int): Boolean
 | 
			
		||||
 | 
			
		||||
  fun changeEndOfLine(editor: VimEditor, caret: VimCaret, count: Int): Boolean
 | 
			
		||||
 | 
			
		||||
  fun changeMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, operatorArguments: OperatorArguments): Boolean
 | 
			
		||||
  fun changeMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, operatorArguments: OperatorArguments, noYank: Boolean = false): Boolean
 | 
			
		||||
 | 
			
		||||
  fun changeCaseToggleCharacter(editor: VimEditor, caret: VimCaret, count: Int): Boolean
 | 
			
		||||
 | 
			
		||||
@@ -98,7 +98,7 @@ interface VimChangeGroup {
 | 
			
		||||
 | 
			
		||||
  fun changeCaseRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: Char): Boolean
 | 
			
		||||
 | 
			
		||||
  fun changeRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType, context: ExecutionContext?): Boolean
 | 
			
		||||
  fun changeRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType, context: ExecutionContext?, noYank: Boolean = false): Boolean
 | 
			
		||||
 | 
			
		||||
  fun changeCaseMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext?, type: Char, argument: Argument, operatorArguments: OperatorArguments): Boolean
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,10 @@ import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.command.Argument
 | 
			
		||||
import com.maddyhome.idea.vim.command.Command
 | 
			
		||||
import com.maddyhome.idea.vim.command.CommandFlags
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine.Companion.getInstance
 | 
			
		||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
			
		||||
import com.maddyhome.idea.vim.command.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.command.VimStateMachine.Companion.getInstance
 | 
			
		||||
import com.maddyhome.idea.vim.common.ChangesListener
 | 
			
		||||
import com.maddyhome.idea.vim.common.Offset
 | 
			
		||||
import com.maddyhome.idea.vim.common.OperatedRange
 | 
			
		||||
@@ -17,10 +17,10 @@ import com.maddyhome.idea.vim.diagnostic.debug
 | 
			
		||||
import com.maddyhome.idea.vim.diagnostic.vimLogger
 | 
			
		||||
import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
			
		||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.helper.inInsertMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.inSingleCommandMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.usesVirtualSpace
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 | 
			
		||||
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_END
 | 
			
		||||
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
 | 
			
		||||
@@ -132,6 +132,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    range: TextRange,
 | 
			
		||||
    type: SelectionType?,
 | 
			
		||||
    noYank: Boolean = false
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    var updatedRange = range
 | 
			
		||||
    // Fix for https://youtrack.jetbrains.net/issue/VIM-35
 | 
			
		||||
@@ -145,6 +146,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (type == null ||
 | 
			
		||||
      noYank ||
 | 
			
		||||
      editor.inInsertMode || injector.registerGroup.storeText(editor, updatedRange, type, true)
 | 
			
		||||
    ) {
 | 
			
		||||
      val startOffsets = updatedRange.startOffsets
 | 
			
		||||
@@ -851,6 +853,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
 | 
			
		||||
    range: TextRange,
 | 
			
		||||
    type: SelectionType?,
 | 
			
		||||
    isChange: Boolean,
 | 
			
		||||
    noYank: Boolean
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
 | 
			
		||||
    // Update the last column before we delete, or we might be retrieving the data for a line that no longer exists
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ sealed class ChangeEditorActionHandler : EditorActionHandlerBase(false) {
 | 
			
		||||
    ): Boolean
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final override fun baseExecute(
 | 
			
		||||
  override fun baseExecute(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user