mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-07 13:34:02 +02:00
Optional function arguments
This commit is contained in:
parent
a3b2b4920a
commit
9e62636059
src
com/maddyhome/idea/vim/vimscript
model
expressions
functions
statements
parser/visitors
main/antlr
test/org/jetbrains/plugins/ideavim/ex/implementation/statements
vimscript-info
@ -34,7 +34,7 @@ import com.maddyhome.idea.vim.vimscript.model.statements.ReturnStatement
|
||||
data class LambdaExpression(val args: List<String>, val expr: Expression) : Expression() {
|
||||
|
||||
override fun evaluate(editor: Editor, context: DataContext, parent: Executable): VimFuncref {
|
||||
val function = FunctionDeclaration(null, getFunctionName(), args, buildBody(), false, setOf(FunctionFlag.CLOSURE), true)
|
||||
val function = FunctionDeclaration(null, getFunctionName(), args, listOf(), buildBody(), false, setOf(FunctionFlag.CLOSURE), true)
|
||||
function.parent = parent
|
||||
return VimFuncref(DefinedFunctionHandler(function), VimList(mutableListOf()), null, VimFuncref.Type.LAMBDA)
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ data class DefinedFunctionHandler(val function: FunctionDeclaration) : FunctionH
|
||||
override val name = function.name
|
||||
override val scope = function.scope
|
||||
override val minimumNumberOfArguments = function.args.size
|
||||
override val maximumNumberOfArguments get() = if (function.hasOptionalArguments) null else function.args.size
|
||||
override val maximumNumberOfArguments get() = if (function.hasOptionalArguments) null else function.args.size + function.defaultArgs.size
|
||||
|
||||
override fun doFunction(argumentValues: List<Expression>, editor: Editor, context: DataContext, parent: Executable): VimDataType {
|
||||
var returnValue: VimDataType? = null
|
||||
@ -128,6 +128,7 @@ data class DefinedFunctionHandler(val function: FunctionDeclaration) : FunctionH
|
||||
}
|
||||
|
||||
private fun initializeFunctionVariables(argumentValues: List<Expression>, editor: Editor, context: DataContext) {
|
||||
// non-optional function arguments
|
||||
for ((index, name) in function.args.withIndex()) {
|
||||
VariableService.storeVariable(
|
||||
Variable(Scope.FUNCTION_VARIABLE, name),
|
||||
@ -137,10 +138,30 @@ data class DefinedFunctionHandler(val function: FunctionDeclaration) : FunctionH
|
||||
function
|
||||
)
|
||||
}
|
||||
// optional function arguments with default values
|
||||
for (index in 0 until function.defaultArgs.size) {
|
||||
val expressionToStore = if (index + function.args.size < argumentValues.size) argumentValues[index + function.args.size] else function.defaultArgs[index].second
|
||||
VariableService.storeVariable(
|
||||
Variable(Scope.FUNCTION_VARIABLE, function.defaultArgs[index].first),
|
||||
expressionToStore.evaluate(editor, context, function),
|
||||
editor,
|
||||
context,
|
||||
function
|
||||
)
|
||||
}
|
||||
// all the other optional arguments passed to function are stored in a:000 variable
|
||||
if (function.hasOptionalArguments) {
|
||||
val remainingArgs = if (function.args.size + function.defaultArgs.size < argumentValues.size) {
|
||||
VimList(
|
||||
argumentValues.subList(function.args.size + function.defaultArgs.size, argumentValues.size)
|
||||
.map { it.evaluate(editor, context, function) }.toMutableList()
|
||||
)
|
||||
} else {
|
||||
VimList(mutableListOf())
|
||||
}
|
||||
VariableService.storeVariable(
|
||||
Variable(Scope.FUNCTION_VARIABLE, "000"),
|
||||
VimList(argumentValues.subList(function.args.size, argumentValues.size).map { it.evaluate(editor, context, function) }.toMutableList()),
|
||||
remainingArgs,
|
||||
editor,
|
||||
context,
|
||||
function
|
||||
|
@ -27,6 +27,7 @@ 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.OneElementSublistExpression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
||||
import com.maddyhome.idea.vim.vimscript.model.functions.DefinedFunctionHandler
|
||||
@ -34,6 +35,7 @@ import com.maddyhome.idea.vim.vimscript.model.functions.DefinedFunctionHandler
|
||||
data class AnonymousFunctionDeclaration(
|
||||
val sublist: OneElementSublistExpression,
|
||||
val args: List<String>,
|
||||
val defaultArgs: List<Pair<String, Expression>>,
|
||||
val body: List<Executable>,
|
||||
val replaceExisting: Boolean,
|
||||
val flags: Set<FunctionFlag>,
|
||||
@ -55,7 +57,7 @@ data class AnonymousFunctionDeclaration(
|
||||
throw ExException("E718: Funcref required")
|
||||
}
|
||||
}
|
||||
val declaration = FunctionDeclaration(null, VimFuncref.anonymousCounter++.toString(), args, body, replaceExisting, flags + FunctionFlag.DICT, hasOptionalArguments)
|
||||
val declaration = FunctionDeclaration(null, VimFuncref.anonymousCounter++.toString(), args, defaultArgs, body, replaceExisting, flags + FunctionFlag.DICT, hasOptionalArguments)
|
||||
container.dictionary[index] = VimFuncref(DefinedFunctionHandler(declaration), VimList(mutableListOf()), container, VimFuncref.Type.FUNCREF)
|
||||
return ExecutionResult.Success
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
||||
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.Scope
|
||||
import com.maddyhome.idea.vim.vimscript.services.FunctionStorage
|
||||
|
||||
@ -13,6 +14,7 @@ data class FunctionDeclaration(
|
||||
val scope: Scope?,
|
||||
val name: String,
|
||||
val args: List<String>,
|
||||
val defaultArgs: List<Pair<String, Expression>>,
|
||||
val body: List<Executable>,
|
||||
val replaceExisting: Boolean,
|
||||
val flags: Set<FunctionFlag>,
|
||||
|
@ -74,6 +74,8 @@ object ExecutableVisitor : VimscriptBaseVisitor<Executable>() {
|
||||
val functionScope = if (ctx.functionScope() != null) Scope.getByValue(ctx.functionScope().text) else null
|
||||
val functionName = ctx.functionName().text
|
||||
val args = ctx.argumentsDeclaration().variableName().map { it.text }
|
||||
val defaultArgs = ctx.argumentsDeclaration().defaultValue()
|
||||
.map { Pair<String, Expression>(it.variableName().text, ExpressionVisitor.visit(it.expr())) }
|
||||
val body = ctx.blockMember().mapNotNull { visitBlockMember(it) }
|
||||
val replaceExisting = ctx.replace != null
|
||||
val flags = mutableSetOf<FunctionFlag?>()
|
||||
@ -81,12 +83,14 @@ object ExecutableVisitor : VimscriptBaseVisitor<Executable>() {
|
||||
for (flag in ctx.functionFlag()) {
|
||||
flags.add(FunctionFlag.getByName(flag.text))
|
||||
}
|
||||
return FunctionDeclaration(functionScope, functionName, args, body, replaceExisting, flags.filterNotNull().toSet(), hasOptionalArguments)
|
||||
return FunctionDeclaration(functionScope, functionName, args, defaultArgs, body, replaceExisting, flags.filterNotNull().toSet(), hasOptionalArguments)
|
||||
}
|
||||
|
||||
override fun visitDictFunctionDefinition(ctx: VimscriptParser.DictFunctionDefinitionContext): Executable {
|
||||
val functionScope = if (ctx.functionScope() != null) Scope.getByValue(ctx.functionScope().text) else null
|
||||
val args = ctx.argumentsDeclaration().variableName().map { it.text }
|
||||
val defaultArgs = ctx.argumentsDeclaration().defaultValue()
|
||||
.map { Pair<String, Expression>(it.variableName().text, ExpressionVisitor.visit(it.expr())) }
|
||||
val body = ctx.blockMember().mapNotNull { visitBlockMember(it) }
|
||||
val replaceExisting = ctx.replace != null
|
||||
val flags = mutableSetOf<FunctionFlag?>()
|
||||
@ -98,7 +102,7 @@ object ExecutableVisitor : VimscriptBaseVisitor<Executable>() {
|
||||
for (i in 2 until ctx.literalDictionaryKey().size) {
|
||||
sublistExpression = OneElementSublistExpression(SimpleExpression(VimString(ctx.literalDictionaryKey(i).text)), sublistExpression)
|
||||
}
|
||||
return AnonymousFunctionDeclaration(sublistExpression, args, body, replaceExisting, flags.filterNotNull().toSet(), hasOptionalArguments)
|
||||
return AnonymousFunctionDeclaration(sublistExpression, args, defaultArgs, body, replaceExisting, flags.filterNotNull().toSet(), hasOptionalArguments)
|
||||
}
|
||||
|
||||
override fun visitTryStatement(ctx: VimscriptParser.TryStatementContext): Executable {
|
||||
|
@ -79,7 +79,8 @@ dictFunctionDefinition:
|
||||
ws_cols ENDFUNCTION WS* (comment | statementSeparator)
|
||||
;
|
||||
functionFlag: RANGE | ABORT | DICT | CLOSURE;
|
||||
argumentsDeclaration: (variableName (WS* COMMA WS* variableName)* (WS* COMMA WS* ETC WS*)? WS*)?;
|
||||
argumentsDeclaration: (variableName (WS* COMMA WS* variableName)* defaultValue* (WS* COMMA WS* ETC WS*)? WS*)?;
|
||||
defaultValue: WS* COMMA WS* variableName WS* ASSIGN WS* expr;
|
||||
|
||||
augroup: ws_cols AUGROUP ~(NEW_LINE | BAR)* statementSeparator
|
||||
blockMember*
|
||||
@ -444,6 +445,7 @@ binaryOperator4: AMPERSAND AMPERSAND;
|
||||
binaryOperator5: LOGICAL_OR;
|
||||
|
||||
register: AT (DIGIT | alphabeticChar | MINUS | COLON | DOT | MOD | NUM | ASSIGN | STAR | PLUS | TILDE | UNDERSCORE | DIV | AT);
|
||||
// todo argumentDeclaration but without default values
|
||||
lambda: L_CURLY WS* argumentsDeclaration WS* ARROW WS* expr WS* R_CURLY;
|
||||
|
||||
variable: (variableScope COLON)? variableName;
|
||||
|
@ -559,5 +559,54 @@ class FunctionDeclarationTest : VimTestCase() {
|
||||
)
|
||||
)
|
||||
assertExOutput("[42, 'optional arg']\n")
|
||||
|
||||
typeText(commandToKeys("delfunction! GetOptionalArgs"))
|
||||
}
|
||||
|
||||
fun `test arguments with default values`() {
|
||||
configureByText("\n")
|
||||
typeText(
|
||||
commandToKeys(
|
||||
"" +
|
||||
"function GetOptionalArgs(name, a = 10, b = 20) |" +
|
||||
" return 'a = ' .. a:a .. ', b = ' .. a:b | " +
|
||||
"endfunction"
|
||||
)
|
||||
)
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional')"))
|
||||
assertExOutput("a = 10, b = 20\n")
|
||||
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional', 42)"))
|
||||
assertExOutput("a = 42, b = 20\n")
|
||||
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional', 100, 200)"))
|
||||
assertExOutput("a = 100, b = 200\n")
|
||||
|
||||
typeText(commandToKeys("delfunction! GetOptionalArgs"))
|
||||
}
|
||||
|
||||
fun `test arguments with default values and optional args`() {
|
||||
configureByText("\n")
|
||||
typeText(
|
||||
commandToKeys(
|
||||
"" +
|
||||
"function GetOptionalArgs(name, a = 10, b = 20, ...) |" +
|
||||
" return {'a': a:a, 'b': a:b, '000': a:000} | " +
|
||||
"endfunction"
|
||||
)
|
||||
)
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional')"))
|
||||
assertExOutput("{'a': 10, 'b': 20, '000': []}\n")
|
||||
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional', 42)"))
|
||||
assertExOutput("{'a': 42, 'b': 20, '000': []}\n")
|
||||
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional', 100, 200)"))
|
||||
assertExOutput("{'a': 100, 'b': 200, '000': []}\n")
|
||||
|
||||
typeText(commandToKeys("echo GetOptionalArgs('this arg is not optional', 100, 200, 300)"))
|
||||
assertExOutput("{'a': 100, 'b': 200, '000': [300]}\n")
|
||||
|
||||
typeText(commandToKeys("delfunction! GetOptionalArgs"))
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,10 @@
|
||||
- [x] `funcref` function
|
||||
- [x] `dict` function flag
|
||||
- [x] anonymous functions
|
||||
- [ ] default value in functions e.g. `function F1(a, b = 10)`
|
||||
- [ ] delayed parsing of if/for/while etc.
|
||||
- [x] default value in functions e.g. `function F1(a, b = 10)`
|
||||
- [ ] `has("ide")` or "ide" option
|
||||
- [ ] reduce number of rules in grammar
|
||||
- [ ] delayed parsing of if/for/while etc.
|
||||
- [ ] classic package structure
|
||||
- [ ] loggers
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user