mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-04-23 13:15:45 +02:00
Move the ideavim-sneak plugin into IdeaVim
The author of the original plugin announced the deprecation of the plugin. However, we've got an approval to move the sources into IdeaVim and continue the development. Original repo: https://github.com/Mishkun/ideavim-sneak Approval: https://twitter.com/ideavim/status/1754512214344478939
This commit is contained in:
parent
855dbfab16
commit
aae0d825e7
ThirdPartyLicenses.md
doc
src
main
java/com/maddyhome/idea/vim/extension
resources/META-INF/includes
test/java/org/jetbrains/plugins/ideavim
ex/implementation/commands
extension/sneak
@ -84,3 +84,8 @@ IV) It is not allowed to remove this license from the distribution of the Vim
|
||||
license for previous Vim releases instead of the license that they came
|
||||
with, at your option.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
File [sneakIcon.png](doc/images/sneakIcon.svg), which is originally an icon of the ideavim-sneak plugin,
|
||||
is merged icons of IdeaVim plugin and a random sneaker by FreePic from flaticon.com.
|
||||
|
@ -44,16 +44,21 @@ All commands with the mappings are supported. See the [full list of supported co
|
||||
|
||||
<details>
|
||||
<summary><h2>sneak</h2></summary>
|
||||
|
||||
|
||||
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
|
||||
|
||||
By [Mikhail Levchenko](https://github.com/Mishkun)
|
||||
Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
|
||||
Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak).
|
||||
|
||||
### Setup:
|
||||
- Install [IdeaVim-sneak](https://plugins.jetbrains.com/plugin/15348-ideavim-sneak) plugin.
|
||||
- Add the following command to `~/.ideavimrc`: `set sneak`
|
||||
- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
|
||||
|
||||
### Instructions
|
||||
|
||||
See the [docs](https://github.com/Mishkun/ideavim-sneak#usage)
|
||||
|
||||
* Type `s` and two chars to start sneaking in forward direction
|
||||
* Type `S` and two chars to start sneaking in backward direction
|
||||
* Type `;` or `,` to proceed with sneaking just as if you were using `f` or `t` commands
|
||||
|
||||
</details>
|
||||
|
||||
|
28
doc/images/sneakIcon.svg
Normal file
28
doc/images/sneakIcon.svg
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="386.498 234 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="ideavim_plugin-a" x1="-6.748%" x2="47.286%" y1="33.61%" y2="85.907%">
|
||||
<stop offset="0" stop-color="#3BEA62"/>
|
||||
<stop offset="1" stop-color="#087CFA"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="matrix(1.238978, 0.90017, -0.90017, 1.238978, 131.776901, -422.953003)" style="">
|
||||
<path d="M 399.962 247.648 C 399.207 246.894 399.147 246.318 399.692 245.453 C 400.237 244.588 401.955 245.886 401.955 245.886 L 401.955 250.737" style="fill: rgb(248, 245, 231);"/>
|
||||
<path d="M 413.846 253.602 C 413.846 255.077 411.827 256.134 409.392 256.134 L 396.381 256.134 C 395.211 256.134 394.232 256.003 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 413.846 253.602" fill="#cce6f6" style=""/>
|
||||
<path d="M 413.846 253.602 C 413.846 253.602 411.475 254.468 408.27 254.468 C 405.94 254.468 398.116 253.433 394.023 252.869 C 392.488 252.658 391.478 252.512 391.478 252.512 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 C 396.446 247.877 396.955 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 406.544 249.03 410.097 250.43 410.097 250.43 C 410.097 250.43 413.061 250.446 413.782 251.167 C 414.503 251.888 414.422 253.218 413.846 253.602 Z M 413.846 253.602" style="fill: rgb(8, 124, 250);"/>
|
||||
<path d="M 394.023 252.869 L 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 394.023 252.869" fill="#9dcae0" style=""/>
|
||||
<path d="M 396.059 247.225 C 395.073 245.986 393.193 250.255 394.023 252.869 C 392.488 252.658 391.478 252.513 391.478 252.513 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 Z M 396.059 247.225" style="fill: rgb(14, 112, 142);"/>
|
||||
<path d="M 403.527 246.924 L 399.174 251.768 C 397.7 250.892 395.578 250.174 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 L 394.341 248.65 L 394.884 248.827 C 395.509 249.031 396.192 248.95 396.753 248.608 L 397.153 248.363 C 397.35 248.454 397.572 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 402.494 246.256 403.02 246.601 403.527 246.924 Z M 403.527 246.924" style="fill: rgb(59, 234, 98);"/>
|
||||
<path d="M 413.847 253.602 C 413.847 253.602 411.475 254.468 408.27 254.468 C 407.586 254.468 406.426 254.378 405.025 254.238 L 405.025 254.237 C 405.025 253.495 406.924 251.743 408.366 251.616 C 408.623 252.865 410.097 252.512 411.219 252.128 C 412.341 251.743 413.783 251.167 413.783 251.167 C 414.503 251.888 414.422 253.218 413.847 253.602 Z M 413.847 253.602" style="fill: rgb(8, 124, 250);"/>
|
||||
<path d="M 394.341 248.65 C 394.214 248.978 394.103 249.339 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 Z M 394.341 248.65" style="fill: rgb(37, 187, 163);"/>
|
||||
<path d="M 408.366 251.616 C 406.924 251.743 405.025 253.495 405.025 254.237 L 405.025 254.238 C 403.784 254.113 402.355 253.948 400.899 253.77 C 400.899 253.051 400.191 252.372 399.174 251.768 L 399.174 251.768 L 403.528 246.924 C 407.331 249.34 410.097 250.43 410.097 250.43 C 410.77 251.102 409.809 251.487 408.366 251.616 Z M 408.366 251.616" style="fill: rgb(248, 245, 231);"/>
|
||||
<polygon fill="url(#ideavim_plugin-a)" fill-rule="evenodd" points="406.356 248.463 403.261 252.616 402.183 248.212 400.984 249.899 401.999 254.236 403.927 254.176 407.639 249.062" style="" transform="matrix(0.994522, 0.104529, -0.104529, 0.994522, 28.475005, -40.88594)"/>
|
||||
<g fill="#fb6572" transform="matrix(0.046265, 0, 0, 0.046265, 390.612823, 245.155533)" style="">
|
||||
<path d="m288.839844 72.65625c-2.007813 0-4.011719-.761719-5.542969-2.292969-3.058594-3.0625-3.058594-8.023437 0-11.082031l20.089844-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.0625 8.023438 0 11.082032l-20.089844 20.089843c-1.527344 1.53125-3.535156 2.292969-5.539062 2.292969zm0 0" style="fill: rgb(56, 228, 105);"/>
|
||||
<path d="m314.589844 87.082031c-2.007813 0-4.011719-.765625-5.542969-2.296875-3.0625-3.058594-3.0625-8.019531 0-11.082031l20.089844-20.085937c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.058594 8.023437 0 11.082031l-20.089844 20.085937c-1.527344 1.53125-3.535156 2.296875-5.539062 2.296875zm0 0" style="fill: rgb(59, 233, 100);"/>
|
||||
<path d="m340.339844 101.507812c-2.007813 0-4.011719-.765624-5.542969-2.296874-3.058594-3.058594-3.058594-8.023438 0-11.082032l20.089844-20.085937c3.0625-3.0625 8.023437-3.0625 11.082031 0 3.058594 3.058593 3.0625 8.023437 0 11.082031l-20.089844 20.085938c-1.527344 1.53125-3.535156 2.296874-5.539062 2.296874zm0 0" style="fill: rgb(59, 233, 100);"/>
|
||||
<path d="m366.089844 115.929688c-2.003906 0-4.011719-.761719-5.539063-2.292969-3.0625-3.0625-3.0625-8.023438 0-11.082031l20.085938-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.0625 3.0625 3.0625 8.023437 0 11.082031l-20.085938 20.089844c-1.53125 1.53125-3.535156 2.292969-5.542968 2.292969zm0 0" style="fill: rgb(59, 233, 100);"/>
|
||||
</g>
|
||||
<path d="M 401.925 247.748 C 401.761 247.748 401.611 247.629 401.573 247.469 C 401.536 247.312 401.611 247.144 401.753 247.067 C 402 246.933 402.318 247.147 402.286 247.426 C 402.265 247.606 402.107 247.748 401.925 247.748 Z M 401.925 247.748" fill="#1e2628" style=""/>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 5.6 KiB |
@ -53,6 +53,11 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
|
||||
@Synchronized
|
||||
private fun registerExtension(extensionBean: ExtensionBeanClass) {
|
||||
val name = extensionBean.name ?: extensionBean.instance.name
|
||||
if (name == "sneak" && extensionBean.name == null) {
|
||||
// Filter out the old ideavim-sneak extension that used to be a separate plugin
|
||||
// https://github.com/Mishkun/ideavim-sneak
|
||||
return
|
||||
}
|
||||
if (name in registeredExtensions) return
|
||||
|
||||
registeredExtensions.add(name)
|
||||
|
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2003-2024 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.extension.sneak
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.markup.EffectType
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.maddyhome.idea.vim.VimProjectService
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.options
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
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
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||
import com.maddyhome.idea.vim.helper.StrictMode
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import java.awt.Font
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK: Long = 300
|
||||
|
||||
// By [Mikhail Levchenko](https://github.com/Mishkun)
|
||||
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
|
||||
internal class IdeaVimSneakExtension : VimExtension {
|
||||
override fun getName(): String = "sneak"
|
||||
|
||||
override fun init() {
|
||||
val highlightHandler = HighlightHandler()
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
|
||||
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
|
||||
|
||||
// workaround to support ; and , commands
|
||||
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"))
|
||||
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
|
||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
|
||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
|
||||
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
|
||||
}
|
||||
|
||||
private class SneakHandler(
|
||||
private val highlightHandler: HighlightHandler,
|
||||
private val direction: Direction,
|
||||
) : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val charone = getChar(editor) ?: return
|
||||
val chartwo = getChar(editor) ?: return
|
||||
val range = Util.jumpTo(editor, charone, chartwo, direction)
|
||||
range?.let { highlightHandler.highlightSneakRange(editor.ij, range) }
|
||||
Util.lastSymbols = "${charone}${chartwo}"
|
||||
Util.lastSDirection = direction
|
||||
}
|
||||
|
||||
private fun getChar(editor: VimEditor): Char? {
|
||||
val key = VimExtensionFacade.inputKeyStroke(editor.ij)
|
||||
return when {
|
||||
key.keyChar == KeyEvent.CHAR_UNDEFINED || key.keyCode == KeyEvent.VK_ESCAPE -> null
|
||||
else -> key.keyChar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class acts as proxy for normal find commands because we need to update [Util.lastSDirection]
|
||||
*/
|
||||
private class SneakMemoryHandler(private val char: String) : VimExtensionHandler {
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
Util.lastSDirection = null
|
||||
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(char), editor)
|
||||
}
|
||||
}
|
||||
|
||||
private class SneakRepeatHandler(
|
||||
private val highlightHandler: HighlightHandler,
|
||||
private val direction: RepeatDirection,
|
||||
) : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val lastSDirection = Util.lastSDirection
|
||||
if (lastSDirection != null) {
|
||||
val (charone, chartwo) = Util.lastSymbols.toList()
|
||||
val jumpRange = Util.jumpTo(editor, charone, chartwo, direction.map(lastSDirection))
|
||||
jumpRange?.let { highlightHandler.highlightSneakRange(editor.ij, jumpRange) }
|
||||
} else {
|
||||
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(direction.symb), editor.ij)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object Util {
|
||||
var lastSDirection: Direction? = null
|
||||
var lastSymbols: String = ""
|
||||
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
|
||||
val caret = editor.primaryCaret()
|
||||
val position = caret.offset.point
|
||||
val chars = editor.text()
|
||||
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
|
||||
if (foundPosition != null) {
|
||||
editor.primaryCaret().moveToOffset(foundPosition)
|
||||
}
|
||||
editor.ij.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
return foundPosition?.let { TextRange(foundPosition, foundPosition + 2) }
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Direction(val offset: Int) {
|
||||
FORWARD(1) {
|
||||
override fun findBiChar(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
position: Int,
|
||||
charone: Char,
|
||||
chartwo: Char
|
||||
): Int? {
|
||||
for (i in (position + offset) until charSequence.length - 1) {
|
||||
if (matches(editor, charSequence, i, charone, chartwo)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
},
|
||||
BACKWARD(-1) {
|
||||
override fun findBiChar(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
position: Int,
|
||||
charone: Char,
|
||||
chartwo: Char
|
||||
): Int? {
|
||||
for (i in (position + offset) downTo 0) {
|
||||
if (matches(editor, charSequence, i, charone, chartwo)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
abstract fun findBiChar(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
position: Int,
|
||||
charone: Char,
|
||||
chartwo: Char,
|
||||
): Int?
|
||||
|
||||
fun matches(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
charPosition: Int,
|
||||
charOne: Char,
|
||||
charTwo: Char,
|
||||
): Boolean {
|
||||
var match = charSequence[charPosition].equals(charOne, ignoreCase = injector.options(editor).ignorecase) &&
|
||||
charSequence[charPosition + 1].equals(charTwo, ignoreCase = injector.options(editor).ignorecase)
|
||||
|
||||
if (injector.options(editor).ignorecase && injector.options(editor).smartcase) {
|
||||
if (charOne.isUpperCase() || charTwo.isUpperCase()) {
|
||||
match = charSequence[charPosition].equals(charOne, ignoreCase = false) &&
|
||||
charSequence[charPosition + 1].equals(charTwo, ignoreCase = false)
|
||||
}
|
||||
}
|
||||
return match
|
||||
}
|
||||
}
|
||||
|
||||
private enum class RepeatDirection(val symb: String) {
|
||||
IDENTICAL(";") {
|
||||
override fun map(direction: Direction): Direction = direction
|
||||
},
|
||||
REVERSE(",") {
|
||||
override fun map(direction: Direction): Direction = when (direction) {
|
||||
Direction.FORWARD -> Direction.BACKWARD
|
||||
Direction.BACKWARD -> Direction.FORWARD
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun map(direction: Direction): Direction
|
||||
}
|
||||
|
||||
private class HighlightHandler {
|
||||
private var editor: Editor? = null
|
||||
private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
|
||||
|
||||
fun highlightSneakRange(editor: Editor, range: TextRange) {
|
||||
clearAllSneakHighlighters()
|
||||
|
||||
this.editor = editor
|
||||
val project = editor.project
|
||||
if (project != null) {
|
||||
Disposer.register(VimProjectService.getInstance(project)) {
|
||||
this.editor = null
|
||||
sneakHighlighters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
if (range.isMultiple) {
|
||||
for (i in 0 until range.size()) {
|
||||
highlightSingleRange(editor, range.startOffsets[i]..range.endOffsets[i])
|
||||
}
|
||||
} else {
|
||||
highlightSingleRange(editor, range.startOffset..range.endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearAllSneakHighlighters() {
|
||||
sneakHighlighters.forEach { highlighter ->
|
||||
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||
}
|
||||
|
||||
sneakHighlighters.clear()
|
||||
}
|
||||
|
||||
private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
|
||||
val highlighter = editor.markupModel.addRangeHighlighter(
|
||||
range.start,
|
||||
range.endInclusive,
|
||||
HighlighterLayer.SELECTION,
|
||||
getHighlightTextAttributes(),
|
||||
HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
|
||||
sneakHighlighters.add(highlighter)
|
||||
|
||||
setClearHighlightRangeTimer(highlighter)
|
||||
}
|
||||
|
||||
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
|
||||
Executors.newSingleThreadScheduledExecutor().schedule({
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||
}
|
||||
}, DEFAULT_HIGHLIGHT_DURATION_SNEAK, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
private fun getHighlightTextAttributes() = TextAttributes(
|
||||
null,
|
||||
EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES.defaultAttributes.backgroundColor,
|
||||
editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
|
||||
EffectType.SEARCH_MATCH,
|
||||
Font.PLAIN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map some <Plug>(keys) command to given handler
|
||||
* and create mapping to <Plug>(prefix)[keys]
|
||||
*/
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
MappingMode.NXO,
|
||||
injector.parser.parseKeys(command(keys)),
|
||||
owner,
|
||||
handler,
|
||||
false
|
||||
)
|
||||
VimExtensionFacade.putKeyMapping(
|
||||
MappingMode.NXO,
|
||||
injector.parser.parseKeys(keys),
|
||||
owner,
|
||||
injector.parser.parseKeys(command(keys)),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
private fun command(keys: String) = "<Plug>(sneak-$keys)"
|
@ -124,6 +124,14 @@
|
||||
<alias name="chrisbra/matchit"/>
|
||||
</aliases>
|
||||
</vimExtension>
|
||||
|
||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.sneak.IdeaVimSneakExtension" name="sneak">
|
||||
<aliases>
|
||||
<alias name="https://github.com/justinmk/vim-sneak"/>
|
||||
<alias name="justinmk/vim-sneak"/>
|
||||
<alias name="vim-sneak"/>
|
||||
</aliases>
|
||||
</vimExtension>
|
||||
</extensions>
|
||||
|
||||
<!-- IdeaVim extensions-->
|
||||
|
@ -164,19 +164,20 @@ class SetCommandTest : VimTestCase() {
|
||||
assertCommandOutput("set all",
|
||||
"""
|
||||
|--- Options ---
|
||||
|noargtextobj noideatracetime scroll=0 nosurround
|
||||
| closenotebooks ideawrite=all scrolljump=1 notextobj-entire
|
||||
|nocommentary noignorecase scrolloff=0 notextobj-indent
|
||||
|nodigraph noincsearch selectmode= timeout
|
||||
|noexchange nomatchit shellcmdflag=-x timeoutlen=1000
|
||||
|nogdefault maxmapdepth=20 shellxescape=@ notrackactionids
|
||||
|nohighlightedyank more shellxquote={ undolevels=1000
|
||||
| history=50 nomultiple-cursors showcmd unifyjumps
|
||||
|nohlsearch noNERDTree showmode virtualedit=
|
||||
|noideaglobalmode nrformats=hex sidescroll=0 novisualbell
|
||||
|noideajoin nonumber sidescrolloff=0 visualdelay=100
|
||||
| ideamarks octopushandler nosmartcase whichwrap=b,s
|
||||
| ideastrictmode norelativenumber startofline wrapscan
|
||||
|noargtextobj ideawrite=all scrolloff=0 notextobj-indent
|
||||
| closenotebooks noignorecase selectmode= timeout
|
||||
|nocommentary noincsearch shellcmdflag=-x timeoutlen=1000
|
||||
|nodigraph nomatchit shellxescape=@ notrackactionids
|
||||
|noexchange maxmapdepth=20 shellxquote={ undolevels=1000
|
||||
|nogdefault more showcmd unifyjumps
|
||||
|nohighlightedyank nomultiple-cursors showmode virtualedit=
|
||||
| history=50 noNERDTree sidescroll=0 novisualbell
|
||||
|nohlsearch nrformats=hex sidescrolloff=0 visualdelay=100
|
||||
|noideaglobalmode nonumber nosmartcase whichwrap=b,s
|
||||
|noideajoin octopushandler nosneak wrapscan
|
||||
| ideamarks norelativenumber startofline
|
||||
| ideastrictmode scroll=0 nosurround
|
||||
|noideatracetime scrolljump=1 notextobj-entire
|
||||
| clipboard=ideaput,autoselect,exclude:cons\|linux
|
||||
| excommandannotation
|
||||
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
|
||||
@ -277,6 +278,7 @@ class SetCommandTest : VimTestCase() {
|
||||
| sidescroll=0
|
||||
| sidescrolloff=0
|
||||
|nosmartcase
|
||||
|nosneak
|
||||
| startofline
|
||||
|nosurround
|
||||
|notextobj-entire
|
||||
|
@ -350,19 +350,20 @@ class SetglobalCommandTest : VimTestCase() {
|
||||
setOsSpecificOptionsToSafeValues()
|
||||
assertCommandOutput("setglobal all", """
|
||||
|--- Global option values ---
|
||||
|noargtextobj noideatracetime scroll=0 nosurround
|
||||
| closenotebooks ideawrite=all scrolljump=1 notextobj-entire
|
||||
|nocommentary noignorecase scrolloff=0 notextobj-indent
|
||||
|nodigraph noincsearch selectmode= timeout
|
||||
|noexchange nomatchit shellcmdflag=-x timeoutlen=1000
|
||||
|nogdefault maxmapdepth=20 shellxescape=@ notrackactionids
|
||||
|nohighlightedyank more shellxquote={ undolevels=1000
|
||||
| history=50 nomultiple-cursors showcmd unifyjumps
|
||||
|nohlsearch noNERDTree showmode virtualedit=
|
||||
|noideaglobalmode nrformats=hex sidescroll=0 novisualbell
|
||||
|noideajoin nonumber sidescrolloff=0 visualdelay=100
|
||||
| ideamarks octopushandler nosmartcase whichwrap=b,s
|
||||
| ideastrictmode norelativenumber startofline wrapscan
|
||||
|noargtextobj ideawrite=all scrolloff=0 notextobj-indent
|
||||
| closenotebooks noignorecase selectmode= timeout
|
||||
|nocommentary noincsearch shellcmdflag=-x timeoutlen=1000
|
||||
|nodigraph nomatchit shellxescape=@ notrackactionids
|
||||
|noexchange maxmapdepth=20 shellxquote={ undolevels=1000
|
||||
|nogdefault more showcmd unifyjumps
|
||||
|nohighlightedyank nomultiple-cursors showmode virtualedit=
|
||||
| history=50 noNERDTree sidescroll=0 novisualbell
|
||||
|nohlsearch nrformats=hex sidescrolloff=0 visualdelay=100
|
||||
|noideaglobalmode nonumber nosmartcase whichwrap=b,s
|
||||
|noideajoin octopushandler nosneak wrapscan
|
||||
| ideamarks norelativenumber startofline
|
||||
| ideastrictmode scroll=0 nosurround
|
||||
|noideatracetime scrolljump=1 notextobj-entire
|
||||
| clipboard=ideaput,autoselect,exclude:cons\|linux
|
||||
| excommandannotation
|
||||
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
|
||||
@ -459,6 +460,7 @@ class SetglobalCommandTest : VimTestCase() {
|
||||
| sidescroll=0
|
||||
| sidescrolloff=0
|
||||
|nosmartcase
|
||||
|nosneak
|
||||
| startofline
|
||||
|nosurround
|
||||
|notextobj-entire
|
||||
|
@ -382,19 +382,20 @@ class SetlocalCommandTest : VimTestCase() {
|
||||
setOsSpecificOptionsToSafeValues()
|
||||
assertCommandOutput("setlocal all", """
|
||||
|--- Local option values ---
|
||||
|noargtextobj ideastrictmode norelativenumber startofline
|
||||
| closenotebooks noideatracetime scroll=0 nosurround
|
||||
|nocommentary ideawrite=all scrolljump=1 notextobj-entire
|
||||
|nodigraph noignorecase scrolloff=-1 notextobj-indent
|
||||
|noexchange noincsearch selectmode= timeout
|
||||
|nogdefault nomatchit shellcmdflag=-x timeoutlen=1000
|
||||
|nohighlightedyank maxmapdepth=20 shellxescape=@ notrackactionids
|
||||
| history=50 more shellxquote={ unifyjumps
|
||||
|nohlsearch nomultiple-cursors showcmd virtualedit=
|
||||
|noideaglobalmode noNERDTree showmode novisualbell
|
||||
|--ideajoin nrformats=hex sidescroll=0 visualdelay=100
|
||||
| ideamarks nonumber sidescrolloff=-1 whichwrap=b,s
|
||||
| idearefactormode= octopushandler nosmartcase wrapscan
|
||||
|noargtextobj noideatracetime scrolljump=1 notextobj-entire
|
||||
| closenotebooks ideawrite=all scrolloff=-1 notextobj-indent
|
||||
|nocommentary noignorecase selectmode= timeout
|
||||
|nodigraph noincsearch shellcmdflag=-x timeoutlen=1000
|
||||
|noexchange nomatchit shellxescape=@ notrackactionids
|
||||
|nogdefault maxmapdepth=20 shellxquote={ unifyjumps
|
||||
|nohighlightedyank more showcmd virtualedit=
|
||||
| history=50 nomultiple-cursors showmode novisualbell
|
||||
|nohlsearch noNERDTree sidescroll=0 visualdelay=100
|
||||
|noideaglobalmode nrformats=hex sidescrolloff=-1 whichwrap=b,s
|
||||
|--ideajoin nonumber nosmartcase wrapscan
|
||||
| ideamarks octopushandler nosneak
|
||||
| idearefactormode= norelativenumber startofline
|
||||
| ideastrictmode scroll=0 nosurround
|
||||
| clipboard=ideaput,autoselect,exclude:cons\|linux
|
||||
| excommandannotation
|
||||
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
|
||||
@ -497,6 +498,7 @@ class SetlocalCommandTest : VimTestCase() {
|
||||
| sidescroll=0
|
||||
| sidescrolloff=-1
|
||||
|nosmartcase
|
||||
|nosneak
|
||||
| startofline
|
||||
|nosurround
|
||||
|notextobj-entire
|
||||
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2003-2024 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 org.jetbrains.plugins.ideavim.extension.sneak
|
||||
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInfo
|
||||
|
||||
class IdeaVimSneakTest : VimTestCase() {
|
||||
@Throws(Exception::class)
|
||||
override fun setUp(testInfo: TestInfo) {
|
||||
super.setUp(testInfo)
|
||||
enableExtensions("sneak")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForward() {
|
||||
val before = "som${c}e text"
|
||||
val after = "some te${c}xt"
|
||||
|
||||
doTest("sxt", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForwardVertical() {
|
||||
val before = """som${c}e text
|
||||
another line
|
||||
third line"""
|
||||
val after = """some text
|
||||
another line
|
||||
thi${c}rd line"""
|
||||
|
||||
doTest("srd", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForwardDoNotIgnoreCase() {
|
||||
val before = "som${c}e teXt"
|
||||
val after = "som${c}e teXt"
|
||||
|
||||
doTest("sxt", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForwardIgnoreCase() {
|
||||
val before = "som${c}e teXt"
|
||||
val after = "some te${c}Xt"
|
||||
|
||||
enableExtensions("ignorecase")
|
||||
|
||||
doTest("sxt", before, after, Mode.NORMAL())
|
||||
doTest("sXt", before, after, Mode.NORMAL())
|
||||
doTest("sXT", before, after, Mode.NORMAL())
|
||||
doTest("sxT", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForwardSmartIgnoreCase() {
|
||||
val before = "som${c}e teXt"
|
||||
val after = "some te${c}Xt"
|
||||
|
||||
enableExtensions("ignorecase", "smartcase")
|
||||
|
||||
doTest("sxt", before, after, Mode.NORMAL())
|
||||
doTest("sXt", before, after, Mode.NORMAL())
|
||||
doTest("sXT", before, before, Mode.NORMAL())
|
||||
doTest("sxT", before, before, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForwardAndFindAgain() {
|
||||
val before = "som${c}e text text"
|
||||
val after = "some text te${c}xt"
|
||||
|
||||
doTest("sxt;", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakForwardAndFindReverseAgain() {
|
||||
val before = "some tex${c}t text"
|
||||
val after = "some ${c}text text"
|
||||
|
||||
doTest("ste,", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakBackward() {
|
||||
val before = "some tex${c}t"
|
||||
val after = "so${c}me text"
|
||||
|
||||
doTest("Sme", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakBackwardVertical() {
|
||||
val before = """some text
|
||||
another line
|
||||
thi${c}rd line"""
|
||||
val after = """so${c}me text
|
||||
another line
|
||||
third line"""
|
||||
|
||||
doTest("Sme", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakBackwardAndFindAgain() {
|
||||
// caret has to be before another character (space here)
|
||||
val before = "some text text${c} "
|
||||
val after = "some ${c}text text "
|
||||
|
||||
doTest("Ste;", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSneakBackwardAndFindReverseAgain() {
|
||||
val before = "some tex${c}t text"
|
||||
val after = "some text ${c}text"
|
||||
|
||||
doTest("Ste,", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEndOfFile() {
|
||||
val before = """first line
|
||||
some te${c}xt
|
||||
another line
|
||||
last line."""
|
||||
val after = """first line
|
||||
some text
|
||||
another line
|
||||
last lin${c}e."""
|
||||
|
||||
doTest("se.", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStartOfFile() {
|
||||
val before = """first line
|
||||
some text
|
||||
another${c} line
|
||||
last line."""
|
||||
val after = """${c}first line
|
||||
some text
|
||||
another line
|
||||
last line."""
|
||||
|
||||
doTest("Sfi", before, after, Mode.NORMAL())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEscapeFirstChar() {
|
||||
val before = "so${c}me dwarf"
|
||||
val after = "some ${c}dwarf"
|
||||
|
||||
doTest("sa<ESC>sdw", before, after, Mode.NORMAL())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user