1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-05-06 21:34:02 +02:00

funcref() function

This commit is contained in:
lippfi 2021-10-01 19:13:54 +03:00
parent 07d753f413
commit dbf0444110
4 changed files with 212 additions and 8 deletions
resources/META-INF/includes
src/com/maddyhome/idea/vim/vimscript/model/functions/handlers
test/org/jetbrains/plugins/ideavim/ex/implementation/functions
vimscript-info

View File

@ -8,5 +8,6 @@
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ExistsFunctionHandler" name="exists"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.LenFunctionHandler" name="len"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.FunctionFunctionHandler" name="function"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.FuncrefFunctionHandler" name="funcref"/>
</extensions>
</idea-plugin>

View File

@ -28,6 +28,7 @@ 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.expressions.Expression
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
import com.maddyhome.idea.vim.vimscript.model.functions.DefinedFunctionHandler
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
import com.maddyhome.idea.vim.vimscript.services.FunctionStorage
@ -72,14 +73,58 @@ object FunctionFunctionHandler : FunctionHandler() {
}
return VimFuncref(function, arglist ?: VimList(mutableListOf()), dictionary ?: VimDictionary(LinkedHashMap()), VimFuncref.Type.FUNCTION)
}
}
private fun String.extractScopeAndName(): Pair<Scope?, String> {
val colonIndex = this.indexOf(":")
if (colonIndex == -1) {
return Pair(null, this)
object FuncrefFunctionHandler : FunctionHandler() {
override val name = "function"
override val minimumNumberOfArguments: Int = 1
override val maximumNumberOfArguments: Int = 3
override fun doFunction(
argumentValues: List<Expression>,
editor: Editor,
context: DataContext,
parent: Executable,
): VimFuncref {
val arg1 = argumentValues[0].evaluate(editor, context, parent)
if (arg1 !is VimString) {
throw ExException("E129: Function name required")
}
val scopeString = this.substring(0, colonIndex)
val nameString = this.substring(colonIndex + 1)
return Pair(Scope.getByValue(scopeString), nameString)
val scopeAndName = arg1.value.extractScopeAndName()
val function = FunctionStorage.getUserDefinedFunction(scopeAndName.first, scopeAndName.second, parent)
?: throw ExException("E700: Unknown function: ${if (scopeAndName.first != null) scopeAndName.first!!.c + ":" else ""}${scopeAndName.second}")
val handler = DefinedFunctionHandler(function)
var arglist: VimList? = null
var dictionary: VimDictionary? = null
val arg2 = argumentValues.getOrNull(1)?.evaluate(editor, context, parent)
val arg3 = argumentValues.getOrNull(2)?.evaluate(editor, context, parent)
if (arg2 is VimDictionary && arg3 is VimDictionary) {
throw ExException("E923: Second argument of function() must be a list or a dict")
}
if (arg2 != null) {
when (arg2) {
is VimList -> arglist = arg2
is VimDictionary -> dictionary = arg2
else -> throw ExException("E923: Second argument of function() must be a list or a dict")
}
}
if (arg3 != null && arg3 !is VimDictionary) {
throw ExException("E922: expected a dict")
}
return VimFuncref(handler, arglist ?: VimList(mutableListOf()), dictionary ?: VimDictionary(LinkedHashMap()), VimFuncref.Type.FUNCREF)
}
}
private fun String.extractScopeAndName(): Pair<Scope?, String> {
val colonIndex = this.indexOf(":")
if (colonIndex == -1) {
return Pair(null, this)
}
val scopeString = this.substring(0, colonIndex)
val nameString = this.substring(colonIndex + 1)
return Pair(Scope.getByValue(scopeString), nameString)
}

View File

@ -0,0 +1,158 @@
/*
* 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 org.jetbrains.plugins.ideavim.ex.implementation.functions
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
class FuncrefTest : VimTestCase() {
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test funcref for built-in function`() {
configureByText("\n")
typeText(commandToKeys("let Ff = funcref('abs')"))
assertPluginError(true)
assertPluginErrorMessageContains("E700: Unknown function: abs")
}
fun `test funcref with arglist`() {
configureByText("\n")
typeText(
commandToKeys(
"""
function! Abs(number) |
return abs(a:number) |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("let Ff = funcref('Abs', [-10])"))
typeText(commandToKeys("echo Ff()"))
assertExOutput("10\n")
typeText(commandToKeys("echo Ff"))
assertExOutput("function('Abs', [-10])\n")
typeText(commandToKeys("delfunction! Abs"))
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test funcref for unknown function`() {
configureByText("\n")
typeText(commandToKeys("let Ff = funcref('Unknown')"))
assertPluginError(true)
assertPluginErrorMessageContains("E700: Unknown function: Unknown")
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test funcref with wrong function name`() {
configureByText("\n")
typeText(commandToKeys("let Ff = funcref(32)"))
assertPluginError(true)
assertPluginErrorMessageContains("E129: Function name required")
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test funcref with wrong second argument`() {
configureByText("\n")
typeText(
commandToKeys(
"""
function! Abs(number) |
return abs(a:number) |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("let Ff = funcref('Abs', 10)"))
assertPluginError(true)
assertPluginErrorMessageContains("E923: Second argument of function() must be a list or a dict")
typeText(commandToKeys("delfunction! Abs"))
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test funcref with wrong third argument`() {
configureByText("\n")
typeText(
commandToKeys(
"""
function! Abs(number) |
return abs(a:number) |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("let Ff = funcref('Abs', [], 40)"))
assertPluginError(true)
assertPluginErrorMessageContains("E922: expected a dict")
typeText(commandToKeys("delfunction! Abs"))
}
fun `test redefining a function`() {
configureByText("\n")
typeText(
commandToKeys(
"""
function! SayHi() |
echo 'hello' |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("let Ff = funcref('SayHi')"))
typeText(commandToKeys("call Ff()"))
assertExOutput("hello\n")
typeText(
commandToKeys(
"""
function! SayHi() |
echo 'hi' |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("call Ff()"))
assertExOutput("hello\n")
typeText(commandToKeys("delfunction! SayHi"))
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test deleting function`() {
configureByText("\n")
typeText(
commandToKeys(
"""
function! SayHi() |
echo 'hello' |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("let Ff = funcref('SayHi')"))
typeText(commandToKeys("delfunction! SayHi"))
typeText(commandToKeys("call Ff()"))
assertPluginError(true)
assertPluginErrorMessageContains("E933: Function was deleted: SayHi")
}
}

View File

@ -26,7 +26,7 @@
- [x] lambdas
- [x] function as method
- [x] `function` function
- [ ] `funcref` function
- [x] `funcref` function
- [ ] dictionary functions
- [ ] anonymous functions
- [ ] `dict` function flag