mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-06-01 10:34:05 +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.actionSystem.DataContext
|
||||||
import com.intellij.openapi.editor.Editor
|
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.ex.ranges.Ranges
|
||||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
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.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.FunctionStorage
|
||||||
|
import com.maddyhome.idea.vim.vimscript.services.VariableService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* see "h :call"
|
* 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 val argFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.SELF_SYNCHRONIZED)
|
||||||
|
|
||||||
override fun processCommand(editor: Editor, context: DataContext): ExecutionResult {
|
override fun processCommand(editor: Editor, context: DataContext): ExecutionResult {
|
||||||
val function = FunctionStorage.getFunctionHandler(functionCall.scope, functionCall.functionName, parent)
|
val function = FunctionStorage.getFunctionHandlerOrNull(functionCall.scope, functionCall.functionName, parent)
|
||||||
function.ranges = ranges
|
if (function != null) {
|
||||||
function.executeFunction(functionCall, editor, context, this)
|
function.ranges = ranges
|
||||||
return ExecutionResult.Success
|
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.actionSystem.DataContext
|
||||||
import com.intellij.openapi.editor.Editor
|
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.Executable
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
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.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() {
|
Expression() {
|
||||||
|
|
||||||
override fun evaluate(editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
override fun evaluate(editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||||
val handler = FunctionStorage.getFunctionHandler(scope, functionName, parent)
|
val handler = FunctionStorage.getFunctionHandlerOrNull(scope, functionName, parent)
|
||||||
return handler.executeFunction(this, editor, context, 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.Executable
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
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.Expression
|
||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
|
||||||
|
|
||||||
abstract class FunctionHandler {
|
abstract class FunctionHandler {
|
||||||
|
|
||||||
@ -35,19 +34,19 @@ abstract class FunctionHandler {
|
|||||||
|
|
||||||
protected abstract fun doFunction(argumentValues: List<Expression>, editor: Editor, context: DataContext, parent: Executable): VimDataType
|
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 {
|
fun executeFunction(name: String, arguments: List<Expression>, editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||||
checkFunctionCall(functionCall)
|
checkFunctionCall(name, arguments)
|
||||||
val result = doFunction(functionCall.arguments, editor, context, parent)
|
val result = doFunction(arguments, editor, context, parent)
|
||||||
ranges = null
|
ranges = null
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkFunctionCall(functionCall: FunctionCallExpression) {
|
private fun checkFunctionCall(name: String, arguments: List<Expression>) {
|
||||||
if (minimumNumberOfArguments != null && functionCall.arguments.size < minimumNumberOfArguments!!) {
|
if (minimumNumberOfArguments != null && arguments.size < minimumNumberOfArguments!!) {
|
||||||
throw ExException("E119: Not enough arguments for function: ${functionCall.functionName}")
|
throw ExException("E119: Not enough arguments for function: $name")
|
||||||
}
|
}
|
||||||
if (maximumNumberOfArguments != null && functionCall.arguments.size > maximumNumberOfArguments!!) {
|
if (maximumNumberOfArguments != null && arguments.size > maximumNumberOfArguments!!) {
|
||||||
throw ExException("E118: Too many arguments for function: ${functionCall.functionName}")
|
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.VimDataType
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDictionary
|
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.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.VimInt
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimList
|
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.VimString
|
||||||
@ -53,6 +54,7 @@ object EmptyFunctionHandler : FunctionHandler() {
|
|||||||
is VimString -> argument.value.isEmpty()
|
is VimString -> argument.value.isEmpty()
|
||||||
is VimInt -> argument.value == 0
|
is VimInt -> argument.value == 0
|
||||||
is VimFloat -> argument.value == 0.0
|
is VimFloat -> argument.value == 0.0
|
||||||
|
is VimFuncref -> false
|
||||||
is VimBlob -> TODO("Not yet implemented")
|
is VimBlob -> TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
return isEmpty.asVimInt()
|
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.ex.vimscript.VimScriptGlobalEnvironment
|
||||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||||
import com.maddyhome.idea.vim.vimscript.model.ExecutableContext
|
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.VimBlob
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
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.VimDictionary
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFloat
|
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.VimInt
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimList
|
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.VimString
|
||||||
@ -142,7 +144,12 @@ object VariableService {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node = node.parent
|
// todo better parent logic
|
||||||
|
node = if (node is Script) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
node.parent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visibleVariables.reverse()
|
visibleVariables.reverse()
|
||||||
@ -163,7 +170,12 @@ object VariableService {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node = node.parent
|
// todo better parent logic
|
||||||
|
node = if (node is Script) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
node.parent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visibleVariables.reverse()
|
visibleVariables.reverse()
|
||||||
@ -225,7 +237,8 @@ object VariableService {
|
|||||||
is VimFloat -> this.value
|
is VimFloat -> this.value
|
||||||
is VimList -> this.values
|
is VimList -> this.values
|
||||||
is VimDictionary -> this.dictionary
|
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] `abort` function flag
|
||||||
- [x] `range` function flag
|
- [x] `range` function flag
|
||||||
- [x] `call` command
|
- [x] `call` command
|
||||||
- [ ] function as method
|
|
||||||
- [x] optional arguments `...`
|
- [x] optional arguments `...`
|
||||||
- [ ] funcref type
|
- [x] funcref type
|
||||||
- [ ] lambdas
|
- [ ] lambdas
|
||||||
- [ ] default value in functions e.g. `function F1(a, b = 10)`
|
- [ ] function as method
|
||||||
|
- [ ] `function` function
|
||||||
|
- [ ] `funcref` function
|
||||||
- [ ] dictionary functions
|
- [ ] dictionary functions
|
||||||
- [ ] anonymous functions
|
- [ ] anonymous functions
|
||||||
- [ ] `dict` function flag
|
- [ ] `dict` function flag
|
||||||
|
- [ ] default value in functions e.g. `function F1(a, b = 10)`
|
||||||
- [ ] pass Lists and Dictionaries by reference
|
- [ ] pass Lists and Dictionaries by reference
|
||||||
- [ ] variable locking (`lock`, `unlock`, `const`)
|
- [ ] variable locking (`lock`, `unlock`, `const`)
|
||||||
- [ ] rewrite OptionManager to vim data types
|
- [ ] rewrite OptionManager to vim data types
|
||||||
@ -50,6 +52,7 @@
|
|||||||
- [ ] delayed parsing of if/for/while etc.
|
- [ ] delayed parsing of if/for/while etc.
|
||||||
- [ ] `has("ide")` or "ide" option
|
- [ ] `has("ide")` or "ide" option
|
||||||
- [ ] `normal` command
|
- [ ] `normal` command
|
||||||
|
- [ ] `finish` statement
|
||||||
- [ ] context dependent parsing e.g. `dict.key`
|
- [ ] context dependent parsing e.g. `dict.key`
|
||||||
- [ ] improve `w:` and `t:` scopes
|
- [ ] improve `w:` and `t:` scopes
|
||||||
- [ ] `v:` scope
|
- [ ] `v:` scope
|
||||||
|
Loading…
Reference in New Issue
Block a user