1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-17 16:31:45 +02:00

Compare commits

..

368 Commits

Author SHA1 Message Date
cc9385f2a9 Set plugin version to chylex-40 2024-08-25 10:17:38 +02:00
32a2384a46 Revert "VIM-2074 Backspace behaviour is incorrect in Replace mode"
This reverts commit 9bbeab80
2024-08-25 10:17:38 +02:00
fdd850de5a Fix(VIM-3615): Escape closes dialog while waiting for more keys 2024-08-25 10:10:21 +02:00
0f7116b136 Add action to run last macro in all opened files 2024-08-25 06:11:12 +02:00
db8f0251fb Stop macro execution after a failed search 2024-08-25 06:11:12 +02:00
2ca2c1e774 Revert per-caret registers 2024-08-25 06:11:10 +02:00
f3c32da4d1 Revert "Factor disposable objects on editor opening"
This reverts commit 1fa78935
2024-08-25 06:10:58 +02:00
1a3b34d457 Fix(VIM-3364): Exception with mapped Generate action 2024-08-25 06:10:58 +02:00
1f9159996d Apply scrolloff after executing native IDEA actions 2024-08-25 06:10:58 +02:00
65c3acd891 Stay on same line after reindenting 2024-08-25 06:10:58 +02:00
223f65c003 Update search register when using f/t 2024-08-25 06:10:58 +02:00
293b854620 Automatically add unambiguous imports after running a macro 2024-08-25 06:10:58 +02:00
9e7c5fd603 Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2024-08-25 06:10:58 +02:00
00c799595f Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2024-08-25 06:10:58 +02:00
8577b5ed20 Add support for count for visual and line motion surround 2024-08-25 06:10:58 +02:00
3af7a991a0 Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2024-08-25 06:10:57 +02:00
212af1798d Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2024-08-25 06:10:57 +02:00
002ef8f72f Respect count with <Action> mappings 2024-08-25 06:10:57 +02:00
9115af6b3d Change matchit plugin to use HTML patterns in unrecognized files 2024-08-25 06:10:57 +02:00
25ca42d371 Reset insert mode when switching active editor 2024-08-25 06:10:57 +02:00
408687c9b3 Disable switching to insert mode for some editors 2024-08-25 06:10:54 +02:00
85e00bf8fc Remove update checker 2024-08-25 06:10:31 +02:00
bd6f2d4b2f Set custom plugin version 2024-08-25 06:10:31 +02:00
IdeaVim Bot
e406885ec6 Add Igor Babko to contributors list 2024-08-24 09:01:39 +00:00
filipp
9bbeab8062 VIM-2074 Backspace behaviour is incorrect in Replace mode 2024-08-23 14:22:48 +03:00
filipp
373bfc4eab Add endOffset to LiveRange 2024-08-23 14:22:48 +03:00
filipp
050f2f7b97 Respect emojis and don't assume that every character is a single offset 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
e30bc14843 Make closing command line safer 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
76d590be11 Support more flexible command line restrictions for Fleet 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
b005328b4a Exclude generated files from .gitignore
I've also added sorting to be more confident that the generated file will be persistent across different machines
2024-08-23 14:18:03 +03:00
Filipp Vakhitov
ad20021cee Remove currentAction and some unused code 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
126de5c218 Remove package-info.java 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
0f7aef3f15 Fix tests 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
f352b84922 Move some Executor logic to its base class 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
789faa7cb2 Add entry to history on <Esc> in COMMAND mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
a338f5768a Better reset 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
8205c74571 Remove some old bindings 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
def40eb409 Save last entry on every command line text update 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
010e8a7541 Support <C-U> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
46c6778b3a Support <Down>, <S-Down>, <C-N>, <PageDown> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
0977bd4400 Support <Up>, <S-Up>, <C-P>, <PageUp> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
db092e9b0a Support history in VimCommandLine 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
5cfb98e188 Remove some old bindings 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
3a95b62885 Support <Insert> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
93e6adf5a9 Support <C-W> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
37204398ff Support <C-Right>, <S-Right> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
b2f450d14d Support <C-Left>, <S-Left> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
61da888571 Fix failing tests 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
fcda97cfb8 Update methods to support searching in any text, not just Editor content 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
1dc7ea6363 Register all shortcuts without references on ExKeyBindings
All the shortcuts will be removed from ExKeyBindings, but they still need to be registered
2024-08-23 14:18:03 +03:00
Filipp Vakhitov
bb507db884 Remove swing bindings that are already implemented 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
7b0482ed94 Fix unwanted beep when can't perform an action 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
1c79b0d59a Support <Right> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
ff4eb31418 Support <Left> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
cb1078cf70 Support <C-H> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
da3e40eaf6 Support <C-E>, <End> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
17f77a9639 Support <C-B>, <Home> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
3d03494354 Support <DEL> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
642caddda7 Support <BS> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
1d97c43e30 Make ExEntryAction public
Fleet registers commands manually, so commands must be accessible
2024-08-23 14:18:03 +03:00
Igor Babko
7ed3e3b53c Update support-guide.md 2024-08-23 12:14:41 +03:00
IdeaVim Bot
aa39ca2006 Add shaun to contributors list 2024-08-19 09:01:43 +00:00
shaun
bb122903de remove Latest Fixes link 2024-08-19 10:23:23 +03:00
shaun
c84a3cf64d [changelog] add YouTrack link for version fixes 2024-08-19 10:23:23 +03:00
IdeaVim Bot
f4c9464b8a Add The1xDeveloper to contributors list 2024-08-17 09:01:38 +00:00
The1xDeveloper
3ba14d05b4 VIM-566: Add za motion support for toggling folds 2024-08-16 12:28:18 +03:00
Filipp Vakhitov
2189b70b87 Fix(VIM-3601): The escape characters in IdeaVim's configuration file are invalid 2024-08-14 18:18:55 +03:00
dependabot[bot]
ea2222f9d5 Bump org.junit.jupiter:junit-jupiter from 5.10.3 to 5.11.0
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.10.3 to 5.11.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.3...r5.11.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-14 18:53:54 +03:00
Filipp Vakhitov
7d68d41888 Fix(VIM-3580): New terminal switches editor to insert mode 2024-08-12 13:44:24 +03:00
Alex Pláte
44749b6d8c Update IdeaVim Plugins.md 2024-08-12 12:16:19 +03:00
Alex Pláte
1ce4dbc569 Update Home.md 2024-08-12 12:14:44 +03:00
Alex Pláte
db59513505 Update Home.md 2024-08-12 12:13:36 +03:00
Filipp Vakhitov
8c9ff9465f Fix(VIM-3584): com.maddyhome.idea.vim.KeyHandler requests com.maddyhome.idea.vim.api.VimKeyGroup instance. Class initialization must not depend on services 2024-08-11 01:12:59 +03:00
Filipp Vakhitov
1a2322ddec OutputPanel no longer ignores key events 2024-08-10 23:55:57 +03:00
dependabot[bot]
fbbd1ebc0d Bump org.antlr:antlr4 from 4.13.1 to 4.13.2
Bumps [org.antlr:antlr4](https://github.com/antlr/antlr4) from 4.13.1 to 4.13.2.
- [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.13.1...4.13.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 18:46:09 +03:00
Alex Plate
8d5df11372 Add a warning when the action is executed from the write action
Related: VIM-3376
2024-08-06 11:11:40 +03:00
Matt Ellis
dfebe542d8 Fix incsearch highlight in multiple Rider instances
Unlike other IDEs, Rider has multiple client sessions. The IDE itself is the "local" session, while the external ReSharper process is treated as a "frontend" process. The code to get local editors was erroneously getting `ALL` sessions, rather than just `LOCAL` sessions, and assuming that the first was the local session. In Rider, opening three instances would add three additional clients, and that would change the order.

I don't know why I changed `LOCAL` to `ALL` when previously changing this bit of code. AIUI, using `LOCAL` should work fine. If it turns out that CWM or remote dev require `ALL`, please document why.

Fixes VIM-3549
2024-08-05 18:58:45 +03:00
Matt Ellis
7fde47c08b Update README.md
Added links to POSIX spec
2024-08-01 14:04:58 +03:00
dependabot[bot]
629919f634 Bump org.jetbrains.intellij.platform from 2.0.0-rc1 to 2.0.0-rc2
Bumps org.jetbrains.intellij.platform from 2.0.0-rc1 to 2.0.0-rc2.

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-31 17:27:09 +02:00
dependabot[bot]
43fff1c73e Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.0.0-1.0.23 to 2.0.0-1.0.24.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.0.0-1.0.23...2.0.0-1.0.24)

---
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-07-31 17:26:53 +02:00
Alex Plate
d02f0e17ca Revert "Fix(VIM-3376): Refactor the way IdeaVim executes actions"
This reverts commit 24514039
2024-07-31 13:14:25 +03:00
Filipp Vakhitov
5dc860f61e Fix(VIM-3569): Pipe-symbol can no longer be used in remaps 2024-07-30 18:02:12 +03:00
Alex Plate
956e726c31 Add test 2024-07-29 17:48:07 +02:00
Alex Plate
446067e2fe Update formatting 2024-07-29 16:57:29 +02:00
Alex Plate
bd53a895c0 Return nullability of editor in action 2024-07-29 16:54:02 +02:00
Alex Plate
e61fed2467 Revert "Use installer in case of the snapshot dependency"
This reverts commit f5e125759f.
2024-07-29 16:51:25 +02:00
Alex Plate
f5e125759f Use installer in case of the snapshot dependency 2024-07-29 16:47:31 +02:00
Alex Plate
622eb887c8 Make style refactoring 2024-07-29 16:37:57 +02:00
Alex Plate
9fb614e16c Pass editor to the "execute" method of the NativeAction 2024-07-29 16:36:01 +02:00
Alex Plate
1e0fa07768 Fix problem with unsatisfied condition 2024-07-29 16:15:58 +02:00
Alex Plate
bfb1d5b7f5 Remove accidential return 2024-07-29 14:08:15 +02:00
Alex Plate
2fad8790a9 Reset key handler in property tests 2024-07-29 13:57:32 +02:00
Alex Plate
9719106a14 Fix incorrect assertions after migration to the new version of gradle plugin 2024-07-29 13:52:14 +02:00
Alex Plate
870a0da2a2 Update UI tests without octopus handler 2024-07-29 13:41:24 +02:00
Alex Plate
d3c315d299 Update logs location in UI tests 2024-07-29 13:39:53 +02:00
Alex Plate
2583b6e792 Print the list of available keys for debugging 2024-07-29 13:27:15 +02:00
Alex Plate
89417eb4f6 Comment out compatibility check for copilot because it's broken for the moment 2024-07-29 13:16:50 +02:00
Alex Plate
560700c9aa Pass editor to the execution of the action
Related: VIM-3376
2024-07-29 12:49:16 +02:00
Alex Plate
24514039e1 Fix(VIM-3376): Refactor the way IdeaVim executes actions
Now instead of a few hacks, we use a special function from the platform
2024-07-29 12:49:16 +02:00
Alex Plate
ff44596c1a Migrate to gradle plugin 2.0.0-rc1 2024-07-26 19:00:31 +03:00
Filipp Vakhitov
b001d63fd9 Do not fire VimInsertListener on every mode change 2024-07-26 17:30:08 +03:00
Filipp Vakhitov
5db96bef28 Fix compilation with IdeaVimExtension 2024-07-26 17:30:08 +03:00
Filipp Vakhitov
39c615cddd Do not cast VimStateMachine classes in vim-engine
It will break if we change implementation in Fleet
2024-07-26 17:30:08 +03:00
Filipp Vakhitov
961173a93b Fix tests 2024-07-26 17:30:08 +03:00
Filipp Vakhitov
92741c6356 Fix compilation 2024-07-26 17:30:08 +03:00
Filipp Vakhitov
643eb2a85f Add context to MappingProcessor 2024-07-26 17:30:08 +03:00
filipp
883744e4ee Move blockInsert to vim-engine 2024-07-26 17:30:08 +03:00
filipp
66173e03be Move reformatCodeRange to vim-engine 2024-07-26 17:30:08 +03:00
filipp
e455722758 Move more methods to vim-engine 2024-07-26 17:30:08 +03:00
filipp
823bdc1561 Replace VimYankGroup with implementation that works with VimListenersNotifier 2024-07-26 17:30:08 +03:00
filipp
f91fda2ca5 Remove VimInsertListener
It can be replaced by ModeChangeListener
2024-07-26 17:30:08 +03:00
filipp
92abd76615 Move more methods to engine 2024-07-26 17:30:08 +03:00
filipp
57c45ca153 Move more methods to engine 2024-07-26 17:30:08 +03:00
filipp
7c623ae4b5 Move case change methods to engine 2024-07-26 17:30:08 +03:00
filipp
f2ef92cdef Remove unused method 2024-07-26 17:30:08 +03:00
Filipp Vakhitov
e8e6eabe97 Fix tests
See https://youtrack.jetbrains.com/issue/VIM-3566/Unit-Tests-Test-UI-Caret-Attributes for more details.
2024-07-26 16:31:47 +03:00
Filipp Vakhitov
ef1c915264 Fix visual mode not being removed.
Steps to reproduce:
1. Select some text inside diff editor
2. Open 'Find in Files' window and search for file
3. Open the found file

Result: current mode is VISUAL
2024-07-26 14:24:01 +03:00
Filipp Vakhitov
a5e2168f7f Fix(VIM-3540): Caret icon is stuck in incorrect mode when having two or more simultaneous instances running
Some of the text input fields where Vim should not work at all had block carets.
It did not happen before, because previously we had a unique VimStateMachine for each editor and for newly created editors it was in INSERT mode. And we did call the updateSecondaryCaretsVisualAttributes method for editors that have nothing to do with Vim, but because of the INSERT mode it was looking OK.
However, now the VimStateMachine is global, and we can't rely on local INSERT anymore.
This commit forbids updating caret visual attributes for editors that do not support Vim.

NOTE: `isIdeaVimDisabledHere` is broken during editor creation handling, it always returns true. However, we do not trigger carets redraws on editor creation and do it on focus events, so it should work.
2024-07-26 00:44:35 +03:00
Filipp Vakhitov
83e5470b3a Fix(VIM-3563): Can't exit insert mode in Readonly File 2024-07-25 22:54:39 +03:00
dependabot[bot]
c446de8979 Bump org.jetbrains.kotlin:kotlin-stdlib from 1.9.24 to 1.9.25
Bumps [org.jetbrains.kotlin:kotlin-stdlib](https://github.com/JetBrains/kotlin) from 1.9.24 to 1.9.25.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.9.25/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.24...v1.9.25)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-24 19:02:14 +03:00
Matt Ellis
13426915f4 Remove special case handling for macro recording 2024-07-23 23:08:24 +03:00
Matt Ellis
d766c3b8ee Add test for nested insert literal action 2024-07-23 23:08:24 +03:00
Matt Ellis
844bc01537 Make functions protected 2024-07-23 23:08:24 +03:00
Matt Ellis
3d2d32b022 Move special case code to actions 2024-07-23 23:08:24 +03:00
Matt Ellis
a8677d3dd7 Refactor expected argument type
We no longer need to track a previous fallback argument type, since we don't support nested commands inside a command builder. We can just return the current argument type, or its fallback
2024-07-23 23:08:24 +03:00
Matt Ellis
7217fdf734 Remove unused special case check
This was to handle nested commands, e.g. inserting a digraph inside a search `d/foo<C-K>OK<CR>`. The command line now has its own command builder, so this check is no longer needed
2024-07-23 23:08:24 +03:00
Matt Ellis
0c867b3869 Make some CommandBuilder properties immutable 2024-07-23 23:08:24 +03:00
Matt Ellis
b3bcab4336 Refactor DigraphResult to sealed classes 2024-07-23 23:08:24 +03:00
Matt Ellis
fe1b48a9b3 Refactor digraph prompt handling 2024-07-23 23:08:24 +03:00
Matt Ellis
b5a0862520 The last command part is the current one 2024-07-23 23:08:24 +03:00
Matt Ellis
babc1f54e5 Return the last valid register if it exists
The last command part is not guaranteed to be a "select register" part. The user might have selected a register then typed an operator, and we might be waiting for a motion.
2024-07-23 23:08:24 +03:00
Matt Ellis
32b910a65b Remove unused functions and properties 2024-07-23 23:08:24 +03:00
Filipp Vakhitov
2aa71a0008 Add "Dracula" theme to mode widget 2024-07-23 19:36:07 +03:00
Filipp Vakhitov
c2c0c2aba2 Fix(VIM-3552): Undo undoes paste and insert in one keypress 2024-07-22 23:08:56 +03:00
Alex Plate
6a10cf5e0d Update the configuration for PY UI tests 2024-07-22 10:08:56 +03:00
Alex Plate
90474a3a4f Fix the mouse button issue 2024-07-19 20:25:45 +03:00
Alex Plate
be43f74bc6 Get rid of deprecated execute method in UI tests 2024-07-19 19:36:11 +03:00
Alex Plate
5916c42cd1 Disable cache for gradle UI tests 2024-07-19 18:52:58 +03:00
Alex Plate
a43c7ece32 Remove deprecated execute function in UI tests 2024-07-19 18:37:14 +03:00
Alex Plate
40c1070b1a Update path to logs in UI tests one more time 2024-07-19 17:52:54 +03:00
Alex Plate
75ccdb2a4d Update qodana after moving the vimscript to the engine 2024-07-19 17:47:49 +03:00
Alex Plate
3de7b0ca78 Disable configuration cache for gradle release task 2024-07-19 17:43:26 +03:00
Alex Plate
448e32a6cc Update AceJump dependency to the latest one 2024-07-19 17:27:02 +03:00
Alex Plate
4a85058ba2 Start using kotlin 2.0
Originally this is needed to update the dependency on AceJump, which uses the kotlin 2.0 compiler, and its classes are not compatible with the old compiler
2024-07-19 17:13:59 +03:00
filipp
7e28deb328 Better logic
In case that user will unsubscribe via setting UI and won't call IdeaVim's action
2024-07-19 16:29:15 +03:00
filipp
f3767b53b7 Subscribe IDE EAP users to IdeaVim EAP 2024-07-19 16:12:33 +03:00
Alex Plate
1026e27e64 Bring back the old version of AceJump dependency because the new one breaks the compilation 2024-07-19 14:58:20 +03:00
Alex Plate
18d653a9ae Update AceJump dependency to the latest one 2024-07-19 14:32:11 +03:00
Alex Plate
fcf4b44443 Save the whole sandbox after the UI test 2024-07-19 09:35:39 +03:00
Alex Plate
907e44b1d7 Change the gradle task for the UI tests 2024-07-19 09:05:11 +03:00
Alex Plate
6c9b39a623 Update gradle plugin and remote robot versions 2024-07-18 17:08:16 +03:00
dependabot[bot]
a3cb093b42 Bump org.jetbrains.intellij.platform from 2.0.0-beta8 to 2.0.0-beta9
Bumps org.jetbrains.intellij.platform from 2.0.0-beta8 to 2.0.0-beta9.

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-17 15:50:17 +00:00
dependabot[bot]
524e854c61 Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.0.0-1.0.22 to 2.0.0-1.0.23.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.0.0-1.0.22...2.0.0-1.0.23)

---
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-07-17 18:49:49 +03:00
Filipp Vakhitov
5588c27037 Attempt to fix VIM-3540
I'm not sure what causes the issue, but everything was working when we were updating visual attributes per each caret and... let update them per each caret
2024-07-17 14:45:50 +03:00
Filipp Vakhitov
90d36eea98 Make myInputInterceptor private 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
f4414de86c Safer getCurrentModalInput() 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
d46102ccaf Remove deprecated method from vim-engine 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
82347f5f0d Fix surround 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
c594f28acb Fix compilation 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
bf6517e58f Add comment 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
aec2f4c435 Remove isCancel argument
It was not used anywhere
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
8f905758d5 Remove typing in ModalInput
Typing is more suitable for command lines than a modal input, and most likely it should be used instead
It is still possible to support typing by properly implementing the handleKey method
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
80cc236f48 Remove modal input on click 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
e432a02a45 Remove confirmChoice method
It used secondary loop
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
d7894fa7f4 ProcessSubstituteCommand refactoring part 9
Finally, moved confirmation dialog to modal input
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
853d7032f0 ProcessSubstituteCommand refactoring part 8 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
5f9f57e1c0 ProcessSubstituteCommand refactoring part 7 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
f4381c8216 ProcessSubstituteCommand refactoring part 6 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
20eee7cae7 ProcessSubstituteCommand refactoring part 5 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
33392c2148 ProcessSubstituteCommand refactoring part 4 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
bb67564fbe ProcessSubstituteCommand refactoring part 3 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
61ccbcd788 ProcessSubstituteCommand refactoring part 2
We do not need the `doAll` condition (because the next code block with `doAll` adjusts line and column for the next match to be correct),
And the line2 can't be >= editor.lineCount()
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
1dbaa3be6d ProcessSubstituteCommand refactoring part 1 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
872bc22830 Remove shouldRecord from KeyConsumer
shouldRecord value was only updated in ModeInputConsumer when the key was not handled. But when the key is not handled, it is not passed to finishedCommandPreparation and the shouldRecord value is not used
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
ce23ed814c Move command line key handling logic to KeyConsumer 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
82cd534756 Remove startExEntry method
We may create a command line via the VimCommandLineService and forget (or do not know) about calling startExEntry necessary. So we move its logic inside the creation of the command line
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
673809d6b9 Move lastCommand to CmdFilterCommand 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
cdbaf73b1e Remove unused fields from VimProcessGroup 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
7f911b7e72 Fix VisualAreaMatcher
It looks for the last selection info in all conditions, not the ones that were specified
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
c03a2dfe7e Add support for finishOn in readInputAndProcess 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
75935ce4d1 Add readInputAndProcess to replace inputString and save us from using secondary loop 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
f0b203409e Replace cancelExEntry with close method in VimCommandLine 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
b3b369eb59 Remove unnecessary write action
`insertRegister` is self synchronized
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
96072982cf Replace deprecated inputString() with ModalInput 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
de016fc445 Always register shortcuts
No matter what kind of panel we have, we want VimShortcutKeyAction to be triggered and pass the shortcut keys (e.g. CTRL-C to close panel)
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
554d9b5f7b Create a Wrapper for ExEntryPanel
ExEntryPanel already extends JPanel, so we need a wrapper to make it extend VimModalInputBase
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
149edefad5 Delegate handling closing keystrokes to implementations
Sometimes it may mean aborting interception (CTRL-C), sometimes finishing it (CR)
2024-07-17 13:30:54 +03:00
Filipp Vakhitov
52d3840c83 Rename argument 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
793677d4fd Add typeText() method to VimModalInput 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
2376ee4877 Fix tests 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
68dcab6262 Introduce ModalInput to get rid of Swing's secondary loop 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
f38fd3512c Fix passing key to editor when the output panel is scrolled to its end 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
e515278ba3 Add label field to VimOutputPanel 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
9cc69e41ee Add KeyHandling logic to VimOutputPanel 2024-07-17 13:30:54 +03:00
Filipp Vakhitov
2109ff235c Fix tests 2024-07-16 20:32:11 +03:00
Filipp Vakhitov
d6910aa81d Fix(VIM-3538): Can't select a block in {} when the cursor is on commented Line 2024-07-16 19:23:43 +03:00
Filipp Vakhitov
8369391902 Fix(VIM-3530): "Yank inner block"/"Change inner block" uses wrong block 2024-07-16 18:47:58 +03:00
Alex Plate
f336807498 Bring back IU for copilot compatibility testing 2024-07-16 09:09:35 +03:00
Alex Plate
14ba5d7126 Convert Troubleshooter field to function to avoid early initialization 2024-07-15 13:54:20 +03:00
Alex Plate
288394d25f Update the notification text for undo 2024-07-15 13:19:03 +03:00
Alex Plate
fb08b5fd65 Prepare to changing the default value of the undo
A notification added to give the information about what happened

Related: VIM-547
2024-07-15 13:16:01 +03:00
Alex Plate
3465e11c3a Initialize the injector in multiple entry points of IntelliJ
Initially, injector was initialized in VimPlugin, assuming that almost every interaction with the plugin goes through it. However, with the plugin evolution, this class starts to be less used.
As IJ doesn't have any single entry point for the plugins, we initialize it in multiple places.
However, the architecture where the plugin might be not initizlied is considered as a bad acrhitecture and should be reviewed.

Related ticket: VIM-3369
2024-07-12 15:09:18 +03:00
Matt Ellis
e07a16863e Show possible IDs based on the action's shortcuts
Fixes VIM-3499
2024-07-12 15:09:02 +03:00
kun-codes
64f7532510 added rename functionality 2024-07-12 09:53:07 +03:00
kun-codes
dd892e77fb added paste functionality 2024-07-12 09:53:07 +03:00
kun-codes
65aeeba521 added copy functionality 2024-07-12 09:53:07 +03:00
dependabot[bot]
ca3e56d0d6 Bump org.mockito.kotlin:mockito-kotlin from 5.3.1 to 5.4.0
Bumps [org.mockito.kotlin:mockito-kotlin](https://github.com/mockito/mockito-kotlin) from 5.3.1 to 5.4.0.
- [Release notes](https://github.com/mockito/mockito-kotlin/releases)
- [Commits](https://github.com/mockito/mockito-kotlin/compare/5.3.1...5.4.0)

---
updated-dependencies:
- dependency-name: org.mockito.kotlin:mockito-kotlin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 18:21:34 +03:00
dependabot[bot]
bcbfb0dc32 Bump org.jetbrains.changelog from 2.2.0 to 2.2.1
Bumps org.jetbrains.changelog from 2.2.0 to 2.2.1.

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 18:21:20 +03:00
Alex Plate
46a4a10e63 Reformat code in tests 2024-07-10 10:06:27 +03:00
Alex Plate
d5bddd077f Disable Gradle configuration cache in CI tests
With gradle plugin 2.0 this cache fails on GitHub Actions. This solution is temporlly and generally the `integrationsTest` should be migrated from `doLast` to some other task approach
2024-07-10 09:02:26 +03:00
Alex Plate
e22fd263cc Test compatibility of copilot on PyCharm
The current execution reports the absence of python plugin. Probably there is a dependency on PyCharm there
2024-07-09 16:35:32 +03:00
Alex Plate
f3902d0ae0 Convert property to function to avoid extra service creation 2024-07-09 16:07:57 +03:00
Alex Plate
f9213ee45d Mute some tests that depend on oldundo option 2024-07-09 15:46:41 +03:00
Alex Plate
281bc2573e Bring back some methods to preserve the compatibility with other plugins 2024-07-09 15:38:50 +03:00
Alex Plate
ae8c7f6bfa Revert "Remove old compatibility layers"
This reverts commit 6cee04a4be.
2024-07-09 15:32:39 +03:00
filipp
c0419d6018 Fix project leak 2024-07-08 13:09:08 +03:00
filipp
ea98e50f65 Sometimes safety is a bad thing 2024-07-08 13:09:08 +03:00
filipp
168174e383 Add missing resetOpPending to KeyHandler.reset 2024-07-08 13:09:08 +03:00
filipp
53cd4e1b88 Remove KeyHandlerStateResetter
It's an oneliner that we can live without
2024-07-08 13:09:08 +03:00
filipp
27e3561bb8 Safer VimListenersNotifier 2024-07-08 13:09:08 +03:00
filipp
9bb9cb13e3 Fix possible bug in the ExOutputModel.show() method and add documentation 2024-07-08 13:09:08 +03:00
filipp
16455f7241 Remove the update() method from VimOutputPanel 2024-07-08 13:09:08 +03:00
filipp
82225aa519 Use new interface instead of the old one 2024-07-08 13:09:08 +03:00
Filipp Vakhitov
5f2baefc6c Introduce new interface for VimOutputPanel to support the output panel in Fleet
Why is the old interface bad?
- it is not obvious. You cannot create a new panel or check if it is already created. Only "getOrCreate" it
- output panel is bound to editor while in Vim it is global
- we have the `isActive` field and the `clear()` method at the same time, because interface implies that you store the same instance of the panel and reactivate it for each output and I don't like it. We also can forget to call `clear()` after reusing panel
- we cannot "build" output before showing to make the panel more efficient. With multiple carets we can only cal `output(oldText + newText)` for each caret, and it is bad. (imagine we have global command with a lot of matches and for each time we will need to call the `output(oldText + newText)`)
- the `output()` method shows panel, activates it and updates it
- there are more things that I do not like, but the points above should be already enough
2024-07-08 13:09:08 +03:00
Filipp Vakhitov
cedcf39723 Fix(VIM-3461): Focus regression 2024-07-08 13:09:08 +03:00
Filipp Vakhitov
4925d9aada Remove ideaglobalmode option
Mode is global now
2024-07-08 13:09:08 +03:00
Filipp Vakhitov
f3e6df32d0 Fix(VIM-3462): IdeaVim not responsive occasionally
This bug was caused by two reasons:
1. KeyHandler state is not longer per-editor and we can't reset it on editor creation
2. We do not need to do such things on editor creation. What actually matters is focusing the editor
2024-07-08 13:09:08 +03:00
Filipp Vakhitov
5aaa8752af Move to new API from deprecated one 2024-07-08 13:09:08 +03:00
Filipp Vakhitov
a1d214316c Make VimStateMachine global
It is global in Vim
2024-07-08 13:09:08 +03:00
filipp
8a1e3eb066 Move toggleInsertOverwrite() to VimEditor
If we execute it from VimStateMachine directly, then mode change listeners are not notified
2024-07-08 13:09:08 +03:00
Alex Plate
75a417773f [VIM-547]: Turn off new undo for the next release as well 2024-07-05 12:14:10 +03:00
IdeaVim Bot
b3b3ee4f21 Add Jake to contributors list 2024-07-04 09:01:54 +00:00
Filipp Vakhitov
07b1db4b28 Fix(VIM-3518): line numbers in :g/ not displayed 2024-07-04 01:50:33 +06:00
Filipp Vakhitov
dc775a0f22 Move number option to vim-engine 2024-07-04 00:11:36 +06:00
Filipp Vakhitov
10228f953e Fix(VIM-3517): Add support for global find & replace Vim-ism — :g/pattern/s//replacement/g 2024-07-03 22:34:42 +06:00
Matt Ellis
afceecadbe Disable the configuration cache for GitHub actions
These tasks should be rewritten to work nicely with Gradle's configuration cache. In the meantime, this allows them to still run as actions.
2024-07-03 19:39:55 +03:00
Matt Ellis
b2a4e59571 Update gradle plugin to beta 8
Fixes an issue with resetting the sandbox after killing the IDE instead of quitting gracefully
2024-07-03 19:39:55 +03:00
Alex Plate
b0b944bbf3 Temporally get rid of the configuration cache 2024-07-03 19:39:31 +03:00
Alex Plate
89a3d74b93 Try not to use the configuration cache in gradle automations at the moment
This is done because the current implementation of the gradle commands is not correct and the cache fails on GitHub actions.
2024-07-03 19:32:10 +03:00
Alex Plate
f4eef04750 Do not bundle kotlin-stdlib and kotlin-reflection to the plugin distribution
IJ has it's own kotlin libs, as well as fleet. So, let's use kotlin reflection library only to compile the project without bundling these jars into the resulting plugin
2024-07-03 19:32:10 +03:00
dependabot[bot]
e62c86b99f Bump org.junit.jupiter:junit-jupiter-api from 5.10.2 to 5.10.3
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.10.2 to 5.10.3.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 19:13:37 +03:00
dependabot[bot]
82bd792da5 Bump org.junit.jupiter:junit-jupiter-engine from 5.10.2 to 5.10.3
Bumps [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.10.2 to 5.10.3.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 18:56:45 +03:00
dependabot[bot]
a58c9065e6 Bump org.junit.jupiter:junit-jupiter from 5.10.2 to 5.10.3
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.10.2 to 5.10.3.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3)

---
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-07-03 18:39:47 +03:00
dependabot[bot]
e8bf984b76 Bump org.junit.jupiter:junit-jupiter-params from 5.10.2 to 5.10.3
Bumps [org.junit.jupiter:junit-jupiter-params](https://github.com/junit-team/junit5) from 5.10.2 to 5.10.3.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 18:39:37 +03:00
Alex Plate
23e1a3499f Do not update the YouTrack tickets in case of patch release
This is usually not needed. Also, if we create a new EAP for the next version, this task will close these tickets on patch release, what is incorrect.
2024-07-03 11:49:14 +03:00
Matt Ellis
6b4e4bacd7 Fix null reference initialising output panel fonts
Fixes VIM-3515
2024-07-03 09:43:48 +03:00
Alex Plate
a84c04ca08 Revert "Temporally comment out release branch reset"
This reverts commit e67c71e440.
2024-07-02 10:38:15 +03:00
Alex Plate
e67c71e440 Temporally comment out release branch reset 2024-07-02 10:05:23 +03:00
Alex Plate
5078ff9c7a Disable check size for releases 2024-07-02 10:04:49 +03:00
Alex Plate
647510de5d Optimize imports for the project 2024-06-28 17:30:17 +03:00
Alex Plate
84e11e4236 Add useful .idea files to git
.name will show the correct name "IdeaVim" in IJ
`misc.xml` contains information about the annotations that should be considered as "EntryPoints" for the function. With this, they'll not be marked as unused
2024-06-28 17:25:56 +03:00
Alex Plate
9538714af1 Remove explicit API mode to enhance coding efficiency and maintain Kotlin's conciseness
Explicit API mode has been removed due to several reasons impacting developer productivity and the natural coding style in Kotlin.

Firstly, the mode was found to be more irritating than beneficial, as it forced unnecessary verbosity without enhancing thoughtfulness in coding. It often prompted automatic 'public' insertion reactions to red-highlighted code, which did not genuinely encourage more deliberate coding decisions.

Secondly, our aim is to form a robust API primarily through interfaces, which inherently define public scope and duty, rendering explicit API mode somewhat redundant.

Lastly, the additional verbosity caused by explicit API mode expanded code lines affecting code readability.

The compatibility with the existing plugins is verified via the compatibility checker, so no JetBrains plugins will be affected
2024-06-28 17:11:16 +03:00
Alex Plate
ffd832d990 Get rid of RegExp class 2024-06-28 17:01:30 +03:00
filipp
8de2b8976b Avoid using deprecated RegExp in VimRegexService 2024-06-28 16:58:13 +03:00
filipp
a6aa26b5d9 Remove deprecated methods 2024-06-28 16:58:13 +03:00
filipp
2505651c68 Deprecate methods in VimStateMachine that duplicate KeyHandler functionality 2024-06-28 16:58:13 +03:00
filipp
e67c7b23ff Remove deprecated code from VimStateMachine 2024-06-28 16:58:13 +03:00
filipp
453cca3b0c Remove resetState() from editor
It mostly resets KeyHandler, so it's more a KeyHandler responsibility. It also utilises deprecated API.
2024-06-28 16:58:13 +03:00
filipp
6cee04a4be Remove old compatibility layers
It is necessary to remove deprecated code from VimStateMachine in future commits
2024-06-28 16:58:13 +03:00
filipp
ae8b9b4773 Mark isOperatorPending as deprecated
It calls the same method in KeyHandler. We do not need it in VimStateMachine
2024-06-28 16:58:12 +03:00
Matt Ellis
e748b7b265 Fix soft wrap inconsistency in run console
IntelliJ has multiple soft wrap options. One for main editors, another for consoles and a third for previews. This can lead to inconsistencies if initialising a console based on a main editor when both have default values, versus the same scenario when the main editor has an explicit value. Furthermore, the run console's soft-wraps toggle button uses the global value, so can get out of sync if the local value is initialised to an explicit value. This change will only copy the soft wrap value over during initialisation for similar editors (main editor, preview, diff) and not for different editors (console).

Fixes VIM-3450
2024-06-28 16:50:25 +03:00
Matt Ellis
c2401ec013 Protect against recursion when opening editors
Fixes VIM-3066
2024-06-28 16:50:25 +03:00
Matt Ellis
8073d7ecd0 Fix copying 'fileencoding' during window opening
The option should be "local-noglobal", and reset to default. Copying the value could cause the file to be converted immediately, possibly with a warning dialog

Fixes VIM-3467
2024-06-28 16:50:25 +03:00
Matt Ellis
64f7859ba7 Fix switching to insert for non-file backed diff
Fixes VIM-3442
2024-06-28 16:50:25 +03:00
Matt Ellis
f1b94d7026 Fix regression ignoring case in global command
Regression in new regex engine
2024-06-28 16:50:25 +03:00
Matt Ellis
79653b6048 Update ex output asserts to remove trailing newline
The trailing newline was never necessary. The text field always stripped it, but the test code didn't. The previous commit pulled this behaviour out of the UI code, so the test code behaves the same as the UI.

This commit fixes all of the tests that were broken by that change. No other logic has been changed.
2024-06-28 16:50:25 +03:00
Matt Ellis
b0e6b72281 Fix regression missing newlines in :print output 2024-06-28 16:50:25 +03:00
Matt Ellis
e6220e5e53 Fix regression clearing output instead of closing 2024-06-28 16:50:25 +03:00
Matt Ellis
3c064845b1 Fix ex text field handling of zoom and font size
The ex text field and output pane will correctly resize and reposition if visible when the IDE zoom value changes. If the editor's font size is zoomed with the mouse wheel, the panels are hidden, so they do not obscure the font zoom indicator widget

Fixes VIM-3417
2024-06-28 16:50:25 +03:00
Alex Plate
736cb219ca Remove the use of RegExp from ExEntryPanel 2024-06-28 16:47:32 +03:00
Alex Plate
fb30e4e387 Adapt the code to the new search helper functions 2024-06-28 16:17:45 +03:00
Alex Plate
74550ffa16 Refactor countWords function 2024-06-28 16:17:45 +03:00
Alex Plate
d0a0672282 Rename .java to .kt 2024-06-28 16:17:45 +03:00
Alex Plate
16e92ddf60 Move SearchHelper classes to kotlin file 2024-06-28 16:17:45 +03:00
Alex Plate
4d8e68d800 Get rid of many unused search functions 2024-06-28 16:17:45 +03:00
Alex Plate
bbebfaf32a Remove some obsolete and deprecated code 2024-06-28 16:17:44 +03:00
filipp
7e56331e47 Fix failing test 2024-06-28 15:47:19 +03:00
Matt Ellis
750db8e71c Fix 'incsearch'+'wrapscan' at bottom of file
If all results are before the caret, make sure it's still possible to highlight the closest match if 'wrapscan' is enabled

Fixes VIM-3505
2024-06-28 13:55:57 +03:00
filipp
4255ef68a3 Post review improvements
Add explanatory comment
2024-06-28 13:55:06 +03:00
Filipp Vakhitov
3313464214 Fix(VIM-3501): Global commands applied to the following lines if matches > 1 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
683ba32a15 Fix test compilation 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
90a60155e5 Move VimscriptParser to vim-engine 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
b25d06ed9e Move Vimscript visitors to vim-engine 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
706ae3dd91 Move GlobalCommand to vim-engine 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
9b15ed8181 Add VimRangeMarker 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
f355bef36b Remove deprecated SearchGroup.java 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
4391e69c48 Move DocumentSearchListener to IjVimSearchGroup 2024-06-28 13:55:06 +03:00
Filipp Vakhitov
0710d80391 Remove SearchGroup from GlobalCommand 2024-06-28 13:55:06 +03:00
filipp
cf41a3a76c Fix(VIM-3507): Mapping exits early on double backslash 2024-06-28 13:46:06 +03:00
jake.peters
31b2cd872f Use when expression instead of convoluted nested if else 2024-06-28 12:39:58 +03:00
jake.peters
2b6945cbb2 Add ability to iterate over all states for unassigned handlers
This is still a WIP
2024-06-28 12:39:58 +03:00
jake.peters
ae5f43918f Use BGT instead of EDT 2024-06-28 12:39:58 +03:00
jake.peters
6b6bc2752e Rename method to be more reflective of its toggle functionality. 2024-06-28 12:39:58 +03:00
jake.peters
4556adae3c Add ability to change all handlers to use either Vim or IDE shortcuts. 2024-06-28 12:39:58 +03:00
Filipp Vakhitov
1b0886041b Fix(VIM-3506): in command mode no longer change cursor to " 2024-06-27 15:46:22 +05:00
dependabot[bot]
16e18f3ca7 Bump io.ktor:ktor-client-core from 2.3.11 to 2.3.12
Bumps [io.ktor:ktor-client-core](https://github.com/ktorio/ktor) from 2.3.11 to 2.3.12.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/2.3.12/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/2.3.11...2.3.12)

---
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-06-26 19:05:22 +03:00
dependabot[bot]
ee0d67fbbb Bump io.ktor:ktor-client-auth from 2.3.11 to 2.3.12
Bumps [io.ktor:ktor-client-auth](https://github.com/ktorio/ktor) from 2.3.11 to 2.3.12.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/2.3.12/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/2.3.11...2.3.12)

---
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-06-26 19:05:12 +03:00
dependabot[bot]
450527f172 Bump io.ktor:ktor-serialization-kotlinx-json from 2.3.11 to 2.3.12
Bumps [io.ktor:ktor-serialization-kotlinx-json](https://github.com/ktorio/ktor) from 2.3.11 to 2.3.12.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/2.3.12/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/2.3.11...2.3.12)

---
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-06-26 18:47:59 +03:00
Matt Ellis
135518ee39 Enable Gradle caching
Without this, running tests could take up to 1 minute, mostly spent configuring Gradle. It appears that the extractor transformer would extract the IDE on each run.
2024-06-26 17:52:50 +03:00
Matt Ellis
58715ecb5f Remove obsolete build feature
Binary releases can be used again now that the latest builds of 241 (2024.1.4) and 242 (EAP) include updates to the Plugin DevKit plugin that can download sources for binary releases. (Binary releases mean the referenced IDE is extracted and run from the same binaries as consumer releases).

This fixes verifyPlugin, which didn't support binary releases being disabled.
2024-06-26 17:52:50 +03:00
IdeaVim Bot
75e26b101d Add Bishwa Saha, Alex Fu to contributors list 2024-06-24 09:55:59 +00:00
Alex Plate
6421a6face Update java version to 17 on github actions 2024-06-24 12:53:55 +03:00
filipp
948520f90a Fix(VIM-3485): Stay in the same split when using (Back) or (Forward) 2024-06-21 17:44:44 +03:00
Matt Ellis
0765118ce2 Stop mapping negative 'scrolljump' to IntelliJ setting 2024-06-21 17:22:36 +03:00
Matt Ellis
efd4c7b617 Fix incorrect scrolling due to 'scrolloff' mapping
If IntelliJ's vertical scroll offset is set to the value of the 'scrolloff' option, IntelliJ's own implementation kicks in. This implementation includes virtual space at the bottom of the file in its calculation, scrolling to a different offset to IdeaVim's. In some circumstances, IdeaVim no longer scrolls, so the last line would no longer stick to the bottom of the screen after `G`. In other circumstances, the scroll would correctly reset, but there would be a visible flash as the editor scrolls to the wrong offset and then back, especially when using backspace at the bottom of the file.

Fixes VIM-3474
2024-06-21 17:22:36 +03:00
Alex Plate
c5346fbece Update NERDTree documentation with newly added commands 2024-06-21 16:56:04 +03:00
Alex Pláte
fe8e8ccc3e Merge pull request #912 from kun-codes/create-files-and-directories
add NERDTree action to create new files and directories
2024-06-21 16:53:01 +03:00
Alex Pláte
eae111bc2c Merge branch 'master' into create-files-and-directories 2024-06-21 16:52:46 +03:00
kun-codes
6a6c1dc6b4 add NERDTree action to delete files and directories 2024-06-21 16:52:01 +03:00
Alex Pláte
86bbb282ab Merge pull request #879 from citizenmatt/feature/gradle-intellij-plugin-v2
Migrate to IntelliJ Platform Gradle Plugin 2.0
2024-06-21 16:31:56 +03:00
Alex Pláte
28aa156cb7 Merge branch 'master' into feature/gradle-intellij-plugin-v2 2024-06-21 16:30:25 +03:00
Matt Ellis
a7814e69de Fix incorrect double registration when opening editors
Ensures that the editor listeners are only registered once when the editor is created. IdeaVim uses two different events to track editor creation, to try to understand how the editor is being created (new window, split, preview, etc.) and this can lead to calling the `EditorListeners.add` method twice. This would create a second `Disposable` and cause handlers to leak. This is most visible when creating the first window and then disabling IdeaVim. This window would still handle drag events like IdeaVim, even changing the caret back to block after the drag finishes.
2024-06-21 13:13:15 +03:00
Alex Fu
1452c116cf Update to include other default key binds 2024-06-21 13:04:36 +03:00
Alex Fu
23dfc4b339 Update docs for multiple-cursors plugin 2024-06-21 13:04:36 +03:00
kun-codes
931d4be972 add NERDTree action to create new files and directories 2024-06-20 17:51:15 +05:30
dependabot[bot]
7dceda587b Bump org.jetbrains.intellij from 1.17.3 to 1.17.4
Bumps org.jetbrains.intellij from 1.17.3 to 1.17.4.

---
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-06-19 18:22:11 +03:00
dependabot[bot]
52a969074d Bump org.eclipse.jgit:org.eclipse.jgit.ssh.apache
Bumps org.eclipse.jgit:org.eclipse.jgit.ssh.apache from 6.9.0.202403050737-r to 6.10.0.202406032230-r.

---
updated-dependencies:
- dependency-name: org.eclipse.jgit:org.eclipse.jgit.ssh.apache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-19 18:22:01 +03:00
lippfi
e7b87d31cf Merge pull request #904 from citizenmatt/bugfix/jumplist
Fix jumplist no longer updated from IDE actions
2024-06-17 17:45:25 +05:00
Matt Ellis
5eb0fae08f Remove explicit since-build from plugin.xml
Let the Gradle plugin manage since-build, based on our target compile version. We leave until-build open ended
2024-06-17 11:18:50 +01:00
Matt Ellis
798d805a0f Provide hint for the CodeQL autobuilder GH action
The autobuilder action can't resolve the 'by project' property to know what our source compatibility is, so we need to give it a hint. If we don't, it will assume the lowest version compatible with the current version of Gradle, which right now is 11.

This value doesn't have to match the `javaVersion` property, but has to allow us to resolve dependencies. Currently, we require at least version 17.
2024-06-17 11:18:50 +01:00
Matt Ellis
0d4ba06e57 Add task to run IDE in split mode 2024-06-17 11:18:43 +01:00
Matt Ellis
4913b13a2d Migrate to IntelliJ Platform Gradle Plugin 2.0
Also updates the GitHub workflows to use the new name for the testIdeUi task, and the TeamCity files to use the new name for the VerifyPlugin task
2024-06-17 11:03:17 +01:00
Matt Ellis
b0bab992db Convert settings.gradle to Kotlin 2024-06-17 10:05:45 +01:00
Matt Ellis
af5f4227b7 Rename settings.gradle to settings.gradle.kts 2024-06-17 10:05:44 +01:00
Matt Ellis
fa6a694ea4 Move some Gradle config blocks around 2024-06-17 10:05:43 +01:00
Filipp Vakhitov
1da7ffc052 Add more logging to spot bug in CommandConsumer 2024-06-15 22:36:28 +02:00
Matt Ellis
c673f5818c Make jumplist project ID key unique
Fixes an issue if multiple projects with the same name are open at the same time
2024-06-14 14:35:48 +01:00
Matt Ellis
ec78a87644 Fix recent places adding to jump list
Fixes VIM-3424
2024-06-14 14:29:12 +01:00
Matt Ellis
69d14ddcf5 Implement clearjumps command 2024-06-14 14:29:12 +01:00
Matt Ellis
f62819df00 Add generated parser files to gitignore 2024-06-14 14:29:11 +01:00
Filipp Vakhitov
39a85b6bc2 Add more logging 2024-06-13 20:58:07 +02:00
dependabot[bot]
f76ae3e867 Bump com.dorongold.task-tree from 3.0.0 to 4.0.0
Bumps com.dorongold.task-tree from 3.0.0 to 4.0.0.

---
updated-dependencies:
- dependency-name: com.dorongold.task-tree
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-12 18:15:21 +03:00
dependabot[bot]
3651e5f2f0 Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 1.9.24-1.0.20 to 2.0.0-1.0.22.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/1.9.24-1.0.20...2.0.0-1.0.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-12 18:15:16 +03:00
Filipp Vakhitov
89e016ef6c Fix infinite loop stuck because of a confirmation dialog appearing over and over 2024-06-12 10:57:45 +03:00
Filipp Vakhitov
e4996f4c4d Reduce distribution size 2024-06-11 01:41:24 +03:00
Filipp Vakhitov
c44ed58142 Make text deletion in change actions a part of insert sequence 2024-06-09 00:33:07 +03:00
Filipp Vakhitov
0091af2a41 Fix(VIM-547): Undo splits Insert mode edits into separate undo chunks
Add tests
2024-06-07 23:03:53 +03:00
Filipp Vakhitov
d1eea68719 Support i_CTRL-G_U 2024-06-07 21:38:11 +03:00
Filipp Vakhitov
133aff7fd8 Add a return type to getInsertSequence method 2024-06-07 21:29:44 +03:00
Filipp Vakhitov
efde94db7a Fix recursive forEachCaretInvocation 2024-06-07 18:05:02 +03:00
Filipp Vakhitov
6ec072b34e End inputSequence on motion in insert mode 2024-06-07 17:12:11 +03:00
Filipp Vakhitov
4027a21514 Support most* cases for starting / ending inputSequence
* - Ending inputSequence on motion in insert mode is not supported in this commit
2024-06-07 16:55:22 +03:00
Filipp Vakhitov
3665b1ab00 Improve UndoRedoHelper to split insert input into multiple undo sequences 2024-06-07 16:27:58 +03:00
Filipp Vakhitov
cf6b292f0c Add methods to work with InsertSequence 2024-06-07 16:24:37 +03:00
Filipp Vakhitov
507e4173d3 Introduce InsertSequence class 2024-06-07 16:09:25 +03:00
Filipp Vakhitov
abc3575d3e Fix command registration order
We should prioritize ij commands over the engine ones
2024-06-07 16:03:27 +03:00
Filipp Vakhitov
2c0ff587e3 Post-review improvements 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
26c87535d6 Fix focus 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
6ac8e672be Fix compilation 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
04ee2dd1e7 Post-rebase fixes 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
3106a98aee Make IjVimSearchGroup a PersistentStateComponent instead of deprecated SearchGroup 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
73769a3472 Move one method to VimProcessGroupBase 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
085e253d77 Fix possible bug in Fleet
Previously, we executed processings first and updated the state afterward. This approach could be problematic for asynchronous key processing, as Fleet might access the current state, which, in the context of async key processing, is a snapshot before execution and does not reflect the actual current KeyHandlerState
2024-06-05 11:35:58 +03:00
Filipp Vakhitov
b2af8f153e Add VimScriptFunctionServiceBase 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
37fb41fca8 Remove exCommandAnnotation option 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
e2b05ab639 Move Vimscript grammar to engine 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
354fd8fef0 Remove EditorHolderService.kt 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
9b97867be1 Support replace mode in command line 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
06685d1721 Move more logic to VimCommandLine interface 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
ae4b88a06b Remove ExEntryPanel and move its method to VimCommandLine 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
c83ecc46ed Rename method 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
c32050a208 Introduce actualText and visibleText for command line 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
4a8c7227e6 Remove default value 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
55e61a7094 Remove count for command line
It was not used anywhere
2024-06-05 11:35:58 +03:00
Filipp Vakhitov
60977d05b6 Deprecate ExEntryPanel 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
601747f720 Changes to replace ExEntryPanel with interface and move more code to engine 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
0c91bc3207 Changes to replace ExEntryPanel with interface and move more code to engine 2024-06-05 11:35:58 +03:00
Filipp Vakhitov
f5cd2c173f Changes to replace ExEntryPanel with interface and move more code to engine 2024-06-05 11:35:58 +03:00
922 changed files with 14824 additions and 18249 deletions

View File

@@ -20,10 +20,10 @@ jobs:
fetch-depth: 300
- name: Get tags
run: git fetch --tags origin
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
@@ -34,7 +34,7 @@ jobs:
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
- name: Update YouTrack
run: ./gradlew updateYoutrackOnCommit
run: ./gradlew --no-configuration-cache updateYoutrackOnCommit
env:
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}

View File

@@ -18,16 +18,16 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 300
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Run tests
run: ./gradlew integrationsTest
run: ./gradlew --no-configuration-cache integrationsTest
env:
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -18,10 +18,10 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 300
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file

View File

@@ -20,17 +20,17 @@ jobs:
fetch-depth: 50
# See end of file updateChangeslog.yml for explanation of this secret
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Update authors
id: update_authors
run: ./gradlew updateMergedPr -PprId=${{ github.event.number }}
run: ./gradlew --no-configuration-cache updateMergedPr -PprId=${{ github.event.number }}
env:
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -16,14 +16,14 @@ jobs:
java-version: 17
- name: Setup FFmpeg
run: brew install ffmpeg
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
# - 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 -Doctopus.handler=false > build/reports/idea.log &
gradle --no-configuration-cache runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
- name: Wait for Idea started
uses: jtalk/url-health-check-action@v3
with:
@@ -37,7 +37,7 @@ jobs:
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
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
- name: Save report
if: always()
uses: actions/upload-artifact@v4
@@ -46,7 +46,7 @@ jobs:
path: |
build/reports
tests/ui-ij-tests/build/reports
sandbox-idea-log
idea-sandbox-log
# build-for-ui-test-linux:
# runs-on: ubuntu-latest
# steps:
@@ -78,4 +78,4 @@ jobs:
# with:
# name: ui-test-fails-report-linux
# path: |
# ui-test-example/build/reports
# ui-test-example/build/reports

View File

@@ -19,14 +19,14 @@ jobs:
python-version: '3.10'
- name: Setup FFmpeg
run: brew install ffmpeg
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
# - 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 &
gradle --no-configuration-cache :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
- name: Wait for Idea started
uses: jtalk/url-health-check-action@v3
with:
@@ -40,7 +40,7 @@ jobs:
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
run: mv build/idea-sandbox/PC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
- name: Save report
if: always()
uses: actions/upload-artifact@v4
@@ -49,4 +49,4 @@ jobs:
path: |
build/reports
tests/ui-py-tests/build/reports
sandbox-idea-log
idea-sandbox-log

View File

@@ -16,14 +16,14 @@ jobs:
java-version: 17
- name: Setup FFmpeg
run: brew install ffmpeg
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
# - 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 &
gradle --no-configuration-cache runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started
uses: jtalk/url-health-check-action@v3
with:
@@ -37,7 +37,7 @@ jobs:
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
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
- name: Save report
if: always()
uses: actions/upload-artifact@v4
@@ -46,7 +46,7 @@ jobs:
path: |
build/reports
tests/ui-ij-tests/build/reports
sandbox-idea-log
idea-sandbox-log
# build-for-ui-test-linux:
# runs-on: ubuntu-latest
# steps:
@@ -78,4 +78,4 @@ jobs:
# with:
# name: ui-test-fails-report-linux
# path: |
# ui-test-example/build/reports
# ui-test-example/build/reports

View File

@@ -25,10 +25,10 @@ jobs:
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
- name: Get tags
run: git fetch --tags origin
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
@@ -40,7 +40,7 @@ jobs:
- name: Update authors
id: update_authors
run: ./gradlew updateAuthors --stacktrace
run: ./gradlew --no-configuration-cache updateAuthors --stacktrace
env:
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -22,10 +22,10 @@ jobs:
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
- name: Get tags
run: git fetch --tags origin
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
@@ -36,7 +36,7 @@ jobs:
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
- name: Update changelog
run: ./gradlew updateChangelog
run: ./gradlew --no-configuration-cache updateChangelog
env:
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
@@ -60,4 +60,4 @@ jobs:
# dependabot updates. See mergeDependatobPR.yml file.
# However, it turned out that GitHub accepts pushes from the actions as a PR and requires checks, that are always
# false for pushing from actions.
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227

View File

@@ -20,10 +20,10 @@ jobs:
fetch-depth: 50
# See end of file updateChangeslog.yml for explanation of this secret
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file

10
.gitignore vendored
View File

@@ -1,5 +1,6 @@
*.swp
/.gradle/
/.intellijPlatform/
/.idea/
!/.idea/scopes
@@ -10,6 +11,8 @@
!/.idea/runConfigurations
!/.idea/codeStyles
!/.idea/vcs.xml
!/.idea/misc.xml
!/.idea/.name
**/build/
**/out/
@@ -22,11 +25,10 @@
.teamcity/*.iml
# Generated by gradle task "generateGrammarSource"
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
# Generated JSONs for lazy classloading
/vim-engine/src/main/resources/ksp-generated
/src/main/resources/ksp-generated
# Created by github automation
settings.xml
.kotlin

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
IdeaVim

22
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<list size="3">
<item index="0" class="java.lang.String" itemvalue="com.intellij.vim.annotations.CommandOrMotion" />
<item index="1" class="java.lang.String" itemvalue="com.intellij.vim.annotations.ExCommand" />
<item index="2" class="java.lang.String" itemvalue="com.intellij.vim.annotations.VimscriptFunction" />
</list>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/.teamcity/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
</project>

View File

@@ -12,7 +12,7 @@
<option name="taskNames">
<list>
<option value="check" />
<option value="runPluginVerifier" />
<option value="verifyPlugin" />
</list>
</option>
<option name="vmOptions" value="" />
@@ -20,6 +20,7 @@
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,25 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle">
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="runIdeSplitMode" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@@ -37,7 +37,7 @@ object Compatibility : IdeaVimBuildType({
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
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
# java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
""".trimIndent()

View File

@@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({
steps {
gradle {
tasks = "clean runPluginVerifier"
tasks = "clean verifyPlugin"
buildFile = ""
enableStacktrace = true
}

View File

@@ -19,8 +19,6 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
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.script
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
object ReleaseMajor : ReleasePlugin("major")
object ReleaseMinor : ReleasePlugin("minor")
@@ -146,6 +144,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
gradle {
name = "Run Integrations"
tasks = "releaseActions"
gradleParams = "--no-configuration-cache"
}
// gradle {
// name = "Slack Notification"
@@ -158,16 +157,4 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
teamcitySshKey = "IdeaVim ssh keys"
}
}
failureConditions {
failOnMetricChange {
metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
threshold = 5
units = BuildFailureOnMetric.MetricUnit.PERCENTS
comparison = BuildFailureOnMetric.MetricComparison.DIFF
compareTo = build {
buildRule = lastSuccessful()
}
}
}
})

View File

@@ -511,6 +511,30 @@ Contributors:
[![icon][github]](https://github.com/Aisper)
&nbsp;
Egor Nikolaevsky
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
[![icon][github]](https://github.com/kun-codes)
&nbsp;
Bishwa Saha,
* [![icon][mail]](mailto:alexfu@fastmail.com)
[![icon][github]](https://github.com/alexfu)
&nbsp;
Alex Fu
* [![icon][mail]](mailto:jakepeters199@hotmail.com)
[![icon][github]](https://github.com/LazyScaper)
&nbsp;
Jake
* [![icon][mail]](mailto:the1xdeveloper@gmail.com)
[![icon][github]](https://github.com/The1xDeveloper)
&nbsp;
The1xDeveloper
* [![icon][mail]](mailto:shaunewilliams@gmail.com)
[![icon][github]](https://github.com/shaunlebron)
&nbsp;
shaun
* [![icon][mail]](mailto:i.i.babko@gmail.com)
[![icon][github]](https://github.com/igorbabko)
&nbsp;
Igor Babko
Previous contributors:

View File

@@ -27,8 +27,8 @@ usual beta standards.
Since version 2.9.0, the changelog can be found on YouTrack
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
* [To Be Released](https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20)
* [Version Fixes](https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20%7BFix%20versions%7D%20asc)
## 2.9.0, 2024-02-20

View File

@@ -62,12 +62,16 @@ for a few days or send it to a friend for testing.
If you are looking for:
- Vim commands (`w`, `<C-O>`, `p`, etc.):
- Any particular command: `package-info.java`.
- Any particular command:
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_commands.json)
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_commands.json)
- How commands are executed in common: `EditorActionHandlerBase`.
- Key mapping: `KeyHandler.handleKey()`.
- Ex commands (`:set`, `:s`, `:nohlsearch`):
- Any particular ex command: package `com.maddyhome.idea.vim.ex.handler`.
- Any particular command:
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_ex_commands.json)
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_ex_commands.json)
- Vim script grammar: `Vimscript.g4`.
- Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
- Vim script executor: `Executor`.

View File

@@ -109,7 +109,6 @@ etc
See also:
* [The list of all supported commands](src/main/java/com/maddyhome/idea/vim/package-info.java)
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
@@ -222,13 +221,13 @@ Ex commands or via `:map` command mappings:
* Execute an action by `{action_id}`. Works from Ex command line.
* Please don't use `:action` in mappings. Use `<Action>` instead.
### Finding action ids:
### Finding action IDs:
* IJ provides `IdeaVim: track action Ids` command to show the id of the executed actions.
* IJ provides `IdeaVim: track action IDs` command to show the id of the executed actions.
This command can be found in "Search everywhere" (double `shift`).
<details>
<summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
<summary><strong>"Track action IDs" Details</strong> (click to see)</summary>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif">
<img src="assets/readme/track_action_light.gif" alt="track action ids"/>
@@ -369,6 +368,8 @@ is the full list of synonyms.
- Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27):
> The local value is set to -123456 when the global value is to be used.
- Vi (not Vim) is a POSIX standard, and [has a spec](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html)! Vim is mostly POSIX compliant when Vi compatibility is selected with the `'compatible'` option, but there are still some differences that can be changed with `'copoptions'`. The spec is interesting because it documents the behaviour of different commands in a stricter style than the user documentation, describing the current line and column after the command, for example. [More details can be found by reading `:help posix`](https://vimhelp.org/vi_diff.txt.html#posix).
License
-------

View File

@@ -1,6 +1,5 @@
IdeaVim project is licensed under MIT license except the following parts of it:
* File [RegExp.kt](src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt) is licensed under Vim License.
* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.

View File

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

View File

@@ -37,7 +37,8 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
val fileContent = json.encodeToString(commands)
val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` }))
val fileContent = json.encodeToString(sortedCommands)
filePath.writeText(fileContent)
return emptyList()

View File

@@ -37,7 +37,8 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
val fileContent = json.encodeToString(commandToClass)
val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
val fileContent = json.encodeToString(sortedCommandToClass)
filePath.writeText(fileContent)
return emptyList()

View File

@@ -37,7 +37,8 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
val fileContent = json.encodeToString(nameToClass)
val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
val fileContent = json.encodeToString(sortedNameToClass)
filePath.writeText(fileContent)
return emptyList()

View File

@@ -13,7 +13,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import com.intellij.vim.processors.VimscriptFunctionProcessor
public class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return VimscriptFunctionProcessor(environment)
}

View File

@@ -32,6 +32,8 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RepositoryBuilder
import org.intellij.markdown.ast.getTextInNode
import org.jetbrains.changelog.Changelog
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
import org.kohsuke.github.GHUser
import java.net.HttpURLConnection
import java.net.URL
@@ -43,19 +45,19 @@ buildscript {
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:2.3.11")
classpath("io.ktor:ktor-client-core:2.3.12")
classpath("io.ktor:ktor-client-cio:2.3.10")
classpath("io.ktor:ktor-client-auth:2.3.11")
classpath("io.ktor:ktor-client-auth:2.3.12")
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
// This comes from the changelog plugin
// classpath("org.jetbrains:markdown:0.3.1")
@@ -63,45 +65,27 @@ buildscript {
}
plugins {
antlr
java
kotlin("jvm") version "1.9.22"
kotlin("jvm") version "2.0.0"
application
id("java-test-fixtures")
id("org.jetbrains.intellij") version "1.17.3"
id("org.jetbrains.changelog") version "2.2.0"
id("org.jetbrains.intellij.platform") version "2.0.0-rc2"
id("org.jetbrains.changelog") version "2.2.1"
id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "3.0.0"
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
id("com.dorongold.task-tree") version "4.0.0"
id("com.google.devtools.ksp") version "2.0.0-1.0.23"
}
ksp {
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
arg("ex_commands_file", "intellij_ex_commands.json")
arg("commands_file", "intellij_commands.json")
}
afterEvaluate {
// tasks.named("kspKotlin").configure { dependsOn("clean") }
tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestKotlin").configure { enabled = false }
}
val moduleSources by configurations.registering
// Import variables from gradle.properties file
val javaVersion: String by project
val kotlinVersion: String by project
val ideaVersion: String by project
val ideaType: String by project
val downloadIdeaSources: String by project
val instrumentPluginCode: String by project
val antlrVersion: String by project
val remoteRobotVersion: String by project
val splitModeVersion: String by project
val publishChannels: String by project
val publishToken: String by project
@@ -109,25 +93,45 @@ val publishToken: String by project
val slackUrl: String by project
val youtrackToken: String by project
val releaseType: String? by project
repositories {
mavenCentral()
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
intellijPlatform {
defaultRepositories()
}
}
dependencies {
api(project(":vim-engine"))
ksp(project(":annotation-processors"))
implementation(project(":annotation-processors"))
compileOnly(project(":annotation-processors"))
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
compileOnly("org.jetbrains:annotations:24.1.0")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
antlr("org.antlr:antlr4:$antlrVersion")
intellijPlatform {
// Note that it is also possible to use local("...") to compile against a locally installed IDE
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
// Or something like: intellijIdeaUltimate(ideaVersion)
create(ideaType, ideaVersion)
pluginVerifier()
zipSigner()
instrumentationTools()
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.19")
plugin("com.intellij.classic.ui", "242.20224.159")
}
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
// --------- Test dependencies ----------
testImplementation(testFixtures(project(":")))
testApi("com.squareup.okhttp3:okhttp:4.12.0")
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
@@ -141,14 +145,20 @@ dependencies {
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
// Can be removed when IJPL-159134 is fixed
// testRuntimeOnly("junit:junit:4.13.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
}
configurations {
@@ -159,6 +169,8 @@ configurations {
tasks {
test {
useJUnitPlatform()
// 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,
// so we can turn it on for local development
@@ -171,6 +183,9 @@ tasks {
}
compileJava {
// CodeQL can't resolve the 'by project' property, so we need to give it a hint. This is the minimum version we need
// so doesn't have to match exactly
// Hint for the CodeQL autobuilder: sourceCompatibility = 17
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
@@ -183,35 +198,95 @@ tasks {
// 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",
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
"-Xskip-prerelease-check",
"-Xallow-unstable-dependencies",
)
// allWarningsAsErrors = true
}
}
compileTestKotlin {
enabled = false
kotlinOptions {
jvmTarget = javaVersion
apiVersion = "1.9"
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
freeCompilerArgs += listOf("-Xskip-prerelease-check", "-Xallow-unstable-dependencies")
// 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")
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
}
// Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use
// a custom task (see below)
runIde {
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
}
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies
// Note that the version must be greater than the plugin's target version, for obvious reasons
// val runIdeCustom by intellijPlatformTesting.runIde.registering {
// type = IntelliJPlatformType.Rider
// version = "2024.1.2"
// }
// Uncomment to run the plugin in a locally installed IDE
// val runIdeLocal by intellijPlatformTesting.runIde.registering {
// localPath = file("/Users/{user}/Applications/WebStorm.app")
// }
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
task {
jvmArgumentProviders += CommandLineArgumentProvider {
listOf(
"-Drobot-server.port=8082",
"-Dide.mac.message.dialogs.as.sheets=false",
"-Djb.privacy.policy.text=<!--999.999-->",
"-Djb.consents.confirmation.enabled=false",
"-Dide.show.tips.on.startup.default.value=false",
"-Doctopus.handler=" + (System.getProperty("octopus.handler") ?: true),
)
}
}
plugins {
robotServerPlugin(remoteRobotVersion)
}
}
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
splitMode = true
splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND
// Frontend split mode support requires 242+
// TODO: Remove this once IdeaVim targets 242, as the task will naturally use the target version to run
version.set(splitModeVersion)
}
// Add plugin open API sources to the plugin ZIP
val sourcesJar by registering(Jar::class) {
dependsOn(moduleSources)
destinationDirectory.set(layout.buildDirectory.dir("libs"))
archiveClassifier.set(DocsType.SOURCES)
from(sourceSets.main.map { it.kotlin })
from(provider {
moduleSources.map {
it.map { jarFile -> zipTree(jarFile) }
}
})
}
buildPlugin {
dependsOn(sourcesJar)
from(sourcesJar) { into("lib/src") }
}
}
java {
@@ -221,7 +296,6 @@ java {
}
kotlin {
explicitApi()
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(javaVersion))
}
@@ -236,96 +310,68 @@ gradle.projectsEvaluated {
// --- Intellij plugin
intellij {
version.set(ideaVersion)
type.set(ideaType)
pluginName.set("IdeaVim")
intellijPlatform {
pluginConfiguration {
name = "IdeaVim"
changeNotes.set(
"""
Undo in IdeaVim now works like in Vim<br/>
Caret movement is no longer a separate undo step, and full insert is undoable in one step.<br/>
<a href="https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks">Share Feedback</a>
<br/>
<br/>
<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>
""".trimIndent()
)
updateSinceUntilBuild.set(false)
ideaVersion {
// Let the Gradle plugin set the since-build version. It defaults to the version of the IDE we're building against
// specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified.
// The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it
// with a null provider (the provider is important).
// By letting the Gradle plugin handle this, the Plugin DevKit IntelliJ plugin cannot help us with the "Usage of
// IntelliJ API not available in older IDEs" inspection. However, since our since-build is the version we compile
// against, we can never get an API that's newer - it would be an unresolved symbol.
untilBuild.set(provider { null })
}
}
downloadSources.set(downloadIdeaSources.toBoolean())
instrumentCode.set(instrumentPluginCode.toBoolean())
intellijRepository.set("https://www.jetbrains.com/intellij-repository")
plugins.set(listOf("AceJump:3.8.11"))
}
tasks {
publishPlugin {
publishing {
channels.set(publishChannels.split(","))
token.set(publishToken)
}
signPlugin {
signing {
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
}
runPluginVerifier {
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
teamCityOutputFormat.set(true)
}
generateGrammarSource {
maxHeapSize = "128m"
arguments.addAll(listOf("-package", "com.maddyhome.idea.vim.vimscript.parser.generated", "-visitor"))
outputDirectory = file("src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated")
}
named("compileKotlin") {
dependsOn("generateGrammarSource")
}
named("compileTestKotlin") {
dependsOn("generateTestGrammarSource")
}
named("compileTestFixturesKotlin") {
dependsOn("generateTestFixturesGrammarSource")
}
// Add plugin open API sources to the plugin ZIP
val createOpenApiSourceJar by registering(Jar::class) {
dependsOn("generateGrammarSource")
// Java sources
from(sourceSets.main.get().java) {
include("**/com/maddyhome/idea/vim/**/*.java")
verifyPlugin {
teamCityOutputFormat = true
ides {
recommended()
}
from(project(":vim-engine").sourceSets.main.get().java) {
include("**/com/maddyhome/idea/vim/**/*.java")
}
// Kotlin sources
from(kotlin.sourceSets.main.get().kotlin) {
include("**/com/maddyhome/idea/vim/**/*.kt")
}
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
include("**/com/maddyhome/idea/vim/**/*.kt")
}
destinationDirectory.set(layout.buildDirectory.dir("libs"))
archiveClassifier.set("src")
}
buildPlugin {
dependsOn(createOpenApiSourceJar)
from(createOpenApiSourceJar) { into("lib/src") }
}
patchPluginXml {
// Don't forget to update plugin.xml
sinceBuild.set("241.15989.150")
changeNotes.set(
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
)
}
instrumentCode.set(instrumentPluginCode.toBoolean())
}
// --- Tests
tasks {
test {
useJUnitPlatform()
}
ksp {
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
arg("ex_commands_file", "intellij_ex_commands.json")
arg("commands_file", "intellij_commands.json")
}
afterEvaluate {
// tasks.named("kspKotlin").configure { dependsOn("clean") }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestKotlin").configure { enabled = false }
}
// --- Changelog
changelog {
@@ -451,6 +497,8 @@ val fixVersionsElementType = "VersionBundleElement"
tasks.register("releaseActions") {
group = "other"
doLast {
if (releaseType == "patch") return@doLast
val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
if (tickets.isNotEmpty()) {
println("Updating statuses for tickets: $tickets")
@@ -910,12 +958,12 @@ fun changes(): List<Change> {
println("Start changes processing")
for (message in messages) {
println("Processing '$message'...")
val lowercaseMessage = message.toLowerCase()
val lowercaseMessage = message.lowercase()
val regex = "^fix\\((vim-\\d+)\\):".toRegex()
val findResult = regex.find(lowercaseMessage)
if (findResult != null) {
println("Message matches")
val value = findResult.groups[1]!!.value.toUpperCase()
val value = findResult.groups[1]!!.value.uppercase()
val shortMessage = message.drop(findResult.range.last + 1).trim()
newFixes += Change(value, shortMessage)
} else {

View File

@@ -1,6 +1,6 @@
Welcome to the IdeaVim wiki!
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins.md)
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples.md)
- List of "set" commands: ["set" commands](set-commands.md)
- Docs about "select" mode in vim: [select mode](Select-mode.md)
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins)
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples)
- List of "set" commands: ["set" commands](set-commands)
- Docs about "select" mode in vim: [select mode](Select-mode)

View File

@@ -82,7 +82,7 @@ Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
### Instructions
[See here](NERDTree-support.md).
[See here](NERDTree-support).
</details>
@@ -129,8 +129,26 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
</details>
### Instructions
https://github.com/terryma/vim-multiple-cursors/blob/master/doc/multiple_cursors.txt
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
```
" Remap multiple-cursors shortcuts to match terryma/vim-multiple-cursors
nmap <C-n> <Plug>NextWholeOccurrence
xmap <C-n> <Plug>NextWholeOccurrence
nmap g<C-n> <Plug>NextOccurrence
xmap g<C-n> <Plug>NextOccurrence
xmap <C-x> <Plug>SkipOccurrence
xmap <C-p> <Plug>RemoveOccurrence
" Note that the default <A-n> and g<A-n> shortcuts don't work on Mac due to dead keys.
" <A-n> is used to enter accented text e.g. ñ
" Feel free to pick your own mappings that are not affected. I like to use <leader>
nmap <leader><C-n> <Plug>AllWholeOccurrences
xmap <leader><C-n> <Plug>AllWholeOccurrences
nmap <leader>g<C-n> <Plug>AllOccurrences
xmap <leader>g<C-n> <Plug>AllOccurrences
```
</details>

View File

@@ -40,30 +40,33 @@ Plug 'nerdtree'
- `:NERDTreeFind`
- `:NERDTreeRefreshRoot`
| Key | Description | Map Setting |
|---------|---------------------------------------------------------|--------------------------------|
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
| Key | Description | Map Setting |
|---------|--------------------------------------------------------|--------------------------------|
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
| `d` | Delete file or directory | `g:NERDTreeMapDelete` |
| `n` | Create File | `g:NERDTreeMapNewFile` |
| `N` | Create Directory | `g:NERDTreeMapNewDir` |
### Troubleshooting

View File

@@ -5,9 +5,9 @@ Using actions from external plugins is the same, as tracking and reusing any IDE
1. Install the plugin via Marketplace
2. Enable action tracking. You can enable it by one of the following ways:
* Execute `:set trackactionids` ex command or just `:set tai`
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action Ids" to find the toggle that enables and disables action tracking
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action IDs" to find the toggle that enables and disables action tracking
3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner:
<img alt="Notification" src="images/action-id-notification.png"/>
4. Copy the action id from the notification to create the following mapping in your .ideavimrc
```map <leader>t <Action>(de.netnexus.CamelCasePlugin.ToggleCamelCase)```
```map <leader>t <Action>(de.netnexus.CamelCasePlugin.ToggleCamelCase)```

View File

@@ -1,7 +1,7 @@
# Support Guide
This document is created to help our support team.
It's not intended to be read by the users as it brings to value to them.
It's not intended to be read by the users as it brings no value to them.
## Support channels
@@ -35,4 +35,4 @@ IdeaVim has multiple YouTrack statuses, main are:
# ~.ideavimrc file
`~/.ideavimrc` is the file that is used for IdeaVim configuration. It may affect behaviour of the program,
so it makes sense to additionally request this file in case the issues can't be reproduced.
so it makes sense to additionally request this file in case the issues can't be reproduced.

View File

@@ -8,22 +8,33 @@
# suppress inspection "UnusedProperty" for whole file
#ideaVersion=LATEST-EAP-SNAPSHOT
ideaVersion=2024.1.1
# ideaVersion is the version of the IDE that will be added as a compile-time dependency. The format can be either
# product version (e.g. 2024.1, 2024.1.1) or build (e.g. 241.15989.150, 241-EAP-SNAPSHOT). The dependency will be
# resolved against the configured repositories, which by default includes Maven releases and snapshots, the CDN used to
# download consumer releases, the plugin marketplace and so on.
# You can find an example list of all CDN based versions for IDEA Community here:
# https://data.services.jetbrains.com/products?code=IC
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
ideaVersion=2024.2
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC
downloadIdeaSources=true
instrumentPluginCode=true
version=chylex-35
version=chylex-40
javaVersion=17
remoteRobotVersion=0.11.22
remoteRobotVersion=0.11.23
antlrVersion=4.10.1
kotlin.incremental.useClasspathSnapshot=false
# [VERSION UPDATE] 2024.2 - remove when IdeaVim targets 2024.2
# Running IdeaVim in split mode requires 242. Update this version once 242 has been released, and remove it completely
# when IdeaVim targets 242, at which point runIdeSplitMode will run correctly with the target version.
# See also runIdeSplitMode
splitModeVersion=242-EAP-SNAPSHOT
# Please don't forget to update kotlin version in buildscript section
# Also update kotlinxSerializationVersion version
kotlinVersion=1.9.22
kotlinVersion=2.0.0
publishToken=token
publishChannels=eap
@@ -36,6 +47,7 @@ youtrackToken=
# Gradle settings
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.caching=true
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
kotlin.stdlib.default.dependency=false

View File

@@ -19,9 +19,8 @@ exclude:
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.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/package-info.java
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
- vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
- tests/ui-fixtures
dependencyIgnores:

View File

@@ -20,17 +20,17 @@ repositories {
}
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.25")
implementation("io.ktor:ktor-client-core:2.3.11")
implementation("io.ktor:ktor-client-core:2.3.12")
implementation("io.ktor:ktor-client-cio:2.3.10")
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
implementation("io.ktor:ktor-client-auth:2.3.11")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
implementation("io.ktor:ktor-client-auth:2.3.12")
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
implementation("com.vdurmont:semver4j:3.1.0")
}

View File

@@ -1,20 +0,0 @@
// Set repository for snapshot versions of gradle plugin
pluginManagement {
repositories {
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
gradlePluginPortal()
}
}
rootProject.name = 'IdeaVIM'
include 'vim-engine'
include 'scripts'
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'

21
settings.gradle.kts Normal file
View File

@@ -0,0 +1,21 @@
// Set repository for snapshot versions of gradle plugin
pluginManagement {
repositories {
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
}
gradlePluginPortal()
}
}
rootProject.name = "IdeaVIM"
include("vim-engine")
include("scripts")
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

@@ -8,14 +8,19 @@
package com.maddyhome.idea.vim
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManagerListener
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.updateSettings.impl.UpdateSettings
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.initInjector
import com.maddyhome.idea.vim.ui.JoinEap
import com.maddyhome.idea.vim.ui.JoinEap.EAP_LINK
/**
* @author Alex Plate
@@ -28,6 +33,11 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
if (firstInitializationOccurred) return
firstInitializationOccurred = true
if (!VimPlugin.getVimState().wasSubscibedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
VimPlugin.getVimState().wasSubscibedToEAPAutomatically = true
UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
}
// This code should be executed once
VimPlugin.getInstance().initialize()
}
@@ -36,6 +46,7 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
// This is a temporal workaround for VIM-2487
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
override fun projectClosingBeforeSave(project: Project) {
initInjector()
// TODO: Confirm context in CWM scenario
if (injector.globalIjOptions().closenotebooks) {
injector.editorGroup.getEditors().forEach { vimEditor ->

View File

@@ -14,36 +14,36 @@ import com.maddyhome.idea.vim.key.MappingOwner
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
public object RegisterActions {
object RegisterActions {
/**
* Register all the key/action mappings for the plugin.
*/
@JvmStatic
public fun registerActions() {
fun registerActions() {
registerVimCommandActions()
registerShortcutsWithoutActions()
}
public fun findAction(id: String): EditorActionHandlerBase? {
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
fun findAction(id: String): EditorActionHandlerBase? {
val commandBean = IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id }
?: EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
return commandBean.instance
}
public fun findActionOrDie(id: String): EditorActionHandlerBase {
fun findActionOrDie(id: String): EditorActionHandlerBase {
return findAction(id) ?: throw RuntimeException("Action $id is not registered")
}
@JvmStatic
public fun unregisterActions() {
fun unregisterActions() {
val keyGroup = VimPlugin.getKeyIfCreated()
keyGroup?.unregisterCommandActions()
}
private fun registerVimCommandActions() {
val parser = VimPlugin.getKey()
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
}
private fun registerShortcutsWithoutActions() {

View File

@@ -37,8 +37,9 @@ import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
import com.maddyhome.idea.vim.listener.VimListenerManager;
import com.maddyhome.idea.vim.newapi.IjVimInjector;
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.vimscript.services.VariableService;
import com.maddyhome.idea.vim.yank.YankGroupBase;
import org.jdom.Element;
@@ -46,6 +47,7 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
@@ -66,7 +68,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
static {
VimInjectorKt.setInjector(new IjVimInjector());
IjVimInjectorKt.initInjector();
}
private final @NotNull VimState state = new VimState();
@@ -123,12 +125,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return (FileGroup)VimInjectorKt.getInjector().getFile();
}
public static @NotNull SearchGroup getSearch() {
return ApplicationManager.getApplication().getService(SearchGroup.class);
public static @NotNull IjVimSearchGroup getSearch() {
return ApplicationManager.getApplication().getService(IjVimSearchGroup.class);
}
public static @Nullable SearchGroup getSearchIfCreated() {
return ApplicationManager.getApplication().getServiceIfCreated(SearchGroup.class);
public static @Nullable IjVimSearchGroup getSearchIfCreated() {
return ApplicationManager.getApplication().getServiceIfCreated(IjVimSearchGroup.class);
}
public static @NotNull ProcessGroup getProcess() {
@@ -283,11 +285,11 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (!ApplicationManager.getApplication().isUnitTestMode()) {
try {
VimInjectorKt.injector.getOptionGroup().startInitVimRc();
injector.getOptionGroup().startInitVimRc();
executeIdeaVimRc(editor);
}
finally {
VimInjectorKt.injector.getOptionGroup().endInitVimRc();
injector.getOptionGroup().endInitVimRc();
}
}
}
@@ -345,14 +347,14 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
}
private void turnOffPlugin(boolean unsubscribe) {
SearchGroup searchGroup = getSearchIfCreated();
IjVimSearchGroup searchGroup = getSearchIfCreated();
if (searchGroup != null) {
searchGroup.turnOff();
}
if (unsubscribe) {
VimListenerManager.INSTANCE.turnOff();
}
ExEntryPanel.fullReset();
injector.getCommandLine().fullReset();
// Unregister vim actions in command mode
RegisterActions.unregisterActions();

View File

@@ -12,13 +12,13 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.maddyhome.idea.vim.group.EditorHolderService
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
@Service(Service.Level.PROJECT)
internal class VimProjectService(val project: Project) : Disposable {
override fun dispose() {
// Not sure if this is a best solution
EditorHolderService.getInstance().editor = null
ExEntryPanel.getInstance().setEditor(null)
}
companion object {

View File

@@ -32,7 +32,7 @@ import javax.swing.KeyStroke
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
* way to get ideavim keys for this plugin. See VIM-3085
*/
public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
private val handler = KeyHandler.getInstance()
private val traceTime = injector.globalOptions().ideatracetime

View File

@@ -8,6 +8,6 @@
package com.maddyhome.idea.vim.action
public object IntellijCommandProvider : CommandProvider {
object IntellijCommandProvider : CommandProvider {
override val commandListFileName: String = "intellij_commands.json"
}

View File

@@ -0,0 +1,52 @@
package com.maddyhome.idea.vim.action
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.DumbAwareAction
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
class VimRunLastMacroInOpenFiles : DumbAwareAction() {
override fun update(e: AnActionEvent) {
val lastRegister = injector.macro.lastRegister
val isEnabled = lastRegister != 0.toChar()
e.presentation.isEnabled = isEnabled
e.presentation.text = if (isEnabled) "Run Macro '${lastRegister}' in Open Files" else "Run Last Macro in Open Files"
}
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.EDT
}
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return
val editors = fileEditorManager.allEditors.filterIsInstance<TextEditor>()
WriteCommandAction.writeCommandAction(project)
.withName(e.presentation.text)
.withGlobalUndo()
.withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION)
.run<RuntimeException> {
val reg = injector.macro.lastRegister
for (editor in editors) {
fileEditorManager.openFile(editor.file, true)
val vimEditor = editor.editor.vim
vimEditor.mode = Mode.NORMAL()
KeyHandler.getInstance().reset(vimEditor)
injector.macro.playbackRegister(vimEditor, IjEditorExecutionContext(e.dataContext), reg, 1)
}
}
}
}

View File

@@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.KeyHandler
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.group.EditorHolderService
import com.maddyhome.idea.vim.group.IjOptionConstants
import com.maddyhome.idea.vim.group.IjOptions
import com.maddyhome.idea.vim.handler.enableOctopus
@@ -44,7 +43,9 @@ import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
import com.maddyhome.idea.vim.listener.AceJumpService
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.initInjector
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import com.maddyhome.idea.vim.ui.ex.ExTextField
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import java.awt.event.InputEvent
@@ -60,11 +61,14 @@ import javax.swing.KeyStroke
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
* way to get ideavim keys for this plugin. See VIM-3085
*/
public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
init {
initInjector()
}
private val traceTime: Boolean
get() {
// Make sure the injector is initialized
VimPlugin.getInstance()
return injector.globalOptions().ideatracetime
}
@@ -257,7 +261,7 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
private fun getEditor(e: AnActionEvent): Editor? {
return e.getData(PlatformDataKeys.EDITOR)
?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
EditorHolderService.getInstance().editor
ExEntryPanel.getInstance().ijEditor
} else {
null
}

View File

@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
@@ -102,7 +102,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val argument = cmd.argument ?: return false
if (!editor.vimStateMachine.isDotRepeatInProgress) {
if (!editor.inRepeatMode) {
argumentCaptured = argument
}
val range = getMotionRange(editor, context, argument, operatorArguments)

View File

@@ -17,7 +17,6 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
@@ -25,7 +24,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val state = editor.vimStateMachine
val state = injector.vimState
val lastCommand = VimRepeater.lastChangeCommand
if (lastCommand == null && Extension.lastExtensionHandler == null) return false

View File

@@ -20,7 +20,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
import com.maddyhome.idea.vim.newapi.ijOptions
@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
public class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
override val type: Command.Type = Command.Type.DELETE
override fun runAsMulticaret(
editor: VimEditor,

View File

@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
import com.maddyhome.idea.vim.newapi.ijOptions
@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.DELETE
override fun execute(

View File

@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
* @author vlan
*/
@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.DELETE
override fun executeForAllCarets(

View File

@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
* @author vlan
*/
@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.DELETE
override fun executeForAllCarets(

View File

@@ -36,6 +36,18 @@ internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELET
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
override val type: Command.Type = Command.Type.MOTION
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
override fun execute(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments
): Boolean {
val undo = injector.undo
val nanoTime = System.nanoTime()
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
return super.execute(editor, context, cmd, operatorArguments)
}
}
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
@@ -48,6 +60,18 @@ internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
override val type: Command.Type = Command.Type.MOTION
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
override fun execute(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments
): Boolean {
val undo = injector.undo
val nanoTime = System.nanoTime()
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
return super.execute(editor, context, cmd, operatorArguments)
}
}
@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
@@ -55,7 +79,7 @@ internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_READONLY
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
injector.actionExecutor.executeAction(IdeActions.ACTION_QUICK_JAVADOC, context)
injector.actionExecutor.executeAction(editor, IdeActions.ACTION_QUICK_JAVADOC, context)
return true
}
}

View File

@@ -9,21 +9,18 @@
package com.maddyhome.idea.vim.command
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.SelectionType
/**
* COMPATIBILITY-LAYER: Additional class
* Please see: https://jb.gg/zo8n0r
*/
public class CommandState(private val machine: VimStateMachine) {
@Deprecated("Use `injector.vimState`")
class CommandState(private val machine: VimStateMachine) {
public val isOperatorPending: Boolean
get() = machine.isOperatorPending(machine.mode)
public val mode: Mode
val mode: Mode
get() {
val myMode = machine.mode
return when (myMode) {
@@ -37,13 +34,23 @@ public class CommandState(private val machine: VimStateMachine) {
}
}
public val commandBuilder: CommandBuilder
get() = machine.commandBuilder
@Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
"KeyHandler.getInstance().keyHandlerState.commandBuilder",
"com.maddyhome.idea.vim.KeyHandler"
)
)
val commandBuilder: CommandBuilder
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
public val mappingState: MappingState
get() = machine.mappingState
@Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
"KeyHandler.getInstance().keyHandlerState.mappingState",
"com.maddyhome.idea.vim.KeyHandler"
)
)
val mappingState: MappingState
get() = KeyHandler.getInstance().keyHandlerState.mappingState
public enum class Mode {
enum class Mode {
// Basic modes
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
@@ -51,22 +58,15 @@ public class CommandState(private val machine: VimStateMachine) {
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
}
public enum class SubMode {
enum class SubMode {
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
}
public companion object {
companion object {
@JvmStatic
public fun getInstance(editor: Editor): CommandState {
return CommandState(editor.vim.vimStateMachine)
@Deprecated("Use `injector.vimState`")
fun getInstance(editor: Editor): CommandState {
return CommandState(injector.vimState)
}
}
}
internal val CommandState.SubMode.engine: SelectionType
get() = when (this) {
CommandState.SubMode.NONE -> error("Unexpected value")
CommandState.SubMode.VISUAL_CHARACTER -> SelectionType.CHARACTER_WISE
CommandState.SubMode.VISUAL_LINE -> SelectionType.LINE_WISE
CommandState.SubMode.VISUAL_BLOCK -> SelectionType.BLOCK_WISE
}

View File

@@ -12,18 +12,18 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
import com.maddyhome.idea.vim.newapi.vim
public class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
public fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
public companion object {
public fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
companion object {
fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
// logical position "expands" tabs
val logicalPosition = editor.offsetToLogicalPosition(offset)
val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
}
public fun atCaret(editor: Editor): CharacterPosition {
fun atCaret(editor: Editor): CharacterPosition {
return fromOffset(editor, editor.caretModel.offset)
}
}

View File

@@ -12,17 +12,17 @@ import com.intellij.application.options.CodeStyle
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
import com.maddyhome.idea.vim.api.VimIndentConfig
internal class IndentConfig private constructor(indentOptions: IndentOptions) {
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig {
private val indentSize = indentOptions.INDENT_SIZE
private val tabSize = indentOptions.TAB_SIZE
private val isUseTabs = indentOptions.USE_TAB_CHARACTER
fun getTotalIndent(count: Int): Int = indentSize * count
override fun getIndentSize(depth: Int): Int = indentSize * depth
override fun createIndentByDepth(depth: Int): String = createIndentBySize(getIndentSize(depth))
fun createIndentByCount(count: Int): String = createIndentBySize(getTotalIndent(count))
fun createIndentBySize(size: Int): String {
override fun createIndentBySize(size: Int): String {
val tabCount: Int
val spaceCount: Int
if (isUseTabs) {

View File

@@ -18,6 +18,8 @@ import kotlin.reflect.KProperty
internal class VimState {
var isIdeaJoinNotified by StateProperty("idea-join")
var isIdeaPutNotified by StateProperty("idea-put")
var isNewUndoNotified by StateProperty("idea-undo")
var wasSubscibedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
fun readData(element: Element) {
val notifications = element.getChild("notifications")

View File

@@ -9,62 +9,142 @@ package com.maddyhome.idea.vim.ex
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.api.VimExOutputPanel
import com.maddyhome.idea.vim.api.VimOutputPanelBase
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.vimExOutput
import com.maddyhome.idea.vim.ui.ExOutputPanel
import java.lang.ref.WeakReference
import javax.swing.KeyStroke
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
public class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanelBase() {
private var isActiveInTestMode = false
override val isActive: Boolean
val editor get() = myEditor.get()
val isActive: Boolean
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.isPanelActive(myEditor)
editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
} else {
isActiveInTestMode
}
override var text: String? = null
override fun addText(text: String, isNewLine: Boolean) {
if (this.text.isNotEmpty() && isNewLine) this.text += "\n$text" else this.text += text
}
override fun show() {
if (editor == null) return
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
if (currentPanel != null && currentPanel != this) currentPanel.close()
editor!!.vimExOutput = this
val exOutputPanel = ExOutputPanel.getInstance(editor!!)
if (!exOutputPanel.myActive) {
if (ApplicationManager.getApplication().isUnitTestMode) {
isActiveInTestMode = true
} else {
exOutputPanel.activate()
}
}
}
override fun scrollPage() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.scrollPage()
}
override fun scrollHalfPage() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.scrollHalfPage()
}
override fun scrollLine() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.scrollLine()
}
override var text: String = ""
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.getInstance(myEditor).text
editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
} else {
// ExOutputPanel always returns a non-null string
field
}
set(value) {
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
// never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
val newValue = value.removeSuffix("\n")
if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.getInstance(myEditor).setText(value ?: "")
editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
} else {
field = value
isActiveInTestMode = !value.isNullOrEmpty()
field = newValue
isActiveInTestMode = newValue.isNotEmpty()
}
}
override var label: String
get() {
val notNullEditor = editor ?: return ""
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return ""
return panel.myLabel.text
}
set(value) {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.myLabel.text = value
}
override fun output(text: String) {
fun output(text: String) {
this.text = text
}
override fun clear() {
text = null
fun clear() {
text = ""
}
override val atEnd: Boolean
get() {
val notNullEditor = editor ?: return false
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return false
return panel.isAtEnd()
}
override fun onBadKey() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.onBadKey()
}
override fun close(key: KeyStroke?) {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.close(key)
}
override fun close() {
if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.getInstance(myEditor).close()
editor?.let { ExOutputPanel.getInstance(it).close() }
}
else {
isActiveInTestMode = false
}
}
public companion object {
companion object {
@JvmStatic
public fun getInstance(editor: Editor): ExOutputModel {
fun getInstance(editor: Editor): ExOutputModel {
var model = editor.vimExOutput
if (model == null) {
model = ExOutputModel(editor)
model = ExOutputModel(WeakReference(editor))
editor.vimExOutput = model
}
return model
}
@JvmStatic
fun tryGetInstance(editor: Editor) = editor.vimExOutput
}
}

View File

@@ -9,7 +9,6 @@ package com.maddyhome.idea.vim.extension
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
@@ -25,13 +24,14 @@ import com.maddyhome.idea.vim.common.CommandAlias
import com.maddyhome.idea.vim.common.CommandAliasHandler
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.noneOfEnum
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.ui.ModalEntry
import com.maddyhome.idea.vim.ui.ex.ExEntryPanelService
import com.maddyhome.idea.vim.vimscript.model.Executable
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
import com.maddyhome.idea.vim.vimscript.model.VimLContext
@@ -51,13 +51,13 @@ import javax.swing.KeyStroke
*
* @author vlan
*/
public object VimExtensionFacade {
object VimExtensionFacade {
private val LOG = logger<VimExtensionFacade>()
/** The 'map' command for mapping keys to handlers defined in extensions. */
@JvmStatic
public fun putExtensionHandlerMapping(
fun putExtensionHandlerMapping(
modes: Set<MappingMode>,
fromKeys: List<KeyStroke>,
pluginOwner: MappingOwner,
@@ -68,13 +68,15 @@ public object VimExtensionFacade {
}
/**
* COMPATIBILITY-LAYER: Additional method
* Please see: https://jb.gg/zo8n0r
*/
/** The 'map' command for mapping keys to handlers defined in extensions. */
@JvmStatic
public fun putExtensionHandlerMapping(
@Deprecated(
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
ReplaceWith(
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
"com.maddyhome.idea.vim.VimPlugin"
)
)
fun putExtensionHandlerMapping(
modes: Set<MappingMode>,
fromKeys: List<KeyStroke>,
pluginOwner: MappingOwner,
@@ -86,7 +88,7 @@ public object VimExtensionFacade {
/** The 'map' command for mapping keys to other keys. */
@JvmStatic
public fun putKeyMapping(
fun putKeyMapping(
modes: Set<MappingMode>,
fromKeys: List<KeyStroke>,
pluginOwner: MappingOwner,
@@ -98,7 +100,7 @@ public object VimExtensionFacade {
/** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
@JvmStatic
public fun putKeyMappingIfMissing(
fun putKeyMappingIfMissing(
modes: Set<MappingMode>,
fromKeys: List<KeyStroke>,
pluginOwner: MappingOwner,
@@ -112,7 +114,7 @@ public object VimExtensionFacade {
/**
* Equivalent to calling 'command' to set up a user-defined command or alias
*/
public fun addCommand(
fun addCommand(
name: String,
handler: CommandAliasHandler,
) {
@@ -123,7 +125,7 @@ public object VimExtensionFacade {
* Equivalent to calling 'command' to set up a user-defined command or alias
*/
@JvmStatic
public fun addCommand(
fun addCommand(
name: String,
minimumNumberOfArguments: Int,
maximumNumberOfArguments: Int,
@@ -141,7 +143,7 @@ public object VimExtensionFacade {
* leaves the editor in the insert mode if it's been activated.
*/
@JvmStatic
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
val keyHandler = KeyHandler.getInstance()
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
@@ -149,8 +151,8 @@ public object VimExtensionFacade {
/** Returns a single key stroke from the user input similar to 'getchar()'. */
@JvmStatic
public fun inputKeyStroke(editor: Editor): KeyStroke {
if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
fun inputKeyStroke(editor: Editor): KeyStroke {
if (editor.vim.inRepeatMode) {
val input = Extension.consumeKeystroke()
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
@@ -181,43 +183,43 @@ public object VimExtensionFacade {
/** Returns a string typed in the input box similar to 'input()'. */
@JvmStatic
public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
}
/** Get the current contents of the given register similar to 'getreg()'. */
@JvmStatic
public fun getRegister(register: Char): List<KeyStroke>? {
fun getRegister(register: Char): List<KeyStroke>? {
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
return reg.keys
}
@JvmStatic
public fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
val reg = caret.registerStorage.getRegister(register) ?: return null
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
val reg = injector.registerGroup.getRegister(register) ?: return null
return reg.keys
}
/** Set the current contents of the given register */
@JvmStatic
public fun setRegister(register: Char, keys: List<KeyStroke?>?) {
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
}
/** Set the current contents of the given register */
@JvmStatic
public fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
}
/** Set the current contents of the given register */
@JvmStatic
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
}
@JvmStatic
public fun exportScriptFunction(
fun exportScriptFunction(
scope: Scope?,
name: String,
args: List<String>,
@@ -253,7 +255,7 @@ public object VimExtensionFacade {
}
}
public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
editor, context, args ->
@@ -274,6 +276,6 @@ public fun VimExtensionFacade.exportOperatorFunction(name: String, function: Ope
}
}
public fun interface ScriptFunction {
public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
}
fun interface ScriptFunction {
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
}

View File

@@ -19,12 +19,12 @@ import com.maddyhome.idea.vim.newapi.ij
* COMPATIBILITY-LAYER: Created a class, renamed original class
* Please see: https://jb.gg/zo8n0r
*/
public interface VimExtensionHandler : ExtensionHandler {
interface VimExtensionHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
execute(editor.ij, context.ij)
}
public fun execute(editor: Editor, context: DataContext)
fun execute(editor: Editor, context: DataContext)
public abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
}

View File

@@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.extension.argtextobj;
import com.intellij.openapi.editor.Document;
import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.command.*;
@@ -23,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.state.KeyHandlerState;
import com.maddyhome.idea.vim.state.mode.Mode;
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
import org.jetbrains.annotations.Nls;
@@ -244,19 +245,18 @@ public class VimArgTextObjExtension implements VimExtension {
@Override
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
IjVimEditor vimEditor = (IjVimEditor) editor;
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
//noinspection DuplicatedCode
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
editor.nativeCarets().forEach((VimCaret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
if (editor.getMode() instanceof Mode.VISUAL) {
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
} else {
InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
@@ -265,7 +265,7 @@ public class VimArgTextObjExtension implements VimExtension {
}
});
} else {
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
}
}

View File

@@ -18,6 +18,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor
@@ -45,7 +46,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
import com.maddyhome.idea.vim.helper.PsiHelper
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
@@ -64,7 +64,7 @@ internal class CommentaryExtension : VimExtension {
selectionType: SelectionType,
resetCaret: Boolean = true,
): Boolean {
val mode = editor.vimStateMachine.mode
val mode = editor.mode
if (mode !is Mode.VISUAL) {
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
}
@@ -79,11 +79,12 @@ internal class CommentaryExtension : VimExtension {
val project = editor.ij.project!!
val callback = { afterCommenting(mode, editor, resetCaret, range) }
actions.any { executeActionWithCallbackOnSuccess(it, project, context, callback) }
actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
}
}
private fun executeActionWithCallbackOnSuccess(
editor: VimEditor,
action: String,
project: Project,
context: ExecutionContext,
@@ -92,7 +93,7 @@ internal class CommentaryExtension : VimExtension {
val res = Ref.create<Boolean>(false)
AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
action,
{ res.set(injector.actionExecutor.executeAction(action, context)) },
{ res.set(injector.actionExecutor.executeAction(editor, name = action, context = context)) },
{ if (res.get()) callback() })
return res.get()
}
@@ -183,10 +184,10 @@ internal class CommentaryExtension : VimExtension {
override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val commandState = editor.vimStateMachine
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
commandState.commandBuilder.completeCommandPart(Argument(command))
val keyState = KeyHandler.getInstance().keyHandlerState
keyState.commandBuilder.completeCommandPart(Argument(command))
}
}

View File

@@ -23,13 +23,15 @@ import com.intellij.util.Alarm
import com.intellij.util.Alarm.ThreadToUse
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.ModeChangeListener
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.common.VimYankListener
import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.VimNlsSafe
import com.maddyhome.idea.vim.listener.VimInsertListener
import com.maddyhome.idea.vim.listener.VimYankListener
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import org.jetbrains.annotations.NonNls
@@ -75,7 +77,7 @@ internal class HighlightColorResetter : LafManagerListener {
*
* When a new text is yanked or user starts editing, the old highlighting would be deleted.
*/
internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeListener {
private val highlightHandler = HighlightHandler()
private var initialised = false
@@ -83,8 +85,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
override fun init() {
// Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
VimPlugin.getYank().addListener(this)
VimPlugin.getChange().addInsertListener(this)
injector.listenersNotifier.modeChangeListeners.add(this)
injector.listenersNotifier.yankListeners.add(this)
// Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
// IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
@@ -105,8 +107,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
override fun dispose() {
// Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
VimPlugin.getYank().removeListener(this)
VimPlugin.getChange().removeInsertListener(this)
injector.listenersNotifier.modeChangeListeners.remove(this)
injector.listenersNotifier.yankListeners.remove(this)
highlightHandler.clearYankHighlighters()
initialised = false
@@ -117,7 +119,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
highlightHandler.highlightYankRange(editor.ij, range)
}
override fun insertModeStarted(editor: Editor) {
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
if (editor.mode !is Mode.INSERT) return
ensureInitialised()
highlightHandler.clearYankHighlighters()
}

View File

@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
@@ -40,7 +41,6 @@ import com.maddyhome.idea.vim.handler.toMotionOrError
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.PsiHelper
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
@@ -91,22 +91,23 @@ internal class Matchit : VimExtension {
private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val commandState = editor.vimStateMachine
val count = commandState.commandBuilder.count
val keyHandler = KeyHandler.getInstance()
val keyState = keyHandler.keyHandlerState
val count = keyState.commandBuilder.count
// Reset the command count so it doesn't transfer onto subsequent commands.
editor.vimStateMachine.commandBuilder.resetCount()
keyState.commandBuilder.resetCount()
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
val isInOpPending = commandState.isOperatorPending(editor.mode)
val isInOpPending = keyHandler.isOperatorPending(editor.mode, keyState)
if (isInOpPending) {
val matchitAction = MatchitAction()
matchitAction.reverse = reverse
matchitAction.isInOpPending = true
commandState.commandBuilder.completeCommandPart(
keyState.commandBuilder.completeCommandPart(
Argument(
Command(
count,

View File

@@ -30,11 +30,11 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.group.visual.vimSetSelection
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.SearchHelper
import com.maddyhome.idea.vim.helper.SearchOptions
import com.maddyhome.idea.vim.helper.endOffsetInclusive
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.findWordUnderCursor
import com.maddyhome.idea.vim.helper.inVisualMode
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
import com.maddyhome.idea.vim.helper.userData
@@ -235,7 +235,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
val text = if (editor.inVisualMode) {
primaryCaret.selectedText ?: return
} else {
val range = SearchHelper.findWordUnderCursor(editor, primaryCaret) ?: return
val range = findWordUnderCursor(editor, primaryCaret) ?: return
if (range.startOffset > primaryCaret.offset) return
IjVimEditor(editor).getText(range)
}
@@ -300,7 +300,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
}
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
val range = SearchHelper.findWordUnderCursor(editor, caret) ?: return null
val range = findWordUnderCursor(editor, caret) ?: return null
if (range.startOffset > caret.offset) return null
enterVisualMode(editor.vim)
@@ -327,6 +327,6 @@ internal class VimMultipleCursorsExtension : VimExtension {
private fun makePattern(text: String, whole: Boolean): String {
// Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
return "\\V\\C" + SearchHelper.makeSearchPattern(text, whole)
return "\\V\\C" + if (whole) "\\<$text\\>" else text
}
}

View File

@@ -478,6 +478,12 @@ internal class NerdTree : VimExtension {
NerdAction.ToIj("SynchronizeCurrentFile"),
)
registerCommand("NERDTreeMapToggleHidden", "I", NerdAction.ToIj("ProjectView.ShowExcludedFiles"))
registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
registerCommand("NERDTreeMapDelete", "d", NerdAction.ToIj("\$Delete"))
registerCommand("NERDTreeMapCopy", "y", NerdAction.ToIj("\$Copy"))
registerCommand("NERDTreeMapPaste", "v", NerdAction.ToIj("\$Paste"))
registerCommand("NERDTreeMapRename", "<C-r>", NerdAction.ToIj("RenameElement"))
registerCommand("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))

View File

@@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.extension.replacewithregister
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
@@ -28,7 +29,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
@@ -144,7 +144,7 @@ internal class ReplaceWithRegister : VimExtension {
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
val registerGroup = injector.registerGroup
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return
var usedType = savedRegister.type
var usedText = savedRegister.text
@@ -166,17 +166,18 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
putToLine = -1,
)
val vimEditor = editor.vim
val keyHandler = KeyHandler.getInstance()
ClipboardOptionHelper.IdeaputDisabler().use {
VimPlugin.getPut().putText(
vimEditor,
context.vim,
putData,
operatorArguments = OperatorArguments(
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
keyHandler.isOperatorPending(vimEditor.mode, keyHandler.keyHandlerState),
0,
editor.vim.mode,
),
saveToRegister = false
)
}
}
}

View File

@@ -36,6 +36,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.group.findBlockRange
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimCaret
@@ -50,6 +51,7 @@ import com.maddyhome.idea.vim.state.mode.selectionType
import org.jetbrains.annotations.NonNls
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
import com.maddyhome.idea.vim.state.mode.returnTo
/**
* Port of vim-surround.
@@ -115,6 +117,9 @@ internal class VimSurroundExtension : VimExtension {
// it.moveToOffset(lineStartOffset)
}
// Jump back to start
if (editor.mode !is Mode.NORMAL) {
editor.mode = Mode.NORMAL()
}
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
}
@@ -136,7 +141,7 @@ internal class VimSurroundExtension : VimExtension {
}
runWriteAction {
// Leave visual mode
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
editor.exitVisualMode()
}
}
}
@@ -306,8 +311,10 @@ internal class VimSurroundExtension : VimExtension {
private fun getSurroundRange(caret: VimCaret): TextRange? {
val editor = caret.editor
val ijEditor = editor.ij
return when (ijEditor.vim.mode) {
if (editor.mode is Mode.CMD_LINE) {
editor.mode = (editor.mode as Mode.CMD_LINE).returnTo()
}
return when (editor.mode) {
is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
else -> null
@@ -350,6 +357,9 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
val tagInput = inputString(editor, context, "<", '>')
if (editor.vim.mode is Mode.CMD_LINE) {
editor.vim.mode = editor.vim.mode.returnTo()
}
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
return if (matcher.find()) {
val tagName = matcher.group(1)
@@ -366,6 +376,9 @@ private fun inputFunctionName(
withInternalSpaces: Boolean,
): Pair<String, String>? {
val functionNameInput = inputString(editor, context, "function: ", null)
if (editor.vim.mode is Mode.CMD_LINE) {
editor.vim.mode = editor.vim.mode.returnTo()
}
if (functionNameInput.isEmpty()) return null
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
}

View File

@@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.extension.textobjentire;
import com.intellij.openapi.editor.Caret;
import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.api.ExecutionContext;
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
import com.maddyhome.idea.vim.api.VimEditor;
@@ -23,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.state.KeyHandlerState;
import com.maddyhome.idea.vim.state.mode.Mode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -133,17 +134,18 @@ public class VimTextObjEntireExtension implements VimExtension {
@Override
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(editor);
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
//noinspection DuplicatedCode
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
if (editor.getMode() instanceof Mode.VISUAL) {
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
} else {
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
@@ -153,7 +155,7 @@ public class VimTextObjEntireExtension implements VimExtension {
});
} else {
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
textObjectHandler, Command.Type.MOTION,
EnumSet.noneOf(CommandFlags.class))));
}

View File

@@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.extension.textobjindent;
import com.intellij.openapi.editor.Caret;
import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.api.ExecutionContext;
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
import com.maddyhome.idea.vim.api.VimEditor;
@@ -24,7 +25,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.state.KeyHandlerState;
import com.maddyhome.idea.vim.state.mode.Mode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -263,17 +264,18 @@ public class VimIndentObject implements VimExtension {
@Override
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
IjVimEditor vimEditor = (IjVimEditor)editor;
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
if (editor.getMode() instanceof Mode.VISUAL) {
EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
} else {
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
@@ -283,7 +285,7 @@ public class VimIndentObject implements VimExtension {
});
} else {
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
textObjectHandler, Command.Type.MOTION,
EnumSet.noneOf(CommandFlags.class))));
}

View File

@@ -15,76 +15,35 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.openapi.editor.event.EditorMouseListener
import com.intellij.openapi.editor.impl.TextRangeInterval
import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiUtilBase
import com.intellij.util.containers.ContainerUtil
import com.maddyhome.idea.vim.EventFacade
import com.maddyhome.idea.vim.api.BufferPosition
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimChangeGroupBase
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimMotionGroupBase
import com.maddyhome.idea.vim.api.anyNonWhitespace
import com.maddyhome.idea.vim.api.getLineEndForOffset
import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.getText
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.lineLength
import com.maddyhome.idea.vim.api.normalizeOffset
import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.IndentConfig.Companion.create
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.group.MotionGroup.Companion.getMotionRange2
import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
import com.maddyhome.idea.vim.handler.commandContinuation
import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.CharacterHelper.changeCase
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.NumberType
import com.maddyhome.idea.vim.helper.SearchHelper
import com.maddyhome.idea.vim.helper.endOffsetInclusive
import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
import com.maddyhome.idea.vim.listener.VimInsertListener
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.regexp.VimRegex
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
import org.jetbrains.annotations.TestOnly
import java.math.BigInteger
import java.util.*
import java.util.function.Consumer
import kotlin.math.max
/**
* Provides all the insert/replace related functionality
*/
public class ChangeGroup : VimChangeGroupBase() {
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
class ChangeGroup : VimChangeGroupBase() {
private val listener: EditorMouseListener = object : EditorMouseListener {
override fun mouseClicked(event: EditorMouseEvent) {
val editor = event.editor
@@ -94,7 +53,7 @@ public class ChangeGroup : VimChangeGroupBase() {
}
}
public fun editorCreated(editor: Editor?, disposable: Disposable) {
fun editorCreated(editor: Editor?, disposable: Disposable) {
EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
}
@@ -102,6 +61,9 @@ public class ChangeGroup : VimChangeGroupBase() {
val editor = (vimEditor as IjVimEditor).editor
val ijContext = context.ij
val doc = vimEditor.editor.document
val undo = injector.undo
val nanoTime = System.nanoTime()
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
CommandProcessor.getInstance().executeCommand(
editor.project, {
ApplicationManager.getApplication()
@@ -141,163 +103,6 @@ public class ChangeGroup : VimChangeGroupBase() {
}
}
override fun getDeleteRangeAndType2(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument,
isChange: Boolean,
operatorArguments: OperatorArguments,
): Pair<TextRange, SelectionType>? {
val range = getMotionRange2(
(editor as IjVimEditor).editor,
(caret as IjVimCaret).caret,
(context as IjEditorExecutionContext).context,
argument,
operatorArguments
)
?: return null
// Delete motion commands that are not linewise become linewise if all the following are true:
// 1) The range is across multiple lines
// 2) There is only whitespace before the start of the range
// 3) There is only whitespace after the end of the range
var type: SelectionType
type = if (argument.motion.isLinewiseMotion()) {
SelectionType.LINE_WISE
} else {
SelectionType.CHARACTER_WISE
}
val motion = argument.motion
if (!isChange && !motion.isLinewiseMotion()) {
val start = editor.offsetToBufferPosition(range.startOffset)
val end = editor.offsetToBufferPosition(range.endOffset)
if (start.line != end.line) {
val offset1 = range.startOffset
if (!editor.anyNonWhitespace(offset1, -1)) {
val offset = range.endOffset
if (!editor.anyNonWhitespace(offset, 1)) {
type = SelectionType.LINE_WISE
}
}
}
}
return Pair(range, type)
}
/**
* Toggles the case of count characters
*
* @param editor The editor to change
* @param caret The caret on which the operation is performed
* @param count The number of characters to change
* @return true if able to change count characters
*/
override fun changeCaseToggleCharacter(editor: VimEditor, caret: VimCaret, count: Int): Boolean {
val allowWrap = injector.options(editor).whichwrap.contains("~")
var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
if (motion is Motion.Error) return false
changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
motion = injector.motion.getHorizontalMotion(
editor,
caret,
count,
false,
allowWrap
) // same but without allow end because we can change till end, but can't move caret there
if (motion is AbsoluteOffset) {
caret.moveToOffset(editor.normalizeOffset(motion.offset, false))
}
return true
}
override fun blockInsert(
editor: VimEditor,
context: ExecutionContext,
range: TextRange,
append: Boolean,
operatorArguments: OperatorArguments,
): Boolean {
val lines = getLinesCountInVisualBlock(editor, range)
val startPosition = editor.offsetToBufferPosition(range.startOffset)
val mode = operatorArguments.mode
val visualBlockMode = mode is VISUAL && mode.selectionType === SelectionType.BLOCK_WISE
for (caret in editor.carets()) {
val line = startPosition.line
var column = startPosition.column
if (!visualBlockMode) {
column = 0
} else if (append) {
column += range.maxLength
if (caret.vimLastColumn == VimMotionGroupBase.LAST_COLUMN) {
column = VimMotionGroupBase.LAST_COLUMN
}
}
val lineLength = editor.lineLength(line)
if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
val offset = editor.getLineEndOffset(line)
insertText(editor, caret, offset, pad)
}
if (visualBlockMode || !append) {
(caret as IjVimCaret).caret.moveToInlayAwareLogicalPosition(LogicalPosition(line, column))
}
if (visualBlockMode) {
setInsertRepeat(lines, column, append)
}
}
if (visualBlockMode || !append) {
insertBeforeCursor(editor, context)
} else {
insertAfterCursor(editor, context)
}
return true
}
/**
* Changes the case of all the characters in the range
*
* @param editor The editor to change
* @param caret The caret to be moved
* @param range The range to change
* @param type The case change type (TOGGLE, UPPER, LOWER)
* @return true if able to delete the text, false if not
*/
override fun changeCaseRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: Char): Boolean {
val starts = range.startOffsets
val ends = range.endOffsets
for (i in ends.indices.reversed()) {
changeCase(editor, caret, starts[i], ends[i], type)
}
caret.moveToOffset(range.startOffset)
return true
}
/**
* This performs the actual case change.
*
* @param editor The editor to change
* @param start The start offset to change
* @param end The end offset to change
* @param type The type of change (TOGGLE, UPPER, LOWER)
*/
private fun changeCase(editor: VimEditor, caret: VimCaret, start: Int, end: Int, type: Char) {
var start = start
var end = end
if (start > end) {
val t = end
end = start
start = t
}
end = editor.normalizeOffset(end, true)
val chars = editor.text()
val sb = StringBuilder()
for (i in start until end) {
sb.append(changeCase(chars[i], type))
}
replaceText(editor, caret, start, end, sb.toString())
}
private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) {
if (caret != editor.primaryCaret()) {
(editor as IjVimEditor).editor.caretModel.addCaret(
@@ -306,88 +111,13 @@ public class ChangeGroup : VimChangeGroupBase() {
}
}
/**
* Changes the case of all the character moved over by the motion argument.
*
* @param editor The editor to change
* @param caret The caret on which motion pretends to be performed
* @param context The data context
* @param type The case change type (TOGGLE, UPPER, LOWER)
* @param argument The motion command
* @return true if able to delete the text, false if not
*/
override fun changeCaseMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext?,
type: Char,
argument: Argument,
operatorArguments: OperatorArguments,
): Boolean {
val range = injector.motion.getMotionRange(
editor, caret, context!!, argument,
operatorArguments
)
return range != null && changeCaseRange(editor, caret, range, type)
}
override fun reformatCodeMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument,
operatorArguments: OperatorArguments,
): Boolean {
val range = injector.motion.getMotionRange(
editor, caret, context, argument,
operatorArguments
)
return range != null && reformatCodeRange(editor, caret, range)
}
override fun reformatCodeSelection(editor: VimEditor, caret: VimCaret, range: VimSelection) {
val textRange = range.toVimTextRange(true)
reformatCodeRange(editor, caret, textRange)
}
private fun reformatCodeRange(editor: VimEditor, caret: VimCaret, range: TextRange): Boolean {
val starts = range.startOffsets
val ends = range.endOffsets
val firstLine = editor.offsetToBufferPosition(range.startOffset).line
for (i in ends.indices.reversed()) {
val startOffset = editor.getLineStartForOffset(starts[i])
val offset = ends[i] - if (startOffset == ends[i]) 0 else 1
val endOffset = editor.getLineEndForOffset(offset)
reformatCode(editor, startOffset, endOffset)
}
val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
caret.moveToOffset(newOffset)
return true
}
private fun reformatCode(editor: VimEditor, start: Int, end: Int) {
override fun reformatCode(editor: VimEditor, start: Int, end: Int) {
val project = (editor as IjVimEditor).editor.project ?: return
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
}
override fun autoIndentMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument,
operatorArguments: OperatorArguments,
) {
val range = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
if (range != null) {
autoIndentRange(
editor, caret, context,
TextRange(range.startOffset, range.endOffsetInclusive)
)
}
}
override fun autoIndentRange(
editor: VimEditor,
caret: VimCaret,
@@ -442,362 +172,14 @@ public class ChangeGroup : VimChangeGroupBase() {
}
}
override fun indentLines(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
lines: Int,
dir: Int,
operatorArguments: OperatorArguments,
) {
val start = caret.offset
val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.add", imports = ["import com.maddyhome.idea.vim.api.injector"]))
fun addInsertListener(listener: VimInsertListener) {
injector.listenersNotifier.modeChangeListeners.add(listener)
}
override fun indentMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument,
dir: Int,
operatorArguments: OperatorArguments,
) {
val range = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
if (range != null) {
indentRange(editor, caret, context, range, 1, dir, operatorArguments)
}
}
override fun indentRange(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
range: TextRange,
count: Int,
dir: Int,
operatorArguments: OperatorArguments,
) {
if (logger.isDebugEnabled) {
logger.debug("count=$count")
}
// Remember the current caret column
val intendedColumn = caret.vimLastColumn
val indentConfig = create((editor as IjVimEditor).editor)
val sline = editor.offsetToBufferPosition(range.startOffset).line
val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
.toInt() else endLogicalPosition.line
if (range.isMultiple) {
val from = editor.offsetToBufferPosition(range.startOffset).column
if (dir == 1) {
// Right shift blockwise selection
val indent = indentConfig.createIndentByCount(count)
for (l in sline..eline) {
val len = editor.lineLength(l)
if (len > from) {
val spos = BufferPosition(l, from, false)
insertText(editor, caret, spos, indent)
}
}
} else {
// Left shift blockwise selection
val chars = editor.text()
for (l in sline..eline) {
val len = editor.lineLength(l)
if (len > from) {
val spos = BufferPosition(l, from, false)
val epos = BufferPosition(l, from + indentConfig.getTotalIndent(count) - 1, false)
val wsoff = editor.bufferPositionToOffset(spos)
val weoff = editor.bufferPositionToOffset(epos)
var pos: Int
pos = wsoff
while (pos <= weoff) {
if (charType(editor, chars[pos], false) !== CharacterHelper.CharacterType.WHITESPACE) {
break
}
pos++
}
if (pos > wsoff) {
deleteText(editor, TextRange(wsoff, pos), null, caret, operatorArguments, true)
}
}
}
}
} else {
// Shift non-blockwise selection
for (l in sline..eline) {
val soff = editor.getLineStartOffset(l)
val eoff = editor.getLineEndOffset(l, true)
val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
val col = editor.offsetToBufferPosition(woff).column
val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
.toInt()
if (col > 0 || soff != eoff) {
val indent = indentConfig.createIndentBySize(limit)
replaceText(editor, caret, soff, woff, indent)
}
}
}
if (!editor.editor.inInsertMode) {
if (!range.isMultiple) {
// The caret has moved, so reset the intended column before trying to get the expected offset
val newCaret = caret.setVimLastColumnAndGetCaret(intendedColumn)
val offset = injector.motion.moveCaretToLineWithStartOfLineOption(editor, sline, caret)
newCaret.moveToOffset(offset)
} else {
caret.moveToOffset(range.startOffset)
}
}
}
/**
* Sort range of text with a given comparator
*
* @param editor The editor to replace text in
* @param range The range to sort
* @param lineComparator The comparator to use to sort
* @param sortOptions The option to sort the range
* @return true if able to sort the text, false if not
*/
override fun sortRange(
editor: VimEditor, caret: VimCaret, range: LineRange, lineComparator: Comparator<String>,
sortOptions: SortOption,
): Boolean {
val startLine = range.startLine
val endLine = range.endLine
val count = range.size
if (count < 2) {
return false
}
val startOffset = editor.getLineStartOffset(startLine)
val endOffset = editor.getLineEndOffset(endLine)
val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(startOffset, endOffset))
val lines = selectedText.split("\n")
val modifiedLines = sortOptions.pattern?.let {
if (sortOptions.sortOnPattern) {
extractPatternFromLines(editor, lines, startLine, it)
} else {
deletePatternFromLines(editor, lines, startLine, it)
}
} ?: lines
val sortedLines = lines.zip(modifiedLines)
.sortedWith { l1, l2 -> lineComparator.compare(l1.second, l2.second) }
.map {it.first}
.toMutableList()
if (sortOptions.unique) {
val iterator = sortedLines.iterator()
var previous: String? = null
while (iterator.hasNext()) {
val current = iterator.next()
if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
iterator.remove()
} else {
previous = current
}
}
}
if (sortedLines.isEmpty()) {
return false
}
replaceText(editor, caret, startOffset, endOffset, StringUtil.join(sortedLines, "\n"))
return true
}
private fun extractPatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
val regex = VimRegex(pattern)
return lines.mapIndexed { i: Int, line: String ->
val result = regex.findInLine(editor, startLine + i, 0)
when (result) {
is VimMatchResult.Success -> result.value
is VimMatchResult.Failure -> line
}
}
}
private fun deletePatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
val regex = VimRegex(pattern)
return lines.mapIndexed { i: Int, line: String ->
val result = regex.findInLine(editor, startLine + i, 0)
when (result) {
is VimMatchResult.Success -> line.substring(result.value.length, line.length)
is VimMatchResult.Failure -> line
}
}
}
/**
* Perform increment and decrement for numbers in visual mode
*
*
* Flag [avalanche] marks if increment (or decrement) should be performed in avalanche mode
* (for v_g_Ctrl-A and v_g_Ctrl-X commands)
*
* @return true
*/
override fun changeNumberVisualMode(
editor: VimEditor,
caret: VimCaret,
selectedRange: TextRange,
count: Int,
avalanche: Boolean,
): Boolean {
val nf: List<String> = injector.options(editor).nrformats
val alpha = nf.contains("alpha")
val hex = nf.contains("hex")
val octal = nf.contains("octal")
val numberRanges = SearchHelper.findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
val newNumbers: MutableList<String?> = ArrayList()
for (i in numberRanges.indices) {
val numberRange = numberRanges[i]
val iCount = if (avalanche) (i + 1) * count else count
val newNumber = changeNumberInRange(editor, numberRange, iCount, alpha, hex, octal)
newNumbers.add(newNumber)
}
for (i in newNumbers.indices.reversed()) {
// Replace text bottom up. In other direction ranges will be desynchronized after inc numbers like 99
val (first) = numberRanges[i]
val newNumber = newNumbers[i]
replaceText(editor, caret, first.startOffset, first.endOffset, newNumber!!)
}
(caret as IjVimCaret).caret.moveToInlayAwareOffset(selectedRange.startOffset)
return true
}
override fun changeNumber(editor: VimEditor, caret: VimCaret, count: Int): Boolean {
val nf: List<String> = injector.options(editor).nrformats
val alpha = nf.contains("alpha")
val hex = nf.contains("hex")
val octal = nf.contains("octal")
val range =
SearchHelper.findNumberUnderCursor((editor as IjVimEditor).editor, (caret as IjVimCaret).caret, alpha, hex, octal)
if (range == null) {
logger.debug("no number on line")
return false
}
val newNumber = changeNumberInRange(editor, range, count, alpha, hex, octal)
return if (newNumber == null) {
false
} else {
replaceText(editor, caret, range.first.startOffset, range.first.endOffset, newNumber)
caret.caret.moveToInlayAwareOffset(range.first.startOffset + newNumber.length - 1)
true
}
}
override fun reset() {
strokes.clear()
repeatCharsCount = 0
if (lastStrokes != null) {
lastStrokes!!.clear()
}
}
override fun saveStrokes(newStrokes: String?) {
val chars = newStrokes!!.toCharArray()
strokes.add(chars)
}
private fun changeNumberInRange(
editor: VimEditor,
range: Pair<TextRange, NumberType>,
count: Int,
alpha: Boolean,
hex: Boolean,
octal: Boolean,
): String? {
val text = editor.getText(range.first)
val numberType = range.second
if (logger.isDebugEnabled) {
logger.debug("found range $range")
logger.debug("text=$text")
}
var number = text
if (text.isEmpty()) {
return null
}
var ch = text[0]
if (hex && NumberType.HEX == numberType) {
if (!text.lowercase(Locale.getDefault()).startsWith(HEX_START)) {
throw RuntimeException("Hex number should start with 0x: $text")
}
for (i in text.length - 1 downTo 2) {
val index = "abcdefABCDEF".indexOf(text[i])
if (index >= 0) {
lastLower = index < 6
break
}
}
var num = BigInteger(text.substring(2), 16)
num = num.add(BigInteger.valueOf(count.toLong()))
if (num.compareTo(BigInteger.ZERO) < 0) {
num = BigInteger(MAX_HEX_INTEGER, 16).add(BigInteger.ONE).add(num)
}
number = num.toString(16)
number = number.padStart(text.length - 2, '0')
if (!lastLower) {
number = number.uppercase(Locale.getDefault())
}
number = text.substring(0, 2) + number
} else if (octal && NumberType.OCT == numberType && text.length > 1) {
if (!text.startsWith("0")) throw RuntimeException("Oct number should start with 0: $text")
var num = BigInteger(text, 8).add(BigInteger.valueOf(count.toLong()))
if (num.compareTo(BigInteger.ZERO) < 0) {
num = BigInteger("1777777777777777777777", 8).add(BigInteger.ONE).add(num)
}
number = num.toString(8)
number = "0" + number.padStart(text.length - 1, '0')
} else if (alpha && NumberType.ALPHA == numberType) {
if (!Character.isLetter(ch)) throw RuntimeException("Not alpha number : $text")
ch += count.toChar().code
if (Character.isLetter(ch)) {
number = ch.toString()
}
} else if (NumberType.DEC == numberType) {
if (ch != '-' && !Character.isDigit(ch)) throw RuntimeException("Not dec number : $text")
var pad = ch == '0'
var len = text.length
if (ch == '-' && text[1] == '0') {
pad = true
len--
}
var num = BigInteger(text)
num = num.add(BigInteger.valueOf(count.toLong()))
number = num.toString()
if (!octal && pad) {
var neg = false
if (number[0] == '-') {
neg = true
number = number.substring(1)
}
number = number.padStart(len, '0')
if (neg) {
number = "-$number"
}
}
}
return number
}
public fun addInsertListener(listener: VimInsertListener) {
insertListeners.add(listener)
}
public fun removeInsertListener(listener: VimInsertListener) {
insertListeners.remove(listener)
}
override fun notifyListeners(editor: VimEditor) {
insertListeners.forEach(Consumer { listener: VimInsertListener -> listener.insertModeStarted((editor as IjVimEditor).editor) })
}
@TestOnly
override fun resetRepeat() {
setInsertRepeat(0, 0, false)
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.remove", imports = ["import com.maddyhome.idea.vim.api.injector"]))
fun removeInsertListener(listener: VimInsertListener) {
injector.listenersNotifier.modeChangeListeners.remove(listener)
}
private companion object {

View File

@@ -11,11 +11,14 @@ package com.maddyhome.idea.vim.group;
import com.intellij.openapi.diagnostic.Logger;
import com.maddyhome.idea.vim.api.VimDigraphGroupBase;
import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.api.VimOutputPanel;
import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.helper.EditorHelper;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import org.jetbrains.annotations.NotNull;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
public class DigraphGroup extends VimDigraphGroupBase {
public void showDigraphs(@NotNull VimEditor editor) {
@@ -71,7 +74,9 @@ public class DigraphGroup extends VimDigraphGroupBase {
}
}
ExOutputModel.getInstance(((IjVimEditor) editor).getEditor()).output(res.toString());
VimOutputPanel output = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
output.addText(res.toString(), true );
output.show();
}
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());

View File

@@ -8,9 +8,7 @@
package com.maddyhome.idea.vim.group;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.find.EditorSearchSession;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.client.ClientAppSession;
import com.intellij.openapi.client.ClientKind;
import com.intellij.openapi.client.ClientSessionsManager;
@@ -20,13 +18,13 @@ import com.intellij.openapi.components.Storage;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.CaretEvent;
import com.intellij.openapi.editor.event.CaretListener;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
import com.intellij.openapi.project.Project;
import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
import com.maddyhome.idea.vim.helper.CommandStateHelper;
import com.maddyhome.idea.vim.helper.EditorHelper;
import com.maddyhome.idea.vim.helper.UserDataManager;
import com.maddyhome.idea.vim.newapi.IjVimDocument;
@@ -38,6 +36,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@@ -45,6 +45,7 @@ import java.util.stream.Stream;
import static com.intellij.openapi.editor.EditorSettings.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
/**
@@ -208,44 +209,14 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
initLineNumbers(editor);
// We add Vim bindings to all opened editors, even read-only editors. We also add bindings to editors that are used
// elsewhere in the IDE, rather than just for editing project files. This includes editors used as part of the UI,
// such as the VCS commit message, or used as read-only viewers for text output, such as log files in run
// configurations or the Git Console tab. And editors are used for interactive stdin/stdout for console-based run
// configurations.
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
// to INSERT mode for interactive editors. Recognising these can be a bit tricky.
// These additional interactive editors are not file-based, but must have a writable document. However, log output
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
// read-only and also hides the caret).
// Furthermore, the interactive stdin/stdout console output is hosted in a read-only editor, but it can still be
// edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's `isViewer` property and
// allows typing if the associated process (if any) is still running. We can get the editor's console view and check
// this ourselves, but we have to wait until the editor has finished initialising before it's available in user
// data.
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
Runnable switchToInsertMode = () -> {
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
KeyHandler.getInstance().reset(new IjVimEditor(editor));
};
if (!editor.isViewer() &&
!EditorHelper.isFileEditor(editor) &&
editor.getDocument().isWritable() &&
!CommandStateHelper.inInsertMode(editor)) {
switchToInsertMode.run();
// Listen for changes to the font size, so we can hide the ex text field/output panel
if (editor instanceof EditorEx editorEx) {
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
}
if (injector.getApplication().isUnitTest()) {
updateCaretsVisualAttributes(new IjVimEditor(editor));
}
ApplicationManager.getApplication().invokeLater(
() -> {
if (editor.isDisposed()) return;
ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
switchToInsertMode.run();
}
});
updateCaretsVisualAttributes(new IjVimEditor(editor));
}
public void editorDeinit(@NotNull Editor editor) {
@@ -253,6 +224,9 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
UserDataManager.unInitializeEditor(editor);
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
if (editor instanceof EditorEx editorEx) {
editorEx.removePropertyChangeListener(FontSizeChangeListener.INSTANCE);
}
}
public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
@@ -264,9 +238,8 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
}
@Nullable
@Override
public Element getState() {
public @Nullable Element getState() {
Element element = new Element("editor");
saveData(element);
return element;
@@ -315,7 +288,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
@Override
public Integer convert(@NotNull Editor editor, int lineNumber) {
final IjVimEditor ijVimEditor = new IjVimEditor(editor);
final boolean number = ijOptions(injector, ijVimEditor).getNumber();
final boolean number = options(injector, ijVimEditor).getNumber();
final int caretLine = editor.getCaretModel().getLogicalPosition().line;
// lineNumber is 1 based
@@ -342,18 +315,16 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
.collect(Collectors.toList());
}
@NotNull
@Override
public Collection<VimEditor> getEditors() {
public @NotNull Collection<VimEditor> getEditors() {
return getLocalEditors()
.filter(UserDataManager::getVimInitialised)
.map(IjVimEditor::new)
.collect(Collectors.toList());
}
@NotNull
@Override
public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
final Document document = ((IjVimDocument)buffer).getDocument();
return getLocalEditors()
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
@@ -373,7 +344,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
// events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
// the remote editors.
// This invocation will always get local editors, regardless of the current context.
List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.ALL);
List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.LOCAL);
if (!appSessions.isEmpty()) {
ClientAppSession localSession = appSessions.get(0);
return localSession.getService(ClientEditorManager.class).editors();
@@ -382,4 +353,37 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
return Stream.empty();
}
}
/**
* Listens to property changes from the editor to hide ex text field/output panel when the editor's font is zoomed
*/
private static class FontSizeChangeListener implements PropertyChangeListener {
public static FontSizeChangeListener INSTANCE = new FontSizeChangeListener();
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (VimPlugin.isNotEnabled()) return;
if (evt.getPropertyName().equals(EditorEx.PROP_FONT_SIZE)) {
Object source = evt.getSource();
if (source instanceof Editor editor) {
// The editor is being zoomed, so hide the command line or output panel, if they're being shown. On the one
// hand, it's a little rude to cancel a command line for the user, but on the other, the panels obscure the
// zoom indicator, so it looks nicer if we hide them.
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
if (activeCommandLine != null) {
activeCommandLine.close(true, false);
}
VimOutputPanel outputPanel = injector.getOutputPanel().getCurrentOutputPanel();
if (outputPanel != null) {
outputPanel.close();
}
VimModalInput modalInput = injector.getModalInput().getCurrentModalInput();
if (modalInput != null) {
modalInput.deactivate(true, 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.group
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
@Service
internal class EditorHolderService {
var editor: Editor? = null
companion object {
@JvmStatic
fun getInstance(): EditorHolderService = service()
}
}

View File

@@ -1,476 +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.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.helper.EditorHelper;
import com.maddyhome.idea.vim.helper.EditorHelperRt;
import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.SearchHelper;
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.state.mode.Mode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
public class FileGroup extends VimFileBase {
public boolean openFile(@NotNull String filename, @NotNull ExecutionContext context) {
if (logger.isDebugEnabled()) {
logger.debug("openFile(" + filename + ")");
}
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext()); // API change - don't merge
if (project == null) return false;
VirtualFile found = findFile(filename, project);
if (found != null) {
if (logger.isDebugEnabled()) {
logger.debug("found file: " + found);
}
// Can't open a file unless it has a known file type. The next call will return the known type.
// If unknown, IDEA will prompt the user to pick a type.
FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project);
//noinspection IfStatementWithIdenticalBranches
if (type != null) {
FileEditorManager fem = FileEditorManager.getInstance(project);
fem.openFile(found, true);
return true;
}
else {
// There was no type and user didn't pick one. Don't open the file
// Return true here because we found the file but the user canceled by not picking a type.
return true;
}
}
else {
VimPlugin.showMessage(MessageHelper.message("unable.to.find.0", filename));
return false;
}
}
@Nullable VirtualFile findFile(@NotNull String filename, @NotNull Project project) {
VirtualFile found;
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
// it only supports forward slash on Unix (tested on Mac)
// VFS works with both directory separators (tested on Mac and Windows)
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
String relativePath = filename.substring(2);
String dir = System.getProperty("user.home");
if (logger.isDebugEnabled()) {
logger.debug("home dir file");
logger.debug("looking for " + relativePath + " in " + dir);
}
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, relativePath));
}
else {
found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
if (found == null) {
found = findByNameInContentRoots(filename, project);
if (found == null) {
found = findByNameInProject(filename, project);
}
}
}
return found;
}
@Nullable
private VirtualFile findByNameInContentRoots(@NotNull String filename, @NotNull Project project) {
VirtualFile found = null;
ProjectRootManager prm = ProjectRootManager.getInstance(project);
VirtualFile[] roots = prm.getContentRoots();
for (int i = 0; i < roots.length; i++) {
if (logger.isDebugEnabled()) {
logger.debug("root[" + i + "] = " + roots[i].getPath());
}
found = roots[i].findFileByRelativePath(filename);
if (found != null) {
break;
}
}
return found;
}
@Nullable
private static VirtualFile findByNameInProject(@NotNull String filename, @NotNull Project project) {
GlobalSearchScope projectScope = ProjectScope.getProjectScope(project);
Collection<VirtualFile> names = FilenameIndex.getVirtualFilesByName(filename, projectScope);
if (!names.isEmpty()) {
return names.stream().findFirst().get();
}
return null;
}
/**
* Closes the current editor.
*/
@Override
public void closeFile(@NotNull VimEditor editor, @NotNull ExecutionContext context) {
final Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
if (project != null) {
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
final EditorWindow window = fileEditorManager.getCurrentWindow();
final VirtualFile virtualFile = fileEditorManager.getCurrentFile();
if (virtualFile != null && window != null) {
// During the work on VIM-2912 I've changed the close function to this one.
// However, the function with manager seems to work weirdly and it causes VIM-2953
//window.getManager().closeFile(virtualFile, true, false);
window.closeFile(virtualFile);
// Get focus after closing tab
window.requestFocus(true);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
// This thing doesn't have an implementation in test mode
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
}
}
}
}
/**
* Closes editor.
*/
@Override
public void closeFile(int number, @NotNull ExecutionContext context) {
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
if (project == null) return;
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
final EditorWindow window = fileEditorManager.getCurrentWindow();
VirtualFile[] editors = fileEditorManager.getOpenFiles();
if (window != null) {
if (number >= 0 && number < editors.length) {
fileEditorManager.closeFile(editors[number], window);
}
} if (!ApplicationManager.getApplication().isUnitTestMode()) {
// This thing doesn't have an implementation in test mode
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
}
}
/**
* Saves specific file in the project.
*/
@Override
public void saveFile(@NotNull ExecutionContext context) {
NativeAction action;
if (globalIjOptions(injector).getIdeawrite().contains(IjOptionConstants.ideawrite_all)) {
action = injector.getNativeActionManager().getSaveAll();
}
else {
action = injector.getNativeActionManager().getSaveCurrent();
}
ExecuteExtensionKt.execute(action, context);
}
/**
* Saves all files in the project.
*/
public void saveFiles(@NotNull ExecutionContext context) {
ExecuteExtensionKt.execute(VimInjectorKt.getInjector().getNativeActionManager().getSaveAll(), context);
}
/**
* Selects then next or previous editor.
*/
@Override
public boolean selectFile(int count, @NotNull ExecutionContext context) {
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
if (project == null) return false;
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
VirtualFile[] editors = fem.getOpenFiles();
if (count == 99) {
count = editors.length - 1;
}
if (count < 0 || count >= editors.length) {
return false;
}
fem.openFile(editors[count], true);
return true;
}
/**
* Selects then next or previous editor.
*/
public void selectNextFile(int count, @NotNull ExecutionContext context) {
Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
if (project == null) return;
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
VirtualFile[] editors = fem.getOpenFiles();
VirtualFile current = fem.getSelectedFiles()[0];
for (int i = 0; i < editors.length; i++) {
if (editors[i].equals(current)) {
int pos = (i + (count % editors.length) + editors.length) % editors.length;
fem.openFile(editors[pos], true);
}
}
}
/**
* Selects previous editor tab.
*/
@Override
public void selectPreviousTab(@NotNull ExecutionContext context) {
Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
if (project == null) return;
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
if (vf != null && vf.isValid()) {
FileEditorManager.getInstance(project).openFile(vf, true);
}
else {
VimPlugin.indicateError();
}
}
/**
* Returns the previous tab.
*/
public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
Project project = PlatformDataKeys.PROJECT.getData(context);
if (project == null) return null;
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
if (vf != null && vf.isValid()) {
return vf;
}
return null;
}
@Nullable Editor selectEditor(Project project, @NotNull VirtualFile file) {
FileEditorManager fMgr = FileEditorManager.getInstance(project);
FileEditor[] feditors = fMgr.openFile(file, true);
if (feditors.length > 0) {
if (feditors[0] instanceof TextEditor) {
Editor editor = ((TextEditor)feditors[0]).getEditor();
if (!editor.isDisposed()) {
return editor;
}
}
}
return null;
}
@Override
public void displayLocationInfo(@NotNull VimEditor vimEditor) {
Editor editor = ((IjVimEditor)vimEditor).getEditor();
StringBuilder msg = new StringBuilder();
Document doc = editor.getDocument();
if (!(VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL)) {
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line);
int endoff = doc.getLineEndOffset(lp.line);
if (endoff < EditorHelperRt.getFileSize(editor) && doc.getCharsSequence().charAt(endoff) == '\n') {
endoff--;
}
int ecol = endoff - doc.getLineStartOffset(lp.line);
LogicalPosition elp = editor.offsetToLogicalPosition(endoff);
msg.append("Col ").append(col + 1);
if (col != lp.column) {
msg.append("-").append(lp.column + 1);
}
msg.append(" of ").append(ecol + 1);
if (ecol != elp.column) {
msg.append("-").append(elp.column + 1);
}
int lline = editor.getCaretModel().getLogicalPosition().line;
int total = new IjVimEditor(editor).lineCount();
msg.append("; Line ").append(lline + 1).append(" of ").append(total);
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
msg.append("; Word ").append(cp.getPosition()).append(" of ").append(cp.getCount());
int offset = editor.getCaretModel().getOffset();
int size = EditorHelperRt.getFileSize(editor);
msg.append("; Character ").append(offset + 1).append(" of ").append(size);
}
else {
msg.append("Selected ");
TextRange vr = new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
editor.getSelectionModel().getBlockSelectionEnds());
vr.normalize();
int lines;
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
int words = cp.getCount();
int word = 0;
if (vr.isMultiple()) {
lines = vr.size();
int cols = vr.getMaxLength();
msg.append(cols).append(" Cols; ");
for (int i = 0; i < vr.size(); i++) {
cp = SearchHelper.countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
word += cp.getCount();
}
}
else {
LogicalPosition slp = editor.offsetToLogicalPosition(vr.getStartOffset());
LogicalPosition elp = editor.offsetToLogicalPosition(vr.getEndOffset());
lines = elp.line - slp.line + 1;
cp = SearchHelper.countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
word = cp.getCount();
}
int total = new IjVimEditor(editor).lineCount();
msg.append(lines).append(" of ").append(total).append(" Lines");
msg.append("; ").append(word).append(" of ").append(words).append(" Words");
int chars = vr.getSelectionCount();
int size = EditorHelperRt.getFileSize(editor);
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters");
}
VimPlugin.showMessage(msg.toString());
}
@Override
public void displayFileInfo(@NotNull VimEditor vimEditor, boolean fullPath) {
Editor editor = ((IjVimEditor)vimEditor).getEditor();
StringBuilder msg = new StringBuilder();
VirtualFile vf = EditorHelper.getVirtualFile(editor);
if (vf != null) {
msg.append('"');
if (fullPath) {
msg.append(vf.getPath());
}
else {
Project project = editor.getProject();
if (project != null) {
VirtualFile root = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(vf);
if (root != null) {
msg.append(vf.getPath().substring(root.getPath().length() + 1));
}
else {
msg.append(vf.getPath());
}
}
}
msg.append("\" ");
}
else {
msg.append("\"[No File]\" ");
}
Document doc = editor.getDocument();
if (!doc.isWritable()) {
msg.append("[RO] ");
}
else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
msg.append("[+] ");
}
int lline = editor.getCaretModel().getLogicalPosition().line;
int total = new IjVimEditor(editor).lineCount();
int pct = (int)((float)lline / (float)total * 100f + 0.5);
msg.append("line ").append(lline + 1).append(" of ").append(total);
msg.append(" --").append(pct).append("%-- ");
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lline);
msg.append("col ").append(col + 1);
if (col != lp.column) {
msg.append("-").append(lp.column + 1);
}
VimPlugin.showMessage(msg.toString());
}
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
/**
* Respond to editor tab selection and remember the last used tab
*/
public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
if (event.getOldFile() != null) {
LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
}
}
@Nullable
@Override
public VimEditor selectEditor(@NotNull String projectId, @NotNull String documentPath, @Nullable String protocol) {
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
if (fileSystem == null) return null;
VirtualFile virtualFile = fileSystem.findFileByPath(documentPath);
if (virtualFile == null) return null;
Project project = Arrays.stream(ProjectManager.getInstance().getOpenProjects())
.filter(p -> injector.getFile().getProjectId(p).equals(projectId))
.findFirst().orElseThrow();
Editor editor = selectEditor(project, virtualFile);
if (editor == null) return null;
return new IjVimEditor(editor);
}
@NotNull
@Override
public String getProjectId(@NotNull Object project) {
if (!(project instanceof Project)) throw new IllegalArgumentException();
return ((Project) project).getName();
}
}

View File

@@ -0,0 +1,444 @@
/*
* 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.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.fileEditor.impl.EditorsSplitters
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.search.FilenameIndex
import com.intellij.psi.search.ProjectScope
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimFileBase
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.MessageHelper.message
import com.maddyhome.idea.vim.helper.countWords
import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.execute
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
import java.io.File
import java.util.*
class FileGroup : VimFileBase() {
override fun openFile(filename: String, context: ExecutionContext): Boolean {
if (logger.isDebugEnabled) {
logger.debug("openFile($filename)")
}
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context)
?: return false // API change - don't merge
val found = findFile(filename, project)
if (found != null) {
if (logger.isDebugEnabled) {
logger.debug("found file: $found")
}
// Can't open a file unless it has a known file type. The next call will return the known type.
// If unknown, IDEA will prompt the user to pick a type.
val type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project)
if (type != null) {
val fem = FileEditorManager.getInstance(project)
fem.openFile(found, true)
return true
} else {
// There was no type and user didn't pick one. Don't open the file
// Return true here because we found the file but the user canceled by not picking a type.
return true
}
} else {
VimPlugin.showMessage(message("unable.to.find.0", filename))
return false
}
}
fun findFile(filename: String, project: Project): VirtualFile? {
var found: VirtualFile?
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
// it only supports forward slash on Unix (tested on Mac)
// VFS works with both directory separators (tested on Mac and Windows)
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
val relativePath = filename.substring(2)
val dir = System.getProperty("user.home")
if (logger.isDebugEnabled) {
logger.debug("home dir file")
logger.debug("looking for $relativePath in $dir")
}
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(dir, relativePath))
} else {
found = LocalFileSystem.getInstance().findFileByIoFile(File(filename))
if (found == null) {
found = findByNameInContentRoots(filename, project)
if (found == null) {
found = findByNameInProject(filename, project)
}
}
}
return found
}
private fun findByNameInContentRoots(filename: String, project: Project): VirtualFile? {
var found: VirtualFile? = null
val prm = ProjectRootManager.getInstance(project)
val roots = prm.contentRoots
for (i in roots.indices) {
if (logger.isDebugEnabled) {
logger.debug("root[" + i + "] = " + roots[i].path)
}
found = roots[i].findFileByRelativePath(filename)
if (found != null) {
break
}
}
return found
}
/**
* Closes the current editor.
*/
override fun closeFile(editor: VimEditor, context: ExecutionContext) {
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext))
if (project != null) {
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
val window = fileEditorManager.currentWindow
val virtualFile = fileEditorManager.currentFile
if (virtualFile != null && window != null) {
// During the work on VIM-2912 I've changed the close function to this one.
// However, the function with manager seems to work weirdly and it causes VIM-2953
//window.getManager().closeFile(virtualFile, true, false);
window.closeFile(virtualFile)
// Get focus after closing tab
window.requestFocus(true)
if (!ApplicationManager.getApplication().isUnitTestMode) {
// This thing doesn't have an implementation in test mode
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
}
}
}
}
/**
* Closes editor.
*/
override fun closeFile(number: Int, context: ExecutionContext) {
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
val window = fileEditorManager.currentWindow
val editors = fileEditorManager.openFiles
if (window != null) {
if (number >= 0 && number < editors.size) {
fileEditorManager.closeFile(editors[number], window)
}
}
if (!ApplicationManager.getApplication().isUnitTestMode) {
// This thing doesn't have an implementation in test mode
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
}
}
/**
* Saves specific file in the project.
*/
override fun saveFile(editor: VimEditor, context: ExecutionContext) {
val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
injector.nativeActionManager.saveAll
} else {
injector.nativeActionManager.saveCurrent
}
action.execute(editor, context)
}
/**
* Saves all files in the project.
*/
override fun saveFiles(editor: VimEditor, context: ExecutionContext) {
injector.nativeActionManager.saveAll.execute(editor, context)
}
/**
* Selects then next or previous editor.
*/
override fun selectFile(count: Int, context: ExecutionContext): Boolean {
var count = count
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return false
val fem = FileEditorManager.getInstance(project) // API change - don't merge
val editors = fem.openFiles
if (count == 99) {
count = editors.size - 1
}
if (count < 0 || count >= editors.size) {
return false
}
fem.openFile(editors[count], true)
return true
}
/**
* Selects then next or previous editor.
*/
override fun selectNextFile(count: Int, context: ExecutionContext) {
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
val fem = FileEditorManager.getInstance(project) // API change - don't merge
val editors = fem.openFiles
val current = fem.selectedFiles[0]
for (i in editors.indices) {
if (editors[i] == current) {
val pos = (i + (count % editors.size) + editors.size) % editors.size
fem.openFile(editors[pos], true)
}
}
}
/**
* Selects previous editor tab.
*/
override fun selectPreviousTab(context: ExecutionContext) {
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext)) ?: return
val vf = getInstance(project).lastTab
if (vf != null && vf.isValid) {
FileEditorManager.getInstance(project).openFile(vf, true)
} else {
VimPlugin.indicateError()
}
}
/**
* Returns the previous tab.
*/
fun getPreviousTab(context: DataContext): VirtualFile? {
val project = PlatformDataKeys.PROJECT.getData(context) ?: return null
val vf = getInstance(project).lastTab
if (vf != null && vf.isValid) {
return vf
}
return null
}
fun selectEditor(project: Project, file: VirtualFile): Editor? {
val fMgr = FileEditorManager.getInstance(project)
val feditors = fMgr.openFile(file, true)
if (feditors.size > 0) {
if (feditors[0] is TextEditor) {
val editor = (feditors[0] as TextEditor).editor
if (!editor.isDisposed) {
return editor
}
}
}
return null
}
override fun displayLocationInfo(vimEditor: VimEditor) {
val editor = (vimEditor as IjVimEditor).editor
val msg = StringBuilder()
val doc = editor.document
if (injector.vimState.mode !is VISUAL) {
val lp = editor.caretModel.logicalPosition
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
var endoff = doc.getLineEndOffset(lp.line)
if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
endoff--
}
val ecol = endoff - doc.getLineStartOffset(lp.line)
val elp = editor.offsetToLogicalPosition(endoff)
msg.append("Col ").append(col + 1)
if (col != lp.column) {
msg.append("-").append(lp.column + 1)
}
msg.append(" of ").append(ecol + 1)
if (ecol != elp.column) {
msg.append("-").append(elp.column + 1)
}
val lline = editor.caretModel.logicalPosition.line
val total = IjVimEditor(editor).lineCount()
msg.append("; Line ").append(lline + 1).append(" of ").append(total)
val cp = countWords(vimEditor)
msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
val offset = editor.caretModel.offset
val size = editor.fileSize
msg.append("; Character ").append(offset + 1).append(" of ").append(size)
} else {
msg.append("Selected ")
val vr = TextRange(
editor.selectionModel.blockSelectionStarts,
editor.selectionModel.blockSelectionEnds
)
vr.normalize()
val lines: Int
var cp = countWords(vimEditor)
val words = cp.count
var word = 0
if (vr.isMultiple) {
lines = vr.size()
val cols = vr.maxLength
msg.append(cols).append(" Cols; ")
for (i in 0 until vr.size()) {
cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
word += cp.count
}
} else {
val slp = editor.offsetToLogicalPosition(vr.startOffset)
val elp = editor.offsetToLogicalPosition(vr.endOffset)
lines = elp.line - slp.line + 1
cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
word = cp.count
}
val total = IjVimEditor(editor).lineCount()
msg.append(lines).append(" of ").append(total).append(" Lines")
msg.append("; ").append(word).append(" of ").append(words).append(" Words")
val chars = vr.selectionCount
val size = editor.fileSize
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
}
VimPlugin.showMessage(msg.toString())
}
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
val editor = (vimEditor as IjVimEditor).editor
val msg = StringBuilder()
val vf = EditorHelper.getVirtualFile(editor)
if (vf != null) {
msg.append('"')
if (fullPath) {
msg.append(vf.path)
} else {
val project = editor.project
if (project != null) {
val root = ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(vf)
if (root != null) {
msg.append(vf.path.substring(root.path.length + 1))
} else {
msg.append(vf.path)
}
}
}
msg.append("\" ")
} else {
msg.append("\"[No File]\" ")
}
val doc = editor.document
if (!doc.isWritable) {
msg.append("[RO] ")
} else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
msg.append("[+] ")
}
val lline = editor.caretModel.logicalPosition.line
val total = IjVimEditor(editor).lineCount()
val pct = (lline.toFloat() / total.toFloat() * 100f + 0.5).toInt()
msg.append("line ").append(lline + 1).append(" of ").append(total)
msg.append(" --").append(pct).append("%-- ")
val lp = editor.caretModel.logicalPosition
val col = editor.caretModel.offset - doc.getLineStartOffset(lline)
msg.append("col ").append(col + 1)
if (col != lp.column) {
msg.append("-").append(lp.column + 1)
}
VimPlugin.showMessage(msg.toString())
}
override fun selectEditor(projectId: String, documentPath: String, protocol: String?): VimEditor? {
val fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol) ?: return null
val virtualFile = fileSystem.findFileByPath(documentPath) ?: return null
val project = Arrays.stream(ProjectManager.getInstance().openProjects)
.filter { p: Project? -> injector.file.getProjectId(p!!) == projectId }
.findFirst().orElseThrow()
val editor = selectEditor(project, virtualFile) ?: return null
return IjVimEditor(editor)
}
override fun getProjectId(project: Any): String {
require(project is Project)
return project.name + "-" + project.locationHash
}
companion object {
private fun findByNameInProject(filename: String, project: Project): VirtualFile? {
val projectScope = ProjectScope.getProjectScope(project)
val names = FilenameIndex.getVirtualFilesByName(filename, projectScope)
if (!names.isEmpty()) {
return names.stream().findFirst().get()
}
return null
}
private val logger = Logger.getInstance(
FileGroup::class.java.name
)
/**
* Respond to editor tab selection and remember the last used tab
*/
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
if (event.oldFile != null) {
getInstance(event.manager.project).lastTab = event.oldFile
}
}
}
}

View File

@@ -14,9 +14,7 @@ import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.history.HistoryBlock;
import com.maddyhome.idea.vim.history.HistoryEntry;
import com.maddyhome.idea.vim.history.VimHistoryBase;
import com.maddyhome.idea.vim.history.*;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -35,21 +33,20 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
logger.debug("saveData");
Element hist = new Element("history");
saveData(hist, SEARCH);
saveData(hist, COMMAND);
saveData(hist, EXPRESSION);
saveData(hist, INPUT);
for (Type type : getHistories().keySet()) {
saveData(hist, type);
}
element.addContent(hist);
}
private void saveData(@NotNull Element element, String key) {
final HistoryBlock block = getHistories().get(key);
private void saveData(@NotNull Element element, VimHistory.Type type) {
final HistoryBlock block = getHistories().get(type);
if (block == null) {
return;
}
final Element root = new Element("history-" + key);
final Element root = new Element("history-" + typeToKey(type));
for (HistoryEntry entry : block.getEntries()) {
final Element entryElement = new Element("entry");
@@ -67,10 +64,10 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
return;
}
readData(hist, SEARCH);
readData(hist, COMMAND);
readData(hist, EXPRESSION);
readData(hist, INPUT);
for (Element child : hist.getChildren()) {
String key = child.getName().replace("history-", "");
readData(hist, key);
}
}
private void readData(@NotNull Element element, String key) {
@@ -80,7 +77,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
}
block = new HistoryBlock();
getHistories().put(key, block);
getHistories().put(getTypeForString(key), block);
final Element root = element.getChild("history-" + key);
if (root != null) {
@@ -94,6 +91,25 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
}
}
private String typeToKey(VimHistory.Type type) {
if (type instanceof VimHistory.Type.Search) {
return SEARCH;
}
if (type instanceof VimHistory.Type.Command) {
return COMMAND;
}
if (type instanceof VimHistory.Type.Expression) {
return EXPRESSION;
}
if (type instanceof VimHistory.Type.Input) {
return INPUT;
}
if (type instanceof VimHistory.Type.Custom) {
return ((Type.Custom) type).getId();
}
return "unreachable";
}
@Nullable
@Override
public Element getState() {

View File

@@ -19,24 +19,22 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
* options
*/
@Suppress("SpellCheckingInspection")
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
public var ide: String by optionProperty(IjOptions.ide)
public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
public val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
public var ideawrite: String by optionProperty(IjOptions.ideawrite)
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
var ide: String by optionProperty(IjOptions.ide)
var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
var ideawrite: String by optionProperty(IjOptions.ideawrite)
val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
var visualdelay: Int by optionProperty(IjOptions.visualdelay)
// Temporary options to control work-in-progress behaviour
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
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 unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
var oldundo: Boolean by optionProperty(IjOptions.oldundo)
var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
}
/**
@@ -44,20 +42,19 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
*
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
*/
public class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
public var breakindent: Boolean by optionProperty(IjOptions.breakindent)
public val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
public var cursorline: Boolean by optionProperty(IjOptions.cursorline)
public var fileformat: String by optionProperty(IjOptions.fileformat)
public var list: Boolean by optionProperty(IjOptions.list)
public var number: Boolean by optionProperty(IjOptions.number)
public var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
public var textwidth: Int by optionProperty(IjOptions.textwidth)
public var wrap: Boolean by optionProperty(IjOptions.wrap)
var breakindent: Boolean by optionProperty(IjOptions.breakindent)
val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
var cursorline: Boolean by optionProperty(IjOptions.cursorline)
var fileformat: String by optionProperty(IjOptions.fileformat)
var list: Boolean by optionProperty(IjOptions.list)
var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
var textwidth: Int by optionProperty(IjOptions.textwidth)
var wrap: Boolean by optionProperty(IjOptions.wrap)
// IntelliJ specific options
public var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
public var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
public var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
}

View File

@@ -26,9 +26,9 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
@Suppress("SpellCheckingInspection")
public object IjOptions {
object IjOptions {
public fun initialise() {
fun initialise() {
// Calling this method allows for deterministic initialisation of IjOptions, specifically initialising the
// properties and registering the IJ specific options. Once added, they can be safely accessed by name, e.g. by the
// implementation of `:set` while executing ~/.ideavimrc
@@ -39,8 +39,8 @@ public object IjOptions {
}
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
public val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
public val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
override fun checkIfValueValid(value: VimDataType, token: String) {
super.checkIfValueValid(value, token)
if (value != VimString.EMPTY) {
@@ -55,19 +55,18 @@ public object IjOptions {
}
}
})
public val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
public val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
public val number: ToggleOption = addOption(ToggleOption("number", LOCAL_TO_WINDOW, "nu", false))
public val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
public val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
public val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))
val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))
// These options are not explicitly listed as local-noglobal in Vim's help, but are set when a new buffer is edited,
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file cnversion, we treat them as
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file conversion, we treat them as
// local-noglobal. See `:help local-noglobal`, `:help 'fileformats'` and `:help 'fileencodings'`
public val bomb: ToggleOption =
val bomb: ToggleOption =
addOption(ToggleOption("bomb", LOCAL_TO_BUFFER, "bomb", false, isLocalNoGlobal = true))
public val fileencoding: StringOption = addOption(
val fileencoding: StringOption = addOption(
StringOption(
"fileencoding",
LOCAL_TO_BUFFER,
@@ -76,7 +75,7 @@ public object IjOptions {
isLocalNoGlobal = true
)
)
public val fileformat: StringOption = addOption(
val fileformat: StringOption = addOption(
StringOption(
"fileformat",
LOCAL_TO_BUFFER,
@@ -88,15 +87,15 @@ public object IjOptions {
)
// IntelliJ specific functionality - custom options
public val ide: StringOption = addOption(
val ide: StringOption = addOption(
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
)
public val ideacopypreprocess: ToggleOption = addOption(
val ideacopypreprocess: ToggleOption = addOption(
ToggleOption("ideacopypreprocess", GLOBAL_OR_LOCAL_TO_BUFFER, "ideacopypreprocess", false)
)
public val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
public val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
public val idearefactormode: StringOption = addOption(
val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
val idearefactormode: StringOption = addOption(
StringOption(
"idearefactormode",
GLOBAL_OR_LOCAL_TO_BUFFER,
@@ -105,7 +104,7 @@ public object IjOptions {
IjOptionConstants.ideaRefactorModeValues
)
)
public val ideastatusicon: StringOption = addOption(
val ideastatusicon: StringOption = addOption(
StringOption(
"ideastatusicon",
GLOBAL,
@@ -114,7 +113,7 @@ public object IjOptions {
IjOptionConstants.ideaStatusIconValues
)
)
public val ideavimsupport: StringListOption = addOption(
val ideavimsupport: StringListOption = addOption(
StringListOption(
"ideavimsupport",
GLOBAL,
@@ -123,27 +122,26 @@ public object IjOptions {
IjOptionConstants.ideavimsupportValues
)
)
@JvmField public val ideawrite: StringOption = addOption(
@JvmField
val ideawrite: StringOption = addOption(
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
)
public val lookupkeys: StringListOption = addOption(
val lookupkeys: StringListOption = addOption(
StringListOption(
"lookupkeys",
GLOBAL,
"lookupkeys",
"<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 visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
// Temporary feature flags during development, not really intended for external use
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, 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))
val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
// derives from Option<VimInt>

View File

@@ -21,12 +21,12 @@ import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
@Service
public class IjVimPsiService: VimPsiService {
class IjVimPsiService: VimPsiService {
override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
val psiElement = psiFile.findElementAt(pos) ?: return null
val language = psiElement.language
val commenter = LanguageCommenters.INSTANCE.forLanguage(language)
val commenter = LanguageCommenters.INSTANCE.forLanguage(language) ?: return null
val psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
val commentText = psiComment.text

View File

@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimRedrawService
import com.maddyhome.idea.vim.api.injector
public class IjVimRedrawService : VimRedrawService {
class IjVimRedrawService : VimRedrawService {
override fun redraw() {
// The only thing IntelliJ needs to redraw is the status line. Everything else is handled automatically.
redrawStatusLine()
@@ -25,11 +25,11 @@ public class IjVimRedrawService : VimRedrawService {
injector.messages.clearStatusBarMessage()
}
public companion object {
companion object {
/**
* Simulate Vim's redraw when the current editor changes
*/
public fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
injector.redrawService.redraw()
}
}
@@ -39,7 +39,7 @@ public class IjVimRedrawService : VimRedrawService {
*
* Only redraw if lines are added/removed.
*/
public object RedrawListener : DocumentListener {
object RedrawListener : DocumentListener {
override fun documentChanged(event: DocumentEvent) {
if (VimPlugin.isNotEnabled()) return
if (event.newFragment.contains("\n") || event.oldFragment.contains("\n")) {

View File

@@ -26,10 +26,7 @@ import com.maddyhome.idea.vim.EventFacade;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
import com.maddyhome.idea.vim.api.NativeAction;
import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.api.VimInjectorKt;
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.key.*;
@@ -80,25 +77,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
((IjVimEditor)editor).getEditor().getComponent());
}
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull Editor editor) {
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
final StringBuilder builder = new StringBuilder();
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
MappingInfo mappingInfo = row.getSecond();
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
builder.append(" ");
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
builder.append(" ");
builder.append(mappingInfo.isRecursive() ? " " : "*");
builder.append(" ");
builder.append(mappingInfo.getPresentableString());
builder.append("\n");
}
ExOutputModel.getInstance(editor).output(builder.toString());
return true;
}
@Override
public void updateShortcutKeysRegistration() {
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
@@ -358,6 +336,22 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
@Override
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull VimEditor editor) {
return showKeyMappings(modes, ((IjVimEditor) editor).getEditor());
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
final StringBuilder builder = new StringBuilder();
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
MappingInfo mappingInfo = row.getSecond();
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
builder.append(" ");
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
builder.append(" ");
builder.append(mappingInfo.isRecursive() ? " " : "*");
builder.append(" ");
builder.append(mappingInfo.getPresentableString());
builder.append("\n");
}
VimOutputPanel outputPanel = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
outputPanel.addText(builder.toString(), true);
outputPanel.show();
return true;
}
}

View File

@@ -47,12 +47,14 @@ import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
import com.maddyhome.idea.vim.helper.isEndAllowed
import com.maddyhome.idea.vim.helper.vimLastColumn
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.listener.AppCodeTemplates
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnTo
import com.maddyhome.idea.vim.state.mode.returnTo
import org.jetbrains.annotations.Range
import kotlin.math.max
import kotlin.math.min
@@ -307,19 +309,32 @@ internal class MotionGroup : VimMotionGroupBase() {
val editor = fileEditor.editor
if (!editor.isDisposed) {
editor.vim.let { vimEditor ->
when (vimEditor.vimStateMachine.mode) {
when (vimEditor.mode) {
is Mode.VISUAL -> {
vimEditor.exitVisualMode()
KeyHandler.getInstance().reset(vimEditor)
}
is Mode.CMD_LINE -> {
injector.processGroup.cancelExEntry(vimEditor, false)
ExOutputModel.getInstance(editor).clear()
val commandLine = injector.commandLine.getActiveCommandLine() ?: return
commandLine.close(refocusOwningEditor = false, resetCaret = false)
injector.outputPanel.getCurrentOutputPanel()?.close()
}
else -> {}
}
}
}
} else {
val state = injector.vimState as VimStateMachineImpl
if (state.mode is Mode.VISUAL) {
val returnTo = state.mode.returnTo
when (returnTo) {
ReturnTo.INSERT -> state.mode = Mode.INSERT
ReturnTo.REPLACE -> state.mode = Mode.REPLACE
null -> state.mode = Mode.NORMAL()
}
}
val keyHandler = KeyHandler.getInstance()
KeyHandler.getInstance().reset(keyHandler.keyHandlerState, state.mode)
}
}
}

View File

@@ -62,6 +62,25 @@ internal class NotificationService(private val project: Project?) {
@Suppress("unused")
constructor() : this(null)
fun notifyAboutNewUndo() {
val notification = Notification(
IDEAVIM_NOTIFICATION_ID,
"Undo in IdeaVim now works like in Vim",
"""
Caret movement is no longer a separate undo step, and full insert is undoable in one step.
""".trimIndent(),
NotificationType.INFORMATION,
)
notification.addAction(object : DumbAwareAction("Share Feedback") {
override fun actionPerformed(p0: AnActionEvent) {
BrowserUtil.browse("https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks")
}
})
notification.notify(project)
}
fun notifyAboutIdeaPut() {
val notification = Notification(
IDEAVIM_NOTIFICATION_ID,
@@ -182,8 +201,8 @@ internal class NotificationService(private val project: Project?) {
).notify(project)
}
fun notifyActionId(id: String?) {
ActionIdNotifier.notifyActionId(id, project)
fun notifyActionId(id: String?, candidates: List<String>? = null) {
ActionIdNotifier.notifyActionId(id, project, candidates)
}
fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
@@ -259,20 +278,31 @@ internal class NotificationService(private val project: Project?) {
object ActionIdNotifier {
private var notification: Notification? = null
private const val NO_ID = "<i>Cannot detect action id</i>"
fun notifyActionId(id: String?, project: Project?) {
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null) {
notification?.expire()
val content = if (id != null) "Action id: $id" else NO_ID
Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).let {
notification = it
it.whenExpired { notification = null }
it.setContent(it.content + "<br><br><small>Use ${ActionCenter.getToolwindowName()} to see previous ids</small>")
val possibleIDs = candidates?.distinct()?.sorted()
val content = when {
id != null -> "Action ID: <code>$id</code><br><br>"
possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>"
possibleIDs.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>"
else -> {
buildString {
append("<p>Multiple possible action IDs. Candidates include:<ul>")
possibleIDs.forEach { append("<li><code>$it</code></li>") }
append("</ul></p>")
}
}
} + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also {
it.whenExpired { notification = null }
it.addAction(StopTracking())
if (id != null) it.addAction(CopyActionId(id, project))
if (id != null || possibleIDs?.size == 1) {
it.addAction(CopyActionId(id ?: possibleIDs?.get(0), project))
}
it.notify(project)
}

View File

@@ -12,10 +12,12 @@ import com.intellij.application.options.CodeStyle
import com.intellij.codeStyle.AbstractConvertLineSeparatorsAction
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.EditorKind
import com.intellij.openapi.editor.EditorSettings.LineNumerationType
import com.intellij.openapi.editor.ScrollPositionCalculator
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
import com.intellij.openapi.fileEditor.TextEditor
@@ -94,12 +96,12 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup, InternalOpt
addOptionValueOverride(IjOptions.fileencoding, FileEncodingOptionMapper())
addOptionValueOverride(IjOptions.fileformat, FileFormatOptionMapper())
addOptionValueOverride(IjOptions.list, ListOptionMapper(IjOptions.list, this))
addOptionValueOverride(IjOptions.number, NumberOptionMapper(IjOptions.number, this))
addOptionValueOverride(IjOptions.relativenumber, RelativeNumberOptionMapper(IjOptions.relativenumber, this))
addOptionValueOverride(IjOptions.textwidth, TextWidthOptionMapper(IjOptions.textwidth))
addOptionValueOverride(IjOptions.wrap, WrapOptionMapper(IjOptions.wrap, this))
// These options are defined and implemented in vim-engine, but IntelliJ has similar features with settings we can map
addOptionValueOverride(Options.number, NumberOptionMapper(Options.number, this))
addOptionValueOverride(Options.scrolljump, ScrollJumpOptionMapper(Options.scrolljump, this))
addOptionValueOverride(Options.sidescroll, SideScrollOptionMapper(Options.sidescroll, this))
addOptionValueOverride(Options.scrolloff, ScrollOffOptionMapper(Options.scrolloff, this))
@@ -926,7 +928,9 @@ private class ScrollJumpOptionMapper(option: NumberOption, internalOptionValueAc
override fun getEffectiveExternalValue(editor: VimEditor) = editor.ij.settings.verticalScrollJump.asVimInt()
override fun setLocalExternalValue(editor: VimEditor, value: VimInt) {
editor.ij.settings.verticalScrollJump = value.value
// Note that Vim supports -1 to -100 as a percentage value. IntelliJ does not have any validation, but does not
// handle or expect negative values
editor.ij.settings.verticalScrollJump = value.value.coerceAtLeast(0)
}
override fun resetLocalExternalValue(editor: VimEditor, defaultValue: VimInt) {
@@ -975,26 +979,35 @@ private class SideScrollOptionMapper(option: NumberOption, internalOptionValueAc
* setting value, and there is no UI to modify the local IntelliJ settings. Once the value has been set in IdeaVim, it
* takes precedence over the global, persistent setting until the option is reset with either `:set scrolloff&` or
* `:setlocal scrolloff<`.
*
* Note that when the IdeaVim value is set, we set the IntelliJ local value to 0 rather than sharing the value. This is
* to prevent conflicts between IntelliJ and IdeaVim's separate implementations for scrolling. IntelliJ's scrolling
* includes virtual space at the bottom of the file, while (Idea)Vim doesn't. Combining this with a non-zero
* `'scrolloff'` value can reposition the bottom of the file. E.g., using `G` will position the last line at the bottom
* of the file, but then IntelliJ moves it up `'scrolloff'` when the caret is moved.
*
* With a large value like `999`, IntelliJ will try to move the current line to the centre of the screen, but then
* IdeaVim will try to reposition. Normally, this doesn't cause too much of a problem, because setting the scroll
* position will cancel any outstanding animations. However, using backspace updates the scroll position with animations
* disabled, so the scroll happens immediately, with a visible "twitch" as the editor scrolls for IntelliJ and then back
* for IdeaVim.
*
* We should consider implementing [ScrollPositionCalculator] which would allow IdeaVim to completely take over
* scrolling from IntelliJ. This would be a non-trivial change, and it might be better to move the scrolling to
* vim-engine so it can also work in Fleet.
*/
private class ScrollOffOptionMapper(option: NumberOption, internalOptionValueAccessor: InternalOptionValueAccessor)
: GlobalLocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(option, internalOptionValueAccessor) {
private class ScrollOffOptionMapper(
scrollOffOption: NumberOption,
internalOptionValueAccessor: InternalOptionValueAccessor,
) : OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(scrollOffOption, internalOptionValueAccessor) {
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_VERTICAL_SCROLL_OFFSET
// The IntelliJ setting is in practice global. The base implementation relies on this fact
override val canUserModifyExternalLocalValue: Boolean = false
override fun getExternalGlobalValue() =
EditorSettingsExternalizable.getInstance().verticalScrollOffset.asVimInt()
override fun getGlobalExternalValue() = EditorSettingsExternalizable.getInstance().verticalScrollOffset.asVimInt()
override fun getEffectiveExternalValue(editor: VimEditor) = editor.ij.settings.verticalScrollOffset.asVimInt()
override fun setLocalExternalValue(editor: VimEditor, value: VimInt) {
editor.ij.settings.verticalScrollOffset = value.value
}
override fun removeLocalExternalValue(editor: VimEditor) {
// Unexpectedly, verticalScrollOffset accepts `-1` as a value to clear any local overrides, and this will reset the
// effective value to return the global value
editor.ij.settings.verticalScrollOffset = -1
override fun suppressExternalLocalValue(editor: VimEditor) {
editor.ij.settings.verticalScrollOffset = 0
}
}
@@ -1002,15 +1015,14 @@ private class ScrollOffOptionMapper(option: NumberOption, internalOptionValueAcc
/**
* Map the `'sidescrolloff'` global-local Vim option to the IntelliJ global-local horizontal scroll offset setting
*
* Ideally, we would implement this in a similar manner to [SideScrollOptionMapper], setting the external local
* horizontal scroll offset value when the user explicitly sets the Vim value, so that IntelliJ could also use the
* value. Unfortunately, IntelliJ's scrolling calculation logic is based on integer font width maths, which causes
* problems with fractional font widths (such as on a Mac when running tests).
* IntelliJ supports horizontal scroll offset in a similar manner to Vim. However, the implementation calculates offsets
* using integer font sizes, which can lead to minor inaccuracies when compared to the IdeaVim implementation, such as
* differences running tests on a Mac.
*
* For example, given a `'sidescrolloff'` value of `10`, and a fractional font width of `7.8`, IntelliJ will scroll `80`
* pixels instead of `78`. This is a very minor difference, but because it overshoots, it means that IdeaVim doesn't
* need to scroll, which in turn can cause issues with `'sidescroll'`, because IntelliJ doesn't support `sidescroll=0`,
* which would scroll to position the caret in the middle of the display.
* need to scroll, which in turn can cause issues with `'sidescroll'` (jump), because IntelliJ doesn't support
* `sidescroll=0`, which would scroll to position the caret in the middle of the display.
*
* It also causes precision problems in the tests. The display is scrolled to a couple of pixels _before_ the leftmost
* column, which means the rightmost column ends a couple of pixels _after_ the rightmost edge of the display. The tests
@@ -1028,78 +1040,98 @@ private class ScrollOffOptionMapper(option: NumberOption, internalOptionValueAcc
* vim-engine so it can also work in Fleet.
*/
private class SideScrollOffOptionMapper(
private val sideScrollOffOption: NumberOption,
private val internalOptionValueAccessor: InternalOptionValueAccessor,
) : GlobalOptionValueOverride<VimInt>, LocalOptionValueOverride<VimInt>, IdeaBackedOptionValueOverride {
sideScrollOffOption: NumberOption,
internalOptionValueAccessor: InternalOptionValueAccessor,
) : OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(sideScrollOffOption, internalOptionValueAccessor) {
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_HORIZONTAL_SCROLL_OFFSET
override fun getGlobalValue(storedValue: OptionValue<VimInt>, editor: VimEditor?): OptionValue<VimInt> {
override fun getExternalGlobalValue() =
EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt()
override fun suppressExternalLocalValue(editor: VimEditor) {
editor.ij.settings.horizontalScrollOffset = 0
}
}
/**
* An abstract base class to map a global-local IDEA setting to a global-local Vim option. The IDEA setting is not
* updated to reflect the Vim changes, but is kept at a neutral value.
*
* This class is used for Vim options that have an IDEA equivalent, but the implementation is handled by IdeaVim, e.g.,
* scroll jumps and offsets. The IDEA value is not updated, and kept to a neutral value, so that the IDEA implementation
* does not interfere with the IdeaVim implementation.
*/
private abstract class OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<T : VimDataType>(
private val option: Option<T>,
private val internalOptionValueAccessor: InternalOptionValueAccessor,
) : GlobalOptionValueOverride<T>, LocalOptionValueOverride<T>, IdeaBackedOptionValueOverride {
override fun getGlobalValue(storedValue: OptionValue<T>, editor: VimEditor?): OptionValue<T> {
if (storedValue is OptionValue.Default) {
return OptionValue.Default(EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt())
return OptionValue.Default(getExternalGlobalValue())
}
// If it's not the default value, it's got to be the stored value
return storedValue
}
override fun setGlobalValue(
storedValue: OptionValue<VimInt>,
newValue: OptionValue<VimInt>,
editor: VimEditor?,
): Boolean {
// The user has typed `:setlocal`. Just make sure that the IntelliJ value doesn't interfere with the Vim value
injector.editorGroup.getEditors().forEach { it.ij.settings.horizontalScrollOffset = 0 }
override fun setGlobalValue(storedValue: OptionValue<T>, newValue: OptionValue<T>, editor: VimEditor?): Boolean {
// The user is updating the global Vim value, via `:setglobal`. IdeaVim scrolling will be using this value. Make
// sure the IntelliJ values won't interfere
// Note that we don't reset the local IntelliJ value for `:set {option}&` or `:set {option}<` because the current
// global IntelliJ value might still interfere with IdeaVim's implementation. We continue to suppress the IntelliJ
// value.
injector.editorGroup.getEditors().forEach { suppressExternalLocalValue(it) }
return storedValue.value != newValue.value
}
override fun getLocalValue(storedValue: OptionValue<VimInt>?, editor: VimEditor): OptionValue<VimInt> {
override fun getLocalValue(storedValue: OptionValue<T>?, editor: VimEditor): OptionValue<T> {
if (storedValue == null) {
// Initialisation. Report the global value of the setting. We ignore the local value because the user doesn't have
// a way to set it, and we set it to 0 so that it doesn't affect our scroll calculations (because IntelliJ doesn't
// handle sidescroll=0 to mean half a page)
return OptionValue.Default(EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt())
// a way to set it. If it has been changed (unlikely if stored value hasn't been set yet), then it would be 0
return OptionValue.Default(getExternalGlobalValue())
}
if (storedValue is OptionValue.Default && storedValue.value != sideScrollOffOption.unsetValue) {
// The local value is set to the default value (as a copy of the global value), so return the global external
// value as a default
return OptionValue.Default(EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt())
if (storedValue is OptionValue.Default && storedValue.value != option.unsetValue) {
// The local value has been reset to Default. It's not the Vim default of "unset", but a copy of the global value.
// Return the current value of the global external value
return OptionValue.Default(getExternalGlobalValue())
}
// Whatever is left is either explicitly set by the user, or option.unsetValue
return storedValue
}
override fun setLocalValue(
storedValue: OptionValue<VimInt>?,
newValue: OptionValue<VimInt>,
editor: VimEditor,
): Boolean {
// This is setting the Vim local value. We do nothing but reset the local horizontal scroll jump so IntelliJ's
// scrolling doesn't affect our scrolling
editor.ij.settings.horizontalScrollOffset = 0
override fun setLocalValue(storedValue: OptionValue<T>?, newValue: OptionValue<T>, editor: VimEditor): Boolean {
// Vim local value is being set. We do nothing but set the local IntelliJ value to 0, so IntelliJ's scrolling
// doesn't affect IdeaVim's scrolling
suppressExternalLocalValue(editor)
return storedValue?.value != newValue.value
}
override fun onGlobalIdeaValueChanged(propertyName: String) {
if (propertyName == ideaPropertyName) {
// Again, just make sure the IntelliJ local value is 0
injector.editorGroup.getEditors().forEach { it.ij.settings.horizontalScrollOffset = 0 }
// The IntelliJ global value has changed. We want to use this as the Vim global value. Since we control scrolling,
// set the local IntelliJ value to 0
injector.editorGroup.getEditors().forEach { suppressExternalLocalValue(it) }
// Update the stored Vim global value. This will not override any existing local values
// Now update the Vim global value to match the new IntelliJ global value. If the current Vim global value is
// Default, then it will already reflect the current global external value. Otherwise, update the Vim global value
// to the external global value.
val globalScope = OptionAccessScope.GLOBAL(null)
val storedValue = internalOptionValueAccessor.getOptionValueInternal(sideScrollOffOption, globalScope)
val storedValue = internalOptionValueAccessor.getOptionValueInternal(option, globalScope)
if (storedValue !is OptionValue.Default) {
val externalGlobalValue = EditorSettingsExternalizable.getInstance().horizontalScrollOffset
internalOptionValueAccessor.setOptionValueInternal(
sideScrollOffOption,
option,
globalScope,
OptionValue.External(VimInt(externalGlobalValue))
OptionValue.External(getExternalGlobalValue())
)
}
}
}
protected abstract fun getExternalGlobalValue(): T
protected abstract fun suppressExternalLocalValue(editor: VimEditor)
}
@@ -1198,23 +1230,63 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
setIsUseSoftWraps(editor, value.asBoolean())
}
private fun getGlobalIsUseSoftWraps(editor: VimEditor): Boolean {
val settings = EditorSettingsExternalizable.getInstance()
if (settings.isUseSoftWraps) {
val masks = settings.softWrapFileMasks
if (masks.trim() == "*") return true
override fun canInitialiseOptionFrom(sourceEditor: VimEditor, targetEditor: VimEditor): Boolean {
// IntelliJ's soft-wrap settings are based on editor kind, so there can be different wrap options for consoles,
// main editors, etc. This is particularly noticeable in the console when running an application. The main editor
// might have the Vim default with line wrap enabled. Initialising the run console will also have a default value,
// and won't be updated by the options subsystem. It might have wrap enabled or not. If the editors were the same
// kind, the same default value would be used.
// However, if the main editor has 'wrap' explicitly set, this value is copied to the console, so the behaviour is
// inconsistent. Furthermore, the run console has a soft-wraps toggle button that works at the global level, and
// IdeaVim only sets the local value, so the toggle button can be inconsistent too.
// By denying copying the main editor value during initialisation, the console gets the default value, and the IDE
// decides what it should be. The behaviour is now more consistent.
// We're happy to initialise diff editors from main editors, as there isn't a different soft wrap setting there.
// Preview tabs might also have different settings, but because they're a type of main editor, it doesn't matter
// so much
fun editorKindToSoftWrapAppliancesPlace(kind: EditorKind) = when (kind) {
EditorKind.UNTYPED,
EditorKind.DIFF,
EditorKind.MAIN_EDITOR -> SoftWrapAppliancePlaces.MAIN_EDITOR
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
// Treat PREVIEW as a kind of MAIN_EDITOR instead of SWAP.PREVIEW. There are fewer noticeable differences
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.MAIN_EDITOR
}
editor.ij.virtualFile?.let { file ->
masks.split(";").forEach { mask ->
val trimmed = mask.trim()
if (trimmed.isNotEmpty() && PatternUtil.fromMask(trimmed).matcher(file.name).matches()) {
return true
val sourceKind = editorKindToSoftWrapAppliancesPlace(sourceEditor.ij.editorKind)
val targetKind = editorKindToSoftWrapAppliancesPlace(targetEditor.ij.editorKind)
return sourceKind == targetKind
}
private fun getGlobalIsUseSoftWraps(editor: VimEditor): Boolean {
val softWrapAppliancePlace = when (editor.ij.editorKind) {
EditorKind.UNTYPED,
EditorKind.DIFF,
EditorKind.MAIN_EDITOR -> SoftWrapAppliancePlaces.MAIN_EDITOR
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.PREVIEW
}
val settings = EditorSettingsExternalizable.getInstance()
if (softWrapAppliancePlace == SoftWrapAppliancePlaces.MAIN_EDITOR) {
if (settings.isUseSoftWraps) {
val masks = settings.softWrapFileMasks
if (masks.trim() == "*") return true
editor.ij.virtualFile?.let { file ->
masks.split(";").forEach { mask ->
val trimmed = mask.trim()
if (trimmed.isNotEmpty() && PatternUtil.fromMask(trimmed).matcher(file.name).matches()) {
return true
}
}
}
}
return false
}
return false
return settings.isUseSoftWraps(softWrapAppliancePlace)
}
private fun getEffectiveIsUseSoftWraps(editor: VimEditor) = editor.ij.settings.isUseSoftWraps
@@ -1241,28 +1313,28 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
}
public class IjOptionConstants {
class IjOptionConstants {
@Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName")
public companion object {
companion object {
public const val idearefactormode_keep: String = "keep"
public const val idearefactormode_select: String = "select"
public const val idearefactormode_visual: String = "visual"
const val idearefactormode_keep: String = "keep"
const val idearefactormode_select: String = "select"
const val idearefactormode_visual: String = "visual"
public const val ideastatusicon_enabled: String = "enabled"
public const val ideastatusicon_gray: String = "gray"
public const val ideastatusicon_disabled: String = "disabled"
const val ideastatusicon_enabled: String = "enabled"
const val ideastatusicon_gray: String = "gray"
const val ideastatusicon_disabled: String = "disabled"
public const val ideavimsupport_dialog: String = "dialog"
public const val ideavimsupport_singleline: String = "singleline"
public const val ideavimsupport_dialoglegacy: String = "dialoglegacy"
const val ideavimsupport_dialog: String = "dialog"
const val ideavimsupport_singleline: String = "singleline"
const val ideavimsupport_dialoglegacy: String = "dialoglegacy"
public const val ideawrite_all: String = "all"
public const val ideawrite_file: String = "file"
const val ideawrite_all: String = "all"
const val ideawrite_file: String = "file"
public val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
public val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
public val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
public val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
}
}

View File

@@ -18,111 +18,22 @@ import com.intellij.openapi.progress.ProgressIndicatorProvider
import com.intellij.openapi.progress.ProgressManager
import com.intellij.util.execution.ParametersListUtil
import com.intellij.util.text.CharSequenceReader
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
import com.maddyhome.idea.vim.KeyProcessResult
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimProcessGroupBase
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.helper.requestFocus
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
import com.maddyhome.idea.vim.state.mode.inVisualMode
import com.maddyhome.idea.vim.state.mode.returnTo
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import java.io.BufferedWriter
import java.io.IOException
import java.io.OutputStreamWriter
import java.io.Reader
import java.io.Writer
import javax.swing.KeyStroke
public class ProcessGroup : VimProcessGroupBase() {
override var lastCommand: String? = null
private set
override var isCommandProcessing: Boolean = false
override var modeBeforeCommandProcessing: Mode? = null
public override fun startExEntry(
editor: VimEditor,
context: ExecutionContext,
command: Command,
label: String,
initialCommandText: String,
) {
// Don't allow ex commands in one line editors
if (editor.isOneLineMode()) return
val currentMode = editor.vimStateMachine.mode
check(currentMode is ReturnableFromCmd) {
"Cannot enable cmd mode from current mode $currentMode"
}
isCommandProcessing = true
modeBeforeCommandProcessing = currentMode
// Make sure the Visual selection marks are up to date before we use them.
injector.markService.setVisualSelectionMarks(editor)
val rangeText = getRange(editor, command)
// Note that we should remove selection and reset caret offset before we switch back to Normal mode and then enter
// Command-line mode. However, some IdeaVim commands can handle multiple carets, including multiple carets with
// selection (which might or might not be a block selection). Unfortunately, because we're just entering
// Command-line mode, we don't know which command is going to be entered, so we can't remove selection here.
// Therefore, we switch to Normal and then Command-line even though we might still have a Visual selection...
// On the plus side, it means we still show selection while editing the command line, which Vim also does
// (Normal then Command-line is not strictly necessary, but done for completeness and autocmd)
// Caret selection is finally handled in Command.execute
editor.mode = Mode.NORMAL()
editor.mode = Mode.CMD_LINE(currentMode)
injector.commandLine.create(editor, context, ":", rangeText + initialCommandText, 1)
}
public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
// is open. So I'll put focus back in the editor and process the key.
val panel = ExEntryPanel.getInstance()
if (panel.isActive) {
processResultBuilder.addExecutionStep { _, _, _ ->
requestFocus(panel.entry)
panel.handleKey(stroke)
}
return true
} else {
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
lambdaEditor.mode = Mode.NORMAL()
getInstance().reset(lambdaEditor)
}
return false
}
}
public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
// If 'cpoptions' contains 'x', then Escape should execute the command line. This is the default for Vi but not Vim.
// IdeaVim does not (currently?) support 'cpoptions', so sticks with Vim's default behaviour. Escape cancels.
editor.mode = editor.mode.returnTo()
getInstance().reset(editor)
val panel = ExEntryPanel.getInstance()
panel.deactivate(true, resetCaret)
}
private fun getRange(editor: VimEditor, cmd: Command) = when {
editor.inVisualMode -> "'<,'>"
cmd.rawCount == 1 -> "."
cmd.rawCount > 1 -> ".,.+" + (cmd.count - 1)
else -> ""
}
class ProcessGroup : VimProcessGroupBase() {
@Throws(ExecutionException::class, ProcessCanceledException::class)
public override fun executeCommand(
override fun executeCommand(
editor: VimEditor,
command: String,
input: CharSequence?,
@@ -184,8 +95,6 @@ public class ProcessGroup : VimProcessGroupBase() {
val progressIndicator = ProgressIndicatorProvider.getInstance().progressIndicator
val output = handler.runProcessWithProgressIndicator(progressIndicator)
lastCommand = command
if (output.isCancelled) {
// TODO: Vim will use whatever text has already been written to stdout
// For whatever reason, we're not getting any here, so just throw an exception
@@ -221,7 +130,7 @@ public class ProcessGroup : VimProcessGroupBase() {
}
}
public companion object {
companion object {
private val logger = logger<ProcessGroup>()
}
}

View File

@@ -14,6 +14,8 @@ import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.VimInjectorKt;
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
import com.maddyhome.idea.vim.register.Register;
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
import com.maddyhome.idea.vim.state.mode.SelectionType;
@@ -35,6 +37,10 @@ import java.util.List;
})
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
static {
IjVimInjectorKt.initInjector();
}
private static final Logger logger = Logger.getInstance(RegisterGroup.class);
public RegisterGroup() {

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.mark.Jump
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.initInjector
import org.jdom.Element
@State(name = "VimJumpsSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
@@ -53,6 +54,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
jumpElem.setAttribute("line", jump.line.toString())
jumpElem.setAttribute("column", jump.col.toString())
jumpElem.setAttribute("filename", StringUtil.notNullize(jump.filepath))
jumpElem.setAttribute("protocol", StringUtil.notNullize(jump.protocol))
projectElement.addContent(jumpElem)
if (logger.isDebug()) {
logger.debug("saved jump = $jump")
@@ -64,6 +66,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
}
override fun loadState(state: Element) {
initInjector()
val projectElements = state.getChildren("project")
for (projectElement in projectElements) {
val jumps = mutableListOf<Jump>()
@@ -73,6 +76,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
Integer.parseInt(jumpElement.getAttributeValue("line")),
Integer.parseInt(jumpElement.getAttributeValue("column")),
jumpElement.getAttributeValue("filename"),
jumpElement.getAttributeValue("protocol", "file"),
)
jumps.add(jump)
}
@@ -87,6 +91,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
internal class JumpsListener(val project: Project) : RecentPlacesListener {
override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
initInjector()
if (!injector.globalIjOptions().unifyjumps) return
val jumpService = injector.jumpService
@@ -94,7 +99,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
// we do not want jumps that were processed before
val jump = buildJump(changePlace) ?: return
jumpService.addJump(project.basePath ?: IjVimEditor.DEFAULT_PROJECT_ID, jump, true)
jumpService.addJump(injector.file.getProjectId(project), jump, true)
}
}
@@ -106,7 +111,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
// we do not want jumps that were processed before
val jump = buildJump(changePlace) ?: return
jumpService.removeJump(project.basePath ?: IjVimEditor.DEFAULT_PROJECT_ID, jump)
jumpService.removeJump(injector.file.getProjectId(project), jump)
}
}
@@ -120,6 +125,6 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
val path = place.file.path
return Jump(line, col, path)
return Jump(line, col, path, place.file.fileSystem.protocol)
}
}

View File

@@ -46,6 +46,7 @@ import com.maddyhome.idea.vim.put.PutData
import com.maddyhome.idea.vim.put.VimPasteProvider
import com.maddyhome.idea.vim.put.VimPutBase
import com.maddyhome.idea.vim.register.RegisterConstants
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.isBlock
import com.maddyhome.idea.vim.state.mode.isChar
@@ -83,6 +84,11 @@ internal class PutGroup : VimPutBase() {
val editor = (vimEditor as IjVimEditor).editor
val context = vimContext.context as DataContext
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
if (injector.vimState.mode is Mode.INSERT) {
val undo = injector.undo
val nanoTime = System.nanoTime()
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
}
EditorHelper.getOrderedCaretsList(editor).forEach { caret ->
val startOffset =
prepareDocumentAndGetStartOffsets(

View File

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

View File

@@ -9,13 +9,9 @@
package com.maddyhome.idea.vim.group.visual
import com.intellij.find.FindManager
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.command.engine
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.SelectionType
/**
@@ -31,12 +27,4 @@ internal class VisualMotionGroup : VimVisualMotionGroupBase() {
return super.autodetectVisualSubmode(editor)
}
/**
* COMPATIBILITY-LAYER: Added a method
* Please see: https://jb.gg/zo8n0r
*/
fun enterVisualMode(editor: Editor, subMode: CommandState.SubMode? = null): Boolean {
return this.enterVisualMode(editor.vim, subMode?.engine)
}
}

View File

@@ -16,6 +16,7 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.maddyhome.idea.vim.api.key
import com.maddyhome.idea.vim.newapi.initInjector
/**
* Logs the chain of handlers for esc and enter
@@ -34,6 +35,8 @@ internal class EditorHandlersChainLogger : ProjectActivity {
private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler")
override suspend fun execute(project: Project) {
initInjector()
if (!enableOctopus) return
val escHandlers = editorHandlers.extensionList

View File

@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
*/
internal abstract class IdeActionHandler(private val actionName: String) : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
injector.actionExecutor.executeAction(actionName, context)
injector.actionExecutor.executeAction(editor, name = actionName, context = context)
injector.scroll.scrollCaretIntoView(editor)
return true
}

View File

@@ -218,13 +218,17 @@ internal class VimEnterHandler(nextHandler: EditorActionHandler?) : VimKeyHandle
internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
override val key: String = "<Esc>"
private val ideaVimSupportDialog
get() = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
val ideaVimSupportDialog =
injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
return editor.isPrimaryEditor() ||
EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode ||
ideaVimSupportDialog && !editor.vim.mode.inNormalMode
EditorHelper.isFileEditor(editor) && vimStateNeedsToHandleEscape(editor) ||
ideaVimSupportDialog && vimStateNeedsToHandleEscape(editor)
}
private fun vimStateNeedsToHandleEscape(editor: Editor): Boolean {
return !editor.vim.mode.inNormalMode || KeyHandler.getInstance().keyHandlerState.mappingState.hasKeys
}
}

View File

@@ -19,6 +19,7 @@ import com.maddyhome.idea.vim.VimPlugin
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.common.EditorListener
import com.maddyhome.idea.vim.common.IsReplaceCharListener
import com.maddyhome.idea.vim.common.ModeChangeListener
import com.maddyhome.idea.vim.newapi.IjVimEditor
@@ -73,7 +74,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
}
private fun Editor.guicursorMode(): GuiCursorMode {
return GuiCursorMode.fromMode(vim.mode, vim.vimStateMachine.isReplaceCharacter)
return GuiCursorMode.fromMode(vim.mode, injector.vimState.isReplaceCharacter)
}
/**
@@ -86,6 +87,7 @@ private fun isBlockCursorOverride() = EditorSettingsExternalizable.getInstance()
private fun Editor.updatePrimaryCaretVisualAttributes() {
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
if (isIdeaVimDisabledHere) return
caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
@@ -99,6 +101,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
private fun Editor.updateSecondaryCaretsVisualAttributes() {
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
if (isIdeaVimDisabledHere) return
// IntelliJ simulates visual block with multiple carets with selections. Do our best to hide them
val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
this.caretModel.allCarets.forEach {
@@ -144,7 +147,7 @@ private object AttributesCache {
@TestOnly
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener, EditorListener {
override fun isReplaceCharChanged(editor: VimEditor) {
updateCaretsVisual(editor)
}
@@ -153,21 +156,19 @@ public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeLi
updateCaretsVisual(editor)
}
private fun updateCaretsVisual(editor: VimEditor) {
if (injector.globalOptions().ideaglobalmode) {
updateAllEditorsCaretsVisual()
} else {
val ijEditor = (editor as IjVimEditor).editor
ijEditor.updateCaretsVisualAttributes()
ijEditor.updateCaretsVisualPosition()
}
override fun focusGained(editor: VimEditor) {
updateCaretsVisual(editor)
}
public fun updateAllEditorsCaretsVisual() {
private fun updateCaretsVisual(editor: VimEditor) {
val ijEditor = (editor as IjVimEditor).editor
ijEditor.updateCaretsVisualAttributes()
ijEditor.updateCaretsVisualPosition()
}
fun updateAllEditorsCaretsVisual() {
injector.editorGroup.getEditors().forEach { editor ->
val ijEditor = (editor as IjVimEditor).editor
ijEditor.updateCaretsVisualAttributes()
ijEditor.updateCaretsVisualPosition()
updateCaretsVisual(editor)
}
}
}

View File

@@ -11,13 +11,7 @@
package com.maddyhome.idea.vim.helper
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.api.hasValue
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.options.OptionAccessScope
import com.maddyhome.idea.vim.options.OptionConstants
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.inVisualMode
@@ -27,57 +21,17 @@ internal val Mode.hasVisualSelection
else -> false
}
/**
* COMPATIBILITY-LAYER: New method
* Please see: https://jb.gg/zo8n0r
*/
public val Editor.mode: CommandState.Mode
get() {
val mode = this.vim.vimStateMachine.mode
return when (mode) {
is Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
Mode.INSERT -> CommandState.Mode.INSERT
is Mode.NORMAL -> CommandState.Mode.COMMAND
is Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
Mode.REPLACE -> CommandState.Mode.REPLACE
is Mode.SELECT -> CommandState.Mode.SELECT
is Mode.VISUAL -> CommandState.Mode.VISUAL
}
}
/**
* COMPATIBILITY-LAYER: New method
* Please see: https://jb.gg/zo8n0r
*/
@Deprecated("Please migrate to VimEditor.isEndAllowed which can correctly access virtualedit at the right scope",
replaceWith = ReplaceWith("VimEditor.isEndAllowed"))
public val CommandState.Mode.isEndAllowed: Boolean
get() {
fun possiblyUsesVirtualSpace(): Boolean {
// virtualedit is GLOBAL_OR_LOCAL_TO_WINDOW. We should be using EFFECTIVE, but we don't have a valid editor (which
// is why this property is deprecated). Fetch the global value, passing in the fallback window to avoid asserts
// DO NOT COPY THIS APPROACH - ALWAYS USE A REAL WINDOW FOR NON-GLOBAL OPTIONS!
return injector.optionGroup.hasValue(Options.virtualedit, OptionAccessScope.GLOBAL(injector.fallbackWindow), OptionConstants.virtualedit_onemore)
}
return when (this) {
CommandState.Mode.INSERT, CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true
CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING -> possiblyUsesVirtualSpace()
CommandState.Mode.INSERT_NORMAL, CommandState.Mode.INSERT_VISUAL, CommandState.Mode.INSERT_SELECT -> possiblyUsesVirtualSpace()
}
}
public val Mode.inNormalMode: Boolean
val Mode.inNormalMode: Boolean
get() = this is Mode.NORMAL
@get:JvmName("inInsertMode")
public val Editor.inInsertMode: Boolean
val Editor.inInsertMode: Boolean
get() = this.vim.mode == Mode.INSERT || this.vim.mode == Mode.REPLACE
@get:JvmName("inVisualMode")
public val Editor.inVisualMode: Boolean
val Editor.inVisualMode: Boolean
get() = this.vim.inVisualMode
@get:JvmName("inExMode")
internal val Editor.inExMode
get() = this.vim.vimStateMachine.mode is Mode.CMD_LINE
get() = this.vim.mode is Mode.CMD_LINE

View File

@@ -100,15 +100,6 @@ public class EditorHelper {
return EngineEditorHelperKt.normalizeVisualLine(new IjVimEditor(editor), line);
}
/**
* COMPATIBILITY-LAYER: Created a function
* Please see: <a href="https://jb.gg/zo8n0r">doc</a>
*/
public static int getVisualLineCount(final @NotNull Editor editor) {
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
return EngineEditorHelperKt.getVisualLineCount(editor1);
}
/**
* Best efforts to ensure that scroll offset doesn't overlap itself.
* <p>

View File

@@ -28,7 +28,7 @@ import javax.swing.JComponent
import javax.swing.JTable
@Deprecated("Use fileSize from VimEditor")
public val Editor.fileSize: Int
val Editor.fileSize: Int
get() = document.textLength
/**

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