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

function() function

This commit is contained in:
lippfi 2021-10-01 17:16:37 +03:00
parent f516e89a5f
commit 07d753f413
5 changed files with 217 additions and 3 deletions
resources/META-INF/includes
src/com/maddyhome/idea/vim/vimscript/model
expressions
functions/handlers
test/org/jetbrains/plugins/ideavim/ex/implementation/functions
vimscript-info

View File

@ -7,5 +7,6 @@
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.SinFunctionHandler" name="sin"/>
<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"/>
</extensions>
</idea-plugin>

View File

@ -30,8 +30,8 @@ enum class Scope(val c: String) {
VIM_VARIABLE("v");
companion object {
fun getByValue(s: String): Scope {
return values().first { it.c == s }
fun getByValue(s: String): Scope? {
return values().firstOrNull { it.c == s }
}
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.ex.ExException
import com.maddyhome.idea.vim.vimscript.model.Executable
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDictionary
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
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.FunctionHandler
import com.maddyhome.idea.vim.vimscript.services.FunctionStorage
object FunctionFunctionHandler : 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 scopeAndName = arg1.value.extractScopeAndName()
val function = FunctionStorage.getFunctionHandlerOrNull(scopeAndName.first, scopeAndName.second, parent)
?: throw ExException("E700: Unknown function: ${if (scopeAndName.first != null) scopeAndName.first!!.c + ":" else ""}${scopeAndName.second}")
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(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)
}
val scopeString = this.substring(0, colonIndex)
val nameString = this.substring(colonIndex + 1)
return Pair(Scope.getByValue(scopeString), nameString)
}
}

View File

@ -0,0 +1,128 @@
/*
* 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 FunctionTest : VimTestCase() {
fun `test function for built-in function`() {
configureByText("\n")
typeText(commandToKeys("let Ff = function('abs')"))
typeText(commandToKeys("echo Ff(-10)"))
assertExOutput("10\n")
typeText(commandToKeys("echo Ff"))
assertExOutput("abs\n")
}
fun `test function with arglist`() {
configureByText("\n")
typeText(commandToKeys("let Ff = function('abs', [-10])"))
typeText(commandToKeys("echo Ff()"))
assertExOutput("10\n")
typeText(commandToKeys("echo Ff"))
assertExOutput("function('abs', [-10])\n")
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test function for unknown function`() {
configureByText("\n")
typeText(commandToKeys("let Ff = function('unknown')"))
assertPluginError(true)
assertPluginErrorMessageContains("E700: Unknown function: unknown")
}
// todo in release 1.9 (good example of multiple exceptions at once)
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test function with wrong function name`() {
configureByText("\n")
typeText(commandToKeys("let Ff = function(32)"))
assertPluginError(true)
assertPluginErrorMessageContains("E129: Function name required")
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test function with wrong second argument`() {
configureByText("\n")
typeText(commandToKeys("let Ff = function('abs', 10)"))
assertPluginError(true)
assertPluginErrorMessageContains("E923: Second argument of function() must be a list or a dict")
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN_ERROR)
fun `test function with wrong third argument`() {
configureByText("\n")
typeText(commandToKeys("let Ff = function('abs', [], 40)"))
assertPluginError(true)
assertPluginErrorMessageContains("E922: expected a dict")
}
fun `test redefining a function`() {
configureByText("\n")
typeText(
commandToKeys(
"""
function! SayHi() |
echo 'hello' |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("let Ff = function('SayHi')"))
typeText(commandToKeys("call Ff()"))
assertExOutput("hello\n")
typeText(
commandToKeys(
"""
function! SayHi() |
echo 'hi' |
endfunction
""".trimIndent()
)
)
typeText(commandToKeys("call Ff()"))
assertExOutput("hi\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 = function('SayHi')"))
typeText(commandToKeys("delfunction! SayHi"))
typeText(commandToKeys("call Ff()"))
assertPluginError(true)
assertPluginErrorMessageContains("E933: Function was deleted: SayHi")
}
}

View File

@ -25,7 +25,7 @@
- [x] funcref type
- [x] lambdas
- [x] function as method
- [ ] `function` function
- [x] `function` function
- [ ] `funcref` function
- [ ] dictionary functions
- [ ] anonymous functions