mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 02:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
			0f0a73c139
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4ca05ba6df | |||
| 114ac40990 | |||
| 2829109eb7 | |||
| f1323139b4 | |||
| df4aa59310 | |||
| 7a3bb5b2d7 | |||
| c4b05957fc | |||
| db83b89931 | |||
| ce27c3e5ba | |||
| 31358bc983 | |||
| f7e1c9c837 | |||
| c46109caa3 | |||
| 9f7ca83306 | |||
| b5761f20d2 | |||
| 305c6d2bf9 | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| * text=auto eol=lf | ||||
							
								
								
									
										1
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							| @@ -6,6 +6,7 @@ | ||||
|         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||
|       </value> | ||||
|     </option> | ||||
|     <option name="LINE_SEPARATOR" value="
" /> | ||||
|     <JavaCodeStyleSettings> | ||||
|       <option name="FIELD_NAME_PREFIX" value="my" /> | ||||
|       <option name="STATIC_FIELD_NAME_PREFIX" value="our" /> | ||||
|   | ||||
| @@ -8,14 +8,15 @@ | ||||
|  | ||||
| # suppress inspection "UnusedProperty" for whole file | ||||
|  | ||||
| ideaVersion=2023.1.2 | ||||
| ideaVersion=2023.2 | ||||
| downloadIdeaSources=true | ||||
| instrumentPluginCode=true | ||||
| version=SNAPSHOT | ||||
| version=chylex-19 | ||||
| javaVersion=17 | ||||
| remoteRobotVersion=0.11.17 | ||||
| antlrVersion=4.10.1 | ||||
|  | ||||
| kotlin.incremental.useClasspathSnapshot=false | ||||
|  | ||||
| # Please don't forget to update kotlin version in buildscript section | ||||
| kotlinVersion=1.8.21 | ||||
|   | ||||
| @@ -14,10 +14,14 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.AnActionWrapper | ||||
| import com.intellij.openapi.actionSystem.IdeActions | ||||
| import com.intellij.openapi.actionSystem.KeyboardShortcut | ||||
| import com.intellij.openapi.actionSystem.PlatformDataKeys | ||||
| import com.intellij.openapi.application.invokeLater | ||||
| import com.intellij.openapi.diagnostic.logger | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.actionSystem.EditorActionManager | ||||
| import com.intellij.openapi.keymap.KeymapManager | ||||
| import com.intellij.openapi.progress.ProcessCanceledException | ||||
| import com.intellij.openapi.project.DumbAware | ||||
| import com.intellij.openapi.util.Key | ||||
| @@ -160,6 +164,14 @@ internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatib | ||||
|         return ActionEnableStatus.no("App code template is active", LogLevel.INFO) | ||||
|       } | ||||
|  | ||||
|       val nextTemplateVariableShortcuts = KeymapManager.getInstance().activeKeymap.getShortcuts(IdeActions.ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE) | ||||
|       if (nextTemplateVariableShortcuts.any { it is KeyboardShortcut && it.firstKeyStroke == keyStroke }) { | ||||
|         val handler = EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE) | ||||
|         if (handler.isEnabled(editor, null, e.dataContext)) { | ||||
|           return ActionEnableStatus.no("Next template variable or finish in-place refactoring", LogLevel.INFO) | ||||
|         } | ||||
|       } | ||||
|        | ||||
|       if (editor.inInsertMode) { | ||||
|         if (keyCode == KeyEvent.VK_TAB) { | ||||
|           // TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view | ||||
|   | ||||
| @@ -231,7 +231,7 @@ private object FileTypePatterns { | ||||
|     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { | ||||
|       this.cMakePatterns | ||||
|     } else { | ||||
|       return null | ||||
|       this.htmlPatterns | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,30 @@ | ||||
| package com.maddyhome.idea.vim.extension.surround | ||||
|  | ||||
| import com.intellij.util.text.CharSequenceSubSequence | ||||
|  | ||||
| internal data class RepeatedCharSequence(val text: CharSequence, val count: Int) : CharSequence { | ||||
|   override val length = text.length * count | ||||
|  | ||||
|   override fun get(index: Int): Char { | ||||
|     if (index < 0 || index >= length) throw IndexOutOfBoundsException() | ||||
|     return text[index % text.length] | ||||
|   } | ||||
|  | ||||
|   override fun subSequence(startIndex: Int, endIndex: Int): CharSequence { | ||||
|     return CharSequenceSubSequence(this, startIndex, endIndex) | ||||
|   } | ||||
|  | ||||
|   override fun toString(): String { | ||||
|     return text.repeat(count) | ||||
|   } | ||||
|    | ||||
|   companion object { | ||||
|     fun of(text: CharSequence, count: Int): CharSequence { | ||||
|       return when (count) { | ||||
|         0 -> "" | ||||
|         1 -> text | ||||
|         else -> RepeatedCharSequence(text, count) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -12,16 +12,14 @@ 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.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroup | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.endsWithNewLine | ||||
| import com.maddyhome.idea.vim.api.getLeadingCharacterOffset | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.api.setChangeMarks | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.state.mode.selectionType | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| @@ -33,12 +31,18 @@ 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.setRegisterForCaret | ||||
| import com.maddyhome.idea.vim.state.mode.mode | ||||
| 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 | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper | ||||
| import com.maddyhome.idea.vim.put.PutData | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.state.mode.mode | ||||
| import com.maddyhome.idea.vim.state.mode.selectionType | ||||
| import org.jetbrains.annotations.NonNls | ||||
| import java.awt.event.KeyEvent | ||||
| import javax.swing.KeyStroke | ||||
| @@ -79,7 +83,7 @@ internal class VimSurroundExtension : VimExtension { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { | ||||
|       setOperatorFunction(Operator()) | ||||
|       setOperatorFunction(Operator(supportsMultipleCursors = false, count = 1)) // TODO | ||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) | ||||
|     } | ||||
|   } | ||||
| @@ -100,7 +104,7 @@ internal class VimSurroundExtension : VimExtension { | ||||
|         val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset) | ||||
|         if (lastNonWhiteSpaceOffset != null) { | ||||
|           val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1) | ||||
|           performSurround(pair, range, it) | ||||
|           performSurround(pair, range, it, count = operatorArguments.count1) | ||||
|         } | ||||
| //        it.moveToOffset(lineStartOffset) | ||||
|       } | ||||
| @@ -120,15 +124,13 @@ internal class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|   private class VSurroundHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { | ||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart | ||||
|       // NB: Operator ignores SelectionType anyway | ||||
|       if (!Operator().apply(editor, context, editor.mode.selectionType)) { | ||||
|       if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) { | ||||
|         return | ||||
|       } | ||||
|       runWriteAction { | ||||
|         // Leave visual mode | ||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||
|         editor.ij.caretModel.moveToOffset(selectionStart) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -149,6 +151,10 @@ internal class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|     companion object { | ||||
|       fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) } | ||||
|       } | ||||
|        | ||||
|       fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         // Save old register values for carets | ||||
|         val surroundings = editor.sortedCarets() | ||||
|           .map { | ||||
| @@ -255,21 +261,42 @@ internal class VimSurroundExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class Operator : OperatorFunction { | ||||
|     override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||
|       val ijEditor = editor.ij | ||||
|       val c = getChar(ijEditor) | ||||
|   private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||
|       val editor = vimEditor.ij | ||||
|       val c = getChar(editor) | ||||
|       if (c.code == 0) return true | ||||
|  | ||||
|       val pair = getOrInputPair(c, ijEditor) ?: return false | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val range = getSurroundRange(editor.currentCaret()) ?: return false | ||||
|       performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE) | ||||
|       val pair = getOrInputPair(c, editor) ?: return false | ||||
|  | ||||
|       runWriteAction { | ||||
|         val change = VimPlugin.getChange() | ||||
|         if (supportsMultipleCursors) { | ||||
|           editor.runWithEveryCaretAndRestore { | ||||
|             applyOnce(editor, change, pair, count) | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           applyOnce(editor, change, pair, count) | ||||
|           // Jump back to start | ||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor) | ||||
|           executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) | ||||
|         } | ||||
|       } | ||||
|       return true | ||||
|     } | ||||
|      | ||||
|     private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) { | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val primaryCaret = editor.caretModel.primaryCaret | ||||
|       val range = getSurroundRange(primaryCaret.vim) | ||||
|       if (range != null) { | ||||
|         val start = RepeatedCharSequence.of(pair.first, count) | ||||
|         val end = RepeatedCharSequence.of(pair.second, count) | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start) | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     private fun getSurroundRange(caret: VimCaret): TextRange? { | ||||
|       val editor = caret.editor | ||||
|       val ijEditor = editor.ij | ||||
| @@ -348,15 +375,15 @@ internal class VimSurroundExtension : VimExtension { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) { | ||||
|     private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) { | ||||
|       runWriteAction { | ||||
|         val editor = caret.editor | ||||
|         val change = VimPlugin.getChange() | ||||
|         val leftSurround = pair.first + if (tagsOnNewLines) "\n" else "" | ||||
|         val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count) | ||||
|  | ||||
|         val isEOF = range.endOffset == editor.text().length | ||||
|         val hasNewLine = editor.endsWithNewLine() | ||||
|         val rightSurround = if (tagsOnNewLines) { | ||||
|         val rightSurround = (if (tagsOnNewLines) { | ||||
|           if (isEOF && !hasNewLine) { | ||||
|             "\n" + pair.second | ||||
|           } else { | ||||
| @@ -364,7 +391,7 @@ internal class VimSurroundExtension : VimExtension { | ||||
|           } | ||||
|         } else { | ||||
|           pair.second | ||||
|         } | ||||
|         }).let { RepeatedCharSequence.of(it, count) } | ||||
|  | ||||
|         change.insertText(editor, caret, range.startOffset, leftSurround) | ||||
|         change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround) | ||||
|   | ||||
| @@ -85,7 +85,7 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|     injector.getMarkService().setVisualSelectionMarks(editor); | ||||
|     VimStateMachine.Companion.getInstance(editor).setMode(Mode.CMD_LINE.INSTANCE); | ||||
|     ExEntryPanel panel = ExEntryPanel.getInstance(); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjEditorExecutionContext) context).getContext(), ":", initText, 1); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjEditorExecutionContext) context).getContext(), ":", initText, cmd.getCount()); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -116,7 +116,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 | ||||
| @@ -127,8 +127,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(); | ||||
|   | ||||
| @@ -335,7 +335,7 @@ public class EditorHelper { | ||||
|  | ||||
|     final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight); | ||||
|     @NotNull final VimEditor editor1 = new IjVimEditor(editor); | ||||
|     final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1; | ||||
|     final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount(); | ||||
|     final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine); | ||||
|  | ||||
|     // For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen. | ||||
|   | ||||
| @@ -12,6 +12,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 | ||||
| @@ -19,6 +20,8 @@ import com.intellij.util.ui.table.JBTableRowEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.group.IjOptionConstants | ||||
| import com.maddyhome.idea.vim.newapi.globalIjOptions | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.mode.inBlockSelection | ||||
| import java.awt.Component | ||||
| import javax.swing.JComponent | ||||
| import javax.swing.JTable | ||||
| @@ -93,3 +96,41 @@ internal val Caret.vimLine: Int | ||||
|  */ | ||||
| internal val Editor.vimLine: Int | ||||
|   get() = this.caretModel.currentCaret.vimLine | ||||
|  | ||||
| internal inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) { | ||||
|   val caretModel = this.caretModel | ||||
|   val carets = if (this.vim.inBlockSelection) 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 | ||||
|   }  | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.api.normalizeVisualColumn | ||||
| import com.maddyhome.idea.vim.api.options | ||||
| import com.maddyhome.idea.vim.command.CommandFlags | ||||
| import com.maddyhome.idea.vim.state.VimStateMachine | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenHeight | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenWidth | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper.getNonNormalizedVisualLineAtBottomOfScreen | ||||
| @@ -29,6 +28,7 @@ import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToBottomOfScre | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToMiddleOfScreen | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToTopOfScreen | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.VimStateMachine | ||||
| import kotlin.math.max | ||||
| import kotlin.math.min | ||||
| import kotlin.math.roundToInt | ||||
| @@ -56,7 +56,7 @@ internal object ScrollViewHelper { | ||||
|     // that this needs to be replaced as a more or less dumb line for line rewrite. | ||||
|     val topLine = getVisualLineAtTopOfScreen(editor) | ||||
|     val bottomLine = getVisualLineAtBottomOfScreen(editor) | ||||
|     val lastLine = vimEditor.getVisualLineCount() - 1 | ||||
|     val lastLine = vimEditor.getVisualLineCount() + editor.settings.additionalLinesCount | ||||
|  | ||||
|     // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred | ||||
|     val scrollOffset = injector.options(vimEditor).scrolloff | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import com.intellij.openapi.command.impl.UndoManagerImpl | ||||
| import com.intellij.openapi.command.undo.UndoManager | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| @@ -23,6 +24,8 @@ import com.maddyhome.idea.vim.group.IjOptions | ||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor | ||||
| import com.maddyhome.idea.vim.newapi.globalIjOptions | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.state.mode.inVisualMode | ||||
| import com.maddyhome.idea.vim.undo.UndoRedoBase | ||||
|  | ||||
| /** | ||||
| @@ -50,6 +53,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|  | ||||
|       if (injector.globalIjOptions().oldundo) { | ||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } | ||||
|         restoreVisualMode(editor) | ||||
|       } else { | ||||
|         performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) | ||||
|  | ||||
| @@ -81,6 +85,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|     if (undoManager.isRedoAvailable(fileEditor)) { | ||||
|       if (injector.globalIjOptions().oldundo) { | ||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } | ||||
|         restoreVisualMode(editor) | ||||
|       } else { | ||||
|         performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) | ||||
|         CommandProcessor.getInstance().runUndoTransparentAction { | ||||
| @@ -115,4 +120,21 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|   private fun ifFilePathChanged(editor: VimEditor, oldPath: String?): Boolean { | ||||
|     return editor.getPath() != oldPath | ||||
|   } | ||||
|  | ||||
|   private fun restoreVisualMode(editor: VimEditor) { | ||||
|     if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) { | ||||
|       val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor) | ||||
|        | ||||
|       // Visual block selection is restored into multiple carets, so multi-carets that form a block are always | ||||
|       // identified as visual block mode, leading to false positives. | ||||
|       // Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore | ||||
|       // visual block mode. | ||||
|       val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE) | ||||
|         SelectionType.CHARACTER_WISE | ||||
|       else | ||||
|         detectedMode | ||||
|        | ||||
|       VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| /* | ||||
|  * Copyright 2003-2023 The IdeaVim authors | ||||
|  * | ||||
|  * Use of this source code is governed by an MIT-style | ||||
|  * license that can be found in the LICENSE.txt file or at | ||||
|  * https://opensource.org/licenses/MIT. | ||||
|  */ | ||||
|  | ||||
| package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.intellij.ide.plugins.StandalonePluginUpdateChecker | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.components.service | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.group.NotificationService | ||||
| import com.maddyhome.idea.vim.icons.VimIcons | ||||
|  | ||||
| @Service(Service.Level.APP) | ||||
| internal class VimStandalonePluginUpdateChecker : StandalonePluginUpdateChecker( | ||||
|   VimPlugin.getPluginId(), | ||||
|   updateTimestampProperty = PROPERTY_NAME, | ||||
|   NotificationService.IDEAVIM_STICKY_GROUP, | ||||
|   VimIcons.IDEAVIM, | ||||
| ) { | ||||
|  | ||||
|   override fun skipUpdateCheck(): Boolean = !VimPlugin.isEnabled() || "dev" in VimPlugin.getVersion() | ||||
|  | ||||
|   companion object { | ||||
|     private const val PROPERTY_NAME = "ideavim.statistics.timestamp" | ||||
|     val instance: VimStandalonePluginUpdateChecker = service() | ||||
|   } | ||||
| } | ||||
| @@ -72,7 +72,6 @@ import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd | ||||
| import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently | ||||
| import com.maddyhome.idea.vim.helper.GuicursorChangeListener | ||||
| import com.maddyhome.idea.vim.helper.StrictMode | ||||
| import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker | ||||
| import com.maddyhome.idea.vim.helper.exitSelectMode | ||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | ||||
| import com.maddyhome.idea.vim.helper.forceBarCursor | ||||
| @@ -89,6 +88,8 @@ import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents | ||||
| import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.VimStateMachine | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||
| import com.maddyhome.idea.vim.state.mode.mode | ||||
| import com.maddyhome.idea.vim.state.mode.selectionType | ||||
| @@ -266,6 +267,16 @@ internal object VimListenerManager { | ||||
|   class VimFileEditorManagerListener : FileEditorManagerListener { | ||||
|     override fun selectionChanged(event: FileEditorManagerEvent) { | ||||
|       if (!VimPlugin.isEnabled()) return | ||||
|        | ||||
|       val newEditor = event.newEditor | ||||
|       if (newEditor is TextEditor) { | ||||
|         val editor = newEditor.editor | ||||
|         if (editor.isInsertMode) { | ||||
|           VimStateMachine.getInstance(editor).mode = Mode.NORMAL() | ||||
|           KeyHandler.getInstance().reset(editor.vim) | ||||
|         } | ||||
|       } | ||||
|        | ||||
|       MotionGroup.fileEditorManagerSelectionChangedCallback(event) | ||||
|       FileGroup.fileEditorManagerSelectionChangedCallback(event) | ||||
|       SearchGroup.fileEditorManagerSelectionChangedCallback(event) | ||||
| @@ -330,8 +341,6 @@ internal object VimListenerManager { | ||||
|  | ||||
|         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) | ||||
|       } | ||||
|  | ||||
|       VimStandalonePluginUpdateChecker.instance.pluginUsed() | ||||
|     } | ||||
|  | ||||
|     override fun editorReleased(event: EditorFactoryEvent) { | ||||
|   | ||||
| @@ -18,12 +18,12 @@ import com.intellij.openapi.diagnostic.logger | ||||
| import com.intellij.openapi.editor.Document | ||||
| import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider | ||||
| import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent | ||||
| import com.intellij.openapi.fileEditor.FileDocumentManager | ||||
| import com.intellij.openapi.project.DumbAwareAction | ||||
| import com.intellij.openapi.util.Disposer | ||||
| import com.intellij.openapi.util.io.FileUtil | ||||
| import com.maddyhome.idea.vim.api.VimrcFileState | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionRegistrar | ||||
| import com.maddyhome.idea.vim.helper.MessageHelper | ||||
| import com.maddyhome.idea.vim.icons.VimIcons | ||||
| import com.maddyhome.idea.vim.key.MappingOwner | ||||
| @@ -149,12 +149,14 @@ internal class ReloadVimRc : DumbAwareAction() { | ||||
|  | ||||
|   override fun actionPerformed(e: AnActionEvent) { | ||||
|     val editor = e.getData(PlatformDataKeys.EDITOR) ?: return | ||||
|     FileDocumentManager.getInstance().saveDocumentAsIs(editor.document) | ||||
|     injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript) | ||||
|     Troubleshooter.instance.removeByType("old-action-notation-in-mappings") | ||||
|  | ||||
|     // Reload the ideavimrc in the context of the current window, as though we had called `:source ~/.ideavimrc` | ||||
|     executeIdeaVimRc(editor.vim) | ||||
|  | ||||
|     // Ensure newly added extensions are initialized | ||||
|     VimExtensionRegistrar.enableDelayedExtensions() | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,8 @@ package com.maddyhome.idea.vim.vimscript | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.diagnostic.logger | ||||
| import com.intellij.openapi.fileEditor.FileDocumentManager | ||||
| import com.intellij.openapi.vfs.VirtualFileManager | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| @@ -88,12 +90,22 @@ internal class Executor : VimScriptExecutorBase() { | ||||
|   override fun executeFile(file: File, editor: VimEditor, indicateErrors: Boolean) { | ||||
|     val context = DataContext.EMPTY_CONTEXT.vim | ||||
|     try { | ||||
|       ensureFileIsSaved(file) | ||||
|       execute(file.readText(), editor, context, skipHistory = true, indicateErrors) | ||||
|     } catch (ignored: IOException) { | ||||
|       LOG.error(ignored) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private fun ensureFileIsSaved(file: File) { | ||||
|     val documentManager = FileDocumentManager.getInstance() | ||||
|  | ||||
|     VirtualFileManager.getInstance().findFileByNioPath(file.toPath()) | ||||
|       ?.let(documentManager::getCachedDocument) | ||||
|       ?.takeIf(documentManager::isDocumentUnsaved) | ||||
|       ?.let(documentManager::saveDocumentAsIs) | ||||
|   } | ||||
|  | ||||
|   @Throws(ExException::class) | ||||
|   override fun executeLastCommand(editor: VimEditor, context: ExecutionContext): Boolean { | ||||
|     val reg = VimPlugin.getRegister().getRegister(':') ?: return false | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.vimscript.model.functions.handlers | ||||
|  | ||||
| import com.intellij.openapi.util.SystemInfoRt | ||||
| import com.intellij.util.system.CpuArch | ||||
| import com.intellij.vim.annotations.VimscriptFunction | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| @@ -23,7 +25,7 @@ internal class HasFunctionHandler : FunctionHandler() { | ||||
|   override val minimumNumberOfArguments = 1 | ||||
|   override val maximumNumberOfArguments = 2 | ||||
|  | ||||
|   private val supportedFeatures = setOf("ide") | ||||
|   private val supportedFeatures = Features.discover() | ||||
|  | ||||
|   override fun doFunction( | ||||
|     argumentValues: List<Expression>, | ||||
| @@ -41,4 +43,40 @@ internal class HasFunctionHandler : FunctionHandler() { | ||||
|       VimInt.ZERO | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   private object Features { | ||||
|     fun discover(): Set<String> { | ||||
|       val features = mutableSetOf("ide") | ||||
|       collectOperatingSystemType(features) | ||||
|       return features | ||||
|     } | ||||
|      | ||||
|     private fun collectOperatingSystemType(target: MutableSet<String>) { | ||||
|       if (SystemInfoRt.isWindows) { | ||||
|         target.add("win32") | ||||
|         if (CpuArch.CURRENT.width == 64) { | ||||
|           target.add("win64") | ||||
|         } | ||||
|       } | ||||
|       else if (SystemInfoRt.isLinux) { | ||||
|         target.add("linux") | ||||
|       } | ||||
|       else if (SystemInfoRt.isMac) { | ||||
|         target.add("mac") | ||||
|         target.add("macunix") | ||||
|         target.add("osx") | ||||
|         target.add("osxdarwin") | ||||
|       } | ||||
|       else if (SystemInfoRt.isFreeBSD) { | ||||
|         target.add("bsd") | ||||
|       } | ||||
|       else if (SystemInfoRt.isSolaris) { | ||||
|         target.add("sun") | ||||
|       } | ||||
|        | ||||
|       if (SystemInfoRt.isUnix) { | ||||
|         target.add("unix") | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -226,12 +226,12 @@ | ||||
|  | ||||
|     <!-- Change --> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction" mappingModes="N" keys="gu"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/> | ||||
| <!--    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/>--> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction" mappingModes="N" keys="~"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction" mappingModes="N" keys="g~"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction" mappingModes="X" keys="~"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction" mappingModes="N" keys="gU"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/> | ||||
| <!--    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/>--> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction" mappingModes="N" keys="r"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction" mappingModes="N" keys="s"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction" mappingModes="N" keys="C"/> | ||||
| @@ -329,8 +329,8 @@ | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.RepeatChangeAction" mappingModes="N" keys="."/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.ExEntryAction" mappingModes="NXO" keys=":"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.ResetModeAction" mappingModes="ALL" keys="«C-\»«C-N»"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="N" keys="«C-R»"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="N"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="NX" keys="U,«C-R»"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="NX"/> | ||||
|  | ||||
|     <!-- Keys --> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.OperatorAction" mappingModes="N" keys="g@"/> | ||||
|   | ||||
| @@ -1,12 +1,4 @@ | ||||
| <!-- | ||||
|   ~ Copyright 2003-2023 The IdeaVim authors | ||||
|   ~ | ||||
|   ~ Use of this source code is governed by an MIT-style | ||||
|   ~ license that can be found in the LICENSE.txt file or at | ||||
|   ~ https://opensource.org/licenses/MIT. | ||||
|   --> | ||||
|  | ||||
| <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> | ||||
|   <description><![CDATA[ | ||||
| @@ -21,13 +13,13 @@ | ||||
|         <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 --> | ||||
|   <!-- Check for [Version Update] tag in YouTrack as well --> | ||||
|   <!-- Also, please update the value in build.gradle.kts file--> | ||||
|   <idea-version since-build="231.7515.13"/> | ||||
|   <idea-version since-build="232"/> | ||||
|  | ||||
|   <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> | ||||
|   <depends>com.intellij.modules.platform</depends> | ||||
| @@ -142,5 +134,6 @@ | ||||
|     </group> | ||||
|  | ||||
|     <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/> | ||||
|     <action id="VimJumpToSource" class="com.intellij.diff.actions.impl.OpenInEditorAction" /> | ||||
|   </actions> | ||||
| </idea-plugin> | ||||
|   | ||||
| @@ -27,6 +27,7 @@ public class FilterVisualLinesAction : VimActionHandler.SingleExecution() { | ||||
|   override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE) | ||||
|  | ||||
|   override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { | ||||
|     injector.markService.setVisualSelectionMarks(editor) | ||||
|     injector.processGroup.startFilterCommand(editor, context, cmd) | ||||
|     editor.exitVisualMode() | ||||
|     return true | ||||
|   | ||||
| @@ -144,7 +144,7 @@ public interface VimChangeGroup { | ||||
|     operatorArguments: OperatorArguments, | ||||
|   ) | ||||
|  | ||||
|   public fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret | ||||
|   public fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret | ||||
|  | ||||
|   public fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret | ||||
|  | ||||
|   | ||||
| @@ -202,7 +202,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|    * @param caret  The caret to start insertion in | ||||
|    * @param str    The text to insert | ||||
|    */ | ||||
|   override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret { | ||||
|   override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret { | ||||
|     (editor as MutableVimEditor).insertText(Offset(offset), str) | ||||
|     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user