mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-05 18:34:03 +02:00
Rewrite displayLocationInfo to avoid findNextWord
Simplifies and fixes counting words, especially when selection intersects words. Also moves implementation to engine and adds more tests.
This commit is contained in:
parent
8eef802ac7
commit
b5937e885d
src
main/java/com/maddyhome/idea/vim
test/java/org/jetbrains/plugins/ideavim/action
vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api
@ -32,17 +32,13 @@ import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimFileBase
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
||||
import com.maddyhome.idea.vim.helper.countWords
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.execute
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
@ -265,92 +261,6 @@ class FileGroup : VimFileBase() {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun displayLocationInfo(vimEditor: VimEditor) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val msg = StringBuilder()
|
||||
val doc = editor.document
|
||||
|
||||
if (injector.vimState.mode !is VISUAL) {
|
||||
val lp = editor.caretModel.logicalPosition
|
||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
||||
var endoff = doc.getLineEndOffset(lp.line)
|
||||
if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
|
||||
endoff--
|
||||
}
|
||||
val ecol = endoff - doc.getLineStartOffset(lp.line)
|
||||
val elp = editor.offsetToLogicalPosition(endoff)
|
||||
|
||||
msg.append("Col ").append(col + 1)
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1)
|
||||
}
|
||||
|
||||
msg.append(" of ").append(ecol + 1)
|
||||
if (ecol != elp.column) {
|
||||
msg.append("-").append(elp.column + 1)
|
||||
}
|
||||
|
||||
val lline = editor.caretModel.logicalPosition.line
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
|
||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total)
|
||||
|
||||
val cp = countWords(vimEditor)
|
||||
|
||||
msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
|
||||
|
||||
val offset = editor.caretModel.offset
|
||||
val size = editor.fileSize
|
||||
|
||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size)
|
||||
} else {
|
||||
msg.append("Selected ")
|
||||
|
||||
val vr = TextRange(
|
||||
editor.selectionModel.blockSelectionStarts,
|
||||
editor.selectionModel.blockSelectionEnds
|
||||
)
|
||||
vr.normalize()
|
||||
|
||||
val lines: Int
|
||||
var cp = countWords(vimEditor)
|
||||
val words = cp.count
|
||||
var word = 0
|
||||
if (vr.isMultiple) {
|
||||
lines = vr.size()
|
||||
val cols = vr.maxLength
|
||||
|
||||
msg.append(cols).append(" Cols; ")
|
||||
|
||||
for (i in 0 until vr.size()) {
|
||||
cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
|
||||
word += cp.count
|
||||
}
|
||||
} else {
|
||||
val slp = editor.offsetToLogicalPosition(vr.startOffset)
|
||||
val elp = editor.offsetToLogicalPosition(vr.endOffset)
|
||||
|
||||
lines = elp.line - slp.line + 1
|
||||
|
||||
cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
|
||||
word = cp.count
|
||||
}
|
||||
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
|
||||
msg.append(lines).append(" of ").append(total).append(" Lines")
|
||||
|
||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words")
|
||||
|
||||
val chars = vr.selectionCount
|
||||
val size = editor.fileSize
|
||||
|
||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString())
|
||||
}
|
||||
|
||||
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val msg = StringBuilder()
|
||||
|
@ -10,11 +10,9 @@ package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
@ -52,48 +50,6 @@ private fun containsUpperCase(pattern: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* This counts all the words in the file.
|
||||
*/
|
||||
fun countWords(
|
||||
vimEditor: VimEditor,
|
||||
start: Int = 0,
|
||||
end: Long = vimEditor.fileSize(),
|
||||
): CountPosition {
|
||||
val offset = vimEditor.currentCaret().offset
|
||||
|
||||
var count = 1
|
||||
var position = 0
|
||||
var last = -1
|
||||
var res = start
|
||||
while (true) {
|
||||
res = injector.searchHelper.findNextWord(vimEditor, res, 1, true, false)
|
||||
if (res == start || res == 0 || res > end || res == last) {
|
||||
break
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
if (res == offset) {
|
||||
position = count
|
||||
} else if (last < offset && res >= offset) {
|
||||
position = if (count == 2) {
|
||||
1
|
||||
} else {
|
||||
count - 1
|
||||
}
|
||||
}
|
||||
|
||||
last = res
|
||||
}
|
||||
|
||||
if (position == 0 && res == offset) {
|
||||
position = count
|
||||
}
|
||||
|
||||
return CountPosition(count, position)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the word under the cursor or the next word to the right of the cursor on the current line.
|
||||
*
|
||||
@ -193,9 +149,3 @@ private fun skip(iterator: IntIterator, n: Int) {
|
||||
var i = n
|
||||
while (i-- != 0 && iterator.hasNext()) iterator.nextInt()
|
||||
}
|
||||
|
||||
class CountPosition(val count: Int, val position: Int)
|
||||
|
||||
private val logger = logger<SearchLogger>()
|
||||
|
||||
private class SearchLogger
|
@ -9,80 +9,231 @@
|
||||
package org.jetbrains.plugins.ideavim.action
|
||||
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
// For all of these tests, note that Vim might show a different total byte count - off by one. This is ok, and not worth
|
||||
// adding a VimBehaviorDiffers annotation for.
|
||||
// It's because Vim requires each line to end with a linefeed character (otherwise it's not a line!) and adds one to
|
||||
// the last line. If the last line ends with a linefeed, that's just the end of the line. In this case, Vim does not
|
||||
// draw an empty line after the last line (because there isn't one!). If we hit enter at the end of the last line, Vim
|
||||
// adds a second linefeed, and there's now a new (empty) line at the end of the file, and the file ends with two
|
||||
// linefeed characters.
|
||||
// IntelliJ treats a linefeed at the end of the last line as a line feed, and draws an empty line. When we initialise
|
||||
// a test with a trailing empty line, IntelliJ only creates one linefeed char, instead of the two that Vim creates.
|
||||
// Maybe we should ensure that each file ends with a linefeed when initialising tests?
|
||||
@Suppress("SpellCheckingInspection")
|
||||
class FileGetLocationInfoActionTest : VimTestCase() {
|
||||
@VimBehaviorDiffers(originalVimAfter = "Col 1 of 11; Line 1 of 6; Word 1 of 32; Byte 1 of 166")
|
||||
@Test
|
||||
fun `test get file info`() {
|
||||
val keys = injector.parser.parseKeys("g<C-G>")
|
||||
val before = """
|
||||
${c}Lorem Ipsum
|
||||
|
||||
Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit
|
||||
Sed in orci mauris.
|
||||
Cras id tellus in ex imperdiet egestas.
|
||||
""".trimIndent()
|
||||
|${c}Lorem Ipsum
|
||||
|
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|consectetur adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText(keys)
|
||||
kotlin.test.assertEquals("Col 1 of 11; Line 1 of 6; Word 1 of 23; Character 1 of 128", VimPlugin.getMessage())
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 11; Line 1 of 6; Word 1 of 21; Byte 1 of 128", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter = "Col 1 of 11; Line 1 of 7; Word 1 of 32; Byte 1 of 167")
|
||||
@Test
|
||||
fun `test get file info with empty line`() {
|
||||
val keys = injector.parser.parseKeys("g<C-G>")
|
||||
val before = """
|
||||
${c}Lorem Ipsum
|
||||
|
||||
Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit
|
||||
Sed in orci mauris.
|
||||
Cras id tellus in ex imperdiet egestas.
|
||||
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(keys)
|
||||
kotlin.test.assertEquals("Col 1 of 11; Line 1 of 7; Word 1 of 24; Character 1 of 129", VimPlugin.getMessage())
|
||||
fun `test get file info with single word`() {
|
||||
configureByText("Lorem")
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 5; Line 1 of 1; Word 1 of 1; Byte 1 of 5", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info of two separate words`() {
|
||||
configureByText("Lorem ipsum")
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 11; Line 1 of 1; Word 1 of 2; Byte 1 of 11", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info of one WORD containing non-word characters`() {
|
||||
configureByText("Lorem,,,,,ipsum")
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 15; Line 1 of 1; Word 1 of 1; Byte 1 of 15", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info of words on multiple lines`() {
|
||||
val before = """
|
||||
|Lorem ipsum dolor sit amet
|
||||
|cons${c}ectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 5 of 27; Line 2 of 2; Word 6 of 8; Byte 32 of 54", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info of words with trailing punctuation on multiple lines`() {
|
||||
val before = """
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|cons${c}ectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 5 of 27; Line 2 of 2; Word 6 of 8; Byte 33 of 55", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info of words with empty lines`() {
|
||||
val before = """
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|
|
||||
|${c}
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 0; Line 4 of 5; Word 5 of 8; Byte 31 of 58", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info on empty line shows zero columns`() {
|
||||
val before = """
|
||||
|Lorem Ipsum
|
||||
|${c}
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|consectetur adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
|
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 0; Line 2 of 7; Word 2 of 21; Byte 13 of 129", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter = "Col 1 of 40; Line 4 of 7; Word 12 of 32; Byte 55 of 167")
|
||||
@Test
|
||||
fun `test get file info in the middle`() {
|
||||
val keys = injector.parser.parseKeys("g<C-G>")
|
||||
val before = """
|
||||
Lorem Ipsum
|
||||
|
||||
Lorem ipsum dolor sit amet,
|
||||
all rocks ${c}and lavender and tufted grass,
|
||||
Sed in orci mauris.
|
||||
Cras id tellus in ex imperdiet egestas.
|
||||
|
||||
""".trimIndent()
|
||||
|Lorem Ipsum
|
||||
|
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|consectetur ${c}adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
|
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText(keys)
|
||||
kotlin.test.assertEquals("Col 11 of 40; Line 4 of 7; Word 11 of 28; Character 52 of 142", VimPlugin.getMessage())
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 13 of 27; Line 4 of 7; Word 9 of 21; Byte 54 of 129", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter = "Col 1 of 0; Line 7 of 7; Word 32 of 32; Byte 167 of 167")
|
||||
@Test
|
||||
fun `test get file info on the last line`() {
|
||||
val keys = injector.parser.parseKeys("g<C-G>")
|
||||
val before = """
|
||||
Lorem Ipsum
|
||||
|
||||
Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit
|
||||
Sed in orci mauris.
|
||||
Cras id tellus in ex imperdiet egestas.
|
||||
$c
|
||||
""".trimIndent()
|
||||
|Lorem Ipsum
|
||||
|
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|consectetur adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
|$c
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText(keys)
|
||||
kotlin.test.assertEquals("Col 1 of 1; Line 7 of 7; Word 24 of 24; Character 130 of 129", VimPlugin.getMessage())
|
||||
typeText("g<C-G>")
|
||||
assertEquals("Col 1 of 0; Line 7 of 7; Word 21 of 21; Byte 130 of 129", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info with single word selected`() {
|
||||
val before = """
|
||||
|Lorem ${c}ipsum dolor sit amet
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("ve", "g<C-G>")
|
||||
assertEquals("Selected 1 of 2 Lines; 1 of 8 Words; 5 of 54 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info with multiple words selected`() {
|
||||
val before = """
|
||||
|Lorem ${c}ipsum dolor sit amet
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("v2e", "g<C-G>")
|
||||
assertEquals("Selected 1 of 2 Lines; 2 of 8 Words; 11 of 54 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info with single WORD selected`() {
|
||||
val before = """
|
||||
|Lorem ${c}ipsum,,,,dolor sit amet
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("vE", "g<C-G>")
|
||||
assertEquals("Selected 1 of 2 Lines; 1 of 7 Words; 14 of 57 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info across multiple selected lines`() {
|
||||
val before = """
|
||||
|Lorem Ipsum
|
||||
|
|
||||
|Lorem ${c}ipsum dolor sit amet,
|
||||
|consectetur adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("v", "jj", "g<C-G>")
|
||||
assertEquals("Selected 3 of 6 Lines; 9 of 21 Words; 57 of 128 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info with empty lines selected`() {
|
||||
val before = """
|
||||
|Lorem ${c}ipsum dolor sit amet,
|
||||
|
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("vG", "g<C-G>")
|
||||
assertEquals("Selected 5 of 5 Lines; 5 of 8 Words; 26 of 58 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info with linewise selection`() {
|
||||
val before = """
|
||||
|Lorem Ipsum
|
||||
|
|
||||
|Lorem ${c}ipsum dolor sit amet,
|
||||
|consectetur adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("V", "jj", "g<C-G>")
|
||||
assertEquals("Selected 3 of 6 Lines; 12 of 21 Words; 76 of 128 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test get file info with blockwise selection`() {
|
||||
val before = """
|
||||
|Lorem Ipsum
|
||||
|
|
||||
|Lorem ${c}ipsum dolor sit amet,
|
||||
|consectetur adipiscing elit
|
||||
|Sed in orci mauris.
|
||||
|Cras id tellus in ex imperdiet egestas.
|
||||
""".trimMargin()
|
||||
configureByText(before)
|
||||
typeText("<C-V>", "jjj", "llll", "g<C-G>")
|
||||
assertEquals("Selected 5 Cols; 4 of 6 Lines; 5 of 21 Words; 20 of 128 Bytes", VimPlugin.getMessage())
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ package com.maddyhome.idea.vim.api
|
||||
interface VimFile {
|
||||
fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean)
|
||||
fun displayHexInfo(editor: VimEditor)
|
||||
fun displayLocationInfo(vimEditor: VimEditor)
|
||||
fun displayLocationInfo(editor: VimEditor)
|
||||
fun selectPreviousTab(context: ExecutionContext)
|
||||
fun saveFile(editor: VimEditor, context: ExecutionContext)
|
||||
fun saveFiles(editor: VimEditor, context: ExecutionContext)
|
||||
|
@ -8,6 +8,13 @@
|
||||
|
||||
package com.maddyhome.idea.vim.api
|
||||
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.helper.CharacterHelper
|
||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
|
||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import java.lang.Long.toHexString
|
||||
|
||||
abstract class VimFileBase : VimFile {
|
||||
@ -17,4 +24,106 @@ abstract class VimFileBase : VimFile {
|
||||
|
||||
injector.messages.showStatusBarMessage(editor, toHexString(ch.code.toLong()))
|
||||
}
|
||||
|
||||
override fun displayLocationInfo(editor: VimEditor) {
|
||||
val msg = buildString {
|
||||
|
||||
val caret = editor.currentCaret()
|
||||
val offset = editor.currentCaret().offset
|
||||
val totalByteCount = editor.fileSize()
|
||||
val totalWordCount = countBigWords(editor, offset)
|
||||
|
||||
if (!editor.inVisualMode) {
|
||||
val pos = caret.getBufferPosition()
|
||||
val line = pos.line + 1
|
||||
val col = pos.column + 1
|
||||
val lineEndOffset = editor.getLineEndOffset(pos.line)
|
||||
val lineEndCol = editor.offsetToBufferPosition(lineEndOffset).column
|
||||
|
||||
// Note that Vim can have different screen columns to buffer columns, and displays these in the form "Col 1-3".
|
||||
// Vim uses screen columns to insert inlay text and symbols such as word wrap indicators, but has a single line
|
||||
// even when wrapped, unlike IntelliJ, which has a virtual line per screen line. Because of this, it's not clear
|
||||
// how IdeaVim could represent this, or even if it would be worthwhile to do so.
|
||||
append("Col ").append(col).append(" of ").append(lineEndCol)
|
||||
append("; Line ").append(line).append(" of ").append(editor.lineCount())
|
||||
append("; Word ").append(totalWordCount.currentWord).append(" of ").append(totalWordCount.count)
|
||||
append("; Byte ").append(offset + 1).append(" of ").append(totalByteCount)
|
||||
}
|
||||
else {
|
||||
|
||||
append("Selected ")
|
||||
|
||||
val selection = VimSelection.create(
|
||||
caret.vimSelectionStart,
|
||||
caret.offset,
|
||||
editor.mode.selectionType ?: CHARACTER_WISE,
|
||||
editor
|
||||
).toVimTextRange()
|
||||
|
||||
val selectedLineCount: Int
|
||||
val selectedWordCount: Int
|
||||
if (selection.isMultiple) {
|
||||
selectedLineCount = selection.size()
|
||||
|
||||
var count = 0
|
||||
for (i in 0 until selection.size()) {
|
||||
val wordCount = countBigWords(editor, offset, selection.startOffsets[i], selection.endOffsets[i])
|
||||
count += wordCount.count
|
||||
}
|
||||
selectedWordCount = count
|
||||
|
||||
append(selection.maxLength).append(" Cols; ")
|
||||
}
|
||||
else {
|
||||
val startPos = editor.offsetToBufferPosition(selection.startOffset)
|
||||
val endPos = editor.offsetToBufferPosition(selection.endOffsetInclusive)
|
||||
|
||||
selectedLineCount = endPos.line - startPos.line + 1
|
||||
|
||||
val wordCount = countBigWords(editor, offset, selection.startOffset, selection.endOffset)
|
||||
selectedWordCount = wordCount.count
|
||||
}
|
||||
|
||||
append(selectedLineCount).append(" of ").append(editor.lineCount()).append(" Lines; ")
|
||||
append(selectedWordCount).append(" of ").append(totalWordCount.count).append(" Words; ")
|
||||
append(selection.selectionCount).append(" of ").append(totalByteCount).append(" Bytes")
|
||||
}
|
||||
}
|
||||
|
||||
injector.messages.showStatusBarMessage(editor, msg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of WORDs that intersect the given range, or exist in the whole file
|
||||
*/
|
||||
private fun countBigWords(
|
||||
editor: VimEditor,
|
||||
caretOffset: Int,
|
||||
start: Int = 0,
|
||||
endExclusive: Int = editor.text().length,
|
||||
): WordCount {
|
||||
|
||||
val chars = editor.text()
|
||||
var wordCount = 0
|
||||
var currentWord = 0
|
||||
|
||||
var lastCharacterType: CharacterHelper.CharacterType? = null
|
||||
for (pos in start until endExclusive) {
|
||||
val characterType = charType(editor, chars[pos], punctuationAsLetters = true)
|
||||
if (characterType != lastCharacterType && characterType != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
wordCount++
|
||||
}
|
||||
|
||||
// The current word is the last counted word
|
||||
if (pos <= caretOffset) {
|
||||
currentWord = wordCount
|
||||
}
|
||||
|
||||
lastCharacterType = characterType
|
||||
}
|
||||
|
||||
return WordCount(wordCount, currentWord)
|
||||
}
|
||||
|
||||
private data class WordCount(val count: Int, val currentWord: Int)
|
||||
|
Loading…
Reference in New Issue
Block a user