mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-03-04 21:32:52 +01:00
Support line and col functions
This commit is contained in:
parent
a1b048a2f9
commit
1813ad400a
resources/META-INF/includes
src/com/maddyhome/idea/vim
option
vimscript/model
test/org/jetbrains/plugins/ideavim/ex/implementation/expressions
@ -1,7 +1,9 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="IdeaVIM">
|
||||
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.AbsFunctionHandler" name="abs"/>
|
||||
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ColFunctionHandler" name="col"/>
|
||||
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.EmptyFunctionHandler" name="empty"/>
|
||||
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.LineFunctionHandler" name="line"/>
|
||||
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.SinFunctionHandler" name="sin"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
@ -655,7 +655,11 @@ object VirtualEditData {
|
||||
const val name = "virtualedit"
|
||||
|
||||
const val onemore = "onemore"
|
||||
val allValues = arrayOf("block", "insert", "all", onemore)
|
||||
const val block = "block"
|
||||
const val insert = "insert"
|
||||
const val all = "all"
|
||||
|
||||
val allValues = arrayOf(block, insert, all, onemore)
|
||||
}
|
||||
|
||||
@NonNls
|
||||
|
@ -35,3 +35,9 @@ private fun parseNumber(octalDecimalOrHexNumber: String): Int {
|
||||
}
|
||||
|
||||
fun Boolean.asVimInt(): VimInt = if (this) VimInt.ONE else VimInt.ZERO
|
||||
|
||||
fun Int.asVimInt(): VimInt = when (this) {
|
||||
0 -> VimInt.ZERO
|
||||
1 -> VimInt.ONE
|
||||
else -> VimInt(this)
|
||||
}
|
||||
|
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2021 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2021 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.vimscript.model.functions.handlers
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.mode
|
||||
import com.maddyhome.idea.vim.helper.vimLine
|
||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
||||
import com.maddyhome.idea.vim.option.OptionsManager
|
||||
import com.maddyhome.idea.vim.vimscript.model.VimContext
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimList
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.asVimInt
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
|
||||
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
|
||||
|
||||
// TODO: 03.08.2021 Support second parameter
|
||||
object LineFunctionHandler : FunctionHandler() {
|
||||
|
||||
override val minimumNumberOfArguments = 1
|
||||
override val maximumNumberOfArguments = 2
|
||||
|
||||
override fun doFunction(
|
||||
argumentValues: List<Expression>,
|
||||
editor: Editor?,
|
||||
context: DataContext?,
|
||||
vimContext: VimContext,
|
||||
): VimInt {
|
||||
if (editor == null) return VimInt.ZERO
|
||||
val argument = argumentValues[0].evaluate(editor, context, vimContext)
|
||||
|
||||
if (argument is VimList) {
|
||||
val (line, _) = getLineAndCol(argument, editor) ?: return VimInt.ZERO
|
||||
return line
|
||||
}
|
||||
|
||||
if (argument !is VimString) return VimInt.ZERO
|
||||
val stringValue = argument.value
|
||||
return getLine(stringValue, editor)
|
||||
}
|
||||
}
|
||||
|
||||
object ColFunctionHandler : FunctionHandler() {
|
||||
|
||||
override val minimumNumberOfArguments = 1
|
||||
override val maximumNumberOfArguments = 1
|
||||
|
||||
override fun doFunction(
|
||||
argumentValues: List<Expression>,
|
||||
editor: Editor?,
|
||||
context: DataContext?,
|
||||
vimContext: VimContext,
|
||||
): VimDataType {
|
||||
val argument = argumentValues[0].evaluate(editor, context, vimContext)
|
||||
if (editor == null) return VimInt.ZERO
|
||||
|
||||
if (argument is VimList) {
|
||||
val (_, col) = getLineAndCol(argument, editor) ?: return VimInt.ZERO
|
||||
return col
|
||||
}
|
||||
|
||||
if (argument !is VimString) return VimInt.ZERO
|
||||
val argumentValue = argument.value
|
||||
return getColumn(argumentValue, editor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLineAndCol(
|
||||
argument: VimList,
|
||||
editor: Editor,
|
||||
): Pair<VimInt, VimInt>? {
|
||||
if (argument.values.size < 2) return null
|
||||
|
||||
val firstArgument = argument.values[0]
|
||||
val line: VimInt
|
||||
if (firstArgument is VimString) {
|
||||
// Try to get line for '.', '$', etc.
|
||||
val calculatedLine = getLine(firstArgument.value, editor)
|
||||
if (calculatedLine == VimInt.ZERO) {
|
||||
// Try to get line as number
|
||||
val parsedInt = firstArgument.toVimNumber()
|
||||
if (parsedInt == VimInt.ZERO) {
|
||||
return null
|
||||
} else {
|
||||
line = parsedInt
|
||||
}
|
||||
} else {
|
||||
line = calculatedLine
|
||||
}
|
||||
} else {
|
||||
line = firstArgument.toVimNumber()
|
||||
}
|
||||
if (line.value - 1 < 0 || line.value - 1 >= editor.document.lineCount) return null
|
||||
val lineLength = lineLength(editor, line.value - 1)
|
||||
|
||||
val columnArgument = argument.values[1]
|
||||
|
||||
val col: VimInt = if (columnArgument is VimString) {
|
||||
val calculatedColumn = getColumn(columnArgument.value, editor)
|
||||
if (calculatedColumn == VimInt.ZERO) {
|
||||
val parsedInt = columnArgument.toVimNumber()
|
||||
if (parsedInt == VimInt.ZERO) {
|
||||
return null
|
||||
} else {
|
||||
parsedInt
|
||||
}
|
||||
} else {
|
||||
calculatedColumn
|
||||
}
|
||||
} else {
|
||||
columnArgument.toVimNumber()
|
||||
}
|
||||
|
||||
if (col.value - 1 < 0 || col.value - 1 >= lineLength) return null
|
||||
return line to col
|
||||
}
|
||||
|
||||
private fun lineLength(editor: Editor, line: Int): Int {
|
||||
return editor.document.getLineEndOffset(line) - editor.document.getLineStartOffset(line)
|
||||
}
|
||||
|
||||
private fun getColumn(argumentValue: String, editor: Editor): VimInt {
|
||||
return when {
|
||||
argumentValue.isEmpty() -> VimInt.ZERO
|
||||
|
||||
// Current line
|
||||
argumentValue == "." -> currentCol(editor)
|
||||
|
||||
// Last column of current line
|
||||
argumentValue == "$" -> lastCol(editor)
|
||||
|
||||
// Mark line
|
||||
argumentValue.length == 2 && argumentValue[0] == '\'' -> {
|
||||
val markLogicalLine = VimPlugin.getMark().getMark(editor, argumentValue[1])?.col ?: return VimInt.ZERO
|
||||
(markLogicalLine + 1).asVimInt()
|
||||
}
|
||||
|
||||
// Start of the visual position
|
||||
argumentValue == "v" -> {
|
||||
if (editor.mode != CommandState.Mode.VISUAL) return currentCol(editor)
|
||||
val vimStart = editor.caretModel.currentCaret.vimSelectionStart
|
||||
(editor.offsetToLogicalPosition(vimStart).column + 1).asVimInt()
|
||||
}
|
||||
|
||||
else -> VimInt.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
private fun currentCol(editor: Editor): VimInt {
|
||||
val logicalPosition = editor.caretModel.currentCaret.logicalPosition
|
||||
var lineLength = lineLength(editor, logicalPosition.line)
|
||||
|
||||
// If virtualedit is set, the col is one more
|
||||
// XXX Should we also check the current mode?
|
||||
if (OptionsManager.virtualedit.value.isNotEmpty()) {
|
||||
lineLength += 1
|
||||
}
|
||||
|
||||
return (logicalPosition.column + 1).coerceAtMost(lineLength).asVimInt()
|
||||
}
|
||||
|
||||
private fun lastCol(editor: Editor): VimInt {
|
||||
val logicalPosition = editor.caretModel.currentCaret.logicalPosition
|
||||
val lineLength = lineLength(editor, logicalPosition.line)
|
||||
return lineLength.asVimInt()
|
||||
}
|
||||
|
||||
fun getLine(stringValue: String, editor: Editor): VimInt {
|
||||
return when {
|
||||
stringValue.isEmpty() -> VimInt.ZERO
|
||||
|
||||
// Current caret line
|
||||
stringValue == "." -> editor.vimLine.asVimInt()
|
||||
|
||||
// Last line in document
|
||||
// Line count because vim counts lines 1-based
|
||||
stringValue == "$" -> editor.document.lineCount.asVimInt()
|
||||
|
||||
// Mark line
|
||||
stringValue.length == 2 && stringValue[0] == '\'' -> {
|
||||
val markLogicalLine = VimPlugin.getMark().getMark(editor, stringValue[1])?.logicalLine ?: return VimInt.ZERO
|
||||
(markLogicalLine + 1).asVimInt()
|
||||
}
|
||||
|
||||
// First visible line
|
||||
stringValue == "w0" -> {
|
||||
val actualVisualTop = EditorHelper.getVisualLineAtTopOfScreen(editor)
|
||||
val actualLogicalTop = EditorHelper.visualLineToLogicalLine(editor, actualVisualTop)
|
||||
(actualLogicalTop + 1).asVimInt()
|
||||
}
|
||||
|
||||
// Last visible line
|
||||
stringValue == "w$" -> {
|
||||
val actualVisualBottom = EditorHelper.getVisualLineAtBottomOfScreen(editor)
|
||||
val actualLogicalBottom = EditorHelper.visualLineToLogicalLine(editor, actualVisualBottom)
|
||||
(actualLogicalBottom + 1).asVimInt()
|
||||
}
|
||||
|
||||
// Start of the visual position
|
||||
stringValue == "v" -> {
|
||||
if (editor.mode != CommandState.Mode.VISUAL) return editor.vimLine.asVimInt()
|
||||
val vimStart = editor.caretModel.currentCaret.vimSelectionStart
|
||||
(editor.offsetToLogicalPosition(vimStart).line + 1).asVimInt()
|
||||
}
|
||||
|
||||
else -> VimInt.ZERO
|
||||
}
|
||||
}
|
@ -56,4 +56,4 @@ object EmptyFunctionHandler : FunctionHandler() {
|
||||
}
|
||||
return isEmpty.asVimInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
package org.jetbrains.plugins.ideavim.ex.implementation.expressions
|
||||
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class BuiltInFunctionTest : VimTestCase() {
|
||||
@ -45,4 +46,89 @@ class BuiltInFunctionTest : VimTestCase() {
|
||||
typeText(commandToKeys("echo empty({1:2}) empty({})"))
|
||||
assertExOutput("0 1\n")
|
||||
}
|
||||
|
||||
fun `test line`() {
|
||||
configureByText("1\n2\n${c}3\n4\n5")
|
||||
typeText(commandToKeys("echo line('.')"))
|
||||
assertExOutput("3\n")
|
||||
|
||||
typeText(commandToKeys("echo line('$')"))
|
||||
assertExOutput("5\n")
|
||||
|
||||
typeText(parseKeys("ma"))
|
||||
typeText(commandToKeys("""echo line("'a") line("'x")"""))
|
||||
assertExOutput("3 0\n")
|
||||
|
||||
setEditorVisibleSize(screenWidth, 3)
|
||||
setPositionAndScroll(2, 2)
|
||||
typeText(commandToKeys("""echo line("w0") line("w$")"""))
|
||||
assertExOutput("3 5\n")
|
||||
|
||||
// Without selection - current line
|
||||
typeText(commandToKeys("""echo line("v")"""))
|
||||
assertExOutput("3\n")
|
||||
// With selection
|
||||
typeText(parseKeys("vj"))
|
||||
typeText(commandToKeys("""echo line("v")"""))
|
||||
assertExOutput("3\n")
|
||||
// Remove selection and check again
|
||||
typeText(parseKeys("<esc>"))
|
||||
typeText(commandToKeys("""echo line("v")"""))
|
||||
assertExOutput("4\n")
|
||||
|
||||
typeText(commandToKeys("""echo line("abs") line(1) line([])"""))
|
||||
assertExOutput("0 0 0\n")
|
||||
|
||||
typeText(commandToKeys("""echo line([1, 1]) line(['.', '$']) line(['$', '$'])"""))
|
||||
assertExOutput("1 4 5\n")
|
||||
|
||||
typeText(commandToKeys("""echo line([0, 1]) line([1, 1]) line([5, 1]) line([6, 1]) line([5, 2])"""))
|
||||
assertExOutput("0 1 5 0 0\n")
|
||||
}
|
||||
|
||||
// XXX virtualedit is not tested
|
||||
fun `test col`() {
|
||||
configureByText(
|
||||
"""
|
||||
1
|
||||
2
|
||||
1234${c}567890
|
||||
4
|
||||
5
|
||||
""".trimIndent()
|
||||
)
|
||||
typeText(commandToKeys("echo col('.')"))
|
||||
assertExOutput("5\n")
|
||||
|
||||
typeText(commandToKeys("echo col('$')"))
|
||||
assertExOutput("10\n")
|
||||
|
||||
typeText(parseKeys("ma"))
|
||||
typeText(commandToKeys("""echo col("'a") col("'z")"""))
|
||||
assertExOutput("5 0\n")
|
||||
|
||||
// Without selection - current line
|
||||
typeText(commandToKeys("""echo col("v")"""))
|
||||
assertExOutput("5\n")
|
||||
// With selection
|
||||
typeText(parseKeys("vll"))
|
||||
typeText(commandToKeys("""echo col("v")"""))
|
||||
assertExOutput("5\n")
|
||||
// Remove selection and check again
|
||||
typeText(parseKeys("<esc>"))
|
||||
typeText(commandToKeys("""echo col("v")"""))
|
||||
assertExOutput("7\n")
|
||||
|
||||
typeText(commandToKeys("echo col('$')"))
|
||||
assertExOutput("10\n")
|
||||
|
||||
typeText(commandToKeys("""echo col("abs") col(1) col([])"""))
|
||||
assertExOutput("0 0 0\n")
|
||||
|
||||
typeText(commandToKeys("""echo col([1, 1]) col(['.', '$']) col(['$', '$'])"""))
|
||||
assertExOutput("1 10 0\n")
|
||||
|
||||
typeText(commandToKeys("""echo col([0, 1]) col([1, 1]) col([5, 1]) col([6, 1]) col([5, 2])"""))
|
||||
assertExOutput("0 1 1 0 0\n")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user