1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-10 15:40:37 +02:00

Rename Range and related classes to Address

An address evaluates to a line, and a range is a collection of addresses
This commit is contained in:
Matt Ellis 2024-03-27 11:22:27 +00:00 committed by Alex Pláte
parent ab7359ffd3
commit 751f51c88f
6 changed files with 216 additions and 176 deletions
src
main/java/com/maddyhome/idea/vim/vimscript/parser/visitors
test/java/org/jetbrains/plugins/ideavim/ex
vim-engine/src/main/kotlin/com/maddyhome/idea/vim
ex/ranges
vimscript/model/functions

View File

@ -12,8 +12,8 @@ import com.intellij.openapi.diagnostic.logger
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.ranges.Range
import com.maddyhome.idea.vim.ex.ranges.Range.Companion.createRange
import com.maddyhome.idea.vim.ex.ranges.Address
import com.maddyhome.idea.vim.ex.ranges.Address.Companion.createRangeAddresses
import com.maddyhome.idea.vim.ex.ranges.Ranges
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.vimscript.model.commands.ActionCommand
@ -147,22 +147,22 @@ internal object CommandVisitor : VimscriptBaseVisitor<Command>() {
}
}
private fun parseRangesUnit(ctx: VimscriptParser.RangeUnitContext): Array<Range> {
private fun parseRangesUnit(ctx: VimscriptParser.RangeUnitContext): Array<Address> {
val valueAndOffset = parseRangeExpression(ctx.rangeExpression())
val move = ctx.rangeSeparator()?.text == ";"
val ranges = createRange(valueAndOffset.first, valueAndOffset.second, move)
if (ranges == null) {
logger.warn("Could not create a range for node ${ctx.text}")
throw ExException("Could not create a range ${ctx.text}")
val addresses = createRangeAddresses(valueAndOffset.first, valueAndOffset.second, move)
if (addresses == null) {
logger.warn("Could not create an address for node ${ctx.text}")
throw ExException("Could not create an address ${ctx.text}")
}
return ranges
return addresses
}
private fun parseRanges(ctx: RangeContext?): Ranges {
val ranges = Ranges()
if (ctx?.rangeUnit() != null) {
for (unit in ctx.rangeUnit()) {
ranges.addRange(parseRangesUnit(unit))
ranges.addAddresses(parseRangesUnit(unit))
}
}
return ranges
@ -221,8 +221,8 @@ internal object CommandVisitor : VimscriptBaseVisitor<Command>() {
ranges = parseRanges(ctx.range())
} else {
ranges = Ranges()
ranges.addRange(
createRange(ctx.shortRange().text, 0, false)
ranges.addAddresses(
createRangeAddresses(ctx.shortRange().text, 0, false)
?: throw ExException("Could not create a range"),
)
}

View File

@ -12,7 +12,7 @@ import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
class RangeTest : VimTestCase() {
class AddressTest : VimTestCase() {
@Test
fun testNoRange() {
configureByText("1\n2\n<caret>3\n4\n5\n")

View File

@ -8,8 +8,8 @@
package org.jetbrains.plugins.ideavim.ex.parser.commands
import com.maddyhome.idea.vim.ex.ranges.LineNumberRange
import com.maddyhome.idea.vim.ex.ranges.MarkRange
import com.maddyhome.idea.vim.ex.ranges.LineAddress
import com.maddyhome.idea.vim.ex.ranges.MarkAddress
import com.maddyhome.idea.vim.vimscript.model.commands.BufferCommand
import com.maddyhome.idea.vim.vimscript.model.commands.DeleteLinesCommand
import com.maddyhome.idea.vim.vimscript.model.commands.EchoCommand
@ -80,8 +80,8 @@ class CommandTests : VimTestCase() {
assertEquals("s", command.command)
assertEquals("/a/b/g", command.argument)
assertEquals(2, command.ranges.size())
assertEquals(MarkRange('a', 0, false), command.ranges.ranges[0])
assertEquals(MarkRange('b', 0, false), command.ranges.ranges[1])
assertEquals(MarkAddress('a', 0, false), command.ranges.addresses[0])
assertEquals(MarkAddress('b', 0, false), command.ranges.addresses[1])
}
// https://github.com/JetBrains/ideavim/discussions/386
@ -100,8 +100,8 @@ class CommandTests : VimTestCase() {
val command = VimscriptParser.parseCommand("10$sp1,${sp2}20${sp3}d")
assertTrue(command is DeleteLinesCommand)
assertEquals(2, command.ranges.size())
assertEquals(LineNumberRange(9, 0, false), command.ranges.ranges[0])
assertEquals(LineNumberRange(19, 0, false), command.ranges.ranges[1])
assertEquals(LineAddress(9, 0, false), command.ranges.addresses[0])
assertEquals(LineAddress(19, 0, false), command.ranges.addresses[1])
}
// VIM-2450

View File

@ -13,50 +13,90 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.Direction
import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger
import org.jetbrains.annotations.TestOnly
import java.util.*
/**
* Base for all Ex command ranges
* Base for all Ex command addresses
*
* An address is a way of specifying a line number, either explicitly by number, or with a symbol to represent current
* line, last line, etc. An address can also have an offset, to allow for relative counting. A range is a collection of
* addresses. Each address is separated either with a comma, or a semicolon. If separated by a semicolon, the caret is
* moved after evaluating the line number.
*
* A range matching the whole file is represented by `%`, which is evaluated as two addresses, for the first and last
* lines in the file.
*
* See `:help range` and `:help {address}`.
*
* @param offset The relative offset added or subtracted from the evaluated line number
* @param isMove True if the caret should be moved after evaluating this address. In the text representation, a
* semicolon follows this address.
*/
public sealed class Range(
// Line offset
protected val offset: Int,
public val isMove: Boolean,
) {
public sealed class Address(public val offset: Int, public val isMove: Boolean) {
/**
* Gets the line number (0 based) specificied by this range. Includes the offset.
* Gets the zero-based line number specified by this address.
*
* If the range included an offset (`+1`, `-1`), this is applied to the returned line number.
*
* Note that the user will have used one-based line numbers, but internally, we use zero-based. This conversion is
* automatically handled.
*
* @param editor The editor to get the line for
* @param lastZero True if last line was set to start of file
* @return The zero based line number, -1 if unable to get the line number
* @param lastZero True if the last line was set to start of file
* @return The zero-based line number or -1 if unable to get the line number
*/
public fun getLine(editor: VimEditor, lastZero: Boolean): Int {
val line = getRangeLine(editor, lastZero)
// TODO: Only apply offset if calculateLine returns a valid line number
val line = calculateLine(editor, lastZero)
return line + offset
}
/**
* Gets the zero-based line number specified by this address.
*
* If the range included an offset (`+1`, `-1`), this is applied to the returned line number.
*
* Note that the user will have used one-based line numbers, but internally, we use zero-based. This conversion is
* automatically handled.
*
* @param editor The editor to get the line for
* @param caret The caret to use for the current line or initial search line, if required
* @param lastZero True if the last line was set to start of file
* @return The zero-based line number or -1 if unable to get the line number
*/
public fun getLine(editor: VimEditor, caret: ImmutableVimCaret, lastZero: Boolean): Int {
return if (offset == 0) getRangeLine(editor, lastZero) else getRangeLine(editor, caret, lastZero) + offset
// TODO: Why does this not pass through caret?
// TODO: Only apply offset if calculateLine returns a valid line number
return if (offset == 0) calculateLine(editor, lastZero) else calculateLine(editor, caret, lastZero) + offset
}
override fun toString(): String = "Range{offset=$offset, move=$isMove}"
/**
* Gets the line number specified by this range without regard to any offset.
* Calculate the line number specified by this address. Does not apply offset
*
* @param editor The editor to get the line for
* @param lastZero True if last line was set to start of file
* @return The zero based line number, -1 if inable to get the line number
* @param lastZero True if the last line was set to start of file
* @return The zero-based line number, -1 if unable to get the line number
*/
protected abstract fun getRangeLine(editor: VimEditor, lastZero: Boolean): Int
protected abstract fun calculateLine(editor: VimEditor, lastZero: Boolean): Int
protected abstract fun getRangeLine(editor: VimEditor, caret: ImmutableVimCaret, lastZero: Boolean): Int
/**
* Calculate the line number specified by this address. Does not apply offset
*
* @param editor The editor to get the line for
* @param caret The caret to use for initial search offset, or to get the current line, etc.
* @param lastZero True if the last line was set to start of file
* @return The zero-based line number, -1 if unable to get the line number
*/
protected abstract fun calculateLine(editor: VimEditor, caret: ImmutableVimCaret, lastZero: Boolean): Int
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Range) return false
if (other !is Address) return false
if (offset != other) return false
if (offset != other.offset) return false
if (isMove != other.isMove) return false
return true
@ -74,31 +114,30 @@ public sealed class Range(
*
* @param str The range text
* @param offset Any offset specified after the range
* @param move True if cursor should be moved to range line
* @param move True if the cursor should be moved to the line that the address evaluates to
* @return The ranges appropriate to the text
*/
@JvmStatic
public fun createRange(str: String, offset: Int, move: Boolean): Array<Range>? {
public fun createRangeAddresses(str: String, offset: Int, move: Boolean): Array<Address>? {
// Current line
if (str.isEmpty() || str == ".") {
return arrayOf(LineNumberRange(offset, move))
return arrayOf(LineAddress(offset, move))
} else if (str == "%") {
return arrayOf(
LineNumberRange(0, 0, move),
LineNumberRange(LineNumberRange.LAST_LINE, offset, move),
LineAddress(0, 0, move),
LineAddress(LineAddress.LAST_LINE, offset, move),
)
} else if (str == "$") {
return arrayOf(LineNumberRange(LineNumberRange.LAST_LINE, offset, move))
return arrayOf(LineAddress(LineAddress.LAST_LINE, offset, move))
} else if (str.startsWith("'") && str.length == 2) {
return arrayOf(MarkRange(str[1], offset, move))
return arrayOf(MarkAddress(str[1], offset, move))
} else if (str.startsWith("/") || str.startsWith("\\/") || str.startsWith("\\&")) {
return arrayOf(SearchRange(str, offset, move))
return arrayOf(SearchAddress(str, offset, move))
} else if (str.startsWith("?") || str.startsWith("\\?")) {
return arrayOf(SearchRange(str, offset, move))
return arrayOf(SearchAddress(str, offset, move))
} else {
try {
val line = str.toInt() - 1
return arrayOf(LineNumberRange(line, offset, move))
val line = str.toInt() - 1 // Convert to 0-based line
return arrayOf(LineAddress(line, offset, move))
} catch (e: NumberFormatException) { // Ignore - we'll send back bad range later.
}
}
@ -111,12 +150,13 @@ public sealed class Range(
/**
* Represents a specific line, the current line, or the last line of a file
*/
public class LineNumberRange : Range {
@TestOnly // Should be private. Constructor is public for test purposes only
public class LineAddress : Address {
/**
* Create a range for the current line
*
* @param offset The range offset
* @param move True if cursor should be moved
* @param move True if the cursor should be moved
*/
public constructor(offset: Int, move: Boolean) : super(offset, move) {
line = CURRENT_LINE
@ -126,20 +166,13 @@ public class LineNumberRange : Range {
* Create a range for the given line
*
* @param offset The range offset
* @param move True if cursor should be moved
* @param move True if the cursor should be moved
*/
public constructor(line: Int, offset: Int, move: Boolean) : super(offset, move) {
this.line = line
}
/**
* Gets the line number specified by this range without regard to any offset.
*
* @param editor The editor to get the line for
* @param lastZero True if last line was set to start of file
* @return The zero based line number, -1 for start of file
*/
override fun getRangeLine(editor: VimEditor, lastZero: Boolean): Int {
override fun calculateLine(editor: VimEditor, lastZero: Boolean): Int {
if (line == CURRENT_LINE) {
line = editor.currentCaret().getBufferPosition().line
} else if (line == LAST_LINE) {
@ -148,32 +181,24 @@ public class LineNumberRange : Range {
return line
}
override fun getRangeLine(
override fun calculateLine(
editor: VimEditor,
caret: ImmutableVimCaret,
lastZero: Boolean,
): Int {
// TODO: This doesn't match the behaviour in the other overload
line = if (line == LAST_LINE) editor.lineCount() - 1 else caret.getBufferPosition().line
return line
}
override fun toString(): String = "LineNumberRange[line=$line, ${super.toString()}]"
override fun toString(): String = "LineAddress[line=$line, ${super.toString()}]"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LineNumberRange
if (line != other.line) return false
if (offset != other.offset) return false
if (isMove != other.isMove) return false
return true
return super.equals(other) && (other as? LineAddress)?.line == this.line
}
override fun hashCode(): Int {
val prime = 31
return isMove.hashCode() + prime * offset.hashCode() + prime * prime * line.hashCode()
return super.hashCode() + 31 * line
}
private var line: Int
@ -187,52 +212,41 @@ public class LineNumberRange : Range {
/**
* Represents the line specified by a mark
*/
public class MarkRange(private val mark: Char, offset: Int, move: Boolean) : Range(offset, move) {
/**
* Gets the line number specified by this range without regard to any offset.
*
* @param editor The editor to get the line for
* @param lastZero True if last line was set to start of file
* @return The zero based line number, -1 if there is no such mark set in the file
*/
override fun getRangeLine(editor: VimEditor, lastZero: Boolean): Int {
val mark = injector.markService.getMark(editor.currentCaret(), mark)
return mark?.line ?: -1
@TestOnly // Should be private. Constructor is visible for test purposes only
public class MarkAddress(private val mark: Char, offset: Int, move: Boolean) : Address(offset, move) {
override fun calculateLine(editor: VimEditor, lastZero: Boolean): Int {
return injector.markService.getMark(editor.currentCaret(), this.mark)?.line ?: -1
}
override fun getRangeLine(editor: VimEditor, caret: ImmutableVimCaret, lastZero: Boolean): Int = getRangeLine(editor, lastZero)
override fun calculateLine(editor: VimEditor, caret: ImmutableVimCaret, lastZero: Boolean): Int {
// TODO: Why is this not passing through the caret?
return calculateLine(editor, lastZero)
}
override fun toString(): String = "MarkAddress[mark=$mark, ${super.toString()}]"
override fun toString(): String = "MarkRange[mark=$mark, ${super.toString()}]"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MarkRange
if (mark != other.mark) return false
if (offset != other.offset) return false
if (isMove != other.isMove) return false
return true
return super.equals(other) && (other as? MarkAddress)?.mark == this.mark
}
override fun hashCode(): Int {
val prime = 31
return prime * prime * mark.hashCode() + prime * offset.hashCode() + isMove.hashCode()
return super.hashCode() + 31 * mark.hashCode()
}
}
/**
* Represents a range given by a search pattern. The pattern can be '\\/', '\\?', '\\&amp;', /{pattern}/,
* or ?{pattern}?. The last two can be repeated 0 or more times after any of the others.
* or ?{pattern}?. The last two can be repeated zero or more times after any of the others.
*/
public class SearchRange(pattern: String, offset: Int, move: Boolean) : Range(offset, move) {
/**
* Parses the pattern into a list of subpatterns and flags
*
* @param pattern The full search pattern
*/
private fun setPattern(pattern: String) {
private class SearchAddress(pattern: String, offset: Int, move: Boolean) : Address(offset, move) {
private companion object {
private val logger = vimLogger<SearchAddress>()
}
private val patterns: MutableList<String?> = mutableListOf()
private val directions: MutableList<Direction> = mutableListOf()
init {
logger.debug { "pattern=$pattern" }
// Search range patterns such as `/one//two/` will be separated by a NULL character, rather than handled as separate
// ranges. A range with an offset, such as `/one/+3/two/` will be treated as two ranges.
@ -269,21 +283,15 @@ public class SearchRange(pattern: String, offset: Int, move: Boolean) : Range(of
}
}
/**
* Gets the line number specified by this range without regard to any offset.
*
* @param editor The editor to get the line for
* @param lastZero True if last line was set to start of file
* @return The zero based line number, -1 if the text was not found
*/
override fun getRangeLine(
override fun calculateLine(
editor: VimEditor,
lastZero: Boolean,
): Int { // Each subsequent pattern is searched for starting in the line after the previous search match
return getRangeLine(editor, editor.currentCaret(), lastZero)
): Int {
// Each subsequent pattern is searched for starting in the line after the previous search match
return calculateLine(editor, editor.currentCaret(), lastZero)
}
override fun getRangeLine(
override fun calculateLine(
editor: VimEditor,
caret: ImmutableVimCaret,
lastZero: Boolean,
@ -300,8 +308,11 @@ public class SearchRange(pattern: String, offset: Int, move: Boolean) : Range(of
// lastPatternOffset is updated for future searches
val patternOffset = if (i == patterns.size - 1) offset else 0
// Note that wrapscan, ignorecase, etc. all come from current option values, as expected
searchOffset = getSearchOffset(editor, line, direction, lastZero)
searchOffset = injector.searchGroup.processSearchRange(editor, pattern!!, patternOffset, searchOffset, direction)
// TODO: Vim throws E385 if it can't find a result and wrapscan isn't set
// TODO: Vim throws E486 if it can't find a result with wrapscan set - IdeaVim does the same
if (searchOffset == -1) break
line = editor.offsetToBufferPosition(searchOffset).line
}
@ -309,6 +320,15 @@ public class SearchRange(pattern: String, offset: Int, move: Boolean) : Range(of
}
private fun getSearchOffset(editor: VimEditor, line: Int, direction: Direction, lastZero: Boolean): Int {
// TODO: I'm not sure this is correct
// lastZero is true if we have an address that evaluates to less than 0. I'm not sure of the circumstances when this
// is expected to be true. It can be true if there are no matches to search, or for something like `1-20` (first
// line, with an offset of minus 20). This would mean that the next search starts at the beginning of the "current"
// line, rather than the following line.
// This leads to behaviour such as `/foo/-20/foo/d` to delete the first line (assuming 'foo' is in the first line),
// which doesn't work in Vim.
// Firstly, we should only return a negative value for an error, which would mean that lastZero is only set when the
// last address cannot be resolved (cannot find search or no defined mark)
return if (direction == Direction.FORWARDS && !lastZero) {
injector.motion.moveCaretToLineEnd(editor, line, true)
} else {
@ -316,38 +336,13 @@ public class SearchRange(pattern: String, offset: Int, move: Boolean) : Range(of
}
}
override fun toString(): String = "SearchRange[patterns=$patterns, ${super.toString()}]"
override fun toString(): String = "SearchAddress[patterns=$patterns, ${super.toString()}]"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as SearchRange
if (patterns != other.patterns) return false
if (directions != other.directions) return false
if (offset != other.offset) return false
if (isMove != other.isMove) return false
return true
return super.equals(other) && (other as? SearchAddress)?.patterns == this.patterns
}
override fun hashCode(): Int {
var result = patterns.hashCode()
result = 31 * result + directions.hashCode()
result = 31 * result + offset.hashCode()
result = 31 * result + isMove.hashCode()
return result
}
private val patterns: MutableList<String?> = mutableListOf()
private val directions: MutableList<Direction> = mutableListOf()
public companion object {
private val logger = vimLogger<SearchRange>()
}
init {
setPattern(pattern)
return super.hashCode() + 31 * patterns.hashCode()
}
}

View File

@ -13,19 +13,26 @@ import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.TextRange
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.TestOnly
import kotlin.math.min
/**
* Handles the set of range values entered as part of an Ex command.
*/
public class Ranges {
// This property should be private, but is used in tests
@TestOnly
public val addresses: MutableList<Address> = mutableListOf()
private var defaultLine = -1
/** Adds a range to the list */
public fun addRange(range: Array<Range>) {
ranges.addAll(range)
public fun addAddresses(range: Array<Address>) {
addresses.addAll(range)
}
/** Gets the number of ranges in the list */
public fun size(): Int = ranges.size
public fun size(): Int = addresses.size
/**
* Sets the default line to be used by this range if no range was actually given by the user. -1 is used to
@ -38,7 +45,7 @@ public class Ranges {
}
/**
* Gets the line of the last range specified in the range list
* If a command expects a line, Vim uses the last line of any range passed to the command
*
* @param editor The editor to get the line for
* @return The line number represented by the range
@ -48,36 +55,61 @@ public class Ranges {
return endLine
}
/**
* If a command expects a line, Vim uses the last line of any range passed to the command
*
* @param editor The editor to get the line for
* @param caret The caret to use for current line, initial search line, etc. if required
* @return The line number represented by the range
*/
public fun getLine(editor: VimEditor, caret: VimCaret): Int {
processRange(editor, caret)
return endLine
}
// TODO: Consider removing this
// Most commands use either a range or a line which is the last line in a range. There should be no need to get the
// first line separate from a range
public fun getFirstLine(editor: VimEditor, caret: VimCaret): Int {
processRange(editor, caret)
return startLine
}
/**
* Gets the count for an Ex command. This is either an explicit count enter at the end of the command or the
* end of the specified range.
* If a command expects a count, Vim uses the last line of the range passed to the command
*
* Note that the command may also have a count passed as an argument, which takes precedence over any range. This
* function only returns the count from the range. It is up to the caller to decide which count to use.
*
* @param editor The editor to get the count for
* @param count The count given at the end of the command or -1 if no such count (use end line)
* @param count The count given at the end of the command or -1 if not provided
* @return count if count != -1, else return end line of range
*/
public fun getCount(editor: VimEditor, count: Int): Int = if (count == -1) getLine(editor) else count
public fun getCount(editor: VimEditor, count: Int): Int {
return if (count == -1) getLine(editor) else count
}
/**
* If a command expects a count, Vim uses the last line of the range passed to the command
*
* Note that the command may also have a count passed as an argument, which takes precedence over any range. This
* function only returns the count from the range. It is up to the caller to decide which count to use.
*
* @param editor The editor to get the count for
* @param caret The caret to use for current line, initial search line, etc. if required
* @param count The count given at the end of the command or -1 if not provided
* @return count if count != -1, else return end line of range
*/
public fun getCount(editor: VimEditor, caret: VimCaret, count: Int): Int {
return if (count == -1) getLine(editor, caret) else count
}
/**
* Gets the line range represented by this range. If a count is given, the range is the range end line through
* Gets the line range represented by this Ex range. If a count is given, the range is the range end line through
* count-1 lines. If no count is given (-1), the range is the range given by the user.
*
* @param editor The editor to get the range for
* @param count The count given at the end of the command or -1 if no such count
* @param count The count given at the end of the command or -1 if not provided
* @return The line range
*/
public fun getLineRange(editor: VimEditor, count: Int): LineRange {
@ -108,6 +140,8 @@ public class Ranges {
* @param count The count given at the end of the command or -1 if no such count
* @return The text range
*/
// TODO: Consider removing this
// TextRange isn't a Vim range, but offsets, so isn't related to Ranges. Consider an extension function on LineRange
public fun getTextRange(editor: VimEditor, count: Int): TextRange {
val lr = getLineRange(editor, count)
val start = editor.getLineStartOffset(lr.startLine)
@ -115,6 +149,8 @@ public class Ranges {
return TextRange(start, min(end, editor.fileSize().toInt()))
}
// TODO: Consider removing this
// TextRange isn't a Vim range, but offsets, so isn't related to Ranges. Consider an extension function on LineRange
public fun getTextRange(editor: VimEditor, caret: VimCaret, count: Int): TextRange {
val lineRange = getLineRange(editor, caret, count)
val start = editor.getLineStartOffset(lineRange.startLine)
@ -130,24 +166,29 @@ public class Ranges {
private fun processRange(editor: VimEditor) {
// Already done
if (done) return
// Start with the range being the current line
startLine = if (defaultLine == -1) editor.currentCaret().getBufferPosition().line else defaultLine
endLine = startLine
var lastZero = false
// Now process each range, moving the cursor if appropriate
for (range in ranges) {
// Now process each address in the range, moving the cursor if appropriate
for (address in addresses) {
startLine = endLine
endLine = range.getLine(editor, lastZero)
if (range.isMove) {
endLine = address.getLine(editor, lastZero)
if (address.isMove) {
editor.primaryCaret().moveToOffset(
injector.motion.moveCaretToLineWithSameColumn(editor, endLine, editor.primaryCaret()),
)
}
// Did that last range represent the start of the file?
// TODO: Reconsider lastZero. I don't think it helps, and might actually cause problems
// Did that last address represent the start of the file?
lastZero = endLine < 0
count++
}
// If only one range given, make the start and end the same
// If only one address is given, make the start and end the same
if (count == 1) {
startLine = endLine
}
@ -155,26 +196,32 @@ public class Ranges {
}
private fun processRange(editor: VimEditor, caret: VimCaret) {
// Start with the range being the current line
startLine = if (defaultLine == -1) caret.getBufferPosition().line else defaultLine
endLine = startLine
// Now process each address in the range, moving the cursor if appropriate
var lastZero = false
for (range in ranges) {
for (address in addresses) {
startLine = endLine
endLine = range.getLine(editor, caret, lastZero)
if (range.isMove) {
caret.moveToOffset(
injector.motion.moveCaretToLineWithSameColumn(editor, endLine, editor.primaryCaret()),
)
endLine = address.getLine(editor, caret, lastZero)
if (address.isMove) {
caret.moveToOffset(injector.motion.moveCaretToLineWithSameColumn(editor, endLine, editor.primaryCaret()))
}
// TODO: Reconsider lastZero. I don't think it helps, and might actually cause problems
// Did that last address represent the start of the file?
lastZero = endLine < 0
++count
}
// If only one address is given, make the start and end the same
if (count == 1) startLine = endLine
count = 0
}
@NonNls
override fun toString(): String = "Ranges[ranges=$ranges]"
override fun toString(): String = "Ranges[addresses=$addresses]"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Ranges) return false
@ -184,7 +231,7 @@ public class Ranges {
if (count != other.count) return false
if (defaultLine != other.defaultLine) return false
if (done != other.done) return false
if (ranges != other.ranges) return false
if (addresses != other.addresses) return false
return true
}
@ -195,14 +242,12 @@ public class Ranges {
result = 31 * result + count
result = 31 * result + defaultLine
result = 31 * result + done.hashCode()
result = 31 * result + ranges.hashCode()
result = 31 * result + addresses.hashCode()
return result
}
private var startLine = 0
private var endLine = 0
private var count = 0
private var defaultLine = -1
private var done = false
public var ranges: MutableList<Range> = mutableListOf()
}

View File

@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.FinishException
import com.maddyhome.idea.vim.ex.ranges.LineNumberRange
import com.maddyhome.idea.vim.ex.ranges.LineAddress
import com.maddyhome.idea.vim.ex.ranges.Ranges
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
import com.maddyhome.idea.vim.vimscript.model.VimLContext
@ -46,10 +46,10 @@ public data class DefinedFunctionHandler(val function: FunctionDeclaration) : Fu
if (!isRangeGiven) {
val currentLine = editor.currentCaret().getBufferPosition().line
ranges = Ranges()
ranges!!.addRange(
ranges!!.addAddresses(
arrayOf(
LineNumberRange(currentLine, 0, false),
LineNumberRange(currentLine, 0, false),
LineAddress(currentLine, 0, false),
LineAddress(currentLine, 0, false),
),
)
}