diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/EscapeFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/EscapeFunctionTest.kt new file mode 100644 index 0000000000..7a55080e67 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/EscapeFunctionTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2003-2024 The IdeaVim authors + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE.txt file or at + * https://opensource.org/licenses/MIT. + */ + +package org.jetbrains.plugins.ideavim.ex.implementation.functions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.Test + + +class EscapeFunctionTest : VimTestCase() { + @Test + fun `test escape windows path with spaces`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('c:\program files\vim', ' \')""")) + assertExOutput("""c:\\program\ files\\vim""") + } + + @Test + fun `test escape multiple special characters`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('special chars: #$^', '#$^')""")) + assertExOutput("""special chars: \#\$\^""") + } + + @Test + fun `test escape when no escaping needed`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('no escaping needed', 'xyz')""")) + assertExOutput("no escaping needed") + } + + @Test + fun `test escape empty strings`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('', '')""")) + assertExOutput("") + } + + @Test + fun `test escape consecutive special characters`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('$$$$', '$')""")) + assertExOutput("""\$\$\$\$""") + } + + @Test + fun `test escape with double backslashes`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('test\\here', '\\')""")) + assertExOutput("""test\\\\here""") + } + + @Test + fun `test escape with unicode characters`() { + configureByText("\n") + typeText(commandToKeys("""echo escape('Hello 👋 #world', '#')""")) + assertExOutput("""Hello 👋 \#world""") + + typeText(commandToKeys("""echo escape('🎉$🎊$', '$')""")) + assertExOutput("""🎉\$🎊\$""") + } +} \ No newline at end of file diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/EscapeFunctionHandler.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/EscapeFunctionHandler.kt new file mode 100644 index 0000000000..484d0cc351 --- /dev/null +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/EscapeFunctionHandler.kt @@ -0,0 +1,45 @@ +package com.maddyhome.idea.vim.vimscript.model.functions.handlers + +import com.intellij.vim.annotations.VimscriptFunction +import com.maddyhome.idea.vim.api.ExecutionContext +import com.maddyhome.idea.vim.api.VimEditor +import com.maddyhome.idea.vim.vimscript.model.VimLContext +import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType +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.functions.FunctionHandler + +/** + * Implementation of Vim's escape() function. + * Escapes characters specified in the second argument that occur in the first + * argument with a backslash. + * Example: escape('c:\program files\vim', ' \') returns 'c:\\program\ files\\vim' + */ +@VimscriptFunction(name = "escape") +internal class EscapeFunctionHandler : FunctionHandler() { + override val minimumNumberOfArguments = 2 + override val maximumNumberOfArguments = 2 + + override fun doFunction( + argumentValues: List, + editor: VimEditor, + context: ExecutionContext, + vimContext: VimLContext, + ): VimDataType { + // Get the input string and characters to escape + val string = argumentValues[0].evaluate(editor, context, vimContext).asString() + val charsToEscape = argumentValues[1].evaluate(editor, context, vimContext).asString() + + // Process each character in the input string + val result = StringBuilder() + for (c in string) { + // If the current character should be escaped, add a backslash before it + if (c in charsToEscape) { + result.append('\\') + } + result.append(c) + } + + return VimString(result.toString()) + } +} \ No newline at end of file diff --git a/vim-engine/src/main/resources/ksp-generated/engine_vimscript_functions.json b/vim-engine/src/main/resources/ksp-generated/engine_vimscript_functions.json index 052099e902..b477a77260 100644 --- a/vim-engine/src/main/resources/ksp-generated/engine_vimscript_functions.json +++ b/vim-engine/src/main/resources/ksp-generated/engine_vimscript_functions.json @@ -2,6 +2,7 @@ "abs": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.AbsFunctionHandler", "col": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.ColFunctionHandler", "empty": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.EmptyFunctionHandler", + "escape": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.EscapeFunctionHandler", "exists": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.ExistsFunctionHandler", "funcref": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.FuncrefFunctionHandler", "function": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.FunctionFunctionHandler",