1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-11-25 07:42:59 +01:00

Compare commits

...

460 Commits

Author SHA1 Message Date
120d889dfb
Implement motions to go to next/previous misspelled word 2024-02-23 10:16:31 +02:00
Matt Ellis
f439474b73 Fix set command tests
Also hides more feature flags and diagnostic settings from users and unit tests. Shows them when in internal mode.
2024-02-23 10:04:23 +02:00
Matt Ellis
d6cd92e256 Migrate extensions to use operatorfunc option 2024-02-23 10:04:23 +02:00
Matt Ellis
3a294268d9 Introduce operatorfunc option
Allows creating custom operators in script, as shown in JetBrains/ideavim#702
2024-02-23 10:04:23 +02:00
Alex Plate
9b81c7e650
Update junit version 2024-02-23 10:03:30 +02:00
Alex Plate
e229fb3ad7
Add new plugin that depends on IdeaVim 2024-02-23 09:26:46 +02:00
Alex Plate
720eae63fa
Fix the incorrect condition in UI tests 2024-02-23 09:23:42 +02:00
Alex Plate
0df96a24bd
Add a missing @BeforeEach in tests 2024-02-22 09:19:24 +02:00
Alex Plate
21a1588ede
Increase wait timeout for UI tests 2024-02-22 09:02:03 +02:00
Alex Plate
7970006e8c
Log the base commit during dev version calculation 2024-02-22 09:02:02 +02:00
dependabot[bot]
418d0cff7f Bump org.junit.jupiter:junit-jupiter from 5.10.1 to 5.10.2
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.10.1 to 5.10.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.1...r5.10.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 17:42:43 +02:00
dependabot[bot]
7284360774 Bump org.jetbrains.intellij from 1.17.0 to 1.17.2
Bumps org.jetbrains.intellij from 1.17.0 to 1.17.2.

---
updated-dependencies:
- dependency-name: org.jetbrains.intellij
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 17:42:15 +02:00
dependabot[bot]
9fc3fadee8 Bump org.antlr:antlr4 from 4.10.1 to 4.13.1
Bumps [org.antlr:antlr4](https://github.com/antlr/antlr4) from 4.10.1 to 4.13.1.
- [Release notes](https://github.com/antlr/antlr4/releases)
- [Changelog](https://github.com/antlr/antlr4/blob/dev/CHANGES.txt)
- [Commits](https://github.com/antlr/antlr4/compare/4.10.1...4.13.1)

---
updated-dependencies:
- dependency-name: org.antlr:antlr4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 17:42:05 +02:00
Alex Plate
3d2db56f63
Use sets during plugin detection to avoid sorting problems 2024-02-21 10:43:44 +02:00
Alex Plate
e9c7cb8670
Update logic of calculation of dev version 2024-02-21 10:40:20 +02:00
dependabot[bot]
87d19274c5 Bump io.ktor:ktor-client-content-negotiation from 2.3.7 to 2.3.8
Bumps [io.ktor:ktor-client-content-negotiation](https://github.com/ktorio/ktor) from 2.3.7 to 2.3.8.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/commits)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-content-negotiation
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 17:10:25 +02:00
dependabot[bot]
3161bf8ffd Bump io.ktor:ktor-client-core from 2.3.7 to 2.3.8
Bumps [io.ktor:ktor-client-core](https://github.com/ktorio/ktor) from 2.3.7 to 2.3.8.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/commits)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 17:10:21 +02:00
Alex Plate
b68865587e
Wait up to 5 mins for initialization of PyCharm 2024-02-20 17:06:32 +02:00
dependabot[bot]
7dc0dbe944 Bump io.ktor:ktor-serialization-kotlinx-json from 2.3.7 to 2.3.8
Bumps [io.ktor:ktor-serialization-kotlinx-json](https://github.com/ktorio/ktor) from 2.3.7 to 2.3.8.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/commits)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-serialization-kotlinx-json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 16:56:35 +02:00
dependabot[bot]
f50a363525 Bump io.ktor:ktor-client-cio from 2.3.7 to 2.3.8
Bumps [io.ktor:ktor-client-cio](https://github.com/ktorio/ktor) from 2.3.7 to 2.3.8.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/commits)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-cio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 16:56:16 +02:00
Alex Plate
57ad4c70d1
Do not analyze test fixtures by qodana 2024-02-20 16:46:43 +02:00
Alex Plate
d3d93b898f
Unregister NotificationService project service
It's not registered as a light service and doesn't need to be registered in xml files
2024-02-20 16:46:07 +02:00
Alex Plate
7d8973edb2
Add tests for new java matchit functionality
From PR https://github.com/JetBrains/ideavim/pull/802
2024-02-20 16:42:28 +02:00
dependabot[bot]
2302b576b0 Bump io.ktor:ktor-client-auth from 2.3.7 to 2.3.8
Bumps [io.ktor:ktor-client-auth](https://github.com/ktorio/ktor) from 2.3.7 to 2.3.8.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/commits)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 16:42:07 +02:00
f4782630d4 Add Matchit support for Java statements 2024-02-20 16:41:34 +02:00
IdeaVim Bot
8c1a2a686f Update changelog after merging PR 2024-02-20 14:33:04 +00:00
32d5e1e6fa Enforce LF line separator in project code style 2024-02-20 16:31:36 +02:00
Alex Plate
a381a1cacc
Wait till all toolwindows initialziation 2024-02-20 16:19:29 +02:00
Alex Plate
73c3c9f7fe
Replace Enum.values() with Enum.entries, as suggested since 1.9 2024-02-20 16:12:34 +02:00
Alex Plate
67ef0a75d5
Update capitalization 2024-02-20 16:12:11 +02:00
Alex Plate
328bc5e95a
Convert some services to light services 2024-02-20 16:10:07 +02:00
Alex Plate
7f8021e37e
Update the function that waits for smart mode 2024-02-20 15:49:42 +02:00
Alex Plate
9701b7e79b
Add test reports to artifacts 2024-02-20 15:10:15 +02:00
Alex Plate
7a52c6fec9
Cleanup tests 2024-02-20 14:51:13 +02:00
Alex Plate
1503639d4b
Remove generated lexing from qodana analyze 2024-02-20 14:51:06 +02:00
Alex Plate
e82f19c852
Add test for checking an issue that
was caught by property tests
2024-02-20 13:52:17 +02:00
Alex Plate
edd69c9c25
Apply patch for qodata TC config 2024-02-20 13:12:14 +02:00
Alex Plate
fc61e369fb
Fix some deprecated calls 2024-02-20 13:11:10 +02:00
aleksei.plate@jetbrains.com
113586b59b TeamCity change in 'Ideavim' project: runners of 'Qodana checks' build configuration were updated 2024-02-20 10:53:37 +00:00
Alex Plate
5dbd5e1c89
Update the changelog 2024-02-20 12:47:06 +02:00
IdeaVim Bot
04b7d9e2c3 Preparation to 2.9.0 release 2024-02-20 10:41:06 +00:00
Alex Plate
5f2743176a
Update qodana configuration on TC 2024-02-20 12:18:24 +02:00
aleksei.plate@jetbrains.com
3723488617 TeamCity change in 'Ideavim' project: runners of 'Qodana checks' build configuration were updated 2024-02-20 10:16:06 +00:00
Alex Plate
0cc17a0791
Make a correct service level for VimProjectService 2024-02-20 12:12:50 +02:00
aleksei.plate@jetbrains.com
05a21e6091 TeamCity change in 'Ideavim' project: 'Qodana checks' build configuration settings were updated 2024-02-20 08:58:47 +00:00
Alex Plate
fc06bc7c6f
Update the qodana baseline 2024-02-20 10:58:08 +02:00
Alex Plate
1bd005adc1
Fix the name of the compatibility function 2024-02-20 10:39:34 +02:00
Alex Plate
4f208d1577
Add new plugin to the list 2024-02-20 10:37:10 +02:00
aleksei.plate@jetbrains.com
eb6e0557a7 TeamCity change in 'Ideavim' project: runners of 'Qodana checks' build configuration were updated 2024-02-20 08:23:51 +00:00
Alex Plate
cf09d66be6
Prototype for vimscript inspection 2024-02-20 06:13:26 +02:00
Alex Plate
76cd127a8a
Bring back function to fix compatibility 2024-02-20 05:25:22 +02:00
Alex Plate
f6dd2a9968
Do not call for setCaretVisible in tests as this causes project leak 2024-02-20 05:20:05 +02:00
aleksei.plate@jetbrains.com
ae05a33e14 TeamCity change in 'Ideavim' project: general settings of 'Tests for IntelliJ Latest EAP' build configuration were updated 2024-02-19 07:13:35 +00:00
aleksei.plate@jetbrains.com
b38fad323b TeamCity change in 'Ideavim' project: general settings of 'Tests for IntelliJ Latest EAP' build configuration were updated 2024-02-19 07:12:50 +00:00
Alex Plate
c6027fcf0f
Add new plugin for compatibility checks 2024-02-19 09:01:10 +02:00
IdeaVim Bot
f4cf06a50e Update changelog. Action id - 7940923443 2024-02-17 10:05:47 +00:00
Alex Plate
86bf8dcc60
Fix the compatibility with platform 2024-02-17 08:56:17 +02:00
Alex Plate
d37898b6d3
Fix(VIM-3234): The space character won't mix in the tab chars after >> and << commands
Because of some reason, the visual position function from the platform starts to return an incorrect column for offsets with tabs. Maybe this is a correct behaviour for the platform, but for IdeaVim it breaks the calculation of the current caret position.

The visual position for calculating the shift was used since 2003, but there is no specific reason to use it and not the buffer (logical) position. So, since it started to cause issues, it's replaced with the buffer position.
2024-02-17 08:54:42 +02:00
Alex Plate
1edd6a9002
Fix the compatibility with the new version of the platform 2024-02-17 08:15:11 +02:00
Alex Plate
f7fa0dcbd1
Update YouTrack query for updating the release status after the release 2024-02-17 08:15:11 +02:00
Alex Plate
4f0a95a803
Bring back setCompletionPhase as this incompatibility was fixed in the latest EAP of the IntelliJ platform 2024-02-17 08:15:11 +02:00
IdeaVim Bot
e443cb0d3c Update changelog. Action id - 7928973613 2024-02-16 10:06:24 +00:00
Alex Plate
6fa228ee08
Fix(VIM-3291): Remove sync of editor selection between different opened editors
This is an old feature implemented by Rick Maddy in 2004, taken from Vim.
 c294063223

 If several buffers for the same file are opened, the selection is synchronized between buffers.
This doesn't happen in IJ natively and I don't see a reason to keep it like that.
This behaviour is removed because it causes issues now, but if we'll figure out the usage, we can bring it back.
2024-02-15 20:34:57 +02:00
Alex Plate
fb9bfbaeeb
Do not check the compatibility of the sneak plugin 2024-02-14 18:05:56 +02:00
Alex Plate
09668f4fcb
Update gradle wrapper to version 8.6 2024-02-14 17:07:20 +02:00
Alex Plate
4c7a6165ed
Fix incorrect logs location in UI tests 2024-02-13 19:26:58 +02:00
Alex Plate
12d0d2613f
Allow sneak plugin to be registered with the original mappings from the sneak plugin 2024-02-13 19:20:41 +02:00
Alex Plate
42ee78cd3d
Disable runIde task for test subprojects 2024-02-13 19:18:18 +02:00
Alex Plate
58d308c1ed
Fix the logging reporting for UI tests 2024-02-13 18:02:55 +02:00
Alex Plate
29e1bcc53d
Wait longer for the python console to intialize 2024-02-13 14:56:14 +02:00
Alex Plate
3531574e5e
Remove intellij plugin dependency for UI tests 2024-02-13 12:59:13 +02:00
Alex Plate
b9721218ab
Enable PyCharm for python UI tests 2024-02-13 12:42:33 +02:00
Alex Plate
a119ea6a29
Fix octopus UI test 2024-02-13 12:34:56 +02:00
Alex Plate
95ef5f5f32
Fix incorrect configuration for UI tests 2024-02-13 08:43:12 +02:00
Alex Plate
b81b18645b
Disable publishPlugin task for tests 2024-02-13 08:34:51 +02:00
Alex Plate
ce591f1b43
Configure UI robot for the root project 2024-02-12 14:49:38 +02:00
Alex Plate
28afa4b3ce
Change the gradle config for UI tests 2024-02-12 13:37:10 +02:00
IdeaVim Bot
89a24d71a6 Update changelog after merging PR 2024-02-10 18:07:26 +00:00
f69630b668 VIM-3238 Fix recording a macro that replays another macro 2024-02-10 20:05:47 +02:00
Alex Plate
a2d34a883b
Fix verifyPlugin task 2024-02-09 16:45:57 +02:00
Alex Plate
5c79b887d8
Do not ask for license for py tests 2024-02-09 16:31:25 +02:00
Alex Plate
d0475bf659
Explicitly specify jupyter version 2024-02-09 16:27:22 +02:00
Alex Plate
85c9576699
Extract common UI test fixtures 2024-02-09 16:22:22 +02:00
Alex Plate
2483450a1f
Rename ui tests for better consistency 2024-02-09 16:11:14 +02:00
Alex Plate
519d5eed06
Set up PyCharm UI test 2024-02-09 16:03:31 +02:00
Alex Plate
d87965775a
Update version of robot 2024-02-08 16:48:03 +02:00
Alex Plate
8c6f81aa00
Fix incorrect location of the video 2024-02-08 16:46:03 +02:00
Alex Plate
6ea0ab0968
Print if neovim testing is enabled or not 2024-02-08 15:22:54 +02:00
Alex Plate
70ab3ecdbe
Make a custom expand function for UI tests 2024-02-08 15:10:47 +02:00
Alex Plate
a24ae616df
Fix the creation of the video for the UI tests 2024-02-08 15:10:36 +02:00
Alex Plate
cc838f614f
Remove the testWithNeovim task
Now you can just run `gradlew test -Dnvim`
2024-02-08 14:53:43 +02:00
Alex Plate
ae62a9f378
Fix the incorrect test 2024-02-08 14:35:52 +02:00
Alex Plate
1b5778a58c
Remove configurations for UI tests 2024-02-08 14:33:02 +02:00
Alex Plate
27a689e7b8
Extract UI tests into the separate module 2024-02-08 14:21:09 +02:00
Alex Plate
8e6c490c62
Reformat the config file 2024-02-08 09:50:13 +02:00
Alex Plate
ccda70fe53
Remove mentions of ktlint 2024-02-08 09:49:17 +02:00
Alex Plate
26c42e4f0d
Turn off fixes for the gradle release with test search issues 2024-02-08 09:48:38 +02:00
Alex Plate
3244dd52eb
The line with compilation error is disabled
In 2024.1 EAP this line causes compilation error due to platform conversion from java to kotlin
The fix is landed in the platform and it should work fine with the new EAP.
However, since our tests are fail now, I'll comment out this line and bring it back in one week.
2024-02-08 09:39:54 +02:00
Alex Plate
4c6807a0c2
Extract long running tests into a separate module 2024-02-08 09:28:26 +02:00
Alex Plate
03a6a2749a
Clean up build.gradle.kts 2024-02-08 09:16:16 +02:00
Alex Plate
82f69456e9
Exclude propery tests into a separate project 2024-02-08 09:14:52 +02:00
filipp.vakhitov
6beda371fb TeamCity change in 'Ideavim / IdeaVim releases' project: VCS roots of 'Publish vim-engine' build configuration were updated 2024-02-07 19:42:16 +00:00
Alex Plate
5b9cb2efc5
Explicitly specify java version for java IDE tests 2024-02-07 16:18:54 +02:00
Alex Plate
733968723c
Explicitly specify an IDE type in the configuration 2024-02-07 16:07:35 +02:00
Alex Plate
63c81d67f2
Extract java tests for IdeaVim to a separate gradle subproject 2024-02-07 16:04:10 +02:00
Alex Plate
ad8ba1dd24
Move @VimBehaviourDiffers into a correct package of testFixtures 2024-02-07 10:07:16 +02:00
Alex Plate
04f821e3e1
Create a testFixtures for the project
This will be needed for extracting the java tests into a separate subproject
Also, cleaned up the ordering of dependencies in build.gradle.kts
2024-02-07 10:05:56 +02:00
Alex Plate
4937985e2c
Bump kotlin version from 1.8.21 to 1.9.22
One of the reasons for that is that 1.9.22 allows internal classes to be available in testFixtures from java KT-34901.
2024-02-07 09:29:15 +02:00
Alex Plate
5fd7d83a70
Apply patches to TeamCity configurations 2024-02-07 08:22:42 +02:00
aleksei.plate@jetbrains.com
699a19d202 TeamCity change in 'Ideavim / IdeaVim releases' project: parameters of 'Publish EAP Build' build configuration were updated 2024-02-07 06:12:25 +00:00
Alex Plate
0b42938197
New testing of the eap release job 2024-02-07 08:05:02 +02:00
Alex Plate
1e2bfb6216
Trying to figure out how to get the youtrack token 2024-02-06 19:25:19 +02:00
Alex Plate
f755a4b23f
Trying to figure out how to get the youtrack token 2024-02-06 19:20:38 +02:00
Alex Plate
4f58e12fca
Trying to figure out how to get the youtrack token 2024-02-06 19:15:14 +02:00
Alex Plate
588cf89531
Rename the youtrackToken for the EAP release 2024-02-06 19:02:58 +02:00
Alex Plate
4fe2dd2706
Add a clearer error message about the missing youtrack token 2024-02-06 18:31:48 +02:00
Alex Plate
11ad605cd6
Add YouTrack token to the EAP release job 2024-02-06 18:28:57 +02:00
Alex Plate
fa9f160bd1
Fix incorrect names in EAP release jobs 2024-02-06 18:21:05 +02:00
Alex Plate
dae1fad54e
Add commenting on YouTrack tickets as a part of EAP release process 2024-02-06 18:08:16 +02:00
IdeaVim Bot
52200188d4 Add Emanuel Gestosa to contributors list 2024-02-06 09:02:35 +00:00
Alex Plate
0d74b9ef0b
Fix tag pushing in the release branch 2024-02-06 10:41:14 +02:00
Alex Plate
549163d274
Comment out everything for pycharm tests because it fails on GitHub 2024-02-06 10:32:58 +02:00
Alex Plate
755018c783
Update release jobs 2024-02-06 10:09:53 +02:00
Filipp Vakhitov
2a1c4b3a1c Better widget order 2024-02-06 00:32:25 +02:00
Alex Plate
aae0d825e7
Move the ideavim-sneak plugin into IdeaVim
The author of the original plugin announced the deprecation of the plugin.
However, we've got an approval to move the sources into IdeaVim and continue the development.

Original repo: https://github.com/Mishkun/ideavim-sneak
Approval: https://twitter.com/ideavim/status/1754512214344478939
2024-02-05 19:28:36 +02:00
Alex Plate
855dbfab16
Fix issues with enter in python console
VIM-3287
2024-02-05 18:31:43 +02:00
IdeaVim Bot
f3a357c559 Update changelog after merging PR 2024-02-05 14:31:53 +00:00
Filipp Vakhitov
63995e8c61 Support e flag for search 2024-02-05 16:29:49 +02:00
Filipp Vakhitov
7062d9b8f8 Enable new regex engine by default 2024-02-05 16:29:49 +02:00
Filipp Vakhitov
ede62f5c75 Fix compilation 2024-02-05 16:29:49 +02:00
Filipp Vakhitov
6386770ff3 Move more tests to src 2024-02-05 16:29:49 +02:00
Filipp Vakhitov
b4e831a81f Fix VisualAreaMatcher & TextRange 2024-02-05 16:29:49 +02:00
Filipp Vakhitov
9b283360ce Minor improvements 2024-02-05 16:29:49 +02:00
Filipp Vakhitov
fabbd4d156 Better SelectionInfo implementation 2024-02-05 16:29:49 +02:00
filipp
9bea5bf5f7 Remove deprecated code 2024-02-05 16:29:49 +02:00
filipp
9fbc990493 Fix visual matching 2024-02-05 16:29:49 +02:00
filipp
b05fdaaa73 Fix tests 2024-02-05 16:29:49 +02:00
filipp
52d5d4d64c Fix Keyword token 2024-02-05 16:29:49 +02:00
filipp
6ec712466c Fix StartOfWordMatcher & EndOfWordMatcher 2024-02-05 16:29:49 +02:00
filipp
6616b8dc07 Simplify MarkMatchers 2024-02-05 16:29:49 +02:00
filipp
807457c718 Hide method and add Deprecated annotation 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
13d2a40903 removing print 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
022b196d6a adding comments and small refactors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7a64216830 getting rid of usages of deprecated classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
bf7d2bd465 marking classes as deprecated 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6e97b591de fixing some error messages 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
fc7c470966 fixing nohlsearch command 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
51492ca121 moving seach methods back to VimSearchGroup base 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ce1df84330 creating new IjVimSearchGroup class 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9b43e2a715 working on kotlin implementation of SearchGroup class 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
732cabd6aa working on processSearchCommand 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7c14801d5c deprecating most of SearchHelper 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
66df09c065 use injector for IjVimSearchHelper calls 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
8fd6985316 deprecating SearchHelper find and findAll 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
feac001499 substitute command working with new engine 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4c47e3a8eb integrating new regex into global command 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7773b625a5 \c token can't get overrided by \C 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
abe1abec72 test for \c token always taking priority 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
023838a96b working on implicit DFA algorithm optimization 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f4e743acc5 VimRegex uses wrapscan option 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
06d58cbda5 integrating options into the main module 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d199dea204 using options in findPrevious 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5722060ed9 testing VimRegex with smartcase set 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d4f7e727c1 VimRegex methods now receive options 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ba9afc3f8e adding usenewregex option to set command tests
rebasing
2024-02-05 16:29:49 +02:00
Emanuel Gestosa
39897bd012 allow findAll to have a max index 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
575d563154 show pattern not found error message 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
2bf46ce2f3 fixing findPrevious not wraping around in some cases 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b49a185efc using the count parameter in find() 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e305ebd1ed fixing patterns with just a AND operator 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6f5c9826f4 fixing patterns with single ^ or $ 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6025eaaca9 showing pattern not found error 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b2441c3cca throwing and catching VimRegexException 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a73599e9ee use non-exact nfas for slightly faster matches 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
58398f40fa using useNewRegex option 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
43f5d5a8e8 integrating findAll 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b20cbd3558 fix findNext getting stuck at line ends 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7f835a407c fix findPrevious not finding matches that start at end-of-line 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9859974db7 integrating findNext and findPrevious 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6c24ddd1a0 adding useNewEngine option 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
bd92ef08ec use explicit stack instead of recursion for backtracking 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
8de6107a17 getting rid of handleTransition method 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e639f03ac7 stop using non-exact start nfas 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f9aac442c1 findAll returns List instead of Sequence 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5fdf675168 rename NFATest to VimRegexEngineTest 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
232f81ff48 commenting new classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
1c4a6b2274 refactoring nfa simulation logic to its own class 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
deb71f8efc cleaning comments 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4596596d9f new findPrevious API method 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
bbb6d42f8d changing find to findNext 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
01efd0f9f0 trying to get antlr to report vim errors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
2d7597d206 clearing some TODOS in VimRegex 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
221741c891 assuring that cursor line and column tokens belong to the same cursor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9f69beb450 test for pattern with multiple cursors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e843d9e9c3 assuring that visual selection tokens belong to the same cursor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
008b3d94fb assuring that all cursor and mark tokens belong to the same cursor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6756d83c55 test for tokens belonging to the same cursor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b52072a2e3 visitors for mark related tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
3afb00d563 tests for mark related tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a30c94fd2f mock mark related methods for regex testing 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f50c29a285 matchers for mark related tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f238b0f138 parsing more mark registers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d0a8c98040 parsing mark related tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b3d161ad97 fix tests not detecting visual area selection properly 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
fce9cf2077 visitor for visual area token 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
efd0e56697 visual area matcher 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b94a9bb9d9 nfa tests for matching inside visual area 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c153cc5a29 mock visual selection 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a680e9a25a visual columns matching 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
3c18c4ef22 fixing parsing of optionally matched sequence 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c4e11b5976 visitor for optionally matched sequence 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
65be51dd48 tests for optionally matched sequence 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9684103f97 parsing optionally matched atoms tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f4c647d430 new doTest method for VimRegexTest 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f1eab3b9c1 dividing regex api tests into seperate classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
545d52bd93 dividing regex tests into internal and public api tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4e42198c09 using multi line strings in VimRegexTest 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
44736a51b9 new NFA doTest method 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e675ffd623 cleaning multiline strings in tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
1f14e06bd3 refactoring editor mock methods 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9871078269 tests receive caret indexes in the text 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5e7a7f4d62 fixing cursor line and column matchers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7d690c6809 visitors for cursor line and column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6edb4266d5 nfa tests for cursor line and column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
799e82d501 matchers for cursor line anc column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a2370bff68 parsing cursor column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c72f3bcd12 parsing cursor line tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
295964a74d mocking VimEditor.offsetToBuffer position and fixing visitors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d77cda0fae visitors for line and column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6da072d47d matchers for line and column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
471a5a1b3e tests for line and column tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cd5da2d237 parsing column related tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
62f67cd626 parsing line related tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
70db96d9e5 allow larger decimal codes inside collections 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
98470111fb fixing octal codes larger than 0o377 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
557a3bb01f fixing mixing % in match character by code tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
dee70acdcb tests for match character by code 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
862b16879c visitors for character codes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ed7249558e parsing match character by code 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4f6c6f4d10 fixing rebase problems 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
650d02d9b3 using TextRange instead of IntRange 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e4041a2f69 adding comments 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4c284a6d13 visitor for negative limited lookbehind 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e14fc801bd fix lookbehinds matches not ending where they were supposed to 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
0478d468e0 adding tests for limited lookbehinds 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4ac98710fb implement limit lookbehinds 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f256f6417e parsing limited lookbehinds 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ca94d55b62 implementing negative lookbehind 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c11c061113 add tests for negative lookbehind 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c15c3eb802 implementing positive lookbehind 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
0ce102b782 visitor for positive lookbehind 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cc48207a99 adding tests for positive lookbehind 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
353ea5fc5d reworking nfa to ignore input until first match 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
64138310cc add more complex \& tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
1c4538af72 implementing \& operator 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
755b47ef19 adding nfa tests for \& operator 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c78a5d3cab allow for a state to have multiple assertitions 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b9b8d30f3b fixing collections with only char class expressions 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9be93212c3 fname class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
89973809af keyword class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e324af356d ident class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f51fc6ed47 return, tab, escape, backspace class visitors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ecce98289a xdigit class 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
23c14aa2e4 upper class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
678d04c5db space class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
691ba75372 punct class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d2d7bbc632 print class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b3b1a6bdb9 lower class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
310125ea01 graph class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
208d1cbba2 digit class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e94154ba80 cntrl class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
582fbdd9e7 blank class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
dd175912f4 alnum class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a6a0ae7a51 alpha class visitor 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
8cdac91a01 base code for char classes expressions 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4c89f41daa adding nfa tests for collection char classes expressions 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
512e826a42 adding new parser tests for collection char classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
bc0d277a21 parsing collection char class expressions 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
169fe5fc5b parse visual \%V token 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
30867702a4 parsing lookbehind tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6131f92ae6 parsing ~ token 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
823a52583c documenting regex code 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e2c6c0539f add more lookahead tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f7f1c0e90d making nested lookahead tests pass 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
eca12607dd pattern visitor is now a singleton 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
006e3e11f9 parser class is now a singleton 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a9982cbdca refactoring temporary field out of parser class 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
0fa9c5a2a2 moving all parsing logic to VimRegexParser class
rebasing
2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cdcc9729d3 add more failing lookahead tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4acf651aa7 adding tests for nested lookahead tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4bba791c65 adding comments and small cleanups 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
662688d3b9 refactoring inappropriate intimacy between nfa and nfaassertion classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
21a3e8fdc4 extract methods refactor in nfa simulation code 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
3815a1d538 add more lookahead tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cbe0c5cfec implementing negative lookahead 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
15db9b30e1 add tests for negative lookahead 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
e891294c0f parsing negative lookahead 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f6b9e7cc26 implementing positive lookahead 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
052fd7162f parsing positive lookahead 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
189acb73f5 dealing with atomic groups in a different way 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ec7c1677b4 allow special escape characters in collections \e \t \r \b \n 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a9474c8e67 allow character codes inside collections \d \o \x \u \U 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
3a70dfc5f3 implementing collections with EOL \_[] 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
669177d803 implementing and testing start and end of word tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b1f43b061c parsing start and end of word tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7ff3c84deb commenting new atomic groups code 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ee642b63ce adding explanatory comment on wierd atomic group test 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
17315e5096 implementing atomic groups 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4e9d52fc62 placeholder for atomic groups 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d7e87f8fc8 rename MultiBoundary to RangeBoundary 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
3efe11f393 refactoring visitors to prepare for different types of multis 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
26c6c464d8 adding tests for atomic groups 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4db654e653 parsing atomic groups 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
048759d374 implement and test start and end of line anywhere in pattern 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
db2424057f parsing start and end line anywhere in pattern 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
472a53e3b9 start and end of line anchors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9e15d91900 adding tests for empty editors 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d5cff281c0 adding comments on new Matchers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
57b6c4dffb collection matcher uses set instead of list 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
908a2d1d8c start and end of file 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
69bdea9273 character classes never ignore case 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5b21a653ee add test for case insensitive matching 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cfddcf1630 ignore case tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f009687ddf matchesAt API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6ddfe29465 matches API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
715c51f673 matchEntire API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b443e8f06a fix quantified capture groups not updating properly 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
0bd0466c9c cleaning adding transitions to states 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ad5db3c9e5 fix capturing groups not updating properly 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
fa3182cb5e adding failing backreferences test 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
3f44bed66e adding comments to Matchers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
2a70530d0f matchAt API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7c542d5fc7 implementing character classes with EOL 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
638dfb7777 parsing character classes with new line 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
1323536a63 testing and implementing backreferences 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
419212e2d4 parsing backreferences 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5f1c234a7d refactor Matcher to return number of consumed characters 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
db1e8301cd implementing and testing lazy quantifiers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
bf94a3c68d parsing lazy quantifiers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
96baa4ffc6 all named character classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
7d472afe61 set match start and end \zs \ze 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f32a4d33a7 support unicode escape sequence in collections 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
0722991955 add test for collection with not special escaped character 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
bcc740cdbc implementing custom collections 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5cf46097f7 ascii character classes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
61dc189f8b char classes and collections base code 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
23c2b008c9 implementing cursor and using mockito to mock editor
rebasing
2024-02-05 16:29:49 +02:00
Emanuel Gestosa
db14afdf3a dot with and without newline 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b7927336d1 implementing dot 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ee23a3d4cd commenting findAll 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
63c0112ffb findAll API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
db08d7d280 find API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
9892525fbc containsMatchIn API function 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
34b87ff6bf adding comments 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
241ad68bd5 fix nfa looping in epsilon transitions 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
a0ec18921b more correct way of handling quantifiers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
45e17eb0b2 fixing quantifiers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
59f0e9ae67 add test for updating capture group 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
af24611c73 capture group submatch 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d4502dda3f VimMatchResult stores matched string value 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c0efa8af5d use IntRange for match range 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
1c06a3fc89 add test for empty group 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
c19fb38d1c implementing grouping 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
5dc1de9daf add nfa test for escaped character 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6774301938 updating comments 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4ef6cf0428 implementing quantifiers 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
ca5f8e4b44 skeleton for NFA testing 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
1907f03abe nfa simulation uses VimEditor instead of String 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
6351a4e4f3 initial nfa 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
fa34c3937f initial nfa definition 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cdac97ebf5 adding some zero-width tokens 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
fe958d28b8 lexer fixing what chars are taken literally 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f71982e1d5 support unicode in collections 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
cb2bfcea53 unicode chars in all lexer modes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
4a9d5bbceb lexer support for unicode characters 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
10809eade6 regex very magic and very nomagic modes 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
43d63527f8 adding comments to parser grammar 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
df51eb54ed using antlr token types 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
b47109ab4d grammar add EOF at end of pattern 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
15b2b68940 making new collection tests pass 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
62a239f6fe add tests for collections 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
d89bc95a0a altering antlr error handling 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
2a76f21b31 regex range basic tests 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
f07e22d742 delete duplicate files 2024-02-05 16:29:49 +02:00
Emanuel Gestosa
058ab7a1ea gradle generate antlr files
rebasing
2024-02-05 16:29:49 +02:00
Emanuel Gestosa
fae3baa640 initial regex grammar 2024-02-05 16:29:49 +02:00
IdeaVim Bot
2c4da9c634 Update changelog. Action id - 7766144293 2024-02-03 10:06:57 +00:00
Alex Plate
8de0313aca
Preparing the UI tests for the PyCharm 2024-02-02 20:16:47 +02:00
Alex Plate
143c5b17f9
Fix(VIM-3055): Fix the issue with double deleting after dot 2024-02-02 19:30:58 +02:00
Alex Plate
ec32fde60d
Release job: Update version of kotlin dsl 2024-02-02 09:16:49 +02:00
Alex Plate
f2ac5d4995
Release job: Do not make slack notification for the patch version 2024-02-02 09:07:17 +02:00
Alex Plate
716962af03
Release job: Use the command line runner to start the tests
TC detects tests and report them in the release job. I just want to have a "Success" word when everything done.
You cannot disable the tests detection on TC, but the detection doesn't work if the tests were started from the command line
2024-02-02 08:57:28 +02:00
Alex Plate
156efde6b9
Release job: Do not make a commit if there are no changes 2024-02-02 08:50:47 +02:00
IdeaVim Bot
a9b7716dfe Preparation to 2.8.2 release 2024-02-01 09:47:48 +00:00
Alex Plate
76a67a6715
Do not add an update entry if missing 2024-02-01 11:43:16 +02:00
Filipp Vakhitov
c3defdcda4 Add sources and JavaDoc to vim-engine jar 2024-02-01 11:07:28 +02:00
filipp.vakhitov
d8092aa916 TeamCity change in 'Ideavim / IdeaVim releases' project: parameters of 'Publish vim-engine' build configuration were updated 2024-02-01 08:15:00 +00:00
filipp.vakhitov
8b5a3d31aa TeamCity change in 'Ideavim / IdeaVim releases' project: parameters of 'Publish vim-engine' build configuration were updated 2024-02-01 08:14:45 +00:00
Filipp Vakhitov
11761b66b2 Revert "Fix(VIM-3183): Execute .ideavimrc on pooled thread"
This reverts commit 1d7796805c.
2024-01-31 20:10:32 +02:00
Filipp Vakhitov
f83f107bd1 Revert "Support triggering option listeners outside EDT"
This reverts commit 5a6f54c96c.
2024-01-31 20:10:18 +02:00
Filipp Vakhitov
f1b90857ff Revert "Fix code in execute requiring EDT or write actions"
This reverts commit e7236beedd.
2024-01-31 20:10:15 +02:00
Filipp Vakhitov
5aea4cdd65 Revert "Avoiding slow operations on EDT"
This reverts commit 00fb5bc6cf.
2024-01-31 20:09:55 +02:00
Filipp Vakhitov
1822a59c70 Revert "Fix(VIM-3273): Config file stopped working"
This reverts commit 04230fdd9c.
2024-01-31 20:09:31 +02:00
Filipp Vakhitov
37c6934802 Revert "Update changelog. Action id - 7724318708"
This reverts commit 434511658b.
2024-01-31 20:09:27 +02:00
Filipp Vakhitov
90c7f747a4 Revert "Fix(VIM-3278) Relative number is broken in 2.8.0"
This reverts commit a1646a7a88.
2024-01-31 20:09:23 +02:00
Filipp Vakhitov
b7efa3dcd6 Revert "Add EDT where it is needed"
This reverts commit 3e9706e6ce.
2024-01-31 20:09:19 +02:00
Filipp Vakhitov
da80f537ac Revert "Avoid concurrent VimScript execution"
This reverts commit 0e03151505.
2024-01-31 20:09:15 +02:00
Filipp Vakhitov
0119912318 Revert "Add readActions & EDT"
This reverts commit 45a2eadc58.
2024-01-31 20:09:08 +02:00
Alex Plate
5e0b1d0161
Update the way the changelog is updated for the patch release 2024-01-31 19:24:27 +02:00
Alex Plate
35145d100b
Update the release jobs to make releases only from the release branch
In this way, we'll avoid the problem that some breaking commits may be added between the last EAP and master branch
2024-01-31 18:50:26 +02:00
Alex Plate
584dd0ba89
Run tests before releasing IdeaVim 2024-01-31 18:21:03 +02:00
Alex Plate
e5f5dc56c9
Remove the obsolete gradle scripts 2024-01-31 18:21:03 +02:00
Alex Plate
880efb012a
Fix compilation issues for the latest EAP 2024-01-31 17:58:19 +02:00
Alex Plate
b95308ac24
Migrate CopilotKeymapCorrector to the new API 2024-01-31 17:42:08 +02:00
aleksei.plate@jetbrains.com
3b192ad357 TeamCity change in 'Ideavim / IdeaVim releases' project: parameters of 'Publish vim-engine' build configuration were updated 2024-01-31 15:01:37 +00:00
aleksei.plate@jetbrains.com
b04938ac5e TeamCity change in 'Ideavim / IdeaVim releases' project: parameters of 'Publish vim-engine' build configuration were updated 2024-01-31 13:37:25 +00:00
Alex Plate
56410ac1f2
Make some classes public because they're used in EasyMotion plugin tests 2024-01-31 15:25:33 +02:00
Filipp Vakhitov
45a2eadc58 Add readActions & EDT 2024-01-31 14:43:23 +02:00
Filipp Vakhitov
0e03151505 Avoid concurrent VimScript execution 2024-01-31 14:42:58 +02:00
Filipp Vakhitov
3e9706e6ce Add EDT where it is needed 2024-01-31 14:09:06 +02:00
Filipp Vakhitov
a1646a7a88 Fix(VIM-3278) Relative number is broken in 2.8.0
Proper options initialization order
2024-01-31 13:34:33 +02:00
IdeaVim Bot
434511658b Update changelog. Action id - 7724318708 2024-01-31 10:06:47 +00:00
Filipp Vakhitov
04230fdd9c Fix(VIM-3273): Config file stopped working 2024-01-31 09:12:23 +02:00
Filipp Vakhitov
2e16ad8a2a Revert "Downgrade ktor dependencies to check if "Publish vim-engine" fails because of them"
This reverts commit 7fb59b0fa9.
2024-01-30 20:13:36 +02:00
Filipp Vakhitov
7fb59b0fa9 Downgrade ktor dependencies to check if "Publish vim-engine" fails because of them 2024-01-30 20:05:45 +02:00
Filipp Vakhitov
24e044bcda Remove deprecated ComplicatedKeysAction.kt 2024-01-30 18:56:38 +02:00
IdeaVim Bot
1093656ec5 Preparation to 2.8.0 release 2024-01-30 10:46:04 +00:00
Alex Plate
674e997060
Fix the condition for the release branch reset step 2024-01-30 12:33:28 +02:00
Alex Plate
37fd124f56
Use bash to reset the branch 2024-01-30 11:34:25 +02:00
Alex Plate
7df2e67312
Re-request commit message 2024-01-30 10:00:08 +02:00
Alex Plate
8ea1f0796c
Add logs to release job 2024-01-30 09:53:51 +02:00
Filipp Vakhitov
00fb5bc6cf Avoiding slow operations on EDT 2024-01-29 13:30:41 +02:00
Filipp Vakhitov
5e01f726d3 Revert "Remove deprecated VimScriptGlobalEnvironment.java"
This reverts commit 5c64ebf1cc.
2024-01-29 12:58:46 +02:00
Filipp Vakhitov
e87290aeea Simplify storing global variables 2024-01-29 12:58:31 +02:00
Filipp Vakhitov
e7236beedd Fix code in execute requiring EDT or write actions 2024-01-29 10:00:55 +02:00
Filipp Vakhitov
5a6f54c96c Support triggering option listeners outside EDT 2024-01-28 14:00:36 +02:00
filipp
7769985439 Merge remote-tracking branch 'origin/master' 2024-01-28 13:37:58 +02:00
IdeaVim Bot
f4afdb21b2 Update changelog. Action id - 7677896605 2024-01-27 10:06:55 +00:00
Alex Plate
cc1b9e0a50
Expand all works a way worse 2024-01-27 07:43:28 +02:00
Alex Plate
2c58740cbb
Expand the full tree in UI tests 2024-01-26 21:49:49 +02:00
Alex Plate
808533b110
Fix(VIM-3260): Processing the offsets at the file end 2024-01-26 17:49:44 +02:00
Alex Plate
e04a15bb99
Add new plugin to check plugin dependencies 2024-01-26 17:23:55 +02:00
Alex Plate
26d4074a61
[VIM-2974] Adopt other parts of key handling to the octopus handler switcher 2024-01-26 16:40:34 +02:00
filipp
0137de5ca2 Add Term widget theme 2024-01-26 15:52:07 +02:00
Alex Plate
b0a1b2edba
Do not make cd in tests 2024-01-26 15:47:13 +02:00
Alex Plate
355c560ddc
Add UI test with disabled octopus handler 2024-01-26 15:32:30 +02:00
Alex Plate
72f286d9c6
Add UI test for multicaret enter in select mode 2024-01-26 15:32:30 +02:00
Alex Plate
db6786414a
[VIM-2974] WIP: Bringing back the octopus handler switch 2024-01-26 15:32:29 +02:00
filipp
f8f046f193 Fix plugin.xml 2024-01-26 14:00:07 +02:00
filipp
6c9ad4ded2 Remove deprecated xml-related code 2024-01-26 13:45:46 +02:00
filipp
32cae8ca11 Remove more deprecated things 2024-01-26 13:05:48 +02:00
filipp
0cb65279d9 Remove deprecated mark-related methods 2024-01-26 13:02:23 +02:00
filipp
412da06554 Remove deprecated ToggleOption.kt 2024-01-26 12:47:06 +02:00
filipp
247f8a2778 Remove deprecated OptionsManager.kt 2024-01-26 12:45:46 +02:00
filipp
017c9a6a70 Remove deprecated OptionService.kt 2024-01-26 12:43:06 +02:00
filipp
eccb2430b5 Remove deprecated MarkGroup.java 2024-01-26 12:39:30 +02:00
filipp
5c64ebf1cc Remove deprecated VimScriptGlobalEnvironment.java 2024-01-26 12:38:28 +02:00
filipp
1d7796805c Fix(VIM-3183): Execute .ideavimrc on pooled thread 2024-01-26 12:27:03 +02:00
dependabot[bot]
3479aaf6f6 Bump org.jetbrains.intellij from 1.16.1 to 1.17.0
Bumps org.jetbrains.intellij from 1.16.1 to 1.17.0.

---
updated-dependencies:
- dependency-name: org.jetbrains.intellij
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-24 20:02:11 +04:00
dependabot[bot]
d2071cf05c Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 1.9.22-1.0.16 to 1.9.22-1.0.17.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/1.9.22-1.0.16...1.9.22-1.0.17)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp:symbol-processing-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-24 20:02:01 +04:00
417 changed files with 105244 additions and 40055 deletions

88
.github/workflows/runUiOctopusTests.yml vendored Normal file
View File

@ -0,0 +1,88 @@
name: Run Non Octopus UI Tests
on:
workflow_dispatch:
schedule:
- cron: '0 12 * * *'
jobs:
build-for-ui-test-mac-os:
if: github.repository == 'JetBrains/ideavim'
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Apply Patch
run: |
git apply tests/ui-ij-tests/src/test/kotlin/ui/octopus.patch
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 17
- name: Setup FFmpeg
uses: FedericoCarboni/setup-ffmpeg@v3
with:
# Not strictly necessary, but it may prevent rate limit
# errors especially on GitHub-hosted macos machines.
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin
run: gradle :buildPlugin
- name: Run Idea
run: |
mkdir -p build/reports
gradle runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started
uses: jtalk/url-health-check-action@v3
with:
url: http://127.0.0.1:8082
max-attempts: 20
retry-delay: 10s
- name: Tests
run: gradle :tests:ui-ij-tests:testUi
- name: Move video
if: always()
run: mv tests/ui-ij-tests/video build/reports
- name: Move sandbox logs
if: always()
run: mv build/idea-sandbox/system/log sandbox-idea-log
- name: Save report
if: always()
uses: actions/upload-artifact@v4
with:
name: ui-test-fails-report-mac
path: |
build/reports
tests/ui-ij-tests/build/reports
sandbox-idea-log
# build-for-ui-test-linux:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Setup Java
# uses: actions/setup-java@v2.1.0
# with:
# distribution: zulu
# java-version: 11
# - name: Build Plugin
# run: gradle :buildPlugin
# - name: Run Idea
# run: |
# export DISPLAY=:99.0
# Xvfb -ac :99 -screen 0 1920x1080x16 &
# mkdir -p build/reports
# gradle :runIdeForUiTests #> build/reports/idea.log
# - name: Wait for Idea started
# uses: jtalk/url-health-check-action@1.5
# with:
# url: http://127.0.0.1:8082
# max-attempts: 15
# retry-delay: 30s
# - name: Tests
# run: gradle :testUi
# - name: Save fails report
# if: ${{ failure() }}
# uses: actions/upload-artifact@v2
# with:
# name: ui-test-fails-report-linux
# path: |
# ui-test-example/build/reports

56
.github/workflows/runUiPyTests.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name: Run UI PyCharm Tests
on:
workflow_dispatch:
schedule:
- cron: '0 12 * * *'
jobs:
build-for-ui-test-mac-os:
if: github.repository == 'JetBrains/ideavim'
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 17
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Setup FFmpeg
uses: FedericoCarboni/setup-ffmpeg@v3
with:
# Not strictly necessary, but it may prevent rate limit
# errors especially on GitHub-hosted macos machines.
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin
run: gradle :buildPlugin
- name: Run Idea
run: |
mkdir -p build/reports
gradle :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
- name: Wait for Idea started
uses: jtalk/url-health-check-action@v3
with:
url: http://127.0.0.1:8082
max-attempts: 20
retry-delay: 10s
- name: Tests
run: gradle :tests:ui-py-tests:testUi
- name: Move video
if: always()
run: mv tests/ui-py-tests/video build/reports
- name: Move sandbox logs
if: always()
run: mv build/idea-sandbox/system/log sandbox-idea-log
- name: Save report
if: always()
uses: actions/upload-artifact@v4
with:
name: ui-test-fails-report-mac
path: |
build/reports
tests/ui-py-tests/build/reports
sandbox-idea-log

View File

@ -13,7 +13,7 @@ jobs:
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: zulu distribution: zulu
java-version: 11 java-version: 17
- name: Setup FFmpeg - name: Setup FFmpeg
uses: FedericoCarboni/setup-ffmpeg@v3 uses: FedericoCarboni/setup-ffmpeg@v3
with: with:
@ -27,7 +27,7 @@ jobs:
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle :runIdeForUiTests > build/reports/idea.log & gradle runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
@ -35,10 +35,10 @@ jobs:
max-attempts: 20 max-attempts: 20
retry-delay: 10s retry-delay: 10s
- name: Tests - name: Tests
run: gradle :testUi run: gradle :tests:ui-ij-tests:testUi
- name: Move video - name: Move video
if: always() if: always()
run: mv video build/reports run: mv tests/ui-ij-tests/video build/reports
- name: Move sandbox logs - name: Move sandbox logs
if: always() if: always()
run: mv build/idea-sandbox/system/log sandbox-idea-log run: mv build/idea-sandbox/system/log sandbox-idea-log
@ -49,6 +49,7 @@ jobs:
name: ui-test-fails-report-mac name: ui-test-fails-report-mac
path: | path: |
build/reports build/reports
tests/ui-ij-tests/build/reports
sandbox-idea-log sandbox-idea-log
# build-for-ui-test-linux: # build-for-ui-test-linux:
# runs-on: ubuntu-latest # runs-on: ubuntu-latest

1
.gitignore vendored
View File

@ -23,6 +23,7 @@
# Generated by gradle task "generateGrammarSource" # Generated by gradle task "generateGrammarSource"
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
# Generated JSONs for lazy classloading # Generated JSONs for lazy classloading
/vim-engine/src/main/resources/ksp-generated /vim-engine/src/main/resources/ksp-generated
/src/main/resources/ksp-generated /src/main/resources/ksp-generated

View File

@ -6,6 +6,7 @@
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="CONTINUATION_INDENT_SIZE" value="4" />
</value> </value>
</option> </option>
<option name="LINE_SEPARATOR" value="&#10;" />
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="FIELD_NAME_PREFIX" value="my" /> <option name="FIELD_NAME_PREFIX" value="my" />
<option name="STATIC_FIELD_NAME_PREFIX" value="our" /> <option name="STATIC_FIELD_NAME_PREFIX" value="our" />

View File

@ -15,7 +15,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.Project import jetbrains.buildServer.configs.kotlin.v2019_2.Project
object Project : Project({ object Project : Project({
description = "Vim engine for IDEs based on the IntelliJ platform" description = "Vim engine for JetBrains IDEs"
subProjects(Releases, OldTests, GitHub) subProjects(Releases, OldTests, GitHub)

View File

@ -34,7 +34,6 @@ object Compatibility : IdeaVimBuildType({
java --version java --version
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}org.jetbrains.IdeaVim-EasyMotion' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}org.jetbrains.IdeaVim-EasyMotion' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}io.github.mishkun.ideavimsneak' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city # Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city

View File

@ -25,7 +25,7 @@ object LongRunning : IdeaVimBuildType({
steps { steps {
gradle { gradle {
tasks = "clean testLongRunning" tasks = "clean :tests:long-running-tests:testLongRunning"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
} }

View File

@ -39,7 +39,7 @@ object Nvim : IdeaVimBuildType({
""".trimIndent() """.trimIndent()
} }
gradle { gradle {
tasks = "clean testWithNeovim" tasks = "clean test -Dnvim"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
} }

View File

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

View File

@ -20,8 +20,8 @@ object PublishVimEngine : IdeaVimBuildType({
params { params {
param("env.ORG_GRADLE_PROJECT_engineVersion", "%build.number%") param("env.ORG_GRADLE_PROJECT_engineVersion", "%build.number%")
param("env.ORG_GRADLE_PROJECT_uploadUrl", "https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") param("env.ORG_GRADLE_PROJECT_uploadUrl", "https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
password("env.ORG_GRADLE_PROJECT_spacePassword", "credentialsJSON:790b4e43-ee83-4184-b81b-678afab60409", display = ParameterDisplay.HIDDEN) password("env.ORG_GRADLE_PROJECT_spacePassword", "credentialsJSON:5ea56f8c-efe7-4e1e-83de-0c02bcc39d0b", display = ParameterDisplay.HIDDEN)
param("env.ORG_GRADLE_PROJECT_spaceUsername", "Aleksei.Plate") param("env.ORG_GRADLE_PROJECT_spaceUsername", "a121c67e-39ac-40e6-bf82-649855ec27b6")
} }
vcs { vcs {

View File

@ -46,8 +46,8 @@ object Qodana : IdeaVimBuildType({
version = Qodana.JVMVersion.LATEST version = Qodana.JVMVersion.LATEST
} }
reportAsTests = true reportAsTests = true
additionalDockerArguments = "-e QODANA_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb24iOiIzUFZrQSIsInByb2plY3QiOiIzN1FlQSIsInRva2VuIjoiM0t2bXoifQ.uohp81tM7iAfvvB6k8faarfpV-OjusAaEbWQ8iNrOgs"
additionalQodanaArguments = "--baseline qodana.sarif.json" additionalQodanaArguments = "--baseline qodana.sarif.json"
cloudToken = "credentialsJSON:6b79412e-9198-4862-9223-c5019488f903"
} }
} }
@ -63,7 +63,6 @@ object Qodana : IdeaVimBuildType({
timezone = "SERVER" timezone = "SERVER"
} }
param("dayOfWeek", "Sunday") param("dayOfWeek", "Sunday")
enabled = false
} }
} }

View File

@ -5,6 +5,7 @@ import _Self.Constants.RELEASE_EAP
import _Self.IdeaVimBuildType import _Self.IdeaVimBuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext
import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
@ -30,6 +31,11 @@ object ReleaseEap : IdeaVimBuildType({
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5", "credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
label = "Slack Token" label = "Slack Token"
) )
password(
"env.YOUTRACK_TOKEN",
"credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622",
display = ParameterDisplay.HIDDEN
)
} }
vcs { vcs {
@ -61,13 +67,9 @@ object ReleaseEap : IdeaVimBuildType({
tasks = "scripts:addReleaseTag" tasks = "scripts:addReleaseTag"
} }
gradle { gradle {
name = "Publish plugin"
tasks = "publishPlugin" tasks = "publishPlugin"
} }
// Same as the script below. However, jgit can't find ssh keys on TeamCity
// gradle {
// name = "Push changes to the repo"
// tasks = "scripts:pushChanges"
// }
script { script {
name = "Push changes to the repo" name = "Push changes to the repo"
scriptContent = """ scriptContent = """
@ -81,6 +83,10 @@ object ReleaseEap : IdeaVimBuildType({
git push origin %build.number% git push origin %build.number%
""".trimIndent() """.trimIndent()
} }
gradle {
name = "YouTrack post release actions"
tasks = "scripts:eapReleaseActions"
}
} }
features { features {

View File

@ -45,7 +45,11 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5", "credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
label = "Slack Token" label = "Slack Token"
) )
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN) password(
"env.ORG_GRADLE_PROJECT_youtrackToken",
"credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3",
display = ParameterDisplay.HIDDEN
)
param("env.ORG_GRADLE_PROJECT_releaseType", releaseType) param("env.ORG_GRADLE_PROJECT_releaseType", releaseType)
} }
@ -65,9 +69,25 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
name = "Pull git history" name = "Pull git history"
scriptContent = "git fetch --unshallow" scriptContent = "git fetch --unshallow"
} }
gradle { script {
name = "Select branch" name = "Reset release branch"
tasks = "scripts:selectBranch" scriptContent = """
if [ "major" = "$releaseType" ] || [ "minor" = "$releaseType" ]
then
echo Resetting the release branch because the release type is $releaseType
git checkout master
latest_eap=${'$'}(git describe --tags --match="[0-9].[0-9]*.[0-9]-eap.[0-9]*" --abbrev=0 HEAD)
echo Latest EAP: ${'$'}latest_eap
commit_of_latest_eap=${'$'}(git rev-list -n 1 ${'$'}latest_eap)
echo Commit of latest EAP: ${'$'}commit_of_latest_eap
git checkout release
git reset --hard ${'$'}commit_of_latest_eap
else
git checkout release
echo Do not reset the release branch because the release type is $releaseType
fi
echo Checked out release branch
""".trimIndent()
} }
gradle { gradle {
name = "Calculate new version" name = "Calculate new version"
@ -89,41 +109,47 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
name = "Add release tag" name = "Add release tag"
tasks = "scripts:addReleaseTag" tasks = "scripts:addReleaseTag"
} }
gradle { script {
name = "Reset release branch" name = "Run tests"
tasks = "scripts:resetReleaseBranch" scriptContent = "./gradlew test"
} }
gradle { gradle {
name = "Publish release" name = "Publish release"
tasks = "publishPlugin" tasks = "publishPlugin"
} }
// gradle { script {
// name = "Push changes to the repo" name = "Checkout master branch"
// tasks = "scripts:pushChangesWithReleaseBranch" scriptContent = """
// } echo Checkout master
git checkout master
""".trimIndent()
}
gradle {
name = "Update change log in master"
tasks = "scripts:changelogUpdateUnreleased"
}
gradle {
name = "Commit preparation changes in master"
tasks = "scripts:commitChanges"
}
script { script {
name = "Push changes to the repo" name = "Push changes to the repo"
scriptContent = """ scriptContent = """
branch=$(git branch --show-current) branch=$(git branch --show-current)
echo current branch is ${'$'}branch echo Current branch is ${'$'}branch
if [ "master" != "${'$'}branch" ]; if [ "master" != "${'$'}branch" ];
then then
git checkout master git checkout master
fi fi
git push origin --tags
git push origin git push origin
if [ "patch" != $releaseType ];
then
git checkout release git checkout release
echo checkout release branch echo checkout release branch
git branch --set-upstream-to=origin/release release git branch --set-upstream-to=origin/release release
git push --tags
git push origin --force git push origin --force
fi # Push tag
git push origin %build.number%
git checkout ${'$'}branch
""".trimIndent() """.trimIndent()
} }
gradle { gradle {

View File

@ -1,11 +1,9 @@
package patches.buildTypes package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.changeBuildType import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
/* /*
This patch script was generated by TeamCity on settings change in UI. This patch script was generated by TeamCity on settings change in UI.
@ -13,6 +11,18 @@ To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
accordingly, and delete the patch script. accordingly, and delete the patch script.
*/ */
changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) { changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
check(artifactRules == """
+:build/reports => build/reports
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
""".trimIndent()) {
"Unexpected option value: artifactRules = $artifactRules"
}
artifactRules = """
+:build/reports => build/reports
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
+:tests/java-tests/build/reports => tests/java-tests/build/reports
""".trimIndent()
expectSteps { expectSteps {
gradle { gradle {
tasks = "clean test" tasks = "clean test"

View File

@ -0,0 +1,19 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'PublishVimEngine'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("PublishVimEngine")) {
vcs {
check(branchFilter == "+:<default>") {
"Unexpected option value: branchFilter = $branchFilter"
}
branchFilter = "+:fleet"
}
}

View File

@ -1,20 +0,0 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'ReleaseMinor'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("ReleaseMinor")) {
params {
expect {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
}
update {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3", display = ParameterDisplay.HIDDEN)
}
}
}

View File

@ -1,20 +0,0 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'ReleasePatch'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("ReleasePatch")) {
params {
expect {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
}
update {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3", display = ParameterDisplay.HIDDEN)
}
}
}

View File

@ -1,17 +0,0 @@
package patches.projects
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the root project
accordingly, and delete the patch script.
*/
changeProject(DslContext.projectId) {
check(description == "Vim engine for IDEs based on the IntelliJ platform") {
"Unexpected description: '$description'"
}
description = "Vim engine for JetBrains IDEs"
}

View File

@ -30,5 +30,5 @@ node (Plugins -> teamcity-configs -> teamcity-configs:generate),
the 'Debug' option is available in the context menu for the task. the 'Debug' option is available in the context menu for the task.
*/ */
version = "2023.05" version = "2023.11"
project(_Self.Project) project(_Self.Project)

View File

@ -491,6 +491,10 @@ Contributors:
[![icon][github]](https://github.com/Infonautica) [![icon][github]](https://github.com/Infonautica)
&nbsp; &nbsp;
Leonid Danilov Leonid Danilov
* [![icon][mail]](mailto:emanuel-367@hotmail.com)
[![icon][github]](https://github.com/emanuelgestosa)
&nbsp;
Emanuel Gestosa
Previous contributors: Previous contributors:

View File

@ -25,6 +25,24 @@ usual beta standards.
## To Be Released ## To Be Released
### Fixes:
* [VIM-3291](https://youtrack.jetbrains.com/issue/VIM-3291) Remove sync of editor selection between different opened editors
* [VIM-3234](https://youtrack.jetbrains.com/issue/VIM-3234) The space character won't mix in the tab chars after >> and << commands
*
### Merged PRs:
* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
* [806](https://github.com/JetBrains/ideavim/pull/806) by [chylex](https://github.com/chylex): Enforce LF line separator in project code style
## 2.9.0, 2024-02-20
### Fixes:
* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
### Merged PRs:
* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
## 2.8.0, 2024-01-30
### Fixes: ### Fixes:
* [VIM-3130](https://youtrack.jetbrains.com/issue/VIM-3130) Change the build version to 2023.1.2 * [VIM-3130](https://youtrack.jetbrains.com/issue/VIM-3130) Change the build version to 2023.1.2
* [VIM-3168](https://youtrack.jetbrains.com/issue/VIM-3168) Do not switch to block caret after enter if the IdeaVim is disabled * [VIM-3168](https://youtrack.jetbrains.com/issue/VIM-3168) Do not switch to block caret after enter if the IdeaVim is disabled
@ -44,6 +62,8 @@ usual beta standards.
* [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape * [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape
* [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode * [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode
* [VIM-3085](https://youtrack.jetbrains.com/issue/VIM-3085) Open access to VimTypedActionHandler and VimShortcutKeyAction * [VIM-3085](https://youtrack.jetbrains.com/issue/VIM-3085) Open access to VimTypedActionHandler and VimShortcutKeyAction
* [VIM-3260](https://youtrack.jetbrains.com/issue/VIM-3260) Processing the offsets at the file end
* [VIM-3183](https://youtrack.jetbrains.com/issue/VIM-3183) Execute .ideavimrc on pooled thread
### Merged PRs: ### Merged PRs:
* [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s… * [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s…

View File

@ -84,3 +84,8 @@ IV) It is not allowed to remove this license from the distribution of the Vim
license for previous Vim releases instead of the license that they came license for previous Vim releases instead of the license that they came
with, at your option. with, at your option.
``` ```
---
File [sneakIcon.png](doc/images/sneakIcon.svg), which is originally an icon of the ideavim-sneak plugin,
is merged icons of IdeaVim plugin and a random sneaker by FreePic from flaticon.com.

View File

@ -8,7 +8,7 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("plugin.serialization") version "1.8.21" kotlin("plugin.serialization") version "1.9.22"
} }
val kotlinxSerializationVersion: String by project val kotlinxSerializationVersion: String by project
@ -21,7 +21,7 @@ repositories {
} }
dependencies { dependencies {
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.16") compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") { 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 // kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
exclude("org.jetbrains.kotlin", "kotlin-stdlib") exclude("org.jetbrains.kotlin", "kotlin-stdlib")

View File

@ -44,7 +44,7 @@ buildscript {
} }
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2") classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r") classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
@ -52,11 +52,11 @@ buildscript {
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r") classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
classpath("org.kohsuke:github-api:1.305") classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:2.3.7") classpath("io.ktor:ktor-client-core:2.3.8")
classpath("io.ktor:ktor-client-cio:2.3.7") classpath("io.ktor:ktor-client-cio:2.3.8")
classpath("io.ktor:ktor-client-auth:2.3.7") classpath("io.ktor:ktor-client-auth:2.3.8")
classpath("io.ktor:ktor-client-content-negotiation:2.3.7") classpath("io.ktor:ktor-client-content-negotiation:2.3.8")
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.7") classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.8")
// This comes from the changelog plugin // This comes from the changelog plugin
// classpath("org.jetbrains:markdown:0.3.1") // classpath("org.jetbrains:markdown:0.3.1")
@ -66,19 +66,17 @@ buildscript {
plugins { plugins {
antlr antlr
java java
kotlin("jvm") version "1.8.21" kotlin("jvm") version "1.9.22"
application application
id("java-test-fixtures")
id("org.jetbrains.intellij") version "1.16.1" id("org.jetbrains.intellij") version "1.17.2"
id("org.jetbrains.changelog") version "2.2.0" id("org.jetbrains.changelog") version "2.2.0"
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
// id("org.jlleitschuh.gradle.ktlint") version "11.3.1"
id("org.jetbrains.kotlinx.kover") version "0.6.1" id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "2.1.1" id("com.dorongold.task-tree") version "2.1.1"
id("com.google.devtools.ksp") version "1.8.21-1.0.11" id("com.google.devtools.ksp") version "1.9.22-1.0.17"
} }
ksp { ksp {
@ -91,6 +89,8 @@ ksp {
afterEvaluate { afterEvaluate {
// tasks.named("kspKotlin").configure { dependsOn("clean") } // tasks.named("kspKotlin").configure { dependsOn("clean") }
tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") } tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestKotlin").configure { enabled = false } tasks.named("kspTestKotlin").configure { enabled = false }
} }
@ -98,10 +98,11 @@ afterEvaluate {
val javaVersion: String by project val javaVersion: String by project
val kotlinVersion: String by project val kotlinVersion: String by project
val ideaVersion: String by project val ideaVersion: String by project
val ideaType: String by project
val downloadIdeaSources: String by project val downloadIdeaSources: String by project
val instrumentPluginCode: String by project val instrumentPluginCode: String by project
val remoteRobotVersion: String by project
val antlrVersion: String by project val antlrVersion: String by project
val remoteRobotVersion: String by project
val publishChannels: String by project val publishChannels: String by project
val publishToken: String by project val publishToken: String by project
@ -115,35 +116,40 @@ repositories {
} }
dependencies { dependencies {
api(project(":vim-engine"))
ksp(project(":annotation-processors"))
implementation(project(":annotation-processors"))
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
compileOnly("org.jetbrains:annotations:24.1.0") compileOnly("org.jetbrains:annotations:24.1.0")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
antlr("org.antlr:antlr4:$antlrVersion")
// --------- Test dependencies ----------
testImplementation(testFixtures(project(":")))
testApi("com.squareup.okhttp3:okhttp:4.12.0")
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3") testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3")
testImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3") testImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3")
testFixturesImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3")
testFixturesImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-test // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-test
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin // https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1") testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
testImplementation("com.automation-remarks:video-recorder-junit5:2.0") testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion") testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
antlr("org.antlr:antlr4:$antlrVersion") testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
api(project(":vim-engine"))
ksp(project(":annotation-processors"))
implementation(project(":annotation-processors"))
testApi("com.squareup.okhttp3:okhttp:4.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
} }
configurations { configurations {
@ -152,74 +158,17 @@ configurations {
} }
} }
// --- Compilation
// This can be moved to other test registration when issue with tests in gradle will be fixed
tasks.register<Test>("testWithNeovim") {
group = "verification"
systemProperty("ideavim.nvim.test", "true")
exclude("/ui/**")
exclude("**/longrunning/**")
exclude("**/propertybased/**")
useJUnitPlatform()
}
tasks.register<Test>("testPropertyBased") {
group = "verification"
// include("**/propertybased/**")
useJUnitPlatform()
}
tasks.register<Test>("testLongRunning") {
group = "verification"
// include("**/longrunning/**")
useJUnitPlatform()
}
tasks { tasks {
// Issue in gradle 7.3 test {
val test by getting(Test::class) {
isScanForTestClasses = false
// Only run tests from classes that end with "Test"
include("**/*Test.class")
include("**/*test.class")
include("**/*Tests.class")
exclude("**/ParserTest.class")
// Set teamcity env variable locally to run additional tests for leaks. // Set teamcity env variable locally to run additional tests for leaks.
// By default, this test runs on TC only, but this test doesn't take a lot of time, // By default, this test runs on TC only, but this test doesn't take a lot of time,
// so we can turn it on for local development // so we can turn it on for local development
if (environment["TEAMCITY_VERSION"] == null) { if (environment["TEAMCITY_VERSION"] == null) {
println("Set env TEAMCITY_VERSION to X") println("Set env TEAMCITY_VERSION to X to enable project leak checks from the platform")
environment("TEAMCITY_VERSION" to "X") environment("TEAMCITY_VERSION" to "X")
} }
}
val testWithNeovim by getting(Test::class) { systemProperty("ideavim.nvim.test", System.getProperty("nvim") ?: false)
isScanForTestClasses = false
// Only run tests from classes that end with "Test"
include("**/*Test.class")
include("**/*test.class")
include("**/*Tests.class")
exclude("**/ParserTest.class")
exclude("**/longrunning/**")
exclude("**/propertybased/**")
}
val testPropertyBased by getting(Test::class) {
isScanForTestClasses = false
// Only run tests from classes that end with "Test"
include("**/propertybased/*Test.class")
include("**/propertybased/*test.class")
include("**/propertybased/*Tests.class")
}
val testLongRunning by getting(Test::class) {
isScanForTestClasses = false
// Only run tests from classes that end with "Test"
include("**/longrunning/**/*Test.class")
include("**/longrunning/**/*test.class")
include("**/longrunning/**/*Tests.class")
exclude("**/longrunning/**/ParserTest.class")
} }
compileJava { compileJava {
@ -232,18 +181,33 @@ tasks {
compileKotlin { compileKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = javaVersion jvmTarget = javaVersion
apiVersion = "1.6" // See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
// For the list of bundled versions
apiVersion = "1.9"
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility") freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
// allWarningsAsErrors = true // allWarningsAsErrors = true
} }
} }
compileTestKotlin { compileTestKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = javaVersion jvmTarget = javaVersion
apiVersion = "1.6" apiVersion = "1.9"
// allWarningsAsErrors = true // allWarningsAsErrors = true
} }
} }
downloadRobotServerPlugin {
version.set(remoteRobotVersion)
}
runIdeForUiTests {
systemProperty("robot-server.port", "8082")
systemProperty("ide.mac.message.dialogs.as.sheets", "false")
systemProperty("jb.privacy.policy.text", "<!--999.999-->")
systemProperty("jb.consents.confirmation.enabled", "false")
systemProperty("ide.show.tips.on.startup.default.value", "false")
}
} }
java { java {
@ -270,6 +234,7 @@ gradle.projectsEvaluated {
intellij { intellij {
version.set(ideaVersion) version.set(ideaVersion)
type.set(ideaType)
pluginName.set("IdeaVim") pluginName.set("IdeaVim")
updateSinceUntilBuild.set(false) updateSinceUntilBuild.set(false)
@ -277,15 +242,10 @@ intellij {
downloadSources.set(downloadIdeaSources.toBoolean()) downloadSources.set(downloadIdeaSources.toBoolean())
instrumentCode.set(instrumentPluginCode.toBoolean()) instrumentCode.set(instrumentPluginCode.toBoolean())
intellijRepository.set("https://www.jetbrains.com/intellij-repository") intellijRepository.set("https://www.jetbrains.com/intellij-repository")
// Yaml is only used for testing. It's part of the IdeaIC distribution, but needs to be included as a reference plugins.set(listOf("AceJump:3.8.11"))
plugins.set(listOf("java", "AceJump:3.8.11", "yaml"/*, "Pythonid:231.8109.2", "com.intellij.clion-swift:231.8109.4"*/))
} }
tasks { tasks {
downloadRobotServerPlugin {
version.set(remoteRobotVersion)
}
publishPlugin { publishPlugin {
channels.set(publishChannels.split(",")) channels.set(publishChannels.split(","))
token.set(publishToken) token.set(publishToken)
@ -297,14 +257,6 @@ tasks {
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD")) password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
} }
runIdeForUiTests {
systemProperty("robot-server.port", "8082")
systemProperty("ide.mac.message.dialogs.as.sheets", "false")
systemProperty("jb.privacy.policy.text", "<!--999.999-->")
systemProperty("jb.consents.confirmation.enabled", "false")
systemProperty("ide.show.tips.on.startup.default.value", "false")
}
runPluginVerifier { runPluginVerifier {
downloadDir.set("${project.buildDir}/pluginVerifier/ides") downloadDir.set("${project.buildDir}/pluginVerifier/ides")
teamCityOutputFormat.set(true) teamCityOutputFormat.set(true)
@ -323,6 +275,9 @@ tasks {
named("compileTestKotlin") { named("compileTestKotlin") {
dependsOn("generateTestGrammarSource") dependsOn("generateTestGrammarSource")
} }
named("compileTestFixturesKotlin") {
dependsOn("generateTestFixturesGrammarSource")
}
// Add plugin open API sources to the plugin ZIP // Add plugin open API sources to the plugin ZIP
val createOpenApiSourceJar by registering(Jar::class) { val createOpenApiSourceJar by registering(Jar::class) {
@ -374,29 +329,14 @@ tasks {
} }
} }
// --- Linting
//ktlint {
// version.set("0.48.2")
//}
// --- Tests // --- Tests
tasks { tasks {
test { test {
useJUnitPlatform() useJUnitPlatform()
exclude("**/propertybased/**")
exclude("**/longrunning/**")
exclude("/ui/**")
} }
} }
tasks.register<Test>("testUi") {
group = "verification"
useJUnitPlatform()
include("/ui/**")
}
// --- Changelog // --- Changelog
changelog { changelog {
@ -415,20 +355,11 @@ koverMerged {
enable() enable()
} }
kover {
instrumentation {
// set of test tasks names to exclude from instrumentation. The results of their execution will not be presented in the report
excludeTasks += "testPropertyBased"
excludeTasks += "testLongRunning"
excludeTasks += "testWithNeovim"
excludeTasks += "testUi"
}
}
// --- Slack notification // --- Slack notification
tasks.register("slackNotification") { tasks.register("slackNotification") {
doLast { doLast {
if (version.toString().last() != '0') return@doLast
if (slackUrl.isBlank()) { if (slackUrl.isBlank()) {
println("Slack Url is not defined") println("Slack Url is not defined")
return@doLast return@doLast
@ -500,12 +431,14 @@ val prId: String by project
tasks.register("updateMergedPr") { tasks.register("updateMergedPr") {
doLast { doLast {
if (project.hasProperty("prId")) { val x = changelog.getUnreleased()
println("Got pr id: $prId") println("x")
updateMergedPr(prId.toInt()) // if (project.hasProperty("prId")) {
} else { // println("Got pr id: $prId")
error("Cannot get prId") // updateMergedPr(prId.toInt())
} // } else {
// error("Cannot get prId")
// }
} }
} }
@ -529,7 +462,7 @@ val fixVersionsElementType = "VersionBundleElement"
tasks.register("releaseActions") { tasks.register("releaseActions") {
group = "other" group = "other"
doLast { doLast {
val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D") val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
if (tickets.isNotEmpty()) { if (tickets.isNotEmpty()) {
println("Updating statuses for tickets: $tickets") println("Updating statuses for tickets: $tickets")
setYoutrackStatus(tickets, "Fixed") setYoutrackStatus(tickets, "Fixed")
@ -617,7 +550,8 @@ fun addReleaseToYoutrack(name: String): String {
println("Creating new release version in YouTrack: $name") println("Creating new release version in YouTrack: $name")
return runBlocking { return runBlocking {
val response = client.post("https://youtrack.jetbrains.com/api/admin/projects/$vimProjectId/customFields/$fixVersionsFieldId/bundle/values?fields=id,name") { val response =
client.post("https://youtrack.jetbrains.com/api/admin/projects/$vimProjectId/customFields/$fixVersionsFieldId/bundle/values?fields=id,name") {
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
accept(ContentType.Application.Json) accept(ContentType.Application.Json)
val request = buildJsonObject { val request = buildJsonObject {
@ -634,7 +568,8 @@ fun getVersionIdByName(name: String): String? {
val client = httpClient() val client = httpClient()
return runBlocking { return runBlocking {
val response = client.get("https://youtrack.jetbrains.com/api/admin/projects/$vimProjectId/customFields/$fixVersionsFieldId/bundle/values?fields=id,name&query=$name") val response =
client.get("https://youtrack.jetbrains.com/api/admin/projects/$vimProjectId/customFields/$fixVersionsFieldId/bundle/values?fields=id,name&query=$name")
response.body<JsonArray>().singleOrNull()?.jsonObject?.get("id")?.jsonPrimitive?.content response.body<JsonArray>().singleOrNull()?.jsonObject?.get("id")?.jsonPrimitive?.content
} }
} }
@ -672,7 +607,8 @@ fun setYoutrackStatus(tickets: Collection<String>, status: String) {
runBlocking { runBlocking {
for (ticket in tickets) { for (ticket in tickets) {
println("Try to set $ticket to $status") println("Try to set $ticket to $status")
val response = client.post("https://youtrack.jetbrains.com/api/issues/$ticket?fields=customFields(id,name,value(id,name))") { val response =
client.post("https://youtrack.jetbrains.com/api/issues/$ticket?fields=customFields(id,name,value(id,name))") {
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
accept(ContentType.Application.Json) accept(ContentType.Application.Json)
val request = buildJsonObject { val request = buildJsonObject {
@ -711,7 +647,8 @@ fun setYoutrackFixVersion(tickets: Collection<String>, version: String) {
runBlocking { runBlocking {
for (ticket in tickets) { for (ticket in tickets) {
println("Try to set fix version $version for $ticket") println("Try to set fix version $version for $ticket")
val response = client.post("https://youtrack.jetbrains.com/api/issues/$ticket?fields=customFields(id,name,value(id,name))") { val response =
client.post("https://youtrack.jetbrains.com/api/issues/$ticket?fields=customFields(id,name,value(id,name))") {
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
accept(ContentType.Application.Json) accept(ContentType.Application.Json)
val request = buildJsonObject { val request = buildJsonObject {
@ -749,7 +686,8 @@ fun getYoutrackStatus(ticket: String): String {
val client = httpClient() val client = httpClient()
return runBlocking { return runBlocking {
val response = client.get("https://youtrack.jetbrains.com/api/issues/$ticket/customFields/123-129?fields=value(name)") val response =
client.get("https://youtrack.jetbrains.com/api/issues/$ticket/customFields/123-129?fields=value(name)")
response.body<JsonObject>()["value"]!!.jsonObject.getValue("name").jsonPrimitive.content response.body<JsonObject>()["value"]!!.jsonObject.getValue("name").jsonPrimitive.content
} }
} }

View File

@ -45,15 +45,20 @@ All commands with the mappings are supported. See the [full list of supported co
<details> <details>
<summary><h2>sneak</h2></summary> <summary><h2>sneak</h2></summary>
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
By [Mikhail Levchenko](https://github.com/Mishkun)
Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak). Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak).
### Setup: ### Setup:
- Install [IdeaVim-sneak](https://plugins.jetbrains.com/plugin/15348-ideavim-sneak) plugin. - Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
- Add the following command to `~/.ideavimrc`: `set sneak`
### Instructions ### Instructions
See the [docs](https://github.com/Mishkun/ideavim-sneak#usage) * Type `s` and two chars to start sneaking in forward direction
* Type `S` and two chars to start sneaking in backward direction
* Type `;` or `,` to proceed with sneaking just as if you were using `f` or `t` commands
</details> </details>

28
doc/images/sneakIcon.svg Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="386.498 234 32 32" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="ideavim_plugin-a" x1="-6.748%" x2="47.286%" y1="33.61%" y2="85.907%">
<stop offset="0" stop-color="#3BEA62"/>
<stop offset="1" stop-color="#087CFA"/>
</linearGradient>
</defs>
<g transform="matrix(1.238978, 0.90017, -0.90017, 1.238978, 131.776901, -422.953003)" style="">
<path d="M 399.962 247.648 C 399.207 246.894 399.147 246.318 399.692 245.453 C 400.237 244.588 401.955 245.886 401.955 245.886 L 401.955 250.737" style="fill: rgb(248, 245, 231);"/>
<path d="M 413.846 253.602 C 413.846 255.077 411.827 256.134 409.392 256.134 L 396.381 256.134 C 395.211 256.134 394.232 256.003 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 413.846 253.602" fill="#cce6f6" style=""/>
<path d="M 413.846 253.602 C 413.846 253.602 411.475 254.468 408.27 254.468 C 405.94 254.468 398.116 253.433 394.023 252.869 C 392.488 252.658 391.478 252.512 391.478 252.512 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 C 396.446 247.877 396.955 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 406.544 249.03 410.097 250.43 410.097 250.43 C 410.097 250.43 413.061 250.446 413.782 251.167 C 414.503 251.888 414.422 253.218 413.846 253.602 Z M 413.846 253.602" style="fill: rgb(8, 124, 250);"/>
<path d="M 394.023 252.869 L 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 394.023 252.869" fill="#9dcae0" style=""/>
<path d="M 396.059 247.225 C 395.073 245.986 393.193 250.255 394.023 252.869 C 392.488 252.658 391.478 252.513 391.478 252.513 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 Z M 396.059 247.225" style="fill: rgb(14, 112, 142);"/>
<path d="M 403.527 246.924 L 399.174 251.768 C 397.7 250.892 395.578 250.174 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 L 394.341 248.65 L 394.884 248.827 C 395.509 249.031 396.192 248.95 396.753 248.608 L 397.153 248.363 C 397.35 248.454 397.572 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 402.494 246.256 403.02 246.601 403.527 246.924 Z M 403.527 246.924" style="fill: rgb(59, 234, 98);"/>
<path d="M 413.847 253.602 C 413.847 253.602 411.475 254.468 408.27 254.468 C 407.586 254.468 406.426 254.378 405.025 254.238 L 405.025 254.237 C 405.025 253.495 406.924 251.743 408.366 251.616 C 408.623 252.865 410.097 252.512 411.219 252.128 C 412.341 251.743 413.783 251.167 413.783 251.167 C 414.503 251.888 414.422 253.218 413.847 253.602 Z M 413.847 253.602" style="fill: rgb(8, 124, 250);"/>
<path d="M 394.341 248.65 C 394.214 248.978 394.103 249.339 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 Z M 394.341 248.65" style="fill: rgb(37, 187, 163);"/>
<path d="M 408.366 251.616 C 406.924 251.743 405.025 253.495 405.025 254.237 L 405.025 254.238 C 403.784 254.113 402.355 253.948 400.899 253.77 C 400.899 253.051 400.191 252.372 399.174 251.768 L 399.174 251.768 L 403.528 246.924 C 407.331 249.34 410.097 250.43 410.097 250.43 C 410.77 251.102 409.809 251.487 408.366 251.616 Z M 408.366 251.616" style="fill: rgb(248, 245, 231);"/>
<polygon fill="url(#ideavim_plugin-a)" fill-rule="evenodd" points="406.356 248.463 403.261 252.616 402.183 248.212 400.984 249.899 401.999 254.236 403.927 254.176 407.639 249.062" style="" transform="matrix(0.994522, 0.104529, -0.104529, 0.994522, 28.475005, -40.88594)"/>
<g fill="#fb6572" transform="matrix(0.046265, 0, 0, 0.046265, 390.612823, 245.155533)" style="">
<path d="m288.839844 72.65625c-2.007813 0-4.011719-.761719-5.542969-2.292969-3.058594-3.0625-3.058594-8.023437 0-11.082031l20.089844-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.0625 8.023438 0 11.082032l-20.089844 20.089843c-1.527344 1.53125-3.535156 2.292969-5.539062 2.292969zm0 0" style="fill: rgb(56, 228, 105);"/>
<path d="m314.589844 87.082031c-2.007813 0-4.011719-.765625-5.542969-2.296875-3.0625-3.058594-3.0625-8.019531 0-11.082031l20.089844-20.085937c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.058594 8.023437 0 11.082031l-20.089844 20.085937c-1.527344 1.53125-3.535156 2.296875-5.539062 2.296875zm0 0" style="fill: rgb(59, 233, 100);"/>
<path d="m340.339844 101.507812c-2.007813 0-4.011719-.765624-5.542969-2.296874-3.058594-3.058594-3.058594-8.023438 0-11.082032l20.089844-20.085937c3.0625-3.0625 8.023437-3.0625 11.082031 0 3.058594 3.058593 3.0625 8.023437 0 11.082031l-20.089844 20.085938c-1.527344 1.53125-3.535156 2.296874-5.539062 2.296874zm0 0" style="fill: rgb(59, 233, 100);"/>
<path d="m366.089844 115.929688c-2.003906 0-4.011719-.761719-5.539063-2.292969-3.0625-3.0625-3.0625-8.023438 0-11.082031l20.085938-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.0625 3.0625 3.0625 8.023437 0 11.082031l-20.085938 20.089844c-1.53125 1.53125-3.535156 2.292969-5.542968 2.292969zm0 0" style="fill: rgb(59, 233, 100);"/>
</g>
<path d="M 401.925 247.748 C 401.761 247.748 401.611 247.629 401.573 247.469 C 401.536 247.312 401.611 247.144 401.753 247.067 C 402 246.933 402.318 247.147 402.286 247.426 C 402.265 247.606 402.107 247.748 401.925 247.748 Z M 401.925 247.748" fill="#1e2628" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -9,17 +9,19 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
ideaVersion=2023.3.2 ideaVersion=2023.3.2
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC
downloadIdeaSources=true downloadIdeaSources=true
instrumentPluginCode=true instrumentPluginCode=true
version=SNAPSHOT version=SNAPSHOT
javaVersion=17 javaVersion=17
remoteRobotVersion=0.11.21 remoteRobotVersion=0.11.22
antlrVersion=4.10.1 antlrVersion=4.10.1
# Please don't forget to update kotlin version in buildscript section # Please don't forget to update kotlin version in buildscript section
# Also update kotlinxSerializationVersion version # Also update kotlinxSerializationVersion version
kotlinVersion=1.8.21 kotlinVersion=1.9.22
publishToken=token publishToken=token
publishChannels=eap publishChannels=eap

Binary file not shown.

View File

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

5
gradlew vendored
View File

@ -130,10 +130,13 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,9 @@ exclude:
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt
- src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated - src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
- src/main/java/com/maddyhome/idea/vim/package-info.java - src/main/java/com/maddyhome/idea/vim/package-info.java
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
- tests/ui-fixtures
dependencyIgnores: dependencyIgnores:
- name: "acejump" - name: "acejump"
- name: "icu4j" - name: "icu4j"

View File

@ -22,11 +22,11 @@ repositories {
dependencies { dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
implementation("io.ktor:ktor-client-core:2.3.7") implementation("io.ktor:ktor-client-core:2.3.8")
implementation("io.ktor:ktor-client-cio:2.3.7") implementation("io.ktor:ktor-client-cio:2.3.8")
implementation("io.ktor:ktor-client-content-negotiation:2.3.7") implementation("io.ktor:ktor-client-content-negotiation:2.3.8")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7") implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.8")
implementation("io.ktor:ktor-client-auth:2.3.7") implementation("io.ktor:ktor-client-auth:2.3.8")
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r") implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh // This is needed for jgit to connect to ssh
@ -93,28 +93,16 @@ tasks.register("addReleaseTag", JavaExec::class) {
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "") args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
} }
tasks.register("resetReleaseBranch", JavaExec::class) { tasks.register("selectBranch", JavaExec::class) {
group = "release" group = "release"
mainClass.set("scripts.release.ResetReleaseBranchKt") mainClass.set("scripts.release.SelectBranchKt")
classpath = sourceSets["main"].runtimeClasspath classpath = sourceSets["main"].runtimeClasspath
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "") args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
} }
tasks.register("pushChanges", JavaExec::class) { tasks.register("eapReleaseActions", JavaExec::class) {
mainClass.set("scripts.PushChangesKt")
classpath = sourceSets["main"].runtimeClasspath
args = listOf(rootProject.rootDir.toString())
}
tasks.register("pushChangesWithReleaseBranch", JavaExec::class) {
mainClass.set("scripts.PushChangesWithReleaseBranchKt")
classpath = sourceSets["main"].runtimeClasspath
args = listOf(rootProject.rootDir.toString(), releaseType ?: "")
}
tasks.register("selectBranch", JavaExec::class) {
group = "release" group = "release"
mainClass.set("scripts.release.SelectBranchKt") mainClass.set("scripts.releaseEap.EapReleaseActionsKt")
classpath = sourceSets["main"].runtimeClasspath classpath = sourceSets["main"].runtimeClasspath
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "") args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
} }

View File

@ -22,7 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
*/ */
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
val knownPlugins = listOf( val knownPlugins = setOf(
"IdeaVimExtension", "IdeaVimExtension",
"github.zgqq.intellij-enhance", "github.zgqq.intellij-enhance",
"org.jetbrains.IdeaVim-EasyMotion", "org.jetbrains.IdeaVim-EasyMotion",
@ -31,7 +31,13 @@ val knownPlugins = listOf(
"com.github.copilot", "com.github.copilot",
"com.github.dankinsoid.multicursor", "com.github.dankinsoid.multicursor",
"com.joshestein.ideavim-quickscope", "com.joshestein.ideavim-quickscope",
"ca.alexgirard.HarpoonIJ", "ca.alexgirard.HarpoonIJ",
"me.kyren223.harpoonforjb", // https://plugins.jetbrains.com/plugin/23771-harpoonforjb
"com.github.erotourtes.harpoon", // https://plugins.jetbrains.com/plugin/21796-harpooner
"me.kyren223.trident", // https://plugins.jetbrains.com/plugin/23818-trident
"com.protoseo.input-source-auto-converter",
// "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for // "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
) )
@ -41,7 +47,7 @@ suspend fun main() {
parameter("dependency", "IdeaVIM") parameter("dependency", "IdeaVIM")
parameter("includeOptional", true) parameter("includeOptional", true)
} }
val output = response.body<List<String>>() val output = response.body<List<String>>().toSet()
println(output) println(output)
if (knownPlugins != output) { if (knownPlugins != output) {
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") } val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }

View File

@ -32,7 +32,7 @@ fun httpClient(): HttpClient {
install(Auth) { install(Auth) {
bearer { bearer {
loadTokens { loadTokens {
val accessToken = System.getenv("YOUTRACK_TOKEN")!! val accessToken = System.getenv("YOUTRACK_TOKEN") ?: error("Missing YOUTRACK_TOKEN")
BearerTokens(accessToken, "") BearerTokens(accessToken, "")
} }
} }

View File

@ -1,37 +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 scripts
import scripts.release.checkoutBranch
import scripts.release.withGit
import scripts.release.withRepo
fun main(args: Array<String>) {
val rootDir = args[0]
println("root dir: $rootDir")
val currentBranch = withRepo(rootDir) { it.branch }
println("Current branch is $currentBranch")
withGit(rootDir) { git ->
if (currentBranch != "master") {
git.checkoutBranch("master")
println("Check out master branch")
}
git.push()
.setPushTags()
.call()
println("Master pushed with tags")
git.checkoutBranch(currentBranch)
println("Checked out $currentBranch branch")
}
}

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 scripts
import scripts.release.checkoutBranch
import scripts.release.withGit
import scripts.release.withRepo
fun main(args: Array<String>) {
val rootDir = args[0]
val releaseType = args[1]
println("root dir: $rootDir")
println("releaseType: $releaseType")
val currentBranch = withRepo(rootDir) { it.branch }
println("Current branch is $currentBranch")
withGit(rootDir) { git ->
if (currentBranch != "master") {
git.checkoutBranch("master")
println("Check out master branch")
}
git.push()
.setPushTags()
.call()
println("Master pushed with tags")
if (releaseType != "patch") {
git.checkoutBranch("release")
println("Checked out release")
git
.push()
.setForce(true)
.setPushTags()
.call()
println("Pushed release branch with tags")
}
else {
println("Do not push release branch because type of release is $releaseType")
}
git.checkoutBranch(currentBranch)
println("Checked out $currentBranch branch")
}
}

View File

@ -8,6 +8,12 @@
package scripts.release package scripts.release
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.revwalk.filter.RevFilter
fun main(args: Array<String>) { fun main(args: Array<String>) {
println("HI!") println("HI!")
val projectDir = args[0] val projectDir = args[0]
@ -19,10 +25,12 @@ fun main(args: Array<String>) {
check(branch == "master") { check(branch == "master") {
"We should be on master branch" "We should be on master branch"
} }
val mergeBaseCommit = getMergeBaseWithMaster(projectDir, objectId)
println("Base commit $mergeBaseCommit")
withGit(projectDir) { git -> withGit(projectDir) { git ->
val log = git.log().setMaxCount(500).call().toList() val log = git.log().setMaxCount(500).call().toList()
println("First commit hash in log: " + log.first().name + " log size: ${log.size}") println("First commit hash in log: " + log.first().name + " log size: ${log.size}")
val logDiff = log.takeWhile { it.id.name != objectId.name } val logDiff = log.takeWhile { it.id.name != mergeBaseCommit }
val numCommits = logDiff.size val numCommits = logDiff.size
println("Log diff size is $numCommits") println("Log diff size is $numCommits")
check(numCommits < 450) { check(numCommits < 450) {
@ -35,3 +43,18 @@ fun main(args: Array<String>) {
println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']") println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
} }
} }
private fun getMergeBaseWithMaster(projectDir: String, tag: ObjectId): String {
withRepo(projectDir) { repo ->
val master = repo.resolve("master")
RevWalk(repo).use { walk ->
val tagRevCommit = walk.parseCommit(tag)
val masterRevCommit = walk.parseCommit(master)
walk.setRevFilter(RevFilter.MERGE_BASE)
walk.markStart(tagRevCommit)
walk.markStart(masterRevCommit)
val mergeBase: RevCommit = walk.next()
return mergeBase.name
}
}
}

View File

@ -8,28 +8,45 @@
package scripts.release package scripts.release
import com.vdurmont.semver4j.Semver
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.readText import kotlin.io.path.readText
import kotlin.io.path.writeText import kotlin.io.path.writeText
private const val toBeReleased = "## To Be Released"
fun main(args: Array<String>) { fun main(args: Array<String>) {
println("Start updating unreleased section") println("Start updating unreleased section")
val (newVersion, rootDir, releaseType) = readArgs(args) val (newVersion, rootDir, releaseType) = readArgs(args)
checkReleaseType(releaseType) checkReleaseType(releaseType)
if (releaseType == "patch") {
println("Skip updating the changelog because release type is 'patch'")
return
}
val currentDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE) val currentDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)
val newItem = "## $newVersion, $currentDate" val newItem = "## $newVersion, $currentDate"
val changelogPath = Path("$rootDir/CHANGES.md") val changelogPath = Path("$rootDir/CHANGES.md")
val changelog = changelogPath.readText() val changelog = changelogPath.readText()
val newChangelog = changelog.replace("## To Be Released", newItem) val newChangelog = if (releaseType == "patch") {
val decreasedVersion = Semver(newVersion).withIncPatch(-1)
val firstEntry = changelog.indexOf("## $decreasedVersion")
if (firstEntry != -1) {
val newLog = StringBuilder(changelog)
newLog.insert(firstEntry, newItem + "\n")
newLog.toString()
} else {
changelog
}
} else {
if (toBeReleased in changelog) {
changelog.replace(toBeReleased, newItem)
} else {
val firstEntry = changelog.indexOf("##")
val newLog = StringBuilder(changelog)
newLog.insert(firstEntry, newItem + "\n")
newLog.toString()
}
}
changelogPath.writeText(newChangelog) changelogPath.writeText(newChangelog)
} }

View File

@ -13,12 +13,8 @@ fun main(args: Array<String>) {
checkReleaseType(releaseType) checkReleaseType(releaseType)
if (releaseType == "patch") {
println("Skip committing changes because release type is 'patch'")
return
}
withGit(rootDir) { git -> withGit(rootDir) { git ->
if (git.diff().call().isNotEmpty()) {
git git
.commit() .commit()
.setAll(true) .setAll(true)
@ -29,5 +25,8 @@ fun main(args: Array<String>) {
val lastGitMessage = git.log().call().first().shortMessage val lastGitMessage = git.log().call().first().shortMessage
println("Changes committed. Last gitlog message: $lastGitMessage") println("Changes committed. Last gitlog message: $lastGitMessage")
} else {
println("No Changes")
}
} }
} }

View File

@ -1,38 +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 scripts.release
fun main(args: Array<String>) {
val (_, rootDir, releaseType) = readArgs(args)
checkReleaseType(releaseType)
checkBranch(rootDir, releaseType)
if (releaseType == "patch") {
println("Skip release branch reset because release type is 'patch'")
return
}
withGit(rootDir) { git ->
val currentCommit = git.log().setMaxCount(1).call().first()
println("Current commit id: ${currentCommit.id.name}")
git.checkoutBranch("release")
println("Checked out release branch")
git.reset()
.setRef(currentCommit.id.name)
.call()
println("release branch reset")
git.checkoutBranch("master")
println("Checked out master branch")
}
}

View File

@ -15,8 +15,7 @@ fun main(args: Array<String>) {
withGit(rootDir) { git -> withGit(rootDir) { git ->
val branchName = when (releaseType) { val branchName = when (releaseType) {
"major", "minor" -> "master" "major", "minor", "patch" -> "release"
"patch" -> "release"
else -> error("Unsupported release type: $releaseType") else -> error("Unsupported release type: $releaseType")
} }

View File

@ -0,0 +1,36 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package scripts.releaseEap
import kotlinx.coroutines.runBlocking
import scripts.addComment
import scripts.getYoutrackTicketsByQuery
import scripts.release.readArgs
import scripts.releasedInEapTagId
import scripts.setTag
fun main(args: Array<String>) {
runBlocking {
val (newVersion, _, _) = readArgs(args)
// Search for Ready to release, but without "IdeaVim Released In EAP" tag
val ticketsToUpdate =
getYoutrackTicketsByQuery("%23%7BReady%20To%20Release%7D%20tag:%20-%7BIdeaVim%20Released%20In%20EAP%7D%20")
println("Have to update the following tickets: $ticketsToUpdate")
ticketsToUpdate.forEach { ticketId ->
setTag(ticketId, releasedInEapTagId)
addComment(
ticketId, """
The fix is available in the IdeaVim $newVersion. See https://jb.gg/ideavim-eap for the instructions on how to get EAP builds as updates within the IDE. You can also wait till the next stable release with this fix, youll get it automatically.
""".trimIndent()
)
}
}
}

View File

@ -11,6 +11,8 @@ package scripts
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.addJsonObject import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
@ -21,6 +23,10 @@ import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject import kotlinx.serialization.json.putJsonObject
// YouTrack tag "IdeaVim Released In EAP"
const val releasedInEapTagId = "68-385032"
suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) { suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) {
val client = httpClient() val client = httpClient()
@ -59,3 +65,59 @@ suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) {
} }
} }
} }
fun getYoutrackTicketsByQuery(query: String): Set<String> {
val client = httpClient()
return runBlocking {
val response = client.get("https://youtrack.jetbrains.com/api/issues/?fields=idReadable&query=project:VIM+$query")
response.body<JsonArray>().mapTo(HashSet()) { it.jsonObject.getValue("idReadable").jsonPrimitive.content }
}
}
/**
* 68-385032
* [issueHumanId] is like VIM-123
* [tagId] is like "145-23"
*/
suspend fun setTag(issueHumanId: String, tagId: String) {
val client = httpClient()
println("Try to add tag $tagId to $issueHumanId")
val response =
// I've updated default url in client, so this may be broken now
client.post("https://youtrack.jetbrains.com/api/issues/$issueHumanId/tags?fields=customFields(id,name,value(id,name))") {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
val request = buildJsonObject {
put("id", tagId)
}
setBody(request)
}
println(response)
println(response.body<String>())
if (!response.status.isSuccess()) {
error("Request failed. $issueHumanId, ${response.body<String>()}")
}
}
suspend fun addComment(issueHumanId: String, text: String) {
val client = httpClient()
println("Try to add comment to $issueHumanId")
val response =
// I've updated default url in client, so this may be broken now
client.post("https://youtrack.jetbrains.com/api/issues/$issueHumanId/comments?fields=customFields(id,name,value(id,name))") {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
val request = buildJsonObject {
put("text", text)
}
setBody(request)
}
println(response)
println(response.body<String>())
if (!response.status.isSuccess()) {
error("Request failed. $issueHumanId, ${response.body<String>()}")
}
}

View File

@ -12,4 +12,9 @@ rootProject.name = 'IdeaVIM'
include 'vim-engine' include 'vim-engine'
include 'scripts' include 'scripts'
include 'annotation-processors' include 'annotation-processors'
include 'tests:java-tests'
include 'tests:property-tests'
include 'tests:long-running-tests'
include 'tests:ui-ij-tests'
include 'tests:ui-py-tests'
include 'tests:ui-fixtures'

View File

@ -7,56 +7,27 @@
*/ */
package com.maddyhome.idea.vim package com.maddyhome.idea.vim
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.ExtensionPointName
import com.maddyhome.idea.vim.action.EngineCommandProvider import com.maddyhome.idea.vim.action.EngineCommandProvider
import com.maddyhome.idea.vim.action.IntellijCommandProvider import com.maddyhome.idea.vim.action.IntellijCommandProvider
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.newapi.IjVimActionsInitiator
import com.maddyhome.idea.vim.newapi.globalIjOptions
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import javax.swing.KeyStroke import javax.swing.KeyStroke
public object RegisterActions { public object RegisterActions {
@Deprecated("Please use @CommandOrMotion annotation instead")
internal val VIM_ACTIONS_EP: ExtensionPointName<ActionBeanClass> = ExtensionPointName.create("IdeaVIM.vimAction")
/** /**
* Register all the key/action mappings for the plugin. * Register all the key/action mappings for the plugin.
*/ */
@JvmStatic @JvmStatic
public fun registerActions() { public fun registerActions() {
registerVimCommandActions() registerVimCommandActions()
if (!injector.globalIjOptions().commandOrMotionAnnotation) { registerEmptyShortcuts() // todo most likely it is not needed
registerEmptyShortcuts()
registerEpListener()
}
}
@Deprecated("Moving to annotations approach instead of xml")
private fun registerEpListener() {
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
VIM_ACTIONS_EP.addChangeListener({
unregisterActions()
registerActions()
}, VimPlugin.getInstance())
} }
public fun findAction(id: String): EditorActionHandlerBase? { public fun findAction(id: String): EditorActionHandlerBase? {
if (injector.globalIjOptions().commandOrMotionAnnotation) {
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null ?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
return commandBean.instance return commandBean.instance
} else {
return VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream()
.filter { vimActionBean: ActionBeanClass -> vimActionBean.actionId == id }
.findFirst().map { obj: ActionBeanClass -> obj.instance }
.orElse(null)
}
} }
public fun findActionOrDie(id: String): EditorActionHandlerBase { public fun findActionOrDie(id: String): EditorActionHandlerBase {
@ -71,24 +42,10 @@ public object RegisterActions {
private fun registerVimCommandActions() { private fun registerVimCommandActions() {
val parser = VimPlugin.getKey() val parser = VimPlugin.getKey()
if (injector.globalIjOptions().commandOrMotionAnnotation) {
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) } EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) } IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
} else {
VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream().map { bean: ActionBeanClass? ->
IjVimActionsInitiator(
bean!!
)
}
.forEach { actionHolder: IjVimActionsInitiator? ->
parser.registerCommandAction(
actionHolder!!
)
}
}
} }
// todo do we really need this?
private fun registerEmptyShortcuts() { private fun registerEmptyShortcuts() {
val parser = VimPlugin.getKey() val parser = VimPlugin.getKey()

View File

@ -39,11 +39,9 @@ import com.maddyhome.idea.vim.listener.VimListenerManager;
import com.maddyhome.idea.vim.newapi.IjVimInjector; import com.maddyhome.idea.vim.newapi.IjVimInjector;
import com.maddyhome.idea.vim.ui.StatusBarIconFactory; import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.vimscript.services.OptionService;
import com.maddyhome.idea.vim.vimscript.services.VariableService; import com.maddyhome.idea.vim.vimscript.services.VariableService;
import com.maddyhome.idea.vim.yank.YankGroupBase; import com.maddyhome.idea.vim.yank.YankGroupBase;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -117,12 +115,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return ApplicationManager.getApplication().getService(CommandGroup.class); return ApplicationManager.getApplication().getService(CommandGroup.class);
} }
@Deprecated // "Please use `injector.markService` instead"
@ApiStatus.ScheduledForRemoval(inVersion = "2.3")
public static @NotNull MarkGroup getMark() {
return ApplicationManager.getApplication().getService(MarkGroup.class);
}
public static @NotNull RegisterGroup getRegister() { public static @NotNull RegisterGroup getRegister() {
return ((RegisterGroup)VimInjectorKt.getInjector().getRegisterGroup()); return ((RegisterGroup)VimInjectorKt.getInjector().getRegisterGroup());
} }
@ -195,13 +187,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return VimInjectorKt.getInjector().getOptionGroup(); return VimInjectorKt.getInjector().getOptionGroup();
} }
/** Deprecated: Use getOptionGroup */
@Deprecated
// Used by which-key 0.8.0, IdeaVimExtension 1.6.5 + 1.6.8
public static @NotNull OptionService getOptionService() {
return VimInjectorKt.getInjector().getOptionService();
}
private static @NotNull NotificationService getNotifications() { private static @NotNull NotificationService getNotifications() {
return getNotifications(null); return getNotifications(null);
} }

View File

@ -14,7 +14,7 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.maddyhome.idea.vim.group.EditorHolderService import com.maddyhome.idea.vim.group.EditorHolderService
@Service @Service(Service.Level.PROJECT)
internal class VimProjectService(val project: Project) : Disposable { internal class VimProjectService(val project: Project) : Disposable {
override fun dispose() { override fun dispose() {
// Not sure if this is a best solution // Not sure if this is a best solution

View File

@ -28,6 +28,7 @@ import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.group.IjOptionConstants import com.maddyhome.idea.vim.group.IjOptionConstants
import com.maddyhome.idea.vim.group.IjOptions import com.maddyhome.idea.vim.group.IjOptions
import com.maddyhome.idea.vim.handler.enableOctopus
import com.maddyhome.idea.vim.handler.isOctopusEnabled import com.maddyhome.idea.vim.handler.isOctopusEnabled
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.HandlerInjector import com.maddyhome.idea.vim.helper.HandlerInjector
@ -116,12 +117,14 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG) if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG)
val editor = getEditor(e) val editor = getEditor(e)
if (editor != null && keyStroke != null) { if (editor != null && keyStroke != null) {
if (enableOctopus) {
if (isOctopusEnabled(keyStroke, editor)) { if (isOctopusEnabled(keyStroke, editor)) {
return ActionEnableStatus.no( return ActionEnableStatus.no(
"Processing VimShortcutKeyAction for the key that is used in the octopus handler", "Processing VimShortcutKeyAction for the key that is used in the octopus handler",
LogLevel.ERROR LogLevel.ERROR
) )
} }
}
if (editor.isIdeaVimDisabledHere) { if (editor.isIdeaVimDisabledHere) {
return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO) return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO)
} }

View File

@ -14,6 +14,7 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
@ -21,6 +22,7 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.common.argumentCaptured import com.maddyhome.idea.vim.common.argumentCaptured
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
@ -29,21 +31,67 @@ import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
// todo make it multicaret // todo make it multicaret
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
val operatorFunction = injector.keyGroup.operatorFunction val func = injector.globalOptions().operatorfunc
if (operatorFunction == null) { if (func.isEmpty()) {
VimPlugin.showMessage(MessageHelper.message("E774")) VimPlugin.showMessage(MessageHelper.message("E774"))
return false return false
} }
val scriptContext = CommandLineVimLContext
// The option value is either a function name, which should have a handler, or it might be a lambda expression, or a
// `function` or `funcref` call expression, all of which will return a funcref (with a handler)
var handler = injector.functionService.getFunctionHandlerOrNull(null, func, scriptContext)
if (handler == null) {
val expression = injector.vimscriptParser.parseExpression(func)
if (expression != null) {
try {
val value = expression.evaluate(editor, context, scriptContext)
if (value is VimFuncref) {
handler = value.handler
}
} catch (ex: ExException) {
// Get the argument for function('...') or funcref('...') for the error message
val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) {
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
}
else {
func
}
VimPlugin.showMessage("E117: Unknown function: $functionName")
return false
}
}
}
if (handler == null) {
VimPlugin.showMessage("E117: Unknown function: $func")
return false
}
val arg = when (selectionType) {
SelectionType.LINE_WISE -> "line"
SelectionType.CHARACTER_WISE -> "char"
SelectionType.BLOCK_WISE -> "block"
}
val saveRepeatHandler = VimRepeater.repeatHandler val saveRepeatHandler = VimRepeater.repeatHandler
injector.markService.setChangeMarks(editor.primaryCaret(), textRange) injector.markService.setChangeMarks(editor.primaryCaret(), textRange)
KeyHandler.getInstance().reset(editor) KeyHandler.getInstance().reset(editor)
val result = operatorFunction.apply(editor, context, selectionType)
val arguments = listOf(SimpleExpression(arg))
handler.executeFunction(arguments, editor, context, scriptContext)
VimRepeater.repeatHandler = saveRepeatHandler VimRepeater.repeatHandler = saveRepeatHandler
return result return true
} }
@CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL])

View File

@ -8,10 +8,9 @@
package com.maddyhome.idea.vim.action.editor package com.maddyhome.idea.vim.action.editor
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode import com.intellij.vim.annotations.Mode
import com.intellij.openapi.actionSystem.IdeActions
import com.maddyhome.idea.vim.action.ComplicatedKeysAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
@ -21,54 +20,32 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.IdeActionHandler import com.maddyhome.idea.vim.handler.IdeActionHandler
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.awt.event.KeyEvent
import java.util.* import java.util.*
import javax.swing.KeyStroke
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE), ComplicatedKeysAction { internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)),
)
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
} }
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE), ComplicatedKeysAction { internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)),
)
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
} }
@CommandOrMotion(keys = ["<Down>", "<kDown>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Down>", "<kDown>"], modes = [Mode.INSERT])
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN), ComplicatedKeysAction { internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0)),
)
override val type: Command.Type = Command.Type.MOTION override val type: Command.Type = Command.Type.MOTION
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
} }
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB), ComplicatedKeysAction { internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)),
)
override val type: Command.Type = Command.Type.INSERT override val type: Command.Type = Command.Type.INSERT
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
} }
@CommandOrMotion(keys = ["<Up>", "<kUp>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Up>", "<kUp>"], modes = [Mode.INSERT])
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP), ComplicatedKeysAction { internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0)),
)
override val type: Command.Type = Command.Type.MOTION override val type: Command.Type = Command.Type.MOTION
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
} }

View File

@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.action.ex
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.action.ComplicatedKeysAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
@ -18,7 +17,6 @@ import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import java.util.* import java.util.*
import javax.swing.KeyStroke
/** /**
* Called by KeyHandler to process the contents of the ex entry panel * Called by KeyHandler to process the contents of the ex entry panel
@ -26,10 +24,7 @@ import javax.swing.KeyStroke
* The mapping for this action means that the ex command is executed as a write action * The mapping for this action means that the ex command is executed as a write action
*/ */
@CommandOrMotion(keys = ["<CR>", "<C-M>", "<C-J>"], modes = [Mode.CMD_LINE]) @CommandOrMotion(keys = ["<CR>", "<C-M>", "<C-J>"], modes = [Mode.CMD_LINE])
public class ProcessExEntryAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction { public class ProcessExEntryAction : VimActionHandler.SingleExecution() {
override val keyStrokesSet: Set<List<KeyStroke>> =
parseKeysSet("<CR>", "<C-M>", 0x0a.toChar().toString(), 0x0d.toChar().toString())
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX) override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX)

View File

@ -23,17 +23,17 @@ public class CommandState(private val machine: VimStateMachine) {
public val isOperatorPending: Boolean public val isOperatorPending: Boolean
get() = machine.isOperatorPending get() = machine.isOperatorPending
public val mode: CommandState.Mode public val mode: Mode
get() { get() {
val myMode = machine.mode val myMode = machine.mode
return when (myMode) { return when (myMode) {
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> CommandState.Mode.INSERT com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> CommandState.Mode.COMMAND is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> CommandState.Mode.REPLACE com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> CommandState.Mode.SELECT is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> CommandState.Mode.VISUAL is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
} }
} }

View File

@ -14,21 +14,34 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.action.change.Extension import com.maddyhome.idea.vim.action.change.Extension
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.common.CommandAlias import com.maddyhome.idea.vim.common.CommandAlias
import com.maddyhome.idea.vim.common.CommandAliasHandler import com.maddyhome.idea.vim.common.CommandAliasHandler
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.CommandLineHelper import com.maddyhome.idea.vim.helper.CommandLineHelper
import com.maddyhome.idea.vim.helper.TestInputModel import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.noneOfEnum
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.ui.ModalEntry import com.maddyhome.idea.vim.ui.ModalEntry
import com.maddyhome.idea.vim.vimscript.model.Executable
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
import com.maddyhome.idea.vim.vimscript.model.VimLContext
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionFlag
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import java.util.*
import javax.swing.KeyStroke import javax.swing.KeyStroke
/** /**
@ -120,12 +133,6 @@ public object VimExtensionFacade {
.setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler)) .setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler))
} }
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
@JvmStatic
public fun setOperatorFunction(function: OperatorFunction) {
VimPlugin.getKey().operatorFunction = function
}
/** /**
* Runs normal mode commands similar to ':normal! {commands}'. * Runs normal mode commands similar to ':normal! {commands}'.
* Mappings doesn't work with this function * Mappings doesn't work with this function
@ -207,4 +214,65 @@ public object VimExtensionFacade {
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) { public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type) VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
} }
@JvmStatic
public fun exportScriptFunction(
scope: Scope?,
name: String,
args: List<String>,
defaultArgs: List<Pair<String, Expression>>,
hasOptionalArguments: Boolean,
flags: EnumSet<FunctionFlag>,
function: ScriptFunction
) {
var functionDeclaration: FunctionDeclaration? = null
val body = listOf(object : Executable {
// This context is set to the function declaration during initialisation and then set to the function execution
// context during execution
override lateinit var vimContext: VimLContext
override var rangeInScript: TextRange = TextRange(0, 0)
override fun execute(editor: VimEditor, context: ExecutionContext): ExecutionResult {
return function.execute(editor, context, functionDeclaration!!.functionVariables)
}
})
functionDeclaration = FunctionDeclaration(
scope,
name,
args,
defaultArgs,
body,
replaceExisting = true,
flags,
hasOptionalArguments
)
functionDeclaration.rangeInScript = TextRange(0, 0)
body.forEach { it.vimContext = functionDeclaration }
injector.functionService.storeFunction(functionDeclaration)
}
}
public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
editor, context, args ->
val type = args["type"]?.asString()
val selectionType = when (type) {
"line" -> SelectionType.LINE_WISE
"block" -> SelectionType.BLOCK_WISE
"char" -> SelectionType.CHARACTER_WISE
else -> return@exportScriptFunction ExecutionResult.Error
}
if (function.apply(editor, context, selectionType)) {
ExecutionResult.Success
}
else {
ExecutionResult.Error
}
}
}
public fun interface ScriptFunction {
public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
} }

View File

@ -53,6 +53,11 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
@Synchronized @Synchronized
private fun registerExtension(extensionBean: ExtensionBeanClass) { private fun registerExtension(extensionBean: ExtensionBeanClass) {
val name = extensionBean.name ?: extensionBean.instance.name val name = extensionBean.name ?: extensionBean.instance.name
if (name == "sneak" && extensionBean.name == null) {
// Filter out the old ideavim-sneak extension that used to be a separate plugin
// https://github.com/Mishkun/ideavim-sneak
return
}
if (name in registeredExtensions) return if (name in registeredExtensions) return
registeredExtensions.add(name) registeredExtensions.add(name)

View File

@ -22,26 +22,26 @@ import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getLineEndOffset import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.command.TextObjectVisualType import com.maddyhome.idea.vim.command.TextObjectVisualType
import com.maddyhome.idea.vim.common.CommandAliasHandler import com.maddyhome.idea.vim.common.CommandAliasHandler
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ranges.Ranges import com.maddyhome.idea.vim.ex.ranges.Ranges
import com.maddyhome.idea.vim.extension.ExtensionHandler import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.extension.VimExtension import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade
import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.handler.TextObjectActionHandler import com.maddyhome.idea.vim.handler.TextObjectActionHandler
import com.maddyhome.idea.vim.helper.PsiHelper import com.maddyhome.idea.vim.helper.PsiHelper
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
@ -49,17 +49,22 @@ import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import java.util.* import java.util.*
internal class CommentaryExtension : VimExtension { internal class CommentaryExtension : VimExtension {
companion object { companion object {
private const val OPERATOR_FUNC = "CommentaryOperatorFunc"
fun doCommentary( fun doCommentary(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
range: TextRange, range: TextRange,
selectionType: SelectionType, selectionType: SelectionType,
resetCaret: Boolean, resetCaret: Boolean = true,
): Boolean { ): Boolean {
val mode = editor.vimStateMachine.mode val mode = editor.vimStateMachine.mode
if (mode !is Mode.VISUAL) { if (mode !is Mode.VISUAL) {
@ -67,8 +72,7 @@ internal class CommentaryExtension : VimExtension {
} }
return runWriteAction { return runWriteAction {
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action // Treat block- and character-wise selections as block comments. Fall back if the first action isn't available
// isn't available
val actions = if (selectionType === SelectionType.LINE_WISE) { val actions = if (selectionType === SelectionType.LINE_WISE) {
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK) listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
} else { } else {
@ -113,6 +117,7 @@ internal class CommentaryExtension : VimExtension {
// first non-whitespace character, then the caret is in the right place. If it's inserted at the first column, // first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
// then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept // then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
// the difference // the difference
// TODO: If we don't move the caret to the start offset, we should maintain the current logical position
if (resetCaret) { if (resetCaret) {
editor.primaryCaret().moveToOffset(range.startOffset) editor.primaryCaret().moveToOffset(range.startOffset)
} }
@ -145,6 +150,16 @@ internal class CommentaryExtension : VimExtension {
putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true) putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true)
addCommand("Commentary", CommentaryCommandAliasHandler()) addCommand("Commentary", CommentaryCommandAliasHandler())
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, CommentaryOperatorFunction())
}
private class CommentaryOperatorFunction : OperatorFunction {
// todo make it multicaret
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
}
} }
/** /**
@ -153,19 +168,13 @@ internal class CommentaryExtension : VimExtension {
* E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to * E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to
* invoke the operator. This object is both the mapping handler and the operator function. * invoke the operator. This object is both the mapping handler and the operator function.
*/ */
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler { private class CommentaryOperatorHandler : ExtensionHandler {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
setOperatorFunction(this) injector.globalOptions().operatorfunc = OPERATOR_FUNC
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
// todo make it multicaret
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
}
} }
private class CommentaryMappingHandler : ExtensionHandler { private class CommentaryMappingHandler : ExtensionHandler {

View File

@ -19,22 +19,23 @@ import com.intellij.openapi.util.Key
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getOffset import com.maddyhome.idea.vim.api.getOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
import com.maddyhome.idea.vim.state.mode.selectionType import com.maddyhome.idea.vim.state.mode.selectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.extension.VimExtension import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.helper.fileSize import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.state.mode.mode import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
@ -72,17 +73,15 @@ internal class VimExchangeExtension : VimExtension {
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true) putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true)
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
} }
companion object { companion object {
@NonNls @NonNls private const val EXCHANGE_CMD = "<Plug>(Exchange)"
const val EXCHANGE_CMD = "<Plug>(Exchange)" @NonNls private const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
@NonNls private const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
@NonNls @NonNls private const val OPERATOR_FUNC = "ExchangeOperatorFunc"
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
@NonNls
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
val EXCHANGE_KEY = Key<Exchange>("exchange") val EXCHANGE_KEY = Key<Exchange>("exchange")
@ -108,7 +107,7 @@ internal class VimExchangeExtension : VimExtension {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
setOperatorFunction(Operator(false)) injector.globalOptions().operatorfunc = OPERATOR_FUNC
executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
} }
} }
@ -125,12 +124,12 @@ internal class VimExchangeExtension : VimExtension {
val mode = editor.mode val mode = editor.mode
// Leave visual mode to create selection marks // Leave visual mode to create selection marks
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE) Operator(true).apply(editor, context, mode.selectionType ?: SelectionType.CHARACTER_WISE)
} }
} }
} }
private class Operator(private val isVisual: Boolean) : OperatorFunction { private class Operator(private val isVisual: Boolean = false) : OperatorFunction {
fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col) fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
fun SelectionType.getString() = when (this) { fun SelectionType.getString() = when (this) {
SelectionType.CHARACTER_WISE -> "v" SelectionType.CHARACTER_WISE -> "v"
@ -148,7 +147,7 @@ internal class VimExchangeExtension : VimExtension {
else -> HighlighterTargetArea.EXACT_RANGE else -> HighlighterTargetArea.EXACT_RANGE
} }
val isVisualLine = ex.type == SelectionType.LINE_WISE val isVisualLine = ex.type == SelectionType.LINE_WISE
val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || (isVisual))) 1 else 0 val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || isVisual)) 1 else 0
return ijEditor.markupModel.addRangeHighlighter( return ijEditor.markupModel.addRangeHighlighter(
ijEditor.getMarkOffset(ex.start), ijEditor.getMarkOffset(ex.start),
(ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize), (ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize),
@ -158,7 +157,7 @@ internal class VimExchangeExtension : VimExtension {
) )
} }
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE) val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: SelectionType.CHARACTER_WISE)
val exchange1 = ijEditor.getUserData(EXCHANGE_KEY) val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
if (exchange1 == null) { if (exchange1 == null) {
val highlighter = highlightExchange(currentExchange) val highlighter = highlightExchange(currentExchange)

View File

@ -217,6 +217,8 @@ private object FileTypePatterns {
return if (fileTypeName in htmlLikeFileTypes) { return if (fileTypeName in htmlLikeFileTypes) {
this.htmlPatterns this.htmlPatterns
} else if (fileTypeName == "JAVA" || fileExtension == "java") {
this.javaPatterns
} else if (fileTypeName == "Ruby" || fileExtension == "rb") { } else if (fileTypeName == "Ruby" || fileExtension == "rb") {
this.rubyPatterns this.rubyPatterns
} else if (fileTypeName == "RHTML" || fileExtension == "erb") { } else if (fileTypeName == "RHTML" || fileExtension == "erb") {
@ -242,6 +244,7 @@ private object FileTypePatterns {
) )
private val htmlPatterns = createHtmlPatterns() private val htmlPatterns = createHtmlPatterns()
private val javaPatterns = createJavaPatterns()
private val rubyPatterns = createRubyPatterns() private val rubyPatterns = createRubyPatterns()
private val rubyAndHtmlPatterns = rubyPatterns + htmlPatterns private val rubyAndHtmlPatterns = rubyPatterns + htmlPatterns
private val phpPatterns = createPhpPatterns() private val phpPatterns = createPhpPatterns()
@ -271,6 +274,14 @@ private object FileTypePatterns {
) )
} }
private fun createJavaPatterns(): LanguagePatterns {
return (
LanguagePatterns("\\b(?<!else\\s+)if\\b", "\\belse\\s+if\\b", "\\belse(?!\\s+if)\\b") +
LanguagePatterns("\\bdo\\b", "\\bwhile\\b") +
LanguagePatterns("\\btry\\b", "\\bcatch\\b", "\\bfinally\\b")
)
}
private fun createRubyPatterns(): LanguagePatterns { private fun createRubyPatterns(): LanguagePatterns {
// Original patterns: https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim // Original patterns: https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim
// We use non-capturing groups (?:) since we don't need any back refs. The \\b marker takes care of word boundaries. // We use non-capturing groups (?:) since we don't need any back refs. The \\b marker takes care of word boundaries.

View File

@ -246,7 +246,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
// Note that ignoreCase is not overridden by the `\C` in the pattern // Note that ignoreCase is not overridden by the `\C` in the pattern
val pattern = makePattern(text, whole) val pattern = makePattern(text, whole)
val matches = SearchHelper.findAll(editor, pattern, 0, -1, false) val matches = injector.searchHelper.findAll(IjVimEditor(editor), pattern, 0, -1, false)
for (match in matches) { for (match in matches) {
if (match.contains(primaryCaret.offset)) { if (match.contains(primaryCaret.offset)) {
primaryCaret.vim.moveToOffset(match.startOffset) primaryCaret.vim.moveToOffset(match.startOffset)
@ -322,7 +322,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
searchOptions.add(SearchOptions.WRAP) searchOptions.add(SearchOptions.WRAP)
} }
return SearchHelper.findPattern(editor, makePattern(text, whole), startOffset, 1, searchOptions)?.startOffset ?: -1 return injector.searchHelper.findPattern(IjVimEditor(editor), makePattern(text, whole), startOffset, 1, searchOptions)?.startOffset ?: -1
} }
private fun makePattern(text: String, whole: Boolean): String { private fun makePattern(text: String, whole: Boolean): String {

View File

@ -14,12 +14,12 @@ import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getLineEndOffset import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
import com.maddyhome.idea.vim.state.mode.isLine import com.maddyhome.idea.vim.state.mode.isLine
import com.maddyhome.idea.vim.state.mode.selectionType import com.maddyhome.idea.vim.state.mode.selectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade import com.maddyhome.idea.vim.extension.VimExtensionFacade
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.helper.exitVisualMode import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.state.mode.mode import com.maddyhome.idea.vim.state.mode.mode
@ -53,11 +53,13 @@ internal class ReplaceWithRegister : VimExtension {
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_OPERATOR), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_OPERATOR), true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("grr"), owner, injector.parser.parseKeys(RWR_LINE), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("grr"), owner, injector.parser.parseKeys(RWR_LINE), true)
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true) putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true)
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
} }
private class RwrVisual : ExtensionHandler { private class RwrVisual : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val typeInEditor = editor.mode.selectionType ?: CHARACTER_WISE val typeInEditor = editor.mode.selectionType ?: SelectionType.CHARACTER_WISE
editor.sortedCarets().forEach { caret -> editor.sortedCarets().forEach { caret ->
val selectionStart = caret.selectionStart val selectionStart = caret.selectionStart
val selectionEnd = caret.selectionEnd val selectionEnd = caret.selectionEnd
@ -73,7 +75,7 @@ internal class ReplaceWithRegister : VimExtension {
override val isRepeatable: Boolean = true override val isRepeatable: Boolean = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
setOperatorFunction(Operator()) injector.globalOptions().operatorfunc = OPERATOR_FUNC
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
} }
@ -112,11 +114,11 @@ internal class ReplaceWithRegister : VimExtension {
editor.primaryCaret() to VimSelection.create( editor.primaryCaret() to VimSelection.create(
range.startOffset, range.startOffset,
range.endOffset - 1, range.endOffset - 1,
selectionType ?: CHARACTER_WISE, selectionType ?: SelectionType.CHARACTER_WISE,
editor, editor,
), ),
), ),
selectionType ?: CHARACTER_WISE, selectionType ?: SelectionType.CHARACTER_WISE,
) )
// todo multicaret // todo multicaret
doReplace(ijEditor, editor.primaryCaret(), visualSelection) doReplace(ijEditor, editor.primaryCaret(), visualSelection)
@ -132,14 +134,10 @@ internal class ReplaceWithRegister : VimExtension {
} }
companion object { companion object {
@NonNls @NonNls private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator" @NonNls private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
@NonNls private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
@NonNls @NonNls private const val OPERATOR_FUNC = "ReplaceWithRegisterOperatorFunc"
private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
@NonNls
private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) { private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
val registerGroup = injector.registerGroup val registerGroup = injector.registerGroup

View File

@ -0,0 +1,321 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.extension.sneak
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.HighlighterLayer
import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.util.Disposer
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.VimProjectService
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.newapi.ij
import java.awt.Font
import java.awt.event.KeyEvent
import javax.swing.Timer
private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK = 300
// By [Mikhail Levchenko](https://github.com/Mishkun)
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
internal class IdeaVimSneakExtension : VimExtension {
override fun getName(): String = "sneak"
override fun init() {
val highlightHandler = HighlightHandler()
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
// workaround to support ; and , commands
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"))
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
}
private class SneakHandler(
private val highlightHandler: HighlightHandler,
private val direction: Direction,
) : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val charone = getChar(editor) ?: return
val chartwo = getChar(editor) ?: return
val range = Util.jumpTo(editor, charone, chartwo, direction)
range?.let { highlightHandler.highlightSneakRange(editor.ij, range) }
Util.lastSymbols = "${charone}${chartwo}"
Util.lastSDirection = direction
}
private fun getChar(editor: VimEditor): Char? {
val key = VimExtensionFacade.inputKeyStroke(editor.ij)
return when {
key.keyChar == KeyEvent.CHAR_UNDEFINED || key.keyCode == KeyEvent.VK_ESCAPE -> null
else -> key.keyChar
}
}
}
/**
* This class acts as proxy for normal find commands because we need to update [Util.lastSDirection]
*/
private class SneakMemoryHandler(private val char: String) : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
Util.lastSDirection = null
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(char), editor)
}
}
private class SneakRepeatHandler(
private val highlightHandler: HighlightHandler,
private val direction: RepeatDirection,
) : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val lastSDirection = Util.lastSDirection
if (lastSDirection != null) {
val (charone, chartwo) = Util.lastSymbols.toList()
val jumpRange = Util.jumpTo(editor, charone, chartwo, direction.map(lastSDirection))
jumpRange?.let { highlightHandler.highlightSneakRange(editor.ij, jumpRange) }
} else {
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(direction.symb), editor.ij)
}
}
}
private object Util {
var lastSDirection: Direction? = null
var lastSymbols: String = ""
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
val caret = editor.primaryCaret()
val position = caret.offset.point
val chars = editor.text()
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
if (foundPosition != null) {
editor.primaryCaret().moveToOffset(foundPosition)
}
editor.ij.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
return foundPosition?.let { TextRange(foundPosition, foundPosition + 2) }
}
}
private enum class Direction(val offset: Int) {
FORWARD(1) {
override fun findBiChar(
editor: VimEditor,
charSequence: CharSequence,
position: Int,
charone: Char,
chartwo: Char
): Int? {
for (i in (position + offset) until charSequence.length - 1) {
if (matches(editor, charSequence, i, charone, chartwo)) {
return i
}
}
return null
}
},
BACKWARD(-1) {
override fun findBiChar(
editor: VimEditor,
charSequence: CharSequence,
position: Int,
charone: Char,
chartwo: Char
): Int? {
for (i in (position + offset) downTo 0) {
if (matches(editor, charSequence, i, charone, chartwo)) {
return i
}
}
return null
}
};
abstract fun findBiChar(
editor: VimEditor,
charSequence: CharSequence,
position: Int,
charone: Char,
chartwo: Char,
): Int?
fun matches(
editor: VimEditor,
charSequence: CharSequence,
charPosition: Int,
charOne: Char,
charTwo: Char,
): Boolean {
var match = charSequence[charPosition].equals(charOne, ignoreCase = injector.options(editor).ignorecase) &&
charSequence[charPosition + 1].equals(charTwo, ignoreCase = injector.options(editor).ignorecase)
if (injector.options(editor).ignorecase && injector.options(editor).smartcase) {
if (charOne.isUpperCase() || charTwo.isUpperCase()) {
match = charSequence[charPosition].equals(charOne, ignoreCase = false) &&
charSequence[charPosition + 1].equals(charTwo, ignoreCase = false)
}
}
return match
}
}
private enum class RepeatDirection(val symb: String) {
IDENTICAL(";") {
override fun map(direction: Direction): Direction = direction
},
REVERSE(",") {
override fun map(direction: Direction): Direction = when (direction) {
Direction.FORWARD -> Direction.BACKWARD
Direction.BACKWARD -> Direction.FORWARD
}
};
abstract fun map(direction: Direction): Direction
}
private class HighlightHandler {
private var editor: Editor? = null
private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
fun highlightSneakRange(editor: Editor, range: TextRange) {
clearAllSneakHighlighters()
this.editor = editor
val project = editor.project
if (project != null) {
Disposer.register(VimProjectService.getInstance(project)) {
this.editor = null
sneakHighlighters.clear()
}
}
if (range.isMultiple) {
for (i in 0 until range.size()) {
highlightSingleRange(editor, range.startOffsets[i]..range.endOffsets[i])
}
} else {
highlightSingleRange(editor, range.startOffset..range.endOffset)
}
}
fun clearAllSneakHighlighters() {
sneakHighlighters.forEach { highlighter ->
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
}
sneakHighlighters.clear()
}
private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
val highlighter = editor.markupModel.addRangeHighlighter(
range.start,
range.endInclusive,
HighlighterLayer.SELECTION,
getHighlightTextAttributes(),
HighlighterTargetArea.EXACT_RANGE
)
sneakHighlighters.add(highlighter)
setClearHighlightRangeTimer(highlighter)
}
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
val timer = Timer(DEFAULT_HIGHLIGHT_DURATION_SNEAK) {
if (editor?.isDisposed != true) {
editor?.markupModel?.removeHighlighter(highlighter)
}
}
timer.isRepeats = false
timer.start()
}
private fun getHighlightTextAttributes() = TextAttributes(
null,
EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES.defaultAttributes.backgroundColor,
editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
EffectType.SEARCH_MATCH,
Font.PLAIN
)
}
}
/**
* Map some <Plug>(keys) command to given handler
* and create mapping to <Plug>(prefix)[keys]
*/
private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
VimExtensionFacade.putExtensionHandlerMapping(
MappingMode.NXO,
injector.parser.parseKeys(command(keys)),
owner,
handler,
false
)
VimExtensionFacade.putExtensionHandlerMapping(
MappingMode.NXO,
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
owner,
handler,
false
)
// This is a combination to meet the following requirements:
// - Now we should support mappings from sneak `Sneak_s` and mappings from the previous version of the plugin `(sneak-s)`
// - The shortcut should not be registered if any of these mappings is overridden in .ideavimrc
// - The shortcut should not be registered if some other shortcut for this key exists
val fromKeys = injector.parser.parseKeys(keys)
val filteredModes = MappingMode.NXO.filterNotTo(HashSet()) {
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
}
val filteredModes2 = MappingMode.NXO.filterNotTo(HashSet()) {
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
}
val filteredFromModes = MappingMode.NXO.filterNotTo(HashSet()) {
injector.keyGroup.hasmapfrom(it, fromKeys)
}
val doubleFiltered = MappingMode.NXO
.filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
.toSet()
putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
putKeyMapping(
doubleFiltered,
fromKeys,
owner,
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
true
)
}
private fun command(keys: String) = "<Plug>(sneak-$keys)"
private fun commandFromOriginalPlugin(keys: String) = "<Plug>Sneak_$keys"

View File

@ -16,6 +16,7 @@ import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.endsWithNewLine import com.maddyhome.idea.vim.api.endsWithNewLine
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
@ -23,14 +24,16 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.extension.VimExtension import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
@ -74,13 +77,15 @@ internal class VimSurroundExtension : VimExtension {
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("ds"), owner, injector.parser.parseKeys("<Plug>DSurround"), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("ds"), owner, injector.parser.parseKeys("<Plug>DSurround"), true)
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true) putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
} }
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
} }
private class YSurroundHandler : ExtensionHandler { private class YSurroundHandler : ExtensionHandler {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
setOperatorFunction(Operator()) injector.globalOptions().operatorfunc = OPERATOR_FUNC
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
} }
@ -288,7 +293,9 @@ private val LOG = logger<VimSurroundExtension>()
private const val REGISTER = '"' private const val REGISTER = '"'
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern() private const val OPERATOR_FUNC = "SurroundOperatorFunc"
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
private val SURROUND_PAIRS = mapOf( private val SURROUND_PAIRS = mapOf(
'b' to ("(" to ")"), 'b' to ("(" to ")"),

View File

@ -535,7 +535,7 @@ public class ChangeGroup : VimChangeGroupBase() {
val soff = editor.getLineStartOffset(l) val soff = editor.getLineStartOffset(l)
val eoff = editor.getLineEndOffset(l, true) val eoff = editor.getLineEndOffset(l, true)
val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l) val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
val col = editor.offsetToVisualPosition(woff).column val col = editor.offsetToBufferPosition(woff).column
val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble()) val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
.toInt() .toInt()
if (col > 0 || soff != eoff) { if (col > 0 || soff != eoff) {

View File

@ -8,9 +8,11 @@
package com.maddyhome.idea.vim.group package com.maddyhome.idea.vim.group
import com.intellij.openapi.components.Service
import com.maddyhome.idea.vim.api.VimCommandGroupBase import com.maddyhome.idea.vim.api.VimCommandGroupBase
/** /**
* @author Elliot Courant * @author Elliot Courant
*/ */
@Service
internal class CommandGroup : VimCommandGroupBase() internal class CommandGroup : VimCommandGroupBase()

View File

@ -20,7 +20,6 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
*/ */
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) { public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
public var ide: String by optionProperty(IjOptions.ide) public var ide: String by optionProperty(IjOptions.ide)
public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks) public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon) public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
@ -29,15 +28,16 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys) public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids) public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
public var visualdelay: Int by optionProperty(IjOptions.visualdelay) public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
public var colorfulmodewidget: Boolean by optionProperty(IjOptions.colorfulmodewidget)
// Temporary options to control work-in-progress behaviour // Temporary options to control work-in-progress behaviour
public var oldundo: Boolean by optionProperty(IjOptions.oldundo) public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation) public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
} }
/** /**

View File

@ -33,8 +33,6 @@ public object IjOptions {
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux")) Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
} }
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true))
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true))
public val ide: StringOption = addOption( public val ide: StringOption = addOption(
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition) StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
) )
@ -81,13 +79,17 @@ public object IjOptions {
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>") "<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
) )
public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false)) public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true))
public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100)) public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isTemporary = true))
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true)) // Temporary feature flags during development, not really intended for external use
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true)) public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true)) public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
public val colorfulmodewidget: ToggleOption = addOption(ToggleOption("colorfulmodewidget", GLOBAL, "colorfulmodewidget", false, isTemporary = true)) public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isHidden = true))
public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isHidden = true))
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
public 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 // This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
// derives from Option<VimInt> // derives from Option<VimInt>

View File

@ -24,17 +24,14 @@ import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.maddyhome.idea.vim.EventFacade; import com.maddyhome.idea.vim.EventFacade;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.ComplicatedKeysAction;
import com.maddyhome.idea.vim.action.VimShortcutKeyAction; import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
import com.maddyhome.idea.vim.action.change.LazyVimCommand; import com.maddyhome.idea.vim.action.change.LazyVimCommand;
import com.maddyhome.idea.vim.api.*; import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.command.MappingMode; import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.ex.ExOutputModel; import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import com.maddyhome.idea.vim.helper.HelperKt; import com.maddyhome.idea.vim.helper.HelperKt;
import com.maddyhome.idea.vim.key.*; import com.maddyhome.idea.vim.key.*;
import com.maddyhome.idea.vim.newapi.IjNativeAction; import com.maddyhome.idea.vim.newapi.IjNativeAction;
import com.maddyhome.idea.vim.newapi.IjVimActionsInitiator;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import kotlin.Pair; import kotlin.Pair;
import kotlin.text.StringsKt; import kotlin.text.StringsKt;
@ -49,6 +46,7 @@ import java.awt.event.KeyEvent;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
/** /**
@ -221,66 +219,23 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE); registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
for (MappingMode mappingMode : command.getModes()) { for (MappingMode mappingMode : command.getModes()) {
Node<VimActionsInitiator> node = getKeyRoot(mappingMode); Node<LazyVimCommand> node = getKeyRoot(mappingMode);
NodesKt.addLeafs(node, keyStrokes, command); NodesKt.addLeafs(node, keyStrokes, command);
} }
} }
} }
@Deprecated
public void registerCommandAction(@NotNull VimActionsInitiator actionHolder) {
IjVimActionsInitiator holder = (IjVimActionsInitiator)actionHolder;
if (!VimPlugin.getPluginId().equals(holder.getBean().getPluginDescriptor().getPluginId())) {
logger.error("IdeaVim doesn't accept contributions to `vimActions` extension points. " +
"Please create a plugin using `VimExtension`. " +
"Plugin to blame: " +
holder.getBean().getPluginDescriptor().getPluginId());
return;
}
Set<List<KeyStroke>> actionKeys = holder.getBean().getParsedKeys();
if (actionKeys == null) {
final EditorActionHandlerBase action = actionHolder.getInstance();
if (action instanceof ComplicatedKeysAction) {
actionKeys = ((ComplicatedKeysAction)action).getKeyStrokesSet();
}
else {
throw new RuntimeException("Cannot register action: " + action.getClass().getName());
}
}
Set<MappingMode> actionModes = holder.getBean().getParsedModes();
if (actionModes == null) {
throw new RuntimeException("Cannot register action: " + holder.getBean().getImplementation());
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
initIdentityChecker();
for (List<KeyStroke> keys : actionKeys) {
checkCommand(actionModes, actionHolder.getInstance(), keys);
}
}
for (List<KeyStroke> keyStrokes : actionKeys) {
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
for (MappingMode mappingMode : actionModes) {
Node<VimActionsInitiator> node = getKeyRoot(mappingMode);
NodesKt.addLeafs(node, keyStrokes, actionHolder);
}
}
}
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) { private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
for (KeyStroke key : keys) { for (KeyStroke key : keys) {
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED && if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
if (!injector.getOptionGroup().getGlobalOptions().getOctopushandler() ||
!(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) && !(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
!(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) { !(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
getRequiredShortcutKeys().add(new RequiredShortcut(key, owner)); getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
} }
} }
} }
}
public static @NotNull ShortcutSet toShortcutSet(@NotNull Collection<RequiredShortcut> requiredShortcuts) { public static @NotNull ShortcutSet toShortcutSet(@NotNull Collection<RequiredShortcut> requiredShortcuts) {
final List<Shortcut> shortcuts = new ArrayList<>(); final List<Shortcut> shortcuts = new ArrayList<>();

View File

@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.group
import com.intellij.codeInsight.completion.CompletionPhase import com.intellij.codeInsight.completion.CompletionPhase
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
@ -25,6 +26,7 @@ import com.maddyhome.idea.vim.newapi.IjVimEditor
/** /**
* Used to handle playback of macros * Used to handle playback of macros
*/ */
@Service
internal class MacroGroup : VimMacroBase() { internal class MacroGroup : VimMacroBase() {
// If it's null, this is the top macro (as in most cases). If it's not null, this macro is executed from top macro // If it's null, this is the top macro (as in most cases). If it's not null, this macro is executed from top macro

View File

@ -1,138 +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.group;
import com.intellij.ide.bookmark.LineBookmark;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.mark.IntellijMark;
import com.maddyhome.idea.vim.mark.Jump;
import com.maddyhome.idea.vim.mark.Mark;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "2.3")
public class MarkGroup {
public List<Jump> jumps = VimInjectorKt.injector.getJumpService().getJumps("");
public void saveJumpLocation(@NotNull Editor editor) {
VimInjectorKt.injector.getJumpService().saveJumpLocation(new IjVimEditor(editor));
}
public void saveJumpLocation(@NotNull VimEditor editor) {
VimInjectorKt.injector.getJumpService().saveJumpLocation(editor);
}
public void setChangeMarks(@NotNull VimEditor vimEditor, @NotNull TextRange range) {
VimMarkService markService = VimInjectorKt.injector.getMarkService();
VimMarkServiceKt.setChangeMarks(markService, vimEditor.primaryCaret(), range);
}
public void addJump(@NotNull VimEditor editor, boolean reset) {
VimJumpServiceKt.addJump(VimInjectorKt.injector.getJumpService(), editor, reset);
}
@Nullable
public Mark getMark(@NotNull VimEditor editor, char ch) {
return VimInjectorKt.injector.getMarkService().getMark(editor.primaryCaret(), ch);
}
@Nullable
public Jump getJump(int count) {
return VimInjectorKt.injector.getJumpService().getJump("", count);
}
@Nullable
public Mark createSystemMark(char ch, int line, int col, @NotNull VimEditor editor) {
Editor ijEditor = ((IjVimEditor)editor).getEditor();
@Nullable LineBookmark systemMark = SystemMarks.createOrGetSystemMark(ch, line, ijEditor);
if (systemMark == null) {
return null;
}
return new IntellijMark(systemMark, col, ijEditor.getProject());
}
public boolean setMark(@NotNull VimEditor editor, char ch, int offset) {
return VimInjectorKt.injector.getMarkService().setMark(editor.primaryCaret(), ch, offset);
}
public boolean setMark(@NotNull VimEditor editor, char ch) {
return VimInjectorKt.injector.getMarkService().setMark(editor, ch);
}
public void includeCurrentCommandAsNavigation(@NotNull VimEditor editor) {
VimInjectorKt.injector.getJumpService().includeCurrentCommandAsNavigation(editor);
}
@Nullable
public Mark getFileMark(@NotNull VimEditor editor, char ch) {
return VimInjectorKt.injector.getMarkService().getMark(editor.primaryCaret(), ch);
}
public void setVisualSelectionMarks(@NotNull VimEditor editor, @NotNull TextRange range) {
VimMarkService markService = VimInjectorKt.injector.getMarkService();
VimMarkServiceKt.setVisualSelectionMarks(markService, editor.primaryCaret(), range);
}
@Nullable
public TextRange getChangeMarks(@NotNull VimEditor editor) {
return VimInjectorKt.injector.getMarkService().getChangeMarks(editor.primaryCaret());
}
@Nullable
public TextRange getVisualSelectionMarks(@NotNull VimEditor editor) {
return VimInjectorKt.injector.getMarkService().getVisualSelectionMarks(editor.primaryCaret());
}
public void resetAllMarks() {
VimInjectorKt.injector.getMarkService().resetAllMarks();
}
public void removeMark(char ch, @NotNull Mark mark) {
VimInjectorKt.injector.getMarkService().removeMark(ch, mark);
}
@NotNull
public List<Mark> getMarks(@NotNull VimEditor editor) {
Set<Mark> marks = VimInjectorKt.injector.getMarkService().getAllLocalMarks(editor.primaryCaret());
marks.addAll(VimInjectorKt.injector.getMarkService().getGlobalMarks(editor));
return new ArrayList<>(marks);
}
public int getJumpSpot() {
return VimInjectorKt.injector.getJumpService().getJumpSpot("");
}
public void updateMarkFromDelete(@Nullable VimEditor editor,
@Nullable HashMap<Character, Mark> marks,
int delStartOff,
int delLength) {
VimInjectorKt.injector.getMarkService().updateMarksFromDelete(editor, delStartOff, delLength);
}
public void updateMarkFromInsert(@Nullable VimEditor editor,
@Nullable HashMap<Character, Mark> marks,
int insStartOff,
int insLength) {
VimInjectorKt.injector.getMarkService().updateMarksFromInsert(editor, insStartOff, insLength);
}
public void dropLastJump() {
VimInjectorKt.injector.getJumpService().dropLastJump("");
}
}

View File

@ -8,6 +8,7 @@
package com.maddyhome.idea.vim.group package com.maddyhome.idea.vim.group
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.components.Service
import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.editor.LogicalPosition
@ -83,6 +84,7 @@ import kotlin.math.min
/** /**
* This handles all motion related commands and marks * This handles all motion related commands and marks
*/ */
@Service
internal class MotionGroup : VimMotionGroupBase() { internal class MotionGroup : VimMotionGroupBase() {
override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) { override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) {
AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset) AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)

View File

@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.KeyboardShortcut import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.ide.CopyPasteManager import com.intellij.openapi.ide.CopyPasteManager
import com.intellij.openapi.keymap.KeymapUtil import com.intellij.openapi.keymap.KeymapUtil
@ -55,6 +56,7 @@ import javax.swing.KeyStroke
* This service is can be used as application level and as project level service. * This service is can be used as application level and as project level service.
* If project is null, this means that this is an application level service and notification will be shown for all projects * If project is null, this means that this is an application level service and notification will be shown for all projects
*/ */
@Service(Service.Level.PROJECT, Service.Level.APP)
internal class NotificationService(private val project: Project?) { internal class NotificationService(private val project: Project?) {
// This constructor is used to create an applicationService // This constructor is used to create an applicationService
@Suppress("unused") @Suppress("unused")

View File

@ -65,28 +65,28 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup {
} }
} }
internal class IjOptionConstants { public class IjOptionConstants {
@Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName") @Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName")
companion object { public companion object {
const val idearefactormode_keep = "keep" public const val idearefactormode_keep: String = "keep"
const val idearefactormode_select = "select" public const val idearefactormode_select: String = "select"
const val idearefactormode_visual = "visual" public const val idearefactormode_visual: String = "visual"
const val ideastatusicon_enabled = "enabled" public const val ideastatusicon_enabled: String = "enabled"
const val ideastatusicon_gray = "gray" public const val ideastatusicon_gray: String = "gray"
const val ideastatusicon_disabled = "disabled" public const val ideastatusicon_disabled: String = "disabled"
const val ideavimsupport_dialog = "dialog" public const val ideavimsupport_dialog: String = "dialog"
const val ideavimsupport_singleline = "singleline" public const val ideavimsupport_singleline: String = "singleline"
const val ideavimsupport_dialoglegacy = "dialoglegacy" public const val ideavimsupport_dialoglegacy: String = "dialoglegacy"
const val ideawrite_all = "all" public const val ideawrite_all: String = "all"
const val ideawrite_file = "file" public const val ideawrite_file: String = "file"
val ideaStatusIconValues = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled) public val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
val ideaRefactorModeValues = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual) public val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
val ideaWriteValues = setOf(ideawrite_all, ideawrite_file) public val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
val ideavimsupportValues = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy) public val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
} }
} }

View File

@ -51,6 +51,8 @@ import javax.swing.SwingUtilities
public class ProcessGroup : VimProcessGroupBase() { public class ProcessGroup : VimProcessGroupBase() {
override var lastCommand: String? = null override var lastCommand: String? = null
private set private set
override var isCommandProcessing: Boolean = false
override var modeBeforeCommandProcessing: Mode? = null
public override fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char) { public override fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char) {
// Don't allow searching in one line editors // Don't allow searching in one line editors
@ -79,6 +81,8 @@ public class ProcessGroup : VimProcessGroupBase() {
"Cannot enable cmd mode from current mode $currentMode" "Cannot enable cmd mode from current mode $currentMode"
} }
isCommandProcessing = true
modeBeforeCommandProcessing = currentMode
val initText = getRange(editor, cmd) val initText = getRange(editor, cmd)
injector.markService.setVisualSelectionMarks(editor) injector.markService.setVisualSelectionMarks(editor)
editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode) editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
@ -134,6 +138,9 @@ public class ProcessGroup : VimProcessGroupBase() {
logger.error(bad) logger.error(bad)
VimPlugin.indicateError() VimPlugin.indicateError()
res = false res = false
} finally {
isCommandProcessing = false
modeBeforeCommandProcessing = null
} }
return res return res

View File

@ -36,10 +36,9 @@ import com.maddyhome.idea.vim.history.HistoryConstants;
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext; import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
import com.maddyhome.idea.vim.newapi.IjVimCaret; import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener; import com.maddyhome.idea.vim.options.GlobalOptionChangeListener;
import com.maddyhome.idea.vim.regexp.CharPointer; import com.maddyhome.idea.vim.regexp.*;
import com.maddyhome.idea.vim.regexp.CharacterClasses;
import com.maddyhome.idea.vim.regexp.RegExp;
import com.maddyhome.idea.vim.ui.ModalEntry; import com.maddyhome.idea.vim.ui.ModalEntry;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.vimscript.model.VimLContext; import com.maddyhome.idea.vim.vimscript.model.VimLContext;
@ -62,13 +61,20 @@ import java.util.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.*; import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
import static com.maddyhome.idea.vim.helper.HelperKt.localEditors; import static com.maddyhome.idea.vim.helper.HelperKt.localEditors;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase; import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER; import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER;
@State(name = "VimSearchSettings", storages = { @State(name = "VimSearchSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED) @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
}) })
public class SearchGroup extends VimSearchGroupBase implements PersistentStateComponent<Element> { @Deprecated
/**
* @deprecated Replace with IjVimSearchGroup
*/
public class SearchGroup extends IjVimSearchGroup implements PersistentStateComponent<Element> {
public SearchGroup() { public SearchGroup() {
super();
if (!globalIjOptions(injector).getUseNewRegex()) {
// TODO: Investigate migrating these listeners to use the effective value change listener // TODO: Investigate migrating these listeners to use the effective value change listener
// This would allow us to update the editor we're told to update, rather than looping over all projects and updating // This would allow us to update the editor we're told to update, rather than looping over all projects and updating
// the highlights in that project's current document's open editors (see VIM-2779). // the highlights in that project's current document's open editors (see VIM-2779).
@ -88,8 +94,13 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible); VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible);
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible); VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible);
} }
}
public void turnOn() { public void turnOn() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.updateSearchHighlights(false);
return;
}
updateSearchHighlights(); updateSearchHighlights();
} }
@ -100,7 +111,12 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
} }
@TestOnly @TestOnly
@Override
public void resetState() { public void resetState() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.resetState();
return;
}
lastPatternIdx = RE_SEARCH; lastPatternIdx = RE_SEARCH;
lastSearch = lastSubstitute = lastReplace = null; lastSearch = lastSubstitute = lastReplace = null;
lastPatternOffset = ""; lastPatternOffset = "";
@ -114,7 +130,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
* *
* @return The pattern used for last search. Can be null * @return The pattern used for last search. Can be null
*/ */
@Override
public @Nullable String getLastSearchPattern() { public @Nullable String getLastSearchPattern() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSearchPattern();
return lastSearch; return lastSearch;
} }
@ -122,7 +140,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
* Get the last pattern used in substitution. * Get the last pattern used in substitution.
* @return The pattern used for the last substitute command. Can be null * @return The pattern used for the last substitute command. Can be null
*/ */
@Override
public @Nullable String getLastSubstitutePattern() { public @Nullable String getLastSubstitutePattern() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSubstitutePattern();
return lastSubstitute; return lastSubstitute;
} }
@ -131,7 +151,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
* *
* @return The pattern last used for either searching or substitution. Can be null * @return The pattern last used for either searching or substitution. Can be null
*/ */
public @Nullable String getLastUsedPattern() { @Override
protected @Nullable String getLastUsedPattern() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastUsedPattern();
switch (lastPatternIdx) { switch (lastPatternIdx) {
case RE_SEARCH: return lastSearch; case RE_SEARCH: return lastSearch;
case RE_SUBST: return lastSubstitute; case RE_SUBST: return lastSubstitute;
@ -195,6 +217,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
@TestOnly @TestOnly
public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern, public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern,
@NotNull String patternOffset, Direction direction) { @NotNull String patternOffset, Direction direction) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.setLastSearchState(pattern, patternOffset, direction);
return;
}
setLastUsedPattern(pattern, RE_SEARCH, true); setLastUsedPattern(pattern, RE_SEARCH, true);
lastIgnoreSmartCase = false; lastIgnoreSmartCase = false;
lastPatternOffset = patternOffset; lastPatternOffset = patternOffset;
@ -226,7 +252,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
int startLine, int startLine,
int endLine, int endLine,
boolean ignoreCase) { boolean ignoreCase) {
return SearchHelper.findAll(editor, pattern, startLine, endLine, ignoreCase); return injector.getSearchHelper().findAll(new IjVimEditor(editor), pattern, startLine, endLine, ignoreCase);
} }
/** /**
@ -254,6 +280,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
*/ */
@Override @Override
public int processSearchCommand(@NotNull VimEditor editor, @NotNull String command, int startOffset, @NotNull Direction dir) { public int processSearchCommand(@NotNull VimEditor editor, @NotNull String command, int startOffset, @NotNull Direction dir) {
if (globalIjOptions(injector).getUseNewRegex()) return super.processSearchCommand(editor, command, startOffset, dir);
boolean isNewPattern = false; boolean isNewPattern = false;
String pattern = null; String pattern = null;
String patternOffset = null; String patternOffset = null;
@ -414,6 +442,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
*/ */
@Override @Override
public int searchWord(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count, boolean whole, @NotNull Direction dir) { public int searchWord(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count, boolean whole, @NotNull Direction dir) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchWord(editor, caret, count, whole, dir);
TextRange range = SearchHelper.findWordUnderCursor(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret()); TextRange range = SearchHelper.findWordUnderCursor(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret());
if (range == null) { if (range == null) {
logger.warn("No range was found"); logger.warn("No range was found");
@ -455,6 +484,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
*/ */
@Override @Override
public int searchNext(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) { public int searchNext(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchNext(editor, caret, count);
return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, lastDir); return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, lastDir);
} }
@ -471,6 +501,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
*/ */
@Override @Override
public int searchPrevious(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) { public int searchPrevious(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchPrevious(editor, caret, count);
return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count,
lastDir.reverse()); lastDir.reverse());
} }
@ -524,6 +555,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
@NotNull @NonNls String excmd, @NotNull @NonNls String excmd,
@NotNull @NonNls String exarg, @NotNull @NonNls String exarg,
@NotNull VimLContext parent) { @NotNull VimLContext parent) {
if (globalIjOptions(injector).getUseNewRegex()) return super.processSubstituteCommand(editor, caret, range, excmd, exarg, parent);
// Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match. // Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
List<ExException> exceptions = new ArrayList<>(); List<ExException> exceptions = new ArrayList<>();
if (CommandStateHelper.inVisualMode(((IjVimEditor) editor).getEditor())) { if (CommandStateHelper.inVisualMode(((IjVimEditor) editor).getEditor())) {
@ -687,8 +720,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
return false; return false;
} }
Pair<Boolean, Triple<Object, String, Object>> booleanregmmatch_tPair = search_regcomp(pat, which_pat, Pair<Boolean, Triple<Object, String, Object>> booleanregmmatch_tPair = search_regcomp(pat, which_pat, RE_SUBST);
RE_SUBST);
if (!booleanregmmatch_tPair.getFirst()) { if (!booleanregmmatch_tPair.getFirst()) {
if (do_error) { if (do_error) {
VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd)); VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd));
@ -696,9 +728,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
} }
return false; return false;
} }
RegExp.regmmatch_T regmatch = (RegExp.regmmatch_T) booleanregmmatch_tPair.getSecond().getFirst(); RegExp.regmmatch_T regmatch = (RegExp.regmmatch_T)booleanregmmatch_tPair.getSecond().getFirst();
String pattern = booleanregmmatch_tPair.getSecond().getSecond(); String pattern = booleanregmmatch_tPair.getSecond().getSecond();
RegExp sp = (RegExp) booleanregmmatch_tPair.getSecond().getThird(); RegExp sp = (RegExp)booleanregmmatch_tPair.getSecond().getThird();
/* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
if (do_ic == 'i') { if (do_ic == 'i') {
@ -731,8 +763,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
resetShowSearchHighlight(); resetShowSearchHighlight();
forceUpdateSearchHighlights(); forceUpdateSearchHighlights();
int start = ((IjVimEditor) editor).getEditor().getDocument().getLineStartOffset(line1); int start = ((IjVimEditor)editor).getEditor().getDocument().getLineStartOffset(line1);
int end = ((IjVimEditor) editor).getEditor().getDocument().getLineEndOffset(line2); int end = ((IjVimEditor)editor).getEditor().getDocument().getLineEndOffset(line2);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("search range=[" + start + "," + end + "]"); logger.debug("search range=[" + start + "," + end + "]");
@ -755,7 +787,6 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
firstMatch = false; firstMatch = false;
} }
String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false); String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false);
if (sub.charAt(0) == '\\' && sub.charAt(1) == '=') { if (sub.charAt(0) == '\\' && sub.charAt(1) == '=') {
String exprString = sub.toString().substring(2); String exprString = sub.toString().substring(2);
@ -764,22 +795,25 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
exceptions.add(new ExException("E15: Invalid expression: " + exprString)); exceptions.add(new ExException("E15: Invalid expression: " + exprString));
expression = new SimpleExpression(new VimString("")); expression = new SimpleExpression(new VimString(""));
} }
} else if (match == null) { }
else if (match == null) {
return false; return false;
} }
int line = lnum + regmatch.startpos[0].lnum; int line = lnum + regmatch.startpos[0].lnum;
CharacterPosition startpos = new CharacterPosition(lnum + regmatch.startpos[0].lnum, regmatch.startpos[0].col); CharacterPosition startpos = new CharacterPosition(lnum + regmatch.startpos[0].lnum, regmatch.startpos[0].col);
CharacterPosition endpos = new CharacterPosition(lnum + regmatch.endpos[0].lnum, regmatch.endpos[0].col); CharacterPosition endpos = new CharacterPosition(lnum + regmatch.endpos[0].lnum, regmatch.endpos[0].col);
int startoff = startpos.toOffset(((IjVimEditor) editor).getEditor()); int startoff = startpos.toOffset(((IjVimEditor)editor).getEditor());
int endoff = endpos.toOffset(((IjVimEditor) editor).getEditor()); int endoff = endpos.toOffset(((IjVimEditor)editor).getEditor());
if (do_all || line != lastLine) { if (do_all || line != lastLine) {
boolean doReplace = true; boolean doReplace = true;
if (do_ask) { if (do_ask) {
RangeHighlighter hl = SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor) editor).getEditor(), startoff, endoff); RangeHighlighter hl =
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor) editor).getEditor(), match, ((IjVimCaret) caret).getCaret(), startoff); SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
((IjVimEditor) editor).getEditor().getMarkupModel().removeHighlighter(hl); endoff);
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), match, ((IjVimCaret)caret).getCaret(), startoff);
((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
switch (choice) { switch (choice) {
case SUBSTITUTE_THIS: case SUBSTITUTE_THIS:
doReplace = true; doReplace = true;
@ -802,25 +836,26 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
} }
} }
if (doReplace) { if (doReplace) {
SubmatchFunctionHandler.Companion.getInstance().setLatestMatch(((IjVimEditor) editor).getEditor().getDocument().getText(new com.intellij.openapi.util.TextRange(startoff, endoff))); SubmatchFunctionHandler.Companion.getInstance().setLatestMatch(
((IjVimEditor)editor).getEditor().getDocument().getText(new com.intellij.openapi.util.TextRange(startoff, endoff)));
caret.moveToOffset(startoff); caret.moveToOffset(startoff);
if (expression != null) { if (expression != null) {
try { try {
match = expression match =
.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent) expression.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent).toInsertableString();
.toInsertableString(); }
} catch (Exception e) { catch (Exception e) {
exceptions.add((ExException) e); exceptions.add((ExException)e);
match = ""; match = "";
} }
} }
String finalMatch = match; String finalMatch = match;
ApplicationManager.getApplication().runWriteAction(() -> ((IjVimEditor) editor).getEditor().getDocument().replaceString(startoff, endoff, ApplicationManager.getApplication().runWriteAction(
finalMatch)); () -> ((IjVimEditor)editor).getEditor().getDocument().replaceString(startoff, endoff, finalMatch));
lastMatch = startoff; lastMatch = startoff;
int newend = startoff + match.length(); int newend = startoff + match.length();
newpos = CharacterPosition.Companion.fromOffset(((IjVimEditor) editor).getEditor(), newend); newpos = CharacterPosition.Companion.fromOffset(((IjVimEditor)editor).getEditor(), newend);
lnum += newpos.line - endpos.line; lnum += newpos.line - endpos.line;
line2 += newpos.line - endpos.line; line2 += newpos.line - endpos.line;
@ -875,6 +910,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
@Override @Override
public void setLastSearchPattern(@Nullable String lastSearchPattern) { public void setLastSearchPattern(@Nullable String lastSearchPattern) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.setLastSearchPattern(lastSearchPattern);
return;
}
this.lastSearch = lastSearchPattern; this.lastSearch = lastSearchPattern;
if (showSearchHighlight) { if (showSearchHighlight) {
resetIncsearchHighlights(); resetIncsearchHighlights();
@ -884,6 +923,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
@Override @Override
public void setLastSubstitutePattern(@Nullable String lastSubstitutePattern) { public void setLastSubstitutePattern(@Nullable String lastSubstitutePattern) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.setLastSubstitutePattern(lastSubstitutePattern);
return;
}
this.lastSubstitute = lastSubstitutePattern; this.lastSubstitute = lastSubstitutePattern;
} }
@ -893,6 +936,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
int patternOffset, int patternOffset,
int startOffset, int startOffset,
@NotNull Direction direction) { @NotNull Direction direction) {
if (globalIjOptions(injector).getUseNewRegex()) return super.processSearchRange(editor, pattern, patternOffset, startOffset, direction);
return processSearchRange(((IjVimEditor) editor).getEditor(), pattern, patternOffset, startOffset, direction); return processSearchRange(((IjVimEditor) editor).getEditor(), pattern, patternOffset, startOffset, direction);
} }
@ -1015,6 +1059,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
*/ */
@Override @Override
public @Nullable TextRange getNextSearchRange(@NotNull VimEditor editor, int count, boolean forwards) { public @Nullable TextRange getNextSearchRange(@NotNull VimEditor editor, int count, boolean forwards) {
if (globalIjOptions(injector).getUseNewRegex()) return super.getNextSearchRange(editor, count, forwards);
editor.removeSecondaryCarets(); editor.removeSecondaryCarets();
TextRange current = findUnderCaret(editor); TextRange current = findUnderCaret(editor);
@ -1046,17 +1091,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
} }
} }
@Override
@Nullable
public TextRange findUnderCaret(@NotNull VimEditor editor) {
final TextRange backSearch = searchBackward(editor, editor.primaryCaret().getOffset().getPoint() + 1, 1);
if (backSearch == null) return null;
return backSearch.contains(editor.primaryCaret().getOffset().getPoint()) ? backSearch : null;
}
@Override @Override
@Nullable @Nullable
public TextRange searchBackward(@NotNull VimEditor editor, int offset, int count) { public TextRange searchBackward(@NotNull VimEditor editor, int offset, int count) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchBackward(editor, offset, count);
// Backward search returns wrongs end offset for some cases. That's why we should perform additional forward search // Backward search returns wrongs end offset for some cases. That's why we should perform additional forward search
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE, SearchOptions.BACKWARDS); final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE, SearchOptions.BACKWARDS);
final TextRange foundBackward = VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), offset, count, searchOptions); final TextRange foundBackward = VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), offset, count, searchOptions);
@ -1074,7 +1112,12 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
// //
// ******************************************************************************************************************* // *******************************************************************************************************************
//region Search highlights //region Search highlights
@Override
public void clearSearchHighlight() { public void clearSearchHighlight() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.clearSearchHighlight();
return;
}
showSearchHighlight = false; showSearchHighlight = false;
updateSearchHighlights(); updateSearchHighlights();
} }
@ -1094,7 +1137,12 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
/** /**
* Reset the search highlights to the last used pattern after highlighting incsearch results. * Reset the search highlights to the last used pattern after highlighting incsearch results.
*/ */
@Override
public void resetIncsearchHighlights() { public void resetIncsearchHighlights() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.resetIncsearchHighlights();
return;
}
SearchHighlightsHelper.updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true); SearchHighlightsHelper.updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true);
} }
@ -1103,9 +1151,13 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
} }
private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) { private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.highlightSearchLines(new IjVimEditor(editor), startLine, endLine);
return;
}
final String pattern = getLastUsedPattern(); final String pattern = getLastUsedPattern();
if (pattern != null) { if (pattern != null) {
final List<TextRange> results = SearchHelper.findAll(editor, pattern, startLine, endLine, final List<TextRange> results = injector.getSearchHelper().findAll(new IjVimEditor(editor), pattern, startLine, endLine,
shouldIgnoreCase(pattern, lastIgnoreSmartCase)); shouldIgnoreCase(pattern, lastIgnoreSmartCase));
SearchHighlightsHelper.highlightSearchResults(editor, pattern, results, -1); SearchHighlightsHelper.highlightSearchResults(editor, pattern, results, -1);
} }
@ -1114,12 +1166,17 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
/** /**
* Updates search highlights when the selected editor changes * Updates search highlights when the selected editor changes
*/ */
public static void fileEditorManagerSelectionChangedCallback(@SuppressWarnings("unused") @NotNull FileEditorManagerEvent event) { public void fileEditorManagerSelectionChangedCallback(@SuppressWarnings("unused") @NotNull FileEditorManagerEvent event) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.updateSearchHighlights(false);
return;
}
VimPlugin.getSearch().updateSearchHighlights(); VimPlugin.getSearch().updateSearchHighlights();
} }
@Override @Override
public Integer findDecimalNumber(@NotNull String line) { public Integer findDecimalNumber(@NotNull String line) {
if (globalIjOptions(injector).getUseNewRegex()) return super.findDecimalNumber(line);
Pair<TextRange, NumberType> searchResult = SearchHelper.findNumberInText(line, 0, false, false, false); Pair<TextRange, NumberType> searchResult = SearchHelper.findNumberInText(line, 0, false, false, false);
if (searchResult != null) { if (searchResult != null) {
TextRange range = searchResult.component1(); TextRange range = searchResult.component1();
@ -1131,6 +1188,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
@NotNull @NotNull
@Override @Override
public Direction getLastSearchDirection() { public Direction getLastSearchDirection() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSearchDirection();
return lastDir; return lastDir;
} }
@ -1274,7 +1332,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS); if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS);
// Uses RE_LAST. We know this is always set before being called // Uses RE_LAST. We know this is always set before being called
TextRange range = SearchHelper.findPattern(editor, getLastUsedPattern(), startOffset, count, searchOptions); TextRange range = injector.getSearchHelper().findPattern(new IjVimEditor(editor), getLastUsedPattern(), startOffset, count, searchOptions);
if (range == null) { if (range == null) {
logger.warn("No range is found"); logger.warn("No range is found");
return -1; return -1;

View File

@ -177,15 +177,6 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
return createOrGetSystemMark(char, line, col, editor) return createOrGetSystemMark(char, line, col, editor)
} }
@Deprecated("Please use removeMark with other signature")
override fun removeMark(ch: Char, mark: Mark) {
if (ch.isGlobalMark()) {
removeGlobalMark(ch)
} else if (ch.isLocalMark()) {
getLocalMarks(mark.filepath).remove(ch)
}
}
override fun removeGlobalMark(char: Char) { override fun removeGlobalMark(char: Char) {
val mark = getGlobalMark(char) val mark = getGlobalMark(char)
if (mark is IntellijMark) { if (mark is IntellijMark) {
@ -279,16 +270,6 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
} }
} }
/**
* COMPATIBILITY-LAYER: Method added
* Please see: [doc](https://jb.gg/zo8n0r)
*
*/
@Deprecated("Please use method with VimEditor")
fun saveJumpLocation(editor: Editor?) {
injector.jumpService.saveJumpLocation(IjVimEditor(editor!!))
}
companion object { companion object {
private const val SAVE_MARK_COUNT = 20 private const val SAVE_MARK_COUNT = 20
private val logger = Logger.getInstance( private val logger = Logger.getInstance(

View File

@ -8,9 +8,11 @@
package com.maddyhome.idea.vim.group package com.maddyhome.idea.vim.group
import com.intellij.openapi.components.Service
import org.apache.commons.codec.binary.Base64 import org.apache.commons.codec.binary.Base64
import org.jdom.Element import org.jdom.Element
@Service
internal class XMLGroup { internal class XMLGroup {
/** /**
* Set the text of an XML element, safely encode it if needed. * Set the text of an XML element, safely encode it if needed.

View File

@ -14,6 +14,7 @@ import com.intellij.ide.DataManager
import com.intellij.ide.PasteProvider import com.intellij.ide.PasteProvider
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.components.Service
import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.editor.ex.EditorEx
@ -51,6 +52,7 @@ import com.maddyhome.idea.vim.state.mode.isChar
import com.maddyhome.idea.vim.state.mode.isLine import com.maddyhome.idea.vim.state.mode.isLine
import java.awt.datatransfer.DataFlavor import java.awt.datatransfer.DataFlavor
@Service
internal class PutGroup : VimPutBase() { internal class PutGroup : VimPutBase() {
override fun getProviderForPasteViaIde( override fun getProviderForPasteViaIde(

View File

@ -52,12 +52,13 @@ import javax.swing.Timer
* editorHasSelection is false. Insert mode ([mode]) has no selection and editor also has no selection, so * editorHasSelection is false. Insert mode ([mode]) has no selection and editor also has no selection, so
* no adjustment gets performed and IdeaVim stays in insert mode. * no adjustment gets performed and IdeaVim stays in insert mode.
*/ */
internal object VimVisualTimer { // Do not remove until it's used in EasyMotion plugin in tests
public object VimVisualTimer {
var swingTimer: Timer? = null public var swingTimer: Timer? = null
var mode: Mode? = null public var mode: Mode? = null
inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) { public inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
swingTimer?.stop() swingTimer?.stop()
if (mode == null) mode = currentMode if (mode == null) mode = currentMode
@ -69,7 +70,7 @@ internal object VimVisualTimer {
swingTimer = timer swingTimer = timer
} }
fun doNow() { public fun doNow() {
val swingTimer1 = swingTimer val swingTimer1 = swingTimer
if (swingTimer1 != null) { if (swingTimer1 != null) {
swingTimer1.stop() swingTimer1.stop()
@ -79,12 +80,12 @@ internal object VimVisualTimer {
} }
} }
fun drop() { public fun drop() {
swingTimer?.stop() swingTimer?.stop()
swingTimer = null swingTimer = null
} }
inline fun timerAction(task: (initialMode: Mode?) -> Unit) { public inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
task(mode) task(mode)
swingTimer = null swingTimer = null
mode = null mode = null

View File

@ -1,102 +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.handler
import com.intellij.serviceContainer.BaseKeyedLazyInstance
import com.intellij.util.SmartList
import com.intellij.util.xmlb.annotations.Attribute
import com.maddyhome.idea.vim.command.MappingMode
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
import javax.swing.KeyStroke
/**
* Action holder for IdeaVim actions.
*
* [implementation] should be subclass of [EditorActionHandlerBase]
*
* [modes] ("mappingModes") defines the action modes. E.g. "NO" - action works in normal and op-pending modes.
* Warning: V - Visual and Select mode. X - Visual mode. (like vmap and xmap).
* Use "ALL" to enable action for all modes.
*
* [keys] comma-separated list of keys for the action. E.g. `gt,gT` - action gets executed on `gt` or `gT`
* Since xml doesn't allow using raw `<` character, use « and » symbols for mappings with modifiers.
* E.g. `«C-U»` - CTRL-U (<C-U> in vim notation)
* If you want to use exactly `<` character, replace it with `&lt;`. E.g. `i&lt;` - i<
* If you want to use comma in mapping, use `«COMMA»`
* Do not place a whitespace around the comma!
*
*
* !! IMPORTANT !!
* You may wonder why the extension points are used instead of any other approach to register actions.
* The reason is startup performance. Using the extension points you don't even have to load classes of actions.
* So, all actions are loaded on demand, including classes in classloader.
*/
@Deprecated(message = "Please use CommandOrMotion annotation")
@ScheduledForRemoval(inVersion = "2.9.0")
internal class ActionBeanClass : BaseKeyedLazyInstance<EditorActionHandlerBase>() {
@Attribute("implementation")
var implementation: String? = null
@Attribute("mappingModes")
var modes: String? = null
@Attribute("keys")
var keys: String? = null
val actionId: String get() = implementation?.let { EditorActionHandlerBase.getActionId(it) } ?: ""
fun getParsedKeys(): Set<List<KeyStroke>>? {
val myKeys = keys ?: return null
val escapedKeys = myKeys.splitByComma()
return EditorActionHandlerBase.parseKeysSet(escapedKeys)
}
override fun getImplementationClassName(): String? = implementation
fun getParsedModes(): Set<MappingMode>? {
val myModes = modes ?: return null
if ("ALL" == myModes) return MappingMode.ALL
val res = mutableListOf<MappingMode>()
for (c in myModes) {
when (c) {
'N' -> res += MappingMode.NORMAL
'X' -> res += MappingMode.VISUAL
'V' -> {
res += MappingMode.VISUAL
res += MappingMode.SELECT
}
'S' -> res += MappingMode.SELECT
'O' -> res += MappingMode.OP_PENDING
'I' -> res += MappingMode.INSERT
'C' -> res += MappingMode.CMD_LINE
else -> error("Wrong mapping mode: $c")
}
}
return res.toSet()
}
private fun String.splitByComma(): List<String> {
if (this.isEmpty()) return ArrayList()
val res = SmartList<String>()
var start = 0
var current = 0
while (current < this.length) {
if (this[current] == ',') {
res += this.substring(start, current)
current++
start = current
}
current++
}
res += this.substring(start, current)
return res
}
}

View File

@ -9,42 +9,70 @@
package com.maddyhome.idea.vim.handler package com.maddyhome.idea.vim.handler
import com.intellij.openapi.actionSystem.KeyboardShortcut import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.keymap.Keymap import com.intellij.openapi.keymap.Keymap
import com.intellij.openapi.keymap.KeymapManagerListener import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity import com.intellij.openapi.startup.ProjectActivity
import com.intellij.util.SingleAlarm
import com.jetbrains.rd.util.ConcurrentHashMap import com.jetbrains.rd.util.ConcurrentHashMap
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key import com.maddyhome.idea.vim.api.key
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
// We use alarm with delay to avoid many actions in case many events are fired at the same time // We use alarm with delay to avoid many actions in case many events are fired at the same time
// [VERSION UPDATE] 2023.3+ Replace SingleAlarm with coroutine flows https://youtrack.jetbrains.com/articles/IJPL-A-8/Alarm-Alternative internal val correctorRequester = MutableSharedFlow<Unit>(replay=1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
internal val correctorRequester = SingleAlarm({ correctCopilotKeymap() }, 1_000)
private val LOG = logger<CopilotKeymapCorrector>() private val LOG = logger<CopilotKeymapCorrector>()
internal class CopilotKeymapCorrector : StartupActivity { internal class CopilotKeymapCorrector : ProjectActivity {
override fun runActivity(project: Project) { override suspend fun execute(project: Project) {
correctorRequester.request() project.service<CopilotKeymapCorrectorService>().start()
correctorRequester.emit(Unit)
} }
} }
/**
* At the moment of release 2023.3 there is a problem that starting a coroutine like this
* right in the project activity will block this project activity in tests.
* To avoid that, there is an intermediate service that will allow to avoid this issue.
*
* However, in general we should start this coroutine right in the [CopilotKeymapCorrector]
*/
@OptIn(FlowPreview::class)
@Service(Service.Level.PROJECT)
internal class CopilotKeymapCorrectorService(private val cs: CoroutineScope) {
fun start() {
cs.launch {
correctorRequester
.debounce(5_000)
.collectLatest { correctCopilotKeymap() }
}
}
}
internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener { internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener {
override fun activeKeymapChanged(keymap: Keymap?) { override fun activeKeymapChanged(keymap: Keymap?) {
correctorRequester.request() check(correctorRequester.tryEmit(Unit))
} }
override fun shortcutChanged(keymap: Keymap, actionId: String) { override fun shortcutChanged(keymap: Keymap, actionId: String) {
correctorRequester.request() check(correctorRequester.tryEmit(Unit))
} }
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) { override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
correctorRequester.request() check(correctorRequester.tryEmit(Unit))
} }
} }
@ -67,6 +95,7 @@ private fun correctCopilotKeymap() {
// This is needed to initialize the injector in case this verification is called to fast // This is needed to initialize the injector in case this verification is called to fast
VimPlugin.getInstance() VimPlugin.getInstance()
if (!enableOctopus) return
if (injector.enabler.isEnabled()) { if (injector.enabler.isEnabled()) {
val keymap = KeymapManagerEx.getInstanceEx().activeKeymap val keymap = KeymapManagerEx.getInstanceEx().activeKeymap
val res = keymap.getShortcuts("copilot.disposeInlays") val res = keymap.getShortcuts("copilot.disposeInlays")

View File

@ -34,6 +34,8 @@ internal class EditorHandlersChainLogger : ProjectActivity {
private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler") private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler")
override suspend fun execute(project: Project) { override suspend fun execute(project: Project) {
if (!enableOctopus) return
val escHandlers = editorHandlers.extensionList val escHandlers = editorHandlers.extensionList
.filter { it.action == "EditorEscape" } .filter { it.action == "EditorEscape" }
.joinToString("\n") { it.implementationClass } .joinToString("\n") { it.implementationClass }

View File

@ -87,6 +87,7 @@ private fun verifyKeymap() {
// This is needed to initialize the injector in case this verification is called to fast // This is needed to initialize the injector in case this verification is called to fast
VimPlugin.getInstance() VimPlugin.getInstance()
if (!enableOctopus) return
if (!injector.enabler.isEnabled()) return if (!injector.enabler.isEnabled()) return
val keymap = KeymapManagerEx.getInstanceEx().activeKeymap val keymap = KeymapManagerEx.getInstanceEx().activeKeymap

View File

@ -27,6 +27,7 @@ import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.util.removeUserData import com.intellij.openapi.util.removeUserData
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key import com.maddyhome.idea.vim.api.key
import com.maddyhome.idea.vim.group.IjOptionConstants import com.maddyhome.idea.vim.group.IjOptionConstants
@ -52,7 +53,7 @@ internal val commandContinuation = Key.create<EditorActionHandler>("commandConti
*/ */
internal class CaretShapeEnterEditorHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal class CaretShapeEnterEditorHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (VimPlugin.isEnabled()) { if (VimPlugin.isEnabled() && enableOctopus) {
invokeLater { invokeLater {
editor.updateCaretsVisualAttributes() editor.updateCaretsVisualAttributes()
} }
@ -128,6 +129,7 @@ internal abstract class OctopusHandler(private val nextHandler: EditorActionHand
if (VimPlugin.isNotEnabled()) return false if (VimPlugin.isNotEnabled()) return false
if (!isHandlerEnabled(editor, dataContext)) return false if (!isHandlerEnabled(editor, dataContext)) return false
if (isNotActualKeyPress(dataContext)) return false if (isNotActualKeyPress(dataContext)) return false
if (!enableOctopus) return false
return true return true
} }
@ -242,6 +244,7 @@ internal class VimEscForRiderHandler(nextHandler: EditorActionHandler) : VimKeyH
override val key: String = "<Esc>" override val key: String = "<Esc>"
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean { override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
if (!enableOctopus) return false
return LookupManager.getActiveLookup(editor) != null return LookupManager.getActiveLookup(editor) != null
} }
} }
@ -257,7 +260,9 @@ internal class VimEscForRiderHandler(nextHandler: EditorActionHandler) : VimKeyH
*/ */
internal class VimEscLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal class VimEscLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (enableOctopus) {
LOG.info("Esc pressed") LOG.info("Esc pressed")
}
nextHandler.execute(editor, caret, dataContext) nextHandler.execute(editor, caret, dataContext)
} }
@ -283,7 +288,9 @@ internal class StartNewLineBeforeCurrentDetector(nextHandler: EditorActionHandle
internal open class StartNewLineDetectorBase(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal open class StartNewLineDetectorBase(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (enableOctopus) {
DataManager.getInstance().saveInDataContext(dataContext, Util.key, true) DataManager.getInstance().saveInDataContext(dataContext, Util.key, true)
}
nextHandler.execute(editor, caret, dataContext) nextHandler.execute(editor, caret, dataContext)
} }
@ -311,7 +318,9 @@ internal open class StartNewLineDetectorBase(private val nextHandler: EditorActi
*/ */
internal class VimEnterLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal class VimEnterLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (enableOctopus) {
LOG.info("Enter pressed") LOG.info("Enter pressed")
}
nextHandler.execute(editor, caret, dataContext) nextHandler.execute(editor, caret, dataContext)
} }
@ -341,6 +350,7 @@ internal abstract class VimKeyHandler(nextHandler: EditorActionHandler?) : Octop
} }
internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean { internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
if (!enableOctopus) return false
// CMD line has a different processing mechanizm: the processing actions are registered // CMD line has a different processing mechanizm: the processing actions are registered
// for the input field component. These keys are not dispatched via the octopus handler. // for the input field component. These keys are not dispatched via the octopus handler.
if (editor.vim.mode is Mode.CMD_LINE) return false if (editor.vim.mode is Mode.CMD_LINE) return false
@ -350,3 +360,6 @@ internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
} }
return false return false
} }
internal val enableOctopus: Boolean
get() = injector.globalOptions().octopushandler

View File

@ -8,6 +8,7 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.CaretVisualAttributes import com.intellij.openapi.editor.CaretVisualAttributes
@ -85,7 +86,10 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this) caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking // 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
if (!ApplicationManager.getApplication().isUnitTestMode) {
(this as? EditorEx)?.setCaretVisible(true) (this as? EditorEx)?.setCaretVisible(true)
}
} }
private fun Editor.updateSecondaryCaretsVisualAttributes() { private fun Editor.updateSecondaryCaretsVisualAttributes() {

View File

@ -46,12 +46,6 @@ public class EditorHelper {
return editor.getScrollingModel().getVisibleAreaOnScrollingFinished(); return editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
} }
//("Use extension function with the same name on VimEditor")
@Deprecated
public static boolean isLineEmpty(final @NotNull Editor editor, final int line, final boolean allowBlanks) {
return EngineEditorHelperKt.isLineEmpty(new IjVimEditor(editor), line, allowBlanks);
}
public static boolean scrollVertically(@NotNull Editor editor, int verticalOffset) { public static boolean scrollVertically(@NotNull Editor editor, int verticalOffset) {
final ScrollingModel scrollingModel = editor.getScrollingModel(); final ScrollingModel scrollingModel = editor.getScrollingModel();
final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished(); final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished();

View File

@ -23,43 +23,6 @@ import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.inBlockSelection import com.maddyhome.idea.vim.state.mode.inBlockSelection
import java.util.stream.Collectors import java.util.stream.Collectors
/**
* This annotation is created for test functions (methods).
* It means that the original vim behavior has small differences from behavior of IdeaVim.
* [shouldBeFixed] flag indicates whether the given functionality should be fixed
* or the given behavior is normal for IdeaVim and should be leaved as is.
*
* E.g. after execution of some commands original vim has the following text:
* Hello1
* Hello2
* Hello3
*
* But IdeaVim gives you:
* Hello1
*
* Hello2
* Hello3
*
* In this case you should still create the test function and mark this function with [VimBehaviorDiffers] annotation.
*
* Why does this annotation exist?
* After creating some functionality you can understand that IdeaVim has a bit different behavior, but you
* cannot fix it right now because of any reason (bugs in IDE,
* the impossibility of this functionality in IDEA (*[shouldBeFixed] == false*), leak of time for fixing).
* In that case, you should NOT remove the corresponding test or leave it without any marks that this test
* not fully convenient with vim, but leave the test with IdeaVim's behavior and put this annotation
* with description of how original vim works.
*
* Note that using this annotation should be avoided as much as possible and behavior of IdeaVim should be as close
* to vim as possible.
*/
@Target(AnnotationTarget.FUNCTION)
internal annotation class VimBehaviorDiffers(
val originalVimAfter: String = "",
val description: String = "",
val shouldBeFixed: Boolean = true,
)
internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b
// TODO Should be replaced with VimEditor.carets() // TODO Should be replaced with VimEditor.carets()

View File

@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.helper; package com.maddyhome.idea.vim.helper;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.lang.CodeDocumentationAwareCommenter; import com.intellij.lang.CodeDocumentationAwareCommenter;
import com.intellij.lang.Commenter; import com.intellij.lang.Commenter;
import com.intellij.lang.Language; import com.intellij.lang.Language;
@ -16,22 +17,28 @@ import com.intellij.lang.LanguageCommenters;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiComment; import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt; import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.api.VimEditor; import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.state.mode.Mode;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.common.CharacterPosition; import com.maddyhome.idea.vim.common.CharacterPosition;
import com.maddyhome.idea.vim.common.Direction; import com.maddyhome.idea.vim.common.Direction;
import com.maddyhome.idea.vim.common.TextRange; import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.newapi.IjVimCaret; import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.regexp.CharPointer; import com.maddyhome.idea.vim.regexp.*;
import com.maddyhome.idea.vim.regexp.RegExp; import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.state.mode.Mode;
import it.unimi.dsi.fastutil.ints.IntComparator;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import kotlin.Pair; import kotlin.Pair;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -42,10 +49,10 @@ import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector; import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.checkInString; import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.checkInString;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase; import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
/** /**
* Helper methods for searching text * Helper methods for searching text
@ -59,6 +66,8 @@ public class SearchHelper {
/** /**
* Find text matching the given pattern. * Find text matching the given pattern.
* *
* @deprecated Use IjVimSearchHelper.findPattern instead
*
* <p>See search.c:searchit</p> * <p>See search.c:searchit</p>
* *
* @param editor The editor to search in * @param editor The editor to search in
@ -69,12 +78,13 @@ public class SearchHelper {
* @return A TextRange representing the result, or null * @return A TextRange representing the result, or null
*/ */
@Nullable @Nullable
@Deprecated
public static TextRange findPattern(@NotNull Editor editor, public static TextRange findPattern(@NotNull Editor editor,
@Nullable String pattern, @Nullable String pattern,
int startOffset, int startOffset,
int count, int count,
EnumSet<SearchOptions> searchOptions) { EnumSet<SearchOptions> searchOptions) {
if (pattern == null || pattern.length() == 0) { if (pattern == null || pattern.isEmpty()) {
logger.warn("Pattern is null or empty. Cannot perform search"); logger.warn("Pattern is null or empty. Cannot perform search");
return null; return null;
} }
@ -347,6 +357,8 @@ public class SearchHelper {
/** /**
* Find all occurrences of the pattern. * Find all occurrences of the pattern.
* *
* @deprecated Use IjVimSearchHelper.findall instead
*
* @param editor The editor to search in * @param editor The editor to search in
* @param pattern The pattern to search for * @param pattern The pattern to search for
* @param startLine The start line of the range to search for * @param startLine The start line of the range to search for
@ -354,12 +366,33 @@ public class SearchHelper {
* @param ignoreCase Case sensitive or insensitive searching * @param ignoreCase Case sensitive or insensitive searching
* @return A list of TextRange objects representing the results * @return A list of TextRange objects representing the results
*/ */
@Deprecated
public static @NotNull List<TextRange> findAll(@NotNull Editor editor, public static @NotNull List<TextRange> findAll(@NotNull Editor editor,
@NotNull String pattern, @NotNull String pattern,
int startLine, int startLine,
int endLine, int endLine,
boolean ignoreCase) { boolean ignoreCase) {
final List<TextRange> results = Lists.newArrayList(); final List<TextRange> results = Lists.newArrayList();
if (globalIjOptions(injector).getUseNewRegex()) {
final List<VimRegexOptions> options = new ArrayList<>();
if (globalOptions(injector).getSmartcase()) options.add(VimRegexOptions.SMART_CASE);
if (globalOptions(injector).getIgnorecase()) options.add(VimRegexOptions.IGNORE_CASE);
VimEditor vimEditor = new IjVimEditor(editor);
try {
// TODO: we shouldn't care about the ignoreCase argument, and instead just look into the editor options.
// It would require a refactor, so for now prepend \c or \C to "force" ignoreCase
String newPattern = (ignoreCase ? "\\c" : "\\C") + pattern;
VimRegex regex = new VimRegex(newPattern);
List<VimMatchResult.Success> foundMatches = regex.findAll(vimEditor, vimEditor.getLineStartOffset(startLine), vimEditor.getLineEndOffset(endLine == -1 ? vimEditor.lineCount() - 1 : endLine) + 1, options);
for (VimMatchResult.Success match : foundMatches) results.add(match.getRange());
return results;
} catch (VimRegexException e) {
injector.getMessages().showStatusBarMessage(vimEditor, e.getMessage());
return results;
}
}
final int lineCount = new IjVimEditor(editor).lineCount(); final int lineCount = new IjVimEditor(editor).lineCount();
final int actualEndLine = endLine == -1 ? lineCount - 1 : endLine; final int actualEndLine = endLine == -1 ? lineCount - 1 : endLine;
@ -402,6 +435,10 @@ public class SearchHelper {
return results; return results;
} }
/**
* @deprecated Use IjVimSearchHelper.findSection instead
*/
@Deprecated
public static int findSection(@NotNull Editor editor, @NotNull Caret caret, char type, int dir, int count) { public static int findSection(@NotNull Editor editor, @NotNull Caret caret, char type, int dir, int count) {
CharSequence chars = editor.getDocument().getCharsSequence(); CharSequence chars = editor.getDocument().getCharsSequence();
int line = caret.getLogicalPosition().line + dir; int line = caret.getLogicalPosition().line + dir;
@ -428,6 +465,10 @@ public class SearchHelper {
return res; return res;
} }
/**
* @deprecated Use IjVimSearchHelper.findUnmatchedBlock instead
*/
@Deprecated
public static int findUnmatchedBlock(@NotNull Editor editor, @NotNull Caret caret, char type, int count) { public static int findUnmatchedBlock(@NotNull Editor editor, @NotNull Caret caret, char type, int count) {
CharSequence chars = editor.getDocument().getCharsSequence(); CharSequence chars = editor.getDocument().getCharsSequence();
int pos = caret.getOffset(); int pos = caret.getOffset();
@ -447,6 +488,8 @@ public class SearchHelper {
/** /**
* Find block enclosing the caret * Find block enclosing the caret
* *
* @deprecated Use IjVimSearchHelper.findBlockRange instead
*
* @param editor The editor to search in * @param editor The editor to search in
* @param caret The caret currently at * @param caret The caret currently at
* @param type The type of block, e.g. (, [, {, < * @param type The type of block, e.g. (, [, {, <
@ -798,6 +841,10 @@ public class SearchHelper {
} }
/**
* @deprecated Use IjVimSearchHelper.findBlockTagRange instead
*/
@Deprecated
public static @Nullable TextRange findBlockTagRange(@NotNull Editor editor, public static @Nullable TextRange findBlockTagRange(@NotNull Editor editor,
@NotNull Caret caret, @NotNull Caret caret,
int count, int count,
@ -1330,6 +1377,10 @@ public class SearchHelper {
return new TextRange(start, end); return new TextRange(start, end);
} }
/**
* @deprecated Use IjVimSearchHelper.findWordUnderCursor instead
*/
@Deprecated
@Contract("_, _, _, _, _, _, _ -> new") @Contract("_, _, _, _, _, _, _ -> new")
public static @NotNull TextRange findWordUnderCursor(@NotNull Editor editor, public static @NotNull TextRange findWordUnderCursor(@NotNull Editor editor,
@NotNull Caret caret, @NotNull Caret caret,
@ -1515,14 +1566,56 @@ public class SearchHelper {
} }
} }
/**
* @deprecated Use IjVimSearchHelper.findMethodStart instead
*/
public static int findMethodStart(@NotNull Editor editor, @NotNull Caret caret, int count) { public static int findMethodStart(@NotNull Editor editor, @NotNull Caret caret, int count) {
return PsiHelper.findMethodStart(editor, caret.getOffset(), count); return PsiHelper.findMethodStart(editor, caret.getOffset(), count);
} }
/**
* @deprecated Use IjVimSearchHelper.findMethodEnd instead
*/
public static int findMethodEnd(@NotNull Editor editor, @NotNull Caret caret, int count) { public static int findMethodEnd(@NotNull Editor editor, @NotNull Caret caret, int count) {
return PsiHelper.findMethodEnd(editor, caret.getOffset(), count); return PsiHelper.findMethodEnd(editor, caret.getOffset(), count);
} }
public static int findMisspelledWords(@NotNull Editor editor,
int startOffset,
int endOffset,
int skipCount,
IntComparator offsetOrdering) {
Project project = editor.getProject();
if (project == null) {
return -1;
}
IntSortedSet offsets = new IntRBTreeSet(offsetOrdering);
DaemonCodeAnalyzerEx.processHighlights(editor.getDocument(), project, SpellCheckerSeveritiesProvider.TYPO,
startOffset, endOffset, highlight -> {
if (highlight.getSeverity() == SpellCheckerSeveritiesProvider.TYPO) {
int offset = highlight.getStartOffset();
if (offset >= startOffset && offset <= endOffset) {
offsets.add(offset);
}
}
return true;
});
if (offsets.isEmpty()) {
return -1;
}
if (skipCount >= offsets.size()) {
return offsets.lastInt();
}
else {
IntIterator offsetIterator = offsets.iterator();
offsetIterator.skip(skipCount);
return offsetIterator.nextInt();
}
}
private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) { private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) {
List<String> pairs = options(injector, vimEditor).getMatchpairs(); List<String> pairs = options(injector, vimEditor).getMatchpairs();
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();

View File

@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.options import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ranges.LineRange import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Contract
import java.awt.Color import java.awt.Color
@ -105,7 +106,7 @@ private fun updateSearchHighlights(
val startLine = searchRange?.startLine ?: 0 val startLine = searchRange?.startLine ?: 0
val endLine = searchRange?.endLine ?: -1 val endLine = searchRange?.endLine ?: -1
val results = val results =
SearchHelper.findAll(editor, pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase)) injector.searchHelper.findAll(IjVimEditor(editor), pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase))
if (results.isNotEmpty()) { if (results.isNotEmpty()) {
currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards) currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards)
highlightSearchResults(editor, pattern, results, currentMatchOffset) highlightSearchResults(editor, pattern, results, currentMatchOffset)
@ -119,7 +120,7 @@ private fun updateSearchHighlights(
} }
if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE) if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE)
if (!forwards) searchOptions.add(SearchOptions.BACKWARDS) if (!forwards) searchOptions.add(SearchOptions.BACKWARDS)
val result = SearchHelper.findPattern(editor, pattern, initialOffset, 1, searchOptions) val result = injector.searchHelper.findPattern(IjVimEditor(editor), pattern, initialOffset, 1, searchOptions)
if (result != null) { if (result != null) {
currentMatchOffset = result.startOffset currentMatchOffset = result.startOffset
val results = listOf(result) val results = listOf(result)

View File

@ -33,8 +33,6 @@ public object StringHelper {
} }
@JvmStatic @JvmStatic
@Deprecated("Use KeyStroke.isCloseKeyStroke", ReplaceWith("stroke.isCloseKeyStroke()")) @Deprecated("Use key.isCloseKeyStroke()", ReplaceWith("key.isCloseKeyStroke()"))
public fun isCloseKeyStroke(stroke: KeyStroke): Boolean { public fun isCloseKeyStroke(key: KeyStroke): Boolean = key.isCloseKeyStroke()
return stroke.isCloseKeyStroke()
}
} }

View File

@ -11,14 +11,15 @@ import com.google.common.collect.Lists
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import javax.swing.KeyStroke import javax.swing.KeyStroke
internal class TestInputModel private constructor() { // Do not remove until it's used in EasyMotion plugin in tests
public class TestInputModel private constructor() {
private val myKeyStrokes: MutableList<KeyStroke> = Lists.newArrayList() private val myKeyStrokes: MutableList<KeyStroke> = Lists.newArrayList()
fun setKeyStrokes(keyStrokes: List<KeyStroke>) { public fun setKeyStrokes(keyStrokes: List<KeyStroke>) {
myKeyStrokes.clear() myKeyStrokes.clear()
myKeyStrokes.addAll(keyStrokes) myKeyStrokes.addAll(keyStrokes)
} }
fun nextKeyStroke(): KeyStroke? { public fun nextKeyStroke(): KeyStroke? {
// Return key from the unfinished mapping // Return key from the unfinished mapping
/* /*
MappingStack mappingStack = KeyHandler.getInstance().getMappingStack(); MappingStack mappingStack = KeyHandler.getInstance().getMappingStack();
@ -33,9 +34,9 @@ if (mappingStack.hasStroke()) {
} }
} }
companion object { public companion object {
@JvmStatic @JvmStatic
fun getInstance(editor: Editor): TestInputModel { public fun getInstance(editor: Editor): TestInputModel {
var model = editor.vimTestInputModel var model = editor.vimTestInputModel
if (model == null) { if (model == null) {
model = TestInputModel() model = TestInputModel()

View File

@ -21,6 +21,6 @@ public final class VimIcons {
public static final @NotNull Icon YOUTRACK = load("/icons/youtrack.svg"); public static final @NotNull Icon YOUTRACK = load("/icons/youtrack.svg");
private static @NotNull Icon load(@NotNull @NonNls String path) { private static @NotNull Icon load(@NotNull @NonNls String path) {
return IconManager.getInstance().getIcon(path, VimIcons.class); return IconManager.getInstance().getIcon(path, VimIcons.class.getClassLoader());
} }
} }

View File

@ -9,11 +9,19 @@
package com.maddyhome.idea.vim.inspections package com.maddyhome.idea.vim.inspections
import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.PsiEditorUtil
import com.maddyhome.idea.vim.extension.ExtensionBeanClass
import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.vimscript.model.commands.SetCommand
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
internal class UsePlugSyntaxInspection : LocalInspectionTool() { internal class UsePlugSyntaxInspection : LocalInspectionTool() {
override fun getGroupDisplayName(): String { override fun getGroupDisplayName(): String {
@ -23,11 +31,54 @@ internal class UsePlugSyntaxInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
val file = holder.file val file = holder.file
if (file.name != ".ideavimrc" && file.name != "_ideavimrc") return PsiElementVisitor.EMPTY_VISITOR if (file.name != ".ideavimrc" && file.name != "_ideavimrc") return PsiElementVisitor.EMPTY_VISITOR
val plugins = buildPlugins()
return object : PsiElementVisitor() { return object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) { override fun visitElement(element: PsiElement) {
if (element !is LeafPsiElement) return if (element !is LeafPsiElement) return
holder.registerProblem(element, TextRange.create(10, 20), "Hi there") val myScript = VimscriptParser.parse(element.text)
myScript.units.forEach { unit ->
if (unit is SetCommand) {
val argument = unit.argument
val alias = plugins[argument]
if (alias != null) {
holder.registerProblem(
element,
unit.rangeInScript.let { TextRange(it.startOffset, it.endOffset - 1) },
"""
Use `Plug` syntax for defining extensions
""".trimIndent(),
object : LocalQuickFix {
override fun getFamilyName(): String {
return "Use Plug syntax"
}
override fun applyFix(p0: Project, p1: ProblemDescriptor) {
val editor = PsiEditorUtil.findEditor(file)
editor?.document?.replaceString(
unit.rangeInScript.startOffset,
unit.rangeInScript.endOffset - 1,
"Plug '$alias'"
)
} }
} }
)
}
}
}
}
}
}
private fun buildPlugins(): HashMap<String, String> {
val res = HashMap<String, String>()
VimExtension.EP_NAME.extensions.forEach { extension: ExtensionBeanClass ->
val alias = extension.aliases?.first { it.name?.count { it == '/' } == 1 }?.name
?: extension.aliases?.firstOrNull()?.name
val name = extension.name
if (alias != null && name != null) {
res[name] = alias
}
}
return res
} }
} }

View File

@ -8,7 +8,7 @@
package com.maddyhome.idea.vim.listener package com.maddyhome.idea.vim.listener
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import org.acejump.session.SessionManager import org.acejump.session.SessionManager
@ -16,12 +16,11 @@ import org.acejump.session.SessionManager
* Key handling for IdeaVim should be updated to editorHandler usage. In this case this class can be safely removed. * Key handling for IdeaVim should be updated to editorHandler usage. In this case this class can be safely removed.
*/ */
@Suppress("DEPRECATION")
internal interface AceJumpService { internal interface AceJumpService {
fun isActive(editor: Editor): Boolean fun isActive(editor: Editor): Boolean
companion object { companion object {
fun getInstance(): AceJumpService? = ServiceManager.getService(AceJumpService::class.java) fun getInstance(): AceJumpService? = ApplicationManager.getApplication().getService(AceJumpService::class.java)
} }
} }

View File

@ -55,6 +55,7 @@ import com.maddyhome.idea.vim.VimTypedActionHandler
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
import com.maddyhome.idea.vim.api.Options import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.coerceOffset
import com.maddyhome.idea.vim.api.getLineEndForOffset import com.maddyhome.idea.vim.api.getLineEndForOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
@ -65,11 +66,9 @@ import com.maddyhome.idea.vim.group.IjOptions
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.OptionGroup import com.maddyhome.idea.vim.group.OptionGroup
import com.maddyhome.idea.vim.group.ScrollGroup import com.maddyhome.idea.vim.group.ScrollGroup
import com.maddyhome.idea.vim.group.SearchGroup
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.group.visual.VimVisualTimer import com.maddyhome.idea.vim.group.visual.VimVisualTimer
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.correctorRequester import com.maddyhome.idea.vim.handler.correctorRequester
import com.maddyhome.idea.vim.handler.keyCheckRequests import com.maddyhome.idea.vim.handler.keyCheckRequests
import com.maddyhome.idea.vim.helper.GuicursorChangeListener import com.maddyhome.idea.vim.helper.GuicursorChangeListener
@ -133,14 +132,14 @@ internal object VimListenerManager {
fun turnOn() { fun turnOn() {
GlobalListeners.enable() GlobalListeners.enable()
EditorListeners.addAll() EditorListeners.addAll()
correctorRequester.request() check(correctorRequester.tryEmit(Unit))
check(keyCheckRequests.tryEmit(Unit)) check(keyCheckRequests.tryEmit(Unit))
} }
fun turnOff() { fun turnOff() {
GlobalListeners.disable() GlobalListeners.disable()
EditorListeners.removeAll() EditorListeners.removeAll()
correctorRequester.request() check(correctorRequester.tryEmit(Unit))
} }
object GlobalListeners { object GlobalListeners {
@ -306,7 +305,7 @@ internal object VimListenerManager {
if (VimPlugin.isNotEnabled()) return if (VimPlugin.isNotEnabled()) return
MotionGroup.fileEditorManagerSelectionChangedCallback(event) MotionGroup.fileEditorManagerSelectionChangedCallback(event)
FileGroup.fileEditorManagerSelectionChangedCallback(event) FileGroup.fileEditorManagerSelectionChangedCallback(event)
SearchGroup.fileEditorManagerSelectionChangedCallback(event) VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event)
OptionGroup.fileEditorManagerSelectionChangedCallback(event) OptionGroup.fileEditorManagerSelectionChangedCallback(event)
} }
} }
@ -432,8 +431,6 @@ internal object VimListenerManager {
} }
private object EditorSelectionHandler : SelectionListener { private object EditorSelectionHandler : SelectionListener {
private var myMakingChanges = false
/** /**
* This event is executed for each caret using [com.intellij.openapi.editor.CaretModel.runForEachCaret] * This event is executed for each caret using [com.intellij.openapi.editor.CaretModel.runForEachCaret]
*/ */
@ -462,11 +459,17 @@ internal object VimListenerManager {
if (lineEnd == endOffset - 1) { if (lineEnd == endOffset - 1) {
// When starting on an empty line and dragging vertically upwards onto // When starting on an empty line and dragging vertically upwards onto
// another line, the selection should include the entirety of the empty line // another line, the selection should include the entirety of the empty line
caret.setSelection(endOffset + 1, startOffset) caret.setSelection(
ijVimEditor.coerceOffset(endOffset + 1).point,
ijVimEditor.coerceOffset(startOffset).point,
)
} else if (lineEnd == startOffset + 1 && startOffset == endOffset) { } else if (lineEnd == startOffset + 1 && startOffset == endOffset) {
// When dragging left from EOL on a non-empty line, the selection // When dragging left from EOL on a non-empty line, the selection
// should include the last character on the line // should include the last character on the line
caret.setSelection(lineEnd, lineEnd - 1) caret.setSelection(
ijVimEditor.coerceOffset(lineEnd).point,
ijVimEditor.coerceOffset(lineEnd - 1).point,
)
} }
} }
//endregion //endregion
@ -476,22 +479,9 @@ internal object VimListenerManager {
IdeaSelectionControl.controlNonVimSelectionChange(editor) IdeaSelectionControl.controlNonVimSelectionChange(editor)
} }
if (myMakingChanges || document is DocumentEx && document.isInEventsHandling) { if (document is DocumentEx && document.isInEventsHandling) {
return return
} }
myMakingChanges = true
try {
// Synchronize selections between editors
val newRange = selectionEvent.newRange
for (e in localEditors(document)) {
if (e != editor) {
e.selectionModel.vimSetSystemSelectionSilently(newRange.startOffset, newRange.endOffset)
}
}
} finally {
myMakingChanges = false
}
} }
} }

View File

@ -1,23 +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.newapi
import com.maddyhome.idea.vim.api.VimActionsInitiator
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import org.jetbrains.annotations.ApiStatus
@Deprecated(message = "Please use CommandOrMotion annotation")
@ApiStatus.ScheduledForRemoval(inVersion = "2.9.0")
internal class IjVimActionsInitiator(val bean: ActionBeanClass) : VimActionsInitiator {
override fun getInstance(): EditorActionHandlerBase = bean.instance
}
internal val VimActionsInitiator.ij: ActionBeanClass
get() = (this as IjVimActionsInitiator).bean

View File

@ -43,6 +43,10 @@ internal class IjVimApplication : VimApplicationBase() {
return ApplicationManager.getApplication().isUnitTestMode return ApplicationManager.getApplication().isUnitTestMode
} }
override fun isInternal(): Boolean {
return ApplicationManager.getApplication().isInternal
}
override fun postKey(stroke: KeyStroke, editor: VimEditor) { override fun postKey(stroke: KeyStroke, editor: VimEditor) {
val component: Component = SwingUtilities.getAncestorOfClass(Window::class.java, editor.ij.component) val component: Component = SwingUtilities.getAncestorOfClass(Window::class.java, editor.ij.component)
val event = createKeyEvent(stroke, component) val event = createKeyEvent(stroke, component)

View File

@ -0,0 +1,181 @@
/*
* 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.newapi
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Ref
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimSearchGroupBase
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.TestInputModel.Companion.getInstance
import com.maddyhome.idea.vim.helper.addSubstitutionConfirmationHighlight
import com.maddyhome.idea.vim.helper.highlightSearchResults
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
import com.maddyhome.idea.vim.helper.shouldIgnoreCase
import com.maddyhome.idea.vim.helper.updateSearchHighlights
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
import com.maddyhome.idea.vim.ui.ModalEntry
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
import com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser.parseExpression
import org.jetbrains.annotations.TestOnly
import javax.swing.KeyStroke
public open class IjVimSearchGroup : VimSearchGroupBase() {
init {
// TODO: Investigate migrating these listeners to use the effective value change listener
// This would allow us to update the editor we're told to update, rather than looping over all projects and updating
// the highlights in that project's current document's open editors (see VIM-2779).
// However, we probably only want to update the editors associated with the current document, so maybe the whole
// code needs to be reworked. We're currently using the same update code for changes in the search term as well as
// changes in the search options.
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.hlsearch) {
resetSearchHighlight()
updateSearchHighlights(true)
}
val updateHighlightsIfVisible = GlobalOptionChangeListener {
if (showSearchHighlight) {
updateSearchHighlights(true)
}
}
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible)
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible)
}
private var showSearchHighlight: Boolean = injector.globalOptions().hlsearch
override fun highlightSearchLines(
editor: VimEditor,
startLine: Int,
endLine: Int,
) {
val pattern = getLastUsedPattern()
if (pattern != null) {
val results = injector.searchHelper.findAll(
editor, pattern, startLine, endLine,
shouldIgnoreCase(pattern, lastIgnoreSmartCase)
)
highlightSearchResults(editor.ij, pattern, results, -1)
}
}
override fun updateSearchHighlights(force: Boolean) {
updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, force)
}
override fun resetIncsearchHighlights() {
updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true)
}
override fun confirmChoice(
editor: VimEditor,
match: String,
caret: VimCaret,
startOffset: Int,
): ReplaceConfirmationChoice {
val result: Ref<ReplaceConfirmationChoice> = Ref.create(ReplaceConfirmationChoice.QUIT)
val keyStrokeProcessor: Function1<KeyStroke, Boolean> = label@{ key: KeyStroke ->
val choice: ReplaceConfirmationChoice
val c = key.keyChar
choice = if (key.isCloseKeyStroke() || c == 'q') {
ReplaceConfirmationChoice.QUIT
} else if (c == 'y') {
ReplaceConfirmationChoice.SUBSTITUTE_THIS
} else if (c == 'l') {
ReplaceConfirmationChoice.SUBSTITUTE_LAST
} else if (c == 'n') {
ReplaceConfirmationChoice.SKIP
} else if (c == 'a') {
ReplaceConfirmationChoice.SUBSTITUTE_ALL
} else {
return@label true
}
// TODO: Handle <C-E> and <C-Y>
result.set(choice)
false
}
if (ApplicationManager.getApplication().isUnitTestMode) {
caret.moveToOffset(startOffset)
val inputModel = getInstance(editor.ij)
var key = inputModel.nextKeyStroke()
while (key != null) {
if (!keyStrokeProcessor.invoke(key)) {
break
}
key = inputModel.nextKeyStroke()
}
} else {
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
val exEntryPanel: com.maddyhome.idea.vim.ui.ex.ExEntryPanel =
com.maddyhome.idea.vim.ui.ex.ExEntryPanel.getInstanceWithoutShortcuts()
val context = injector.executionContextManager.onEditor(editor, null)
exEntryPanel.activate(
editor.ij,
(context as IjEditorExecutionContext).context,
MessageHelper.message("replace.with.0", match),
"",
1
)
caret.moveToOffset(startOffset)
ModalEntry.activate(editor, keyStrokeProcessor)
exEntryPanel.deactivate(true, false)
}
return result.get()
}
override fun parseVimScriptExpression(expressionString: String): Expression? {
return parseExpression(expressionString)
}
override fun addSubstitutionConfirmationHighlight(editor: VimEditor, startOffset: Int, endOffset: Int) {
val hl = addSubstitutionConfirmationHighlight(
(editor as IjVimEditor).editor,
startOffset,
endOffset
)
editor.editor.markupModel.removeHighlighter(hl)
}
override fun setLatestMatch(match: String) {
SubmatchFunctionHandler.getInstance().latestMatch = match
}
override fun replaceString(
editor: VimEditor,
startOffset: Int,
endOffset: Int,
newString: String,
) {
ApplicationManager.getApplication().runWriteAction {
(editor as IjVimEditor).editor.document.replaceString(startOffset, endOffset, newString)
}
}
@TestOnly
override fun resetState() {
super.resetState()
showSearchHighlight = injector.globalOptions().hlsearch
}
override fun resetSearchHighlight() {
showSearchHighlight = injector.globalOptions().hlsearch
}
override fun clearSearchHighlight() {
showSearchHighlight = false
updateSearchHighlights(false)
}
}

View File

@ -9,49 +9,146 @@
package com.maddyhome.idea.vim.newapi package com.maddyhome.idea.vim.newapi
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.Logger
import com.maddyhome.idea.vim.api.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimSearchHelperBase import com.maddyhome.idea.vim.api.VimSearchHelperBase
import com.maddyhome.idea.vim.api.anyNonWhitespace
import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.normalizeOffset
import com.maddyhome.idea.vim.common.Direction
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
import com.maddyhome.idea.vim.helper.PsiHelper
import com.maddyhome.idea.vim.helper.SearchHelper import com.maddyhome.idea.vim.helper.SearchHelper
import com.maddyhome.idea.vim.helper.SearchOptions import com.maddyhome.idea.vim.helper.SearchOptions
import com.maddyhome.idea.vim.helper.checkInString
import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
import it.unimi.dsi.fastutil.ints.IntComparator
import it.unimi.dsi.fastutil.ints.IntComparators
import java.util.* import java.util.*
import java.util.function.Function
import java.util.regex.Pattern
import kotlin.math.abs
import kotlin.math.max
@Service @Service
internal class IjVimSearchHelper : VimSearchHelperBase() { internal class IjVimSearchHelper : VimSearchHelperBase() {
override fun findSection(editor: VimEditor, caret: ImmutableVimCaret, type: Char, dir: Int, count: Int): Int {
return SearchHelper.findSection( companion object {
(editor as IjVimEditor).editor, private const val BLOCK_CHARS = "{}()[]<>"
(caret as IjVimCaret).caret, private val logger = Logger.getInstance(IjVimSearchHelper::class.java.name)
type, }
dir, override fun findSection(
count, editor: VimEditor,
caret: ImmutableVimCaret,
type: Char,
direction: Int,
count: Int,
) )
: Int {
val documentText: CharSequence = editor.ij.document.charsSequence
var currentLine: Int = caret.ij.logicalPosition.line + direction
var resultOffset = -1
var remainingTargets = count
while (currentLine in 1 until editor.lineCount() && remainingTargets > 0) {
val lineStartOffset = editor.getLineStartOffset(currentLine)
if (lineStartOffset < documentText.length) {
val currentChar = documentText[lineStartOffset]
if (currentChar == type || currentChar == '\u000C') {
resultOffset = lineStartOffset
remainingTargets--
}
}
currentLine += direction
}
if (resultOffset == -1) {
resultOffset = if (direction < 0) 0 else documentText.length - 1
}
return resultOffset
} }
override fun findMethodEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int { override fun findMethodEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
return SearchHelper.findMethodEnd( return PsiHelper.findMethodEnd(editor.ij, caret.ij.offset, count)
(editor as IjVimEditor).editor,
(caret as IjVimCaret).caret,
count,
)
} }
override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int { override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
return SearchHelper.findMethodStart( return PsiHelper.findMethodStart(editor.ij, caret.ij.offset, count)
(editor as IjVimEditor).editor,
(caret as IjVimCaret).caret,
count,
)
} }
override fun findUnmatchedBlock(editor: VimEditor, caret: ImmutableVimCaret, type: Char, count: Int): Int { override fun findUnmatchedBlock(editor: VimEditor, caret: ImmutableVimCaret, type: Char, count: Int): Int {
return SearchHelper.findUnmatchedBlock( val chars: CharSequence = editor.ij.document.charsSequence
(editor as IjVimEditor).editor, var pos: Int = caret.ij.offset
(caret as IjVimCaret).caret, val loc = BLOCK_CHARS.indexOf(type)
type, // What direction should we go now (-1 is backward, 1 is forward)
count, val dir = if (loc % 2 == 0) Direction.BACKWARDS else Direction.FORWARDS
) // Which character did we find and which should we now search for
val match = BLOCK_CHARS[loc]
val found = BLOCK_CHARS[loc - dir.toInt()]
if (pos < chars.length && chars[pos] == type) {
pos += dir.toInt()
}
return findBlockLocation(chars, found, match, dir, pos, count)
}
private fun findBlockLocation(
chars: CharSequence,
found: Char,
match: Char,
dir: Direction,
pos: Int,
cnt: Int,
): Int {
var position = pos
var count = cnt
var res = -1
val initialPos = position
val initialInString = checkInString(chars, position, true)
val inCheckPosF =
Function { x: Int -> if (dir === Direction.BACKWARDS && x > 0) x - 1 else x + 1 }
val inCheckPos = inCheckPosF.apply(position)
var inString = checkInString(chars, inCheckPos, true)
var inChar = checkInString(chars, inCheckPos, false)
var stack = 0
// Search to start or end of file, as appropriate
val charsToSearch: Set<Char> = HashSet(listOf('\'', '"', '\n', match, found))
while (position >= 0 && position < chars.length && count > 0) {
val (c, second) = SearchHelper.findPositionOfFirstCharacter(chars, position, charsToSearch, true, dir) ?: return -1
position = second
// If we found a match and we're not in a string...
if (c == match && (!inString) && !inChar) {
// We found our match
if (stack == 0) {
res = position
count--
} else {
stack--
}
} else if (c == '\n') {
inString = false
inChar = false
} else if (position != initialPos) {
// We found another character like our original - belongs to another pair
if (!inString && !inChar && c == found) {
stack++
} else if (!inChar) {
inString = checkInString(chars, inCheckPosF.apply(position), true)
} else if (!inString) {
inChar = checkInString(chars, inCheckPosF.apply(position), false)
}
}
position += dir.toInt()
}
return res
} }
override fun findPattern( override fun findPattern(
@ -61,11 +158,44 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
count: Int, count: Int,
searchOptions: EnumSet<SearchOptions>?, searchOptions: EnumSet<SearchOptions>?,
): TextRange? { ): TextRange? {
return SearchHelper.findPattern(editor.ij, pattern, startOffset, count, searchOptions) return if (injector.globalIjOptions().useNewRegex) super.findPattern(editor, pattern, startOffset, count, searchOptions)
else SearchHelper.findPattern(editor.ij, pattern, startOffset, count, searchOptions)
}
override fun findAll(
editor: VimEditor,
pattern: String,
startLine: Int,
endLine: Int,
ignoreCase: Boolean,
): List<TextRange> {
return if (injector.globalIjOptions().useNewRegex) super.findAll(editor, pattern, startLine, endLine, ignoreCase)
else SearchHelper.findAll(editor.ij, pattern, startLine, endLine, ignoreCase)
} }
override fun findNextCharacterOnLine(editor: VimEditor, caret: ImmutableVimCaret, count: Int, ch: Char): Int { override fun findNextCharacterOnLine(editor: VimEditor, caret: ImmutableVimCaret, count: Int, ch: Char): Int {
return SearchHelper.findNextCharacterOnLine(editor.ij, caret.ij, count, ch) val line: Int = caret.ij.logicalPosition.line
val start = editor.getLineStartOffset(line)
val end = editor.getLineEndOffset(line, true)
val chars: CharSequence = editor.ij.document.charsSequence
var found = 0
val step = if (count >= 0) 1 else -1
var pos: Int = caret.ij.offset + step
while (pos in start until end && pos < chars.length) {
if (chars[pos] == ch) {
found++
if (found == abs(count)) {
break
}
}
pos += step
}
return if (found == abs(count)) {
pos
} else {
-1
}
} }
override fun findWordUnderCursor( override fun findWordUnderCursor(
@ -77,11 +207,347 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
isBig: Boolean, isBig: Boolean,
hasSelection: Boolean, hasSelection: Boolean,
): TextRange { ): TextRange {
return SearchHelper.findWordUnderCursor(editor.ij, caret.ij, count, dir, isOuter, isBig, hasSelection) if (logger.isDebugEnabled) {
logger.debug("count=$count")
logger.debug("dir=$dir")
logger.debug("isOuter=$isOuter")
logger.debug("isBig=$isBig")
logger.debug("hasSelection=$hasSelection")
}
val chars: CharSequence = editor.ij.document.charsSequence
//int min = EditorHelper.getLineStartOffset(editor, EditorHelper.getCurrentLogicalLine(editor));
//int max = EditorHelper.getLineEndOffset(editor, EditorHelper.getCurrentLogicalLine(editor), true);
val min = 0
val max: Int = editor.ij.fileSize
if (max == 0) return TextRange(0, 0)
if (logger.isDebugEnabled) {
logger.debug("min=$min")
logger.debug("max=$max")
}
val pos: Int = caret.ij.offset
if (chars.length <= pos) return TextRange(chars.length - 1, chars.length - 1)
val startSpace = charType(editor, chars[pos], isBig) === CharacterHelper.CharacterType.WHITESPACE
// Find word start
val onWordStart = pos == min ||
charType(editor, chars[pos - 1], isBig) !==
charType(editor, chars[pos], isBig)
var start = pos
if (logger.isDebugEnabled) {
logger.debug("pos=$pos")
logger.debug("onWordStart=$onWordStart")
}
if (!onWordStart && !(startSpace && isOuter) || hasSelection || count > 1 && dir == -1) {
start = if (dir == 1) {
findNextWord(editor, pos, -1, isBig, !isOuter)
} else {
findNextWord(
editor,
pos,
-(count - if (onWordStart && !hasSelection) 1 else 0),
isBig,
!isOuter
)
}
start = editor.normalizeOffset(start, false)
}
if (logger.isDebugEnabled) logger.debug("start=$start")
// Find word end
// Find word end
val onWordEnd = pos >= max - 1 ||
charType(editor, chars[pos + 1], isBig) !==
charType(editor, chars[pos], isBig)
if (logger.isDebugEnabled) logger.debug("onWordEnd=$onWordEnd")
var end = pos
if (!onWordEnd || hasSelection || count > 1 && dir == 1 || startSpace && isOuter) {
end = if (dir == 1) {
val c = count - if (onWordEnd && !hasSelection && (!(startSpace && isOuter) || startSpace && !isOuter)) 1 else 0
findNextWordEnd(editor, pos, c, isBig, !isOuter)
} else {
findNextWordEnd(editor, pos, 1, isBig, !isOuter)
}
}
if (logger.isDebugEnabled) logger.debug("end=$end")
var goBack = startSpace && !hasSelection || !startSpace && hasSelection && !onWordStart
if (dir == 1 && isOuter) {
var firstEnd = end
if (count > 1) {
firstEnd = findNextWordEnd(editor, pos, 1, isBig, false)
}
if (firstEnd < max - 1) {
if (charType(editor, chars[firstEnd + 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
goBack = true
}
}
}
if (dir == -1 && isOuter && startSpace) {
if (pos > min) {
if (charType(editor, chars[pos - 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
goBack = true
}
}
}
var goForward = dir == 1 && isOuter && (!startSpace && !onWordEnd || startSpace && onWordEnd && hasSelection)
if (!goForward && dir == 1 && isOuter) {
var firstEnd = end
if (count > 1) {
firstEnd = findNextWordEnd(editor, pos, 1, isBig, false)
}
if (firstEnd < max - 1) {
if (charType(editor, chars[firstEnd + 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
goForward = true
}
}
}
if (!goForward && dir == 1 && isOuter && !startSpace && !hasSelection) {
if (end < max - 1) {
if (charType(editor, chars[end + 1], !isBig) !==
charType(editor, chars[end], !isBig)
) {
goForward = true
}
}
}
if (logger.isDebugEnabled) {
logger.debug("goBack=$goBack")
logger.debug("goForward=$goForward")
}
if (goForward) {
if (editor.anyNonWhitespace(end, 1)) {
while (end + 1 < max &&
charType(editor, chars[end + 1], false) === CharacterHelper.CharacterType.WHITESPACE
) {
end++
}
}
}
if (goBack) {
if (editor.anyNonWhitespace(start, -1)) {
while (start > min &&
charType(editor, chars[start - 1], false) === CharacterHelper.CharacterType.WHITESPACE
) {
start--
}
}
}
if (logger.isDebugEnabled) {
logger.debug("start=$start")
logger.debug("end=$end")
}
// End offset is exclusive
return TextRange(start, end + 1)
} }
override fun findBlockTagRange(editor: VimEditor, caret: ImmutableVimCaret, count: Int, isOuter: Boolean): TextRange? { override fun findBlockTagRange(editor: VimEditor, caret: ImmutableVimCaret, count: Int, isOuter: Boolean): TextRange? {
return SearchHelper.findBlockTagRange(editor.ij, caret.ij, count, isOuter) var counter = count
var isOuterVariable = isOuter
val position: Int = caret.ij.offset
val sequence: CharSequence = editor.ij.document.charsSequence
val selectionStart: Int = caret.ij.selectionStart
val selectionEnd: Int = caret.ij.selectionEnd
val isRangeSelection = selectionEnd - selectionStart > 1
var searchStartPosition: Int
searchStartPosition = if (!isRangeSelection) {
val line: Int = caret.ij.logicalPosition.line
val lineBegin: Int = editor.ij.document.getLineStartOffset(line)
ignoreWhitespaceAtLineStart(sequence, lineBegin, position)
} else {
selectionEnd
}
if (isInHTMLTag(sequence, searchStartPosition, false)) {
// caret is inside opening tag. Move to closing '>'.
while (searchStartPosition < sequence.length && sequence[searchStartPosition] != '>') {
searchStartPosition++
}
} else if (isInHTMLTag(sequence, searchStartPosition, true)) {
// caret is inside closing tag. Move to starting '<'.
while (searchStartPosition > 0 && sequence[searchStartPosition] != '<') {
searchStartPosition--
}
}
while (true) {
val (closingTagTextRange, tagName) = findUnmatchedClosingTag(sequence, searchStartPosition, counter)
?: return null
val openingTag = findUnmatchedOpeningTag(sequence, closingTagTextRange.startOffset, tagName)
?: return null
if (isRangeSelection && openingTag.endOffset - 1 >= selectionStart) {
// If there was already some text selected and the new selection would not extend further, we try again
searchStartPosition = closingTagTextRange.endOffset
counter = 1
continue
}
var selectionEndWithoutNewline = selectionEnd
while (selectionEndWithoutNewline < sequence.length && sequence[selectionEndWithoutNewline] == '\n') {
selectionEndWithoutNewline++
}
val mode = getInstance(editor).mode
if (mode is VISUAL) {
if (closingTagTextRange.startOffset == selectionEndWithoutNewline &&
openingTag.endOffset == selectionStart
) {
// Special case: if the inner tag is already selected we should like isOuter is active
// Note that we need to ignore newlines, because their selection is lost between multiple "it" invocations
isOuterVariable = true
} else if (openingTag.endOffset == closingTagTextRange.startOffset &&
selectionStart == openingTag.endOffset
) {
// Special case: for an empty tag pair (e.g. <a></a>) the whole tag is selected if the caret is in the middle.
isOuterVariable = true
}
}
return if (isOuterVariable) {
TextRange(openingTag.startOffset, closingTagTextRange.endOffset)
} else {
TextRange(openingTag.endOffset, closingTagTextRange.startOffset)
}
}
}
/**
* returns new position which ignore whitespaces at beginning of the line
*/
private fun ignoreWhitespaceAtLineStart(seq: CharSequence, lineStart: Int, pos: Int): Int {
var position = pos
if (seq.subSequence(lineStart, position).chars().allMatch { codePoint: Int ->
Character.isWhitespace(
codePoint
)
}) {
while (position < seq.length && seq[position] != '\n' && Character.isWhitespace(seq[position])) {
position++
}
}
return position
}
/**
* Returns true if there is a html at the given position. Ignores tags with a trailing slash like <aaa></aaa>.
*/
private fun isInHTMLTag(sequence: CharSequence, position: Int, isEndtag: Boolean): Boolean {
var openingBracket = -1
run {
var i = position
while (i >= 0 && i < sequence.length) {
if (sequence[i] == '<') {
openingBracket = i
break
}
if (sequence[i] == '>' && i != position) {
return false
}
i--
}
}
if (openingBracket == -1) {
return false
}
val hasSlashAfterOpening = openingBracket + 1 < sequence.length && sequence[openingBracket + 1] == '/'
if (isEndtag && !hasSlashAfterOpening || !isEndtag && hasSlashAfterOpening) {
return false
}
var closingBracket = -1
for (i in openingBracket until sequence.length) {
if (sequence[i] == '>') {
closingBracket = i
break
}
}
return closingBracket != -1 && sequence[closingBracket - 1] != '/'
}
private fun findUnmatchedOpeningTag(
sequence: CharSequence,
position: Int,
tagName: String,
): TextRange? {
val quotedTagName = Pattern.quote(tagName)
val patternString = ("(</%s>)" // match closing tags
+
"|(<%s" // or opening tags starting with tagName
+
"(\\s([^>]*" // After at least one whitespace there might be additional text in the tag. E.g. <html lang="en">
+
"[^/])?)?>)") // Slash is not allowed as last character (this would be a self closing tag).
val tagPattern =
Pattern.compile(String.format(patternString, quotedTagName, quotedTagName), Pattern.CASE_INSENSITIVE)
val matcher = tagPattern.matcher(sequence.subSequence(0, position + 1))
val openTags: Deque<TextRange> = ArrayDeque()
while (matcher.find()) {
val match = TextRange(matcher.start(), matcher.end())
if (sequence[matcher.start() + 1] == '/') {
if (!openTags.isEmpty()) {
openTags.pop()
}
} else {
openTags.push(match)
}
}
return if (openTags.isEmpty()) {
null
} else {
openTags.pop()
}
}
private fun findUnmatchedClosingTag(
sequence: CharSequence,
position: Int,
count: Int,
): Pair<TextRange, String>? {
// The tag name may contain any characters except slashes, whitespace and '>'
var counter = count
val tagNamePattern = "([^/\\s>]+)"
// An opening tag consists of '<' followed by a tag name, optionally some additional text after whitespace and a '>'
val openingTagPattern = String.format("<%s(?:\\s[^>]*)?>", tagNamePattern)
val closingTagPattern = String.format("</%s>", tagNamePattern)
val tagPattern = Pattern.compile(String.format("(?:%s)|(?:%s)", openingTagPattern, closingTagPattern))
val matcher = tagPattern.matcher(sequence.subSequence(position, sequence.length))
val openTags: Deque<String> = ArrayDeque()
while (matcher.find()) {
val isClosingTag = matcher.group(1) == null
if (isClosingTag) {
val tagName = matcher.group(2)
// Ignore unmatched open tags. Either the file is malformed or it might be a tag like <br> that does not need to be closed.
while (!openTags.isEmpty() && !openTags.peek().equals(tagName, ignoreCase = true)) {
openTags.pop()
}
if (openTags.isEmpty()) {
if (counter <= 1) {
return Pair(TextRange(position + matcher.start(), position + matcher.end()), tagName)
} else {
counter--
}
} else {
openTags.pop()
}
} else {
val tagName = matcher.group(1)
openTags.push(tagName)
}
}
return null
} }
override fun findBlockRange( override fun findBlockRange(
@ -91,6 +557,160 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
count: Int, count: Int,
isOuter: Boolean, isOuter: Boolean,
): TextRange? { ): TextRange? {
return SearchHelper.findBlockRange(editor.ij, caret.ij, type, count, isOuter) val chars: CharSequence = editor.ij.document.charsSequence
var pos: Int = caret.ij.offset
var start: Int = caret.ij.selectionStart
var end: Int = caret.ij.selectionEnd
val loc = BLOCK_CHARS.indexOf(type)
val close = BLOCK_CHARS[loc + 1]
// extend the range for blank line after type and before close, as they are excluded when inner match
if (!isOuter) {
if (start > 1 && chars[start - 2] == type && chars[start - 1] == '\n') {
start--
}
if (end < chars.length && chars[end] == '\n') {
var isSingleLineAllWhiteSpaceUntilClose = false
var countWhiteSpaceCharacter = 1
while (end + countWhiteSpaceCharacter < chars.length) {
if (Character.isWhitespace(chars[end + countWhiteSpaceCharacter]) &&
chars[end + countWhiteSpaceCharacter] != '\n'
) {
countWhiteSpaceCharacter++
continue
}
if (chars[end + countWhiteSpaceCharacter] == close) {
isSingleLineAllWhiteSpaceUntilClose = true
}
break
}
if (isSingleLineAllWhiteSpaceUntilClose) {
end += countWhiteSpaceCharacter
}
}
}
var rangeSelection = end - start > 1
if (rangeSelection && start == 0) // early return not only for optimization
{
return null // but also not to break the interval semantic on this edge case (see below)
}
/* In case of successive inner selection. We want to break out of
* the block delimiter of the current inner selection.
* In other terms, for the rest of the algorithm, a previous inner selection of a block
* if equivalent to an outer one. */
/* In case of successive inner selection. We want to break out of
* the block delimiter of the current inner selection.
* In other terms, for the rest of the algorithm, a previous inner selection of a block
* if equivalent to an outer one. */if (!isOuter && start - 1 >= 0 && type == chars[start - 1] && end < chars.length && close == chars[end]) {
start -= 1
pos = start
rangeSelection = true
}
/* when one char is selected, we want to find the enclosing block of (start,end]
* although when a range of characters is selected, we want the enclosing block of [start, end]
* shifting the position allow to express which kind of interval we work on */
/* when one char is selected, we want to find the enclosing block of (start,end]
* although when a range of characters is selected, we want the enclosing block of [start, end]
* shifting the position allow to express which kind of interval we work on */if (rangeSelection) pos =
max(0.0, (start - 1).toDouble()).toInt()
val initialPosIsInString = checkInString(chars, pos, true)
var bstart = -1
var bend = -1
var startPosInStringFound = false
if (initialPosIsInString) {
val quoteRange = injector.searchHelper
.findBlockQuoteInLineRange(editor, caret, '"', false)
if (quoteRange != null) {
val startOffset = quoteRange.startOffset
val endOffset = quoteRange.endOffset
val subSequence = chars.subSequence(startOffset, endOffset)
val inQuotePos = pos - startOffset
var inQuoteStart =
findBlockLocation(subSequence, close, type, Direction.BACKWARDS, inQuotePos, count)
if (inQuoteStart == -1) {
inQuoteStart =
findBlockLocation(subSequence, close, type, Direction.FORWARDS, inQuotePos, count)
}
if (inQuoteStart != -1) {
startPosInStringFound = true
val inQuoteEnd =
findBlockLocation(subSequence, type, close, Direction.FORWARDS, inQuoteStart, 1)
if (inQuoteEnd != -1) {
bstart = inQuoteStart + startOffset
bend = inQuoteEnd + startOffset
}
}
}
}
if (!startPosInStringFound) {
bstart = findBlockLocation(chars, close, type, Direction.BACKWARDS, pos, count)
if (bstart == -1) {
bstart = findBlockLocation(chars, close, type, Direction.FORWARDS, pos, count)
}
if (bstart != -1) {
bend = findBlockLocation(chars, type, close, Direction.FORWARDS, bstart, 1)
}
}
if (bstart == -1 || bend == -1) {
return null
}
if (!isOuter) {
bstart++
// exclude first line break after start for inner match
if (chars[bstart] == '\n') {
bstart++
}
val o = editor.getLineStartForOffset(bend)
var allWhite = true
for (i in o until bend) {
if (!Character.isWhitespace(chars[i])) {
allWhite = false
break
}
}
if (allWhite) {
bend = o - 2
} else {
bend--
}
}
// End offset exclusive
return TextRange(bstart, bend + 1)
}
override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
val startOffset: Int
val endOffset: Int
val skipCount: Int
val offsetOrdering: IntComparator
if (count < 0) {
startOffset = 0
endOffset = caret.offset.point - 1
skipCount = -count - 1
offsetOrdering = IntComparators.OPPOSITE_COMPARATOR
}
else {
startOffset = caret.offset.point + 1
endOffset = editor.ij.document.textLength
skipCount = count - 1
offsetOrdering = IntComparators.NATURAL_COMPARATOR
}
return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering)
} }
} }

View File

@ -1,39 +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.
*/
@file:Suppress("DEPRECATION", "unused")
package com.maddyhome.idea.vim.option
import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.options.helpers.KeywordOptionHelper
/**
* COMPATIBILITY-LAYER: Added a class and package
* Please see: https://jb.gg/zo8n0r
*/
@Deprecated("Use VimInjector.optionService")
// Used by:
// ideavim-sneak 1.2.0 which is current - ignorecase + smartcase, both just access isSet property
// IdeaVim-EasyMotion 1.9 + 1.10 - OptionsManger.iskeyword.toRegex() (plus more in tests, which are already broken)
// (which-key 0.6.2 uses timeout + timeoutlen, now removed. That plugin version is broken due to other changes)
public object OptionsManager {
public val ignorecase: ToggleOption
get() = ToggleOption(Options.ignorecase)
public val smartcase: ToggleOption
get() = ToggleOption(Options.smartcase)
public val iskeyword: KeywordOption
get() = KeywordOption(KeywordOptionHelper)
}
@Deprecated("No longer used. Use StringOption or KeywordOptionHelper")
public class KeywordOption(public val helper: KeywordOptionHelper) {
public fun toRegex(): List<String> {
return helper.toRegex()
}
}

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