Compare commits
8 Commits
cb10ff7789
...
35003b0bab
Author | SHA1 | Date |
---|---|---|
chylex | 35003b0bab | |
breandan | dc137e4d23 | |
breandan | 6858b745e6 | |
breandan | 1505e98d66 | |
breandan | 2b7015474e | |
breandan | 8d62b0d130 | |
chylex | 828940a53a | |
chylex | 41071dbaf9 |
|
@ -2,10 +2,14 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 3.8.5
|
||||||
|
|
||||||
|
- Restores <kbd>Tab</kbd>/<kbd>Shift</kbd>+<kbd>Tab</kbd> functionality, [#356](https://github.com/acejump/AceJump/issues/356)
|
||||||
|
|
||||||
## 3.8.4
|
## 3.8.4
|
||||||
|
|
||||||
- Fixes Declaration Mode in Rider, [#379](https://github.com/acejump/AceJump/issues/379)
|
- Fixes Declaration Mode in Rider, [#379](https://github.com/acejump/AceJump/issues/379), thanks to @igor-akhmetov for helping diagnose!
|
||||||
- Thanks to @igor-akhmetov for the help diagnosing!
|
- 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)!
|
||||||
|
|
||||||
## 3.8.3
|
## 3.8.3
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ AceJump search is [smart case](http://ideavim.sourceforge.net/vim/usr_27.html#vi
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
- Press <kbd>Tab</kbd> when searching to jump to the next group of matches in the editor. *This feature is supported in `3.6.3` and currently unavailable in `3.7.0`.*
|
- 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`.*
|
||||||
|
|
||||||
- If you make a mistake searching, just press <kbd>Backspace</kbd> to restart from scratch.
|
- If you make a mistake searching, just press <kbd>Backspace</kbd> to restart from scratch.
|
||||||
|
|
||||||
|
@ -178,6 +178,7 @@ The following plugins have a similar UI for navigating text and web browsing:
|
||||||
| [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) |
|
| [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) |
|
| [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/) |
|
| [EasyMotion](https://github.com/easymotion/vim-easymotion) | [⬇](https://vimawesome.com/plugin/easymotion) | [Vim](http://www.vim.org/) | :x: | [Vimscript](http://learnvimscriptthehardway.stevelosh.com/) |
|
||||||
|
| [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/) |
|
||||||
| [Sublime EasyMotion](https://github.com/tednaleid/sublime-EasyMotion) | [⬇](https://packagecontrol.io/packages/EasyMotion) | [Sublime](https://www.sublimetext.com/) | :x: | [Python](https://www.python.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/) |
|
| [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/) |
|
| [Jumpy](https://github.com/DavidLGoldberg/jumpy) | [⬇](https://atom.io/packages/jumpy) | [Atom](https://atom.io/) | :heavy_check_mark: | [CoffeeScript](http://coffeescript.org/) |
|
||||||
|
@ -205,7 +206,7 @@ The following individuals have significantly improved AceJump through their cont
|
||||||
|
|
||||||
* [John Lindquist](https://github.com/johnlindquist) for creating AceJump and supporting it for many years.
|
* [John Lindquist](https://github.com/johnlindquist) for creating AceJump and supporting it for many years.
|
||||||
* [Breandan Considine](https://github.com/breandan) for maintaining the project and adding some new features.
|
* [Breandan Considine](https://github.com/breandan) for maintaining the project and adding some new features.
|
||||||
* [Daniel Chýlek](https://github.com/chylex) for numerous [performance optimizations](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3Achylex), [bug fixes](https://github.com/acejump/AceJump/issues/348#issuecomment-739454920) and [refactoring](https://github.com/acejump/AceJump/pull/353).
|
* [chylex](https://github.com/chylex) for numerous [performance optimizations](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3Achylex), [bug fixes](https://github.com/acejump/AceJump/issues/348#issuecomment-739454920) and [refactoring](https://github.com/acejump/AceJump/pull/353).
|
||||||
* [Alex Plate](https://github.com/AlexPl292) for submitting [several PRs](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3AAlexPl292).
|
* [Alex Plate](https://github.com/AlexPl292) for submitting [several PRs](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3AAlexPl292).
|
||||||
* [Sven Speckmaier](https://github.com/svensp) for [improving](https://github.com/acejump/AceJump/pull/214) search latency.
|
* [Sven Speckmaier](https://github.com/svensp) for [improving](https://github.com/acejump/AceJump/pull/214) search latency.
|
||||||
* [Stefan Monnier](https://www.iro.umontreal.ca/~monnier/) for algorithmic advice and maintaining Emacs for several years.
|
* [Stefan Monnier](https://www.iro.umontreal.ca/~monnier/) for algorithmic advice and maintaining Emacs for several years.
|
||||||
|
|
|
@ -4,9 +4,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
idea apply true
|
idea apply true
|
||||||
kotlin("jvm") version "1.5.30"
|
kotlin("jvm") version "1.6.0-RC2"
|
||||||
id("org.jetbrains.intellij") version "1.1.6"
|
id("org.jetbrains.intellij") version "1.2.1"
|
||||||
id("org.jetbrains.changelog") version "1.3.0"
|
id("org.jetbrains.changelog") version "1.3.1"
|
||||||
id("com.github.ben-manes.versions") version "0.39.0"
|
id("com.github.ben-manes.versions") version "0.39.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ tasks {
|
||||||
}
|
}
|
||||||
|
|
||||||
changelog {
|
changelog {
|
||||||
version.set("3.8.4")
|
version.set("3.8.5")
|
||||||
path.set("${project.projectDir}/CHANGES.md")
|
path.set("${project.projectDir}/CHANGES.md")
|
||||||
header.set(provider { "[${project.version}] - ${date()}" })
|
header.set(provider { "[${project.version}] - ${date()}" })
|
||||||
itemPrefix.set("-")
|
itemPrefix.set("-")
|
||||||
|
@ -70,4 +70,4 @@ intellij {
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "org.acejump"
|
group = "org.acejump"
|
||||||
version = "3.8.4"
|
version = "3.8.5"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package org.acejump
|
package org.acejump
|
||||||
|
|
||||||
import com.anyascii.AnyAscii
|
import com.anyascii.AnyAscii
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.diff.util.DiffUtil.getLineCount
|
||||||
|
import com.intellij.openapi.editor.*
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||||
import org.acejump.config.AceConfig
|
import org.acejump.config.AceConfig
|
||||||
|
import kotlin.math.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This annotation is a marker which means that the annotated function is
|
* This annotation is a marker which means that the annotated function is
|
||||||
|
@ -117,3 +119,137 @@ fun MutableMap<Editor, IntArrayList>.clone(): MutableMap<Editor, IntArrayList> {
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Editor.offsetCenter(first: Int, second: Int): LogicalPosition {
|
||||||
|
val firstIndexLine = offsetToLogicalPosition(first).line
|
||||||
|
val lastIndexLine = offsetToLogicalPosition(second).line
|
||||||
|
val center = (firstIndexLine + lastIndexLine) / 2
|
||||||
|
return offsetToLogicalPosition(getLineStartOffset(center))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the offset of the start of the requested line.
|
||||||
|
*
|
||||||
|
* @param line The logical line to get the start offset for.
|
||||||
|
*
|
||||||
|
* @return 0 if line is < 0, file size of line is bigger than file, else the
|
||||||
|
* start offset for the line
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.getLineStartOffset(line: Int) =
|
||||||
|
when {
|
||||||
|
line < 0 -> 0
|
||||||
|
line >= getLineCount(document) -> getFileSize()
|
||||||
|
else -> document.getLineStartOffset(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the offset of the end of the requested line.
|
||||||
|
*
|
||||||
|
* @param line The logical line to get the end offset for
|
||||||
|
*
|
||||||
|
* @param allowEnd True include newline
|
||||||
|
*
|
||||||
|
* @return 0 if line is < 0, file size of line is bigger than file, else the
|
||||||
|
* end offset for the line
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.getLineEndOffset(line: Int, allowEnd: Boolean = true) =
|
||||||
|
when {
|
||||||
|
line < 0 -> 0
|
||||||
|
line >= getLineCount(document) -> getFileSize(allowEnd)
|
||||||
|
else -> document.getLineEndOffset(line) - if (allowEnd) 0 else 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of lines than can be displayed on the screen at one time.
|
||||||
|
* This is rounded down to the nearest whole line if there is a partial line
|
||||||
|
* visible at the bottom of the screen.
|
||||||
|
*
|
||||||
|
* @return The number of screen lines
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.getScreenHeight() =
|
||||||
|
(scrollingModel.visibleArea.y + scrollingModel.visibleArea.height -
|
||||||
|
getVisualLineAtTopOfScreen() * lineHeight) / lineHeight
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a set of helper methods for working with editors.
|
||||||
|
* All line and column values are zero based.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.getVisualLineAtTopOfScreen() =
|
||||||
|
(scrollingModel.verticalScrollOffset + lineHeight - 1) / lineHeight
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the actual number of characters in the file
|
||||||
|
*
|
||||||
|
* @param countNewLines True include newline
|
||||||
|
*
|
||||||
|
* @return The file's character count
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.getFileSize(countNewLines: Boolean = false): Int {
|
||||||
|
val len = document.textLength
|
||||||
|
val doc = document.charsSequence
|
||||||
|
return if (countNewLines || len == 0 || doc[len - 1] != '\n') len else len - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the supplied logical line is within the range 0 (incl) and the
|
||||||
|
* number of logical lines in the file (excl).
|
||||||
|
*
|
||||||
|
* @param line The logical line number to normalize
|
||||||
|
*
|
||||||
|
* @return The normalized logical line number
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.normalizeLine(line: Int) = max(0, min(line, getLineCount(document) - 1))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a visual line number to a logical line number.
|
||||||
|
*
|
||||||
|
* @param line The visual line number to convert
|
||||||
|
*
|
||||||
|
* @return The logical line number
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.visualLineToLogicalLine(line: Int) =
|
||||||
|
normalizeLine(visualToLogicalPosition(
|
||||||
|
VisualPosition(line.coerceAtLeast(0), 0)).line)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the supplied offset for the given logical line is within the
|
||||||
|
* range for the line. If allowEnd is true, the range will allow for the offset
|
||||||
|
* to be one past the last character on the line.
|
||||||
|
*
|
||||||
|
* @param line The logical line number
|
||||||
|
*
|
||||||
|
* @param offset The offset to normalize
|
||||||
|
*
|
||||||
|
* @param allowEnd true if the offset can be one past the last character on the
|
||||||
|
* line, false if not
|
||||||
|
*
|
||||||
|
* @return The normalized column number
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Editor.normalizeOffset(line: Int, offset: Int, allowEnd: Boolean = true) =
|
||||||
|
if (getFileSize(allowEnd) == 0) 0 else
|
||||||
|
max(min(offset, getLineEndOffset(line, allowEnd)), getLineStartOffset(line))
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,14 @@ sealed class AceEditorAction(private val originalHandler: EditorActionHandler):
|
||||||
override fun run(session: Session) = session.visitNextTag()
|
override fun run(session: Session) = session.visitNextTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScrollToNextScreenful(originalHandler: EditorActionHandler): AceEditorAction(originalHandler) {
|
||||||
|
override fun run(session: Session) { session.scrollToNextScreenful() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScrollToPreviousScreenful(originalHandler: EditorActionHandler): AceEditorAction(originalHandler) {
|
||||||
|
override fun run(session: Session) { session.scrollToPreviousScreenful() }
|
||||||
|
}
|
||||||
|
|
||||||
class SearchLineStarts(originalHandler: EditorActionHandler): AceEditorAction(originalHandler) {
|
class SearchLineStarts(originalHandler: EditorActionHandler): AceEditorAction(originalHandler) {
|
||||||
override fun run(session: Session) = session.startRegexSearch(LINE_STARTS, WHOLE_FILE)
|
override fun run(session: Session) = session.startRegexSearch(LINE_STARTS, WHOLE_FILE)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package org.acejump.action
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scrollTo(position: LogicalPosition) = editor.run {
|
||||||
|
scrollingModel.disableAnimation()
|
||||||
|
scrollingModel.scrollTo(position, ScrollType.CENTER)
|
||||||
|
|
||||||
|
val firstInView = textMatches.first { it in editor.getView() }
|
||||||
|
val horizontalOffset = offsetToLogicalPosition(firstInView).column
|
||||||
|
if (horizontalOffset > scrollingModel.visibleArea.width)
|
||||||
|
scrollingModel.scrollHorizontally(horizontalOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
val textMatches by lazy { searchProcessor.results[editor]!! }
|
||||||
|
|
||||||
|
private fun findPreviousPosition(): LogicalPosition? {
|
||||||
|
val prevIndex = textMatches.toList().dropLastWhile { it > editor.getView().first }
|
||||||
|
.lastOrNull() ?: textMatches.lastOrNull() ?: return null
|
||||||
|
|
||||||
|
val prevLineNum = editor.offsetToLogicalPosition(prevIndex).line
|
||||||
|
|
||||||
|
// Try to capture as many previous results as will fit in a screenful
|
||||||
|
fun maximizeCoverageOfPreviousOccurrence(): LogicalPosition {
|
||||||
|
val minVisibleLine = prevLineNum - editor.getScreenHeight()
|
||||||
|
val firstVisibleIndex = editor.getLineStartOffset(minVisibleLine)
|
||||||
|
val firstIndex = textMatches.dropWhile { it < firstVisibleIndex }.first()
|
||||||
|
return editor.offsetCenter(firstIndex, prevIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return maximizeCoverageOfPreviousOccurrence()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the center of the next set of results that will fit in the editor.
|
||||||
|
* [textMatches] must be sorted prior to using Scroller. If [textMatches] have
|
||||||
|
* not previously been sorted, the result of calling this method is undefined.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private fun findNextPosition(): LogicalPosition? {
|
||||||
|
val nextIndex = textMatches.dropWhile { it <= editor.getView().last }
|
||||||
|
.firstOrNull() ?: textMatches.firstOrNull() ?: return null
|
||||||
|
|
||||||
|
val nextLineNum = editor.offsetToLogicalPosition(nextIndex).line
|
||||||
|
|
||||||
|
// Try to capture as many subsequent results as will fit in a screenful
|
||||||
|
fun maximizeCoverageOfNextOccurrence(): LogicalPosition {
|
||||||
|
val maxVisibleLine = nextLineNum + editor.getScreenHeight()
|
||||||
|
val lastVisibleIndex = editor.getLineEndOffset(maxVisibleLine, true)
|
||||||
|
val lastIndex = textMatches.toList().dropLastWhile { it > lastVisibleIndex }.last()
|
||||||
|
return editor.offsetCenter(nextIndex, lastIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return maximizeCoverageOfNextOccurrence()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package org.acejump.action
|
package org.acejump.action
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.*
|
||||||
import com.intellij.openapi.editor.ScrollType.RELATIVE
|
import com.intellij.openapi.editor.ScrollType.*
|
||||||
import com.intellij.openapi.editor.SelectionModel
|
import org.acejump.*
|
||||||
import org.acejump.search.SearchProcessor
|
import org.acejump.search.SearchProcessor
|
||||||
import org.acejump.search.Tag
|
import org.acejump.search.Tag
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -40,9 +40,7 @@ internal class TagVisitor(private val editor: Editor, private val searchProcesso
|
||||||
val targetOffset = listOfNotNull(
|
val targetOffset = listOfNotNull(
|
||||||
results.getOrNull(index - 1),
|
results.getOrNull(index - 1),
|
||||||
results.getOrNull(index)
|
results.getOrNull(index)
|
||||||
).minBy {
|
).minByOrNull { abs(it - caret) }
|
||||||
abs(it - caret)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetOffset != null)
|
if (targetOffset != null)
|
||||||
editor.scrollingModel.scrollTo(editor.offsetToLogicalPosition(targetOffset), RELATIVE)
|
editor.scrollingModel.scrollTo(editor.offsetToLogicalPosition(targetOffset), RELATIVE)
|
||||||
|
@ -68,4 +66,5 @@ internal class TagVisitor(private val editor: Editor, private val searchProcesso
|
||||||
editor.scrollingModel.scrollToCaret(RELATIVE)
|
editor.scrollingModel.scrollToCaret(RELATIVE)
|
||||||
return onlyResult
|
return onlyResult
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ internal class AceSettingsPanel {
|
||||||
|
|
||||||
// Removal pending support for https://youtrack.jetbrains.com/issue/KT-8575
|
// Removal pending support for https://youtrack.jetbrains.com/issue/KT-8575
|
||||||
|
|
||||||
private operator fun JTextComponent.getValue(a: AceSettingsPanel, p: KProperty<*>) = text.toLowerCase()
|
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 JTextComponent.setValue(a: AceSettingsPanel, p: KProperty<*>, s: String) = setText(s)
|
||||||
|
|
||||||
private operator fun ColorPanel.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedColor
|
private operator fun ColorPanel.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedColor
|
||||||
|
@ -153,7 +153,7 @@ internal class AceSettingsPanel {
|
||||||
private operator fun JCheckBox.getValue(a: AceSettingsPanel, p: KProperty<*>) = isSelected
|
private operator fun JCheckBox.getValue(a: AceSettingsPanel, p: KProperty<*>) = isSelected
|
||||||
private operator fun JCheckBox.setValue(a: AceSettingsPanel, p: KProperty<*>, selected: Boolean) = setSelected(selected)
|
private operator fun JCheckBox.setValue(a: AceSettingsPanel, p: KProperty<*>, selected: Boolean) = setSelected(selected)
|
||||||
|
|
||||||
private operator fun <T> ComboBox<T>.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedItem as T
|
private inline operator fun <reified T> ComboBox<T>.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedItem as T
|
||||||
private operator fun <T> ComboBox<T>.setValue(a: AceSettingsPanel, p: KProperty<*>, item: T) = setSelectedItem(item)
|
private operator fun <T> ComboBox<T>.setValue(a: AceSettingsPanel, p: KProperty<*>, item: T) = setSelectedItem(item)
|
||||||
|
|
||||||
private inline fun <reified T: Enum<T>> ComboBox<T>.setupEnumItems(crossinline onChanged: (T) -> Unit) {
|
private inline fun <reified T: Enum<T>> ComboBox<T>.setupEnumItems(crossinline onChanged: (T) -> Unit) {
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package org.acejump.input
|
package org.acejump.input
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||||
|
import java.awt.geom.Point2D
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines common keyboard layouts. Each layout has a key priority order,
|
* Defines common keyboard layouts. Each layout has a key priority order,
|
||||||
* based on each key's distance from the home row and how ergonomically
|
* based on each key's distance from the home row and how ergonomically
|
||||||
* difficult they are to press.
|
* difficult they are to press.
|
||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@ -19,5 +24,38 @@ enum class KeyLayout(internal val rows: Array<String>, priority: String) {
|
||||||
internal val allChars = rows.joinToString("").toCharArray().apply(CharArray::sort).joinToString("")
|
internal val allChars = rows.joinToString("").toCharArray().apply(CharArray::sort).joinToString("")
|
||||||
internal val allPriorities = priority.mapIndexed { index, char -> char to index }.toMap()
|
internal val allPriorities = priority.mapIndexed { index, char -> char to index }.toMap()
|
||||||
|
|
||||||
internal inline fun priority(crossinline tagToChar: (String) -> Char): (String) -> Int? = { allPriorities[tagToChar(it)] }
|
private val keyDistances: Map<Char, Object2IntMap<Char>> by lazy {
|
||||||
|
val keyDistanceMap = mutableMapOf<Char, Object2IntMap<Char>>()
|
||||||
|
val keyLocations = mutableMapOf<Char, Point2D>()
|
||||||
|
|
||||||
|
for ((rowIndex, rowChars) in rows.withIndex()) {
|
||||||
|
val keyY = rowIndex * 1.2F // Slightly increase cost of traveling between rows.
|
||||||
|
|
||||||
|
for ((columnIndex, char) in rowChars.withIndex()) {
|
||||||
|
val keyX = columnIndex + (0.25F * rowIndex) // Assume a 1/4-key uniform stagger.
|
||||||
|
keyLocations[char] = Point2D.Float(keyX, keyY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (fromChar in allChars) {
|
||||||
|
val distances = Object2IntOpenHashMap<Char>()
|
||||||
|
val fromLocation = keyLocations.getValue(fromChar)
|
||||||
|
|
||||||
|
for (toChar in allChars) {
|
||||||
|
distances[toChar] = floor(2F * fromLocation.distanceSq(keyLocations.getValue(toChar))).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
keyDistanceMap[fromChar] = distances
|
||||||
|
}
|
||||||
|
|
||||||
|
keyDistanceMap
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun priority(crossinline tagToChar: (String) -> Char): (String) -> Int? {
|
||||||
|
return { allPriorities[tagToChar(it)] }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun distanceBetweenKeys(char1: Char, char2: Char): Int {
|
||||||
|
return keyDistances.getValue(char1).getValue(char2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,48 +7,6 @@ import org.acejump.config.AceSettings
|
||||||
* with repeated keys (ex. FF, JJ) or adjacent keys (ex. GH, UJ).
|
* with repeated keys (ex. FF, JJ) or adjacent keys (ex. GH, UJ).
|
||||||
*/
|
*/
|
||||||
internal object KeyLayoutCache {
|
internal object KeyLayoutCache {
|
||||||
/**
|
|
||||||
* Stores keys ordered by proximity to other keys for the QWERTY layout.
|
|
||||||
* TODO: Support more layouts, perhaps generate automatically.
|
|
||||||
*/
|
|
||||||
private val qwertyCharacterDistances = mapOf(
|
|
||||||
'j' to "jikmnhuolbgypvftcdrxsezawq8796054321",
|
|
||||||
'f' to "ftgvcdryhbxseujnzawqikmolp5463728190",
|
|
||||||
'k' to "kolmjipnhubgyvftcdrxsezawq9807654321",
|
|
||||||
'd' to "drfcxsetgvzawyhbqujnikmolp4352617890",
|
|
||||||
'l' to "lkopmjinhubgyvftcdrxsezawq0987654321",
|
|
||||||
's' to "sedxzawrfcqtgvyhbujnikmolp3241567890",
|
|
||||||
'a' to "aqwszedxrfctgvyhbujnikmolp1234567890",
|
|
||||||
'h' to "hujnbgyikmvftolcdrpxsezawq6758493021",
|
|
||||||
'g' to "gyhbvftujncdrikmxseolzawpq5647382910",
|
|
||||||
'y' to "yuhgtijnbvfrokmcdeplxswzaq6758493021",
|
|
||||||
't' to "tygfruhbvcdeijnxswokmzaqpl5647382910",
|
|
||||||
'u' to "uijhyokmnbgtplvfrcdexswzaq7869504321",
|
|
||||||
'r' to "rtfdeygvcxswuhbzaqijnokmpl4536271890",
|
|
||||||
'n' to "nbhjmvgyuiklocftpxdrzseawq7685940321",
|
|
||||||
'v' to "vcfgbxdrtyhnzseujmawikqolp5463728190",
|
|
||||||
'm' to "mnjkbhuilvgyopcftxdrzseawq8970654321",
|
|
||||||
'c' to "cxdfvzsertgbawyhnqujmikolp4352617890",
|
|
||||||
'b' to "bvghncftyujmxdrikzseolawqp6574839201",
|
|
||||||
'i' to "iokjuplmnhybgtvfrcdexswzaq8970654321",
|
|
||||||
'e' to "erdswtfcxzaqygvuhbijnokmpl3425167890",
|
|
||||||
'x' to "xzsdcawerfvqtgbyhnujmikolp3241567890",
|
|
||||||
'z' to "zasxqwedcrfvtgbyhnujmikolp1234567890",
|
|
||||||
'o' to "oplkimjunhybgtvfrcdexswzaq9087654321",
|
|
||||||
'w' to "wesaqrdxztfcygvuhbijnokmpl2314567890",
|
|
||||||
'p' to "plokimjunhybgtvfrcdexswzaq0987654321",
|
|
||||||
'q' to "qwaeszrdxtfcygvuhbijnokmpl1234567890",
|
|
||||||
'1' to "1234567890qawzsexdrcftvgybhunjimkolp",
|
|
||||||
'2' to "2134567890qwasezxdrcftvgybhunjimkolp",
|
|
||||||
'3' to "3241567890weqasdrzxcftvgybhunjimkolp",
|
|
||||||
'4' to "4352617890erwsdftqazxcvgybhunjimkolp",
|
|
||||||
'5' to "5463728190rtedfgywsxcvbhuqaznjimkolp",
|
|
||||||
'6' to "6574839201tyrfghuedcvbnjiwsxmkoqazlp",
|
|
||||||
'7' to "7685940321yutghjirfvbnmkoedclpwsxqaz",
|
|
||||||
'8' to "8796054321uiyhjkotgbnmlprfvedcwsxqaz",
|
|
||||||
'9' to "9807654321ioujklpyhnmtgbrfvedcwsxqaz",
|
|
||||||
'0' to "0987654321opiklujmyhntgbrfvedcwsxqaz").mapValues { (_, v) -> v.mapIndexed { index, char -> char to index }.toMap() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts tags according to current keyboard layout settings, and some predefined rules that force tags with digits, and tags with two
|
* Sorts tags according to current keyboard layout settings, and some predefined rules that force tags with digits, and tags with two
|
||||||
* keys far apart, to be sorted after other (easier to type) tags.
|
* keys far apart, to be sorted after other (easier to type) tags.
|
||||||
|
@ -74,7 +32,7 @@ internal object KeyLayoutCache {
|
||||||
fun reset(settings: AceSettings) {
|
fun reset(settings: AceSettings) {
|
||||||
tagOrder = compareBy(
|
tagOrder = compareBy(
|
||||||
{ it[0].isDigit() || it[1].isDigit() },
|
{ it[0].isDigit() || it[1].isDigit() },
|
||||||
{ qwertyCharacterDistances.getValue(it[0]).getValue(it[1]) },
|
{ settings.layout.distanceBetweenKeys(it[0], it[1]) },
|
||||||
settings.layout.priority { it[0] }
|
settings.layout.priority { it[0] }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,10 @@ internal class SearchProcessor private constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = query
|
var query: SearchQuery = query
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var results = results
|
var results: MutableMap<Editor, IntArrayList> = results
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -73,7 +73,7 @@ internal class Solver private constructor(
|
||||||
private val newTagIndices = newResults.keys.associateWith { IntOpenHashSet() }
|
private val newTagIndices = newResults.keys.associateWith { IntOpenHashSet() }
|
||||||
|
|
||||||
private var allWordFragments =
|
private var allWordFragments =
|
||||||
HashSet<String>(allResults.values.sumBy(IntList::size)).apply {
|
HashSet<String>(allResults.values.sumOf(IntList::size)).apply {
|
||||||
for ((editor, offsets) in allResults) {
|
for ((editor, offsets) in allResults) {
|
||||||
val iter = offsets.iterator()
|
val iter = offsets.iterator()
|
||||||
while (iter.hasNext()) forEachWordFragment(editor, iter.nextInt()) { add(it) }
|
while (iter.hasNext()) forEachWordFragment(editor, iter.nextInt()) { add(it) }
|
||||||
|
@ -119,7 +119,7 @@ internal class Solver private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalAssigned = 0
|
var totalAssigned = 0
|
||||||
val totalResults = newResults.values.sumBy(IntList::size)
|
val totalResults = newResults.values.sumOf(IntList::size)
|
||||||
|
|
||||||
for (tag in sortedTags) {
|
for (tag in sortedTags) {
|
||||||
if (totalAssigned == totalResults) {
|
if (totalAssigned == totalResults) {
|
||||||
|
@ -191,7 +191,7 @@ internal class Solver private constructor(
|
||||||
val builder = StringBuilder(1 + right - left)
|
val builder = StringBuilder(1 + right - left)
|
||||||
|
|
||||||
for (i in left..right) {
|
for (i in left..right) {
|
||||||
builder.append(chars[i].toLowerCase())
|
builder.append(chars[i].lowercase())
|
||||||
callback(builder.toString())
|
callback(builder.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ internal class Tagger(private val editors: List<Editor>) {
|
||||||
*/
|
*/
|
||||||
fun markOrJump(query: SearchQuery, results: Map<Editor, IntList>): TaggingResult {
|
fun markOrJump(query: SearchQuery, results: Map<Editor, IntList>): TaggingResult {
|
||||||
val isRegex = query is SearchQuery.RegularExpression
|
val isRegex = query is SearchQuery.RegularExpression
|
||||||
val queryText = if (isRegex) " ${query.rawText}" else query.rawText[0] + query.rawText.drop(1).toLowerCase()
|
val queryText = if (isRegex) " ${query.rawText}" else query.rawText[0] + query.rawText.drop(1).lowercase()
|
||||||
|
|
||||||
val availableTags = allPossibleTags.filter { !queryText.endsWith(it[0]) && it !in tagMap }
|
val availableTags = allPossibleTags.filter { !queryText.endsWith(it[0]) && it !in tagMap }
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.intellij.openapi.editor.colors.EditorColors.CARET_COLOR
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||||
import org.acejump.*
|
import org.acejump.*
|
||||||
|
import org.acejump.action.TagScroller
|
||||||
import org.acejump.action.TagJumper
|
import org.acejump.action.TagJumper
|
||||||
import org.acejump.action.TagVisitor
|
import org.acejump.action.TagVisitor
|
||||||
import org.acejump.boundaries.Boundaries
|
import org.acejump.boundaries.Boundaries
|
||||||
|
@ -65,7 +66,10 @@ class Session(private val mainEditor: Editor, private val jumpEditors: List<Edit
|
||||||
|
|
||||||
private val tagVisitor
|
private val tagVisitor
|
||||||
get() = searchProcessor?.let { TagVisitor(mainEditor, it, tagJumper) }
|
get() = searchProcessor?.let { TagVisitor(mainEditor, it, tagJumper) }
|
||||||
|
|
||||||
|
private val tagScroller
|
||||||
|
get() = searchProcessor?.let { TagScroller(mainEditor, it) }
|
||||||
|
|
||||||
private val textHighlighter = TextHighlighter()
|
private val textHighlighter = TextHighlighter()
|
||||||
private val tagCanvases = jumpEditors.associateWith(::TagCanvas)
|
private val tagCanvases = jumpEditors.associateWith(::TagCanvas)
|
||||||
|
|
||||||
|
@ -236,6 +240,16 @@ class Session(private val mainEditor: Editor, private val jumpEditors: List<Edit
|
||||||
fun visitNextTag() =
|
fun visitNextTag() =
|
||||||
if (tagVisitor?.visitNext() == true) end() else Unit
|
if (tagVisitor?.visitNext() == true) end() else Unit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [TagVisitor.visitPrevious]. If there are no tags, nothing happens.
|
||||||
|
*/
|
||||||
|
fun scrollToNextScreenful() = tagScroller?.scroll(true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [TagVisitor.visitNext]. If there are no tags, nothing happens.
|
||||||
|
*/
|
||||||
|
fun scrollToPreviousScreenful() = tagScroller?.scroll(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends this session.
|
* Ends this session.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.acejump.view
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.ui.ColorUtil
|
import com.intellij.ui.ColorUtil
|
||||||
|
import com.intellij.ui.JreHiDpiUtil
|
||||||
import com.intellij.ui.scale.JBUIScale
|
import com.intellij.ui.scale.JBUIScale
|
||||||
import org.acejump.boundaries.EditorOffsetCache
|
import org.acejump.boundaries.EditorOffsetCache
|
||||||
import org.acejump.boundaries.StandardBoundaries.VISIBLE_ON_SCREEN
|
import org.acejump.boundaries.StandardBoundaries.VISIBLE_ON_SCREEN
|
||||||
|
@ -39,9 +40,9 @@ class TagMarker(
|
||||||
val hasSpaceRight = offset + 1 >= chars.length || chars[offset + 1].isWhitespace()
|
val hasSpaceRight = offset + 1 >= chars.length || chars[offset + 1].isWhitespace()
|
||||||
|
|
||||||
val displayedTag = if (literalQueryText != null && literalQueryText.last().equals(tag.first(), ignoreCase = true))
|
val displayedTag = if (literalQueryText != null && literalQueryText.last().equals(tag.first(), ignoreCase = true))
|
||||||
tag.drop(1).toUpperCase()
|
tag.drop(1).uppercase()
|
||||||
else
|
else
|
||||||
tag.toUpperCase()
|
tag.uppercase()
|
||||||
|
|
||||||
return TagMarker(displayedTag, offset, offset + max(0, matching - 1), tag.length - displayedTag.length, hasSpaceRight)
|
return TagMarker(displayedTag, offset, offset + max(0, matching - 1), tag.length - displayedTag.length, hasSpaceRight)
|
||||||
}
|
}
|
||||||
|
@ -51,7 +52,16 @@ class TagMarker(
|
||||||
*/
|
*/
|
||||||
private fun drawHighlight(g: Graphics2D, rect: Rectangle, color: Color) {
|
private fun drawHighlight(g: Graphics2D, rect: Rectangle, color: Color) {
|
||||||
g.color = color
|
g.color = color
|
||||||
g.fillRoundRect(rect.x, rect.y, rect.width, rect.height + 1, ARC, ARC)
|
|
||||||
|
// Workaround for misalignment on high DPI screens.
|
||||||
|
if (JreHiDpiUtil.isJreHiDPI(g)) {
|
||||||
|
g.translate(0.0, -0.5)
|
||||||
|
g.fillRoundRect(rect.x, rect.y, rect.width, rect.height + 1, ARC, ARC)
|
||||||
|
g.translate(0.0, 0.5)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g.fillRoundRect(rect.x, rect.y, rect.width, rect.height + 1, ARC, ARC)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -105,7 +105,7 @@ internal class TextHighlighter {
|
||||||
|
|
||||||
val queryText = " " +
|
val queryText = " " +
|
||||||
if (query is SearchQuery.RegularExpression) query.toRegex().toString()
|
if (query is SearchQuery.RegularExpression) query.toRegex().toString()
|
||||||
else query.rawText[0] + query.rawText.drop(1).toLowerCase()
|
else query.rawText[0] + query.rawText.drop(1).lowercase()
|
||||||
val label2 = NotificationLabel(queryText)
|
val label2 = NotificationLabel(queryText)
|
||||||
|
|
||||||
val label3 = NotificationLabel(
|
val label3 = NotificationLabel(
|
||||||
|
@ -164,7 +164,7 @@ internal class TextHighlighter {
|
||||||
val end = EditorOffsetCache.Uncached.offsetToXY(editor, endOffset)
|
val end = EditorOffsetCache.Uncached.offsetToXY(editor, endOffset)
|
||||||
|
|
||||||
g.color = AceConfig.textHighlightColor
|
g.color = AceConfig.textHighlightColor
|
||||||
g.fillRect(start.x, start.y + 1, end.x - start.x, editor.lineHeight - 1)
|
g.fillRect(start.x, start.y, end.x - start.x, editor.lineHeight)
|
||||||
|
|
||||||
g.color = AceConfig.tagBackgroundColor
|
g.color = AceConfig.tagBackgroundColor
|
||||||
g.drawRect(start.x, start.y, end.x - start.x, editor.lineHeight)
|
g.drawRect(start.x, start.y, end.x - start.x, editor.lineHeight)
|
||||||
|
@ -213,7 +213,7 @@ internal class TextHighlighter {
|
||||||
val lastCharWidth = editor.component.getFontMetrics(font).charWidth(char)
|
val lastCharWidth = editor.component.getFontMetrics(font).charWidth(char)
|
||||||
|
|
||||||
g.color = AceConfig.textHighlightColor
|
g.color = AceConfig.textHighlightColor
|
||||||
g.fillRect(pos.x, pos.y + 1, lastCharWidth, editor.lineHeight - 1)
|
g.fillRect(pos.x, pos.y, lastCharWidth, editor.lineHeight)
|
||||||
|
|
||||||
g.color = AceConfig.tagBackgroundColor
|
g.color = AceConfig.tagBackgroundColor
|
||||||
g.drawRect(pos.x, pos.y, lastCharWidth, editor.lineHeight)
|
g.drawRect(pos.x, pos.y, lastCharWidth, editor.lineHeight)
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
implementationClass="org.acejump.action.AceEditorAction$SelectBackward"/>
|
implementationClass="org.acejump.action.AceEditorAction$SelectBackward"/>
|
||||||
<editorActionHandler action="EditorEnter" order="first"
|
<editorActionHandler action="EditorEnter" order="first"
|
||||||
implementationClass="org.acejump.action.AceEditorAction$SelectForward"/>
|
implementationClass="org.acejump.action.AceEditorAction$SelectForward"/>
|
||||||
|
<editorActionHandler action="EditorTab" order="first"
|
||||||
|
implementationClass="org.acejump.action.AceEditorAction$ScrollToNextScreenful"/>
|
||||||
|
<editorActionHandler action="EditorUnindentSelection" order="first"
|
||||||
|
implementationClass="org.acejump.action.AceEditorAction$ScrollToPreviousScreenful"/>
|
||||||
<editorActionHandler action="EditorUp" order="first"
|
<editorActionHandler action="EditorUp" order="first"
|
||||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineStarts"/>
|
implementationClass="org.acejump.action.AceEditorAction$SearchLineStarts"/>
|
||||||
<editorActionHandler action="EditorLeft" order="first"
|
<editorActionHandler action="EditorLeft" order="first"
|
||||||
|
@ -37,6 +41,7 @@
|
||||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"/>
|
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"/>
|
||||||
<editorActionHandler action="EditorLineEnd" order="first"
|
<editorActionHandler action="EditorLineEnd" order="first"
|
||||||
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"/>
|
implementationClass="org.acejump.action.AceEditorAction$SearchLineEnds"/>
|
||||||
|
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<actions>
|
<actions>
|
||||||
|
|
|
@ -65,7 +65,7 @@ class AceTest : BaseTest() {
|
||||||
fun `test shift selection`() {
|
fun `test shift selection`() {
|
||||||
"<caret>testing 1234".search("4")
|
"<caret>testing 1234".search("4")
|
||||||
|
|
||||||
typeAndWaitForResults(session.tags[0].key.toUpperCase())
|
typeAndWaitForResults(session.tags[0].key.uppercase())
|
||||||
|
|
||||||
myFixture.checkResult("<selection>testing 123<caret></selection>4")
|
myFixture.checkResult("<selection>testing 123<caret></selection>4")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,7 @@ class ExternalUsageTest: BaseTest() {
|
||||||
fun `test externally tagged results and listener notification`() {
|
fun `test externally tagged results and listener notification`() {
|
||||||
makeEditor("test externally tagged results")
|
makeEditor("test externally tagged results")
|
||||||
|
|
||||||
SessionManager.start(myFixture.editor)
|
SessionManager.start(myFixture.editor).markResults(sortedSetOf(4, 10, 15))
|
||||||
.markResults(sortedSetOf(4, 10, 15))
|
|
||||||
|
|
||||||
TestCase.assertEquals(3, session.tags.size)
|
TestCase.assertEquals(3, session.tags.size)
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,6 @@ class LatencyTest: BaseTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
fun `test lorem ipsum latency`() = `test tag latency`(
|
fun `test lorem ipsum latency`() = `test tag latency`(
|
||||||
File(
|
File(javaClass.classLoader.getResource("lipsum.txt")!!.file).readText()
|
||||||
javaClass.classLoader.getResource("lipsum.txt")!!.file
|
|
||||||
).readText()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,7 @@ abstract class BaseTest: FileEditorManagerTestCase() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val session
|
protected val session get() = SessionManager[myFixture.editor]!!
|
||||||
get() = SessionManager[myFixture.editor]!!
|
|
||||||
|
|
||||||
override fun tearDown() {
|
override fun tearDown() {
|
||||||
resetEditor()
|
resetEditor()
|
||||||
|
|
Loading…
Reference in New Issue