mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-07 05:34:02 +02:00
Funcref
This commit is contained in:
parent
9cf922ae80
commit
d93fb1fdfc
src/com/maddyhome/idea/vim/vimscript
model
commands
datatypes
expressions
functions
services
vimscript-info
@ -20,10 +20,14 @@ package com.maddyhome.idea.vim.vimscript.model.commands
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Variable
|
||||
import com.maddyhome.idea.vim.vimscript.services.FunctionStorage
|
||||
import com.maddyhome.idea.vim.vimscript.services.VariableService
|
||||
|
||||
/**
|
||||
* see "h :call"
|
||||
@ -33,9 +37,19 @@ class CallCommand(val ranges: Ranges, val functionCall: FunctionCallExpression)
|
||||
override val argFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.SELF_SYNCHRONIZED)
|
||||
|
||||
override fun processCommand(editor: Editor, context: DataContext): ExecutionResult {
|
||||
val function = FunctionStorage.getFunctionHandler(functionCall.scope, functionCall.functionName, parent)
|
||||
function.ranges = ranges
|
||||
function.executeFunction(functionCall, editor, context, this)
|
||||
return ExecutionResult.Success
|
||||
val function = FunctionStorage.getFunctionHandlerOrNull(functionCall.scope, functionCall.functionName, parent)
|
||||
if (function != null) {
|
||||
function.ranges = ranges
|
||||
function.executeFunction(functionCall.functionName, functionCall.arguments, editor, context, this)
|
||||
return ExecutionResult.Success
|
||||
}
|
||||
|
||||
val funcref = VariableService.getNullableVariableValue(Variable(functionCall.scope, functionCall.functionName), editor, context, parent)
|
||||
if (funcref is VimFuncref) {
|
||||
funcref.execute(functionCall.arguments, editor, context, parent)
|
||||
return ExecutionResult.Success
|
||||
}
|
||||
|
||||
throw ExException("E117: Unknown function: ${if (functionCall.scope != null) functionCall.scope.c + ":" else ""}${functionCall.functionName}")
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.datatypes
|
||||
|
||||
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.expressions.Expression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
||||
import com.maddyhome.idea.vim.vimscript.model.functions.DefinedFunctionHandler
|
||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
|
||||
import com.maddyhome.idea.vim.vimscript.services.FunctionStorage
|
||||
|
||||
data class VimFuncref(
|
||||
private val definition: FunctionDeclaration,
|
||||
val arguments: VimList,
|
||||
val dictionary: VimDictionary, // todo notice me senpai :(
|
||||
val type: Type,
|
||||
val isDeleted: Boolean,
|
||||
) : VimDataType() {
|
||||
|
||||
companion object {
|
||||
var lambdaCounter = 0
|
||||
}
|
||||
|
||||
override fun asDouble(): Double {
|
||||
throw ExException("E703: using Funcref as a Number")
|
||||
}
|
||||
|
||||
override fun asString(): String {
|
||||
throw ExException("E729: using Funcref as a String")
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return when (type) {
|
||||
Type.LAMBDA -> "function('<lambda>${this.definition.name}')"
|
||||
Type.FUNCREF -> "function('${this.definition.name}')"
|
||||
Type.FUNCTION -> this.definition.name
|
||||
}
|
||||
}
|
||||
|
||||
override fun toVimNumber(): VimInt {
|
||||
throw ExException("E703: using Funcref as a Number")
|
||||
}
|
||||
|
||||
fun execute(args: List<Expression>, editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||
val allArguments = listOf(this.arguments.values.map { SimpleExpression(it) }, args).flatten()
|
||||
if (isDeleted) {
|
||||
throw ExException("E933: Function was deleted: ${this.definition.name}")
|
||||
}
|
||||
val definition = when (type) {
|
||||
Type.LAMBDA, Type.FUNCREF -> this.definition
|
||||
Type.FUNCTION -> {
|
||||
FunctionStorage.getUserDefinedFunction(definition.scope, definition.name, parent)
|
||||
?: throw ExException("E117: Unknown function: ${this.definition.name}")
|
||||
}
|
||||
}
|
||||
return DefinedFunctionHandler(definition).executeFunction(definition.name, allArguments, editor, context, parent)
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
LAMBDA,
|
||||
FUNCREF,
|
||||
FUNCTION,
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,26 @@ package com.maddyhome.idea.vim.vimscript.model.expressions
|
||||
|
||||
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.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
|
||||
import com.maddyhome.idea.vim.vimscript.services.FunctionStorage
|
||||
import com.maddyhome.idea.vim.vimscript.services.VariableService
|
||||
|
||||
data class FunctionCallExpression(val scope: Scope?, val functionName: String, val arguments: List<Expression>) :
|
||||
data class FunctionCallExpression(val scope: Scope?, val functionName: String, val arguments: MutableList<Expression>) :
|
||||
Expression() {
|
||||
|
||||
override fun evaluate(editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||
val handler = FunctionStorage.getFunctionHandler(scope, functionName, parent)
|
||||
return handler.executeFunction(this, editor, context, parent)
|
||||
val handler = FunctionStorage.getFunctionHandlerOrNull(scope, functionName, parent)
|
||||
if (handler != null) {
|
||||
return handler.executeFunction(this.functionName, this.arguments, editor, context, parent)
|
||||
}
|
||||
|
||||
val funcref = VariableService.getNullableVariableValue(Variable(scope, functionName), editor, context, parent)
|
||||
if (funcref is VimFuncref) {
|
||||
return funcref.execute(arguments, editor, context, parent)
|
||||
}
|
||||
throw ExException("E117: Unknown function: ${if (scope != null) scope.c + ":" else ""}$functionName")
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
||||
|
||||
abstract class FunctionHandler {
|
||||
|
||||
@ -35,19 +34,19 @@ abstract class FunctionHandler {
|
||||
|
||||
protected abstract fun doFunction(argumentValues: List<Expression>, editor: Editor, context: DataContext, parent: Executable): VimDataType
|
||||
|
||||
fun executeFunction(functionCall: FunctionCallExpression, editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||
checkFunctionCall(functionCall)
|
||||
val result = doFunction(functionCall.arguments, editor, context, parent)
|
||||
fun executeFunction(name: String, arguments: List<Expression>, editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||
checkFunctionCall(name, arguments)
|
||||
val result = doFunction(arguments, editor, context, parent)
|
||||
ranges = null
|
||||
return result
|
||||
}
|
||||
|
||||
private fun checkFunctionCall(functionCall: FunctionCallExpression) {
|
||||
if (minimumNumberOfArguments != null && functionCall.arguments.size < minimumNumberOfArguments!!) {
|
||||
throw ExException("E119: Not enough arguments for function: ${functionCall.functionName}")
|
||||
private fun checkFunctionCall(name: String, arguments: List<Expression>) {
|
||||
if (minimumNumberOfArguments != null && arguments.size < minimumNumberOfArguments!!) {
|
||||
throw ExException("E119: Not enough arguments for function: $name")
|
||||
}
|
||||
if (maximumNumberOfArguments != null && functionCall.arguments.size > maximumNumberOfArguments!!) {
|
||||
throw ExException("E118: Too many arguments for function: ${functionCall.functionName}")
|
||||
if (maximumNumberOfArguments != null && arguments.size > maximumNumberOfArguments!!) {
|
||||
throw ExException("E118: Too many arguments for function: $name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimBlob
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDictionary
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFloat
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
|
||||
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
|
||||
@ -53,6 +54,7 @@ object EmptyFunctionHandler : FunctionHandler() {
|
||||
is VimString -> argument.value.isEmpty()
|
||||
is VimInt -> argument.value == 0
|
||||
is VimFloat -> argument.value == 0.0
|
||||
is VimFuncref -> false
|
||||
is VimBlob -> TODO("Not yet implemented")
|
||||
}
|
||||
return isEmpty.asVimInt()
|
||||
|
@ -25,10 +25,12 @@ import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment
|
||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||
import com.maddyhome.idea.vim.vimscript.model.ExecutableContext
|
||||
import com.maddyhome.idea.vim.vimscript.model.Script
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimBlob
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDictionary
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFloat
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
|
||||
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
|
||||
@ -142,7 +144,12 @@ object VariableService {
|
||||
break
|
||||
}
|
||||
}
|
||||
node = node.parent
|
||||
// todo better parent logic
|
||||
node = if (node is Script) {
|
||||
null
|
||||
} else {
|
||||
node.parent
|
||||
}
|
||||
}
|
||||
|
||||
visibleVariables.reverse()
|
||||
@ -163,7 +170,12 @@ object VariableService {
|
||||
break
|
||||
}
|
||||
}
|
||||
node = node.parent
|
||||
// todo better parent logic
|
||||
node = if (node is Script) {
|
||||
null
|
||||
} else {
|
||||
node.parent
|
||||
}
|
||||
}
|
||||
|
||||
visibleVariables.reverse()
|
||||
@ -225,7 +237,8 @@ object VariableService {
|
||||
is VimFloat -> this.value
|
||||
is VimList -> this.values
|
||||
is VimDictionary -> this.dictionary
|
||||
is VimBlob -> throw NotImplementedError("Blobs are not implemented yet :(")
|
||||
is VimBlob -> "blob"
|
||||
is VimFuncref -> "funcref"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,16 @@
|
||||
- [x] `abort` function flag
|
||||
- [x] `range` function flag
|
||||
- [x] `call` command
|
||||
- [ ] function as method
|
||||
- [x] optional arguments `...`
|
||||
- [ ] funcref type
|
||||
- [x] funcref type
|
||||
- [ ] lambdas
|
||||
- [ ] default value in functions e.g. `function F1(a, b = 10)`
|
||||
- [ ] function as method
|
||||
- [ ] `function` function
|
||||
- [ ] `funcref` function
|
||||
- [ ] dictionary functions
|
||||
- [ ] anonymous functions
|
||||
- [ ] `dict` function flag
|
||||
- [ ] default value in functions e.g. `function F1(a, b = 10)`
|
||||
- [ ] pass Lists and Dictionaries by reference
|
||||
- [ ] variable locking (`lock`, `unlock`, `const`)
|
||||
- [ ] rewrite OptionManager to vim data types
|
||||
@ -50,6 +52,7 @@
|
||||
- [ ] delayed parsing of if/for/while etc.
|
||||
- [ ] `has("ide")` or "ide" option
|
||||
- [ ] `normal` command
|
||||
- [ ] `finish` statement
|
||||
- [ ] context dependent parsing e.g. `dict.key`
|
||||
- [ ] improve `w:` and `t:` scopes
|
||||
- [ ] `v:` scope
|
||||
|
Loading…
Reference in New Issue
Block a user