diff --git a/src/main/resources/dictionaries/ideavim.dic b/src/main/resources/dictionaries/ideavim.dic index 35af1df6f..a6ec1f5e8 100644 --- a/src/main/resources/dictionaries/ideavim.dic +++ b/src/main/resources/dictionaries/ideavim.dic @@ -170,6 +170,7 @@ funcref getcmdtype getreg getregtype +isinf tolower toupper diff --git a/src/main/resources/messages/IdeaVimBundle.properties b/src/main/resources/messages/IdeaVimBundle.properties index ebe7fb462..92ca2490b 100644 --- a/src/main/resources/messages/IdeaVimBundle.properties +++ b/src/main/resources/messages/IdeaVimBundle.properties @@ -88,6 +88,7 @@ E730=E730: Using a List as a String E731=E731: Using a Dictionary as a String E774=E774: 'operatorfunc' is empty E805=E805: Using a Float as a Number +E808=E808: Number or Float required e841.reserved.name.cannot.be.used.for.user.defined.command=E841: Reserved name, cannot be used for user defined command E939=E939: Positive count required E1211=E1211: List required for argument {0} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AbsFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AbsFunctionTest.kt index 469666394..0a1baf108 100644 --- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AbsFunctionTest.kt +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AbsFunctionTest.kt @@ -21,7 +21,37 @@ class AbsFunctionTest : VimTestCase() { } @Test - fun `test abs`() { - assertCommandOutput("echo abs(-123) abs(2)", "123 2") + fun `test abs with float value`() { + assertCommandOutput("echo abs(1.456)", "1.456") + assertCommandOutput("echo abs(-5.456)", "5.456") + } + + @Test + fun `test abs with integer value`() { + assertCommandOutput("echo abs(-4)", "4") + } + + @Test + fun `test abs with coerced float value`() { + assertCommandOutput("""echo abs("-5.456")""", "5") + } + + @Test + fun `test abs with coerced integer value`() { + assertCommandOutput("""echo abs("-4")""", "4") + } + + @Test + fun `test abs with list causes error`() { + enterCommand("""echo abs([-5.456])""") + assertPluginError(true) + assertPluginErrorMessageContains("E745: Using a List as a Number") + } + + @Test + fun `test abs with dictionary causes error`() { + enterCommand("""echo abs({1: -5.456})""") + assertPluginError(true) + assertPluginErrorMessageContains("E728: Using a Dictionary as a Number") } } diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AcosFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AcosFunctionTest.kt new file mode 100644 index 000000000..5e60bcc81 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AcosFunctionTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class AcosFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test acos with Number`() { + assertCommandOutput("echo acos(0)", "1.570796") + assertCommandOutput("echo acos(-1) acos(0) acos(1)", "3.141593 1.570796 0.0") + } + + @Test + fun `test acos with Float`() { + assertCommandOutput("echo acos(-0.5)", "2.094395") + assertCommandOutput("echo acos(-1.0) acos(0.0) acos(1.0)", "3.141593 1.570796 0.0") + } + + @Test + fun `test acos with value greater than 1 returns nan`() { + assertCommandOutput("echo acos(1.1)", "nan") + } + + @Test + fun `test acos with value less than -1 returns nan`() { + assertCommandOutput("echo acos(-1.1)", "nan") + } + + @Test + fun `test acos with string causes errors`() { + enterCommand("echo acos('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test acos with invalid string causes errors`() { + enterCommand("echo acos('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test acos with list causes errors`() { + enterCommand("echo acos([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test acos with dictionary causes errors`() { + enterCommand("echo acos({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AsinFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AsinFunctionTest.kt new file mode 100644 index 000000000..b803e121d --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AsinFunctionTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class AsinFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test asin with Number`() { + assertCommandOutput("echo asin(1)", "1.570796") + assertCommandOutput("echo asin(-1) asin(0) asin(1)", "-1.570796 0.0 1.570796") + } + + @Test + fun `test asin with Float`() { + assertCommandOutput("echo asin(1.0)", "1.570796") + assertCommandOutput("echo asin(0.8) asin(-0.5)", "0.927295 -0.523599") + } + + @Test + fun `test asin with value greater than 1 returns nan`() { + assertCommandOutput("echo asin(1.1)", "nan") + } + + @Test + fun `test asin with value less than -1 returns nan`() { + assertCommandOutput("echo asin(-1.1)", "nan") + } + + @Test + fun `test asin with string causes errors`() { + enterCommand("echo asin('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test asin with invalid string causes errors`() { + enterCommand("echo asin('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test asin with list causes errors`() { + enterCommand("echo asin([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test asin with dictionary causes errors`() { + enterCommand("echo asin({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/Atan2FunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/Atan2FunctionTest.kt new file mode 100644 index 000000000..a600fe1f7 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/Atan2FunctionTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class Atan2FunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test atan2 with Number`() { + assertCommandOutput("echo atan2(100, 100)", "0.785398") + assertCommandOutput("echo atan2(-1, 1) atan2(1, -1)", "-0.785398 2.356194") + } + + @Test + fun `test atan2 with Float`() { + assertCommandOutput("echo atan2(-1.0, 1.0) atan2(1.0, -1.0)", "-0.785398 2.356194") + } + + @Test + fun `test atan2 with string causes errors`() { + enterCommand("echo atan2('1.0', 1.0)") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan2 with string causes errors 2`() { + enterCommand("echo atan2(1.0, '1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan2 with invalid string causes errors`() { + enterCommand("echo atan2('cheese', 1.0)") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + @Test + + fun `test atan2 with invalid string causes errors 2`() { + enterCommand("echo atan2(1.0, 'cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan2 with list causes errors`() { + enterCommand("echo atan2([1.0], 1.0)") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan2 with list causes errors 2`() { + enterCommand("echo atan2(1.0, [1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan2 with dictionary causes errors`() { + enterCommand("echo atan2({1: 1.0}, 1.0)") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan2 with dictionary causes errors 2`() { + enterCommand("echo atan2(1.0, {1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AtanFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AtanFunctionTest.kt new file mode 100644 index 000000000..47b263310 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/AtanFunctionTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class AtanFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test atan with Number`() { + assertCommandOutput("echo atan(100)", "1.560797") + assertCommandOutput("echo atan(0) atan(1)", "0.0 0.785398") + } + + @Test + fun `test atan with Float`() { + assertCommandOutput("echo atan(100.0) atan(-4.01)", "1.560797 -1.326405") + } + + @Test + fun `test atan with string causes errors`() { + enterCommand("echo atan('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan with invalid string causes errors`() { + enterCommand("echo atan('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan with list causes errors`() { + enterCommand("echo atan([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test atan with dictionary causes errors`() { + enterCommand("echo atan({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/CosFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/CosFunctionTest.kt new file mode 100644 index 000000000..2ebe0357d --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/CosFunctionTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class CosFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test cos with Number`() { + assertCommandOutput("echo cos(100)", "0.862319") + assertCommandOutput("echo cos(0) cos(1)", "1.0 0.540302") + } + + @Test + fun `test cos with Float`() { + assertCommandOutput("echo cos(-4.01)", "-0.646043") + assertCommandOutput("echo cos(0.0) cos(1.0)", "1.0 0.540302") + } + + @Test + fun `test cos with string causes errors`() { + enterCommand("echo cos('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test cos with invalid string causes errors`() { + enterCommand("echo cos('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test cos with list causes errors`() { + enterCommand("echo cos([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test cos with dictionary causes errors`() { + enterCommand("echo cos({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/CoshFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/CoshFunctionTest.kt new file mode 100644 index 000000000..1598f6ab7 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/CoshFunctionTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class CoshFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test cosh with Number`() { + assertCommandOutput("echo cosh(100)", "1.344059e43") + assertCommandOutput("echo cosh(0) cosh(1)", "1.0 1.543081") + } + + @Test + fun `test cosh with Float`() { + // The Vim docs show cosh(-0.5) == -1.127626, but the function returns the positive value... + // We are matching Vim's actual behaviour here + assertCommandOutput("echo cosh(0.5) cosh(-0.5)", "1.127626 1.127626") + } + + @Test + fun `test cosh with string causes errors`() { + enterCommand("echo cosh('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test cosh with invalid string causes errors`() { + enterCommand("echo cosh('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test cosh with list causes errors`() { + enterCommand("echo cosh([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test cosh with dictionary causes errors`() { + enterCommand("echo cosh({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinFunctionTest.kt index 2a0fc1a08..ffc43959d 100644 --- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinFunctionTest.kt +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinFunctionTest.kt @@ -21,7 +21,42 @@ class SinFunctionTest : VimTestCase() { } @Test - fun `test sin`() { + fun `test sin with Number`() { + assertCommandOutput("echo sin(100)", "-0.506366") assertCommandOutput("echo sin(0) sin(1)", "0.0 0.841471") } + + @Test + fun `test sin with Float`() { + assertCommandOutput("echo sin(-4.01)", "0.763301") + assertCommandOutput("echo sin(0.0) sin(1.0)", "0.0 0.841471") + } + + @Test + fun `test sin with string causes errors`() { + enterCommand("echo sin('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test sin with invalid string causes errors`() { + enterCommand("echo sin('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test sin with list causes errors`() { + enterCommand("echo sin([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test sin with dictionary causes errors`() { + enterCommand("echo sin({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } } diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinhFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinhFunctionTest.kt new file mode 100644 index 000000000..9f64d67df --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/SinhFunctionTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class SinhFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test sinh with Number`() { + assertCommandOutput("echo sinh(100)", "1.344059e43") + assertCommandOutput("echo sinh(0) sinh(1)", "0.0 1.175201") + } + + @Test + fun `test sinh with Float`() { + assertCommandOutput("echo sinh(0.5) sinh(-0.9)", "0.521095 -1.026517") + } + + @Test + fun `test sinh with string causes errors`() { + enterCommand("echo sinh('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test sinh with invalid string causes errors`() { + enterCommand("echo sinh('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test sinh with list causes errors`() { + enterCommand("echo sinh([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test sinh with dictionary causes errors`() { + enterCommand("echo sinh({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/TanFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/TanFunctionTest.kt new file mode 100644 index 000000000..346fcc873 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/TanFunctionTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class TanFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test tan with Number`() { + assertCommandOutput("echo tan(10)", "0.648361") + assertCommandOutput("echo tan(0) tan(1)", "0.0 1.557408") + } + + @Test + fun `test tan with Float`() { + assertCommandOutput("echo tan(-4.01)", "-1.181502") + assertCommandOutput("echo tan(0.0) tan(1.0)", "0.0 1.557408") + } + + @Test + fun `test tan with string causes errors`() { + enterCommand("echo tan('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test tan with invalid string causes errors`() { + enterCommand("echo tan('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test tan with list causes errors`() { + enterCommand("echo tan([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test tan with dictionary causes errors`() { + enterCommand("echo tan({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/TanhFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/TanhFunctionTest.kt new file mode 100644 index 000000000..788d2b972 --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/floatFunctions/TanhFunctionTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2003-2025 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.floatFunctions + +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +class TanhFunctionTest : VimTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + configureByText("\n") + } + + @Test + fun `test tanh with Number`() { + assertCommandOutput("echo tanh(100)", "1.0") + assertCommandOutput("echo tanh(0) tanh(1)", "0.0 0.761594") + } + + @Test + fun `test tanh with Float`() { + assertCommandOutput("echo tanh(0.5) tanh(-1.0)", "0.462117 -0.761594") + } + + @Test + fun `test tanh with string causes errors`() { + enterCommand("echo tanh('1.0')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test tanh with invalid string causes errors`() { + enterCommand("echo tanh('cheese')") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test tanh with list causes errors`() { + enterCommand("echo tanh([1.0])") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } + + @Test + fun `test tanh with dictionary causes errors`() { + enterCommand("echo tanh({1: 1.0})") + assertPluginError(true) + assertPluginErrorMessageContains("E808: Number or Float required") + } +} diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/datatypes/VimFloat.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/datatypes/VimFloat.kt index 2c56a54d3..17009cfc7 100644 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/datatypes/VimFloat.kt +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/datatypes/VimFloat.kt @@ -9,8 +9,8 @@ package com.maddyhome.idea.vim.vimscript.model.datatypes import com.maddyhome.idea.vim.ex.ExException -import java.math.BigDecimal -import java.math.RoundingMode +import java.text.DecimalFormat +import kotlin.math.abs data class VimFloat(val value: Double) : VimDataType() { @@ -27,8 +27,15 @@ data class VimFloat(val value: Double) : VimDataType() { } override fun toString(): String { - val bigDecimal = BigDecimal(value).setScale(6, RoundingMode.HALF_UP) - return bigDecimal.toDouble().toString() + if (value.isNaN()) return "nan" + return if (abs(value) >= 1e6 || (abs(value) < 1e-3 && value != 0.0)) { + val formatter = DecimalFormat("0.0#####E0") + formatter.decimalFormatSymbols = formatter.decimalFormatSymbols.apply { exponentSeparator = "e" } + formatter.format(value) + } + else { + DecimalFormat("0.0#####").format(value) + } } override fun deepCopy(level: Int): VimFloat { diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/FloatFunctionHandlerBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/FloatFunctionHandlerBase.kt new file mode 100644 index 000000000..c6ee93331 --- /dev/null +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/FloatFunctionHandlerBase.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2003-2025 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 com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions + +import com.maddyhome.idea.vim.api.ExecutionContext +import com.maddyhome.idea.vim.api.VimEditor +import com.maddyhome.idea.vim.ex.exExceptionMessage +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.VimFloat +import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt +import com.maddyhome.idea.vim.vimscript.model.expressions.Expression +import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler + +internal abstract class UnaryFloatFunctionHandlerBase : FunctionHandler() { + override val minimumNumberOfArguments = 1 + override val maximumNumberOfArguments = 1 + + override fun doFunction( + argumentValues: List<Expression>, + editor: VimEditor, + context: ExecutionContext, + vimContext: VimLContext, + ): VimDataType { + val argument = argumentValues[0].evaluate(editor, context, vimContext) + if (argument !is VimFloat && argument !is VimInt) { + throw exExceptionMessage("E808") // E808: Number or Float required + } + return VimFloat(invoke(argument.asDouble())) + } + + protected abstract fun invoke(argument: Double): Double +} + +internal abstract class BinaryFloatFunctionHandlerBase : FunctionHandler() { + override val minimumNumberOfArguments = 2 + override val maximumNumberOfArguments = 2 + + override fun doFunction( + argumentValues: List<Expression>, + editor: VimEditor, + context: ExecutionContext, + vimContext: VimLContext, + ): VimDataType { + val arg1 = argumentValues[0].evaluate(editor, context, vimContext) + val arg2 = argumentValues[1].evaluate(editor, context, vimContext) + if ((arg1 !is VimFloat && arg1 !is VimInt) || (arg2 !is VimFloat && arg2 !is VimInt)) { + throw exExceptionMessage("E808") // E808: Number or Float required + } + return VimFloat(invoke(arg1.asDouble(), arg2.asDouble())) + } + + protected abstract fun invoke(arg1: Double, arg2: Double): Double +} diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/HyperbolicFunctionHandlers.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/HyperbolicFunctionHandlers.kt new file mode 100644 index 000000000..0c24770af --- /dev/null +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/HyperbolicFunctionHandlers.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2003-2025 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 com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions + +import com.intellij.vim.annotations.VimscriptFunction +import kotlin.math.cosh +import kotlin.math.sinh +import kotlin.math.tanh + +@VimscriptFunction(name = "cosh") +internal class CoshFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = cosh(argument) +} + +@VimscriptFunction(name = "sinh") +internal class SinhFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = sinh(argument) +} + +@VimscriptFunction(name = "tanh") +internal class TanhFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = tanh(argument) +} diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/SinFunctionHandler.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/SinFunctionHandler.kt deleted file mode 100644 index f9f1b723b..000000000 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/SinFunctionHandler.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2003-2025 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 com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions - -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.VimFloat -import com.maddyhome.idea.vim.vimscript.model.expressions.Expression -import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler -import kotlin.math.sin - -@VimscriptFunction(name = "sin") -internal class SinFunctionHandler : FunctionHandler() { - override val minimumNumberOfArguments = 1 - override val maximumNumberOfArguments = 1 - - override fun doFunction( - argumentValues: List<Expression>, - editor: VimEditor, - context: ExecutionContext, - vimContext: VimLContext, - ): VimDataType { - val argument = argumentValues[0].evaluate(editor, context, vimContext) - return VimFloat(sin(argument.asDouble())) - } -} diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/TrigonometricFunctionHandlers.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/TrigonometricFunctionHandlers.kt new file mode 100644 index 000000000..06bdb5f7d --- /dev/null +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/functions/handlers/floatFunctions/TrigonometricFunctionHandlers.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2003-2025 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 com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions + +import com.intellij.vim.annotations.VimscriptFunction +import kotlin.math.acos +import kotlin.math.asin +import kotlin.math.atan +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.tan + +@VimscriptFunction(name = "acos") +internal class AcosFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = acos(argument) +} + +@VimscriptFunction(name = "asin") +internal class AsinFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = asin(argument) +} + +@VimscriptFunction(name = "atan") +internal class AtanFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = atan(argument) +} + +@VimscriptFunction(name = "atan2") +internal class Atan2FunctionHandler : BinaryFloatFunctionHandlerBase() { + override fun invoke(arg1: Double, arg2: Double) = atan2(arg1, arg2) +} + +@VimscriptFunction(name = "cos") +internal class CosFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = cos(argument) +} + +@VimscriptFunction(name = "sin") +internal class SinFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = sin(argument) +} + +@VimscriptFunction(name = "tan") +internal class TanFunctionHandler : UnaryFloatFunctionHandlerBase() { + override fun invoke(argument: Double) = tan(argument) +} 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 dc0121730..f88276cb6 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 @@ -1,7 +1,13 @@ { "abs": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.AbsFunctionHandler", + "acos": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.AcosFunctionHandler", "and": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.bitwiseFunctions.AndFunctionHandler", + "asin": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.AsinFunctionHandler", + "atan": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.AtanFunctionHandler", + "atan2": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.Atan2FunctionHandler", "col": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.cursorFunctions.ColFunctionHandler", + "cos": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.CosFunctionHandler", + "cosh": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.CoshFunctionHandler", "empty": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.EmptyFunctionHandler", "escape": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.stringFunctions.EscapeFunctionHandler", "exists": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.variousFunctions.ExistsFunctionHandler", @@ -15,9 +21,12 @@ "line": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.cursorFunctions.LineFunctionHandler", "or": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.bitwiseFunctions.OrFunctionHandler", "sin": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.SinFunctionHandler", + "sinh": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.SinhFunctionHandler", "split": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.listFunctions.SplitFunctionHandler", "submatch": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.stringFunctions.SubmatchFunctionHandler", + "tan": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.TanFunctionHandler", + "tanh": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.floatFunctions.TanhFunctionHandler", "tolower": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.stringFunctions.TolowerFunctionHandler", "toupper": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.stringFunctions.ToupperFunctionHandler", "xor": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.bitwiseFunctions.XorFunctionHandler" -} \ No newline at end of file +}