mirror of
https://github.com/chylex/IntelliJ-AceJump.git
synced 2025-09-16 07:24:50 +02:00
Compare commits
100 Commits
35003b0bab
...
pr-optimiz
Author | SHA1 | Date | |
---|---|---|---|
04c1519261
|
|||
![]() |
efc81b8fde | ||
![]() |
974c5cf258 | ||
![]() |
ae458094cf | ||
![]() |
db62460d83 | ||
![]() |
09888c2c34 | ||
![]() |
1d1f469663 | ||
![]() |
21df680792 | ||
![]() |
1af49c2e91 | ||
![]() |
ec7a769ae7 | ||
![]() |
d6d162e4cc | ||
![]() |
9d478c5b6e | ||
![]() |
b66868664e | ||
![]() |
c68487c989 | ||
![]() |
ca2b0a8dbe | ||
![]() |
76bc72335b | ||
![]() |
fe4e180aeb | ||
![]() |
932c933969 | ||
![]() |
16f205b11b | ||
d0ead7d87a
|
|||
![]() |
c2e672f869 | ||
![]() |
1256ffc237 | ||
![]() |
29a71bdddc | ||
![]() |
9d2e7f7569 | ||
![]() |
751e6d890b | ||
![]() |
283c1a5644 | ||
![]() |
73a37df7e2 | ||
9921409aff
|
|||
![]() |
3039b5fbb3 | ||
cb3d4559f9
|
|||
![]() |
d905909e31 | ||
![]() |
64c6956b88 | ||
![]() |
b1a044dbf8 | ||
![]() |
e46cbe1d06 | ||
![]() |
f64e25a0a9 | ||
![]() |
eba043dfb2 | ||
![]() |
a746b0a516 | ||
![]() |
209b6c97b7 | ||
![]() |
3c89cd07b9 | ||
![]() |
2ffcc2c57a | ||
![]() |
56b1dfa9a9 | ||
![]() |
48cadfef4c | ||
![]() |
6d47331c71 | ||
![]() |
3339f6bdb8 | ||
![]() |
e7e85a6944 | ||
![]() |
1839efb59f | ||
![]() |
da913f61b6 | ||
![]() |
7dd12a0457 | ||
![]() |
dae7bd8fa4 | ||
![]() |
7d39ea2fef | ||
![]() |
64ad5039f2 | ||
![]() |
fb3bfa068e | ||
![]() |
c52f046a6f | ||
![]() |
ba739501e8 | ||
![]() |
e939479368 | ||
![]() |
1c604ce03c | ||
![]() |
099c852e32 | ||
![]() |
6642eff1e0 | ||
![]() |
73ba06b296 | ||
![]() |
9507496092 | ||
![]() |
99e0a50884 | ||
![]() |
c070b211e3 | ||
![]() |
6a2b5dd422 | ||
![]() |
b7024842ba | ||
![]() |
4831f38b34 | ||
![]() |
07014455e6 | ||
![]() |
ab6c6a702b | ||
![]() |
8b9e8bb89b | ||
![]() |
6c6544cf47 | ||
![]() |
7fe9d13f48 | ||
![]() |
f1f9ec5974 | ||
![]() |
5271d97c00 | ||
![]() |
872a99c84c | ||
![]() |
f8b2db5090 | ||
![]() |
5f0965c921 | ||
![]() |
5ee0245f0b | ||
![]() |
176d450855 | ||
![]() |
1341c6aff4 | ||
![]() |
0b0622dcdf | ||
![]() |
929b08e9d0 | ||
![]() |
54ea6ac9c1 | ||
![]() |
e477a487a2 | ||
![]() |
f56dc3d457 | ||
![]() |
0befd7895e | ||
![]() |
df94f4ce2a | ||
![]() |
978edff59e | ||
![]() |
f9938590fb | ||
![]() |
c534192902 | ||
![]() |
6deac53165 | ||
![]() |
2c4d4431bc | ||
![]() |
2185da7cb4 | ||
![]() |
c2a71a2c43 | ||
![]() |
d29dd3fa34 | ||
![]() |
fdbcea0b4f | ||
![]() |
c14ae702ba | ||
![]() |
8de4495885 | ||
![]() |
dd75e1ddea | ||
![]() |
02f9ecec5a | ||
f17f4ef8c4
|
|||
![]() |
c9034bced6 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: breandan
|
81
CHANGES.md
81
CHANGES.md
@@ -2,14 +2,79 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 3.8.19
|
||||
|
||||
- Enable support for 2024.1, fixing ([#457](https://github.com/acejump/AceJump/issues/457))
|
||||
|
||||
## 3.8.18
|
||||
|
||||
- Disable tagging and jumping to folded regions ([#453](https://github.com/acejump/AceJump/issues/453)), thanks to [@chylex](https://github.com/chylex)
|
||||
- Update hint styling and show mode ([#394](https://github.com/acejump/AceJump/issues/394)) when "Show hint with search text" is enabled
|
||||
- Fixes "Char sequence is empty" ([#404](https://github.com/acejump/AceJump/issues/404)) when "Map Unicode to ASCII" is enabled
|
||||
|
||||
## 3.8.17
|
||||
|
||||
- Add buttons to reset colors to default values in Settings, [#411](https://github.com/acejump/AceJump/issues/411), thanks to [@chylex](https://github.com/chylex)
|
||||
- Unbundle conflicting Kotlin Standard Library version, [#449](https://github.com/acejump/AceJump/issues/449), thanks to [@chylex](https://github.com/chylex)
|
||||
- Fix some instances of "Read access not allowed", [#447](https://github.com/acejump/AceJump/issues/447), thanks to [@h0tk3y](https://github.com/h0tk3y)
|
||||
|
||||
## 3.8.16
|
||||
|
||||
- Fix issue with unselectable tags, [#446](https://github.com/acejump/AceJump/issues/446)
|
||||
|
||||
## 3.8.15
|
||||
|
||||
- Forbid jumping to offscreen tags, [#442](https://github.com/acejump/AceJump/issues/442)
|
||||
|
||||
## 3.8.14
|
||||
|
||||
- Fixes NoSuchFieldError: Companion on older platform versions, [#432](https://github.com/acejump/AceJump/issues/432), [#434](https://github.com/acejump/AceJump/issues/434), [#435](https://github.com/acejump/AceJump/issues/432), [#437](https://github.com/acejump/AceJump/issues/437), [#438](https://github.com/acejump/AceJump/issues/438), thanks to [@wuruofan](https://github.com/wuruofan)
|
||||
|
||||
## 3.8.13
|
||||
|
||||
- Fixes color settings not being persisted, [#431](https://github.com/acejump/AceJump/issues/431)
|
||||
|
||||
## 3.8.12
|
||||
|
||||
- Fixes tag cycling issue with Enter/Shift+Enter, [#429](https://github.com/acejump/AceJump/issues/429)
|
||||
|
||||
## 3.8.11
|
||||
|
||||
- Fixes UI issue affecting mode cycling order, [#426](https://github.com/acejump/AceJump/issues/426)
|
||||
|
||||
## 3.8.10
|
||||
|
||||
- Fixes regression in 3.8.9 breaking cross-tab selection, [#417](https://github.com/acejump/AceJump/issues/417)
|
||||
|
||||
## 3.8.9
|
||||
|
||||
- Add ids to editor action handlers, [#410](https://github.com/acejump/AceJump/pull/410), thanks to [@AlexPl292](https://github.com/AlexPl292)
|
||||
- Update API to IJ-2022.3 and JDK to 17
|
||||
|
||||
## 3.8.8
|
||||
|
||||
- Add AZERTY keyboard layout, [#398](https://github.com/acejump/AceJump/pull/398), thanks to [@delphinaubin](https://github.com/delphinaubin)
|
||||
- Add bounded toggle mode to start jump mode before or after the caret, [#401](https://github.com/acejump/AceJump/pull/401), thanks to [@colossatr0n](https://github.com/colossatr0n)
|
||||
- Remove only the highlighters added by AceJump when jump session ends, [#407](https://github.com/acejump/AceJump/pull/407), thanks to [@huoguangjin](https://github.com/huoguangjin)
|
||||
|
||||
## 3.8.7
|
||||
|
||||
- Fixes Unicode-ASCII regression, [#399](https://github.com/acejump/AceJump/issues/399)
|
||||
|
||||
## 3.8.6
|
||||
|
||||
- Adds AZERTY keyboard layout, [#398](https://github.com/acejump/AceJump/pull/398), thanks to [@delphinaubin](https://github.com/delphinaubin)
|
||||
|
||||
## 3.8.5
|
||||
|
||||
- Improves tag order for non-QWERTY layouts, [#385](https://github.com/acejump/AceJump/issues/385)
|
||||
- Restores <kbd>Tab</kbd>/<kbd>Shift</kbd>+<kbd>Tab</kbd> functionality, [#356](https://github.com/acejump/AceJump/issues/356)
|
||||
- Fixes tag cycling with <kbd>Enter</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>, [#380](https://github.com/acejump/AceJump/issues/380), thanks [@AlexPl292](https://github.com/AlexPl292)
|
||||
|
||||
## 3.8.4
|
||||
|
||||
- Fixes Declaration Mode in Rider, [#379](https://github.com/acejump/AceJump/issues/379), thanks to @igor-akhmetov for helping diagnose!
|
||||
- Fixes highlight offset on high-DPI screens, [#362](https://github.com/acejump/AceJump/issues/362), thanks to @chylex for [the PR](https://github.com/acejump/AceJump/pull/384)!
|
||||
- Fixes Declaration Mode in Rider, [#379](https://github.com/acejump/AceJump/issues/379), thanks to [@igor-akhmetov](https://github.com/igor-akhmetov) for helping diagnose!
|
||||
- Fixes highlight offset on high-DPI screens, [#362](https://github.com/acejump/AceJump/issues/362), thanks to [@chylex](https://github.com/chylex) for [the PR](https://github.com/acejump/AceJump/pull/384)!
|
||||
|
||||
## 3.8.3
|
||||
|
||||
@@ -20,7 +85,7 @@
|
||||
|
||||
- Add option to display current search text, [#375](https://github.com/acejump/AceJump/issues/375)
|
||||
- Fixes a bug where editor was not focused, [#374](https://github.com/acejump/AceJump/issues/374)
|
||||
- Thanks to @SaiKai for the PRs!
|
||||
- Thanks to [@SaiKai](https://github.com/SaiKai) for the PRs!
|
||||
|
||||
## 3.8.1
|
||||
|
||||
@@ -35,7 +100,7 @@
|
||||
- Update AceJump extension API to include tag information, [#357](https://github.com/acejump/AceJump/pull/357)
|
||||
- Allow defining jump mode with boundaries, [#358](https://github.com/acejump/AceJump/pull/358)
|
||||
- Use Kotlin classes for actions, [#359](https://github.com/acejump/AceJump/pull/359)
|
||||
- Thanks to @AlexPl292 for the PRs!
|
||||
- Thanks to [@AlexPl292](https://github.com/AlexPl292) for the PRs!
|
||||
|
||||
## 3.7.0
|
||||
- Improvements to tag latency
|
||||
@@ -49,7 +114,7 @@
|
||||
- Increase limit for what is considered a large file
|
||||
- Major refactoring, [#350](https://github.com/acejump/AceJump/pull/353)
|
||||
- [Many bug fixes](https://github.com/acejump/AceJump/issues/348#issuecomment-739454920): [#338](https://github.com/acejump/AceJump/issues/338), [#336](https://github.com/acejump/AceJump/issues/336), [#329](https://github.com/acejump/AceJump/issues/329), [#327](https://github.com/acejump/AceJump/issues/327), [#310](https://github.com/acejump/AceJump/issues/310), [#233](https://github.com/acejump/AceJump/issues/233), [#228](https://github.com/acejump/AceJump/issues/228), [#187](https://github.com/acejump/AceJump/issues/187), [#147](https://github.com/acejump/AceJump/issues/147), [#132](https://github.com/acejump/AceJump/issues/132), [#71](https://github.com/acejump/AceJump/issues/71)
|
||||
- Huge thanks to @chylex for [all the PRs](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3Achylex)!
|
||||
- Huge thanks to [@chylex](https://github.com/chylex) for [all the PRs](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3Achylex)!
|
||||
|
||||
## 3.6.3
|
||||
|
||||
@@ -59,12 +124,12 @@
|
||||
|
||||
## 3.6.2
|
||||
|
||||
- Fixes [#226](https://github.com/acejump/AceJump/issues/226). Thanks @AlexPl292!
|
||||
- Fixes [#226](https://github.com/acejump/AceJump/issues/226). Thanks [@AlexPl292](https://github.com/AlexPl292)!
|
||||
- Update Pinyin engine.
|
||||
|
||||
## 3.6.1
|
||||
|
||||
- Fixes [#324](https://github.com/acejump/AceJump/issues/324). Thanks @AlexPl292!
|
||||
- Fixes [#324](https://github.com/acejump/AceJump/issues/324). Thanks [@AlexPl292](https://github.com/AlexPl292)!
|
||||
- Fixes [#325](https://github.com/acejump/AceJump/issues/325).
|
||||
- Fixes Pinyin support.
|
||||
|
||||
@@ -72,7 +137,7 @@
|
||||
|
||||
- Adds support for Chinese [#314](https://github.com/acejump/AceJump/issues/314).
|
||||
- Fixes constantly loading settings page [#303](https://github.com/acejump/AceJump/issues/303).
|
||||
- Honor camel humps [#315](https://github.com/acejump/AceJump/issues/315). Thanks to @clojj.
|
||||
- Honor camel humps [#315](https://github.com/acejump/AceJump/issues/315). Thanks to [@clojj](https://github.com/clojj).
|
||||
- Support dynamic application reloading [#322](https://github.com/acejump/AceJump/issues/322).
|
||||
|
||||
## 3.5.9
|
||||
|
62
README.md
62
README.md
@@ -1,7 +1,7 @@
|
||||
<p align="center"><a href="https://plugins.jetbrains.com/plugin/7086"> <img src="logo.png" alt="AceJumpLogo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub" title="JetBrains on GitHub"><img src="http://jb.gg/badges/team.svg"></a>
|
||||
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub" title="JetBrains on GitHub"><img src="https://jb.gg/badges/team.svg"></a>
|
||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=acejump_buildplugin&guest=1" title="Build Plugin"><img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:acejump_buildplugin/statusIcon.svg"></a>
|
||||
<a href="https://plugins.jetbrains.com/plugin/7086-acejump" title="Jetbrains Plugin"><img src="https://img.shields.io/jetbrains/plugin/v/7086-acejump.svg"></a>
|
||||
<a href="LICENSE" title="License"><img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg"></a>
|
||||
@@ -24,16 +24,18 @@ Press the AceJump shortcut, followed by <kbd>→</kbd> to target the last, <kbd>
|
||||
|
||||

|
||||
|
||||
AceJump search is [smart case](http://ideavim.sourceforge.net/vim/usr_27.html#vim.27%2E1) sensitive, however tag selection is *not* case sensitive. Holding down <kbd>Shift</kbd> when typing the last tag character will select all text from the current cursor position to that destination.
|
||||
|
||||
## Tips
|
||||
|
||||
- Press <kbd>Tab</kbd> when searching to jump to the next group of matches in the editor. *This feature is unavailable in `3.6.4-3.8.4`.*
|
||||
- Press <kbd>Tab</kbd> when searching to jump to the next group of matches in the editor.
|
||||
|
||||
- If you make a mistake searching, just press <kbd>Backspace</kbd> to restart from scratch.
|
||||
|
||||
- If no matches can be found on-screen, AceJump will scroll to the next match it can find.
|
||||
|
||||
- Note that search is [smart case](http://ideavim.sourceforge.net/vim/usr_27.html#vim.27%2E1) sensitive, however tag selection is *not* case sensitive.
|
||||
|
||||
- Holding down <kbd>Shift</kbd> when typing the last tag character will select all text from the current cursor position to that destination.
|
||||
|
||||
- Pressing <kbd>Enter</kbd> or <kbd>Shift</kbd>+<kbd>Enter</kbd> during a search will cycle through tagged results on screen.
|
||||
|
||||
- To select a location and continue editing, just press <kbd>Esc</kbd>.
|
||||
@@ -54,8 +56,6 @@ AceJump search is [smart case](http://ideavim.sourceforge.net/vim/usr_27.html#vi
|
||||
|
||||
AceJump can be [installed directly from the IDE](https://www.jetbrains.com/help/idea/managing-plugins.html#install), via **Settings | Plugins | Browse Repositories... | 🔍 "AceJump"**.
|
||||
|
||||
[Canary builds](https://teamcity.jetbrains.com/repository/download/acejump_buildplugin/.lastSuccessful/AceJump.zip?guest=1) are provided courtesy of [TeamCity](https://www.jetbrains.com/teamcity/). These can be downloaded and [installed from disk](https://www.jetbrains.com/help/idea/managing-plugins.html#install_plugin_from_disk).
|
||||
|
||||
## Configuring
|
||||
|
||||
[IdeaVim](https://plugins.jetbrains.com/plugin/164) users can choose to activate AceJump with a single keystroke (<kbd>f</kbd>, <kbd>F</kbd> and <kbd>g</kbd> are arbitrary) by running:
|
||||
@@ -64,15 +64,25 @@ AceJump can be [installed directly from the IDE](https://www.jetbrains.com/help/
|
||||
echo -e '
|
||||
|
||||
" Press `f` to activate AceJump
|
||||
map f :action AceAction<CR>
|
||||
map f <Action>(AceAction)
|
||||
" Press `F` to activate Target Mode
|
||||
map F :action AceTargetAction<CR>
|
||||
map F <Action>(AceTargetAction)
|
||||
" Press `g` to activate Line Mode
|
||||
map g :action AceLineAction<CR>
|
||||
map g <Action>(AceLineAction)
|
||||
|
||||
' >> ~/.ideavimrc
|
||||
```
|
||||
|
||||
To customize AceJump's behavior further with additional actions, see the `<action>` tags in [plugin.xml](src/main/resources/META-INF/plugin.xml). The following example shows how to activate AceJump before or after the caret.
|
||||
|
||||
```
|
||||
" Press `S` in normal mode to activate AceJump mode before the caret
|
||||
nmap S <Action>(AceBackwardAction)
|
||||
|
||||
" Press `s` in normal mode to activate AceJump mode after the caret
|
||||
nmap s <Action>(AceForwardAction)
|
||||
```
|
||||
|
||||
To change the default keyboard shortcuts, open **File \| Settings \| Keymap \| 🔍 "AceJump" \| AceJump \|** <kbd>Enter⏎</kbd>.
|
||||
|
||||

|
||||
@@ -168,29 +178,38 @@ AceJump is inspired by prior work, but adds several improvements, including:
|
||||
The following plugins have a similar UI for navigating text and web browsing:
|
||||
|
||||
| Source Code | Download | Application | Actively Maintained | Language |
|
||||
|:----------------------------------------------------------------------|:-------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------:|:-------------------:|:------------------------------------------------------------------------:|
|
||||
|:----------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------:|:-------------------:|:------------------------------------------------------------------------:|
|
||||
| AceJump | [⬇](https://plugins.jetbrains.com/plugin/7086-acejump) | [IntelliJ Platform](https://jetbrains.com) | :heavy_check_mark: | [Kotlin](http://kotlinlang.org/) |
|
||||
| [IdeaVim-EasyMotion](https://github.com/AlexPl292/IdeaVim-EasyMotion) | [⬇](https://github.com/AlexPl292/IdeaVim-EasyMotion) | [IntelliJ Platform](https://jetbrains.com) | :heavy_check_mark: | [Kotlin](http://kotlinlang.org/) |
|
||||
| [AceJump-Lite](https://github.com/EeeMt/AceJump-Lite) | [⬇](https://plugins.jetbrains.com/plugin/9803-acejump-lite) | [IntelliJ Platform](https://jetbrains.com) | :heavy_check_mark: | [Java](https://www.java.com) |
|
||||
| [KJump](https://github.com/a690700752/KJump) | [⬇](https://plugins.jetbrains.com/plugin/10149-kjump) | [IntelliJ Platform](https://jetbrains.com) | :heavy_check_mark: | [Java](https://www.java.com) |
|
||||
| [emacsIDEAs](https://github.com/whunmr/emacsIDEAs) | [⬇](https://plugins.jetbrains.com/plugin/7163-emacsideas) | [IntelliJ Platform](https://jetbrains.com) | :heavy_check_mark: | [Java](https://www.java.com) |
|
||||
| [TraceJump](https://github.com/acejump/tracejump) | [⬇](https://github.com/acejump/tracejump) | Desktop | :heavy_check_mark: | [Kotlin](http://kotlinlang.org/) |
|
||||
| [KJump](https://github.com/a690700752/KJump) | [⬇](https://plugins.jetbrains.com/plugin/10149-kjump) | [IntelliJ Platform](https://jetbrains.com) | :heavy_check_mark: | [Kotlin](http://kotlinlang.org/) |
|
||||
| [AceJump-Lite](https://github.com/EeeMt/AceJump-Lite) | [⬇](https://plugins.jetbrains.com/plugin/9803-acejump-lite) | [IntelliJ Platform](https://jetbrains.com) | :x: | [Java](https://www.java.com) |
|
||||
| [emacsIDEAs](https://github.com/whunmr/emacsIDEAs) | [⬇](https://plugins.jetbrains.com/plugin/7163-emacsideas) | [IntelliJ Platform](https://jetbrains.com) | :x: | [Java](https://www.java.com) |
|
||||
| [TraceJump](https://github.com/acejump/tracejump) | [⬇](https://github.com/acejump/tracejump) | Desktop | :x: | [Kotlin](http://kotlinlang.org/) |
|
||||
| [ace-jump-mode](https://github.com/winterTTr/ace-jump-mode) | [⬇](https://melpa.org/#/ace-jump-mode) | [emacs](https://www.gnu.org/software/emacs/) | :x: | [Emacs Lisp](https://www.gnu.org/software/emacs/manual/eintr.html) |
|
||||
| [avy](https://github.com/abo-abo/avy) | [⬇](https://melpa.org/#/avy) | [emacs](https://www.gnu.org/software/emacs/) | :heavy_check_mark: | [Emacs Lisp](https://www.gnu.org/software/emacs/manual/eintr.html) |
|
||||
| [EasyMotion](https://github.com/easymotion/vim-easymotion) | [⬇](https://vimawesome.com/plugin/easymotion) | [Vim](http://www.vim.org/) | :x: | [Vimscript](http://learnvimscriptthehardway.stevelosh.com/) |
|
||||
| [eyeliner.nvim](https://github.com/jinh0/eyeliner.nvim) | [⬇](https://github.com/jinh0/eyeliner.nvim?tab=readme-ov-file#-installation) | [NeoVim](https://neovim.io/) | :heavy_check_mark: | [Lua](https://www.lua.org/) |
|
||||
| [Hop](https://github.com/phaazon/hop.nvim) | [⬇](https://github.com/phaazon/hop.nvim#installation) | [NeoVim](https://neovim.io/) | :heavy_check_mark: | [Lua](https://www.lua.org/) |
|
||||
| [leap.nvim](https://github.com/ggandor/leap.nvim) | [⬇](https://github.com/ggandor/leap.nvim#installation) | [NeoVim](https://neovim.io/) | :heavy_check_mark: | [Fennel](https://fennel-lang.org) |
|
||||
| [lightspeed.nvim](https://github.com/ggandor/lightspeed.nvim) | [⬇](https://github.com/ggandor/lightspeed.nvim#installation) | [NeoVim](https://neovim.io/) | :x: | [Fennel](https://fennel-lang.org) |
|
||||
| [Sublime EasyMotion](https://github.com/tednaleid/sublime-EasyMotion) | [⬇](https://packagecontrol.io/packages/EasyMotion) | [Sublime](https://www.sublimetext.com/) | :x: | [Python](https://www.python.org/) |
|
||||
| [AceJump](https://github.com/ice9js/ace-jump-sublime) | [⬇](https://packagecontrol.io/packages/AceJump) | [Sublime](https://www.sublimetext.com/) | :heavy_check_mark: | [Python](https://www.python.org/) |
|
||||
| [Jumpy](https://github.com/DavidLGoldberg/jumpy) | [⬇](https://atom.io/packages/jumpy) | [Atom](https://atom.io/) | :heavy_check_mark: | [CoffeeScript](http://coffeescript.org/) |
|
||||
| [AceJump](https://github.com/ice9js/ace-jump-sublime) | [⬇](https://packagecontrol.io/packages/AceJump) | [Sublime](https://www.sublimetext.com/) | :x: | [Python](https://www.python.org/) |
|
||||
| [Jumpy](https://github.com/DavidLGoldberg/jumpy) | [⬇](https://atom.io/packages/jumpy) | [Atom](https://atom.io/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [Jumpy2](https://github.com/DavidLGoldberg/jumpy2) | [⬇](https://marketplace.visualstudio.com/items?itemName=DavidLGoldberg.jumpy2) | [Visual Studio Code](https://code.visualstudio.com/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [Find-Jump](https://github.com/msafi/xvsc/tree/master/findJump) | [⬇](https://marketplace.visualstudio.com/items?itemName=mksafi.find-jump) | [Visual Studio Code](https://code.visualstudio.com/) | :x: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [MetaGo](https://github.com/metaseed/metaGo) | [⬇](https://marketplace.visualstudio.com/items?itemName=metaseed.metago) | [Visual Studio Code](https://code.visualstudio.com/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [VSCodeVim](https://github.com/VSCodeVim/Vim) | [⬇](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim) | [Visual Studio Code](https://code.visualstudio.com/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [CodeAceJumper](https://github.com/lucax88x/CodeAceJumper) | [⬇](https://marketplace.visualstudio.com/items?itemName=lucax88x.codeacejumper) | [Visual Studio Code](https://code.visualstudio.com/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [CodeAceJumper](https://github.com/lucax88x/CodeAceJumper) | [⬇](https://marketplace.visualstudio.com/items?itemName=lucax88x.codeacejumper) | [Visual Studio Code](https://code.visualstudio.com/) | :x: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [AceJump](https://github.com/jsturtevant/ace-jump) | [⬇](https://marketplace.visualstudio.com/items?itemName=jsturtevant.AceJump) | [Visual Studio](https://www.visualstudio.com/) | :x: | [C#](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/) |
|
||||
| [EasyMotion](https://github.com/jaredpar/EasyMotion) | [⬇](https://marketplace.visualstudio.com/items?itemName=JaredParMSFT.EasyMotion) | [Visual Studio](https://www.visualstudio.com/) | :x: | [C#](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/) |
|
||||
| [cVim](https://github.com/1995eaton/chromium-vim) | [⬇](https://chrome.google.com/webstore/detail/cvim/ihlenndgcmojhcghmfjfneahoeklbjjh) | [Chrome](https://www.google.com/chrome) | :heavy_check_mark: | [JavaScript](https://www.javascript.com/) |
|
||||
| [SurfingKeys](https://github.com/brookhong/Surfingkeys) | [⬇](https://chrome.google.com/webstore/detail/surfingkeys/gfbliohnnapiefjpjlpjnehglfpaknnc) | [Chrome](https://www.google.com/chrome) / [Firefox](https://www.mozilla.org/firefox) | :heavy_check_mark: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Vimium](https://github.com/philc/vimium) | [⬇](https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb) | [Chrome](https://www.google.com/chrome) | :heavy_check_mark: | [CoffeeScript](http://coffeescript.org/) |
|
||||
| [tmux-fingers](https://github.com/Morantron/tmux-fingers) | [⬇](https://github.com/Morantron/tmux-fingers#using-tmux-plugin-manager) | [tmux](https://github.com/tmux/tmux) | :heavy_check_mark: | [Crystal](https://crystal-lang.org/) |
|
||||
| [tmux-thumb](https://github.com/Morantron/tmux-fingers) | [⬇](https://github.com/fcsonline/tmux-thumbs#using-tmux-plugin-manager) | [tmux](https://github.com/tmux/tmux) | :heavy_check_mark: | [Rust](https://www.rust-lang.org/) |
|
||||
| [tmux-jump](https://github.com/schasse/tmux-jump) | [⬇](https://github.com/schasse/tmux-jump#installation-via-tpm) | [tmux](https://github.com/tmux/tmux) | :heavy_check_mark: | [Ruby](https://www.ruby-lang.org) |
|
||||
| [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) | [⬇](https://github.com/tmux-plugins/tmux-copycat?tab=readme-ov-file#installation-with-tmux-plugin-manager-recommended) | [tmux](https://github.com/tmux/tmux) | :x: | [Shell](https://www.shellscript.sh/) |
|
||||
| [cVim](https://github.com/1995eaton/chromium-vim) | [⬇](https://chrome.google.com/webstore/detail/cvim/ihlenndgcmojhcghmfjfneahoeklbjjh) | [Chrome](https://www.google.com/chrome) | :x: | [JavaScript](https://www.javascript.com/) |
|
||||
| [SurfingKeys](https://github.com/brookhong/Surfingkeys) | [⬇](https://chrome.google.com/webstore/detail/surfingkeys/gfbliohnnapiefjpjlpjnehglfpaknnc) | [Chrome](https://www.google.com/chrome)/[Firefox](https://www.mozilla.org/firefox)/[Edge](https://microsoftedge.microsoft.com/) | :heavy_check_mark: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Vimium](https://github.com/philc/vimium) | [⬇](https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb) | [Chrome](https://www.google.com/chrome)/[Firefox](https://www.mozilla.org/firefox)/[Edge](https://microsoftedge.microsoft.com/) | :heavy_check_mark: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Vimium-C](https://github.com/gdh1995/vimium-c) | [⬇](https://microsoftedge.microsoft.com/addons/detail/vimium-c-all-by-keyboar/aibcglbfblnogfjhbcmmpobjhnomhcdo) | [Chrome](https://www.google.com/chrome)/[Firefox](https://www.mozilla.org/firefox)/[Edge](https://microsoftedge.microsoft.com/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [Vrome](https://github.com/jinzhu/vrome) | [⬇](https://chrome.google.com/webstore/detail/vrome/godjoomfiimiddapohpmfklhgmbfffjj) | [Chrome](https://www.google.com/chrome) | :x: | [CoffeeScript](http://coffeescript.org/) |
|
||||
| [ViChrome](https://github.com/k2nr/ViChrome) | [⬇](https://chrome.google.com/webstore/detail/vichrome/gghkfhpblkcmlkmpcpgaajbbiikbhpdi) | [Chrome](https://www.google.com/chrome) | :x: | [CoffeeScript](http://coffeescript.org/) |
|
||||
| [VimFx](https://github.com/akhodakivskiy/VimFx) | [⬇](https://github.com/akhodakivskiy/VimFx/releases) | [Firefox](https://www.mozilla.org/firefox) | :heavy_check_mark: | [CoffeeScript](http://coffeescript.org/) |
|
||||
@@ -198,7 +217,8 @@ The following plugins have a similar UI for navigating text and web browsing:
|
||||
| [Pentadactyl](https://github.com/5digits/dactyl) | [⬇](http://bug.5digits.org/pentadactyl/#sect-download) | [Firefox](https://www.mozilla.org/firefox) | :x: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Vim Vixen](https://github.com/ueokande/vim-vixen) | [⬇](https://addons.mozilla.org/firefox/addon/vim-vixen/) | [Firefox 57+](https://blog.mozilla.org/addons/2017/09/28/webextensions-in-firefox-57/) | :heavy_check_mark: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Tridactyl](https://github.com/tridactyl/tridactyl) | [⬇](https://addons.mozilla.org/firefox/addon/tridactyl-vim/) | [Firefox 57+](https://blog.mozilla.org/addons/2017/09/28/webextensions-in-firefox-57/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
| [Vimari](https://github.com/guyht/vimari) | [⬇](https://github.com/guyht/vimari/releases) | [Safari](https://www.apple.com/safari/) | :heavy_check_mark: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Vimari](https://github.com/guyht/vimari) | [⬇](https://github.com/guyht/vimari/releases) | [Safari](https://www.apple.com/safari/) | :x: | [JavaScript](https://www.javascript.com/) |
|
||||
| [Jump To Link](https://github.com/mrjackphil/obsidian-jump-to-link) | [⬇](https://obsidian.md/plugins?id=mrj-jump-to-link) | [Obsidian](https://obsidian.md/) | :heavy_check_mark: | [TypeScript](https://www.typescriptlang.org/) |
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
|
103
build.gradle.kts
103
build.gradle.kts
@@ -1,73 +1,100 @@
|
||||
import org.jetbrains.changelog.*
|
||||
import org.jetbrains.intellij.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.changelog.Changelog.OutputType.HTML
|
||||
import org.jetbrains.changelog.date
|
||||
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
|
||||
|
||||
plugins {
|
||||
idea apply true
|
||||
kotlin("jvm") version "1.6.0-RC2"
|
||||
id("org.jetbrains.intellij") version "1.2.1"
|
||||
id("org.jetbrains.changelog") version "1.3.1"
|
||||
id("com.github.ben-manes.versions") version "0.39.0"
|
||||
idea
|
||||
alias(libs.plugins.kotlin) // Kotlin support
|
||||
alias(libs.plugins.intelliJPlatform) // IntelliJ Platform Gradle Plugin
|
||||
alias(libs.plugins.changelog) // Gradle Changelog Plugin
|
||||
alias(libs.plugins.kover) // Gradle Kover Plugin
|
||||
id("com.github.ben-manes.versions") version "0.51.0"
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
named<Zip>("buildPlugin") {
|
||||
dependsOn("test")
|
||||
archiveFileName.set("AceJump.zip")
|
||||
archiveFileName = "AceJump.zip"
|
||||
}
|
||||
|
||||
withType<RunIdeTask> {
|
||||
dependsOn("test")
|
||||
runIde {
|
||||
findProperty("luginDev")?.let { args = listOf(projectDir.absolutePath) }
|
||||
}
|
||||
|
||||
publishPlugin {
|
||||
val intellijPublishToken: String? by project
|
||||
token.set(intellijPublishToken)
|
||||
token = intellijPublishToken
|
||||
}
|
||||
|
||||
patchPluginXml {
|
||||
sinceBuild.set("203.7717.56")
|
||||
changeNotes.set(provider {
|
||||
changelog.getAll().values.take(2).last().toHTML()
|
||||
})
|
||||
sinceBuild = "223.7571.182"
|
||||
changeNotes = provider {
|
||||
changelog.renderItem(changelog.getAll().values.take(2).last(), HTML)
|
||||
}
|
||||
}
|
||||
|
||||
runPluginVerifier {
|
||||
ideVersions.set(listOf("2021.2.1"))
|
||||
// Remove pending: https://youtrack.jetbrains.com/issue/IDEA-278926
|
||||
val test by getting(Test::class) {
|
||||
isScanForTestClasses = false
|
||||
// Only run tests from classes that end with "Test"
|
||||
include("**/AceTest.class")
|
||||
include("**/ExternalUsageTest.class")
|
||||
include("**/LatencyTest.class")
|
||||
afterTest(
|
||||
KotlinClosure2({ desc: TestDescriptor, result: TestResult ->
|
||||
println("Completed `${desc.displayName}` in ${result.endTime - result.startTime}ms")
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
sourceSets.all {
|
||||
languageSettings.apply {
|
||||
languageVersion = "2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val acejumpVersion = "3.8.19"
|
||||
|
||||
changelog {
|
||||
version.set("3.8.5")
|
||||
path.set("${project.projectDir}/CHANGES.md")
|
||||
header.set(provider { "[${project.version}] - ${date()}" })
|
||||
itemPrefix.set("-")
|
||||
unreleasedTerm.set("Unreleased")
|
||||
version = acejumpVersion
|
||||
path = "${project.projectDir}/CHANGES.md"
|
||||
header = provider { "[${project.version}] - ${date()}" }
|
||||
itemPrefix = "-"
|
||||
unreleasedTerm = "Unreleased"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://jitpack.io")
|
||||
intellijPlatform.defaultRepositories()
|
||||
// intellijPlatform.localPlatformArtifacts()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// gradle-intellij-plugin doesn't attach sources properly for Kotlin :(
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
compileOnly(kotlin("stdlib-jdk8"))
|
||||
implementation("com.anyascii:anyascii:0.3.0")
|
||||
// https://github.com/anyascii/anyascii
|
||||
implementation("com.anyascii:anyascii:0.3.2")
|
||||
intellijPlatform{
|
||||
testImplementation(libs.junit)
|
||||
|
||||
bundledPlugins("com.intellij.java")
|
||||
create("IC", "2024.1.4")
|
||||
pluginVerifier()
|
||||
instrumentationTools()
|
||||
testFramework(TestFrameworkType.Platform)
|
||||
}
|
||||
}
|
||||
|
||||
intellij {
|
||||
version.set("2021.2.1")
|
||||
pluginName.set("AceJump")
|
||||
updateSinceUntilBuild.set(false)
|
||||
plugins.set(listOf("java"))
|
||||
intellijPlatform {
|
||||
pluginConfiguration {
|
||||
version = acejumpVersion
|
||||
name = "AceJump"
|
||||
}
|
||||
|
||||
pluginVerification.ides.recommended()
|
||||
}
|
||||
|
||||
group = "org.acejump"
|
||||
version = "3.8.5"
|
||||
version = acejumpVersion
|
||||
|
4
gradle.properties
Normal file
4
gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
kotlin.stdlib.default.dependency=false
|
||||
kotlin.incremental.useClasspathSnapshot=false
|
||||
|
||||
org.gradle.jvmargs=-Xmx2048m
|
18
gradle/libs.versions.toml
Normal file
18
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[versions]
|
||||
# libraries
|
||||
junit = "4.13.2"
|
||||
|
||||
# plugins
|
||||
changelog = "2.2.1"
|
||||
intelliJPlatform = "2.0.0"
|
||||
kotlin = "2.0.0"
|
||||
kover = "0.8.3"
|
||||
|
||||
[libraries]
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
|
||||
[plugins]
|
||||
changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
|
||||
intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -2,9 +2,12 @@ package org.acejump
|
||||
|
||||
import com.anyascii.AnyAscii
|
||||
import com.intellij.diff.util.DiffUtil.getLineCount
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.*
|
||||
import com.intellij.openapi.util.Computable
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import org.acejump.config.AceConfig
|
||||
import java.awt.Point
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
@@ -38,7 +41,7 @@ object EditorsCache {
|
||||
}
|
||||
|
||||
fun CharSequence.mapToASCII() =
|
||||
map { AnyAscii.transliterate("$it").first() }.joinToString("")
|
||||
map { AnyAscii.transliterate("$it").firstOrNull() ?: it }.joinToString("")
|
||||
|
||||
/**
|
||||
* Returns true if [this] contains [otherText] at the specified offset.
|
||||
@@ -127,18 +130,15 @@ fun Editor.offsetCenter(first: Int, second: Int): LogicalPosition {
|
||||
return offsetToLogicalPosition(getLineStartOffset(center))
|
||||
}
|
||||
|
||||
// Borrowed from Editor.calculateVisibleRange() but only available after 232.6095.10
|
||||
fun Editor.getView(): IntRange {
|
||||
val firstVisibleLine = max(0, getVisualLineAtTopOfScreen() - 1)
|
||||
val firstLine = visualLineToLogicalLine(firstVisibleLine)
|
||||
val startOffset = getLineStartOffset(firstLine)
|
||||
|
||||
val height = getScreenHeight() + 2
|
||||
val lastLine = visualLineToLogicalLine(firstVisibleLine + height)
|
||||
var endOffset = getLineEndOffset(lastLine, true)
|
||||
endOffset = normalizeOffset(lastLine, endOffset)
|
||||
endOffset = min(max(0, document.textLength - 1), endOffset + 1)
|
||||
|
||||
return startOffset..endOffset
|
||||
ApplicationManager.getApplication().assertIsDispatchThread()
|
||||
val rect = scrollingModel.visibleArea
|
||||
val startPosition = xyToLogicalPosition(Point(rect.x, rect.y))
|
||||
val visibleStart = logicalPositionToOffset(startPosition)
|
||||
val endPosition = xyToLogicalPosition(Point(rect.x + rect.width, rect.y + rect.height))
|
||||
val visibleEnd = logicalPositionToOffset(LogicalPosition(endPosition.line + 1, 0))
|
||||
return visibleStart..visibleEnd
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,3 +253,9 @@ fun Editor.normalizeOffset(line: Int, offset: Int, allowEnd: Boolean = true) =
|
||||
if (getFileSize(allowEnd) == 0) 0 else
|
||||
max(min(offset, getLineEndOffset(line, allowEnd)), getLineStartOffset(line))
|
||||
|
||||
// https://plugins.jetbrains.com/docs/intellij/general-threading-rules.html#read-access
|
||||
fun <T> read(action: () -> T): T =
|
||||
ApplicationManager.getApplication().runReadAction(Computable { action() })
|
||||
|
||||
fun <T> write(action: () -> T): T =
|
||||
ApplicationManager.getApplication().runWriteAction(Computable { action() })
|
||||
|
@@ -1,17 +1,30 @@
|
||||
package org.acejump.action
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys.LAST_ACTIVE_FILE_EDITOR
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.acejump.boundaries.Boundaries
|
||||
import org.acejump.boundaries.StandardBoundaries.*
|
||||
import org.acejump.boundaries.StandardBoundaries.AFTER_CARET
|
||||
import org.acejump.boundaries.StandardBoundaries.BEFORE_CARET
|
||||
import org.acejump.boundaries.StandardBoundaries.WHOLE_FILE
|
||||
import org.acejump.input.JumpMode
|
||||
import org.acejump.input.JumpMode.*
|
||||
import org.acejump.input.JumpMode.DECLARATION
|
||||
import org.acejump.input.JumpMode.JUMP
|
||||
import org.acejump.input.JumpMode.JUMP_END
|
||||
import org.acejump.input.JumpMode.TARGET
|
||||
import org.acejump.search.Pattern
|
||||
import org.acejump.search.Pattern.*
|
||||
import org.acejump.search.Pattern.ALL_WORDS
|
||||
import org.acejump.search.Pattern.LINE_ALL_MARKS
|
||||
import org.acejump.search.Pattern.LINE_ENDS
|
||||
import org.acejump.search.Pattern.LINE_INDENTS
|
||||
import org.acejump.search.Pattern.LINE_STARTS
|
||||
import org.acejump.session.Session
|
||||
import org.acejump.session.SessionManager
|
||||
|
||||
@@ -19,25 +32,28 @@ import org.acejump.session.SessionManager
|
||||
* Base class for keyboard-activated actions that create or update an AceJump [Session].
|
||||
*/
|
||||
sealed class AceAction: DumbAwareAction() {
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||
|
||||
final override fun update(action: AnActionEvent) {
|
||||
action.presentation.isEnabled = action.getData(EDITOR) != null
|
||||
action.presentation.isEnabled =
|
||||
(action.getData(EDITOR) ?: (action.getData(LAST_ACTIVE_FILE_EDITOR) as? TextEditor)?.editor) != null
|
||||
}
|
||||
|
||||
final override fun actionPerformed(e: AnActionEvent) {
|
||||
val editor = e.getData(EDITOR) ?: return
|
||||
val editor = e.getData(EDITOR) ?: (e.getData(LAST_ACTIVE_FILE_EDITOR) as? TextEditor)?.editor ?: return
|
||||
val project = e.project
|
||||
|
||||
if (project != null) {
|
||||
try {
|
||||
val openEditors = FileEditorManagerEx.getInstanceEx(project).splitters.selectedEditors
|
||||
val fem = FileEditorManager.getInstance(project) as FileEditorManagerEx
|
||||
val openEditors = fem.splitters.getSelectedEditors()
|
||||
.mapNotNull { (it as? TextEditor)?.editor }
|
||||
.sortedBy { if (it === editor) 0 else 1 }
|
||||
invoke(SessionManager.start(editor, openEditors))
|
||||
} catch (e: IncorrectOperationException) {
|
||||
invoke(SessionManager.start(editor))
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
invoke(SessionManager.start(editor))
|
||||
}
|
||||
}
|
||||
@@ -51,6 +67,13 @@ sealed class AceAction: DumbAwareAction() {
|
||||
final override fun invoke(session: Session) = session.toggleJumpMode(mode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic action type that toggles a specific [JumpMode] with [Boundaries].
|
||||
*/
|
||||
abstract class BaseToggleBoundedJumpModeAction(private val mode: JumpMode, private val boundaries: Boundaries): AceAction() {
|
||||
final override fun invoke(session: Session) = session.toggleJumpMode(mode, boundaries)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic action type that starts a regex search.
|
||||
*/
|
||||
@@ -74,12 +97,17 @@ sealed class AceAction: DumbAwareAction() {
|
||||
|
||||
// @formatter:off
|
||||
|
||||
// Unbounded Toggle Modes
|
||||
class ToggleJumpMode : BaseToggleJumpModeAction(JUMP)
|
||||
class ToggleJumpEndMode : BaseToggleJumpModeAction(JUMP_END)
|
||||
class ToggleTargetMode : BaseToggleJumpModeAction(TARGET)
|
||||
class ToggleDeclarationMode : BaseToggleJumpModeAction(DECLARATION)
|
||||
|
||||
// Bounded Toggle Modes
|
||||
class ToggleBackwardJumpMode : BaseToggleBoundedJumpModeAction(JUMP, BEFORE_CARET)
|
||||
class ToggleForwardJumpMode : BaseToggleBoundedJumpModeAction(JUMP, AFTER_CARET)
|
||||
|
||||
// Regex Modes
|
||||
class StartAllWordsMode : BaseRegexSearchAction(ALL_WORDS, WHOLE_FILE)
|
||||
class StartAllWordsBackwardsMode : BaseRegexSearchAction(ALL_WORDS, BEFORE_CARET)
|
||||
class StartAllWordsForwardMode : BaseRegexSearchAction(ALL_WORDS, AFTER_CARET)
|
||||
|
@@ -1,22 +1,31 @@
|
||||
package org.acejump.action
|
||||
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.actionSystem.IdeActions.*
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.IdeActions.ACTION_GOTO_DECLARATION
|
||||
import com.intellij.openapi.actionSystem.IdeActions.ACTION_GOTO_TYPE_DECLARATION
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.playback.commands.ActionCommand
|
||||
import org.acejump.*
|
||||
import org.acejump.countMatchingCharacters
|
||||
import org.acejump.immutableText
|
||||
import org.acejump.input.JumpMode
|
||||
import org.acejump.input.JumpMode.*
|
||||
import org.acejump.input.JumpMode.DECLARATION
|
||||
import org.acejump.input.JumpMode.JUMP_END
|
||||
import org.acejump.input.JumpMode.TARGET
|
||||
import org.acejump.isWordPart
|
||||
import org.acejump.search.SearchProcessor
|
||||
import org.acejump.search.Tag
|
||||
import org.acejump.wordEnd
|
||||
import org.acejump.wordStart
|
||||
|
||||
/**
|
||||
* Performs [JumpMode] navigation and actions.
|
||||
@@ -98,9 +107,9 @@ internal class TagJumper(private val mode: JumpMode, private val searchProcessor
|
||||
|
||||
private fun ensureEditorFocused(editor: Editor) {
|
||||
val project = editor.project ?: return
|
||||
val fem = FileEditorManagerEx.getInstanceEx(project)
|
||||
val fem = FileEditorManager.getInstance(project) as FileEditorManagerEx
|
||||
|
||||
val window = fem.windows.firstOrNull { (it.selectedEditor?.selectedWithProvider?.fileEditor as? TextEditor)?.editor === editor }
|
||||
val window = fem.windows.firstOrNull { (it.getSelectedComposite(false)?.selectedWithProvider?.fileEditor as? TextEditor)?.editor === editor }
|
||||
if (window != null && window !== fem.currentWindow) {
|
||||
fem.currentWindow = window
|
||||
}
|
||||
|
@@ -5,10 +5,10 @@ import org.acejump.*
|
||||
import org.acejump.search.SearchProcessor
|
||||
|
||||
internal class TagScroller(private val editor: Editor, private val searchProcessor: SearchProcessor) {
|
||||
fun scroll(forward: Boolean = true): Boolean {
|
||||
val position = if (forward) findNextPosition() else findPreviousPosition()
|
||||
return if (position != null) true.also { scrollTo(position) } else false
|
||||
}
|
||||
fun scroll(
|
||||
forward: Boolean = true,
|
||||
position: LogicalPosition? = if (forward) findNextPosition() else findPreviousPosition()
|
||||
) = if (position != null) true.also { scrollTo(position) } else false
|
||||
|
||||
private fun scrollTo(position: LogicalPosition) = editor.run {
|
||||
scrollingModel.disableAnimation()
|
||||
|
@@ -2,7 +2,6 @@ package org.acejump.action
|
||||
|
||||
import com.intellij.openapi.editor.*
|
||||
import com.intellij.openapi.editor.ScrollType.*
|
||||
import org.acejump.*
|
||||
import org.acejump.search.SearchProcessor
|
||||
import org.acejump.search.Tag
|
||||
import kotlin.math.abs
|
||||
|
@@ -16,12 +16,14 @@ interface Boundaries {
|
||||
* offsets outside the boundary, for ex. when the boundary is rectangular
|
||||
* and the file has long lines which are only partially visible.
|
||||
*/
|
||||
fun getOffsetRange(editor: Editor, cache: EditorOffsetCache = EditorOffsetCache.Uncached): IntRange
|
||||
fun getOffsetRange(editor: Editor, cache: EditorOffsetCache = EditorOffsetCache.Uncached): IntRange =
|
||||
StandardBoundaries.VISIBLE_ON_SCREEN.getOffsetRange(editor, cache)
|
||||
|
||||
/**
|
||||
* Returns whether the editor offset is included within the boundary.
|
||||
*/
|
||||
fun isOffsetInside(editor: Editor, offset: Int, cache: EditorOffsetCache = EditorOffsetCache.Uncached): Boolean
|
||||
fun isOffsetInside(editor: Editor, offset: Int, cache: EditorOffsetCache = EditorOffsetCache.Uncached): Boolean =
|
||||
StandardBoundaries.VISIBLE_ON_SCREEN.isOffsetInside(editor, offset, cache)
|
||||
|
||||
/**
|
||||
* Creates a boundary so that an offset/range is within the boundary
|
||||
|
@@ -3,6 +3,7 @@ package org.acejump.boundaries
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import org.acejump.read
|
||||
import java.awt.Point
|
||||
|
||||
/**
|
||||
@@ -20,6 +21,11 @@ sealed class EditorOffsetCache {
|
||||
*/
|
||||
abstract fun visibleArea(editor: Editor): Pair<Point, Point>
|
||||
|
||||
/**
|
||||
* Returns whether the offset is in the visible area rectangle.
|
||||
*/
|
||||
abstract fun isVisible(editor: Editor, offset: Int): Boolean
|
||||
|
||||
/**
|
||||
* Returns the editor offset at the provided pixel coordinate.
|
||||
*/
|
||||
@@ -30,32 +36,46 @@ sealed class EditorOffsetCache {
|
||||
*/
|
||||
abstract fun offsetToXY(editor: Editor, offset: Int): Point
|
||||
|
||||
companion object {
|
||||
fun new(): EditorOffsetCache = Cache()
|
||||
}
|
||||
companion object { fun new(): EditorOffsetCache = Cache() }
|
||||
|
||||
private class Cache: EditorOffsetCache() {
|
||||
private var visibleArea: Pair<Point, Point>? = null
|
||||
private val pointToOffset = Object2IntOpenHashMap<Point>().apply { defaultReturnValue(-1) }
|
||||
private val lineToVisibleOffsetRange = Int2ObjectOpenHashMap<IntRange>()
|
||||
private val pointToOffset =
|
||||
Object2IntOpenHashMap<Point>().apply { defaultReturnValue(-1) }
|
||||
private val offsetToPoint = Int2ObjectOpenHashMap<Point>()
|
||||
|
||||
override fun visibleArea(editor: Editor): Pair<Point, Point> =
|
||||
visibleArea ?: Uncached.visibleArea(editor).also { visibleArea = it }
|
||||
|
||||
override fun isVisible(editor: Editor, offset: Int): Boolean {
|
||||
val visualLine = editor.offsetToVisualLine(offset, false)
|
||||
|
||||
var visibleRange = lineToVisibleOffsetRange.get(visualLine)
|
||||
if (visibleRange == null) {
|
||||
val (topLeft, bottomRight) = visibleArea(editor)
|
||||
val lineY = editor.visualLineToY(visualLine)
|
||||
|
||||
val firstVisibleOffset = xyToOffset(editor, Point(topLeft.x, lineY))
|
||||
val lastVisibleOffset = xyToOffset(editor, Point(bottomRight.x, lineY))
|
||||
|
||||
visibleRange = firstVisibleOffset..lastVisibleOffset
|
||||
lineToVisibleOffsetRange.put(visualLine, visibleRange)
|
||||
}
|
||||
|
||||
return offset in visibleRange
|
||||
}
|
||||
|
||||
override fun xyToOffset(editor: Editor, pos: Point): Int =
|
||||
pointToOffset.getInt(pos).let { offset ->
|
||||
if (offset != -1) offset
|
||||
else Uncached.xyToOffset(editor, pos).also {
|
||||
@Suppress("ReplacePutWithAssignment")
|
||||
pointToOffset.put(pos, it)
|
||||
}
|
||||
else Uncached.xyToOffset(editor, pos)
|
||||
.also { pointToOffset.put(pos, it) }
|
||||
}
|
||||
|
||||
override fun offsetToXY(editor: Editor, offset: Int) =
|
||||
offsetToPoint.get(offset) ?: Uncached.offsetToXY(editor, offset).also {
|
||||
@Suppress("ReplacePutWithAssignment")
|
||||
offsetToPoint.put(offset, it)
|
||||
}
|
||||
offsetToPoint.get(offset) ?: Uncached.offsetToXY(editor, offset)
|
||||
.also { offsetToPoint.put(offset, it) }
|
||||
}
|
||||
|
||||
object Uncached: EditorOffsetCache() {
|
||||
@@ -68,8 +88,17 @@ sealed class EditorOffsetCache {
|
||||
)
|
||||
}
|
||||
|
||||
override fun isVisible(editor: Editor, offset: Int): Boolean {
|
||||
val (topLeft, bottomRight) = visibleArea(editor)
|
||||
val pos = offsetToXY(editor, offset)
|
||||
val x = pos.x
|
||||
val y = pos.y
|
||||
|
||||
return x >= topLeft.x && y >= topLeft.y && x <= bottomRight.x && y <= bottomRight.y
|
||||
}
|
||||
|
||||
override fun xyToOffset(editor: Editor, pos: Point): Int =
|
||||
editor.logicalPositionToOffset(editor.xyToLogicalPosition(pos))
|
||||
read { editor.logicalPositionToOffset(editor.xyToLogicalPosition(pos)) }
|
||||
|
||||
override fun offsetToXY(editor: Editor, offset: Int): Point =
|
||||
editor.offsetToXY(offset, true, false)
|
||||
|
@@ -5,10 +5,10 @@ import com.intellij.openapi.editor.Editor
|
||||
enum class StandardBoundaries : Boundaries {
|
||||
WHOLE_FILE {
|
||||
override fun getOffsetRange(editor: Editor, cache: EditorOffsetCache) =
|
||||
0 until editor.document.textLength
|
||||
0..editor.document.textLength
|
||||
|
||||
override fun isOffsetInside(editor: Editor, offset: Int, cache: EditorOffsetCache) =
|
||||
offset in (0 until editor.document.textLength)
|
||||
offset in (0..editor.document.textLength)
|
||||
},
|
||||
|
||||
VISIBLE_ON_SCREEN {
|
||||
@@ -21,22 +21,7 @@ enum class StandardBoundaries : Boundaries {
|
||||
}
|
||||
|
||||
override fun isOffsetInside(editor: Editor, offset: Int, cache: EditorOffsetCache): Boolean {
|
||||
// If we are not using a cache, calling getOffsetRange will cause
|
||||
// additional 1-2 pixel coordinate -> offset lookups, which is a lot
|
||||
// more expensive than one lookup compared against the visible area.
|
||||
|
||||
// However, if we are using a cache, it's likely that the topmost and
|
||||
// bottommost positions are already cached whereas the provided offset
|
||||
// isn't, so we save a lookup for every offset outside the range.
|
||||
|
||||
if (cache !== EditorOffsetCache.Uncached && offset !in getOffsetRange(editor, cache)) return false
|
||||
|
||||
val (topLeft, bottomRight) = cache.visibleArea(editor)
|
||||
val pos = cache.offsetToXY(editor, offset)
|
||||
val x = pos.x
|
||||
val y = pos.y
|
||||
|
||||
return x >= topLeft.x && y >= topLeft.y && x <= bottomRight.x && y <= bottomRight.y
|
||||
return cache.isVisible(editor, offset)
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package org.acejump.config
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.util.application
|
||||
import org.acejump.input.KeyLayoutCache
|
||||
|
||||
/**
|
||||
@@ -16,19 +16,19 @@ class AceConfig: PersistentStateComponent<AceSettings> {
|
||||
private var aceSettings = AceSettings()
|
||||
|
||||
companion object {
|
||||
val settings get() = ServiceManager.getService(AceConfig::class.java).aceSettings
|
||||
val settings get() = application.getService(AceConfig::class.java).aceSettings
|
||||
|
||||
// @formatter:off
|
||||
val layout get() = settings.layout
|
||||
val cycleModes get() = settings.let { arrayOf(it.cycleMode1, it.cycleMode2, it.cycleMode3, it.cycleMode4) }
|
||||
val minQueryLength get() = settings.minQueryLength
|
||||
val jumpModeColor get() = settings.jumpModeColor
|
||||
val jumpEndModeColor get() = settings.jumpEndModeColor
|
||||
val targetModeColor get() = settings.targetModeColor
|
||||
val definitionModeColor get() = settings.definitionModeColor
|
||||
val textHighlightColor get() = settings.textHighlightColor
|
||||
val tagForegroundColor get() = settings.tagForegroundColor
|
||||
val tagBackgroundColor get() = settings.tagBackgroundColor
|
||||
val jumpModeColor get() = settings.getJumpModeJBC()
|
||||
val jumpEndModeColor get() = settings.getJumpEndModeJBC()
|
||||
val targetModeColor get() = settings.getTargetModeJBC()
|
||||
val definitionModeColor get() = settings.getDefinitionModeJBC()
|
||||
val textHighlightColor get() = settings.getTextHighlightJBC()
|
||||
val tagForegroundColor get() = settings.getTagForegroundJBC()
|
||||
val tagBackgroundColor get() = settings.getTagBackgroundJBC()
|
||||
val searchWholeFile get() = settings.searchWholeFile
|
||||
val mapToASCII get() = settings.mapToASCII
|
||||
val showSearchNotification get() = settings.showSearchNotification
|
||||
|
@@ -19,13 +19,13 @@ class AceConfigurable: Configurable {
|
||||
panel.cycleMode3 != settings.cycleMode3 ||
|
||||
panel.cycleMode4 != settings.cycleMode4 ||
|
||||
panel.minQueryLengthInt != settings.minQueryLength ||
|
||||
panel.jumpModeColor != settings.jumpModeColor ||
|
||||
panel.jumpEndModeColor != settings.jumpEndModeColor ||
|
||||
panel.targetModeColor != settings.targetModeColor ||
|
||||
panel.definitionModeColor != settings.definitionModeColor ||
|
||||
panel.textHighlightColor != settings.textHighlightColor ||
|
||||
panel.tagForegroundColor != settings.tagForegroundColor ||
|
||||
panel.tagBackgroundColor != settings.tagBackgroundColor ||
|
||||
panel.jumpModeColor?.rgb != settings.jumpModeColor ||
|
||||
panel.jumpEndModeColor?.rgb != settings.jumpEndModeColor ||
|
||||
panel.targetModeColor?.rgb != settings.targetModeColor ||
|
||||
panel.definitionModeColor?.rgb != settings.definitionModeColor ||
|
||||
panel.textHighlightColor?.rgb != settings.textHighlightColor ||
|
||||
panel.tagForegroundColor?.rgb != settings.tagForegroundColor ||
|
||||
panel.tagBackgroundColor?.rgb != settings.tagBackgroundColor ||
|
||||
panel.searchWholeFile != settings.searchWholeFile ||
|
||||
panel.mapToASCII != settings.mapToASCII ||
|
||||
panel.showSearchNotification != settings.showSearchNotification
|
||||
@@ -38,13 +38,13 @@ class AceConfigurable: Configurable {
|
||||
settings.cycleMode3 = panel.cycleMode3
|
||||
settings.cycleMode4 = panel.cycleMode4
|
||||
settings.minQueryLength = panel.minQueryLengthInt ?: settings.minQueryLength
|
||||
panel.jumpModeColor?.let { settings.jumpModeColor = it }
|
||||
panel.jumpEndModeColor?.let { settings.jumpEndModeColor = it }
|
||||
panel.targetModeColor?.let { settings.targetModeColor = it }
|
||||
panel.definitionModeColor?.let { settings.definitionModeColor = it }
|
||||
panel.textHighlightColor?.let { settings.textHighlightColor = it }
|
||||
panel.tagForegroundColor?.let { settings.tagForegroundColor = it }
|
||||
panel.tagBackgroundColor?.let { settings.tagBackgroundColor = it }
|
||||
panel.jumpModeColor?.let { settings.jumpModeColor = it.rgb }
|
||||
panel.jumpEndModeColor?.let { settings.jumpEndModeColor = it.rgb }
|
||||
panel.targetModeColor?.let { settings.targetModeColor = it.rgb }
|
||||
panel.definitionModeColor?.let { settings.definitionModeColor = it.rgb }
|
||||
panel.textHighlightColor?.let { settings.textHighlightColor = it.rgb }
|
||||
panel.tagForegroundColor?.let { settings.tagForegroundColor = it.rgb }
|
||||
panel.tagBackgroundColor?.let { settings.tagBackgroundColor = it.rgb }
|
||||
settings.searchWholeFile = panel.searchWholeFile
|
||||
settings.mapToASCII = panel.mapToASCII
|
||||
settings.showSearchNotification = panel.showSearchNotification
|
||||
|
@@ -1,11 +1,8 @@
|
||||
package org.acejump.config
|
||||
|
||||
import com.intellij.util.xmlb.Converter
|
||||
import com.intellij.util.xmlb.annotations.OptionTag
|
||||
import org.acejump.input.JumpMode
|
||||
import org.acejump.input.KeyLayout
|
||||
import com.intellij.ui.JBColor
|
||||
import org.acejump.input.*
|
||||
import org.acejump.input.KeyLayout.QWERTY
|
||||
import java.awt.Color
|
||||
|
||||
data class AceSettings(
|
||||
var layout: KeyLayout = QWERTY,
|
||||
@@ -16,35 +13,31 @@ data class AceSettings(
|
||||
var cycleMode4: JumpMode = JumpMode.JUMP_END,
|
||||
var minQueryLength: Int = 1,
|
||||
|
||||
@OptionTag("jumpModeRGB", converter = ColorConverter::class)
|
||||
var jumpModeColor: Color = Color(0xFFFFFF),
|
||||
var jumpModeColor: Int = 0xFFFFFF,
|
||||
|
||||
@OptionTag("jumpEndModeRGB", converter = ColorConverter::class)
|
||||
var jumpEndModeColor: Color = Color(0x33E78A),
|
||||
var jumpEndModeColor: Int = 0x33E78A,
|
||||
|
||||
@OptionTag("targetModeRGB", converter = ColorConverter::class)
|
||||
var targetModeColor: Color = Color(0xFFB700),
|
||||
var targetModeColor: Int = 0xFFB700,
|
||||
|
||||
@OptionTag("definitionModeRGB", converter = ColorConverter::class)
|
||||
var definitionModeColor: Color = Color(0x6FC5FF),
|
||||
var definitionModeColor: Int = 0x6FC5FF,
|
||||
|
||||
@OptionTag("textHighlightRGB", converter = ColorConverter::class)
|
||||
var textHighlightColor: Color = Color(0x394B58),
|
||||
var textHighlightColor: Int = 0x394B58,
|
||||
|
||||
@OptionTag("tagForegroundRGB", converter = ColorConverter::class)
|
||||
var tagForegroundColor: Color = Color(0xFFFFFF),
|
||||
var tagForegroundColor: Int = 0xFFFFFF,
|
||||
|
||||
@OptionTag("tagBackgroundRGB", converter = ColorConverter::class)
|
||||
var tagBackgroundColor: Color = Color(0x008299),
|
||||
var tagBackgroundColor: Int = 0x008299,
|
||||
|
||||
var searchWholeFile: Boolean = true,
|
||||
|
||||
var mapToASCII: Boolean = false,
|
||||
|
||||
var showSearchNotification: Boolean = false
|
||||
)
|
||||
|
||||
internal class ColorConverter: Converter<Color>() {
|
||||
override fun toString(value: Color) = value.rgb.toString()
|
||||
override fun fromString(value: String) = value.toIntOrNull()?.let(::Color)
|
||||
) {
|
||||
fun getJumpModeJBC() = JBColor.namedColor("jumpModeRGB", jumpModeColor)
|
||||
fun getJumpEndModeJBC() = JBColor.namedColor("jumpEndModeRGB", jumpEndModeColor)
|
||||
fun getTargetModeJBC() = JBColor.namedColor("targetModeRGB", targetModeColor)
|
||||
fun getDefinitionModeJBC() = JBColor.namedColor("definitionModeRGB", definitionModeColor)
|
||||
fun getTextHighlightJBC() = JBColor.namedColor("textHighlightRGB", textHighlightColor)
|
||||
fun getTagForegroundJBC() = JBColor.namedColor("tagForegroundRGB", tagForegroundColor)
|
||||
fun getTagBackgroundJBC() = JBColor.namedColor("tagBackgroundRGB", tagBackgroundColor)
|
||||
}
|
||||
|
@@ -1,21 +1,12 @@
|
||||
package org.acejump.config
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.ui.ColorPanel
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import com.intellij.ui.components.JBTextArea
|
||||
import com.intellij.ui.components.JBTextField
|
||||
import com.intellij.ui.layout.Cell
|
||||
import com.intellij.ui.layout.GrowPolicy.MEDIUM_TEXT
|
||||
import com.intellij.ui.layout.GrowPolicy.SHORT_TEXT
|
||||
import com.intellij.ui.layout.panel
|
||||
import com.intellij.ui.components.*
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import org.acejump.input.JumpMode
|
||||
import org.acejump.input.KeyLayout
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import javax.swing.JCheckBox
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
import java.awt.*
|
||||
import javax.swing.*
|
||||
import javax.swing.text.JTextComponent
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@@ -24,6 +15,8 @@ import kotlin.reflect.KProperty
|
||||
*/
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
internal class AceSettingsPanel {
|
||||
private val defaults = AceSettings()
|
||||
|
||||
private val tagCharsField = JBTextField()
|
||||
private val keyboardLayoutCombo = ComboBox<KeyLayout>()
|
||||
private val keyboardLayoutArea = JBTextArea().apply { isEditable = false }
|
||||
@@ -32,13 +25,13 @@ internal class AceSettingsPanel {
|
||||
private val cycleModeCombo3 = ComboBox<JumpMode>()
|
||||
private val cycleModeCombo4 = ComboBox<JumpMode>()
|
||||
private val minQueryLengthField = JBTextField()
|
||||
private val jumpModeColorWheel = ColorPanel()
|
||||
private val jumpEndModeColorWheel = ColorPanel()
|
||||
private val targetModeColorWheel = ColorPanel()
|
||||
private val definitionModeColorWheel = ColorPanel()
|
||||
private val textHighlightColorWheel = ColorPanel()
|
||||
private val tagForegroundColorWheel = ColorPanel()
|
||||
private val tagBackgroundColorWheel = ColorPanel()
|
||||
private val jumpModeColorWheel = ResettableColorPicker(defaults.getJumpModeJBC())
|
||||
private val jumpEndModeColorWheel = ResettableColorPicker(defaults.getJumpEndModeJBC())
|
||||
private val targetModeColorWheel = ResettableColorPicker(defaults.getTargetModeJBC())
|
||||
private val definitionModeColorWheel = ResettableColorPicker(defaults.getDefinitionModeJBC())
|
||||
private val textHighlightColorWheel = ResettableColorPicker(defaults.getTextHighlightJBC())
|
||||
private val tagForegroundColorWheel = ResettableColorPicker(defaults.getTagForegroundJBC())
|
||||
private val tagBackgroundColorWheel = ResettableColorPicker(defaults.getTagBackgroundJBC())
|
||||
private val searchWholeFileCheckBox = JBCheckBox()
|
||||
private val mapToASCIICheckBox = JBCheckBox()
|
||||
private val showSearchNotificationCheckBox = JBCheckBox()
|
||||
@@ -54,45 +47,40 @@ internal class AceSettingsPanel {
|
||||
}
|
||||
|
||||
internal val rootPanel: JPanel = panel {
|
||||
fun Cell.short(component: JComponent) = component(growPolicy = SHORT_TEXT)
|
||||
fun Cell.medium(component: JComponent) = component(growPolicy = MEDIUM_TEXT)
|
||||
|
||||
titledRow("Characters and Layout") {
|
||||
row("Allowed characters in tags:") { medium(tagCharsField) }
|
||||
row("Keyboard layout:") { short(keyboardLayoutCombo) }
|
||||
row("Keyboard design:") { short(keyboardLayoutArea) }
|
||||
group("Characters and Layout") {
|
||||
row("Allowed characters in tags:") { cell(tagCharsField).columns(COLUMNS_LARGE) }
|
||||
row("Keyboard layout:") { cell(keyboardLayoutCombo).columns(COLUMNS_SHORT) }
|
||||
row("Keyboard design:") { cell(keyboardLayoutArea).columns(COLUMNS_SHORT) }
|
||||
}
|
||||
|
||||
titledRow("Modes") {
|
||||
row("Cycle order:") { cell { cycleModeCombo1() } }
|
||||
row("") {
|
||||
cell(isVerticalFlow = true) {
|
||||
cycleModeCombo2()
|
||||
cycleModeCombo3()
|
||||
cycleModeCombo4()
|
||||
}
|
||||
group("Modes") {
|
||||
row("Cycle order:") {
|
||||
cell(cycleModeCombo1).columns(10)
|
||||
cell(cycleModeCombo2).columns(10)
|
||||
cell(cycleModeCombo3).columns(10)
|
||||
cell(cycleModeCombo4).columns(10)
|
||||
}
|
||||
}
|
||||
|
||||
titledRow("Colors") {
|
||||
row("Jump mode caret background:") { short(jumpModeColorWheel) }
|
||||
row("Jump to End mode caret background:") { short(jumpEndModeColorWheel) }
|
||||
row("Target mode caret background:") { short(targetModeColorWheel) }
|
||||
row("Definition mode caret background:") { short(definitionModeColorWheel) }
|
||||
row("Searched text background:") { short(textHighlightColorWheel) }
|
||||
row("Tag foreground:") { short(tagForegroundColorWheel) }
|
||||
row("Tag background:") { short(tagBackgroundColorWheel) }
|
||||
group("Colors") {
|
||||
row("Jump mode caret background:") { cell(jumpModeColorWheel) }
|
||||
row("Jump to End mode caret background:") { cell(jumpEndModeColorWheel) }
|
||||
row("Target mode caret background:") { cell(targetModeColorWheel) }
|
||||
row("Definition mode caret background:") { cell(definitionModeColorWheel) }
|
||||
row("Searched text background:") { cell(textHighlightColorWheel) }
|
||||
row("Tag foreground:") { cell(tagForegroundColorWheel) }
|
||||
row("Tag background:") { cell(tagBackgroundColorWheel) }
|
||||
}
|
||||
|
||||
titledRow("Behavior") {
|
||||
row { short(searchWholeFileCheckBox.apply { text = "Search whole file" }) }
|
||||
row("Minimum typed characters (1-10):") { short(minQueryLengthField) }
|
||||
group("Behavior") {
|
||||
row { cell(searchWholeFileCheckBox.apply { text = "Search whole file" }) }
|
||||
row("Minimum typed characters (1-10):") { cell(minQueryLengthField) }
|
||||
}
|
||||
titledRow("Language Settings") {
|
||||
row { short(mapToASCIICheckBox.apply { text = "Map unicode to ASCII" }) }
|
||||
group("Language Settings") {
|
||||
row { cell(mapToASCIICheckBox.apply { text = "Map unicode to ASCII" }) }
|
||||
}
|
||||
titledRow("Visual") {
|
||||
row { short(showSearchNotificationCheckBox.apply { text = "Show hint with search text" }) }
|
||||
group("Visual") {
|
||||
row { cell(showSearchNotificationCheckBox.apply { text = "Show hint with search text" }) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +118,13 @@ internal class AceSettingsPanel {
|
||||
cycleMode3 = settings.cycleMode3
|
||||
cycleMode4 = settings.cycleMode4
|
||||
minQueryLength = settings.minQueryLength.toString()
|
||||
jumpModeColor = settings.jumpModeColor
|
||||
jumpEndModeColor = settings.jumpEndModeColor
|
||||
targetModeColor = settings.targetModeColor
|
||||
definitionModeColor = settings.definitionModeColor
|
||||
textHighlightColor = settings.textHighlightColor
|
||||
tagForegroundColor = settings.tagForegroundColor
|
||||
tagBackgroundColor = settings.tagBackgroundColor
|
||||
jumpModeColor = settings.getJumpModeJBC()
|
||||
jumpEndModeColor = settings.getJumpEndModeJBC()
|
||||
targetModeColor = settings.getTargetModeJBC()
|
||||
definitionModeColor = settings.getDefinitionModeJBC()
|
||||
textHighlightColor = settings.getTextHighlightJBC()
|
||||
tagForegroundColor = settings.getTagForegroundJBC()
|
||||
tagBackgroundColor = settings.getTagBackgroundJBC()
|
||||
searchWholeFile = settings.searchWholeFile
|
||||
mapToASCII = settings.mapToASCII
|
||||
showSearchNotification = settings.showSearchNotification
|
||||
@@ -147,8 +135,8 @@ internal class AceSettingsPanel {
|
||||
private operator fun JTextComponent.getValue(a: AceSettingsPanel, p: KProperty<*>) = text.lowercase()
|
||||
private operator fun JTextComponent.setValue(a: AceSettingsPanel, p: KProperty<*>, s: String) = setText(s)
|
||||
|
||||
private operator fun ColorPanel.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedColor
|
||||
private operator fun ColorPanel.setValue(a: AceSettingsPanel, p: KProperty<*>, c: Color?) = setSelectedColor(c)
|
||||
private operator fun ResettableColorPicker.getValue(a: AceSettingsPanel, p: KProperty<*>) = getSelectedColor()
|
||||
private operator fun ResettableColorPicker.setValue(a: AceSettingsPanel, p: KProperty<*>, c: Color?) = setSelectedColor(c)
|
||||
|
||||
private operator fun JCheckBox.getValue(a: AceSettingsPanel, p: KProperty<*>) = isSelected
|
||||
private operator fun JCheckBox.setValue(a: AceSettingsPanel, p: KProperty<*>, selected: Boolean) = setSelected(selected)
|
||||
|
47
src/main/kotlin/org/acejump/config/ResettableColorPicker.kt
Normal file
47
src/main/kotlin/org/acejump/config/ResettableColorPicker.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package org.acejump.config
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton
|
||||
import com.intellij.ui.ColorPanel
|
||||
import com.intellij.ui.JBColor
|
||||
import java.awt.*
|
||||
import javax.swing.*
|
||||
|
||||
internal class ResettableColorPicker(private val defaultColor: JBColor) : JPanel(FlowLayout()) {
|
||||
private val resetAction = object : AnAction({ "Reset to Default" }, AllIcons.General.Reset) {
|
||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
||||
return ActionUpdateThread.EDT
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.isEnabled = colorPanel.selectedColor != defaultColor
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
setSelectedColor(defaultColor)
|
||||
}
|
||||
}
|
||||
|
||||
private val colorPanel = ColorPanel()
|
||||
private val resetButton = ActionButton(resetAction, null, ActionPlaces.UNKNOWN, ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE)
|
||||
|
||||
init {
|
||||
add(colorPanel)
|
||||
add(resetButton)
|
||||
setSelectedColor(defaultColor)
|
||||
|
||||
colorPanel.addActionListener {
|
||||
resetButton.update()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedColor(): Color? {
|
||||
return colorPanel.selectedColor
|
||||
}
|
||||
|
||||
fun setSelectedColor(color: Color?) {
|
||||
colorPanel.selectedColor = color
|
||||
resetButton.update()
|
||||
}
|
||||
}
|
@@ -61,13 +61,12 @@ enum class JumpMode {
|
||||
*/
|
||||
DECLARATION;
|
||||
|
||||
val caretColor: Color
|
||||
get() = when (this) {
|
||||
val caretColor: Color get() = when (this) {
|
||||
DISABLED -> AbstractColorsScheme.INHERITED_COLOR_MARKER
|
||||
JUMP -> AceConfig.jumpModeColor
|
||||
JUMP_END -> AceConfig.jumpEndModeColor
|
||||
DECLARATION -> AceConfig.definitionModeColor
|
||||
TARGET -> AceConfig.targetModeColor
|
||||
DISABLED -> AbstractColorsScheme.INHERITED_COLOR_MARKER
|
||||
DECLARATION -> AceConfig.definitionModeColor
|
||||
}
|
||||
|
||||
override fun toString() = when (this) {
|
||||
|
@@ -19,7 +19,10 @@ enum class KeyLayout(internal val rows: Array<String>, priority: String) {
|
||||
QWERTZ(arrayOf("1234567890", "qwertzuiop", "asdfghjkl", "yxcvbnm"), priority = "fjghdkslavncmbxyrutzeiwoqp5849673210"),
|
||||
QGMLWY(arrayOf("1234567890", "qgmlwyfub", "dstnriaeoh", "zxcvjkp"), priority = "naterisodhvkcpjxzlfmuwygbq5849673210"),
|
||||
QGMLWB(arrayOf("1234567890", "qgmlwbyuv", "dstnriaeoh", "zxcfjkp"), priority = "naterisodhfkcpjxzlymuwbgvq5849673210"),
|
||||
NORMAN(arrayOf("1234567890", "qwdfkjurl", "asetgynioh", "zxcvbpm"), priority = "tneigysoahbvpcmxzjkufrdlwq5849673210");
|
||||
NORMAN(arrayOf("1234567890", "qwdfkjurl", "asetgynioh", "zxcvbpm"), priority = "tneigysoahbvpcmxzjkufrdlwq5849673210"),
|
||||
AZERTY(arrayOf("1234567890", "azertyuiop", "qsdfghjklm", "wxcvbn"), priority = "fjghdkslqvncmbxwrutyeizoap5849673210"),
|
||||
CANARY(arrayOf("1234567890", "wlypbzfou", "crstgmneia", "qjvdkxh"), priority = "tngmseracidxvhkjqpfbzyoluw5849673210"),
|
||||
ENGRAM(arrayOf("1234567890", "byouldwvz", "cieahtsnq", "gxjkrmfp"), priority = "ahetiscnkrjmodulywxfgpbvqz3847295610");
|
||||
|
||||
internal val allChars = rows.joinToString("").toCharArray().apply(CharArray::sort).joinToString("")
|
||||
internal val allPriorities = priority.mapIndexed { index, char -> char to index }.toMap()
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package org.acejump.search
|
||||
|
||||
enum class Pattern(val regex: String) {
|
||||
LINE_STARTS("^.|^\\n"),
|
||||
LINE_STARTS("^.|^\\n|(?<!.)\\Z"),
|
||||
LINE_ENDS("\\n|\\Z"),
|
||||
LINE_INDENTS("[^\\s].*|^\\n"),
|
||||
LINE_ALL_MARKS(LINE_ENDS.regex + "|" + LINE_STARTS.regex + "|" + LINE_INDENTS.regex),
|
||||
LINE_INDENTS("[^\\s].*|^\\n|(?<!.)\\Z"),
|
||||
LINE_ALL_MARKS(listOf(LINE_ENDS, LINE_STARTS, LINE_INDENTS).flatMap { it.regex.split("|") }.distinct().joinToString("|")),
|
||||
ALL_WORDS("(?<=[^a-zA-Z0-9_]|\\A)[a-zA-Z0-9_]");
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package org.acejump.search
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import org.acejump.boundaries.Boundaries
|
||||
import org.acejump.boundaries.EditorOffsetCache
|
||||
import org.acejump.immutableText
|
||||
import org.acejump.isWordPart
|
||||
import org.acejump.matchesAt
|
||||
@@ -12,7 +13,6 @@ import org.acejump.matchesAt
|
||||
* previous results when the user [type]s a character.
|
||||
*/
|
||||
internal class SearchProcessor private constructor(
|
||||
private val editors: List<Editor>,
|
||||
query: SearchQuery,
|
||||
results: MutableMap<Editor, IntArrayList>
|
||||
) {
|
||||
@@ -24,14 +24,15 @@ internal class SearchProcessor private constructor(
|
||||
SearchProcessor(editors, SearchQuery.RegularExpression(pattern), boundaries)
|
||||
}
|
||||
|
||||
private constructor(editors: List<Editor>, query: SearchQuery, boundaries: Boundaries) : this(editors, query, mutableMapOf()) {
|
||||
private constructor(editors: List<Editor>, query: SearchQuery, boundaries: Boundaries) : this(query, mutableMapOf()) {
|
||||
val regex = query.toRegex()
|
||||
|
||||
if (regex != null) {
|
||||
for (editor in editors) {
|
||||
val cache = EditorOffsetCache.new()
|
||||
val offsets = IntArrayList()
|
||||
|
||||
val offsetRange = boundaries.getOffsetRange(editor)
|
||||
val offsetRange = boundaries.getOffsetRange(editor, cache)
|
||||
var result = regex.find(editor.immutableText, offsetRange.first)
|
||||
|
||||
while (result != null) {
|
||||
@@ -43,7 +44,7 @@ internal class SearchProcessor private constructor(
|
||||
if (highlightEnd > offsetRange.last) {
|
||||
break
|
||||
}
|
||||
else if (boundaries.isOffsetInside(editor, index)) {
|
||||
else if (boundaries.isOffsetInside(editor, index, cache)) {
|
||||
offsets.add(index)
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ internal class SearchProcessor private constructor(
|
||||
*/
|
||||
fun type(char: Char, tagger: Tagger): Boolean {
|
||||
val newQuery = query.rawText + char
|
||||
val canMatchTag = tagger.canQueryMatchAnyTag(newQuery)
|
||||
val canMatchTag = tagger.canQueryMatchAnyVisibleTag(newQuery)
|
||||
|
||||
// If the typed character is not compatible with any existing tag or as
|
||||
// a continuation of any previous occurrence, reject the query change
|
||||
|
@@ -51,7 +51,7 @@ internal sealed class SearchQuery {
|
||||
class RegularExpression(private var pattern: String): SearchQuery() {
|
||||
override val rawText = ""
|
||||
|
||||
override fun getHighlightLength(text: CharSequence, offset: Int) = 1
|
||||
override fun getHighlightLength(text: CharSequence, offset: Int) = 0
|
||||
|
||||
override fun toRegex(): Regex =
|
||||
Regex(pattern, setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
|
||||
|
@@ -10,7 +10,7 @@ import org.acejump.immutableText
|
||||
import org.acejump.input.KeyLayoutCache
|
||||
import org.acejump.isWordPart
|
||||
import org.acejump.wordEndPlus
|
||||
import java.util.*
|
||||
import java.util.IdentityHashMap
|
||||
import kotlin.math.max
|
||||
|
||||
/*
|
||||
@@ -89,6 +89,10 @@ internal class Solver private constructor(
|
||||
while (iter.hasNext()) {
|
||||
val site = iter.nextInt()
|
||||
|
||||
if (editor.foldingModel.isOffsetCollapsed(site)) {
|
||||
continue
|
||||
}
|
||||
|
||||
for ((firstLetter, tags) in tagsByFirstLetter.entries) {
|
||||
if (canTagBeginWithChar(editor, site, firstLetter)) {
|
||||
for (tag in tags) {
|
||||
@@ -187,6 +191,9 @@ internal class Solver private constructor(
|
||||
val chars = editor.immutableText
|
||||
val left = max(0, site + queryLength - 1)
|
||||
val right = chars.wordEndPlus(site)
|
||||
if (right >= chars.length) {
|
||||
return
|
||||
}
|
||||
|
||||
val builder = StringBuilder(1 + right - left)
|
||||
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package org.acejump.search
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.acejump.getView
|
||||
|
||||
data class Tag(val editor: Editor, val offset: Int)
|
||||
data class Tag(val editor: Editor, val offset: Int) {
|
||||
fun isVisible() = offset in editor.getView() && !editor.foldingModel.isOffsetCollapsed(offset)
|
||||
}
|
||||
|
@@ -5,13 +5,10 @@ import com.google.common.collect.HashBiMap
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import org.acejump.ExternalUsage
|
||||
import org.acejump.*
|
||||
import org.acejump.boundaries.EditorOffsetCache
|
||||
import org.acejump.boundaries.StandardBoundaries.VISIBLE_ON_SCREEN
|
||||
import org.acejump.immutableText
|
||||
import org.acejump.input.KeyLayoutCache.allPossibleTags
|
||||
import org.acejump.isWordPart
|
||||
import org.acejump.matchesAt
|
||||
import org.acejump.view.TagMarker
|
||||
import java.util.AbstractMap.SimpleImmutableEntry
|
||||
import kotlin.collections.component1
|
||||
@@ -143,23 +140,22 @@ internal class Tagger(private val editors: List<Editor>) {
|
||||
}
|
||||
}
|
||||
|
||||
private infix fun Map.Entry<String, Tag>.solves(query: String): Boolean {
|
||||
return query.endsWith(key, true) && isTagCompatibleWithQuery(key, value, query)
|
||||
}
|
||||
private infix fun Map.Entry<String, Tag>.solves(query: String): Boolean =
|
||||
query.endsWith(key, true) && isTagCompatibleWithQuery(key, value, query)
|
||||
|
||||
private fun isTagCompatibleWithQuery(marker: String, tag: Tag, query: String): Boolean {
|
||||
return tag.editor.immutableText.matchesAt(tag.offset, getPlaintextPortion(query, marker), ignoreCase = true)
|
||||
}
|
||||
private fun isTagCompatibleWithQuery(marker: String, tag: Tag, query: String): Boolean =
|
||||
tag.editor.immutableText.matchesAt(tag.offset, getPlaintextPortion(query, marker), ignoreCase = true)
|
||||
|
||||
fun isQueryCompatibleWithTagAt(query: String, tag: Tag): Boolean {
|
||||
return tagMap.inverse()[tag].let { it != null && isTagCompatibleWithQuery(it, tag, query) }
|
||||
}
|
||||
fun isQueryCompatibleWithTagAt(query: String, tag: Tag): Boolean =
|
||||
tagMap.inverse()[tag].let { it != null && isTagCompatibleWithQuery(it, tag, query) }
|
||||
|
||||
fun canQueryMatchAnyTag(query: String): Boolean {
|
||||
return tagMap.any { (tag, offset) ->
|
||||
val tagPortion = getTagPortion(query, tag)
|
||||
tagPortion.isNotEmpty() && tag.startsWith(tagPortion, ignoreCase = true) && isTagCompatibleWithQuery(tag, offset, query)
|
||||
}
|
||||
fun canQueryMatchAnyVisibleTag(query: String): Boolean =
|
||||
tagMap.any { (label, tag) ->
|
||||
val tagPortion = getTagPortion(query, label)
|
||||
tagPortion.isNotEmpty()
|
||||
&& label.startsWith(tagPortion, ignoreCase = true)
|
||||
&& isTagCompatibleWithQuery(label, tag, query)
|
||||
&& tag.isVisible()
|
||||
}
|
||||
|
||||
private fun removeResultsWithOverlappingTags(editor: Editor, offsets: IntList) {
|
||||
|
@@ -10,7 +10,6 @@ import com.intellij.openapi.editor.Editor
|
||||
internal data class EditorSettings(
|
||||
private val isBlockCursor: Boolean,
|
||||
private val isBlinkCaret: Boolean,
|
||||
private val isReadOnly: Boolean
|
||||
) {
|
||||
companion object {
|
||||
fun setup(editor: Editor): EditorSettings {
|
||||
@@ -20,12 +19,10 @@ internal data class EditorSettings(
|
||||
val original = EditorSettings(
|
||||
isBlockCursor = settings.isBlockCursor,
|
||||
isBlinkCaret = settings.isBlinkCaret,
|
||||
isReadOnly = !document.isWritable
|
||||
)
|
||||
|
||||
settings.isBlockCursor = true
|
||||
settings.isBlinkCaret = false
|
||||
document.setReadOnly(true)
|
||||
|
||||
return original
|
||||
}
|
||||
@@ -37,6 +34,5 @@ internal data class EditorSettings(
|
||||
|
||||
settings.isBlockCursor = isBlockCursor
|
||||
settings.isBlinkCaret = isBlinkCaret
|
||||
document.setReadOnly(isReadOnly)
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import com.intellij.openapi.editor.event.CaretEvent
|
||||
import com.intellij.openapi.editor.event.CaretListener
|
||||
import org.acejump.boundaries.EditorOffsetCache
|
||||
import org.acejump.boundaries.StandardBoundaries.VISIBLE_ON_SCREEN
|
||||
import org.acejump.read
|
||||
import java.awt.Graphics
|
||||
import java.awt.Graphics2D
|
||||
import java.awt.Rectangle
|
||||
@@ -53,7 +54,7 @@ internal class TagCanvas(private val editor: Editor): JComponent(), CaretListene
|
||||
}
|
||||
|
||||
override fun paint(g: Graphics) =
|
||||
if (!markers.isNullOrEmpty()) super.paint(g) else Unit
|
||||
read { if (!markers.isNullOrEmpty()) super.paint(g) else Unit }
|
||||
|
||||
override fun paintChildren(g: Graphics) {
|
||||
super.paintChildren(g)
|
||||
@@ -66,6 +67,7 @@ internal class TagCanvas(private val editor: Editor): JComponent(), CaretListene
|
||||
|
||||
val cache = EditorOffsetCache.new()
|
||||
val viewRange = VISIBLE_ON_SCREEN.getOffsetRange(editor, cache)
|
||||
val foldingModel = editor.foldingModel
|
||||
val occupied = mutableListOf<Rectangle>()
|
||||
|
||||
// If there is a tag at the caret location, prioritize its rendering over
|
||||
@@ -81,8 +83,9 @@ internal class TagCanvas(private val editor: Editor): JComponent(), CaretListene
|
||||
val caretMarker = markers.find { it.offsetL == caretOffset || it.offsetR == caretOffset }
|
||||
caretMarker?.paint(g, editor, cache, font, occupied)
|
||||
|
||||
for (marker in markers)
|
||||
if (marker.isOffsetInRange(viewRange) && marker !== caretMarker)
|
||||
for (marker in markers) {
|
||||
if (marker.isOffsetInRange(viewRange) && !foldingModel.isOffsetCollapsed(marker.offsetL) && marker !== caretMarker)
|
||||
marker.paint(g, editor, cache, font, occupied)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -81,6 +81,10 @@ class TagMarker(
|
||||
g.color = AceConfig.tagForegroundColor
|
||||
g.drawString(text, x, y)
|
||||
}
|
||||
|
||||
private fun isLineEnding(char: Char): Boolean {
|
||||
return char == '\n' || char == '\r'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +110,7 @@ class TagMarker(
|
||||
private fun alignTag(editor: Editor, cache: EditorOffsetCache, font: TagFont, occupied: List<Rectangle>): Rectangle? {
|
||||
val boundaries = VISIBLE_ON_SCREEN
|
||||
|
||||
if (hasSpaceRight || offsetL == 0 || editor.immutableText[offsetL - 1].let { it == '\n' || it == '\r' }) {
|
||||
if (hasSpaceRight || offsetL !in 1 until editor.document.textLength || isLineEnding(editor.immutableText[offsetL - 1])) {
|
||||
val rectR = createRightAlignedTagRect(editor, cache, font)
|
||||
return rectR.takeIf { boundaries.isOffsetInside(editor, offsetR, cache) && occupied.none(rectR::intersects) }
|
||||
}
|
||||
@@ -124,7 +128,15 @@ class TagMarker(
|
||||
|
||||
private fun createRightAlignedTagRect(editor: Editor, cache: EditorOffsetCache, font: TagFont): Rectangle {
|
||||
val pos = cache.offsetToXY(editor, offsetR)
|
||||
val shift = font.editorFontMetrics.charWidth(editor.immutableText[offsetR]) + (font.tagCharWidth * shiftR)
|
||||
|
||||
val char = if (offsetR >= editor.document.textLength)
|
||||
' ' // Use the width of a space on the last line.
|
||||
else editor.immutableText[offsetR].let {
|
||||
// Use the width of a space on empty lines.
|
||||
if (isLineEnding(it)) ' ' else it
|
||||
}
|
||||
|
||||
val shift = font.editorFontMetrics.charWidth(char) + (font.tagCharWidth * shiftR)
|
||||
return Rectangle(pos.x + shift, pos.y, (font.tagCharWidth * length) + 4, font.lineHeight)
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,8 @@ import com.intellij.openapi.editor.colors.EditorFontType
|
||||
import com.intellij.openapi.editor.markup.*
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea.EXACT_RANGE
|
||||
import com.intellij.ui.*
|
||||
import com.intellij.ui.util.preferredHeight
|
||||
import com.intellij.util.DocumentUtil
|
||||
import com.intellij.util.ui.*
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import org.acejump.*
|
||||
@@ -31,7 +33,7 @@ internal class TextHighlighter {
|
||||
/**
|
||||
* Label for the search notification.
|
||||
*/
|
||||
private class NotificationLabel constructor(text: String?): JLabel(text) {
|
||||
private class NotificationLabel(text: String?): JLabel(text) {
|
||||
init {
|
||||
background = HintUtil.getInformationColor()
|
||||
foreground = JBColor.foreground()
|
||||
@@ -61,9 +63,7 @@ internal class TextHighlighter {
|
||||
val modifications = (highlights?.size ?: 0) + offsets.size
|
||||
val enableBulkEditing = modifications > 1000
|
||||
|
||||
try {
|
||||
if (enableBulkEditing) document.isInBulkUpdate = true
|
||||
|
||||
DocumentUtil.executeInBulk(document, enableBulkEditing) {
|
||||
highlights?.forEach(markup::removeHighlighter)
|
||||
previousHighlights[editor] = Array(offsets.size) { index ->
|
||||
val start = offsets.getInt(index)
|
||||
@@ -72,8 +72,6 @@ internal class TextHighlighter {
|
||||
markup.addRangeHighlighter(start, end, LAYER, null, EXACT_RANGE)
|
||||
.apply { customRenderer = renderer }
|
||||
}
|
||||
} finally {
|
||||
if (enableBulkEditing) document.isInBulkUpdate = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,16 +94,15 @@ internal class TextHighlighter {
|
||||
previousHint?.hide()
|
||||
|
||||
// add notification hint to first editor
|
||||
results.keys.first().let {
|
||||
val component: JComponent = it.component
|
||||
val editor = results.keys.first()
|
||||
val component: JComponent = editor.component
|
||||
|
||||
val label1: JLabel = NotificationLabel(" " +
|
||||
CodeInsightBundle.message("incremental.search.tooltip.prefix"))
|
||||
label1.font = UIUtil.getLabelFont().deriveFont(Font.BOLD)
|
||||
val label1 = NotificationLabel(" $jumpMode Mode:")
|
||||
.apply { font = UIUtil.getLabelFont().deriveFont(Font.BOLD) }
|
||||
|
||||
val queryText = " " +
|
||||
if (query is SearchQuery.RegularExpression) query.toRegex().toString()
|
||||
else query.rawText[0] + query.rawText.drop(1).lowercase()
|
||||
(if (query is SearchQuery.RegularExpression) query.toRegex().toString()
|
||||
else query.rawText[0] + query.rawText.drop(1).lowercase()) + " "
|
||||
val label2 = NotificationLabel(queryText)
|
||||
|
||||
val label3 = NotificationLabel(
|
||||
@@ -119,35 +116,37 @@ internal class TextHighlighter {
|
||||
add(label2, BorderLayout.CENTER)
|
||||
add(label3, BorderLayout.EAST)
|
||||
border = BorderFactory.createLineBorder(
|
||||
if (jumpMode == JumpMode.DISABLED) Color.BLACK else jumpMode.caretColor
|
||||
if (jumpMode == JumpMode.DISABLED) JBColor.BLACK else jumpMode.caretColor
|
||||
)
|
||||
|
||||
preferredSize = Dimension(it.contentComponent.width +
|
||||
label1.preferredSize.width, preferredSize.height)
|
||||
preferredHeight = label1.preferredSize.height + 10
|
||||
}
|
||||
|
||||
val hint = LightweightHint(panel)
|
||||
|
||||
val x = SwingUtilities.convertPoint(component, 0, 0, component).x
|
||||
val y: Int = -hint.component.preferredSize.height
|
||||
val p = SwingUtilities.convertPoint(component, x, y,
|
||||
component.rootPane.layeredPane)
|
||||
val p = SwingUtilities.convertPoint(
|
||||
component, x, y,
|
||||
component.rootPane.layeredPane
|
||||
)
|
||||
|
||||
HintManagerImpl.getInstanceImpl().showEditorHint(
|
||||
hint,
|
||||
it,
|
||||
editor,
|
||||
p,
|
||||
HIDE_BY_ESCAPE or HIDE_BY_TEXT_CHANGE,
|
||||
0,
|
||||
false,
|
||||
HintHint(it, p).setAwtTooltip(false)
|
||||
HintHint(editor, p).setAwtTooltip(false)
|
||||
)
|
||||
previousHint = hint
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
previousHighlights.keys.forEach { it.markupModel.removeAllHighlighters() }
|
||||
previousHighlights.forEach { (editor, highlighters) ->
|
||||
highlighters.forEach(editor.markupModel::removeHighlighter)
|
||||
}
|
||||
previousHighlights.clear()
|
||||
previousHint?.hide()
|
||||
}
|
||||
|
@@ -20,27 +20,38 @@
|
||||
id="preferences.AceConfigurable" dynamic="true"/>
|
||||
|
||||
<editorActionHandler action="EditorEscape" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$Reset"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$Reset"
|
||||
id="AceHandlerEscape"/>
|
||||
<editorActionHandler action="EditorBackSpace" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$ClearSearch"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$ClearSearch"
|
||||
id="AceHandlerBackSpace"/>
|
||||
<editorActionHandler action="EditorStartNewLine" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SelectBackward"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SelectBackward"
|
||||
id="AceHandlerStartNewLine"/>
|
||||
<editorActionHandler action="EditorEnter" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SelectForward"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SelectForward"
|
||||
id="AceHandlerEnter"/>
|
||||
<editorActionHandler action="EditorTab" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$ScrollToNextScreenful"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$ScrollToNextScreenful"
|
||||
id="AceHandlerTab"/>
|
||||
<editorActionHandler action="EditorUnindentSelection" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$ScrollToPreviousScreenful"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$ScrollToPreviousScreenful"
|
||||
id="AceHandlerUnindentSelection"/>
|
||||
<editorActionHandler action="EditorUp" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineStarts"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineStarts"
|
||||
id="AceHandlerUp"/>
|
||||
<editorActionHandler action="EditorLeft" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineIndents"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineIndents"
|
||||
id="AceHandlerLeft"/>
|
||||
<editorActionHandler action="EditorLineStart" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineIndents"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineIndents"
|
||||
id="AceHandlerLineStart"/>
|
||||
<editorActionHandler action="EditorRight" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"
|
||||
id="AceHandlerRight"/>
|
||||
<editorActionHandler action="EditorLineEnd" order="first"
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"/>
|
||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"
|
||||
id="AceHandlerLineEnd"/>
|
||||
|
||||
</extensions>
|
||||
|
||||
@@ -55,6 +66,12 @@
|
||||
<action id="AceReverseAction"
|
||||
class="org.acejump.action.AceAction$ActivateOrReverseCycleMode"
|
||||
text="Activate / Reverse Cycle AceJump Mode"/>
|
||||
<action id="AceForwardAction"
|
||||
class="org.acejump.action.AceAction$ToggleForwardJumpMode"
|
||||
text="Start AceJump in Jump After Caret Mode"/>
|
||||
<action id="AceBackwardAction"
|
||||
class="org.acejump.action.AceAction$ToggleBackwardJumpMode"
|
||||
text="Start AceJump in Jump Before Caret Mode"/>
|
||||
<action id="AceWordStartAction"
|
||||
class="org.acejump.action.AceAction$ToggleJumpMode"
|
||||
text="Start AceJump in Jump Mode"/>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 40 41" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<svg width="100%" height="100%" viewBox="0 0 40 41" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,0,-635)">
|
||||
<g id="pluginIcon" transform="matrix(0.154082,0,0,0.154082,-39.7746,553.645)">
|
||||
<rect x="258.139" y="529.264" width="259.602" height="259.602" style="fill:none;"/>
|
||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -121,11 +121,11 @@ class AceTest : BaseTest() {
|
||||
}
|
||||
|
||||
fun `test line mode`() {
|
||||
makeEditor(" test\n three\n lines\n")
|
||||
makeEditor(" test\n three\n lines")
|
||||
|
||||
takeAction(AceAction.StartAllLineMarksMode())
|
||||
|
||||
assertEquals(8, session.tags.size) // last empty line does not count
|
||||
assertEquals(9, session.tags.size)
|
||||
}
|
||||
|
||||
fun `test chinese selection`() {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import com.intellij.mock.MockVirtualFile
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
@@ -41,12 +43,12 @@ class ExternalUsageTest: BaseTest() {
|
||||
fun `test externally tagged results with multiple editors`() {
|
||||
val fileA = MockVirtualFile("a.txt", "first file")
|
||||
val fileB = MockVirtualFile("b.txt", "second file with more markers")
|
||||
myManager.openFile(fileA, true)
|
||||
myManager.openFile(fileB, false)
|
||||
manager?.openFile(fileA, true)
|
||||
manager?.openFile(fileB, false)
|
||||
|
||||
val mainEditor = (myManager.selectedEditor as TextEditor).editor
|
||||
val editorA = (myManager.getEditors(fileA).single() as TextEditor).editor
|
||||
val editorB = (myManager.getEditors(fileB).single() as TextEditor).editor
|
||||
val mainEditor = (manager?.selectedEditor as TextEditor).editor
|
||||
val editorA = (manager?.getEditors(fileA)?.single() as TextEditor).editor
|
||||
val editorB = (manager?.getEditors(fileB)?.single() as TextEditor).editor
|
||||
|
||||
val session = SessionManager.start(mainEditor, listOf(editorA, editorB))
|
||||
|
||||
@@ -85,7 +87,7 @@ class ExternalUsageTest: BaseTest() {
|
||||
SessionManager.start(myFixture.editor)
|
||||
.startRegexSearch("[aeiou]+", WHOLE_FILE)
|
||||
|
||||
TestCase.assertEquals(8, session.tags.size)
|
||||
TestCase.assertEquals(9, session.tags.size)
|
||||
}
|
||||
|
||||
fun `test external jump with bounds`() {
|
||||
@@ -166,4 +168,21 @@ class ExternalUsageTest: BaseTest() {
|
||||
TestCase.assertEquals(mark, detectedMark)
|
||||
TestCase.assertEquals("", detectedQuery)
|
||||
}
|
||||
|
||||
fun `test do not remove other highlights when the session ends`() {
|
||||
makeEditor("test do not remove other highlights when the session ends")
|
||||
|
||||
val markupModel = myFixture.editor.markupModel
|
||||
val layer = HighlighterLayer.SELECTION - 1
|
||||
val existedHighlighter = markupModel.addRangeHighlighter(0, 1, layer, null, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
takeAction(AceAction.StartAllWordsMode())
|
||||
val mark = session.tags[0].key
|
||||
typeAndWaitForResults(mark)
|
||||
|
||||
TestCase.assertEquals("last session should be disposed", null, SessionManager[myFixture.editor])
|
||||
TestCase.assertTrue("existed highlighter should not be removed", existedHighlighter.isValid)
|
||||
|
||||
existedHighlighter.dispose()
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import kotlin.system.measureTimeMillis
|
||||
|
||||
@Ignore
|
||||
class LatencyTest: BaseTest() {
|
||||
|
||||
private fun `test tag latency`(editorText: String) {
|
||||
val chars = editorText.toCharArray().distinct().filter { !it.isWhitespace() }
|
||||
val avg = averageTimeWithWarmup(warmupRuns = 10, timedRuns = 10) {
|
||||
|
@@ -38,7 +38,7 @@ abstract class BaseTest: FileEditorManagerTestCase() {
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
assertEmpty(it.markupModel.allHighlighters)
|
||||
}
|
||||
myManager.closeAllFiles()
|
||||
manager?.closeAllFiles()
|
||||
}
|
||||
|
||||
fun typeAndWaitForResults(string: String) {
|
||||
|
Reference in New Issue
Block a user