1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-07-05 05:38:52 +02:00

Compare commits

..

24 Commits

Author SHA1 Message Date
295f0b6c53
Set plugin version to chylex-45 2025-03-27 11:30:21 +01:00
2643301ea9
Actions and Enter are broken in Rider... 2025-03-27 11:30:08 +01:00
51f15782af
Make camelCase motions adjust based on direction of visual selection 2025-03-27 11:30:08 +01:00
de930ed57c
Make search highlights temporary 2025-03-25 09:25:25 +01:00
590d5bd22d
Exit insert mode after refactoring 2025-03-25 09:25:25 +01:00
69c748d881
Add action to run last macro in all opened files 2025-03-25 09:25:25 +01:00
144cc5c3fc
Stop macro execution after a failed search 2025-03-25 09:25:24 +01:00
fb270cdbc5
Revert per-caret registers 2025-03-25 09:25:24 +01:00
ce6a296233
Fix(VIM-3364): Exception with mapped Generate action 2025-03-25 09:25:24 +01:00
949f359b98
Apply scrolloff after executing native IDEA actions 2025-03-25 09:25:24 +01:00
c922426e02
Stay on same line after reindenting 2025-03-25 09:25:24 +01:00
9240e82f2d
Update search register when using f/t 2025-03-25 09:25:24 +01:00
a1639d80b0
Automatically add unambiguous imports after running a macro 2025-03-25 09:25:24 +01:00
7860b98107
Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2025-03-25 09:25:24 +01:00
7157f9c5a5
Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2025-03-25 09:25:24 +01:00
1522618cd6
Add support for count for visual and line motion surround 2025-03-25 09:25:24 +01:00
09862c8356
Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2025-03-25 09:25:24 +01:00
c6ef3f286f
Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2025-03-25 09:25:24 +01:00
b358e63444
Respect count with <Action> mappings 2025-03-25 09:25:24 +01:00
7ac743c604
Change matchit plugin to use HTML patterns in unrecognized files 2025-03-25 09:25:24 +01:00
db3d3fc608
Reset insert mode when switching active editor 2025-03-25 09:25:24 +01:00
14d313907b
Remove notifications about configuration options 2025-03-25 09:25:24 +01:00
fe37a69544
Remove update checker 2025-03-25 09:25:24 +01:00
54de3dac25
Set custom plugin version 2025-03-25 09:25:24 +01:00
227 changed files with 3135 additions and 4011 deletions

View File

@ -28,12 +28,10 @@ jobs:
uses: jtalk/url-health-check-action@v3
with:
url: http://127.0.0.1:8082
max-attempts: 100
max-attempts: 20
retry-delay: 10s
- name: Tests
run: gradle :tests:ui-rd-tests:testUi
env:
RIDER_LICENSE: ${{ secrets.RIDER_LICENSE }}
- name: Move video
if: always()
run: mv tests/ui-rd-tests/video build/reports

View File

@ -5,11 +5,11 @@ object Constants {
const val EAP_CHANNEL = "eap"
const val DEV_CHANNEL = "Dev"
const val NVIM_TESTS = "2025.1"
const val PROPERTY_TESTS = "2025.1"
const val LONG_RUNNING_TESTS = "2025.1"
const val RELEASE = "2025.1"
const val NVIM_TESTS = "2024.3.3"
const val PROPERTY_TESTS = "2024.3.3"
const val LONG_RUNNING_TESTS = "2024.3.3"
const val RELEASE = "2024.3.3"
const val RELEASE_DEV = "2025.1"
const val RELEASE_EAP = "2025.1"
const val RELEASE_DEV = "2024.3.3"
const val RELEASE_EAP = "2024.3.3"
}

View File

@ -23,8 +23,8 @@ object Project : Project({
vcsRoot(ReleasesVcsRoot)
// Active tests
buildType(TestingBuildType("Latest EAP", version = "LATEST-EAP-SNAPSHOT"))
buildType(TestingBuildType("2025.1"))
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(TestingBuildType("2024.3.3", "<default>"))
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(PropertyBased)

View File

@ -43,8 +43,6 @@ object Compatibility : IdeaVimBuildType({
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.peekaboo' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.switch' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.miksuki.HighlightCursor' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.ugarosa.idea.edgemotion' [latest-IU] -team-city
""".trimIndent()
}
}

View File

@ -24,7 +24,6 @@ object PropertyBased : IdeaVimBuildType({
steps {
gradle {
clearConditions()
tasks = "clean :tests:property-tests:testPropertyBased"
buildFile = ""
enableStacktrace = true

View File

@ -12,7 +12,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
open class TestingBuildType(
private val testName: String,
private val branch: String = "<default>",
private val branch: String,
private val version: String = testName,
private val javaVersion: String? = null,
private val javaPlugin: Boolean = true,

View File

@ -26,13 +26,6 @@ Previous maintainers:
&nbsp;
Andrey Vlasovskikh
Previous support members:
* [![icon][mail]](mailto:lejia.chen@jetbrains.com)
[![icon][github-off]](#)
&nbsp;
Lejia Chen
Contributors:
* [![icon][mail]](mailto:yole@jetbrains.com)
@ -594,22 +587,6 @@ Contributors:
[![icon][github]](https://github.com/Iliya-usov)
&nbsp;
Ilya Usov
* [![icon][mail]](mailto:peterHoburg@users.noreply.github.com)
[![icon][github]](https://github.com/peterHoburg)
&nbsp;
Peter Hoburg
* [![icon][mail]](mailto:erotourtes@gmail.com)
[![icon][github]](https://github.com/erotourtes)
&nbsp;
Max Siryk
* [![icon][mail]](mailto:ivan.yarkov@jetbrains.com)
[![icon][github]](https://github.com/MToolMakerJB)
&nbsp;
Ivan Yarkov
* [![icon][mail]](mailto:mia.vucinic@jetbrains.com)
[![icon][github]](https://github.com/vumi19)
&nbsp;
Mia Vucinic
Previous contributors:

View File

@ -92,26 +92,20 @@ Here are some examples of supported vim features and commands:
[IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/IdeaVim-Plugins):
* argtextobj
* commentary
* easymotion
* exchange
* FunctionTextObj
* highlightedyank
* indent-object
* matchit.vim
* Mini.ai
* multiple-cursors
* vim-easymotion
* NERDTree
* paragraph-motion
* Peekaboo
* quick-scope
* vim-surround
* vim-multiple-cursors
* vim-commentary
* argtextobj.vim
* vim-textobj-entire
* ReplaceWithRegister
* sneak
* surround
* Switch
* textobj-entire
* Which-Key
* vim-exchange
* vim-highlightedyank
* vim-paragraph-motion
* vim-indent-object
* match.it
etc
See also:

View File

@ -21,7 +21,7 @@ repositories {
}
dependencies {
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.21-2.0.1")
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.10-1.0.29")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
exclude("org.jetbrains.kotlin", "kotlin-stdlib")

View File

@ -31,7 +31,6 @@ import kotlinx.serialization.json.putJsonObject
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RepositoryBuilder
import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.ast.impl.ListCompositeNode
import org.jetbrains.changelog.Changelog
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
@ -51,14 +50,14 @@ buildscript {
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.2.0.202503040940-r")
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:3.1.3")
classpath("io.ktor:ktor-client-cio:3.1.3")
classpath("io.ktor:ktor-client-auth:3.1.3")
classpath("io.ktor:ktor-client-content-negotiation:3.1.3")
classpath("io.ktor:ktor-serialization-kotlinx-json:3.1.3")
classpath("io.ktor:ktor-client-core:3.1.1")
classpath("io.ktor:ktor-client-cio:3.1.1")
classpath("io.ktor:ktor-client-auth:3.1.1")
classpath("io.ktor:ktor-client-content-negotiation:3.1.1")
classpath("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
// This comes from the changelog plugin
// classpath("org.jetbrains:markdown:0.3.1")
@ -70,12 +69,7 @@ plugins {
kotlin("jvm") version "2.0.21"
application
id("java-test-fixtures")
// NOTE: Unignore "test block comment falls back to line comment when not available" test
// After changing this version. It supposed to work on the next version of the gradle plugin
// Or go report to the devs that this test still fails.
id("org.jetbrains.intellij.platform") version "2.5.0"
id("org.jetbrains.intellij.platform") version "2.3.0"
id("org.jetbrains.changelog") version "2.2.1"
id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "4.0.1"
@ -118,11 +112,7 @@ dependencies {
intellijPlatform {
// Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
var useInstaller = "EAP-SNAPSHOT" !in ideaVersion
if (ideaType == "RD") {
// Using Rider as a target IntelliJ Platform with `useInstaller = true` is currently not supported, please set `useInstaller = false` instead. See: https://github.com/JetBrains/intellij-platform-gradle-plugin/issues/1852
useInstaller = false
}
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
// Note that it is also possible to use local("...") to compile against a locally installed IDE
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
@ -137,7 +127,7 @@ dependencies {
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.19")
plugin("com.intellij.classic.ui", "251.23774.318")
plugin("com.intellij.classic.ui", "243.21565.122")
bundledPlugins("org.jetbrains.plugins.terminal", "com.intellij.modules.json")
}
@ -161,17 +151,17 @@ dependencies {
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.12.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.12.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.12.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
// Can be removed when IJPL-159134 is fixed
// testRuntimeOnly("junit:junit:4.13.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
}
@ -260,7 +250,6 @@ tasks {
// a custom task (see below)
runIde {
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
systemProperty("idea.trust.all.projects", "true")
}
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies
@ -833,9 +822,7 @@ fun updateAuthors(uncheckedEmails: Set<String>) {
org.intellij.markdown.parser.MarkdownParser(org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor())
val tree = parser.buildMarkdownTreeFromString(authors)
val contributorsSection = tree.children
.filter { it is ListCompositeNode }
.single { it.getTextInNode(authors).contains("yole") }
val contributorsSection = tree.children[24]
val existingEmails = mutableSetOf<String>()
for (child in contributorsSection.children) {
if (child.children.size > 1) {

View File

@ -16,95 +16,10 @@ in `~/.ideavimrc`. E.g. `set nosurround`.
Available plugins:
<details>
<summary><h2>argtextobj: Provides a text-object 'a' argument</h2></summary>
Original plugin: [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699).
### Summary:
This plugin provides a text-object 'a' (argument).
You can d(elete), c(hange), v(select)... an argument or inner argument in familiar ways.
That is, such as 'daa'(delete-an-argument) 'cia'(change-inner-argument) 'via'(select-inner-argument).
What this script does is more than just typing
F,dt,
because it recognizes inclusion relationship of parentheses.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/argtextobj.vim'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'argtextobj.vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
<br/>
<code>set argtextobj</code>
</details>
### Instructions
By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
this syntax parenthesis, must come before angle brackets in the list.
https://www.vim.org/scripts/script.php?script_id=2699
</details>
<details>
<summary><h2>commentary: Adds mapping for quickly commenting stuff out</h2></summary>
By [Daniel Leong](https://github.com/dhleong)
Original plugin: [commentary.vim](https://github.com/tpope/vim-commentary).
### Summary:
Comment stuff out.
Use gcc to comment out a line (takes a count), gc to comment out the target of a motion
(for example, gcap to comment out a paragraph), gc in visual mode to comment out the selection,
and gc in operator pending mode to target a comment.
You can also use it as a command, either with a range like :7,17Commentary,
or as part of a :global invocation like with :g/TODO/Commentary.
That's it.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-commentary'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tpope/vim-commentary'</code>
<br/>
<code>Plug 'https://github.com/tpope/vim-commentary'</code>
<br/>
<code>Plug 'vim-commentary'</code>
<br/>
<code>Plug 'tcomment_vim'</code>
<br/>
<code>set commentary</code>
</details>
### Instructions
https://github.com/tpope/vim-commentary/blob/master/doc/commentary.txt
</details>
<details>
<summary><h2>easymotion: Simplifies some motions</h2></summary>
<summary><h2>easymotion</h2></summary>
Original plugin: [vim-easymotion](https://github.com/easymotion/vim-easymotion).
### Summary:
EasyMotion provides a much simpler way to use some motions in vim.
It takes the \<number> out of \<number>w or \<number>f{char} by highlighting all possible choices
and allowing you to press one key to jump directly to the target.
### Setup:
- Install [IdeaVim-EasyMotion](https://plugins.jetbrains.com/plugin/13360-ideavim-easymotion/)
and [AceJump](https://plugins.jetbrains.com/plugin/7086-acejump/) plugins.
@ -126,176 +41,80 @@ All commands with the mappings are supported. See the [full list of supported co
</details>
<details>
<summary><h2>exchange: Easy text exchange operator</h2></summary>
<summary><h2>sneak</h2></summary>
By [fan-tom](https://github.com/fan-tom)
Original plugin: [vim-exchange](https://github.com/tommcdo/vim-exchange).
### Summary:
Easy text exchange operator for Vim.
<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:
- Add the following command to `~/.ideavimrc`: `Plug 'tommcdo/vim-exchange'`
- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
### Instructions
* 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>
<details>
<summary><h2>NERDTree</h2></summary>
Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'preservim/nerdtree'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tommcdo/vim-exchange'</code>
<code>Plugin 'preservim/nerdtree'</code>
<br/>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code>
<code>Plug 'https://github.com/preservim/nerdtree'</code>
<br/>
<code>Plug 'vim-exchange'</code>
<code>Plug 'nerdtree'</code>
<br/>
<code>set exchange</code>
<code>set NERDTree</code>
</details>
### Instructions
https://github.com/tommcdo/vim-exchange/blob/master/doc/exchange.txt
[See here](NERDTree-support.md).
</details>
<details>
<summary><h2>FunctionTextObj: Adds text objects for manipulating functions/methods</h2></summary>
By Julien Phalip
### Summary:
An extension for IdeaVim that adds text objects for manipulating functions/methods in your code.
Similar to how iw operates on words or i" operates on quoted strings,
this plugin provides if and af to operate on functions
### Setup
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
</details>
<details>
<summary><h2>highlightedyank: Highlights the yanked region</h2></summary>
By [KostkaBrukowa](https://github.com/KostkaBrukowa)
Original plugin: [vim-highlightedyank](https://github.com/machakann/vim-highlightedyank).
### Summary:
Make the yanked region apparent!
<summary><h2>surround</h2></summary>
Original plugin: [vim-surround](https://github.com/tpope/vim-surround).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'machakann/vim-highlightedyank'`
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-surround'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'machakann/vim-highlightedyank'</code>
<code>Plugin 'tpope/vim-surround'</code>
<br/>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
<br/>
<code>Plug 'vim-highlightedyank'</code>
<code>Plug 'vim-surround'</code>
<br/>
<code>set highlightedyank</code>
<code>set surround</code>
</details>
### Instructions
If you want to optimize highlight duration, assign a time in milliseconds:
`let g:highlightedyank_highlight_duration = "1000"`
A negative number makes the highlight persistent.
If you want to change background color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
If you want to change text color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
https://github.com/tpope/vim-surround/blob/master/doc/surround.txt
</details>
<details>
<summary><h2>indent-object: Adds text objects for manipulating sentences/paragraphs/etc...</h2></summary>
By [Shrikant Sharat Kandula](https://github.com/sharat87)
Original plugin: [vim-indent-object](https://github.com/michaeljsmith/vim-indent-object).
### Summary:
Vim text objects provide a convenient way to select and operate on various types of objects.
These objects include regions surrounded by various types of brackets and various parts of language
(ie sentences, paragraphs, etc).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'michaeljsmith/vim-indent-object'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'https://github.com/michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'vim-indent-object'</code>
<br/>
<code>set textobj-indent</code>
</details>
### Instructions
https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt
</details>
<details>
<summary><h2>matchit.vim: Extends the % key functionality</h2></summary>
By [Martin Yzeiri](https://github.com/myzeiri)
Original plugin: [matchit.vim](https://github.com/chrisbra/matchit).
### Summary:
In Vim, as in plain vi, the percent key, |%|, jumps the cursor from a brace, bracket, or paren to its match.
This can be configured with the 'matchpairs' option.
The matchit plugin extends this in several ways...
### Setup:
- Add the following command to `~/.ideavimrc`: `packadd matchit`
<details>
<summary>Alternative syntax</summary>
<code>Plug 'vim-matchit'</code>
<br/>
<code>Plug 'chrisbra/matchit'</code>
<br/>
<code>set matchit</code>
</details>
### Instructions
https://github.com/adelarsq/vim-matchit/blob/master/doc/matchit.txt
</details>
<details>
<summary><h2>Mini.ai: Extend and create a/i textobjects (IMPORTANT: The plugin is not related with artificial intelligence)</h2></summary>
### Summary:
Extend and create a/i textobjects
### Features:
Provides additional text object motions for handling quotes and brackets. The following motions are included:
- aq: Around any quotes.
- iq: Inside any quotes.
- ab: Around any parentheses, curly braces, and square brackets.
- ib: Inside any parentheses, curly braces, and square brackets.
Original plugin: [mini.ai](https://github.com/echasnovski/mini.ai).
### Setup:
- Add the following command to `~/.ideavimrc`: `set mini-ai`
</details>
<details>
<summary><h2>multiple-cursors: Extends multicursor support</h2></summary>
<summary><h2>multiple-cursors</h2></summary>
Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'terryma/vim-multiple-cursors'`
<details>
@ -308,7 +127,7 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
<br/>
<code>set multiple-cursors</code>
</details>
### Instructions
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
@ -334,118 +153,38 @@ xmap <leader>g<C-n> <Plug>AllOccurrences
</details>
<details>
<summary><h2>NERDTree: Adds NERDTree navigation to the project panel</h2></summary>
Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
<summary><h2>commentary</h2></summary>
### Summary:
Adds NERDTree navigation to the project panel.
By [Daniel Leong](https://github.com/dhleong)
Original plugin: [commentary.vim](https://github.com/tpope/vim-commentary).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'preservim/nerdtree'`
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-commentary'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'preservim/nerdtree'</code>
<code>Plugin 'tpope/vim-commentary'</code>
<br/>
<code>Plug 'https://github.com/preservim/nerdtree'</code>
<code>Plug 'https://github.com/tpope/vim-commentary'</code>
<br/>
<code>Plug 'nerdtree'</code>
<code>Plug 'vim-commentary'</code>
<br/>
<code>set NERDTree</code>
<code>Plug 'tcomment_vim'</code>
<br/>
<code>set commentary</code>
</details>
### Instructions
[See here](NERDTree-support.md).
https://github.com/tpope/vim-commentary/blob/master/doc/commentary.txt
</details>
<details>
<summary><h2>paragraph-motion: Extends the { and } motions to ignore whitespace on otherwise empty lines</h2></summary>
Original plugin: [vim-paragraph-motion](https://github.com/dbakker/vim-paragraph-motion).
### Summary:
Normally the { and } motions only match completely empty lines.
With this plugin lines that only contain whitespace are also matched.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'dbakker/vim-paragraph-motion'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'Improved-paragraph-motion'</code>
<br/>
<code>set vim-paragraph-motion</code>
</details>
### Instructions
https://github.com/dbakker/vim-paragraph-motion#vim-paragraph-motion
</details>
<details>
<summary><h2>Peekaboo: Extends " @ CTRL-r to show a popup of the register contents</h2></summary>
By Julien Phalip
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
### Summary:
Peekaboo extends " and @ in normal mode and <CTRL-R> in insert mode so you can see the contents of the registers.
### Setup
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
</details>
<details>
<summary><h2>quick-scope: Always-on highlight for a unique character in every word on a line to help use f, F, etc.</h2></summary>
Original plugin: [quick-scope](https://github.com/unblevable/quick-scope).
### Summary:
An always-on highlight for a unique character in every word on a line to help you use f, F and family.
This plugin should help you get to any word on a line in two or three keystrokes with Vim's built-in f<char>
(which moves your cursor to <char>).
### Setup:
- Install [IdeaVim-Quickscope](https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope) plugin.
- Add the following command to `~/.ideavimrc`: `set quickscope`
### Instructions
https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope
</details>
<details>
<summary><h2>ReplaceWithRegister: Adds two-in-one command that replaces text with the contents of a register.</h2></summary>
<summary><h2>ReplaceWithRegister</h2></summary>
By [igrekster](https://github.com/igrekster)
Original plugin: [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister).
### Summary:
This plugin offers a two-in-one command that replaces text covered by a
{motion}, entire line(s) or the current selection with the contents of a
register; the old text is deleted into the black-hole register, i.e. it's
gone. (But of course, the command can be easily undone.)
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/ReplaceWithRegister'`
<details>
@ -464,99 +203,78 @@ gone. (But of course, the command can be easily undone.)
<br/>
<code>set ReplaceWithRegister</code>
</details>
### Instructions
https://github.com/vim-scripts/ReplaceWithRegister/blob/master/doc/ReplaceWithRegister.txt
</details>
<details>
<summary><h2>sneak: Jump to any location specified by two characters</h2></summary>
<summary><h2>argtextobj</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).
### Summary:
Jump to any location specified by two characters.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
### Instructions
* 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>
<details>
<summary><h2>surround: Adds provides mappings to easily delete, change, and add surroundings in pairs</h2></summary>
Original plugin: [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699).
Original plugin: [vim-surround](https://github.com/tpope/vim-surround).
### Summary:
Surround.vim is all about "surroundings": parentheses, brackets, quotes, XML tags, and more.
The plugin provides mappings to easily delete, change and add such surroundings in pairs.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-surround'`
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/argtextobj.vim'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tpope/vim-surround'</code>
<code>Plugin 'vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
<code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'vim-surround'</code>
<code>Plug 'argtextobj.vim'</code>
<br/>
<code>set surround</code>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
<br/>
<code>set argtextobj</code>
</details>
### Instructions
https://github.com/tpope/vim-surround/blob/master/doc/surround.txt
By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
this syntax parenthesis, must come before angle brackets in the list.
https://www.vim.org/scripts/script.php?script_id=2699
</details>
<details>
<summary><h2>Switch: Switch some text under the cursor based on regex patterns</h2></summary>
By Julien Phalip
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
### Summary:
The purpose of the plugin is to switch some text under the cursor based on regex patterns.
The main entry point is a single command, :Switch.
When the command is executed,
the plugin looks for one of a few specific patterns under the cursor and performs a substitution depending on it.
### Setup
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
<summary><h2>exchange</h2></summary>
By [fan-tom](https://github.com/fan-tom)
Original plugin: [vim-exchange](https://github.com/tommcdo/vim-exchange).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tommcdo/vim-exchange'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'vim-exchange'</code>
<br/>
<code>set exchange</code>
</details>
### Instructions
https://plugins.jetbrains.com/plugin/25899-vim-switch
https://github.com/tommcdo/vim-exchange/blob/master/doc/exchange.txt
</details>
<details>
<summary><h2>textobj-entire: Adds mapping for selecting entire contents of file regardless of cursor position</h2></summary>
<summary><h2>textobj-entire</h2></summary>
By [Alexandre Grison](https://github.com/agrison)
Original plugin: [vim-textobj-entire](https://github.com/kana/vim-textobj-entire).
### Summary:
vim-textobj-entire is a Vim plugin to provide text objects
(ae and ie by default) to select the entire content of a buffer.
Though these are trivial operations (e.g. ggVG), text object versions are more handy,
because you do not have to be conscious of the cursor position (e.g. vae).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'kana/vim-textobj-entire'`
<details>
@ -577,13 +295,158 @@ https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
</details>
<details>
<summary><h2>Which-Key: Displays available keybindings in popup</h2></summary>
<summary><h2>highlightedyank</h2></summary>
By [KostkaBrukowa](https://github.com/KostkaBrukowa)
Original plugin: [vim-highlightedyank](https://github.com/machakann/vim-highlightedyank).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'machakann/vim-highlightedyank'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'vim-highlightedyank'</code>
<br/>
<code>set highlightedyank</code>
</details>
### Instructions
If you want to optimize highlight duration, assign a time in milliseconds:
`let g:highlightedyank_highlight_duration = "1000"`
A negative number makes the highlight persistent.
If you want to change background color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
If you want to change text color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
</details>
<details>
<summary><h2>vim-paragraph-motion</h2></summary>
Original plugin: [vim-paragraph-motion](https://github.com/dbakker/vim-paragraph-motion).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'dbakker/vim-paragraph-motion'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'Improved-paragraph-motion'</code>
<br/>
<code>set vim-paragraph-motion</code>
</details>
### Instructions
https://github.com/dbakker/vim-paragraph-motion#vim-paragraph-motion
</details>
<details>
<summary><h2>vim-indent-object</h2></summary>
By [Shrikant Sharat Kandula](https://github.com/sharat87)
Original plugin: [vim-indent-object](https://github.com/michaeljsmith/vim-indent-object).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'michaeljsmith/vim-indent-object'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'https://github.com/michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'vim-indent-object'</code>
<br/>
<code>set textobj-indent</code>
</details>
### Instructions
https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt
</details>
<details>
<summary><h2>matchit.vim</h2></summary>
By [Martin Yzeiri](https://github.com/myzeiri)
Original plugin: [matchit.vim](https://github.com/chrisbra/matchit).
### Setup:
- Add the following command to `~/.ideavimrc`: `packadd matchit`
<details>
<summary>Alternative syntax</summary>
<code>Plug 'vim-matchit'</code>
<br/>
<code>Plug 'chrisbra/matchit'</code>
<br/>
<code>set matchit</code>
</details>
### Instructions
https://github.com/adelarsq/vim-matchit/blob/master/doc/matchit.txt
</details>
<details>
<summary><h2>IdeaVim-Quickscope</h2></summary>
Original plugin: [quick-scope](https://github.com/unblevable/quick-scope).
### Setup:
- Install [IdeaVim-Quickscope](https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope) plugin.
- Add the following command to `~/.ideavimrc`: `set quickscope`
### Instructions
https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope
</details>
<details>
<summary><h2>Mini.ai: Extend and create a/i textobjects (IMPORTANT: The plugin is not related with artificial intelligence)</h2></summary>
### Features:
Provides additional text object motions for handling quotes and brackets. The following motions are included:
- aq: Around any quotes.
- iq: Inside any quotes.
- ab: Around any parentheses, curly braces, and square brackets.
- ib: Inside any parentheses, curly braces, and square brackets.
Original plugin: [mini.ai](https://github.com/echasnovski/mini.ai).
### Setup:
- Add the following command to `~/.ideavimrc`: `set mini-ai`
</details>
<details>
<summary><h2>Which-Key</h2></summary>
Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
### Summary:
vim-which-key is vim port of emacs-which-key that displays available keybindings in popup.
### Setup:
- Install [Which-Key](https://plugins.jetbrains.com/plugin/15976-which-key) plugin.
- Add the following command to `~/.ideavimrc`: `set which-key`
@ -593,3 +456,49 @@ vim-which-key is vim port of emacs-which-key that displays available keybindings
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
</details>
<details>
<summary><h2>Vim Peekaboo</h2></summary>
By Julien Phalip
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
### Setup
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
</details>
<details>
<summary><h2>FunctionTextObj</h2></summary>
By Julien Phalip
### Setup
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
</details>
<details>
<summary><h2>Switch</h2></summary>
By Julien Phalip
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
### Setup
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25899-vim-switch

View File

@ -16,11 +16,11 @@
# https://data.services.jetbrains.com/products?code=IC
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
ideaVersion=2025.1
ideaVersion=2024.3
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC
instrumentPluginCode=true
version=chylex-47
version=chylex-45
javaVersion=21
remoteRobotVersion=0.11.23
antlrVersion=4.10.1

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

3
gradlew vendored
View File

@ -86,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

View File

@ -20,17 +20,17 @@ repositories {
}
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.21")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.10")
implementation("io.ktor:ktor-client-core:3.1.3")
implementation("io.ktor:ktor-client-cio:3.1.3")
implementation("io.ktor:ktor-client-content-negotiation:3.1.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.3")
implementation("io.ktor:ktor-client-auth:3.1.3")
implementation("io.ktor:ktor-client-core:3.1.1")
implementation("io.ktor:ktor-client-cio:3.1.1")
implementation("io.ktor:ktor-client-content-negotiation:3.1.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
implementation("io.ktor:ktor-client-auth:3.1.1")
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.2.0.202503040940-r")
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
implementation("com.vdurmont:semver4j:3.1.0")
}

View File

@ -43,8 +43,6 @@ val knownPlugins = setOf(
"com.julienphalip.ideavim.peekaboo", // https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
"com.julienphalip.ideavim.switch", // https://plugins.jetbrains.com/plugin/25899-vim-switch
"com.julienphalip.ideavim.functiontextobj", // https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
"com.miksuki.HighlightCursor", // https://plugins.jetbrains.com/plugin/26743-highlightcursor
"com.ugarosa.idea.edgemotion", // https://plugins.jetbrains.com/plugin/27211-edgemotion
)
suspend fun main() {

View File

@ -355,12 +355,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (unsubscribe) {
VimListenerManager.INSTANCE.turnOff();
}
// Use getServiceIfCreated to avoid creating the service during the dispose (this is prohibited by the platform)
@Nullable VimCommandLineService service =
ApplicationManager.getApplication().getServiceIfCreated(VimCommandLineService.class);
if (service != null) {
service.fullReset();
}
injector.getCommandLine().fullReset();
// Unregister vim actions in command mode
RegisterActions.unregisterActions();
@ -376,7 +371,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (isEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
stateUpdated = true;
if (SystemInfo.isMac) {
final Boolean enabled = MacKeyRepeat.INSTANCE.isEnabled();
final MacKeyRepeat keyRepeat = MacKeyRepeat.getInstance();
final Boolean enabled = keyRepeat.isEnabled();
final Boolean isKeyRepeat = getEditor().isKeyRepeat();
if ((enabled == null || !enabled) && (isKeyRepeat == null || isKeyRepeat)) {
// This system property is used in IJ ui robot to hide the startup tips
@ -386,7 +382,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (showNotification) {
if (VimPlugin.getNotifications().enableRepeatingMode() == Messages.YES) {
getEditor().setKeyRepeat(true);
MacKeyRepeat.INSTANCE.setEnabled(true);
keyRepeat.setEnabled(true);
}
else {
getEditor().setKeyRepeat(false);

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.action
import com.google.common.collect.ImmutableSet
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.lookup.LookupManager
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
@ -183,6 +182,9 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
}
if (editor.inInsertMode) {
if (keyCode == KeyEvent.VK_ENTER) {
return ActionEnableStatus.no("Enter action in insert mode", LogLevel.INFO)
}
if (keyCode == KeyEvent.VK_TAB) {
// TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view
// There are multiple actions registered for VK_TAB. The important items, in order, are this, the Live
@ -209,10 +211,6 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
return ActionEnableStatus.yes("Vim only editor keys", LogLevel.INFO)
}
if (CompletionService.getCompletionService().currentCompletion != null) {
return ActionEnableStatus.no("Code completion active", LogLevel.INFO)
}
val savedShortcutConflicts = VimPlugin.getKey().savedShortcutConflicts
val info = savedShortcutConflicts[keyStroke]
return when (info?.forEditor(editor.vim)) {

View File

@ -0,0 +1,78 @@
/*
* 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.command
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.VimStateMachine
import org.jetbrains.annotations.ApiStatus
/**
* COMPATIBILITY-LAYER: Additional class
* Please see: https://jb.gg/zo8n0r
*/
@Deprecated("Use `injector.vimState`")
@ApiStatus.ScheduledForRemoval
class CommandState(private val machine: VimStateMachine) {
val mode: Mode
get() {
val myMode = machine.mode
return when (myMode) {
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
}
}
@get:Deprecated(
"Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
"KeyHandler.getInstance().keyHandlerState.commandBuilder",
"com.maddyhome.idea.vim.KeyHandler"
)
)
@get:ApiStatus.ScheduledForRemoval
val commandBuilder: CommandBuilder
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
@Deprecated(
"Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
"KeyHandler.getInstance().keyHandlerState.mappingState",
"com.maddyhome.idea.vim.KeyHandler"
)
)
val mappingState: MappingState
get() = KeyHandler.getInstance().keyHandlerState.mappingState
enum class Mode {
// Basic modes
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
// Additional modes
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
}
enum class SubMode {
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
}
companion object {
@JvmStatic
@Deprecated("Use `injector.vimState`")
@ApiStatus.ScheduledForRemoval
fun getInstance(editor: Editor): CommandState {
return CommandState(injector.vimState)
}
}
}

View File

@ -11,17 +11,16 @@ package com.maddyhome.idea.vim.customization.feature.terminal
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.key.IdeaVimDisablerExtensionPoint
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isAlternateBufferEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isAlternateBufferModelEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputModelEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isPromptEditor
/**
* The only implementation is defined right here.
*/
// [VERSION UPDATE] 2025.1+ Add 2 new predicates
internal class IdeaVimTerminalDisablerExtension : IdeaVimDisablerExtensionPoint {
override fun isDisabledForEditor(editor: Editor): Boolean {
return editor.isPromptEditor || editor.isOutputEditor || editor.isAlternateBufferEditor
|| editor.isOutputModelEditor || editor.isAlternateBufferModelEditor
// || editor.isOutputModelEditor || editor.isAlternateBufferModelEditor
}
}

View File

@ -214,13 +214,7 @@ object VimExtensionFacade {
/** Set the current contents of the given register */
@JvmStatic
fun setRegisterForCaret(
editor: VimEditor,
context: ExecutionContext,
register: Char,
caret: ImmutableVimCaret,
keys: List<KeyStroke?>?,
) {
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
}

View File

@ -221,16 +221,14 @@ internal class CommentaryExtension : VimExtension {
val endOffset = editor.vim.getLineEndOffset(logicalLine, true)
val startElement = file.findElementAt(startOffset) ?: return false
var next: PsiElement? = startElement
var hasComment = false
while (next != null && next.textRange.startOffset <= endOffset) {
when {
next is PsiWhiteSpace -> {} // Skip whitespace elementl
isComment(next) -> hasComment = true // Mark when we find a comment
else -> return false // Non-comment content found, exit early
if (next !is PsiWhiteSpace && !isComment(next)) {
return false
}
next = PsiTreeUtil.nextLeaf(next, true)
}
return hasComment
return true
}
private fun isComment(element: PsiElement) =

View File

@ -11,7 +11,6 @@ import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
@ -142,10 +141,6 @@ internal class VimSurroundExtension : VimExtension {
runWriteAction {
// Leave visual mode
editor.exitVisualMode()
// Reset the key handler so that the command trie is updated for the new mode (Normal)
// TODO: This should probably be handled by ToHandlerMapping.execute
KeyHandler.getInstance().reset(editor)
}
}
}
@ -174,8 +169,8 @@ internal class VimSurroundExtension : VimExtension {
val surroundings = editor.sortedCarets()
.map {
val oldValue: List<KeyStroke>? = getRegisterForCaret(editor, context, REGISTER, it)
setRegisterForCaret(editor, context, REGISTER, it, null)
SurroundingInfo(editor, context, it, null, oldValue, false)
setRegisterForCaret(REGISTER, it, null)
SurroundingInfo(it, null, oldValue, false)
}
// Delete surrounding's content
@ -264,16 +259,9 @@ internal class VimSurroundExtension : VimExtension {
}
}
private data class SurroundingInfo(
val editor: VimEditor,
val context: ExecutionContext,
val caret: VimCaret,
var innerText: List<KeyStroke>?,
val oldRegisterContent: List<KeyStroke>?,
var isValidSurrounding: Boolean,
) {
private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?, var isValidSurrounding: Boolean) {
fun restoreRegister() {
setRegisterForCaret(editor, context, REGISTER, caret, oldRegisterContent)
setRegisterForCaret(REGISTER, caret, oldRegisterContent)
}
}

View File

@ -7,7 +7,7 @@
*/
package com.maddyhome.idea.vim.group
import com.intellij.codeInsight.actions.AsyncActionExecutionService
import com.intellij.codeInsight.actions.AsyncActionExecutionService.Companion.getInstance
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.application.ApplicationManager
@ -15,7 +15,6 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.openapi.editor.event.EditorMouseListener
@ -34,7 +33,7 @@ import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.commandContinuation
import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.key.KeyHandlerKeeper
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
import com.maddyhome.idea.vim.listener.VimInsertListener
import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
@ -62,21 +61,8 @@ class ChangeGroup : VimChangeGroupBase() {
}
override fun type(vimEditor: VimEditor, context: ExecutionContext, key: Char) {
doType(vimEditor, context) {
it.execute(vimEditor.ij, key, context.ij)
}
}
override fun type(vimEditor: VimEditor, context: ExecutionContext, string: String) {
doType(vimEditor, context) { handler ->
string.forEach { char ->
handler.execute(vimEditor.ij, char, context.ij)
}
}
}
private fun doType(vimEditor: VimEditor, context: ExecutionContext, action: (TypedActionHandler) -> Unit) {
val editor = (vimEditor as IjVimEditor).editor
val ijContext = context.ij
val doc = vimEditor.editor.document
val undo = injector.undo
@ -89,9 +75,8 @@ class ChangeGroup : VimChangeGroupBase() {
}
CommandProcessor.getInstance().executeCommand(
editor.project, {
ApplicationManager.getApplication().runWriteAction {
action(KeyHandlerKeeper.getInstance().originalHandler)
}
ApplicationManager.getApplication()
.runWriteAction { getInstance().originalHandler.execute(editor, key, ijContext) }
}, "", doc,
UndoConfirmationPolicy.DEFAULT, doc
)
@ -165,7 +150,7 @@ class ChangeGroup : VimChangeGroupBase() {
var copiedText: IjVimCopiedText? = null
try {
if (injector.registerGroup.isPrimaryRegisterSupported()) {
copiedText = injector.clipboardManager.getPrimaryContent() as IjVimCopiedText
copiedText = injector.clipboardManager.getPrimaryContent(editor, context) as IjVimCopiedText
}
} catch (e: Exception) {
// FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
@ -184,7 +169,7 @@ class ChangeGroup : VimChangeGroupBase() {
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
}
if (project != null) {
AsyncActionExecutionService.getInstance(project)
getInstance(project)
.withExecutionAfterAction(IdeActions.ACTION_EDITOR_AUTO_INDENT_LINES, actionExecution, afterAction)
} else {
actionExecution.invoke()

View File

@ -31,8 +31,10 @@ open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(sco
// Temporary options to control work-in-progress behaviour
var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
var oldundo: Boolean by optionProperty(IjOptions.oldundo)
var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
}
/**

View File

@ -141,8 +141,12 @@ object IjOptions {
// Temporary feature flags during development, not really intended for external use
val closenotebooks: ToggleOption =
addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
val commandOrMotionAnnotation: ToggleOption =
addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
val vimscriptFunctionAnnotation: ToggleOption =
addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
// derives from Option<VimInt>

View File

@ -58,7 +58,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
public void registerRequiredShortcutKeys(@NotNull VimEditor editor) {
EventFacade.getInstance()
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()),
((IjVimEditor)editor).getEditor().getContentComponent());
((IjVimEditor)editor).getEditor().getComponent());
}
public void registerShortcutsForLookup(@NotNull LookupImpl lookup) {
@ -69,7 +69,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
void unregisterShortcutKeys(@NotNull VimEditor editor) {
EventFacade.getInstance().unregisterCustomShortcutSet(VimShortcutKeyAction.getInstance(),
((IjVimEditor)editor).getEditor().getContentComponent());
((IjVimEditor)editor).getEditor().getComponent());
}
@Override

View File

@ -37,7 +37,6 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.KeyMapIssue
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.icons.VimIcons
import com.maddyhome.idea.vim.key.ShortcutOwner
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
import com.maddyhome.idea.vim.newapi.globalIjOptions
@ -136,30 +135,8 @@ internal class NotificationService(private val project: Project?) {
).notify(project)
}
/**
* Shows a notification that the user can reenable IdeaVim by clicking on the IdeaVim icon in the status bar.
*/
fun showReenableNotification(project: Project) {
val notification = Notification(
IDEAVIM_NOTIFICATION_ID,
IDEAVIM_NOTIFICATION_TITLE,
"IdeaVim has been disabled. You can reenable it by clicking on the gray IdeaVim icon in the status bar.",
NotificationType.INFORMATION,
)
notification.icon = VimIcons.IDEAVIM_DISABLED
notification.addAction(object : DumbAwareAction("Reenable IdeaVim") {
override fun actionPerformed(e: AnActionEvent) {
VimPlugin.setEnabled(true)
notification.expire()
}
})
notification.notify(project)
}
fun notifyActionId(id: String?, candidates: List<String>? = null, intentionName: String?) {
ActionIdNotifier.notifyActionId(id, project, candidates, intentionName)
fun notifyActionId(id: String?, candidates: List<String>? = null) {
ActionIdNotifier.notifyActionId(id, project, candidates)
}
fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
@ -237,15 +214,12 @@ internal class NotificationService(private val project: Project?) {
object ActionIdNotifier {
private var notification: Notification? = null
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null, intentionName: String? = null) {
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null) {
notification?.expire()
val possibleIDs = candidates?.distinct()?.sorted()
val content = when {
id != null -> "Action ID: <code>$id</code><br><br>"
possibleIDs.isNullOrEmpty() && !intentionName.isNullOrEmpty() -> {
"Intention \"$intentionName\" does not have an action ID.<br><br>"
}
possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>"
possibleIDs.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>"
else -> {

View File

@ -44,7 +44,7 @@ internal class SystemMarks {
internal fun Project.createLineBookmark(editor: Editor, line: Int, mnemonic: Char): LineBookmark? {
val bookmarksManager = BookmarksManager.getInstance(this) ?: return null
val lineBookmarkProvider = LineBookmarkProvider.Util.find(this) ?: return null
val lineBookmarkProvider = LineBookmarkProvider.find(this) ?: return null
val bookmark = lineBookmarkProvider.createBookmark(editor, line) as LineBookmark? ?: return null
val type = BookmarkType.get(mnemonic)
if (type == BookmarkType.DEFAULT) return null

View File

@ -19,7 +19,6 @@ import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.ide.CopyPasteManager
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
@ -207,9 +206,8 @@ internal class PutGroup : VimPutBase() {
startOffset: Int,
endOffset: Int,
): Int {
// Temp fix for VIM-2808 for Rider and Clion. Should be removed after rider will fix it's issues
// Disable for client due to VIM-3857
if (isRider() || isClionNova() || PlatformUtils.isJetBrainsClient()) return endOffset
// Temp fix for VIM-2808. Should be removed after rider will fix it's issues
if (isRider() || isClionNova()) return endOffset
val startLine = editor.offsetToBufferPosition(startOffset).line
val endLine = editor.offsetToBufferPosition(endOffset - 1).line

View File

@ -96,7 +96,7 @@ internal object IdeaSelectionControl {
} else {
logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}")
if (editor.vim.inVisualMode) editor.vim.exitVisualMode()
if (editor.vim.inSelectMode) editor.vim.exitSelectMode(false)
if (editor.vim.inSelectMode) editor.exitSelectMode(false)
if (editor.vim.inNormalMode) {
activateMode(editor, chooseNonSelectionMode(editor))

View File

@ -17,6 +17,7 @@ import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.jetbrains.rd.util.ConcurrentHashMap
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key
@ -27,8 +28,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import org.jetbrains.annotations.NonNls
import java.util.concurrent.ConcurrentHashMap
// We use alarm with delay to avoid many actions in case many events are fired at the same time
@ -68,7 +67,11 @@ internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener {
check(correctorRequester.tryEmit(Unit))
}
override fun shortcutsChanged(keymap: Keymap, actionIds: @NonNls Collection<String>, fromSettings: Boolean) {
override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(correctorRequester.tryEmit(Unit))
}
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
check(correctorRequester.tryEmit(Unit))
}
}

View File

@ -28,7 +28,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import org.jetbrains.annotations.NonNls
import javax.swing.KeyStroke
// We use alarm with delay to avoid many notifications in case many events are fired at the same time
@ -68,7 +67,11 @@ internal class IdeaVimKeymapChangedListener : KeymapManagerListener {
check(keyCheckRequests.tryEmit(Unit))
}
override fun shortcutsChanged(keymap: Keymap, actionIds: @NonNls Collection<String>, fromSettings: Boolean) {
override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(keyCheckRequests.tryEmit(Unit))
}
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
check(keyCheckRequests.tryEmit(Unit))
}
}

View File

@ -60,7 +60,7 @@ internal fun Editor.updateCaretsVisualAttributes() {
* Used when Vim emulation is disabled
*/
internal fun Editor.removeCaretsVisualAttributes() {
caretModel.allCarets.forEach { it.visualAttributes = CaretVisualAttributes.getDefault() }
caretModel.allCarets.forEach { it.visualAttributes = CaretVisualAttributes.DEFAULT }
}
internal fun Editor.hasBlockOrUnderscoreCaret() = isBlockCursorOverride() ||
@ -95,8 +95,8 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
// NOTE: At the moment, this causes project leak in tests
// IJPL-928 - this will be fixed in 2025.2
// [VERSION UPDATE] 2025.2 - remove if wrapping
// IJPL-928 - this will be fixed in 2024.2
// [VERSION UPDATE] 2024.2 - remove if wrapping
if (!ApplicationManager.getApplication().isUnitTestMode) {
(this as? EditorEx)?.setCaretVisible(true)
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.helper
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
@Deprecated("Do not use context wrappers, use existing provided contexts. If no context available, use `injector.getExecutionContextManager().getEditorExecutionContext(editor)`")
internal class EditorDataContext @Deprecated("Please use `init` method") constructor(
private val editor: Editor,
private val editorContext: DataContext,
private val contextDelegate: DataContext? = null,
) : DataContext, UserDataHolder {
/**
* Returns the object corresponding to the specified data identifier. Some of the supported data identifiers are
* defined in the [PlatformDataKeys] class.
*
* @param dataId the data identifier for which the value is requested.
* @return the value, or null if no value is available in the current context for this identifier.
*/
override fun getData(dataId: String): Any? = when {
PlatformDataKeys.EDITOR.name == dataId -> editor
PlatformDataKeys.PROJECT.name == dataId -> editor.project
PlatformDataKeys.VIRTUAL_FILE.name == dataId -> EditorHelper.getVirtualFile(editor)
else -> editorContext.getData(dataId) ?: contextDelegate?.getData(dataId)
}
override fun <T : Any?> getUserData(key: Key<T>): T? {
return if (contextDelegate is UserDataHolder) {
contextDelegate.getUserData(key)
} else {
null
}
}
override fun <T : Any?> putUserData(key: Key<T>, value: T?) {
if (contextDelegate is UserDataHolder) {
contextDelegate.putUserData(key, value)
}
}
companion object {
@Suppress("DEPRECATION")
@JvmStatic
fun init(editor: Editor, contextDelegate: DataContext? = null): EditorDataContext {
val editorContext = EditorUtil.getEditorDataContext(editor)
return if (contextDelegate is EditorDataContext) {
if (editor === contextDelegate.editor) {
contextDelegate
} else {
EditorDataContext(editor, editorContext, contextDelegate.contextDelegate)
}
} else {
EditorDataContext(editor, editorContext, contextDelegate)
}
}
}
}

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.helper;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.EditorImpl;
@ -16,7 +15,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileUtil;
import com.intellij.testFramework.LightVirtualFile;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.api.VimEditor;
@ -654,21 +652,7 @@ public class EditorHelper {
*/
public static boolean isFileEditor(@NotNull Editor editor) {
final VirtualFile virtualFile = getVirtualFile(editor);
if (virtualFile == null) return false;
if (virtualFile instanceof LightVirtualFile) {
var hostVirtualFile = getHostFileFromInjectedFile(virtualFile);
if (hostVirtualFile == null) return false;
return !(hostVirtualFile instanceof LightVirtualFile);
}
return true;
}
private static @Nullable VirtualFile getHostFileFromInjectedFile(@NotNull VirtualFile virtualFile) {
final var vf = VirtualFileUtil.originalFileOrSelf(virtualFile);
if (vf instanceof VirtualFileWindow) {
return ((VirtualFileWindow)vf).getDelegate();
}
return null;
return virtualFile != null && !(virtualFile instanceof LightVirtualFile);
}
/**

View File

@ -14,7 +14,6 @@ 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.EditorKind
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.util.ui.table.JBTableRowEditor
@ -43,14 +42,9 @@ internal val Editor.isIdeaVimDisabledHere: Boolean
return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) ||
!ClientId.isCurrentlyUnderLocalId || // CWM-927
(ideaVimDisabledForSingleLine(ideaVimSupportValue) && isSingleLine()) ||
IdeaVimDisablerExtensionPoint.isDisabledForEditor(this) ||
isAiChat() // VIM-3786
IdeaVimDisablerExtensionPoint.isDisabledForEditor(this)
}
private fun Editor.isAiChat(): Boolean {
return EditorHelper.getVirtualFile(this)?.name?.contains("AIAssistantInput") == true
}
private fun ideaVimDisabledInDialog(ideaVimSupportValue: StringListOptionValue): Boolean {
return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog)
&& !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
@ -77,19 +71,6 @@ internal fun Editor.isPrimaryEditor(): Boolean {
return fileEditorManager.allEditors.any { fileEditor -> this == EditorUtil.getEditorEx(fileEditor) }
}
/**
* Checks if the editor should be treated like a terminal. I.e. switch to Insert mode automatically
*
* A "terminal" editor is an editor used for purposes other than mainstream editing, such as a terminal, console, log
* viewer, etc. In this instance, the editor is writable, the document is writable, but it's not backed by a real file
* and it's not the diff viewer. We also check that if it's an injected language fragment backed by a real file.
*/
internal fun Editor.isTerminalEditor(): Boolean {
return !isViewer
&& document.isWritable
&& this.editorKind == EditorKind.CONSOLE
}
// Optimized clone of com.intellij.ide.ui.laf.darcula.DarculaUIUtil.isTableCellEditor
private fun isTableCellEditor(c: Component): Boolean {
return (java.lang.Boolean.TRUE == (c as JComponent).getClientProperty("JComboBox.isTableCellEditor")) ||

View File

@ -8,15 +8,19 @@
package com.maddyhome.idea.vim.helper
import com.intellij.execution.actions.StopAction
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.EmptyAction
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.actionSystem.impl.Utils
import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy
@ -24,7 +28,9 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.util.registry.Registry
import com.maddyhome.idea.vim.RegisterActions
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.NativeAction
@ -32,11 +38,14 @@ import com.maddyhome.idea.vim.api.VimActionExecutor
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.ide.isRider
import com.maddyhome.idea.vim.newapi.IjNativeAction
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.runFromVimKey
import org.jetbrains.annotations.NonNls
import java.awt.Component
import javax.swing.JComponent
import javax.swing.SwingUtilities
@Service
internal class IjActionExecutor : VimActionExecutor {
@ -55,11 +64,8 @@ internal class IjActionExecutor : VimActionExecutor {
override val ACTION_EXPAND_REGION_RECURSIVELY: String
get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
override val ACTION_EXPAND_COLLAPSE_TOGGLE: String
get() = IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION
override val ACTION_UNDO: String
get() = IdeActions.ACTION_UNDO
override val ACTION_REDO: String
get() = IdeActions.ACTION_REDO
// [VERSION UPDATE] 2024.3+ Replace raw "ExpandCollapseToggleAction" with IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION from the platform.
get() = "ExpandCollapseToggleAction"
var isRunningActionFromVim: Boolean = false
@ -71,28 +77,78 @@ internal class IjActionExecutor : VimActionExecutor {
}
val ijAction = (action as IjNativeAction).action
try {
isRunningActionFromVim = true
// The context component should be editor. This is especially important when running the `:action` commands
// because at the moment of execution, the focused component is Ex Field, not editor.
val contextComponent = editor?.ij?.contentComponent
val place = ijAction.choosePlace()
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, place, true)
res.waitFor(5_000)
return res.isDone
} finally {
isRunningActionFromVim = false
if (Registry.`is`("ideavim.old.action.execution", true) || isRider()) {
return manualActionExecution(context, ijAction)
} else {
try {
isRunningActionFromVim = true
// The context component should be editor. This is especially important when running the `:action` commands
// because at the moment of execution, the focused component is Ex Field, not editor.
val contextComponent = editor?.ij?.contentComponent
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, "IdeaVim", true)
res.waitFor(5_000)
return res.isDone
} finally {
isRunningActionFromVim = false
}
}
}
// Note: We should find a proper place for the IdeaVim actions
// Currently, we use "IdeaVim" except a few actions
private fun AnAction.choosePlace(): String {
// StopAction works fine if `StopAction.isPlaceGlobal` returns true
// Or if there is a specific data stored in the context. This data, however, is stored
// only if the run window is in focus.
if (this is StopAction) return ActionPlaces.ACTION_SEARCH
return "IdeaVim"
private fun manualActionExecution(
context: ExecutionContext,
ijAction: AnAction,
): Boolean {
/**
* Data context that defines that some action was started from IdeaVim.
* You can call use [runFromVimKey] key to define if intellij action was started from IdeaVim
*/
val dataContext = SimpleDataContext.getSimpleContext(runFromVimKey, true, context.ij)
val actionId = ActionManager.getInstance().getId(ijAction)
@Suppress("removal", "DEPRECATION") val event = AnActionEvent(
null,
dataContext,
ActionPlaces.KEYBOARD_SHORTCUT,
ijAction.templatePresentation.clone(),
ActionManager.getInstance(),
0,
)
Utils.initUpdateSession(event)
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems
// because rider uses an async update method. See VIM-1819.
// This method executes inside lastUpdateAndCheckDumb
// Another related issue: VIM-2604
// This is a hack to fix the tests and fix VIM-3332
// We should get rid of it in VIM-3376
if (actionId == "RunClass" || actionId == IdeActions.ACTION_COMMENT_LINE || actionId == IdeActions.ACTION_COMMENT_BLOCK) {
@Suppress("removal", "OverrideOnly", "DEPRECATION")
ijAction.beforeActionPerformedUpdate(event)
if (!event.presentation.isEnabled) return false
} else {
if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
}
if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
// Some ActionGroups should not be performed but shown as a popup
val popup = JBPopupFactory.getInstance()
.createActionGroupPopup(event.presentation.text, ijAction, dataContext, false, null, -1)
val component = dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT)
if (component != null) {
val window = SwingUtilities.getWindowAncestor(component)
if (window != null) {
popup.showInCenterOf(window)
}
return true
}
popup.showInFocusCenter()
return true
} else {
performDumbAwareWithCallbacks(ijAction, event) {
@Suppress("OverrideOnly")
ijAction.actionPerformed(event)
}
return true
}
}
/**

View File

@ -0,0 +1,75 @@
/*
* 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.google.common.io.CharStreams;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @author vlan
*/
public class MacKeyRepeat {
@VimNlsSafe public static final String FMT = "defaults %s -globalDomain ApplePressAndHoldEnabled";
@NotNull private static final MacKeyRepeat INSTANCE = new MacKeyRepeat();
@NonNls private static final String EXEC_COMMAND = "launchctl stop com.apple.SystemUIServer.agent";
@NonNls private static final String delete = "delete";
@NonNls private static final String write = "write";
@NonNls private static final String read = "read";
public static @NotNull MacKeyRepeat getInstance() {
return INSTANCE;
}
private static @NotNull String read(@NotNull InputStream stream) throws IOException {
return CharStreams.toString(new InputStreamReader(stream));
}
public @Nullable Boolean isEnabled() {
final String command = String.format(FMT, read);
try {
final Process process = Runtime.getRuntime().exec(command);
final String data = read(process.getInputStream()).trim();
try {
return Integer.parseInt(data) == 0;
}
catch (NumberFormatException e) {
return null;
}
}
catch (IOException e) {
return null;
}
}
public void setEnabled(@Nullable Boolean value) {
final String command;
if (value == null) {
command = String.format(FMT, delete);
}
else {
final String arg = value ? "0" : "1";
command = String.format(FMT, write) + " " + arg;
}
try {
final Runtime runtime = Runtime.getRuntime();
final Process defaults = runtime.exec(command);
defaults.waitFor();
final Process restartSystemUI = runtime.exec(EXEC_COMMAND);
restartSystemUI.waitFor();
}
catch (IOException | InterruptedException ignored) {
}
}
}

View File

@ -1,54 +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.google.common.io.CharStreams
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
object MacKeyRepeat {
var isEnabled: Boolean?
get() {
return try {
val process = Runtime.getRuntime().exec(READ_COMMAND)
val data = read(process.inputStream).trim().toIntOrNull() ?: return null
data == 0
} catch (_: IOException) {
null
}
}
set(value) {
val command: Array<String>
if (value == null) {
command = DELETE_COMMAND
} else {
val arg = if (value) "0" else "1"
command = WRITE_COMMAND + arg
}
try {
val runtime = Runtime.getRuntime()
val defaults = runtime.exec(command)
defaults.waitFor()
val restartSystemUI: Process = runtime.exec(EXEC_COMMAND)
restartSystemUI.waitFor()
} catch (_: IOException) {
} catch (_: InterruptedException) {
}
}
private val EXEC_COMMAND = arrayOf("launchctl", "stop", "com.apple.SystemUIServer.agent")
private val READ_COMMAND = arrayOf("defaults", "read", "-globalDomain", "ApplePressAndHoldEnabled")
private val WRITE_COMMAND = arrayOf("defaults", "write", "-globalDomain", "ApplePressAndHoldEnabled")
private val DELETE_COMMAND = arrayOf("defaults", "delete", "-globalDomain", "ApplePressAndHoldEnabled")
@Throws(IOException::class)
private fun read(stream: InputStream): String {
return CharStreams.toString(InputStreamReader(stream))
}
}

View File

@ -24,6 +24,28 @@ import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.inSelectMode
/** [adjustCaretPosition] - if true, caret will be moved one char left if it's on the line end */
internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
val vimEditor = this.vim
if (!vimEditor.inSelectMode) return
vimEditor.mode = vimEditor.mode.returnTo
SelectionVimListenerSuppressor.lock().use {
this.caretModel.allCarets.forEach {
// NOTE: I think it should be write action, but the exception shows only an absence of the read action
injector.application.runReadAction { it.removeSelection() }
it.vim.vimSelectionStartClear()
if (adjustCaretPosition && !vimEditor.isEndAllowed) {
val lineEnd = IjVimEditor(this).getLineEndForOffset(it.offset)
val lineStart = IjVimEditor(this).getLineStartForOffset(it.offset)
if (it.offset == lineEnd && it.offset != lineStart) {
it.moveToInlayAwareOffset(it.offset - 1)
}
}
}
}
}
/** [adjustCaretPosition] - if true, caret will be moved one char left if it's on the line end */
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.inSelectMode) return

View File

@ -42,7 +42,7 @@ public class PsiHelper {
if (file == null) {
return -1;
}
StructureViewBuilder structureViewBuilder = LanguageStructureViewBuilder.getInstance().getStructureViewBuilder(file);
StructureViewBuilder structureViewBuilder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(file);
if (!(structureViewBuilder instanceof TreeBasedStructureViewBuilder builder)) return -1;
StructureViewModel model = builder.createStructureViewModel(editor);

View File

@ -19,7 +19,6 @@ import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.TextEditorWithPreview
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
@ -44,12 +43,6 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
}
override fun undo(editor: VimEditor, context: ExecutionContext): Boolean {
if (PlatformUtils.isJetBrainsClient()) {
// Note: Remote Dev has special hacks for undo/redo, so we don't use the manager.
// The action is sent directly to the backend using the internal API.
return injector.actionExecutor.executeAction(editor, injector.actionExecutor.ACTION_UNDO, context)
}
val ijContext = context.context as DataContext
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val textEditor = getTextEditor(editor.ij)
@ -113,10 +106,6 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
}
override fun redo(editor: VimEditor, context: ExecutionContext): Boolean {
if (PlatformUtils.isJetBrainsClient()) {
return injector.actionExecutor.executeAction(editor, injector.actionExecutor.ACTION_REDO, context)
}
val ijContext = context.context as DataContext
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val textEditor = getTextEditor(editor.ij)

View File

@ -10,6 +10,8 @@ package com.maddyhome.idea.vim.listener
import com.intellij.execution.impl.ConsoleViewImpl
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorKind
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.LastUsedEditorInfo
import com.maddyhome.idea.vim.VimPlugin
@ -17,8 +19,8 @@ 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.common.EditorListener
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.helper.isTerminalEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.Mode
@ -55,7 +57,7 @@ class IJEditorFocusListener : EditorListener {
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
val ijEditor = editor.ij
val isCurrentEditorTerminal = ijEditor.isTerminalEditor()
val isCurrentEditorTerminal = isTerminal(ijEditor)
KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, false)
@ -64,10 +66,8 @@ class IJEditorFocusListener : EditorListener {
VimPlugin.getChange().insertBeforeCursor(editor, context)
KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, true)
}
if (isCurrentEditorTerminal) {
if (!ijEditor.inInsertMode) {
switchToInsertMode.run()
}
if (isCurrentEditorTerminal && !ijEditor.inInsertMode) {
switchToInsertMode.run()
} else if (ijEditor.isInsertMode && (oldEditorInfo.isInsertModeForced || !ijEditor.document.isWritable)) {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
val mode = injector.vimState.mode
@ -87,4 +87,12 @@ class IJEditorFocusListener : EditorListener {
}
KeyHandler.getInstance().reset(editor)
}
// By "terminal" we refer to some editor that should switch to INSERT mode on focus
private fun isTerminal(ijEditor: Editor): Boolean {
return !ijEditor.isViewer &&
!EditorHelper.isFileEditor(ijEditor) &&
ijEditor.document.isWritable &&
ijEditor.editorKind != EditorKind.DIFF
}
}

View File

@ -20,7 +20,6 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.codeInsight.template.impl.TemplateState
import com.intellij.codeInsight.template.impl.actions.NextVariableAction
import com.intellij.find.FindModelListener
import com.intellij.ide.actions.ApplyIntentionAction
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
@ -100,14 +99,10 @@ internal object IdeaSpecifics {
} else {
emptyList()
}
val intentionName = if (action is ApplyIntentionAction) {
action.name
}
else null
// We can still get empty ID and empty candidates. Notably, for the tool window toggle buttons on the new UI.
// We could filter out action events with `place == ActionPlaces.TOOLWINDOW_TOOLBAR_BAR`
VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates, intentionName)
VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates)
}
}

View File

@ -9,7 +9,6 @@
package com.maddyhome.idea.vim.listener
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.AnActionResult
@ -17,8 +16,6 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.ex.AnActionListener
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorAction
import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.event.CaretEvent
import com.intellij.openapi.editor.event.CaretListener
import com.maddyhome.idea.vim.VimPlugin
@ -27,16 +24,10 @@ import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.newapi.vim
internal class RiderActionListener : AnActionListener {
private var editor: Editor? = null
private fun shouldExecuteOnFrontend(action: EditorAction): Boolean {
val isInsertMode = editor?.vim?.insertMode
return isInsertMode == false && action is EnterAction
}
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
if (VimPlugin.isNotEnabled()) return
@ -44,12 +35,6 @@ internal class RiderActionListener : AnActionListener {
if (hostEditor != null) {
editor = hostEditor
}
// Fixes RIDER-123506
if (action is EditorAction) {
val key = ActionPlaces.EXECUTE_EDITOR_ACTION_ON_FRONTEND
editor?.putUserData(key, shouldExecuteOnFrontend(action))
}
}
override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {

View File

@ -468,9 +468,7 @@ internal object VimListenerManager {
openingEditor == null -> LocalOptionInitialisationScenario.EDIT
else -> LocalOptionInitialisationScenario.NEW
}
SlowOperations.knownIssue("VIM-3648").use {
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
}
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
firstEditorInitialised = true
} else {
// We've got a virtual file, so FileOpenedSyncListener will be called. Save data
@ -819,7 +817,7 @@ internal object VimListenerManager {
if (editor.inVisualMode) {
editor.vim.exitVisualMode()
} else if (editor.vim.inSelectMode) {
editor.vim.exitSelectMode(false)
editor.exitSelectMode(false)
KeyHandler.getInstance().reset(editor.vim)
}
}

View File

@ -18,7 +18,7 @@ internal class IntellijMark(bookmark: LineBookmark, override val col: Int, proje
private val project: WeakReference<Project?> = WeakReference(project)
override val key = BookmarksManager.getInstance(project)?.getType(bookmark)?.mnemonic ?: ' '
override val key = BookmarksManager.getInstance(project)?.getType(bookmark)?.mnemonic!!
override val line: Int
get() = getMark()?.line ?: 0
override val filepath: String

View File

@ -39,16 +39,26 @@ import java.io.IOException
@Service
internal class IjClipboardManager : VimClipboardManager {
override fun getPrimaryContent(): IjVimCopiedText? {
@Deprecated("Please use com.maddyhome.idea.vim.api.VimClipboardManager#getPrimaryTextAndTransferableData")
override fun getPrimaryTextAndTransferableData(): Pair<String, List<Any>?>? {
val clipboard = Toolkit.getDefaultToolkit()?.systemSelection ?: return null
val contents = clipboard.getContents(null) ?: return null
val (text, transferableData) = getTextAndTransferableData(contents) ?: return null
return getTextAndTransferableData(contents)
}
override fun getPrimaryContent(editor: VimEditor, context: ExecutionContext): IjVimCopiedText? {
val (text, transferableData) = getPrimaryTextAndTransferableData() ?: return null
return IjVimCopiedText(text, transferableData ?: emptyList())
}
override fun getClipboardContent(editor: VimEditor, context: ExecutionContext): VimCopiedText? {
@Deprecated("Please use com.maddyhome.idea.vim.api.VimClipboardManager#getClipboardTextAndTransferableData")
override fun getClipboardTextAndTransferableData(): Pair<String, List<Any>?>? {
val contents = getContents() ?: return null
val (text, transferableData) = getTextAndTransferableData(contents) ?: return null
return getTextAndTransferableData(contents)
}
override fun getClipboardContent(editor: VimEditor, context: ExecutionContext): VimCopiedText? {
val (text, transferableData) = getClipboardTextAndTransferableData() ?: return null
return IjVimCopiedText(text, transferableData ?: emptyList())
}
@ -115,6 +125,14 @@ internal class IjClipboardManager : VimClipboardManager {
// return setPrimaryText(entry.text, entry.rawText, entry.transferableData) != null
// }
@Deprecated("Please use com.maddyhome.idea.vim.api.VimClipboardManager#setPrimaryText")
override fun setPrimaryText(text: String, rawText: String, transferableData: List<Any>): Transferable? {
return handleTextSetting(text, rawText, transferableData) { content ->
val clipboard = Toolkit.getDefaultToolkit()?.systemSelection ?: return@handleTextSetting null
clipboard.setContents(content, EmptyClipboardOwner.INSTANCE)
}
}
override fun collectCopiedText(
editor: VimEditor,
context: ExecutionContext,
@ -242,6 +260,6 @@ internal class IjClipboardManager : VimClipboardManager {
}
}
data class IjVimCopiedText(override val text: String, override val transferableData: List<Any>) : VimCopiedText {
data class IjVimCopiedText(override val text: String, val transferableData: List<Any>) : VimCopiedText {
override fun updateText(newText: String): VimCopiedText = IjVimCopiedText(newText, transferableData)
}

View File

@ -32,7 +32,7 @@ internal class IjVimApplication : VimApplicationBase() {
return ApplicationManager.getApplication().isDispatchThread
}
override fun invokeLater(editor: VimEditor, action: () -> Unit) {
override fun invokeLater(action: () -> Unit, editor: VimEditor) {
ApplicationManager.getApplication()
.invokeLater(action, ModalityState.stateForComponent((editor as IjVimEditor).editor.component))
}

View File

@ -36,7 +36,7 @@ import com.maddyhome.idea.vim.api.VimIndentConfig
import com.maddyhome.idea.vim.api.VimScrollingModel
import com.maddyhome.idea.vim.api.VimSelectionModel
import com.maddyhome.idea.vim.api.VimVisualPosition
import com.maddyhome.idea.vim.api.VimVirtualFile
import com.maddyhome.idea.vim.api.VirtualFile
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.IndentConfig
import com.maddyhome.idea.vim.common.LiveRange
@ -301,13 +301,12 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
return editor.logicalPositionToOffset(logicalPosition)
}
override fun getVirtualFile(): VimVirtualFile? {
override fun getVirtualFile(): VirtualFile? {
val vf = EditorHelper.getVirtualFile(editor)
return vf?.let {
object : VimVirtualFile {
object : VirtualFile {
override val path: String = vf.path
override val protocol: String = vf.fileSystem.protocol
override val extension: String? = vf.extension
}
}
}

View File

@ -213,6 +213,16 @@ internal class IjVimInjector : VimInjectorBase() {
override val redrawService: VimRedrawService
get() = service()
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: VimEditor): VimStateMachine {
return vimState
}
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: Any): VimStateMachine {
return vimState
}
override val engineEditorHelper: EngineEditorHelper
get() = service<IjEditorHelper>()
override val editorGroup: VimEditorGroup

View File

@ -1,99 +0,0 @@
/*
* Copyright 2003-2025 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.troubleshooting
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotificationProvider
import com.intellij.ui.EditorNotifications
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.icons.VimIcons
import com.maddyhome.idea.vim.vimscript.services.VimRcService
import java.util.function.Function
import javax.swing.JComponent
private var warningExplicitlyDisabled = false
private object CommandsCounter {
var commandsBeforeAutoDisable = 10
var initialized = false
@Synchronized
fun init() {
if (initialized) return
initialized = true
KeyHandler.getInstance().addCommandListener {
commandsBeforeAutoDisable -= 1
if (commandsBeforeAutoDisable <= 0) {
warningExplicitlyDisabled = true
}
}
}
}
/**
* Warning for the new users who may install IdeaVim plugin accidentally.
*/
internal class AccidentalInstallDetectorEditorNotificationProvider : EditorNotificationProvider, DumbAware {
override fun collectNotificationData(
project: Project,
file: VirtualFile,
): Function<in FileEditor, out JComponent?>? {
CommandsCounter.init()
// Note: Currently, enable this only for GoLand as it was a request from this IDE (VIM-3784).
// However, we can enable it for other IDEs if needed.
if (!PlatformUtils.isGoIde()) return null
if (warningExplicitlyDisabled) return null
if (VimPlugin.isNotEnabled()) return null
if (VimRcService.findIdeaVimRc() != null) return null
if (!injector.enabler.isNewIdeaVimUser()) return null
return Function { fileEditor: FileEditor ->
val panel = EditorNotificationPanel(fileEditor, EditorNotificationPanel.Status.Info)
panel.text = getText()
panel.icon(VimIcons.IDEAVIM)
KeyHandler.getInstance().addCommandListener {
if (CommandsCounter.commandsBeforeAutoDisable <= 0) {
KeyHandler.getInstance().removeAllCommandListeners()
EditorNotifications.getInstance(project).removeNotificationsForProvider(this)
}
panel.text = getText()
panel.invalidate()
panel.repaint()
}
@Suppress("DialogTitleCapitalization")
panel.createActionLabel("Disable IdeaVim") {
VimPlugin.setEnabled(false)
VimPlugin.getNotifications(project).showReenableNotification(project)
EditorNotifications.getInstance(project).removeNotificationsForProvider(this)
warningExplicitlyDisabled = true
}
panel.createActionLabel("Dismiss") {
EditorNotifications.getInstance(project).removeNotificationsForProvider(this)
warningExplicitlyDisabled = true
}
panel
}
}
private fun getText(): String {
return "<html>Youre using the IdeaVim plugin. If youre not familiar with Vim, consider disabling it. This message will disappear after ${CommandsCounter.commandsBeforeAutoDisable} commands.</html>"
}
}

View File

@ -428,6 +428,14 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
return active;
}
/**
* @deprecated Use getVisibleText()
*/
@Deprecated(forRemoval = true)
public @NotNull String getText() {
return entry.getText();
}
@Override
public @NotNull String getVisibleText() {
return entry.getText();

View File

@ -132,9 +132,6 @@
key="ideavim.only.in.editor.component"/>
<registryKey defaultValue="false" description="Old action execution mechanism" key="ideavim.old.action.execution"
restartRequired="false"/>
<editorNotificationProvider
implementation="com.maddyhome.idea.vim.troubleshooting.AccidentalInstallDetectorEditorNotificationProvider"/>
</extensions>
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>

View File

@ -85,23 +85,11 @@ E545=E545: Missing colon: {0}
E546=E546: Illegal mode: {0}
E548=E548: Digit expected: {0}
E549=E549: Illegal percentage: {0}
E691=E691: Can only compare List with List
E692=E692: Invalid operation for List
E694=E694: Invalid operation for Funcrefs
E695=E695: Cannot index a Funcref
E701=E701: Invalid type for len()
E703=E703: Using a Funcref as a Number
E728=E728: Using a Dictionary as a Number
E729=E729: Using a Funcref as a String
E730=E730: Using a List as a String
E731=E731: Using a Dictionary as a String
E735=E735: Can only compare Dictionary with Dictionary
E736=E736: Invalid operation for Dictionary
E745=E745: Using a List as a Number
E774=E774: 'operatorfunc' is empty
E804=E804: Cannot use '%' with Float
E805=E805: Using a Float as a Number
E806=E806: Using a Float as a String
E808=E808: Number or Float required
e841.reserved.name.cannot.be.used.for.user.defined.command=E841: Reserved name, cannot be used for user defined command
E939=E939: Positive count required

View File

@ -1,108 +0,0 @@
/*
* Copyright 2003-2025 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.action
import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class ChangeCaseTest : VimTestCase() {
/**
* Note: The tests for duplicated commands (gugu and gUgU) might fail due to issues with the test environment,
* specifically related to file refresh operations. This is a known issue with the test infrastructure
* and not with the actual functionality being tested.
*
* The tests for guu and gUU test the same functionality and should pass.
*/
@Test
fun testChangeCaseLowerLineAction() {
typeTextInFile(
injector.parser.parseKeys("guu"),
"""
H${c}ELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent(),
)
assertState(
"""
${c}hello world
${c}this is a test
${c}for case conversion
""".trimIndent()
)
}
@Disabled("Not yet supported")
@Test
fun testChangeCaseLowerLineActionDuplicated() {
typeTextInFile(
injector.parser.parseKeys("gugu"),
"""
H${c}ELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent(),
)
assertState(
"""
${c}hello world
${c}this is a test
${c}for case conversion
""".trimIndent()
)
}
@Test
fun testChangeCaseUpperLineAction() {
typeTextInFile(
injector.parser.parseKeys("gUU"),
"""
h${c}ello world
${c}this is a test
${c}for case conversion
""".trimIndent(),
)
assertState(
"""
${c}HELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent()
)
}
@Disabled("Not yet supported")
@Test
fun testChangeCaseUpperLineActionDuplicated() {
typeTextInFile(
injector.parser.parseKeys("gUgU"),
"""
h${c}ello world
${c}this is a test
${c}for case conversion
""".trimIndent(),
)
assertState(
"""
${c}HELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent()
)
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright 2003-2025 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.action
import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class ChangeRot13Test : VimTestCase() {
@Test
fun testChangeRot13MotionAction() {
typeTextInFile(
injector.parser.parseKeys("g?2w"),
"H${c}ello World ${c}This is a ${c}test for ROT13 ${c}encoding\n",
)
assertState("H${c}ryyb Jbeyq ${c}Guvf vf a ${c}grfg sbe ROT13 ${c}rapbqvat\n")
}
@Test
fun testChangeRot13VisualAction() {
typeTextInFile(
injector.parser.parseKeys("v2wg?"),
"H${c}ello World ${c}This is a ${c}test for ROT13 ${c}encoding\n",
)
assertState("H${c}ryyb Jbeyq Guvf vf n ${c}grfg sbe EOT13 ${c}rapbqvat\n")
}
@Test
fun testChangeRot13LineAction() {
typeTextInFile(
injector.parser.parseKeys("g??"),
"""
H${c}ello World
${c}This is a test
${c}for ROT13 encoding
""".trimIndent(),
)
assertState(
"""
${c}Uryyb Jbeyq
${c}Guvf vf n grfg
${c}sbe EBG13 rapbqvat
""".trimIndent()
)
}
@Disabled("Not yet supported")
@Test
fun testChangeRot13LineActionDuplicated() {
typeTextInFile(
injector.parser.parseKeys("g?g?"),
"""
H${c}ello World
${c}This is a test
${c}for ROT13 encoding
""".trimIndent(),
)
assertState(
"""
${c}Uryyb Jbeyq
${c}Guvf vf n grfg
${c}sbe EBG13 rapbqvat
""".trimIndent()
)
}
@Test
fun testChangeRot13NonEnglishLetters() {
typeTextInFile(
injector.parser.parseKeys("g?$"),
"${c}Привет мир! Hello world!\n",
)
assertState("${c}Привет мир! Uryyb jbeyq!\n")
}
@Test
fun testChangeRot13FullAlphabet() {
typeTextInFile(
injector.parser.parseKeys("g?$"),
"${c}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n",
)
assertState("${c}nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM\n")
}
@Test
fun testChangeRot13Symbols() {
typeTextInFile(
injector.parser.parseKeys("g?$"),
"${c}!@#$%^&*()_+-=[]{}|;:'\",.<>/?\n",
)
assertState("${c}!@#$%^&*()_+-=[]{}|;:'\",.<>/?\n")
}
}

View File

@ -9,6 +9,7 @@
package org.jetbrains.plugins.ideavim.action.copy
import com.intellij.notification.ActionCenter
import com.intellij.notification.EventLog
import com.intellij.notification.Notification
import com.intellij.openapi.application.ApplicationManager
import com.maddyhome.idea.vim.VimPlugin
@ -104,7 +105,7 @@ class IdeaPutNotificationsTest : VimTestCase() {
}
typeText(injector.parser.parseKeys("p"))
val notifications = ActionCenter.getNotifications(fixture.project)
val notifications = EventLog.getLogModel(fixture.project).notifications
kotlin.test.assertTrue(notifications.isEmpty() || notifications.last().isExpired || OptionConstants.clipboard_ideaput !in notifications.last().content)
}

View File

@ -77,12 +77,4 @@ class AsciiCommandTest : VimTestCase() {
enterCommand("ascii")
assertEquals("<⓪> 9450, Hex 24ea, Oct 22352, Digr (0", VimPlugin.getMessage())
}
@Test
fun `test shows custom digraph with 32-bit Unicode codepoint`() {
configureByText("🔴")
enterCommand("digraph cr 128308")
enterCommand("ascii")
assertEquals("<🔴> 128308, Hex 1f534, Oct 372464, Digr cr", VimPlugin.getMessage())
}
}

View File

@ -33,26 +33,20 @@ class DigraphsCommandTest : VimTestCase() {
@Test
fun `test add custom digraph`() {
enterCommand("digraph (0 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
}
@Test
fun `test add custom 32-bit digraph`() {
enterCommand("digraph cr 128308")
assertEquals("🔴", String(Character.toChars(injector.digraphGroup.getCharacterForDigraph('c', 'r'))))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
}
@Test
fun `test add custom digraph matches reversed characters`() {
enterCommand("digraph (0 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('0', '(').toChar())
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('0', '('))
}
@Test
fun `test add multiple custom digraphs`() {
enterCommand("digraph (0 9450 (1 9312")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1').toChar())
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1'))
}
@Test
@ -86,14 +80,14 @@ class DigraphsCommandTest : VimTestCase() {
@Test
fun `test add custom digraph with more than two characters add custom digraph with initial two characters`() {
enterCommand("digraph aaaa 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('a', 'a').toChar())
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('a', 'a'))
}
@Test
fun `test add custom digraphs until error`() {
enterCommand("digraph (0 9450 (1 9312 (2")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1').toChar())
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1'))
assertPluginError(true)
assertPluginErrorMessageContains("E39: Number expected")
}
@ -101,15 +95,15 @@ class DigraphsCommandTest : VimTestCase() {
@Test
fun `test custom digraph overwrites existing custom digraph`() {
enterCommand("digraph (0 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
enterCommand("digraph (0 10003")
assertEquals('✓', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
assertEquals('✓', injector.digraphGroup.getCharacterForDigraph('(', '0'))
}
@Test
fun `test custom digraph overwrites existing default digraph`() {
enterCommand("digraph OK 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('O', 'K').toChar())
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('O', 'K'))
}
@Test
@ -871,244 +865,6 @@ class DigraphsCommandTest : VimTestCase() {
)
}
@Test
fun `test digraph output with 32-bit custom digraphs`() {
enterCommand("digraph cr 128308") // 🔴
assertCommandOutput(
"digraphs",
"""
|NU ^@ 10 SH ^A 1 SX ^B 2 EX ^C 3 ET ^D 4 EQ ^E 5
|AK ^F 6 BL ^G 7 BS ^H 8 HT ^I 9 LF ^J 10 VT ^K 11
|FF ^L 12 CR ^M 13 SO ^N 14 SI ^O 15 DL ^P 16 D1 ^Q 17
|D2 ^R 18 D3 ^S 19 D4 ^T 20 NK ^U 21 SY ^V 22 EB ^W 23
|CN ^X 24 EM ^Y 25 SB ^Z 26 EC ^[ 27 FS ^\ 28 GS ^] 29
|RS ^^ 30 US ^_ 31 SP 32 Nb # 35 DO $ 36 At @ 64
|<( [ 91 // \ 92 )> ] 93 '> ^ 94 '! ` 96 (! { 123
|!! | 124 !) } 125 '? ~ 126 DT ^? 127 PA <80> 128 HO <81> 129
|BH <82> 130 NH <83> 131 IN <84> 132 NL <85> 133 SA <86> 134 ES <87> 135
|HS <88> 136 HJ <89> 137 VS <8a> 138 PD <8b> 139 PU <8c> 140 RI <8d> 141
|S2 <8e> 142 S3 <8f> 143 DC <90> 144 P1 <91> 145 P2 <92> 146 TS <93> 147
|CC <94> 148 MW <95> 149 SG <96> 150 EG <97> 151 SS <98> 152 GC <99> 153
|SC <9a> 154 CI <9b> 155 ST <9c> 156 OC <9d> 157 PM <9e> 158 AC <9f> 159
|NS   160 !I ¡ 161 ~! ¡ 161 Ct ¢ 162 c| ¢ 162 Pd £ 163
|$$ £ 163 Cu ¤ 164 ox ¤ 164 Ye ¥ 165 Y- ¥ 165 BB ¦ 166
||| ¦ 166 SE § 167 ': ¨ 168 Co © 169 cO © 169 -a ª 170
|<< « 171 NO ¬ 172 -, ¬ 172 -- <ad> 173 Rg ® 174 'm ¯ 175
|-= ¯ 175 DG ° 176 ~o ° 176 +- ± 177 2S ² 178 22 ² 178
|3S ³ 179 33 ³ 179 '' ´ 180 My µ 181 PI 182 pp 182
|.M · 183 ~. · 183 ', ¸ 184 1S ¹ 185 11 ¹ 185 -o º 186
|>> » 187 14 ¼ 188 12 ½ 189 34 ¾ 190 ?I ¿ 191 ~? ¿ 191
|A! À 192 A` À 192 A' Á 193 A> Â 194 A^ Â 194 A? Ã 195
|A~ Ã 195 A: Ä 196 A" Ä 196 AA Å 197 A@ Å 197 AE Æ 198
|C, Ç 199 E! È 200 E` È 200 E' É 201 E> Ê 202 E^ Ê 202
|E: Ë 203 E" Ë 203 I! Ì 204 I` Ì 204 I' Í 205 I> Î 206
|I^ Î 206 I: Ï 207 I" Ï 207 D- Ð 208 N? Ñ 209 N~ Ñ 209
|O! Ò 210 O` Ò 210 O' Ó 211 O> Ô 212 O^ Ô 212 O? Õ 213
|O~ Õ 213 O: Ö 214 *X × 215 /\ × 215 O/ Ø 216 U! Ù 217
|U` Ù 217 U' Ú 218 U> Û 219 U^ Û 219 U: Ü 220 Y' Ý 221
|TH Þ 222 Ip Þ 222 ss ß 223 a! à 224 a` à 224 a' á 225
|a> â 226 a^ â 226 a? ã 227 a~ ã 227 a: ä 228 a" ä 228
|aa å 229 a@ å 229 ae æ 230 c, ç 231 e! è 232 e` è 232
|e' é 233 e> ê 234 e^ ê 234 e: ë 235 e" ë 235 i! ì 236
|i` ì 236 i' í 237 i> î 238 i^ î 238 i: ï 239 d- ð 240
|n? ñ 241 n~ ñ 241 o! ò 242 o` ò 242 o' ó 243 o> ô 244
|o^ ô 244 o? õ 245 o~ õ 245 o: ö 246 -: ÷ 247 o/ ø 248
|u! ù 249 u` ù 249 u' ú 250 u> û 251 u^ û 251 u: ü 252
|y' ý 253 th þ 254 y: ÿ 255 y" ÿ 255 A- Ā 256 a- ā 257
|A( Ă 258 a( ă 259 A; Ą 260 a; ą 261 C' Ć 262 c' ć 263
|C> Ĉ 264 c> ĉ 265 C. Ċ 266 c. ċ 267 C< Č 268 c< č 269
|D< Ď 270 d< ď 271 D/ Đ 272 d/ đ 273 E- Ē 274 e- ē 275
|E( Ĕ 276 e( ĕ 277 E. Ė 278 e. ė 279 E; Ę 280 e; ę 281
|E< Ě 282 e< ě 283 G> Ĝ 284 g> ĝ 285 G( Ğ 286 g( ğ 287
|G. Ġ 288 g. ġ 289 G, Ģ 290 g, ģ 291 H> Ĥ 292 h> ĥ 293
|H/ Ħ 294 h/ ħ 295 I? Ĩ 296 i? ĩ 297 I- Ī 298 i- ī 299
|I( Ĭ 300 i( ĭ 301 I; Į 302 i; į 303 I. İ 304 i. ı 305
|IJ IJ 306 ij ij 307 J> Ĵ 308 j> ĵ 309 K, Ķ 310 k, ķ 311
|kk ĸ 312 L' Ĺ 313 l' ĺ 314 L, Ļ 315 l, ļ 316 L< Ľ 317
|l< ľ 318 L. Ŀ 319 l. ŀ 320 L/ Ł 321 l/ ł 322 N' Ń 323
|n' ń 324 N, Ņ 325 n, ņ 326 N< Ň 327 n< ň 328 'n ʼn 329
|NG Ŋ 330 ng ŋ 331 O- Ō 332 o- ō 333 O( Ŏ 334 o( ŏ 335
|O" Ő 336 o" ő 337 OE Œ 338 oe œ 339 R' Ŕ 340 r' ŕ 341
|R, Ŗ 342 r, ŗ 343 R< Ř 344 r< ř 345 S' Ś 346 s' ś 347
|S> Ŝ 348 s> ŝ 349 S, Ş 350 s, ş 351 S< Š 352 s< š 353
|T, Ţ 354 t, ţ 355 T< Ť 356 t< ť 357 T/ Ŧ 358 t/ ŧ 359
|U? Ũ 360 u? ũ 361 U- Ū 362 u- ū 363 U( Ŭ 364 u( ŭ 365
|U0 Ů 366 u0 ů 367 U" Ű 368 u" ű 369 U; Ų 370 u; ų 371
|W> Ŵ 372 w> ŵ 373 Y> Ŷ 374 y> ŷ 375 Y: Ÿ 376 Z' Ź 377
|z' ź 378 Z. Ż 379 z. ż 380 Z< Ž 381 z< ž 382 O9 Ơ 416
|o9 ơ 417 OI Ƣ 418 oi ƣ 419 yr Ʀ 422 U9 Ư 431 u9 ư 432
|Z/ Ƶ 437 z/ ƶ 438 ED Ʒ 439 A< Ǎ 461 a< ǎ 462 I< Ǐ 463
|i< ǐ 464 O< Ǒ 465 o< ǒ 466 U< Ǔ 467 u< ǔ 468 A1 Ǟ 478
|a1 ǟ 479 A7 Ǡ 480 a7 ǡ 481 A3 Ǣ 482 a3 ǣ 483 G/ Ǥ 484
|g/ ǥ 485 G< Ǧ 486 g< ǧ 487 K< Ǩ 488 k< ǩ 489 O; Ǫ 490
|o; ǫ 491 O1 Ǭ 492 o1 ǭ 493 EZ Ǯ 494 ez ǯ 495 j< ǰ 496
|G' Ǵ 500 g' ǵ 501 ;S ʿ 703 '< ˇ 711 '( ˘ 728 '. ˙ 729
|'0 ˚ 730 '; ˛ 731 '" ˝ 733 A% Ά 902 E% Έ 904 Y% Ή 905
|I% Ί 906 O% Ό 908 U% Ύ 910 W% Ώ 911 i3 ΐ 912 A* Α 913
|B* Β 914 G* Γ 915 D* Δ 916 E* Ε 917 Z* Ζ 918 Y* Η 919
|H* Θ 920 I* Ι 921 K* Κ 922 L* Λ 923 M* Μ 924 N* Ν 925
|C* Ξ 926 O* Ο 927 P* Π 928 R* Ρ 929 S* Σ 931 T* Τ 932
|U* Υ 933 F* Φ 934 X* Χ 935 Q* Ψ 936 W* Ω 937 J* Ϊ 938
|V* Ϋ 939 a% ά 940 e% έ 941 y% ή 942 i% ί 943 u3 ΰ 944
|a* α 945 b* β 946 g* γ 947 d* δ 948 e* ε 949 z* ζ 950
|y* η 951 h* θ 952 i* ι 953 k* κ 954 l* λ 955 m* μ 956
|n* ν 957 c* ξ 958 o* ο 959 p* π 960 r* ρ 961 *s ς 962
|s* σ 963 t* τ 964 u* υ 965 f* φ 966 x* χ 967 q* ψ 968
|w* ω 969 j* ϊ 970 v* ϋ 971 o% ό 972 u% ύ 973 w% ώ 974
|'G Ϙ 984 ,G ϙ 985 T3 Ϛ 986 t3 ϛ 987 M3 Ϝ 988 m3 ϝ 989
|K3 Ϟ 990 k3 ϟ 991 P3 Ϡ 992 p3 ϡ 993 '% ϴ 1012 j3 ϵ 1013
|IO Ё 1025 D% Ђ 1026 G% Ѓ 1027 IE Є 1028 DS Ѕ 1029 II І 1030
|YI Ї 1031 J% Ј 1032 LJ Љ 1033 NJ Њ 1034 Ts Ћ 1035 KJ Ќ 1036
|V% Ў 1038 DZ Џ 1039 A= А 1040 B= Б 1041 V= В 1042 G= Г 1043
|D= Д 1044 E= Е 1045 Z% Ж 1046 Z= З 1047 I= И 1048 J= Й 1049
|K= К 1050 L= Л 1051 M= М 1052 N= Н 1053 O= О 1054 P= П 1055
|R= Р 1056 S= С 1057 T= Т 1058 U= У 1059 F= Ф 1060 H= Х 1061
|C= Ц 1062 C% Ч 1063 S% Ш 1064 Sc Щ 1065 =" Ъ 1066 Y= Ы 1067
|%" Ь 1068 JE Э 1069 JU Ю 1070 JA Я 1071 a= а 1072 b= б 1073
|v= в 1074 g= г 1075 d= д 1076 e= е 1077 z% ж 1078 z= з 1079
|i= и 1080 j= й 1081 k= к 1082 l= л 1083 m= м 1084 n= н 1085
|o= о 1086 p= п 1087 r= р 1088 s= с 1089 t= т 1090 u= у 1091
|f= ф 1092 h= х 1093 c= ц 1094 c% ч 1095 s% ш 1096 sc щ 1097
|=' ъ 1098 y= ы 1099 %' ь 1100 je э 1101 ju ю 1102 ja я 1103
|io ё 1105 d% ђ 1106 g% ѓ 1107 ie є 1108 ds ѕ 1109 ii і 1110
|yi ї 1111 j% ј 1112 lj љ 1113 nj њ 1114 ts ћ 1115 kj ќ 1116
|v% ў 1118 dz џ 1119 Y3 Ѣ 1122 y3 ѣ 1123 O3 Ѫ 1130 o3 ѫ 1131
|F3 Ѳ 1138 f3 ѳ 1139 V3 Ѵ 1140 v3 ѵ 1141 C3 Ҁ 1152 c3 ҁ 1153
|G3 Ґ 1168 g3 ґ 1169 A+ א 1488 B+ ב 1489 G+ ג 1490 D+ ד 1491
|H+ ה 1492 W+ ו 1493 Z+ ז 1494 X+ ח 1495 Tj ט 1496 J+ י 1497
|K% ך 1498 K+ כ 1499 L+ ל 1500 M% ם 1501 M+ מ 1502 N% ן 1503
|N+ נ 1504 S+ ס 1505 E+ ע 1506 P% ף 1507 P+ פ 1508 Zj ץ 1509
|ZJ צ 1510 Q+ ק 1511 R+ ר 1512 Sh ש 1513 T+ ת 1514 ,+ ، 1548
|;+ ؛ 1563 ?+ ؟ 1567 H' ء 1569 aM آ 1570 aH أ 1571 wH ؤ 1572
|ah إ 1573 yH ئ 1574 a+ ا 1575 b+ ب 1576 tm ة 1577 t+ ت 1578
|tk ث 1579 g+ ج 1580 hk ح 1581 x+ خ 1582 d+ د 1583 dk ذ 1584
|r+ ر 1585 z+ ز 1586 s+ س 1587 sn ش 1588 c+ ص 1589 dd ض 1590
|tj ط 1591 zH ظ 1592 e+ ع 1593 i+ غ 1594 ++ ـ 1600 f+ ف 1601
|q+ ق 1602 k+ ك 1603 l+ ل 1604 m+ م 1605 n+ ن 1606 h+ ه 1607
|w+ و 1608 j+ ى 1609 y+ ي 1610 :+ ً 1611 "+ ٌ 1612 =+ ٍ 1613
|/+ َ 1614 '+ ُ 1615 1+ ِ 1616 3+ ّ 1617 0+ ْ 1618 aS ٰ 1648
|p+ پ 1662 v+ ڤ 1700 gf گ 1711 0a ۰ 1776 1a ۱ 1777 2a ۲ 1778
|3a ۳ 1779 4a ۴ 1780 5a ۵ 1781 6a ۶ 1782 7a ۷ 1783 8a ۸ 1784
|9a ۹ 1785 B. 7682 b. 7683 B_ 7686 b_ 7687 D. 7690
|d. 7691 D_ 7694 d_ 7695 D, 7696 d, 7697 F. 7710
|f. 7711 G- 7712 g- 7713 H. 7714 h. 7715 H: 7718
|h: 7719 H, 7720 h, 7721 K' 7728 k' 7729 K_ 7732
|k_ 7733 L_ 7738 l_ 7739 M' 7742 m' ḿ 7743 M. 7744
|m. 7745 N. 7748 n. 7749 N_ 7752 n_ 7753 P' 7764
|p' 7765 P. 7766 p. 7767 R. 7768 r. 7769 R_ 7774
|r_ 7775 S. 7776 s. 7777 T. 7786 t. 7787 T_ 7790
|t_ 7791 V? 7804 v? 7805 W! 7808 W` 7808 w! 7809
|w` 7809 W' 7810 w' 7811 W: 7812 w: 7813 W. 7814
|w. 7815 X. 7818 x. 7819 X: 7820 x: 7821 Y. 7822
|y. 7823 Z> 7824 z> 7825 Z_ 7828 z_ 7829 h_ 7830
|t: 7831 w0 7832 y0 7833 A2 7842 a2 7843 E2 7866
|e2 7867 E? 7868 e? 7869 I2 7880 i2 7881 O2 7886
|o2 7887 U2 7910 u2 7911 Y! 7922 Y` 7922 y! 7923
|y` 7923 Y2 7926 y2 7927 Y? 7928 y? 7929 ;' 7936
|,' 7937 ;! 7938 ,! 7939 ?; 7940 ?, 7941 !: 7942
|?: 7943 1N 8194 1M 8195 3M 8196 4M 8197 6M 8198
|1T 8201 1H 8202 -1 8208 -N 8211 -M 8212 -3 8213
|!2 8214 =2 8215 '6 8216 '9 8217 .9 8218 9' 8219
|"6 “ 8220 "9 8221 :9 8222 9" ‟ 8223 /- † 8224 /= ‡ 8225
|oo 8226 .. 8229 ,. 8230 %0 8240 1' 8242 2' 8243
|3' 8244 4' 8279 1" 8245 2" 8246 3" ‷ 8247 Ca ‸ 8248
|<1 8249 >1 8250 :X 8251 '- 8254 /f 8260 0S 8304
|4S 8308 5S 8309 6S 8310 7S 8311 8S 8312 9S 8313
|+S 8314 -S 8315 =S 8316 (S 8317 )S 8318 nS 8319
|0s 8320 1s 8321 2s 8322 3s 8323 4s 8324 5s 8325
|6s 8326 7s 8327 8s 8328 9s 8329 +s 8330 -s 8331
|=s 8332 (s 8333 )s 8334 Li 8356 Pt 8359 W= 8361
|=e 8364 Eu 8364 =R 8381 =P 8381 oC 8451 co 8453
|oF 8457 N0 8470 PO 8471 Rx 8478 SM 8480 TM 8482
|Om 8486 AO 8491 13 8531 23 8532 15 8533 25 8534
|35 8535 45 8536 16 8537 56 8538 18 8539 38 8540
|58 8541 78 8542 1R 8544 2R 8545 3R 8546 4R 8547
|5R 8548 6R 8549 7R 8550 8R 8551 9R 8552 aR 8553
|bR 8554 cR 8555 1r 8560 2r 8561 3r 8562 4r 8563
|5r 8564 6r 8565 7r 8566 8r 8567 9r 8568 ar 8569
|br 8570 cr 8571 <- 8592 -! 8593 -> 8594 -v 8595
|<> 8596 UD 8597 <= 8656 => 8658 == 8660 FA 8704
|dP 8706 TE 8707 /0 8709 DE 8710 NB 8711 (- 8712
|-) 8715 *P 8719 +Z 8721 -2 8722 -+ 8723 *- 8727
|Ob 8728 Sb 8729 RT 8730 0( 8733 00 8734 -L 8735
|-V 8736 PP 8741 AN 8743 OR 8744 (U 8745 )U 8746
|In 8747 DI 8748 Io 8750 .: 8756 :. 8757 :R 8758
|:: 8759 ?1 8764 CG 8766 ?- 8771 ?= 8773 ?2 8776
|=? 8780 HI 8787 != 8800 =3 8801 =< 8804 >= 8805
|<* 8810 *> 8811 !< 8814 !> 8815 (C 8834 )C 8835
|(_ 8838 )_ 8839 0. 8857 02 8858 -T 8869 .P 8901
|:3 8942 .3 8943 Eh 8962 <7 8968 >7 8969 7< 8970
|7> 8971 NI 8976 (A 8978 TR 8981 Iu 8992 Il 8993
|</ 9001 /> 9002 Vs 9251 1h 9280 3h 9281 2h 9282
|4h 9283 1j 9286 2j 9287 3j 9288 4j 9289 1. 9352
|2. 9353 3. 9354 4. 9355 5. 9356 6. 9357 7. 9358
|8. 9359 9. 9360 hh 9472 HH 9473 vv 9474 VV 9475
|3- 9476 3_ 9477 3! 9478 3/ 9479 4- 9480 4_ 9481
|4! 9482 4/ 9483 dr 9484 dR 9485 Dr 9486 DR 9487
|dl 9488 dL 9489 Dl 9490 LD 9491 ur 9492 uR 9493
|Ur 9494 UR 9495 ul 9496 uL 9497 Ul 9498 UL 9499
|vr 9500 vR 9501 Vr 9504 VR 9507 vl 9508 vL 9509
|Vl 9512 VL 9515 dh 9516 dH 9519 Dh 9520 DH 9523
|uh 9524 uH 9527 Uh 9528 UH 9531 vh 9532 vH 9535
|Vh 9538 VH 9547 FD 9585 BD 9586 TB 9600 LB 9604
|FB 9608 lB 9612 RB 9616 .S 9617 :S 9618 ?S 9619
|fS 9632 OS 9633 RO 9634 Rr 9635 RF 9636 RY 9637
|RH 9638 RZ 9639 RK 9640 RX 9641 sB 9642 SR 9644
|Or 9645 UT 9650 uT 9651 PR 9654 Tr 9655 Dt 9660
|dT 9661 PL 9664 Tl 9665 Db 9670 Dw 9671 LZ 9674
|0m 9675 0o 9678 0M 9679 0L 9680 0R 9681 Sn 9688
|Ic 9689 Fd 9698 Bd 9699 *2 9733 *1 9734 <H 9756
|>H 9758 0u 9786 0U 9787 SU 9788 Fm 9792 Ml 9794
|cS 9824 cH 9825 cD 9826 cC 9827 Md 9833 M8 9834
|M2 9835 Mb 9837 Mx 9838 MX 9839 OK 10003 XX 10007
|-X 10016 IS   12288 ,_ 12289 ._ 12290 +" 〃 12291 +_ 〄 12292
|*_ 12293 ;_ 12294 0_ 12295 <+ 12298 >+ 12299 <' 12300
|>' 12301 <" 『 12302 >" 12303 (" 【 12304 )" 12305 =T 12306
|=_ 12307 (' 12308 )' 12309 (I 12310 )I 12311 -? 12316
|A5 12353 a5 12354 I5 12355 i5 12356 U5 12357 u5 12358
|E5 12359 e5 12360 O5 12361 o5 12362 ka 12363 ga 12364
|ki 12365 gi 12366 ku 12367 gu 12368 ke 12369 ge 12370
|ko 12371 go 12372 sa 12373 za 12374 si 12375 zi 12376
|su 12377 zu 12378 se 12379 ze 12380 so 12381 zo 12382
|ta 12383 da 12384 ti 12385 di 12386 tU 12387 tu 12388
|du 12389 te 12390 de 12391 to 12392 do 12393 na 12394
|ni 12395 nu 12396 ne 12397 no 12398 ha 12399 ba 12400
|pa 12401 hi 12402 bi 12403 pi 12404 hu 12405 bu 12406
|pu 12407 he 12408 be 12409 pe 12410 ho 12411 bo 12412
|po 12413 ma 12414 mi 12415 mu 12416 me 12417 mo 12418
|yA 12419 ya 12420 yU 12421 yu 12422 yO 12423 yo 12424
|ra 12425 ri 12426 ru 12427 re 12428 ro 12429 wA 12430
|wa 12431 wi 12432 we 12433 wo 12434 n5 12435 vu 12436
|"5 ゛ 12443 05 ゜ 12444 *5 ゝ 12445 +5 ゞ 12446 a6 ァ 12449 A6 ア 12450
|i6 12451 I6 12452 u6 12453 U6 12454 e6 12455 E6 12456
|o6 12457 O6 12458 Ka 12459 Ga 12460 Ki 12461 Gi 12462
|Ku 12463 Gu 12464 Ke 12465 Ge 12466 Ko 12467 Go 12468
|Sa 12469 Za 12470 Si 12471 Zi 12472 Su 12473 Zu 12474
|Se 12475 Ze 12476 So 12477 Zo 12478 Ta 12479 Da 12480
|Ti 12481 Di 12482 TU 12483 Tu 12484 Du 12485 Te 12486
|De 12487 To 12488 Do 12489 Na 12490 Ni 12491 Nu 12492
|Ne 12493 No 12494 Ha 12495 Ba 12496 Pa 12497 Hi 12498
|Bi 12499 Pi 12500 Hu 12501 Bu 12502 Pu 12503 He 12504
|Be 12505 Pe 12506 Ho 12507 Bo 12508 Po 12509 Ma 12510
|Mi 12511 Mu 12512 Me 12513 Mo 12514 YA 12515 Ya 12516
|YU 12517 Yu 12518 YO 12519 Yo 12520 Ra 12521 Ri 12522
|Ru 12523 Re 12524 Ro 12525 WA 12526 Wa 12527 Wi 12528
|We 12529 Wo 12530 N6 12531 Vu 12532 KA 12533 KE 12534
|Va 12535 Vi 12536 Ve 12537 Vo 12538 .6 12539 -6 12540
|*6 12541 +6 12542 b4 12549 p4 12550 m4 12551 f4 12552
|d4 12553 t4 12554 n4 12555 l4 12556 g4 12557 k4 12558
|h4 12559 j4 12560 q4 12561 x4 12562 zh 12563 ch 12564
|sh 12565 r4 12566 z4 12567 c4 12568 s4 12569 a4 12570
|o4 12571 e4 12572 ai 12574 ei 12575 au 12576 ou 12577
|an 12578 en 12579 aN 12580 eN 12581 er 12582 i4 12583
|u4 12584 iu 12585 v4 12586 nG 12587 gn 12588 1c 12832
|2c 12833 3c 12834 4c 12835 5c 12836 6c 12837 7c 12838
|8c 12839 9c 12840 ff 64256 fi 64257 fl 64258 ft 64261
|st 64262 cr 🔴 128308
""".trimMargin()
)
}
@Test
fun `test digraph output with headers and custom digraphs`() {
enterCommand("digraphs (0 9450 (2 9313 (1 9312")

View File

@ -13,7 +13,7 @@ import com.intellij.openapi.application.ApplicationManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.history.VimHistory
import com.maddyhome.idea.vim.history.HistoryConstants
import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@ -240,7 +240,7 @@ class GlobalCommandTest : VimTestCase() {
@Test
fun `test check history`() {
VimPlugin.getHistory().clear()
val initialEntries = VimPlugin.getHistory().getEntries(VimHistory.Type.Command, 0, 0)
val initialEntries = VimPlugin.getHistory().getEntries(HistoryConstants.COMMAND, 0, 0)
doTest(
"g/found/d",
initialText,
@ -252,7 +252,7 @@ class GlobalCommandTest : VimTestCase() {
hard by the torrent of a mountain pass.
""".trimIndent(),
)
val entries = VimPlugin.getHistory().getEntries(VimHistory.Type.Command, 0, 0)
val entries = VimPlugin.getHistory().getEntries(HistoryConstants.COMMAND, 0, 0)
kotlin.test.assertEquals(1, entries.size - initialEntries.size)
val element = entries.last()
kotlin.test.assertEquals("g/found/d", element.entry)

View File

@ -10,7 +10,6 @@ package org.jetbrains.plugins.ideavim.ex.implementation.commands
import com.intellij.idea.TestFor
import com.intellij.testFramework.LoggedErrorProcessor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.keys
import com.maddyhome.idea.vim.command.MappingMode
@ -22,7 +21,6 @@ import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.exceptionMappingOwner
import org.jetbrains.plugins.ideavim.waitAndAssert
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@ -107,16 +105,16 @@ class MapCommandTest : VimTestCase() {
enterCommand("imap <C-Down> <C-O>gt")
enterCommand("nmap ,f <Plug>Foo")
enterCommand("nmap <Plug>Foo iHello<Esc>")
assertCommandOutput("imap",
enterCommand("imap")
assertExOutput(
"""
|i <C-Down> <C-O>gt
|i bar <Esc>
|i foo bar
""".trimMargin(),
)
assertCommandOutput("map",
enterCommand("map")
assertExOutput(
"""
| <C-Down> gt
|n <Plug>Foo iHello<Esc>
@ -144,9 +142,11 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("map")
// Note that Vim doesn't appear to have an order. Items are kinda sorted, but also not. I.e. `m{something}` are
// grouped together, but followed later by `g{something}`. We'll sort by {lhs}, so we're at least consistent
assertCommandOutput("map",
assertExOutput(
"""
| all foo
|n normal foo
@ -163,7 +163,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("nmap",
enterCommand("nmap")
assertExOutput(
"""
| all foo
|n normal foo
@ -176,7 +178,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("vmap",
enterCommand("vmap")
assertExOutput(
"""
| all foo
|s select foo
@ -191,7 +195,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("smap",
enterCommand("smap")
assertExOutput(
"""
| all foo
|s select foo
@ -205,7 +211,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("xmap",
enterCommand("xmap")
assertExOutput(
"""
| all foo
|x visual foo
@ -219,7 +227,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("omap",
enterCommand("omap")
assertExOutput(
"""
| all foo
|o op-pending foo
@ -232,7 +242,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("map!",
enterCommand("map!")
assertExOutput(
"""
|c cmdline foo
|i insert foo
@ -255,7 +267,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("imap",
enterCommand("imap")
assertExOutput(
"""
|i insert foo
|! insert+cmdline foo
@ -269,7 +283,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("lmap",
enterCommand("lmap")
assertExOutput(
"""
|l lang foo
""".trimMargin()
@ -281,7 +297,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
assertCommandOutput("cmap",
enterCommand("cmap")
assertExOutput(
"""
|c cmdline foo
|! insert+cmdline foo
@ -295,9 +313,10 @@ class MapCommandTest : VimTestCase() {
addTestMaps() // Adds a mapping of all for NVO
enterCommand("sunmap all") // Removes Select from the NVO mapping for foo
enterCommand("map")
// Note that the formatting is exactly how Vim shows it. Messy, isn't it?
assertCommandOutput("map",
assertExOutput(
"""
|noxall foo
|n normal foo
@ -315,8 +334,9 @@ class MapCommandTest : VimTestCase() {
addTestMaps() // Adds a mapping of all for NVO
enterCommand("vunmap all") // Removes Visual+Select from the NVO mapping for foo
enterCommand("map")
assertCommandOutput("map",
assertExOutput(
"""
|no all foo
|n normal foo
@ -335,7 +355,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("vmap foo baz") // Visual, Select
// Just to be sure we're set up correctly
assertCommandOutput("map",
enterCommand("map")
assertExOutput(
"""
|no foo bar
|v foo baz
@ -345,7 +366,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("sunmap foo")
enterCommand("ounmap foo")
assertCommandOutput("map",
enterCommand("map")
assertExOutput(
"""
|n foo bar
|x foo baz
@ -361,7 +383,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nmap fee bap")
enterCommand("nmap zzz ppp")
assertCommandOutput("map f",
enterCommand("map f")
assertExOutput(
"""
|n fee bap
| foo bar
@ -374,7 +397,8 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
enterCommand("map foo bar")
assertCommandOutput("map ",
enterCommand("map ")
assertExOutput(
"""
| foo bar
""".trimMargin()
@ -386,7 +410,8 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
enterCommand("imap foo bar")
assertCommandOutput("imap f ",
enterCommand("imap f ")
assertExOutput(
"""
|i foo bar
""".trimMargin()
@ -404,7 +429,7 @@ class MapCommandTest : VimTestCase() {
}
@Test
fun `test dd with mapping starting with d`() {
fun testddWithMapping() {
configureByText(
"""
Hello$c 1
@ -435,8 +460,8 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
enterCommand("inoremap jj <Esc>")
enterCommand("imap foo bar")
assertCommandOutput("imap",
enterCommand("imap")
assertExOutput(
"""
|i foo bar
|i jj * <Esc>
@ -466,7 +491,8 @@ class MapCommandTest : VimTestCase() {
""".trimIndent(),
)
assertOffset(1)
assertCommandOutput("nmap", "n <Right> * <Nop>")
enterCommand("nmap")
assertExOutput("n <Right> * <Nop>")
}
@Test
@ -479,8 +505,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nmap <script> ,e /e<CR>")
enterCommand("nmap <expr> ,f '/f<CR>'")
enterCommand("nmap <unique> ,g /g<CR>")
assertCommandOutput("nmap",
enterCommand("nmap")
assertExOutput(
"""
|n ,a /a<CR>
|n ,b /b<CR>
@ -587,8 +613,8 @@ class MapCommandTest : VimTestCase() {
typeText("i" + "#" + "<Esc>")
assertState("#\n")
assertMode(Mode.NORMAL())
assertCommandOutput("imap", "i # * X<C-H>#")
enterCommand("imap")
assertExOutput("i # * X<C-H>#")
}
// VIM-679 |:map|
@ -612,7 +638,8 @@ class MapCommandTest : VimTestCase() {
""".trimIndent(),
)
assertMode(Mode.NORMAL())
assertCommandOutput("map", " <C-X>i dd")
enterCommand("map")
assertExOutput(" <C-X>i dd")
typeText("<C-X>i")
assertState("bar\n")
}
@ -727,59 +754,6 @@ class MapCommandTest : VimTestCase() {
assertState("Bye\n")
}
@Test
fun `test map applies longest mapping`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcd ABCD")
typeText("i", "abcd", "<Esc>")
assertState("ABCD\n")
}
@Test
fun `test map falls back to previous longest mapping when abandoned`() {
configureByText("\n")
enterCommand("imap abc ABC")
enterCommand("imap abcd ABCD")
typeText("i", "abcg", "<Esc>")
assertState("ABCg\n")
}
@Test
fun `test map falls back to previous longest mapping when abandoned with shorter prefix`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcd ABCD")
typeText("i", "abcg", "<Esc>")
assertState("ABcg\n")
}
@Test
fun `test map falls back to previous longest mapping after timeout`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcd ABCD")
enterCommand("set timeoutlen=100")
typeText("i", "abc")
waitAndAssert(injector.globalOptions().timeoutlen + 100) {
fixture.editor.document.text == "ABc\n"
}
assertState("ABc\n")
}
@Test
fun `test map falls back to previous longest mapping after timeout with shorter prefix`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcde ABCDE")
enterCommand("set timeoutlen=100")
typeText("i", "abcd")
waitAndAssert(injector.globalOptions().timeoutlen + 100) {
fixture.editor.document.text == "ABcd\n"
}
assertState("ABcd\n")
}
@TestWithoutNeovim(SkipNeovimReason.PLUG)
@Test
fun testPlugMapping() {
@ -799,6 +773,16 @@ class MapCommandTest : VimTestCase() {
assertState("123${c}7890")
}
@TestWithoutNeovim(SkipNeovimReason.PLUG)
@Test
fun testIncompleteMapping() {
configureByText("123${c}4567890")
enterCommand("map <Plug>(Hi)l lll")
enterCommand("map I <Plug>(Hi)")
typeText("Ih")
assertState("12${c}34567890")
}
@Test
fun testIntersectingCommands2() {
configureByText("123${c}4567890")
@ -807,86 +791,6 @@ class MapCommandTest : VimTestCase() {
assertState("123${c}567890")
}
@Test
fun `test partial Plug mapping`() {
doTest(
listOf("i", "Xy"),
"Lorem $c ipsum dolor sit amet",
"Lorem Hello ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap <Plug>xy Hello")
enterCommand("imap X <Plug>x")
}
}
@Test
fun `test abandoned Plug mapping replays all keys as text`() {
// The default Vim behaviour for an abandoned mapping is to replay it, character by character, even if that makes no
// sense for the mode or mapping, and will move the caret or delete text or whatever. This is also true for <Plug>
// mappings, even though `<Plug>` is a special char that can't be typed by the user. When used in Insert mode, Vim
// expands the special char to the string "<Plug>".
doTest(
listOf("i", "Xz"),
"Lorem $c ipsum dolor sit amet",
"Lorem <Plug>xz ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap <Plug>xy Hello")
enterCommand("imap X <Plug>x")
}
}
@Test
fun `test partial Action mapping`() {
doTest(
listOf("i", "X(EditorToggleCase)"),
"Lorem ${c}ipsum dolor sit amet",
"Lorem IPSUM dolor sit amet",
Mode.INSERT
) {
enterCommand("imap X <Action>")
}
}
@Test
fun `test abandoned Action mapping replays all keys as text`() {
// The mapping is looking for `<Action>(...)`, so we need to feed it a key that is not part of this mapping. A space
// char will work
doTest(
listOf("i", "X( "),
"Lorem $c ipsum dolor sit amet",
"Lorem <Action>( ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap X <Action>")
}
}
@Test
fun `test abandoned Action mapping replays all keys as text 2`() {
doTest(
listOf("i", "Xz"),
"Lorem $c ipsum dolor sit amet",
"Lorem <Action>z ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap X <Action>")
}
}
@Test
fun `test timedout Action mapping replays all keys as text`() {
configureByText("Lorem $c ipsum dolor sit amet")
enterCommand("imap X <Action>")
enterCommand("set timeoutlen=100")
typeText("i", "X")
waitAndAssert(injector.globalOptions().timeoutlen + 100) {
fixture.editor.document.text == "Lorem <Action> ipsum dolor sit amet"
}
assertState("Lorem <Action> ipsum dolor sit amet")
}
@TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT)
@Test
fun testMapZero() {
@ -999,7 +903,7 @@ class MapCommandTest : VimTestCase() {
@Test
fun `test rhs with triangle brackets`() {
fun `test rhc with triangle brackets`() {
configureByText("\n")
enterCommand("inoremap p <p>")
typeText("ip")
@ -1038,8 +942,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nmap ,g :action Back<C-M>")
enterCommand("nmap ,h :action Back<C-m>")
enterCommand("nmap ,i :action Back<c-m>")
assertCommandOutput("nmap",
enterCommand("nmap")
assertExOutput(
"""
|n ,a <Action>(Back)
|n ,b <Action>(Back)
@ -1066,8 +970,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nnoremap ,g :action Back<C-M>")
enterCommand("nnoremap ,h :action Back<C-m>")
enterCommand("nnoremap ,i :action Back<c-m>")
assertCommandOutput("nnoremap",
enterCommand("nnoremap")
assertExOutput(
"""
|n ,a <Action>(Back)
|n ,b <Action>(Back)

View File

@ -148,7 +148,7 @@ class MoveCommandTest : VimTestCase() {
enterCommand("m 0")
assertState(
"""
${c}For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, nothing.
@ -173,8 +173,8 @@ class MoveCommandTest : VimTestCase() {
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|${c}Ut id dapibus augue.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
|Ut id dapibus augue.
|${c}Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@ -194,7 +194,7 @@ class MoveCommandTest : VimTestCase() {
assertState(
"""
For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
${c}See, nothing.
See, nothing.
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
""".trimIndent(),
@ -216,7 +216,7 @@ class MoveCommandTest : VimTestCase() {
"""
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
${c}See, nothing.
See, not${c}hing.
For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
""".trimIndent(),
)
@ -238,103 +238,8 @@ class MoveCommandTest : VimTestCase() {
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, nothing.
${c}For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
""".trimIndent(),
)
}
// VIM-3837
@Test
fun `test moving relative line positions caret correctly`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|2
|1
|${c}3
|1
|2
""".trimMargin(),
"""
|2
|1
|3
|${c}2
|1
""".trimMargin()
)
}
@Test
fun `test moving relative line positions caret correctly 2`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet ${c}tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Ut id dapibus augue.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@Test
fun `test moving lines positions caret correctly with nostartofline option`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet ${c}tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Orci varius na${c}toque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Ut id dapibus augue.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
) {
enterCommand("set nostartofline")
}
}
@Test
fun `test moving lines positions caret correctly with nostartofline option on shorter line`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id ${c}venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Ut id dapibus augue${c}.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
) {
enterCommand("set nostartofline")
}
}
}

View File

@ -8,40 +8,33 @@
package org.jetbrains.plugins.ideavim.ex.implementation.commands
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
/*
class NormalCommandTest : VimTestCase() {
@Test
fun `test simple execution`() {
doTest(exCommand("normal x"), "123<caret>456", "123<caret>56")
doTest("normal x", "123<caret>456", "123<caret>56")
}
@Test
fun `test short command`() {
doTest(exCommand("norm x"), "123<caret>456", "123<caret>56")
}
@Test
fun `test normal command automatically exits Insert mode`() {
doTest(exCommand("normal iFoo"), "123<caret>456", "123Fo<caret>o456", Mode.NORMAL())
doTest("norm x", "123<caret>456", "123<caret>56")
}
@Test
fun `test multiple commands`() {
doTest(exCommand("normal xiNewText"), "123<caret>456", "123NewTex<caret>t56")
doTest("normal xiNewText", "123<caret>456", "123NewTex<caret>t56")
}
@Test
fun `test normal command with current line range moves caret to start of line before executing command`() {
doTest(exCommand(".norm x"), "123<caret>456", "<caret>23456")
fun `test range single stroke`() {
doTest(".norm x", "123<caret>456", "<caret>23456")
}
@Test
fun `test normal command with multi-line range`() {
fun `test range multiple strokes`() {
doTest(
exCommand("1,3norm x"),
"1,3norm x",
"""
123456
123456
@ -60,7 +53,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test normal command with single letter mapping`() {
fun `test with mapping`() {
configureByText(
"""
<caret>123456
@ -68,8 +61,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
enterCommand("map G dd")
enterCommand("normal G")
typeText(commandToKeys("map G dd"))
typeText(commandToKeys("normal G"))
assertState(
"""
<caret>123456
@ -79,28 +72,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test normal command with multi-letter mapping`() {
doTest(
exCommand("normal dd"),
"""
|${c}Lorem ipsum dolor sit amet,
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet,
|consectetur adipiscing elit
|Sed in orci mauris.
|${c}Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
) {
enterCommand("map dd G")
}
}
@Test
fun `test normal command with disabled mapping`() {
fun `test with disabled mapping`() {
configureByText(
"""
<caret>123456
@ -108,8 +80,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
enterCommand("map G dd")
enterCommand("normal! G")
typeText(commandToKeys("map G dd"))
typeText(commandToKeys("normal! G"))
assertState(
"""
123456
@ -120,7 +92,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test normal from Visual mode runs command on start of each line in range`() {
fun `test from visual mode`() {
configureByText(
"""
<caret>123456
@ -130,8 +102,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText("Vjj")
enterCommand("normal x") // Will give `:'<,'>normal x`
typeText(parseKeys("Vjj"))
typeText(commandToKeys("normal x"))
assertState(
"""
23456
@ -144,7 +116,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test normal command switches to Visual mode`() {
fun `test execute visual mode`() {
configureByText(
"""
<caret>123456
@ -154,8 +126,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
enterCommand("normal Vjj")
typeText("x")
typeText(commandToKeys("normal Vjj"))
typeText(parseKeys("x"))
assertState(
"""
<caret>123456
@ -176,8 +148,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText("qqxq", "jVjjj")
enterCommand("norm @q")
typeText(parseKeys("qqxq", "jVjjj"))
typeText(commandToKeys("norm @q"))
assertState(
"""
23456
@ -193,22 +165,29 @@ class NormalCommandTest : VimTestCase() {
@Test
fun `test command executes at selection start`() {
configureByText("hello <caret>world !")
typeText("vw")
enterCommand("<C-u>norm x")
typeText(parseKeys("vw"))
typeText(parseKeys(":<C-u>norm x<CR>"))
assertState("hello <caret>orld !")
}
@Test
fun `test false escape`() {
configureByText("hello <caret>world !")
enterCommand("norm i<Esc>")
typeText(commandToKeys("norm i<Esc>"))
assertState("hello <Esc<caret>>world !")
}
@Test
fun `test C-R`() {
configureByText("""myprop: "my value"""")
enterCommand("""exe "norm ^dei-\<C-R>\"-"""")
assertState("""-myprop-: "my value"""")
configureByText("myprop: \"my value\"")
typeText(commandToKeys("exe \"norm ^dei-\\<C-R>\\\"-\""))
assertState("-myprop-: \"my value\"")
}
private fun doTest(command: String, before: String, after: String) {
myFixture.configureByText("a.java", before)
typeText(commandToKeys(command))
myFixture.checkResult(after)
}
}
*/

View File

@ -1,109 +0,0 @@
/*
* Copyright 2003-2025 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.ex.implementation.commands
import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.vimscript.model.commands.SmileCommand
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class SmileCommandTest : VimTestCase() {
private fun loadResourceContent(resourcePath: String): String {
return SmileCommand::class.java.getResourceAsStream(resourcePath)
?.bufferedReader()
?.use { it.readText() }
?: throw IllegalStateException("Could not load resource: $resourcePath")
}
@Test
fun `test smile command with default file`() {
configureByText("\n")
typeText(commandToKeys("smile"))
val output: String = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent: String = loadResourceContent(SmileCommand.DEFAULT_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
@Test
fun `test smile command with kotlin file`() {
configureByText("\n")
configureByFileName("Test.kt")
typeText(commandToKeys("smile"))
val output = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent = loadResourceContent(SmileCommand.KOTLIN_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
@Test
fun `test smile command with kotlin script file`() {
configureByText("\n")
configureByFileName("Test.kts")
typeText(commandToKeys("smile"))
val output: String = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent: String = loadResourceContent(SmileCommand.KOTLIN_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
@Test
fun `test smile command with java file`() {
configureByText("\n")
configureByFileName("Test.java")
typeText(commandToKeys("smile"))
val output: String = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent: String = loadResourceContent(SmileCommand.JAVA_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
@Test
fun `test smile command with python file`() {
configureByText("\n")
configureByFileName("Test.py")
typeText(commandToKeys("smile"))
val output: String = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent: String = loadResourceContent(SmileCommand.PYTHON_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
@Test
fun `test smile command with unknown file extension`() {
configureByText("\n")
configureByFileName("Test.unknown")
typeText(commandToKeys("smile"))
val output: String = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent: String = loadResourceContent(SmileCommand.DEFAULT_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
@Test
fun `test smile command with gitignore file`() {
configureByText("\n")
configureByFileName(".gitignore")
typeText(commandToKeys("smile"))
val output: String = ExOutputModel.getInstance(fixture.editor).text.trimEnd()
val expectedContent: String = loadResourceContent(SmileCommand.DEFAULT_RESOURCE_PATH).trimEnd()
assertEquals(expectedContent, output)
}
}

View File

@ -192,32 +192,6 @@ class SubstituteCommandTest : VimTestCase() {
)
}
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
)
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
fun `test ampersand group`() {
doTest(
exCommand("s/a\\|b/z&/g"),
"${c}abcdefg",
"zazbcdefg",
)
}
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
)
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
fun `test missing group`() {
doTest(
exCommand("s/b/<\\7>/"),
"${c}abc",
"a<>c",
)
}
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
@ -1354,60 +1328,4 @@ class SubstituteCommandTest : VimTestCase() {
" comment ",
)
}
// VIM-3510
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
)
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
fun `test replace action U`() {
doTest(
exCommand("s/\\(foo\\)/\\U\\1bar/"),
"${c}a foo",
"a FOOBAR",
)
}
// VIM-3510
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
)
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
fun `test replace action U and E`() {
doTest(
exCommand("s/\\(foo\\)/\\U\\1\\ebar/"),
"${c}a foo",
"a FOObar",
)
}
// VIM-3510
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
)
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
fun `test replace action u`() {
doTest(
exCommand("s/\\(foo\\)/\\u\\1bar/"),
"${c}a foo",
"a Foobar",
)
}
// VIM-3510
@OptionTest(
VimOption(TestOptionConstants.smartcase, doesntAffectTest = true),
VimOption(TestOptionConstants.ignorecase, doesntAffectTest = true),
)
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
fun `test replace action u and empty group`() {
doTest(
exCommand("s/a foo\\(\\)/a foo\\u\\1bar/"),
"${c}a foo",
"a fooBar",
)
}
}

View File

@ -10,7 +10,6 @@ package org.jetbrains.plugins.ideavim.ex.implementation.expressions.datatypes
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFloat
import org.junit.jupiter.api.Test
import java.util.Locale
import kotlin.test.assertEquals
class VimFloatTest {
@ -24,15 +23,4 @@ class VimFloatTest {
fun `round 7 digits`() {
assertEquals("1.0", VimFloat(0.9999999).toString())
}
@Test
fun `use point as decimal separator always`() {
val oldLocale = Locale.getDefault()
Locale.setDefault(Locale.GERMANY) // In Germany, they use a comma as a decimal separator, i.e., "3,14".
try {
assertEquals("3.14", VimFloat(3.14).toString())
} finally {
Locale.setDefault(oldLocale)
}
}
}

View File

@ -11,7 +11,6 @@ package org.jetbrains.plugins.ideavim.ex.implementation.expressions.operators
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.ex.evaluate
import org.jetbrains.plugins.ideavim.productForArguments
import org.junit.jupiter.params.ParameterizedTest
@ -19,7 +18,7 @@ import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import kotlin.test.assertEquals
class ConcatenationOperatorTest : VimTestCase() {
class ConcatenationOperatorTest {
companion object {
@JvmStatic
@ -48,7 +47,7 @@ class ConcatenationOperatorTest : VimTestCase() {
try {
VimscriptParser.parseExpression("3.4$sp1$operator${sp2}2")!!.evaluate()
} catch (e: ExException) {
assertEquals("E806: Using a Float as a String", e.message)
assertEquals("E806: using Float as a String", e.message)
}
}
@ -58,7 +57,7 @@ class ConcatenationOperatorTest : VimTestCase() {
try {
VimscriptParser.parseExpression("3.4$sp1$operator${sp2}2.2")!!.evaluate()
} catch (e: ExException) {
assertEquals("E806: Using a Float as a String", e.message)
assertEquals("E806: using Float as a String", e.message)
}
}
@ -68,7 +67,7 @@ class ConcatenationOperatorTest : VimTestCase() {
try {
VimscriptParser.parseExpression("'string'$sp1$operator${sp2}3.4")!!.evaluate()
} catch (e: ExException) {
assertEquals("E806: Using a Float as a String", e.message)
assertEquals("E806: using Float as a String", e.message)
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright 2003-2025 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.ex.implementation.expressions.operators
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.ex.evaluate
import org.junit.jupiter.api.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class DoesNotMatchOperatorTest : VimTestCase() {
@Test
fun `test does not match operator returns false when pattern matches string`() {
assertFalse(evaluate("'lorem ipsum' !~ 'l*sum'").asBoolean())
}
@Test
fun `test does not match operator returns true when pattern does not match string`() {
assertTrue(evaluate("'lorem ipsum' !~ 'l*foo'").asBoolean())
}
@Test
fun `test does not match operator returns false when pattern matches case`() {
assertFalse(evaluate("'Lorem Ipsum' !~ 'L*I'").asBoolean())
}
@Test
fun `test does not match operator returns true when pattern does not match case`() {
assertTrue(evaluate("'Lorem Ipsum' !~ 'l*i'").asBoolean())
}
@Test
fun `test does not match operator with 'noignorecase' returns false with different case pattern`() {
injector.globalOptions().ignorecase = true // Default is false
assertFalse(evaluate("'Lorem Ipsum' !~ 'l*i'").asBoolean())
}
// Case-sensitive operator
@Test
fun `test case-sensitive does not match operator returns false when pattern matches string`() {
assertFalse(evaluate("'lorem ipsum' !~# 'l*sum'").asBoolean())
}
@Test
fun `test case-sensitive does not match operator returns true when pattern does not match string`() {
assertTrue(evaluate("'lorem ipsum' !~# 'l*foo'").asBoolean())
}
@Test
fun `test case-sensitive does not match operator returns true when pattern does not match string case`() {
assertTrue(evaluate("'lorem ipsum' !~# 'L*Sum'").asBoolean())
}
// Case-insensitive operator
@Test
fun `test case-insensitive does not match operator returns false when pattern matches string`() {
assertFalse(evaluate("'lorem ipsum' !~? 'l*sum'").asBoolean())
}
@Test
fun `test case-insensitive does not match operator returns true when pattern does not match string`() {
assertTrue(evaluate("'lorem ipsum' !~? 'l*foo'").asBoolean())
}
@Test
fun `test case-insensitive does not match operator returns false when pattern does not match string case`() {
assertFalse(evaluate("'lorem ipsum' !~? 'L*Sum'").asBoolean())
}
private fun evaluate(expression: String) = VimscriptParser.parseExpression(expression)!!.evaluate()
}

View File

@ -8,9 +8,6 @@
package org.jetbrains.plugins.ideavim.ex.implementation.expressions.operators
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDictionary
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFloat
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimList
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
@ -23,67 +20,25 @@ import kotlin.test.assertEquals
class FalsyOperatorTest : VimTestCase() {
@Test
fun `test non-zero Number treated as truthy`() {
assertEquals(VimInt(42), VimscriptParser.parseExpression("42 ?? 999")!!.evaluate())
fun `left expression is true`() {
assertEquals(VimInt("42"), VimscriptParser.parseExpression("42 ?? 999")!!.evaluate())
}
@Test
fun `test non-zero negative Number treated as truthy`() {
assertEquals(VimInt(-1), VimscriptParser.parseExpression("-1 ?? 999")!!.evaluate())
fun `left expression is false`() {
assertEquals(VimInt("42"), VimscriptParser.parseExpression("0 ?? 42")!!.evaluate())
}
@Test
fun `test Number 0 is treated as falsy`() {
assertEquals(VimInt(42), VimscriptParser.parseExpression("0 ?? 42")!!.evaluate())
}
@Test
fun `test zero Float is treated as falsy`() {
assertEquals(VimInt(42), VimscriptParser.parseExpression("0.0 ?? 42")!!.evaluate())
}
@Test
fun `test non-zero Float treated as truthy`() {
assertEquals(VimFloat(42.0), VimscriptParser.parseExpression("42.0 ?? 999")!!.evaluate())
}
@Test
fun `test empty String treated as falsy`() {
assertEquals(VimString("string is empty"), VimscriptParser.parseExpression("'' ?? 'string is empty'")!!.evaluate())
}
@Test
fun `test String '0' treated as falsy`() {
assertEquals(VimString("string is empty"), VimscriptParser.parseExpression("'0' ?? 'string is empty'")!!.evaluate())
}
@Test
fun `test non-empty String treated as truthy`() {
assertEquals(VimString("string is not empty"), VimscriptParser.parseExpression("'string is not empty' ?? 'string is empty'")!!.evaluate())
}
@Test
fun `test empty list treated as falsy`() {
fun `empty list as a left expression`() {
assertEquals(VimString("list is empty"), VimscriptParser.parseExpression("[] ?? 'list is empty'")!!.evaluate())
}
@Test
fun `test non-empty List treated as truthy`() {
fun `nonempty list as a left expression`() {
assertEquals(
VimList(mutableListOf(VimInt(1), VimInt(2), VimInt(3))),
VimscriptParser.parseExpression("[1, 2, 3] ?? 'list is empty'")!!.evaluate(),
)
}
@Test
fun `test empty Dictionary treated as falsy`() {
assertEquals(VimString("dict is empty"), VimscriptParser.parseExpression("{} ?? 'dict is empty'")!!.evaluate())
}
@Test
fun `test non-empty Dictionary treated as truthy`() {
val dictionary = LinkedHashMap<VimString, VimDataType>()
dictionary.put(VimString("1"), VimInt(1))
assertEquals(VimDictionary(dictionary), VimscriptParser.parseExpression("{'1': 1} ?? 'dict is empty'")!!.evaluate())
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright 2003-2025 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.ex.implementation.expressions.operators
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.ex.evaluate
import org.junit.jupiter.api.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class MatchesOperatorTest : VimTestCase() {
@Test
fun `test matches operator returns true when pattern matches string`() {
assertTrue(evaluate("'lorem ipsum' =~ 'l*sum'").asBoolean())
}
@Test
fun `test matches operator returns false when pattern does not match string`() {
assertFalse(evaluate("'lorem ipsum' =~ 'l*foo'").asBoolean())
}
@Test
fun `test matches operator returns true when pattern matches case`() {
assertTrue(evaluate("'Lorem Ipsum' =~ 'L*I'").asBoolean())
}
@Test
fun `test matches operator returns false when pattern does not match case`() {
assertFalse(evaluate("'Lorem Ipsum' =~ 'l*i'").asBoolean())
}
@Test
fun `test matches operator with 'noignorecase' returns true with different case pattern`() {
injector.globalOptions().ignorecase = true // Default is false
assertTrue(evaluate("'Lorem Ipsum' =~ 'l*i'").asBoolean())
}
// Case-sensitive operator
@Test
fun `test case-sensitive matches operator returns true when pattern matches string`() {
assertTrue(evaluate("'lorem ipsum' =~# 'l*sum'").asBoolean())
}
@Test
fun `test case-sensitive matches operator returns false when pattern does not match string`() {
assertFalse(evaluate("'lorem ipsum' =~# 'l*foo'").asBoolean())
}
@Test
fun `test case-sensitive matches operator returns false when pattern does not match string case`() {
assertFalse(evaluate("'lorem ipsum' =~# 'L*Sum'").asBoolean())
}
// Case-insensitive operator
@Test
fun `test case-insensitive matches operator returns true when pattern matches string`() {
assertTrue(evaluate("'lorem ipsum' =~? 'l*sum'").asBoolean())
}
@Test
fun `test case-insensitive matches operator returns false when pattern does not match string`() {
assertFalse(evaluate("'lorem ipsum' =~? 'l*foo'").asBoolean())
}
@Test
fun `test case-insensitive matches operator returns true when pattern does not match string case`() {
assertTrue(evaluate("'lorem ipsum' =~? 'L*Sum'").asBoolean())
}
private fun evaluate(expression: String) = VimscriptParser.parseExpression(expression)!!.evaluate()
}

View File

@ -37,7 +37,8 @@ class ColFunctionTest : VimTestCase() {
// With selection - make sure to delete the '<,'> that is automatically prepended when entering Command-line mode
// with a selection
typeText("vll")
assertCommandOutput("""<C-U>echo col("v")""", "5")
typeText(":<C-U>echo col(\"v\")<CR>") // enterCommand/assertCommandOutput cannot handle <C-U>!
assertExOutput("5")
// Remove selection and check again - note that exiting Command-line mode removes selection and switches back to
// Normal. This <esc> does nothing

View File

@ -32,7 +32,8 @@ class LineFunctionTest : VimTestCase() {
// With selection - make sure to delete the '<,'> that is automatically prepended when entering Command-line mode
// with a selection
typeText("vj")
assertCommandOutput("""<C-U>echo line("v")""", "3")
typeText(""":<C-U>echo line("v")<CR>""") // enterCommand/assertCommandOutput cannot handle <C-U>!
assertExOutput("3")
// Remove selection and check again - note that exiting Command-line mode removes selection and switches back to
// Normal. This <esc> does nothing
@ -45,50 +46,4 @@ class LineFunctionTest : VimTestCase() {
"0 1 5 0 5 0"
)
}
@Test
fun `test line with selection`() {
doTest(
listOf("V2j", "q"),
"""
|1
|${c}2
|3
|4
|5
""".trimMargin(),
"""
|1
|2
|3
|4 - line(v)==${c}2
|5
""".trimMargin(),
) {
enterCommand("vmap <expr> q '<Esc>a - line(v)=='.line('v').'<Esc>'")
}
}
@Test
fun `test line with reverse selection`() {
doTest(
listOf("V2k", "q"),
"""
|1
|2
|3
|${c}4
|5
""".trimMargin(),
"""
|1
|2 - line(v)==${c}4
|3
|4
|5
""".trimMargin(),
) {
enterCommand("vmap <expr> q '<Esc>a - line(v)=='.line('v').'<Esc>'")
}
}
}

View File

@ -46,7 +46,9 @@ class RegisterVariableTest : VimTestCase() {
typeText("vl\"zy")
val vimEditor = fixture.editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
val register = injector.registerGroup.getRegisters(vimEditor, context).first { reg -> reg.name == 'z' }
val register = injector.registerGroup.getRegisters(vimEditor, context)
.filter { reg -> reg.name == 'z' }
.first()
assertEquals("ab", register.text)
}

View File

@ -8,6 +8,7 @@
package org.jetbrains.plugins.ideavim.extension.miniai
import com.intellij.ide.highlighter.JavaFileType
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.BeforeEach
@ -30,6 +31,7 @@ class MiniAIExtensionTest : VimTestCase() {
"<caret>This is a \"'simple'\" test",
"This is a \"<caret>\" test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -42,6 +44,7 @@ class MiniAIExtensionTest : VimTestCase() {
"'balanced'false <caret>string'balanced'",
"'balanced'false string'<caret>'",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -54,6 +57,7 @@ class MiniAIExtensionTest : VimTestCase() {
"\"balanced\"false <caret>string\"balanced\"",
"\"balanced\"false string\"<caret>\"",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -66,6 +70,7 @@ class MiniAIExtensionTest : VimTestCase() {
"`balanced`false <caret>string`balanced`",
"`balanced`false string`<caret>`",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -82,6 +87,7 @@ class MiniAIExtensionTest : VimTestCase() {
'""",
"''",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -97,6 +103,7 @@ class MiniAIExtensionTest : VimTestCase() {
"""",
"\"\"",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -113,6 +120,7 @@ class MiniAIExtensionTest : VimTestCase() {
`""",
"``",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -134,6 +142,7 @@ class MiniAIExtensionTest : VimTestCase() {
'<caret>'
""",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -152,6 +161,7 @@ print(something)
{}
""",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -173,6 +183,7 @@ print(something)
}
""".trimIndent(),
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -185,6 +196,7 @@ print(something)
"<caret>This is a `'simple'` test",
"This is a `<caret>` test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -198,6 +210,7 @@ print(something)
"<caret>This is a '\"simple\"' test",
"This is a '<caret>' test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -209,6 +222,7 @@ print(something)
"this 'simple<caret> \"test\"'",
"this '<caret>'",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -220,6 +234,7 @@ print(something)
"this 'simple<caret> \"test\"'",
"this '<caret>'",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -231,6 +246,7 @@ print(something)
"this \"simple<caret> 'test'\"",
"this \"<caret>\"",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -242,6 +258,7 @@ print(something)
"this \"simple<caret> 'test'\"",
"this \"<caret>\"",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -253,6 +270,7 @@ print(something)
"this `simple<caret> \"test\"`",
"this `<caret>`",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -264,6 +282,7 @@ print(something)
"this `simple<caret> \"test\"`",
"this `<caret>`",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -275,6 +294,7 @@ print(something)
"this 'simple<caret> \"test\"'",
"this <caret>",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -286,6 +306,7 @@ print(something)
"this 'simple<caret> \"test\"' test",
"this <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -297,6 +318,7 @@ print(something)
"this \"simple<caret> 'test'\"",
"this <caret>",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -308,6 +330,7 @@ print(something)
"this \"simple<caret> 'test'\" test",
"this <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -319,6 +342,7 @@ print(something)
"this `simple<caret> \"test\"`",
"this <caret>",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -330,6 +354,7 @@ print(something)
"this `simple<caret> \"test\"` test",
"this <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -342,6 +367,7 @@ print(something)
"this 'simple \"<caret>test\"'",
"this 'simple <caret>'",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -353,6 +379,7 @@ print(something)
"this 'simple \"<caret>test\"'",
"this 'simple '",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -365,6 +392,7 @@ print(something)
"this 'simple \"nested `<caret>test`\"'",
"this 'simple \"nested <caret>\"'",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -376,6 +404,7 @@ print(something)
"this 'simple \"nested `<caret>test`\"'",
"this 'simple \"nested <caret>\"'",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -387,6 +416,7 @@ print(something)
"<caret>This is a 'simple' test",
"This is a '<caret>' test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -399,6 +429,7 @@ print(something)
"<caret>This is a 'simple test",
"<caret>This is a 'simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -411,6 +442,7 @@ print(something)
"<caret>This is a \"simple\" test",
"This is a \"<caret>\" test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -423,6 +455,7 @@ print(something)
"<caret>This is a \"simple test",
"<caret>This is a \"simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -435,6 +468,7 @@ print(something)
"<caret>This is a `simple` test",
"This is a `<caret>` test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -447,6 +481,7 @@ print(something)
"<caret>This is a `simple test",
"<caret>This is a `simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -459,6 +494,7 @@ print(something)
"<caret>This is a 'simple' test",
"This is a <caret> test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -471,6 +507,7 @@ print(something)
"<caret>This is a 'simple test",
"<caret>This is a 'simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -483,6 +520,7 @@ print(something)
"<caret>This is a \"simple\" test",
"This is a <caret> test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -495,6 +533,7 @@ print(something)
"<caret>This is a \"simple test",
"<caret>This is a \"simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -506,6 +545,7 @@ print(something)
"<caret>This is a `simple` test",
"This is a <caret> test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -518,6 +558,7 @@ print(something)
"<caret>This is a `simple test",
"<caret>This is a `simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -529,6 +570,7 @@ print(something)
"<caret>This is a (simple) test",
"This is a (<caret>) test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -541,6 +583,7 @@ print(something)
"<caret>This is a (simple test",
"<caret>This is a (simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -553,6 +596,7 @@ print(something)
"<caret>This is a [simple] test",
"This is a [<caret>] test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -565,6 +609,7 @@ print(something)
"<caret>This is a [simple test",
"<caret>This is a [simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -577,6 +622,7 @@ print(something)
"<caret>This is a {simple} test",
"This is a {<caret>} test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -589,6 +635,7 @@ print(something)
"<caret>This is a {simple test",
"<caret>This is a {simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -601,6 +648,7 @@ print(something)
"<caret>This is a (simple) test",
"This is a <caret> test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -613,6 +661,7 @@ print(something)
"<caret>This is a (simple test",
"<caret>This is a (simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -625,6 +674,7 @@ print(something)
"<caret>This is a [simple] test",
"This is a <caret> test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -636,6 +686,7 @@ print(something)
"<caret>This is a [simple test",
"<caret>This is a [simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -648,6 +699,7 @@ print(something)
"<caret>This is a {simple} test",
"This is a <caret> test",
Mode.INSERT,
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -660,6 +712,7 @@ print(something)
"<caret>This is a {simple test",
"<caret>This is a {simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -672,6 +725,7 @@ print(something)
"<caret>This is a 'simple' test",
"This is a '<caret>' test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -684,6 +738,7 @@ print(something)
"<caret>This is a 'simple test",
"<caret>This is a 'simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -696,6 +751,7 @@ print(something)
"<caret>This is a \"simple\" test",
"This is a \"<caret>\" test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -708,6 +764,7 @@ print(something)
"<caret>This is a \"simple test",
"<caret>This is a \"simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -720,6 +777,7 @@ print(something)
"<caret>This is a `simple` test",
"This is a `<caret>` test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -732,6 +790,7 @@ print(something)
"<caret>This is a `simple test",
"<caret>This is a `simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -744,6 +803,7 @@ print(something)
"<caret>This is a 'simple' test",
"This is a <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -755,6 +815,7 @@ print(something)
"<caret>This is a 'simple test",
"<caret>This is a 'simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -767,6 +828,7 @@ print(something)
"<caret>This is a \"simple\" test",
"This is a <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -779,6 +841,7 @@ print(something)
"<caret>This is a \"simple test",
"<caret>This is a \"simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -791,6 +854,7 @@ print(something)
"<caret>This is a `simple` test",
"This is a <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -803,6 +867,7 @@ print(something)
"<caret>This is a `simple test",
"<caret>This is a `simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -815,6 +880,7 @@ print(something)
"<caret>This is a (simple) test",
"This is a (<caret>) test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -827,6 +893,7 @@ print(something)
"<caret>This is a (simple test",
"<caret>This is a (simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -839,6 +906,7 @@ print(something)
"<caret>This is a [simple] test",
"This is a [<caret>] test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -851,6 +919,7 @@ print(something)
"<caret>This is a [simple test",
"<caret>This is a [simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -863,6 +932,7 @@ print(something)
"<caret>This is a {simple} test",
"This is a {<caret>} test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -875,6 +945,7 @@ print(something)
"<caret>This is a {simple test",
"<caret>This is a {simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -887,6 +958,7 @@ print(something)
"<caret>This is a (simple) test",
"This is a <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -899,6 +971,7 @@ print(something)
"<caret>This is a (simple test",
"<caret>This is a (simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -911,6 +984,7 @@ print(something)
"<caret>This is a [simple] test",
"This is a <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -923,6 +997,7 @@ print(something)
"<caret>This is a [simple test",
"<caret>This is a [simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -935,6 +1010,7 @@ print(something)
"<caret>This is a {simple} test",
"This is a <caret> test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}
@ -947,6 +1023,7 @@ print(something)
"<caret>This is a {simple test",
"<caret>This is a {simple test",
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
assertSelection(null)
}

View File

@ -8,7 +8,6 @@
package org.jetbrains.plugins.ideavim.extension.replacewithregister
import com.intellij.idea.TestFor
import com.intellij.openapi.application.ApplicationManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
@ -651,137 +650,4 @@ class ReplaceWithRegisterTest : VimTestCase() {
assertState("one two three")
enterCommand("set clipboard&")
}
@TestFor(issues = ["VIM-2263"])
@Test
fun `test replace with count from register a`() {
val before = """
${c}first line
second line
third line
fourth line
fifth line
""".trimIndent()
configureByText(before)
typeText("\"ay$")
val context = injector.executionContextManager.getEditorExecutionContext(fixture.editor.vim)
val aRegText = injector.registerGroup.getRegister(fixture.editor.vim, context, 'a' )!!.text
assertEquals("first line", aRegText)
val defaultRegText = injector.registerGroup.getRegister(fixture.editor.vim, context, '"' )!!.text
assertEquals("first line", defaultRegText )
typeText("j")
typeText("y$")
val aRegTextAfter = injector.registerGroup.getRegister(fixture.editor.vim, context, 'a' )!!.text
assertEquals("first line", aRegTextAfter)
val defaultRegTextAfter = injector.registerGroup.getRegister(fixture.editor.vim, context, '"' )!!.text
assertEquals("second line", defaultRegTextAfter )
typeText("\"a2grr")
val expected = """
first line
${c}first line
fourth line
fifth line
""".trimIndent()
assertState(expected)
}
@TestFor(issues = ["VIM-2263"])
@Test
fun `test replace from the register a (without count)`() {
val before = """
${c}first line
second line
third line
fourth line
fifth line
""".trimIndent()
configureByText(before)
typeText("\"ay$")
val context = injector.executionContextManager.getEditorExecutionContext(fixture.editor.vim)
val aRegText = injector.registerGroup.getRegister(fixture.editor.vim, context, 'a' )!!.text
assertEquals("first line", aRegText)
val defaultRegText = injector.registerGroup.getRegister(fixture.editor.vim, context, '"' )!!.text
assertEquals("first line", defaultRegText )
typeText("j")
typeText("y$")
val aRegTextAfter = injector.registerGroup.getRegister(fixture.editor.vim, context, 'a' )!!.text
assertEquals("first line", aRegTextAfter)
val defaultRegTextAfter = injector.registerGroup.getRegister(fixture.editor.vim, context, '"' )!!.text
assertEquals("second line", defaultRegTextAfter )
typeText("\"agrr")
val expected = """
first line
${c}first line
third line
fourth line
fifth line
""".trimIndent()
assertState(expected)
}
@TestFor(issues = ["VIM-2263"])
@Test
fun `test replace from the register a to the end of the line`() {
val before = """
${c}first line
second line
third line
fourth line
fifth line
""".trimIndent()
configureByText(before)
typeText("\"ayiw")
val context = injector.executionContextManager.getEditorExecutionContext(fixture.editor.vim)
val aRegText = injector.registerGroup.getRegister(fixture.editor.vim, context, 'a' )!!.text
assertEquals("first", aRegText)
val defaultRegText = injector.registerGroup.getRegister(fixture.editor.vim, context, '"' )!!.text
assertEquals("first", defaultRegText )
typeText("j")
typeText("y$")
typeText("w")
val aRegTextAfter = injector.registerGroup.getRegister(fixture.editor.vim, context, 'a' )!!.text
assertEquals("first", aRegTextAfter)
val defaultRegTextAfter = injector.registerGroup.getRegister(fixture.editor.vim, context, '"' )!!.text
assertEquals("second line", defaultRegTextAfter )
typeText("\"agr$")
val expected = """
first line
second firs${c}t
third line
fourth line
fifth line
""".trimIndent()
assertState(expected)
}
}

View File

@ -633,17 +633,6 @@ class VimSurroundExtensionTest : VimTestCase() {
doTest(listOf(motion), before, after, Mode.NORMAL())
}
// VIM-3841
@Test
fun `test return to Normal mode after surround in Visual mode`() {
doTest(
listOf("veS\"", "i"),
"lorem ${c}ipsum dolor sit amet",
"lorem ${c}\"ipsum\" dolor sit amet",
Mode.INSERT,
)
}
companion object {
@JvmStatic
fun removeWhiteSpaceWithClosingBracketParams() = listOf(

View File

@ -32,7 +32,7 @@ class VimListenersTest : VimTestCase() {
super.setUp(testInfo)
val manager =
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope(name = "VimListenersTest"))
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope())
fixture.project.replaceService(FileEditorManager::class.java, manager, fixture.testRootDisposable)
VimListenerTestObject.disposedCounter = 0

View File

@ -67,7 +67,7 @@ class EffectiveOptionChangeListenerTest : VimTestCase() {
// Copied from FileEditorManagerTestCase to allow us to split windows
manager =
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope(name = "EffectiveOptionChangeListenerTest"))
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope())
fixture.project.replaceService(FileEditorManager::class.java, manager, fixture.testRootDisposable)
// Create a new editor that will represent a new buffer in a separate window. It will have default values

View File

@ -61,7 +61,7 @@ class OptionDeclaredScopeTest : VimTestCase() {
super.setUp(testInfo)
// Copied from FileEditorManagerTestCase to allow us to split windows
fileEditorManager = FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope(name = "OptionDeclaredScopeTest"))
fileEditorManager = FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope())
fixture.project.replaceService(FileEditorManager::class.java, fileEditorManager, fixture.testRootDisposable)
// Create a new editor that will represent a new buffer in a separate window. It will have default values
@ -160,7 +160,7 @@ class OptionDeclaredScopeTest : VimTestCase() {
}
val virtualFile = editor.virtualFile
if (virtualFile != null) {
if (editorWindow != null && virtualFile != null) {
editorWindow.closeFile(virtualFile)
editorWindow.requestFocus(true)
}

View File

@ -50,7 +50,7 @@ class TextWidthOptionMapperTest : VimTestCase() {
// Copied from FileEditorManagerTestCase to allow us to split windows
val manager =
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope(name = "TextWidthOptionMapperTest"))
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope())
fixture.project.replaceService(FileEditorManager::class.java, manager, fixture.testRootDisposable)
ApplicationManager.getApplication().invokeAndWait {

View File

@ -37,7 +37,7 @@ class WrapOptionMapperTest : VimTestCase() {
// Copied from FileEditorManagerTestCase to allow us to split windows
manager =
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope(name = "WrapOptionMapperTest"))
FileEditorManagerImpl(fixture.project, (fixture.project as ComponentManagerEx).getCoroutineScope().childScope())
fixture.project.replaceService(FileEditorManager::class.java, manager, fixture.testRootDisposable)
configureByText("\n")

View File

@ -8,7 +8,6 @@
package org.jetbrains.plugins.ideavim.ui
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.ui.ShowCmd
import org.jetbrains.plugins.ideavim.SkipNeovimReason
@ -17,6 +16,7 @@ import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.waitAndAssert
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
import kotlin.test.assertEquals
@ -87,12 +87,15 @@ class ShowCmdTest : VimTestCase() {
assertEquals("32d", getShowCmdText())
}
// TODO: This test fails because IdeaVim's mapping handler doesn't correctly expand unhandled keys on timeout
@Test
@Disabled
fun `test showcmd expands ambiguous mapped keys on timeout`() {
// `rrr` should timeout and replay `rr` which is mapped to `42`
enterCommand("nmap rr 42")
enterCommand("nmap rrr 55")
typeText(injector.parser.parseKeys("12rr"))
waitAndAssert(injector.globalOptions().timeoutlen + 100) { "1242" == getShowCmdText() }
waitAndAssert { "1242" == getShowCmdText() }
}
@TestWithoutNeovim(reason = SkipNeovimReason.SHOW_CMD)

View File

@ -137,7 +137,7 @@ abstract class VimTestCase {
VimPlugin.getSearch().resetState()
if (VimPlugin.isNotEnabled()) VimPlugin.setEnabled(true)
injector.globalOptions().ideastrictmode = true
Checks.reset()
VimTestCase.Checks.reset()
clearClipboard()
// Make sure the entry text field gets a bounds, or we won't be able to work out caret location
@ -442,9 +442,9 @@ abstract class VimTestCase {
editor,
)
val project = fixture.project
when (Checks.keyHandler) {
Checks.KeyHandlerMethod.DIRECT_TO_VIM -> typeText(keys.filterNotNull(), editor, project)
Checks.KeyHandlerMethod.VIA_IDE -> typeTextViaIde(keys.filterNotNull(), editor)
when (VimTestCase.Checks.keyHandler) {
VimTestCase.Checks.KeyHandlerMethod.DIRECT_TO_VIM -> typeText(keys.filterNotNull(), editor, project)
VimTestCase.Checks.KeyHandlerMethod.VIA_IDE -> typeTextViaIde(keys.filterNotNull(), editor)
}
return editor
}
@ -749,7 +749,7 @@ abstract class VimTestCase {
}
protected fun assertCaretsVisualAttributes() {
if (!Checks.caretShape) return
if (!VimTestCase.Checks.caretShape) return
val editor = fixture.editor
val attributes = GuiCursorOptionHelper.getAttributes(getGuiCursorMode(editor))
val colour = editor.colorsScheme.getColor(EditorColors.CARET_COLOR)
@ -946,8 +946,8 @@ abstract class VimTestCase {
}
// Disable or enable checks for the particular test
protected inline fun setupChecks(setup: Checks.() -> Unit) {
Checks.setup()
protected inline fun setupChecks(setup: VimTestCase.Checks.() -> Unit) {
VimTestCase.Checks.setup()
}
protected fun assertExException(expectedErrorMessage: String, action: () -> Unit) {
@ -1082,24 +1082,11 @@ abstract class VimTestCase {
@JvmStatic
fun commandToKeys(command: String): List<KeyStroke> {
val keys: MutableList<KeyStroke> = ArrayList()
keys.addAll(injector.parser.parseKeys(":"))
var startIndex = if (command.startsWith(":")) 1 else 0
// Special case support for <C-U>
startIndex = if (command.substring(startIndex).startsWith("<C-U>", ignoreCase = true)) {
keys.addAll(injector.parser.parseKeys("<C-U>"))
startIndex + 5
}
else {
startIndex
}
// We don't parse the rest of the command, to avoid parsing special keys in e.g. maps. Note that values such as
// `<expr>` or `<args>` would be correctly handled by parseKeys
keys.addAll(injector.parser.stringToKeys(command.substring(startIndex)))
if (keys.last().keyCode != KeyEvent.VK_ENTER) {
keys.addAll(injector.parser.parseKeys("<Enter>"))
if (!command.startsWith(":")) {
keys.addAll(injector.parser.parseKeys(":"))
}
keys.addAll(injector.parser.stringToKeys(command)) // Avoids trying to parse 'command ... <args>' as a special char
keys.addAll(injector.parser.parseKeys("<Enter>"))
return keys
}

View File

@ -32,21 +32,18 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testImplementation(testFixtures(project(":"))) // The root project
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
intellijPlatform {
// Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
var useInstaller = "EAP-SNAPSHOT" !in ideaVersion
if (ideaType == "RD") {
// Using Rider as a target IntelliJ Platform with `useInstaller = true` is currently not supported, please set `useInstaller = false` instead. See: https://github.com/JetBrains/intellij-platform-gradle-plugin/issues/1852
useInstaller = false
}
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
create(ideaType, ideaVersion, useInstaller)
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
bundledPlugins("com.intellij.java", "org.jetbrains.plugins.yaml")
instrumentationTools()
}
}

View File

@ -15,7 +15,6 @@ import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimJavaTestCase
import org.jetbrains.yaml.YAMLFileType
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
@ -438,6 +437,63 @@ class CommentaryExtensionTest : VimJavaTestCase() {
)
}
@Test
fun `test text object deletes single line comment from leading whitespace 2`() {
doTest(
"dgc",
"""
<caret>
// Comment 1
final Int value = 42;
""".trimIndent(),
"""
final Int value = 42;
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
}
@Test
fun `test text object deletes single line comment from leading whitespace 3`() {
doTest(
"dgc",
"""
final Int value1 = 42;
<caret>
// Comment 1
final Int value2 = 42;
""".trimIndent(),
"""
final Int value1 = 42;
final Int value2 = 42;
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
}
@Test
fun `test text object deletes single line comment from trailing whitespace`() {
doTest(
"dgc",
"""
// Comment 1
<caret>
final Int value = 42;
""".trimIndent(),
"""
final Int value = 42;
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
}
@Test
fun `test text object deletes single line comments separated by whitespace`() {
doTest(
@ -449,10 +505,26 @@ class CommentaryExtensionTest : VimJavaTestCase() {
final Int value = 42;
""".trimIndent(),
"""
final Int value = 42;
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
}
@Test
fun `test text object deletes disjointed single line comments from whitespace`() {
doTest(
"dgc",
"""
// Comment 1
<caret>
// Comment 2
final Int value = 42;
""".trimIndent(),
"""
final Int value = 42;
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
@ -583,10 +655,6 @@ class CommentaryExtensionTest : VimJavaTestCase() {
final Int value = 42;
""".trimIndent(),
"""
/* Comment 1
* Comment 2
* Comment 3 */
final Int value = 42;
""".trimIndent(),
Mode.NORMAL(),
@ -636,7 +704,8 @@ class CommentaryExtensionTest : VimJavaTestCase() {
doTest(
"dgc",
"""
/**<caret>
<caret>
/**
* Cool summary, dude
* @param value the value, innit
* @param name what's your name?
@ -694,79 +763,6 @@ class CommentaryExtensionTest : VimJavaTestCase() {
}
""".trimIndent(),
"""
/* Block comment */
/**
* Cool summary, dude
* @param value the value, innit
* @param name what's your name?
*/
public void something(int value, String name) {
}
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
}
@Test
fun `test text object deletes JavaDoc comment and adjoining comments separated by whitespace 2`() {
doTest(
"dgc",
"""
// This should be deleted too
/* <caret>Block comment */
/**
* Cool summary, dude
* @param value the value, innit
* @param name what's your name?
*/
public void something(int value, String name) {
}
""".trimIndent(),
"""
// This should be deleted too
/**
* Cool summary, dude
* @param value the value, innit
* @param name what's your name?
*/
public void something(int value, String name) {
}
""".trimIndent(),
Mode.NORMAL(),
JavaFileType.INSTANCE,
)
}
@Test
fun `test text object deletes JavaDoc comment and adjoining comments separated by whitespace 3`() {
doTest(
"dgc",
"""
// This should be deleted too
/* Block comment */
/**<caret>
* Cool summary, dude
* @param value the value, innit
* @param name what's your name?
*/
public void something(int value, String name) {
}
""".trimIndent(),
"""
// This should be deleted too
/* Block comment */
public void something(int value, String name) {
}
""".trimIndent(),
@ -875,7 +871,6 @@ class CommentaryExtensionTest : VimJavaTestCase() {
}
@Test
@Disabled("Doesn't work with the new version of IntelliJ and gradle plugin")
fun `test block comment falls back to line comment when not available`() {
doTest(
"gcw",

View File

@ -25,16 +25,17 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testImplementation(testFixtures(project(":"))) // The root project
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
intellijPlatform {
// Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
create(ideaType, ideaVersion, false)
create(ideaType, ideaVersion, useInstaller)
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
instrumentationTools()
}
}

View File

@ -25,17 +25,18 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testImplementation(testFixtures(project(":"))) // The root project
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
intellijPlatform {
// Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
create(ideaType, ideaVersion, false)
create(ideaType, ideaVersion, useInstaller)
bundledPlugins("com.intellij.java")
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
instrumentationTools()
}
}

View File

@ -15,7 +15,7 @@ val javaVersion: String by project
val remoteRobotVersion: String by project
dependencies {
testFixturesImplementation("org.junit.jupiter:junit-jupiter:5.12.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter:5.12.0")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testFixturesImplementation(testFixtures(project(":"))) // The root project

View File

@ -16,6 +16,7 @@ import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.fixtures.JTreeFixture
import com.intellij.remoterobot.search.locators.byXpath
import com.intellij.remoterobot.stepsProcessing.step
import com.intellij.remoterobot.utils.WaitForConditionTimeoutException
import com.intellij.remoterobot.utils.waitFor
import java.time.Duration
@ -32,7 +33,12 @@ class IdeaFrame(
val projectViewTree: JTreeFixture
get() {
return find<JTreeFixture>(byXpath("MyProjectViewTree", "//div[@class='MyProjectViewTree']"), Duration.ofSeconds(30))
return try {
find<JTreeFixture>(byXpath("MyProjectViewTree", "//div[@class='MyProjectViewTree']"), Duration.ofSeconds(30))
} catch (_: WaitForConditionTimeoutException) {
// [VERSION UPDATE] 2025.1+ Leave only MyProjectViewTree.
find<JTreeFixture>(byXpath("ProjectViewTree", "//div[@class='ProjectViewTree']"), Duration.ofSeconds(30))
}
}
val projectName

View File

@ -13,10 +13,9 @@ import com.intellij.remoterobot.stepsProcessing.StepWorker
object StepsLogger {
private var initializaed = false
private val initializationLock = Any()
@JvmStatic
fun init() = synchronized(initializationLock) {
fun init() = synchronized(initializaed) {
if (initializaed.not()) {
StepWorker.registerProcessor(StepLogger())
initializaed = true

View File

@ -9,6 +9,7 @@
import com.intellij.remoterobot.RemoteRobot
import com.intellij.remoterobot.data.RemoteComponent
import com.intellij.remoterobot.fixtures.CommonContainerFixture
import com.intellij.remoterobot.fixtures.ComponentFixture
import com.intellij.remoterobot.fixtures.DefaultXpath
import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.search.locators.byXpath
@ -24,10 +25,13 @@ class ManageLicensesFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteCompo
CommonContainerFixture(remoteRobot, remoteComponent) {
fun enableFreeTier() {
radioButton("Activation code").click()
/// Note: The license code is obfuscated, so we use the class `W`. But a better solution is required.
textFields(byXpath("//div[@class='W']")).first().text = System.getenv("RIDER_LICENSE")
button("Activate").click()
find<ComponentFixture>(
byXpath(
"//div[@class='SegmentedButton' and @action='Start trial (null)']",
)
).click()
// checkBox("I agree with", contains = true).select()
button("Start Free 30-Day Trial").click()
button("Close").click()
}
}

View File

@ -45,54 +45,9 @@ class RiderUiTest {
val editor = editor("Program.cs")
testEnterWorksInNormalMode(editor)
testReformatCodeUsingMappingWithSpace(editor)
testEnterInInsertMode(editor)
}
}
private fun IdeaFrame.testEnterInInsertMode(editor: Editor) {
editor.findText(" for more information").click()
keyboard {
enterText("A")
enter()
}
Thread.sleep(1000)
assertEquals(
"""
|// See https://aka.ms/new-console-template for more information
|
|
|Console.WriteLine("Hello, World!");
""".trimMargin(), editor.text
)
keyboard {
escape()
enterText("dd")
}
}
private fun IdeaFrame.testReformatCodeUsingMappingWithSpace(editor: Editor) {
editor.findText(" for more information").click()
keyboard {
enterText("jj>>")
enterText(":map <Space>x <Action>(ReformatCode)")
enter()
enterText(" x")
}
Thread.sleep(1000)
assertEquals(
"""
|// See https://aka.ms/new-console-template for more information
|
|Console.WriteLine("Hello, World!");
""".trimMargin(), editor.text
)
}
private fun IdeaFrame.testEnterWorksInNormalMode(editor: Editor) {
editor.findText(" for more information").click()
keyboard {

View File

@ -45,13 +45,13 @@ afterEvaluate {
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.12.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.12.0")
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
// Can be removed when IJPL-159134 is fixed
// testRuntimeOnly("junit:junit:4.13.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.12.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.12.0")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-test
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")

View File

@ -34,8 +34,6 @@ import com.maddyhome.idea.vim.key.consumers.SelectRegisterConsumer
import com.maddyhome.idea.vim.state.KeyHandlerState
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.ConcurrentLinkedDeque
import javax.swing.KeyStroke
/**
@ -44,7 +42,7 @@ import javax.swing.KeyStroke
*/
// TODO for future refactorings (PRs are welcome)
// 1. avoid using handleKeyRecursionCount & shouldRecord
// 2. maybe we can live without allowKeyMappings: Boolean
// 2. maybe we can live without allowKeyMappings: Boolean & mappingCompleted: Boolean
class KeyHandler {
private val keyConsumers: List<KeyConsumer> = listOf(
ModalInputConsumer(),
@ -61,24 +59,6 @@ class KeyHandler {
)
private var handleKeyRecursionCount = 0
private var commandListener: ConcurrentLinkedDeque<() -> Unit> = ConcurrentLinkedDeque()
/**
* This is an internal API of IdeaVim. External plugins must not use it.
*/
@ApiStatus.Internal
fun addCommandListener(listener: () -> Unit) {
commandListener.add(listener)
}
/**
* This is an internal API of IdeaVim. External plugins must not use it.
*/
@ApiStatus.Internal
fun removeAllCommandListeners() {
commandListener.clear()
}
// KeyHandlerState requires injector.keyGroup to be initialized and that's why we don't create it immediately and have this here
// TODO figure out a better solution
private val defaultKeyHandlerState by lazy { KeyHandlerState() }
@ -103,15 +83,14 @@ class KeyHandler {
* @param context The data context
*/
fun handleKey(editor: VimEditor, key: KeyStroke, context: ExecutionContext, keyState: KeyHandlerState) {
commandListener.forEach { it() }
handleKey(editor, key, context, allowKeyMappings = true, mappingCompleted = false, keyState)
}
/**
* Handling input keys with additional parameters
*
* @param allowKeyMappings If we allow key mappings or not
* @param mappingCompleted No longer used
* @param allowKeyMappings - If we allow key mappings or not
* @param mappingCompleted - if true, we don't check if the mapping is incomplete
*/
fun handleKey(
editor: VimEditor,
@ -121,7 +100,13 @@ class KeyHandler {
mappingCompleted: Boolean,
keyState: KeyHandlerState,
) {
val result = processKey(key, editor, allowKeyMappings, KeyProcessResult.SynchronousKeyProcessBuilder(keyState))
val result = processKey(
key,
editor,
allowKeyMappings,
mappingCompleted,
KeyProcessResult.SynchronousKeyProcessBuilder(keyState)
)
if (result is KeyProcessResult.Executable) {
result.execute(editor, context)
}
@ -134,30 +119,31 @@ class KeyHandler {
* Alternatively, if we understand the key, we return a 'KeyProcessResult.Executable', which contains a runnable that
* could execute the key if needed.
*/
private fun processKey(
fun processKey(
key: KeyStroke,
editor: VimEditor,
allowKeyMappings: Boolean,
keyProcessResultBuilder: KeyProcessResult.KeyProcessResultBuilder,
mappingCompleted: Boolean,
processBuilder: KeyProcessResult.KeyProcessResultBuilder,
): KeyProcessResult {
synchronized(lock) {
logger.trace {
"""
------- Key Handler -------
Start key processing. allowKeyMappings: $allowKeyMappings
Start key processing. allowKeyMappings: $allowKeyMappings, mappingCompleted: $mappingCompleted
Key: $key
""".trimIndent()
}
logger.trace { keyProcessResultBuilder.state.toString() }
logger.trace { processBuilder.state.toString() }
logger.trace { "Mode = ${editor.mode}" }
val maxMapDepth = injector.globalOptions().maxmapdepth
if (handleKeyRecursionCount >= maxMapDepth) {
keyProcessResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
processBuilder.addExecutionStep { _, lambdaEditor, _ ->
logger.warn("Key handling, maximum recursion of the key received. maxdepth=$maxMapDepth")
injector.messages.showStatusBarMessage(lambdaEditor, injector.messages.message("E223"))
injector.messages.indicateError()
}
return keyProcessResultBuilder.build()
return processBuilder.build()
}
injector.messages.clearError()
@ -167,25 +153,25 @@ class KeyHandler {
handleKeyRecursionCount++
try {
val isProcessed = keyConsumers.any {
it.consumeKey(key, editor, allowKeyMappings, keyProcessResultBuilder)
it.consumeKey(key, editor, allowKeyMappings, mappingCompleted, processBuilder)
}
if (isProcessed) {
logger.trace { "Key was successfully caught by consumer" }
keyProcessResultBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
finishedCommandPreparation(lambdaEditor, lambdaContext, key, shouldRecord, lambdaKeyState)
}
} else {
// Key wasn't processed by any of the consumers, so we reset our key state
onUnknownKey(editor, keyProcessResultBuilder.state)
updateState(keyProcessResultBuilder.state)
onUnknownKey(editor, processBuilder.state)
updateState(processBuilder.state)
return KeyProcessResult.Unknown.apply {
handleKeyRecursionCount-- // because onFinish will now be executed for unknown
}
}
} finally {
keyProcessResultBuilder.onFinish = { handleKeyRecursionCount-- }
processBuilder.onFinish = { handleKeyRecursionCount-- }
}
return keyProcessResultBuilder.build()
return processBuilder.build()
}
}
@ -239,7 +225,7 @@ class KeyHandler {
val operatorArguments = OperatorArguments(command.rawCount, editorState.mode)
// If we were in "operator pending" mode, reset back to normal mode.
// But opening command line should not reset operator pending mode (e.g. `d/foo`)
// But opening command line should not reset operator pending mode (e.g. `d/foo`
if (!command.flags.contains(CommandFlags.FLAG_START_EX)) {
editor.resetOpPending()
}

View File

@ -1,95 +0,0 @@
/*
* Copyright 2003-2025 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.action.change.change
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.DuplicableOperatorAction
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
@CommandOrMotion(keys = ["g?"], modes = [Mode.NORMAL])
class ChangeRot13MotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableOperatorAction {
override val type: Command.Type = Command.Type.CHANGE
override val argumentType: Argument.Type = Argument.Type.MOTION
override val duplicateWith: Char = '?'
override fun execute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments,
): Boolean {
if (argument == null) return false
val range = injector.motion.getMotionRange(
editor, caret, context, argument,
operatorArguments
) ?: return false
return applyRot13(editor, caret, range)
}
companion object {
/**
* Apply ROT13 encoding to the text in the given range.
* Only ASCII letters below 0x80 are converted.
*/
fun applyRot13(editor: VimEditor, caret: VimCaret, range: TextRange): Boolean {
val starts = range.startOffsets
val ends = range.endOffsets
for (i in ends.indices.reversed()) {
applyRot13ToRange(editor, caret, starts[i], ends[i])
}
caret.moveToOffset(range.startOffset)
return true
}
/**
* Apply ROT13 encoding to the text between start and end offsets.
* Only ASCII letters below 0x80 are converted.
*/
private fun applyRot13ToRange(editor: VimEditor, caret: VimCaret, start: Int, end: Int) {
val (newStart, newEnd) = if (start > end) end to start else start to end
val changedText = buildString {
for (i in newStart until newEnd) {
append(rot13(editor.text()[i]))
}
}
injector.changeGroup.replaceText(editor, caret, newStart, newEnd, changedText)
}
/**
* Apply ROT13 encoding to a single character.
*
* Only ASCII letters below 0x80 are converted, see https://github.com/vim/vim/blob/470317f78b110b4559cecb26039b5f93447c1bf0/src/ops.c#L1591
*/
private fun rot13(ch: Char): Char {
// Only convert ASCII letters below 0x80
return when {
ch.code < 0x80 && ch in 'a'..'z' -> 'a' + (ch - 'a' + 13) % 26
ch.code < 0x80 && ch in 'A'..'Z' -> 'A' + (ch - 'A' + 13) % 26
else -> ch
}
}
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2003-2025 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.action.change.change
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
/**
* @author vlan
*/
@CommandOrMotion(keys = ["g?"], modes = [Mode.VISUAL])
class ChangeRot13VisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE
override fun executeAction(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
range: VimSelection,
operatorArguments: OperatorArguments,
): Boolean {
return ChangeRot13MotionAction.applyRot13(editor, caret, range.toVimTextRange(false))
}
}

View File

@ -70,6 +70,6 @@ class MotionDownNotLineWiseAction : MotionActionHandler.ForEachCaret() {
argument: Argument?,
operatorArguments: OperatorArguments,
): Motion {
return injector.motion.getVerticalMotionOffset(editor, caret, operatorArguments.count1, bufferLines = true)
return injector.motion.getVerticalMotionOffset(editor, caret, operatorArguments.count1)
}
}

View File

@ -70,6 +70,6 @@ class MotionUpNotLineWiseAction : MotionActionHandler.ForEachCaret() {
argument: Argument?,
operatorArguments: OperatorArguments,
): Motion {
return injector.motion.getVerticalMotionOffset(editor, caret, -operatorArguments.count1, bufferLines = true)
return injector.motion.getVerticalMotionOffset(editor, caret, -operatorArguments.count1)
}
}

Some files were not shown because too many files have changed in this diff Show More