@google/gemini-cli 0.10.0-preview.1 → 0.11.0-nightly.20251020.a96f0659

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/README.md +9 -0
  2. package/dist/google-gemini-cli-0.11.0-nightly.20251015.203bad7c.tgz +0 -0
  3. package/dist/package.json +2 -2
  4. package/dist/src/commands/extensions/examples/mcp-server/example.d.ts +6 -0
  5. package/dist/src/commands/extensions/examples/mcp-server/example.js +46 -0
  6. package/dist/src/commands/extensions/examples/mcp-server/example.js.map +1 -0
  7. package/dist/src/commands/extensions/install.d.ts +1 -0
  8. package/dist/src/commands/extensions/install.js +15 -2
  9. package/dist/src/commands/extensions/install.js.map +1 -1
  10. package/dist/src/commands/extensions/list.js +3 -2
  11. package/dist/src/commands/extensions/list.js.map +1 -1
  12. package/dist/src/commands/extensions/update.js +2 -2
  13. package/dist/src/commands/extensions/update.js.map +1 -1
  14. package/dist/src/commands/mcp/add.test.d.ts +6 -0
  15. package/dist/src/commands/mcp/add.test.js +234 -0
  16. package/dist/src/commands/mcp/add.test.js.map +1 -0
  17. package/dist/src/commands/mcp/list.js +5 -3
  18. package/dist/src/commands/mcp/list.js.map +1 -1
  19. package/dist/src/commands/mcp/list.test.d.ts +6 -0
  20. package/dist/src/commands/mcp/list.test.js +117 -0
  21. package/dist/src/commands/mcp/list.test.js.map +1 -0
  22. package/dist/src/commands/mcp/remove.test.d.ts +6 -0
  23. package/dist/src/commands/mcp/remove.test.js +175 -0
  24. package/dist/src/commands/mcp/remove.test.js.map +1 -0
  25. package/dist/src/commands/mcp.test.d.ts +6 -0
  26. package/dist/src/commands/mcp.test.js +62 -0
  27. package/dist/src/commands/mcp.test.js.map +1 -0
  28. package/dist/src/config/auth.js +3 -1
  29. package/dist/src/config/auth.js.map +1 -1
  30. package/dist/src/config/auth.test.js +3 -1
  31. package/dist/src/config/auth.test.js.map +1 -1
  32. package/dist/src/config/config.d.ts +0 -11
  33. package/dist/src/config/config.integration.test.d.ts +6 -0
  34. package/dist/src/config/config.integration.test.js +351 -0
  35. package/dist/src/config/config.integration.test.js.map +1 -0
  36. package/dist/src/config/config.js +19 -74
  37. package/dist/src/config/config.js.map +1 -1
  38. package/dist/src/config/config.test.d.ts +6 -0
  39. package/dist/src/config/config.test.js +2001 -0
  40. package/dist/src/config/config.test.js.map +1 -0
  41. package/dist/src/config/extension.d.ts +1 -4
  42. package/dist/src/config/extension.js +64 -69
  43. package/dist/src/config/extension.js.map +1 -1
  44. package/dist/src/config/extension.test.d.ts +6 -0
  45. package/dist/src/config/extension.test.js +1176 -0
  46. package/dist/src/config/extension.test.js.map +1 -0
  47. package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
  48. package/dist/src/config/extensions/extensionEnablement.js +4 -3
  49. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  50. package/dist/src/config/extensions/extensionEnablement.test.js +21 -18
  51. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  52. package/dist/src/config/extensions/github.d.ts +15 -7
  53. package/dist/src/config/extensions/github.js +95 -21
  54. package/dist/src/config/extensions/github.js.map +1 -1
  55. package/dist/src/config/extensions/github.test.js +14 -12
  56. package/dist/src/config/extensions/github.test.js.map +1 -1
  57. package/dist/src/config/extensions/update.test.js +9 -9
  58. package/dist/src/config/extensions/update.test.js.map +1 -1
  59. package/dist/src/config/keyBindings.d.ts +2 -1
  60. package/dist/src/config/keyBindings.js +4 -2
  61. package/dist/src/config/keyBindings.js.map +1 -1
  62. package/dist/src/config/policy.js +13 -14
  63. package/dist/src/config/policy.js.map +1 -1
  64. package/dist/src/config/policy.test.js +3 -3
  65. package/dist/src/config/policy.test.js.map +1 -1
  66. package/dist/src/config/sandboxConfig.d.ts +0 -1
  67. package/dist/src/config/sandboxConfig.js +1 -3
  68. package/dist/src/config/sandboxConfig.js.map +1 -1
  69. package/dist/src/config/settings.test.d.ts +6 -0
  70. package/dist/src/config/settings.test.js +1937 -0
  71. package/dist/src/config/settings.test.js.map +1 -0
  72. package/dist/src/gemini.js +12 -10
  73. package/dist/src/gemini.js.map +1 -1
  74. package/dist/src/gemini.test.js +34 -11
  75. package/dist/src/gemini.test.js.map +1 -1
  76. package/dist/src/generated/git-commit.d.ts +2 -2
  77. package/dist/src/generated/git-commit.js +2 -2
  78. package/dist/src/generated/git-commit.js.map +1 -1
  79. package/dist/src/nonInteractiveCli.js +91 -3
  80. package/dist/src/nonInteractiveCli.js.map +1 -1
  81. package/dist/src/nonInteractiveCli.test.d.ts +6 -0
  82. package/dist/src/nonInteractiveCli.test.js +711 -0
  83. package/dist/src/nonInteractiveCli.test.js.map +1 -0
  84. package/dist/src/services/FileCommandLoader.test.d.ts +6 -0
  85. package/dist/src/services/FileCommandLoader.test.js +971 -0
  86. package/dist/src/services/FileCommandLoader.test.js.map +1 -0
  87. package/dist/src/services/prompt-processors/argumentProcessor.test.d.ts +6 -0
  88. package/dist/src/services/prompt-processors/argumentProcessor.test.js +40 -0
  89. package/dist/src/services/prompt-processors/argumentProcessor.test.js.map +1 -0
  90. package/dist/src/services/prompt-processors/shellProcessor.test.d.ts +6 -0
  91. package/dist/src/services/prompt-processors/shellProcessor.test.js +482 -0
  92. package/dist/src/services/prompt-processors/shellProcessor.test.js.map +1 -0
  93. package/dist/src/test-utils/render.d.ts +2 -1
  94. package/dist/src/test-utils/render.js +5 -2
  95. package/dist/src/test-utils/render.js.map +1 -1
  96. package/dist/src/ui/App.test.d.ts +6 -0
  97. package/dist/src/ui/App.test.js +110 -0
  98. package/dist/src/ui/App.test.js.map +1 -0
  99. package/dist/src/ui/AppContainer.js +39 -26
  100. package/dist/src/ui/AppContainer.js.map +1 -1
  101. package/dist/src/ui/AppContainer.test.js +35 -9
  102. package/dist/src/ui/AppContainer.test.js.map +1 -1
  103. package/dist/src/ui/auth/AuthDialog.d.ts +1 -1
  104. package/dist/src/ui/auth/AuthDialog.js +3 -1
  105. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  106. package/dist/src/ui/auth/useAuth.d.ts +1 -1
  107. package/dist/src/ui/auth/useAuth.js +3 -1
  108. package/dist/src/ui/auth/useAuth.js.map +1 -1
  109. package/dist/src/ui/commands/aboutCommand.js +1 -1
  110. package/dist/src/ui/commands/aboutCommand.test.d.ts +6 -0
  111. package/dist/src/ui/commands/aboutCommand.test.js +130 -0
  112. package/dist/src/ui/commands/aboutCommand.test.js.map +1 -0
  113. package/dist/src/ui/commands/authCommand.js +1 -1
  114. package/dist/src/ui/commands/authCommand.test.d.ts +6 -0
  115. package/dist/src/ui/commands/authCommand.test.js +30 -0
  116. package/dist/src/ui/commands/authCommand.test.js.map +1 -0
  117. package/dist/src/ui/commands/bugCommand.js +1 -1
  118. package/dist/src/ui/commands/bugCommand.test.d.ts +6 -0
  119. package/dist/src/ui/commands/bugCommand.test.js +105 -0
  120. package/dist/src/ui/commands/bugCommand.test.js.map +1 -0
  121. package/dist/src/ui/commands/chatCommand.js +1 -1
  122. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  123. package/dist/src/ui/commands/chatCommand.test.d.ts +6 -0
  124. package/dist/src/ui/commands/chatCommand.test.js +555 -0
  125. package/dist/src/ui/commands/chatCommand.test.js.map +1 -0
  126. package/dist/src/ui/commands/clearCommand.js +1 -1
  127. package/dist/src/ui/commands/clearCommand.test.d.ts +6 -0
  128. package/dist/src/ui/commands/clearCommand.test.js +76 -0
  129. package/dist/src/ui/commands/clearCommand.test.js.map +1 -0
  130. package/dist/src/ui/commands/compressCommand.js +1 -1
  131. package/dist/src/ui/commands/compressCommand.js.map +1 -1
  132. package/dist/src/ui/commands/compressCommand.test.d.ts +6 -0
  133. package/dist/src/ui/commands/compressCommand.test.js +98 -0
  134. package/dist/src/ui/commands/compressCommand.test.js.map +1 -0
  135. package/dist/src/ui/commands/copyCommand.test.d.ts +6 -0
  136. package/dist/src/ui/commands/copyCommand.test.js +242 -0
  137. package/dist/src/ui/commands/copyCommand.test.js.map +1 -0
  138. package/dist/src/ui/commands/corgiCommand.js +1 -1
  139. package/dist/src/ui/commands/corgiCommand.js.map +1 -1
  140. package/dist/src/ui/commands/corgiCommand.test.d.ts +6 -0
  141. package/dist/src/ui/commands/corgiCommand.test.js +28 -0
  142. package/dist/src/ui/commands/corgiCommand.test.js.map +1 -0
  143. package/dist/src/ui/commands/directoryCommand.test.d.ts +6 -0
  144. package/dist/src/ui/commands/directoryCommand.test.js +145 -0
  145. package/dist/src/ui/commands/directoryCommand.test.js.map +1 -0
  146. package/dist/src/ui/commands/docsCommand.js +1 -1
  147. package/dist/src/ui/commands/docsCommand.test.d.ts +6 -0
  148. package/dist/src/ui/commands/docsCommand.test.js +72 -0
  149. package/dist/src/ui/commands/docsCommand.test.js.map +1 -0
  150. package/dist/src/ui/commands/editorCommand.js +1 -1
  151. package/dist/src/ui/commands/editorCommand.test.d.ts +6 -0
  152. package/dist/src/ui/commands/editorCommand.test.js +27 -0
  153. package/dist/src/ui/commands/editorCommand.test.js.map +1 -0
  154. package/dist/src/ui/commands/extensionsCommand.test.d.ts +6 -0
  155. package/dist/src/ui/commands/extensionsCommand.test.js +241 -0
  156. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -0
  157. package/dist/src/ui/commands/helpCommand.js +1 -1
  158. package/dist/src/ui/commands/helpCommand.test.d.ts +6 -0
  159. package/dist/src/ui/commands/helpCommand.test.js +42 -0
  160. package/dist/src/ui/commands/helpCommand.test.js.map +1 -0
  161. package/dist/src/ui/commands/ideCommand.js +6 -6
  162. package/dist/src/ui/commands/ideCommand.test.d.ts +6 -0
  163. package/dist/src/ui/commands/ideCommand.test.js +203 -0
  164. package/dist/src/ui/commands/ideCommand.test.js.map +1 -0
  165. package/dist/src/ui/commands/initCommand.js +1 -1
  166. package/dist/src/ui/commands/initCommand.js.map +1 -1
  167. package/dist/src/ui/commands/initCommand.test.d.ts +6 -0
  168. package/dist/src/ui/commands/initCommand.test.js +80 -0
  169. package/dist/src/ui/commands/initCommand.test.js.map +1 -0
  170. package/dist/src/ui/commands/mcpCommand.js +98 -88
  171. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  172. package/dist/src/ui/commands/mcpCommand.test.d.ts +6 -0
  173. package/dist/src/ui/commands/mcpCommand.test.js +148 -0
  174. package/dist/src/ui/commands/mcpCommand.test.js.map +1 -0
  175. package/dist/src/ui/commands/memoryCommand.js +5 -5
  176. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  177. package/dist/src/ui/commands/memoryCommand.test.d.ts +6 -0
  178. package/dist/src/ui/commands/memoryCommand.test.js +266 -0
  179. package/dist/src/ui/commands/memoryCommand.test.js.map +1 -0
  180. package/dist/src/ui/commands/privacyCommand.js +1 -1
  181. package/dist/src/ui/commands/privacyCommand.test.d.ts +6 -0
  182. package/dist/src/ui/commands/privacyCommand.test.js +32 -0
  183. package/dist/src/ui/commands/privacyCommand.test.js.map +1 -0
  184. package/dist/src/ui/commands/quitCommand.js +1 -1
  185. package/dist/src/ui/commands/quitCommand.test.d.ts +6 -0
  186. package/dist/src/ui/commands/quitCommand.test.js +50 -0
  187. package/dist/src/ui/commands/quitCommand.test.js.map +1 -0
  188. package/dist/src/ui/commands/restoreCommand.test.d.ts +6 -0
  189. package/dist/src/ui/commands/restoreCommand.test.js +190 -0
  190. package/dist/src/ui/commands/restoreCommand.test.js.map +1 -0
  191. package/dist/src/ui/commands/settingsCommand.test.d.ts +6 -0
  192. package/dist/src/ui/commands/settingsCommand.test.js +30 -0
  193. package/dist/src/ui/commands/settingsCommand.test.js.map +1 -0
  194. package/dist/src/ui/commands/setupGithubCommand.test.js +1 -2
  195. package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
  196. package/dist/src/ui/commands/statsCommand.js +3 -3
  197. package/dist/src/ui/commands/statsCommand.js.map +1 -1
  198. package/dist/src/ui/commands/statsCommand.test.d.ts +6 -0
  199. package/dist/src/ui/commands/statsCommand.test.js +53 -0
  200. package/dist/src/ui/commands/statsCommand.test.js.map +1 -0
  201. package/dist/src/ui/commands/terminalSetupCommand.test.d.ts +6 -0
  202. package/dist/src/ui/commands/terminalSetupCommand.test.js +66 -0
  203. package/dist/src/ui/commands/terminalSetupCommand.test.js.map +1 -0
  204. package/dist/src/ui/commands/themeCommand.js +1 -1
  205. package/dist/src/ui/commands/themeCommand.test.d.ts +6 -0
  206. package/dist/src/ui/commands/themeCommand.test.js +32 -0
  207. package/dist/src/ui/commands/themeCommand.test.js.map +1 -0
  208. package/dist/src/ui/commands/toolsCommand.js +1 -1
  209. package/dist/src/ui/commands/toolsCommand.test.d.ts +6 -0
  210. package/dist/src/ui/commands/toolsCommand.test.js +100 -0
  211. package/dist/src/ui/commands/toolsCommand.test.js.map +1 -0
  212. package/dist/src/ui/commands/vimCommand.js +1 -1
  213. package/dist/src/ui/components/Composer.js +5 -3
  214. package/dist/src/ui/components/Composer.js.map +1 -1
  215. package/dist/src/ui/components/Composer.test.js +16 -1
  216. package/dist/src/ui/components/Composer.test.js.map +1 -1
  217. package/dist/src/ui/components/ContextSummaryDisplay.d.ts +0 -1
  218. package/dist/src/ui/components/ContextSummaryDisplay.js +2 -12
  219. package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
  220. package/dist/src/ui/components/ContextSummaryDisplay.test.d.ts +6 -0
  221. package/dist/src/ui/components/ContextSummaryDisplay.test.js +66 -0
  222. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -0
  223. package/dist/src/ui/components/DialogManager.js +1 -5
  224. package/dist/src/ui/components/DialogManager.js.map +1 -1
  225. package/dist/src/ui/components/EditorSettingsDialog.js +1 -1
  226. package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
  227. package/dist/src/ui/components/FolderTrustDialog.test.js +7 -3
  228. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  229. package/dist/src/ui/components/Footer.js +1 -1
  230. package/dist/src/ui/components/Footer.js.map +1 -1
  231. package/dist/src/ui/components/Footer.test.d.ts +6 -0
  232. package/dist/src/ui/components/Footer.test.js +231 -0
  233. package/dist/src/ui/components/Footer.test.js.map +1 -0
  234. package/dist/src/ui/components/InputPrompt.d.ts +4 -0
  235. package/dist/src/ui/components/InputPrompt.js +53 -4
  236. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  237. package/dist/src/ui/components/InputPrompt.test.d.ts +6 -0
  238. package/dist/src/ui/components/InputPrompt.test.js +1716 -0
  239. package/dist/src/ui/components/InputPrompt.test.js.map +1 -0
  240. package/dist/src/ui/components/ModelStatsDisplay.test.d.ts +6 -0
  241. package/dist/src/ui/components/ModelStatsDisplay.test.js +285 -0
  242. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -0
  243. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +22 -18
  244. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
  245. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +10 -2
  246. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  247. package/dist/src/ui/components/QueuedMessageDisplay.js +3 -3
  248. package/dist/src/ui/components/QueuedMessageDisplay.js.map +1 -1
  249. package/dist/src/ui/components/QueuedMessageDisplay.test.js +4 -0
  250. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  251. package/dist/src/ui/components/RawMarkdownIndicator.d.ts +7 -0
  252. package/dist/src/ui/components/RawMarkdownIndicator.js +8 -0
  253. package/dist/src/ui/components/RawMarkdownIndicator.js.map +1 -0
  254. package/dist/src/ui/components/SessionSummaryDisplay.test.d.ts +6 -0
  255. package/dist/src/ui/components/SessionSummaryDisplay.test.js +74 -0
  256. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -0
  257. package/dist/src/ui/components/SettingsDialog.js +8 -8
  258. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  259. package/dist/src/ui/components/SettingsDialog.test.js +188 -56
  260. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  261. package/dist/src/ui/components/StatsDisplay.test.d.ts +6 -0
  262. package/dist/src/ui/components/StatsDisplay.test.js +351 -0
  263. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -0
  264. package/dist/src/ui/components/ThemeDialog.d.ts +4 -2
  265. package/dist/src/ui/components/ThemeDialog.js +3 -3
  266. package/dist/src/ui/components/ThemeDialog.js.map +1 -1
  267. package/dist/src/ui/components/ThemeDialog.test.js +13 -0
  268. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  269. package/dist/src/ui/components/ToolStatsDisplay.test.d.ts +6 -0
  270. package/dist/src/ui/components/ToolStatsDisplay.test.js +227 -0
  271. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -0
  272. package/dist/src/ui/components/messages/GeminiMessage.js +3 -1
  273. package/dist/src/ui/components/messages/GeminiMessage.js.map +1 -1
  274. package/dist/src/ui/components/messages/GeminiMessage.test.d.ts +6 -0
  275. package/dist/src/ui/components/messages/GeminiMessage.test.js +35 -0
  276. package/dist/src/ui/components/messages/GeminiMessage.test.js.map +1 -0
  277. package/dist/src/ui/components/messages/GeminiMessageContent.js +3 -1
  278. package/dist/src/ui/components/messages/GeminiMessageContent.js.map +1 -1
  279. package/dist/src/ui/components/messages/Todo.d.ts +7 -0
  280. package/dist/src/ui/components/messages/Todo.js +59 -0
  281. package/dist/src/ui/components/messages/Todo.js.map +1 -0
  282. package/dist/src/ui/components/messages/Todo.test.d.ts +6 -0
  283. package/dist/src/ui/components/messages/Todo.test.js +113 -0
  284. package/dist/src/ui/components/messages/Todo.test.js.map +1 -0
  285. package/dist/src/ui/components/messages/ToolGroupMessage.js +1 -1
  286. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  287. package/dist/src/ui/components/messages/ToolMessage.js +8 -3
  288. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  289. package/dist/src/ui/components/messages/ToolMessage.test.js +2 -2
  290. package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
  291. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.d.ts +6 -0
  292. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js +30 -0
  293. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js.map +1 -0
  294. package/dist/src/ui/components/messages/UserShellMessage.js +1 -1
  295. package/dist/src/ui/components/messages/UserShellMessage.js.map +1 -1
  296. package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
  297. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  298. package/dist/src/ui/components/shared/text-buffer.test.d.ts +6 -0
  299. package/dist/src/ui/components/shared/text-buffer.test.js +1554 -0
  300. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -0
  301. package/dist/src/ui/components/shared/vim-buffer-actions.test.d.ts +6 -0
  302. package/dist/src/ui/components/shared/vim-buffer-actions.test.js +951 -0
  303. package/dist/src/ui/components/shared/vim-buffer-actions.test.js.map +1 -0
  304. package/dist/src/ui/components/views/McpStatus.d.ts +0 -1
  305. package/dist/src/ui/components/views/McpStatus.js +2 -2
  306. package/dist/src/ui/components/views/McpStatus.js.map +1 -1
  307. package/dist/src/ui/components/views/McpStatus.test.js +0 -5
  308. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  309. package/dist/src/ui/components/views/ToolsList.test.js +4 -4
  310. package/dist/src/ui/components/views/ToolsList.test.js.map +1 -1
  311. package/dist/src/ui/contexts/KeypressContext.d.ts +1 -0
  312. package/dist/src/ui/contexts/KeypressContext.js +176 -50
  313. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  314. package/dist/src/ui/contexts/KeypressContext.test.js +413 -14
  315. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  316. package/dist/src/ui/contexts/SessionContext.test.d.ts +6 -0
  317. package/dist/src/ui/contexts/SessionContext.test.js +177 -0
  318. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -0
  319. package/dist/src/ui/contexts/UIActionsContext.d.ts +5 -4
  320. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  321. package/dist/src/ui/contexts/UIStateContext.d.ts +3 -3
  322. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  323. package/dist/src/ui/hooks/slashCommandProcessor.test.d.ts +6 -0
  324. package/dist/src/ui/hooks/slashCommandProcessor.test.js +779 -0
  325. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -0
  326. package/dist/src/ui/hooks/useAtCompletion.js +2 -2
  327. package/dist/src/ui/hooks/useAtCompletion.js.map +1 -1
  328. package/dist/src/ui/hooks/useAtCompletion.test.d.ts +6 -0
  329. package/dist/src/ui/hooks/useAtCompletion.test.js +385 -0
  330. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -0
  331. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +0 -1
  332. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  333. package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -1
  334. package/dist/src/ui/hooks/useCommandCompletion.js +5 -3
  335. package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
  336. package/dist/src/ui/hooks/useCommandCompletion.test.d.ts +6 -0
  337. package/dist/src/ui/hooks/useCommandCompletion.test.js +375 -0
  338. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -0
  339. package/dist/src/ui/hooks/useConsoleMessages.test.d.ts +6 -0
  340. package/dist/src/ui/hooks/useConsoleMessages.test.js +110 -0
  341. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -0
  342. package/dist/src/ui/hooks/useExtensionUpdates.test.js +3 -3
  343. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  344. package/dist/src/ui/hooks/useFocus.test.d.ts +6 -0
  345. package/dist/src/ui/hooks/useFocus.test.js +115 -0
  346. package/dist/src/ui/hooks/useFocus.test.js.map +1 -0
  347. package/dist/src/ui/hooks/useFolderTrust.test.d.ts +6 -0
  348. package/dist/src/ui/hooks/useFolderTrust.test.js +164 -0
  349. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -0
  350. package/dist/src/ui/hooks/useGeminiStream.js +33 -31
  351. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  352. package/dist/src/ui/hooks/useGeminiStream.test.d.ts +6 -0
  353. package/dist/src/ui/hooks/useGeminiStream.test.js +1936 -0
  354. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -0
  355. package/dist/src/ui/hooks/useKeypress.test.d.ts +6 -0
  356. package/dist/src/ui/hooks/useKeypress.test.js +234 -0
  357. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -0
  358. package/dist/src/ui/hooks/useLoadingIndicator.test.js +5 -0
  359. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  360. package/dist/src/ui/hooks/useMessageQueue.d.ts +1 -0
  361. package/dist/src/ui/hooks/useMessageQueue.js +14 -0
  362. package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
  363. package/dist/src/ui/hooks/useMessageQueue.test.js +121 -0
  364. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  365. package/dist/src/ui/hooks/usePhraseCycler.d.ts +1 -0
  366. package/dist/src/ui/hooks/usePhraseCycler.js +156 -5
  367. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  368. package/dist/src/ui/hooks/usePhraseCycler.test.d.ts +6 -0
  369. package/dist/src/ui/hooks/usePhraseCycler.test.js +155 -0
  370. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -0
  371. package/dist/src/ui/hooks/useThemeCommand.d.ts +2 -1
  372. package/dist/src/ui/hooks/useThemeCommand.js +6 -0
  373. package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
  374. package/dist/src/ui/hooks/useToolScheduler.test.js +3 -0
  375. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  376. package/dist/src/ui/hooks/vim.test.d.ts +6 -0
  377. package/dist/src/ui/hooks/vim.test.js +1389 -0
  378. package/dist/src/ui/hooks/vim.test.js.map +1 -0
  379. package/dist/src/ui/keyMatchers.test.js +9 -3
  380. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  381. package/dist/src/ui/themes/theme.test.d.ts +6 -0
  382. package/dist/src/ui/themes/theme.test.js +85 -0
  383. package/dist/src/ui/themes/theme.test.js.map +1 -0
  384. package/dist/src/ui/types.d.ts +0 -1
  385. package/dist/src/ui/types.js.map +1 -1
  386. package/dist/src/ui/utils/CodeColorizer.d.ts +1 -1
  387. package/dist/src/ui/utils/CodeColorizer.js +4 -2
  388. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  389. package/dist/src/ui/utils/MarkdownDisplay.d.ts +1 -0
  390. package/dist/src/ui/utils/MarkdownDisplay.js +8 -1
  391. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  392. package/dist/src/ui/utils/commandUtils.js +18 -2
  393. package/dist/src/ui/utils/commandUtils.js.map +1 -1
  394. package/dist/src/ui/utils/commandUtils.test.js +61 -6
  395. package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
  396. package/dist/src/ui/utils/updateCheck.d.ts +2 -1
  397. package/dist/src/ui/utils/updateCheck.js +4 -1
  398. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  399. package/dist/src/ui/utils/updateCheck.test.js +25 -10
  400. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  401. package/dist/src/utils/errors.d.ts +1 -0
  402. package/dist/src/utils/errors.js +66 -5
  403. package/dist/src/utils/errors.js.map +1 -1
  404. package/dist/src/validateNonInterActiveAuth.test.d.ts +6 -0
  405. package/dist/src/validateNonInterActiveAuth.test.js +336 -0
  406. package/dist/src/validateNonInterActiveAuth.test.js.map +1 -0
  407. package/dist/src/zed-integration/zedIntegration.js +1 -2
  408. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  409. package/dist/tsconfig.tsbuildinfo +1 -1
  410. package/package.json +3 -3
  411. package/dist/google-gemini-cli-0.10.0-preview.0.tgz +0 -0
  412. package/dist/src/ui/components/WorkspaceMigrationDialog.d.ts +0 -11
  413. package/dist/src/ui/components/WorkspaceMigrationDialog.js +0 -44
  414. package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +0 -1
  415. package/dist/src/ui/hooks/useWorkspaceMigration.d.ts +0 -13
  416. package/dist/src/ui/hooks/useWorkspaceMigration.js +0 -59
  417. package/dist/src/ui/hooks/useWorkspaceMigration.js.map +0 -1
@@ -0,0 +1,951 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { describe, it, expect } from 'vitest';
7
+ import { handleVimAction } from './vim-buffer-actions.js';
8
+ const defaultVisualLayout = {
9
+ visualLines: [''],
10
+ logicalToVisualMap: [[[0, 0]]],
11
+ visualToLogicalMap: [[0, 0]],
12
+ };
13
+ // Helper to create test state
14
+ const createTestState = (lines = ['hello world'], cursorRow = 0, cursorCol = 0) => ({
15
+ lines,
16
+ cursorRow,
17
+ cursorCol,
18
+ preferredCol: null,
19
+ undoStack: [],
20
+ redoStack: [],
21
+ clipboard: null,
22
+ selectionAnchor: null,
23
+ viewportWidth: 80,
24
+ viewportHeight: 24,
25
+ visualLayout: defaultVisualLayout,
26
+ });
27
+ describe('vim-buffer-actions', () => {
28
+ describe('Movement commands', () => {
29
+ describe('vim_move_left', () => {
30
+ it('should move cursor left by count', () => {
31
+ const state = createTestState(['hello world'], 0, 5);
32
+ const action = {
33
+ type: 'vim_move_left',
34
+ payload: { count: 3 },
35
+ };
36
+ const result = handleVimAction(state, action);
37
+ expect(result).toHaveOnlyValidCharacters();
38
+ expect(result.cursorCol).toBe(2);
39
+ expect(result.preferredCol).toBeNull();
40
+ });
41
+ it('should not move past beginning of line', () => {
42
+ const state = createTestState(['hello'], 0, 2);
43
+ const action = {
44
+ type: 'vim_move_left',
45
+ payload: { count: 5 },
46
+ };
47
+ const result = handleVimAction(state, action);
48
+ expect(result).toHaveOnlyValidCharacters();
49
+ expect(result.cursorCol).toBe(0);
50
+ });
51
+ it('should wrap to previous line when at beginning', () => {
52
+ const state = createTestState(['line1', 'line2'], 1, 0);
53
+ const action = {
54
+ type: 'vim_move_left',
55
+ payload: { count: 1 },
56
+ };
57
+ const result = handleVimAction(state, action);
58
+ expect(result).toHaveOnlyValidCharacters();
59
+ expect(result.cursorRow).toBe(0);
60
+ expect(result.cursorCol).toBe(4); // On last character '1' of 'line1'
61
+ });
62
+ it('should handle multiple line wrapping', () => {
63
+ const state = createTestState(['abc', 'def', 'ghi'], 2, 0);
64
+ const action = {
65
+ type: 'vim_move_left',
66
+ payload: { count: 5 },
67
+ };
68
+ const result = handleVimAction(state, action);
69
+ expect(result).toHaveOnlyValidCharacters();
70
+ expect(result.cursorRow).toBe(0);
71
+ expect(result.cursorCol).toBe(1); // On 'b' after 5 left movements
72
+ });
73
+ it('should correctly handle h/l movement between lines', () => {
74
+ // Start at end of first line at 'd' (position 10)
75
+ let state = createTestState(['hello world', 'foo bar'], 0, 10);
76
+ // Move right - should go to beginning of next line
77
+ state = handleVimAction(state, {
78
+ type: 'vim_move_right',
79
+ payload: { count: 1 },
80
+ });
81
+ expect(state).toHaveOnlyValidCharacters();
82
+ expect(state.cursorRow).toBe(1);
83
+ expect(state.cursorCol).toBe(0); // Should be on 'f'
84
+ // Move left - should go back to end of previous line on 'd'
85
+ state = handleVimAction(state, {
86
+ type: 'vim_move_left',
87
+ payload: { count: 1 },
88
+ });
89
+ expect(state).toHaveOnlyValidCharacters();
90
+ expect(state.cursorRow).toBe(0);
91
+ expect(state.cursorCol).toBe(10); // Should be on 'd', not past it
92
+ });
93
+ });
94
+ describe('vim_move_right', () => {
95
+ it('should move cursor right by count', () => {
96
+ const state = createTestState(['hello world'], 0, 2);
97
+ const action = {
98
+ type: 'vim_move_right',
99
+ payload: { count: 3 },
100
+ };
101
+ const result = handleVimAction(state, action);
102
+ expect(result).toHaveOnlyValidCharacters();
103
+ expect(result.cursorCol).toBe(5);
104
+ });
105
+ it('should not move past last character of line', () => {
106
+ const state = createTestState(['hello'], 0, 3);
107
+ const action = {
108
+ type: 'vim_move_right',
109
+ payload: { count: 5 },
110
+ };
111
+ const result = handleVimAction(state, action);
112
+ expect(result).toHaveOnlyValidCharacters();
113
+ expect(result.cursorCol).toBe(4); // Last character of 'hello'
114
+ });
115
+ it('should wrap to next line when at end', () => {
116
+ const state = createTestState(['line1', 'line2'], 0, 4); // At end of 'line1'
117
+ const action = {
118
+ type: 'vim_move_right',
119
+ payload: { count: 1 },
120
+ };
121
+ const result = handleVimAction(state, action);
122
+ expect(result).toHaveOnlyValidCharacters();
123
+ expect(result.cursorRow).toBe(1);
124
+ expect(result.cursorCol).toBe(0);
125
+ });
126
+ it('should skip over combining marks to avoid cursor disappearing', () => {
127
+ // Test case for combining character cursor disappearing bug
128
+ // "café test" where é is represented as e + combining acute accent
129
+ const state = createTestState(['cafe\u0301 test'], 0, 2); // Start at 'f'
130
+ const action = {
131
+ type: 'vim_move_right',
132
+ payload: { count: 1 },
133
+ };
134
+ const result = handleVimAction(state, action);
135
+ expect(result).toHaveOnlyValidCharacters();
136
+ expect(result.cursorCol).toBe(3); // Should be on 'e' of 'café'
137
+ // Move right again - should skip combining mark and land on space
138
+ const result2 = handleVimAction(result, action);
139
+ expect(result2).toHaveOnlyValidCharacters();
140
+ expect(result2.cursorCol).toBe(5); // Should be on space after 'café'
141
+ });
142
+ });
143
+ describe('vim_move_up', () => {
144
+ it('should move cursor up by count', () => {
145
+ const state = createTestState(['line1', 'line2', 'line3'], 2, 3);
146
+ const action = { type: 'vim_move_up', payload: { count: 2 } };
147
+ const result = handleVimAction(state, action);
148
+ expect(result).toHaveOnlyValidCharacters();
149
+ expect(result.cursorRow).toBe(0);
150
+ expect(result.cursorCol).toBe(3);
151
+ });
152
+ it('should not move past first line', () => {
153
+ const state = createTestState(['line1', 'line2'], 1, 3);
154
+ const action = { type: 'vim_move_up', payload: { count: 5 } };
155
+ const result = handleVimAction(state, action);
156
+ expect(result).toHaveOnlyValidCharacters();
157
+ expect(result.cursorRow).toBe(0);
158
+ });
159
+ it('should adjust column for shorter lines', () => {
160
+ const state = createTestState(['short', 'very long line'], 1, 10);
161
+ const action = { type: 'vim_move_up', payload: { count: 1 } };
162
+ const result = handleVimAction(state, action);
163
+ expect(result).toHaveOnlyValidCharacters();
164
+ expect(result.cursorRow).toBe(0);
165
+ expect(result.cursorCol).toBe(4); // Last character 't' of 'short', not past it
166
+ });
167
+ });
168
+ describe('vim_move_down', () => {
169
+ it('should move cursor down by count', () => {
170
+ const state = createTestState(['line1', 'line2', 'line3'], 0, 2);
171
+ const action = {
172
+ type: 'vim_move_down',
173
+ payload: { count: 2 },
174
+ };
175
+ const result = handleVimAction(state, action);
176
+ expect(result).toHaveOnlyValidCharacters();
177
+ expect(result.cursorRow).toBe(2);
178
+ expect(result.cursorCol).toBe(2);
179
+ });
180
+ it('should not move past last line', () => {
181
+ const state = createTestState(['line1', 'line2'], 0, 2);
182
+ const action = {
183
+ type: 'vim_move_down',
184
+ payload: { count: 5 },
185
+ };
186
+ const result = handleVimAction(state, action);
187
+ expect(result).toHaveOnlyValidCharacters();
188
+ expect(result.cursorRow).toBe(1);
189
+ });
190
+ });
191
+ describe('vim_move_word_forward', () => {
192
+ it('should move to start of next word', () => {
193
+ const state = createTestState(['hello world test'], 0, 0);
194
+ const action = {
195
+ type: 'vim_move_word_forward',
196
+ payload: { count: 1 },
197
+ };
198
+ const result = handleVimAction(state, action);
199
+ expect(result).toHaveOnlyValidCharacters();
200
+ expect(result.cursorCol).toBe(6); // Start of 'world'
201
+ });
202
+ it('should handle multiple words', () => {
203
+ const state = createTestState(['hello world test'], 0, 0);
204
+ const action = {
205
+ type: 'vim_move_word_forward',
206
+ payload: { count: 2 },
207
+ };
208
+ const result = handleVimAction(state, action);
209
+ expect(result).toHaveOnlyValidCharacters();
210
+ expect(result.cursorCol).toBe(12); // Start of 'test'
211
+ });
212
+ it('should handle punctuation correctly', () => {
213
+ const state = createTestState(['hello, world!'], 0, 0);
214
+ const action = {
215
+ type: 'vim_move_word_forward',
216
+ payload: { count: 1 },
217
+ };
218
+ const result = handleVimAction(state, action);
219
+ expect(result).toHaveOnlyValidCharacters();
220
+ expect(result.cursorCol).toBe(5); // Start of ','
221
+ });
222
+ it('should move across empty lines when starting from within a word', () => {
223
+ // Testing the exact scenario: cursor on 'w' of 'hello world', w should move to next line
224
+ const state = createTestState(['hello world', ''], 0, 6); // At 'w' of 'world'
225
+ const action = {
226
+ type: 'vim_move_word_forward',
227
+ payload: { count: 1 },
228
+ };
229
+ const result = handleVimAction(state, action);
230
+ expect(result).toHaveOnlyValidCharacters();
231
+ expect(result.cursorRow).toBe(1);
232
+ expect(result.cursorCol).toBe(0); // Beginning of empty line
233
+ });
234
+ });
235
+ describe('vim_move_word_backward', () => {
236
+ it('should move to start of previous word', () => {
237
+ const state = createTestState(['hello world test'], 0, 12);
238
+ const action = {
239
+ type: 'vim_move_word_backward',
240
+ payload: { count: 1 },
241
+ };
242
+ const result = handleVimAction(state, action);
243
+ expect(result).toHaveOnlyValidCharacters();
244
+ expect(result.cursorCol).toBe(6); // Start of 'world'
245
+ });
246
+ it('should handle multiple words', () => {
247
+ const state = createTestState(['hello world test'], 0, 12);
248
+ const action = {
249
+ type: 'vim_move_word_backward',
250
+ payload: { count: 2 },
251
+ };
252
+ const result = handleVimAction(state, action);
253
+ expect(result).toHaveOnlyValidCharacters();
254
+ expect(result.cursorCol).toBe(0); // Start of 'hello'
255
+ });
256
+ });
257
+ describe('vim_move_word_end', () => {
258
+ it('should move to end of current word', () => {
259
+ const state = createTestState(['hello world'], 0, 0);
260
+ const action = {
261
+ type: 'vim_move_word_end',
262
+ payload: { count: 1 },
263
+ };
264
+ const result = handleVimAction(state, action);
265
+ expect(result).toHaveOnlyValidCharacters();
266
+ expect(result.cursorCol).toBe(4); // End of 'hello'
267
+ });
268
+ it('should move to end of next word if already at word end', () => {
269
+ const state = createTestState(['hello world'], 0, 4);
270
+ const action = {
271
+ type: 'vim_move_word_end',
272
+ payload: { count: 1 },
273
+ };
274
+ const result = handleVimAction(state, action);
275
+ expect(result).toHaveOnlyValidCharacters();
276
+ expect(result.cursorCol).toBe(10); // End of 'world'
277
+ });
278
+ it('should move across empty lines when at word end', () => {
279
+ const state = createTestState(['hello world', '', 'test'], 0, 10); // At 'd' of 'world'
280
+ const action = {
281
+ type: 'vim_move_word_end',
282
+ payload: { count: 1 },
283
+ };
284
+ const result = handleVimAction(state, action);
285
+ expect(result).toHaveOnlyValidCharacters();
286
+ expect(result.cursorRow).toBe(2);
287
+ expect(result.cursorCol).toBe(3); // Should be at 't' (end of 'test')
288
+ });
289
+ it('should handle consecutive word-end movements across empty lines', () => {
290
+ // Testing the exact scenario: cursor on 'w' of world, press 'e' twice
291
+ const state = createTestState(['hello world', ''], 0, 6); // At 'w' of 'world'
292
+ // First 'e' should move to 'd' of 'world'
293
+ let result = handleVimAction(state, {
294
+ type: 'vim_move_word_end',
295
+ payload: { count: 1 },
296
+ });
297
+ expect(result).toHaveOnlyValidCharacters();
298
+ expect(result.cursorRow).toBe(0);
299
+ expect(result.cursorCol).toBe(10); // At 'd' of 'world'
300
+ // Second 'e' should move to the empty line (end of file in this case)
301
+ result = handleVimAction(result, {
302
+ type: 'vim_move_word_end',
303
+ payload: { count: 1 },
304
+ });
305
+ expect(result).toHaveOnlyValidCharacters();
306
+ expect(result.cursorRow).toBe(1);
307
+ expect(result.cursorCol).toBe(0); // Empty line has col 0
308
+ });
309
+ it('should handle combining characters - advance from end of base character', () => {
310
+ // Test case for combining character word end bug
311
+ // "café test" where é is represented as e + combining acute accent
312
+ const state = createTestState(['cafe\u0301 test'], 0, 0); // Start at 'c'
313
+ // First 'e' command should move to the 'e' (position 3)
314
+ let result = handleVimAction(state, {
315
+ type: 'vim_move_word_end',
316
+ payload: { count: 1 },
317
+ });
318
+ expect(result).toHaveOnlyValidCharacters();
319
+ expect(result.cursorCol).toBe(3); // At 'e' of café
320
+ // Second 'e' command should advance to end of "test" (position 9), not stay stuck
321
+ result = handleVimAction(result, {
322
+ type: 'vim_move_word_end',
323
+ payload: { count: 1 },
324
+ });
325
+ expect(result).toHaveOnlyValidCharacters();
326
+ expect(result.cursorCol).toBe(9); // At 't' of "test"
327
+ });
328
+ it('should handle precomposed characters with diacritics', () => {
329
+ // Test case with precomposed é for comparison
330
+ const state = createTestState(['café test'], 0, 0);
331
+ // First 'e' command should move to the 'é' (position 3)
332
+ let result = handleVimAction(state, {
333
+ type: 'vim_move_word_end',
334
+ payload: { count: 1 },
335
+ });
336
+ expect(result).toHaveOnlyValidCharacters();
337
+ expect(result.cursorCol).toBe(3); // At 'é' of café
338
+ // Second 'e' command should advance to end of "test" (position 8)
339
+ result = handleVimAction(result, {
340
+ type: 'vim_move_word_end',
341
+ payload: { count: 1 },
342
+ });
343
+ expect(result).toHaveOnlyValidCharacters();
344
+ expect(result.cursorCol).toBe(8); // At 't' of "test"
345
+ });
346
+ });
347
+ describe('Position commands', () => {
348
+ it('vim_move_to_line_start should move to column 0', () => {
349
+ const state = createTestState(['hello world'], 0, 5);
350
+ const action = { type: 'vim_move_to_line_start' };
351
+ const result = handleVimAction(state, action);
352
+ expect(result).toHaveOnlyValidCharacters();
353
+ expect(result.cursorCol).toBe(0);
354
+ });
355
+ it('vim_move_to_line_end should move to last character', () => {
356
+ const state = createTestState(['hello world'], 0, 0);
357
+ const action = { type: 'vim_move_to_line_end' };
358
+ const result = handleVimAction(state, action);
359
+ expect(result).toHaveOnlyValidCharacters();
360
+ expect(result.cursorCol).toBe(10); // Last character of 'hello world'
361
+ });
362
+ it('vim_move_to_first_nonwhitespace should skip leading whitespace', () => {
363
+ const state = createTestState([' hello world'], 0, 0);
364
+ const action = { type: 'vim_move_to_first_nonwhitespace' };
365
+ const result = handleVimAction(state, action);
366
+ expect(result).toHaveOnlyValidCharacters();
367
+ expect(result.cursorCol).toBe(3); // Position of 'h'
368
+ });
369
+ it('vim_move_to_first_line should move to row 0', () => {
370
+ const state = createTestState(['line1', 'line2', 'line3'], 2, 5);
371
+ const action = { type: 'vim_move_to_first_line' };
372
+ const result = handleVimAction(state, action);
373
+ expect(result).toHaveOnlyValidCharacters();
374
+ expect(result.cursorRow).toBe(0);
375
+ expect(result.cursorCol).toBe(0);
376
+ });
377
+ it('vim_move_to_last_line should move to last row', () => {
378
+ const state = createTestState(['line1', 'line2', 'line3'], 0, 5);
379
+ const action = { type: 'vim_move_to_last_line' };
380
+ const result = handleVimAction(state, action);
381
+ expect(result).toHaveOnlyValidCharacters();
382
+ expect(result.cursorRow).toBe(2);
383
+ expect(result.cursorCol).toBe(0);
384
+ });
385
+ it('vim_move_to_line should move to specific line', () => {
386
+ const state = createTestState(['line1', 'line2', 'line3'], 0, 5);
387
+ const action = {
388
+ type: 'vim_move_to_line',
389
+ payload: { lineNumber: 2 },
390
+ };
391
+ const result = handleVimAction(state, action);
392
+ expect(result).toHaveOnlyValidCharacters();
393
+ expect(result.cursorRow).toBe(1); // 0-indexed
394
+ expect(result.cursorCol).toBe(0);
395
+ });
396
+ it('vim_move_to_line should clamp to valid range', () => {
397
+ const state = createTestState(['line1', 'line2'], 0, 0);
398
+ const action = {
399
+ type: 'vim_move_to_line',
400
+ payload: { lineNumber: 10 },
401
+ };
402
+ const result = handleVimAction(state, action);
403
+ expect(result).toHaveOnlyValidCharacters();
404
+ expect(result.cursorRow).toBe(1); // Last line
405
+ });
406
+ });
407
+ });
408
+ describe('Edit commands', () => {
409
+ describe('vim_delete_char', () => {
410
+ it('should delete single character', () => {
411
+ const state = createTestState(['hello'], 0, 1);
412
+ const action = {
413
+ type: 'vim_delete_char',
414
+ payload: { count: 1 },
415
+ };
416
+ const result = handleVimAction(state, action);
417
+ expect(result).toHaveOnlyValidCharacters();
418
+ expect(result.lines[0]).toBe('hllo');
419
+ expect(result.cursorCol).toBe(1);
420
+ });
421
+ it('should delete multiple characters', () => {
422
+ const state = createTestState(['hello'], 0, 1);
423
+ const action = {
424
+ type: 'vim_delete_char',
425
+ payload: { count: 3 },
426
+ };
427
+ const result = handleVimAction(state, action);
428
+ expect(result).toHaveOnlyValidCharacters();
429
+ expect(result.lines[0]).toBe('ho');
430
+ expect(result.cursorCol).toBe(1);
431
+ });
432
+ it('should not delete past end of line', () => {
433
+ const state = createTestState(['hello'], 0, 3);
434
+ const action = {
435
+ type: 'vim_delete_char',
436
+ payload: { count: 5 },
437
+ };
438
+ const result = handleVimAction(state, action);
439
+ expect(result).toHaveOnlyValidCharacters();
440
+ expect(result.lines[0]).toBe('hel');
441
+ expect(result.cursorCol).toBe(3);
442
+ });
443
+ it('should do nothing at end of line', () => {
444
+ const state = createTestState(['hello'], 0, 5);
445
+ const action = {
446
+ type: 'vim_delete_char',
447
+ payload: { count: 1 },
448
+ };
449
+ const result = handleVimAction(state, action);
450
+ expect(result).toHaveOnlyValidCharacters();
451
+ expect(result.lines[0]).toBe('hello');
452
+ expect(result.cursorCol).toBe(5);
453
+ });
454
+ });
455
+ describe('vim_delete_word_forward', () => {
456
+ it('should delete from cursor to next word start', () => {
457
+ const state = createTestState(['hello world test'], 0, 0);
458
+ const action = {
459
+ type: 'vim_delete_word_forward',
460
+ payload: { count: 1 },
461
+ };
462
+ const result = handleVimAction(state, action);
463
+ expect(result).toHaveOnlyValidCharacters();
464
+ expect(result.lines[0]).toBe('world test');
465
+ expect(result.cursorCol).toBe(0);
466
+ });
467
+ it('should delete multiple words', () => {
468
+ const state = createTestState(['hello world test'], 0, 0);
469
+ const action = {
470
+ type: 'vim_delete_word_forward',
471
+ payload: { count: 2 },
472
+ };
473
+ const result = handleVimAction(state, action);
474
+ expect(result).toHaveOnlyValidCharacters();
475
+ expect(result.lines[0]).toBe('test');
476
+ expect(result.cursorCol).toBe(0);
477
+ });
478
+ it('should delete to end if no more words', () => {
479
+ const state = createTestState(['hello world'], 0, 6);
480
+ const action = {
481
+ type: 'vim_delete_word_forward',
482
+ payload: { count: 2 },
483
+ };
484
+ const result = handleVimAction(state, action);
485
+ expect(result).toHaveOnlyValidCharacters();
486
+ expect(result.lines[0]).toBe('hello ');
487
+ expect(result.cursorCol).toBe(6);
488
+ });
489
+ });
490
+ describe('vim_delete_word_backward', () => {
491
+ it('should delete from cursor to previous word start', () => {
492
+ const state = createTestState(['hello world test'], 0, 12);
493
+ const action = {
494
+ type: 'vim_delete_word_backward',
495
+ payload: { count: 1 },
496
+ };
497
+ const result = handleVimAction(state, action);
498
+ expect(result).toHaveOnlyValidCharacters();
499
+ expect(result.lines[0]).toBe('hello test');
500
+ expect(result.cursorCol).toBe(6);
501
+ });
502
+ it('should delete multiple words backward', () => {
503
+ const state = createTestState(['hello world test'], 0, 12);
504
+ const action = {
505
+ type: 'vim_delete_word_backward',
506
+ payload: { count: 2 },
507
+ };
508
+ const result = handleVimAction(state, action);
509
+ expect(result).toHaveOnlyValidCharacters();
510
+ expect(result.lines[0]).toBe('test');
511
+ expect(result.cursorCol).toBe(0);
512
+ });
513
+ });
514
+ describe('vim_delete_line', () => {
515
+ it('should delete current line', () => {
516
+ const state = createTestState(['line1', 'line2', 'line3'], 1, 2);
517
+ const action = {
518
+ type: 'vim_delete_line',
519
+ payload: { count: 1 },
520
+ };
521
+ const result = handleVimAction(state, action);
522
+ expect(result).toHaveOnlyValidCharacters();
523
+ expect(result.lines).toEqual(['line1', 'line3']);
524
+ expect(result.cursorRow).toBe(1);
525
+ expect(result.cursorCol).toBe(0);
526
+ });
527
+ it('should delete multiple lines', () => {
528
+ const state = createTestState(['line1', 'line2', 'line3'], 0, 2);
529
+ const action = {
530
+ type: 'vim_delete_line',
531
+ payload: { count: 2 },
532
+ };
533
+ const result = handleVimAction(state, action);
534
+ expect(result).toHaveOnlyValidCharacters();
535
+ expect(result.lines).toEqual(['line3']);
536
+ expect(result.cursorRow).toBe(0);
537
+ expect(result.cursorCol).toBe(0);
538
+ });
539
+ it('should leave empty line when deleting all lines', () => {
540
+ const state = createTestState(['only line'], 0, 0);
541
+ const action = {
542
+ type: 'vim_delete_line',
543
+ payload: { count: 1 },
544
+ };
545
+ const result = handleVimAction(state, action);
546
+ expect(result).toHaveOnlyValidCharacters();
547
+ expect(result.lines).toEqual(['']);
548
+ expect(result.cursorRow).toBe(0);
549
+ expect(result.cursorCol).toBe(0);
550
+ });
551
+ });
552
+ describe('vim_delete_to_end_of_line', () => {
553
+ it('should delete from cursor to end of line', () => {
554
+ const state = createTestState(['hello world'], 0, 5);
555
+ const action = { type: 'vim_delete_to_end_of_line' };
556
+ const result = handleVimAction(state, action);
557
+ expect(result).toHaveOnlyValidCharacters();
558
+ expect(result.lines[0]).toBe('hello');
559
+ expect(result.cursorCol).toBe(5);
560
+ });
561
+ it('should do nothing at end of line', () => {
562
+ const state = createTestState(['hello'], 0, 5);
563
+ const action = { type: 'vim_delete_to_end_of_line' };
564
+ const result = handleVimAction(state, action);
565
+ expect(result).toHaveOnlyValidCharacters();
566
+ expect(result.lines[0]).toBe('hello');
567
+ });
568
+ });
569
+ });
570
+ describe('Insert mode commands', () => {
571
+ describe('vim_insert_at_cursor', () => {
572
+ it('should not change cursor position', () => {
573
+ const state = createTestState(['hello'], 0, 2);
574
+ const action = { type: 'vim_insert_at_cursor' };
575
+ const result = handleVimAction(state, action);
576
+ expect(result).toHaveOnlyValidCharacters();
577
+ expect(result.cursorRow).toBe(0);
578
+ expect(result.cursorCol).toBe(2);
579
+ });
580
+ });
581
+ describe('vim_append_at_cursor', () => {
582
+ it('should move cursor right by one', () => {
583
+ const state = createTestState(['hello'], 0, 2);
584
+ const action = { type: 'vim_append_at_cursor' };
585
+ const result = handleVimAction(state, action);
586
+ expect(result).toHaveOnlyValidCharacters();
587
+ expect(result.cursorCol).toBe(3);
588
+ });
589
+ it('should not move past end of line', () => {
590
+ const state = createTestState(['hello'], 0, 5);
591
+ const action = { type: 'vim_append_at_cursor' };
592
+ const result = handleVimAction(state, action);
593
+ expect(result).toHaveOnlyValidCharacters();
594
+ expect(result.cursorCol).toBe(5);
595
+ });
596
+ });
597
+ describe('vim_append_at_line_end', () => {
598
+ it('should move cursor to end of line', () => {
599
+ const state = createTestState(['hello world'], 0, 3);
600
+ const action = { type: 'vim_append_at_line_end' };
601
+ const result = handleVimAction(state, action);
602
+ expect(result).toHaveOnlyValidCharacters();
603
+ expect(result.cursorCol).toBe(11);
604
+ });
605
+ });
606
+ describe('vim_insert_at_line_start', () => {
607
+ it('should move to first non-whitespace character', () => {
608
+ const state = createTestState([' hello world'], 0, 5);
609
+ const action = { type: 'vim_insert_at_line_start' };
610
+ const result = handleVimAction(state, action);
611
+ expect(result).toHaveOnlyValidCharacters();
612
+ expect(result.cursorCol).toBe(2);
613
+ });
614
+ it('should move to column 0 for line with only whitespace', () => {
615
+ const state = createTestState([' '], 0, 1);
616
+ const action = { type: 'vim_insert_at_line_start' };
617
+ const result = handleVimAction(state, action);
618
+ expect(result).toHaveOnlyValidCharacters();
619
+ expect(result.cursorCol).toBe(3);
620
+ });
621
+ });
622
+ describe('vim_open_line_below', () => {
623
+ it('should insert a new line below the current one', () => {
624
+ const state = createTestState(['hello world'], 0, 5);
625
+ const action = { type: 'vim_open_line_below' };
626
+ const result = handleVimAction(state, action);
627
+ expect(result).toHaveOnlyValidCharacters();
628
+ expect(result.lines).toEqual(['hello world', '']);
629
+ expect(result.cursorRow).toBe(1);
630
+ expect(result.cursorCol).toBe(0);
631
+ });
632
+ });
633
+ describe('vim_open_line_above', () => {
634
+ it('should insert a new line above the current one', () => {
635
+ const state = createTestState(['hello', 'world'], 1, 2);
636
+ const action = { type: 'vim_open_line_above' };
637
+ const result = handleVimAction(state, action);
638
+ expect(result).toHaveOnlyValidCharacters();
639
+ expect(result.lines).toEqual(['hello', '', 'world']);
640
+ expect(result.cursorRow).toBe(1);
641
+ expect(result.cursorCol).toBe(0);
642
+ });
643
+ });
644
+ describe('vim_escape_insert_mode', () => {
645
+ it('should move cursor left', () => {
646
+ const state = createTestState(['hello'], 0, 3);
647
+ const action = { type: 'vim_escape_insert_mode' };
648
+ const result = handleVimAction(state, action);
649
+ expect(result).toHaveOnlyValidCharacters();
650
+ expect(result.cursorCol).toBe(2);
651
+ });
652
+ it('should not move past beginning of line', () => {
653
+ const state = createTestState(['hello'], 0, 0);
654
+ const action = { type: 'vim_escape_insert_mode' };
655
+ const result = handleVimAction(state, action);
656
+ expect(result).toHaveOnlyValidCharacters();
657
+ expect(result.cursorCol).toBe(0);
658
+ });
659
+ });
660
+ });
661
+ describe('Change commands', () => {
662
+ describe('vim_change_word_forward', () => {
663
+ it('should delete from cursor to next word start', () => {
664
+ const state = createTestState(['hello world test'], 0, 0);
665
+ const action = {
666
+ type: 'vim_change_word_forward',
667
+ payload: { count: 1 },
668
+ };
669
+ const result = handleVimAction(state, action);
670
+ expect(result).toHaveOnlyValidCharacters();
671
+ expect(result.lines[0]).toBe('world test');
672
+ expect(result.cursorCol).toBe(0);
673
+ });
674
+ });
675
+ describe('vim_change_line', () => {
676
+ it('should delete entire line content', () => {
677
+ const state = createTestState(['hello world'], 0, 5);
678
+ const action = {
679
+ type: 'vim_change_line',
680
+ payload: { count: 1 },
681
+ };
682
+ const result = handleVimAction(state, action);
683
+ expect(result).toHaveOnlyValidCharacters();
684
+ expect(result.lines[0]).toBe('');
685
+ expect(result.cursorCol).toBe(0);
686
+ });
687
+ });
688
+ describe('vim_change_movement', () => {
689
+ it('should change characters to the left', () => {
690
+ const state = createTestState(['hello world'], 0, 5);
691
+ const action = {
692
+ type: 'vim_change_movement',
693
+ payload: { movement: 'h', count: 2 },
694
+ };
695
+ const result = handleVimAction(state, action);
696
+ expect(result).toHaveOnlyValidCharacters();
697
+ expect(result.lines[0]).toBe('hel world');
698
+ expect(result.cursorCol).toBe(3);
699
+ });
700
+ it('should change characters to the right', () => {
701
+ const state = createTestState(['hello world'], 0, 5);
702
+ const action = {
703
+ type: 'vim_change_movement',
704
+ payload: { movement: 'l', count: 3 },
705
+ };
706
+ const result = handleVimAction(state, action);
707
+ expect(result).toHaveOnlyValidCharacters();
708
+ expect(result.lines[0]).toBe('hellorld'); // Deletes ' wo' (3 chars to the right)
709
+ expect(result.cursorCol).toBe(5);
710
+ });
711
+ it('should change multiple lines down', () => {
712
+ const state = createTestState(['line1', 'line2', 'line3'], 0, 2);
713
+ const action = {
714
+ type: 'vim_change_movement',
715
+ payload: { movement: 'j', count: 2 },
716
+ };
717
+ const result = handleVimAction(state, action);
718
+ expect(result).toHaveOnlyValidCharacters();
719
+ // The movement 'j' with count 2 changes 2 lines starting from cursor row
720
+ // Since we're at cursor position 2, it changes lines starting from current row
721
+ expect(result.lines).toEqual(['line1', 'line2', 'line3']); // No change because count > available lines
722
+ expect(result.cursorRow).toBe(0);
723
+ expect(result.cursorCol).toBe(2);
724
+ });
725
+ });
726
+ });
727
+ describe('Edge cases', () => {
728
+ it('should handle empty text', () => {
729
+ const state = createTestState([''], 0, 0);
730
+ const action = {
731
+ type: 'vim_move_word_forward',
732
+ payload: { count: 1 },
733
+ };
734
+ const result = handleVimAction(state, action);
735
+ expect(result).toHaveOnlyValidCharacters();
736
+ expect(result.cursorRow).toBe(0);
737
+ expect(result.cursorCol).toBe(0);
738
+ });
739
+ it('should handle single character line', () => {
740
+ const state = createTestState(['a'], 0, 0);
741
+ const action = { type: 'vim_move_to_line_end' };
742
+ const result = handleVimAction(state, action);
743
+ expect(result).toHaveOnlyValidCharacters();
744
+ expect(result.cursorCol).toBe(0); // Should be last character position
745
+ });
746
+ it('should handle empty lines in multi-line text', () => {
747
+ const state = createTestState(['line1', '', 'line3'], 1, 0);
748
+ const action = {
749
+ type: 'vim_move_word_forward',
750
+ payload: { count: 1 },
751
+ };
752
+ const result = handleVimAction(state, action);
753
+ expect(result).toHaveOnlyValidCharacters();
754
+ // Should move to next line with content
755
+ expect(result.cursorRow).toBe(2);
756
+ expect(result.cursorCol).toBe(0);
757
+ });
758
+ it('should preserve undo stack in operations', () => {
759
+ const state = createTestState(['hello'], 0, 0);
760
+ state.undoStack = [{ lines: ['previous'], cursorRow: 0, cursorCol: 0 }];
761
+ const action = {
762
+ type: 'vim_delete_char',
763
+ payload: { count: 1 },
764
+ };
765
+ const result = handleVimAction(state, action);
766
+ expect(result).toHaveOnlyValidCharacters();
767
+ expect(result.undoStack).toHaveLength(2); // Original plus new snapshot
768
+ });
769
+ });
770
+ describe('UTF-32 character handling in word/line operations', () => {
771
+ describe('Right-to-left text handling', () => {
772
+ it('should handle Arabic text in word movements', () => {
773
+ const state = createTestState(['hello مرحبا world'], 0, 0);
774
+ // Move to end of 'hello'
775
+ let result = handleVimAction(state, {
776
+ type: 'vim_move_word_end',
777
+ payload: { count: 1 },
778
+ });
779
+ expect(result).toHaveOnlyValidCharacters();
780
+ expect(result.cursorCol).toBe(4); // End of 'hello'
781
+ // Move to end of Arabic word
782
+ result = handleVimAction(result, {
783
+ type: 'vim_move_word_end',
784
+ payload: { count: 1 },
785
+ });
786
+ expect(result).toHaveOnlyValidCharacters();
787
+ expect(result.cursorCol).toBe(10); // End of Arabic word 'مرحبا'
788
+ });
789
+ });
790
+ describe('Chinese character handling', () => {
791
+ it('should handle Chinese characters in word movements', () => {
792
+ const state = createTestState(['hello 你好 world'], 0, 0);
793
+ // Move to end of 'hello'
794
+ let result = handleVimAction(state, {
795
+ type: 'vim_move_word_end',
796
+ payload: { count: 1 },
797
+ });
798
+ expect(result).toHaveOnlyValidCharacters();
799
+ expect(result.cursorCol).toBe(4); // End of 'hello'
800
+ // Move forward to start of 'world'
801
+ result = handleVimAction(result, {
802
+ type: 'vim_move_word_forward',
803
+ payload: { count: 1 },
804
+ });
805
+ expect(result).toHaveOnlyValidCharacters();
806
+ expect(result.cursorCol).toBe(6); // Start of '你好'
807
+ });
808
+ });
809
+ describe('Mixed script handling', () => {
810
+ it('should handle mixed Latin and non-Latin scripts with word end commands', () => {
811
+ const state = createTestState(['test中文test'], 0, 0);
812
+ let result = handleVimAction(state, {
813
+ type: 'vim_move_word_end',
814
+ payload: { count: 1 },
815
+ });
816
+ expect(result).toHaveOnlyValidCharacters();
817
+ expect(result.cursorCol).toBe(3); // End of 'test'
818
+ // Second word end command should move to end of '中文'
819
+ result = handleVimAction(result, {
820
+ type: 'vim_move_word_end',
821
+ payload: { count: 1 },
822
+ });
823
+ expect(result).toHaveOnlyValidCharacters();
824
+ expect(result.cursorCol).toBe(5); // End of '中文'
825
+ });
826
+ it('should handle mixed Latin and non-Latin scripts with word forward commands', () => {
827
+ const state = createTestState(['test中文test'], 0, 0);
828
+ let result = handleVimAction(state, {
829
+ type: 'vim_move_word_forward',
830
+ payload: { count: 1 },
831
+ });
832
+ expect(result).toHaveOnlyValidCharacters();
833
+ expect(result.cursorCol).toBe(4); // Start of '中'
834
+ // Second word forward command should move to start of final 'test'
835
+ result = handleVimAction(result, {
836
+ type: 'vim_move_word_forward',
837
+ payload: { count: 1 },
838
+ });
839
+ expect(result).toHaveOnlyValidCharacters();
840
+ expect(result.cursorCol).toBe(6); // Start of final 'test'
841
+ });
842
+ it('should handle mixed Latin and non-Latin scripts with word backward commands', () => {
843
+ const state = createTestState(['test中文test'], 0, 9); // Start at end of final 'test'
844
+ let result = handleVimAction(state, {
845
+ type: 'vim_move_word_backward',
846
+ payload: { count: 1 },
847
+ });
848
+ expect(result).toHaveOnlyValidCharacters();
849
+ expect(result.cursorCol).toBe(6); // Start of final 'test'
850
+ // Second word backward command should move to start of '中文'
851
+ result = handleVimAction(result, {
852
+ type: 'vim_move_word_backward',
853
+ payload: { count: 1 },
854
+ });
855
+ expect(result).toHaveOnlyValidCharacters();
856
+ expect(result.cursorCol).toBe(4); // Start of '中'
857
+ });
858
+ it('should handle Unicode block characters consistently with w and e commands', () => {
859
+ const state = createTestState(['██ █████ ██'], 0, 0);
860
+ // Test w command progression
861
+ let wResult = handleVimAction(state, {
862
+ type: 'vim_move_word_forward',
863
+ payload: { count: 1 },
864
+ });
865
+ expect(wResult).toHaveOnlyValidCharacters();
866
+ expect(wResult.cursorCol).toBe(3); // Start of second block sequence
867
+ wResult = handleVimAction(wResult, {
868
+ type: 'vim_move_word_forward',
869
+ payload: { count: 1 },
870
+ });
871
+ expect(wResult).toHaveOnlyValidCharacters();
872
+ expect(wResult.cursorCol).toBe(9); // Start of third block sequence
873
+ // Test e command progression from beginning
874
+ let eResult = handleVimAction(state, {
875
+ type: 'vim_move_word_end',
876
+ payload: { count: 1 },
877
+ });
878
+ expect(eResult).toHaveOnlyValidCharacters();
879
+ expect(eResult.cursorCol).toBe(1); // End of first block sequence
880
+ eResult = handleVimAction(eResult, {
881
+ type: 'vim_move_word_end',
882
+ payload: { count: 1 },
883
+ });
884
+ expect(eResult).toHaveOnlyValidCharacters();
885
+ expect(eResult.cursorCol).toBe(7); // End of second block sequence
886
+ eResult = handleVimAction(eResult, {
887
+ type: 'vim_move_word_end',
888
+ payload: { count: 1 },
889
+ });
890
+ expect(eResult).toHaveOnlyValidCharacters();
891
+ expect(eResult.cursorCol).toBe(10); // End of third block sequence
892
+ });
893
+ it('should handle strings starting with Chinese characters', () => {
894
+ const state = createTestState(['中文test英文word'], 0, 0);
895
+ // Test 'w' command - when at start of non-Latin word, w moves to next word
896
+ let wResult = handleVimAction(state, {
897
+ type: 'vim_move_word_forward',
898
+ payload: { count: 1 },
899
+ });
900
+ expect(wResult).toHaveOnlyValidCharacters();
901
+ expect(wResult.cursorCol).toBe(2); // Start of 'test'
902
+ wResult = handleVimAction(wResult, {
903
+ type: 'vim_move_word_forward',
904
+ payload: { count: 1 },
905
+ });
906
+ expect(wResult.cursorCol).toBe(6); // Start of '英文'
907
+ // Test 'e' command
908
+ let eResult = handleVimAction(state, {
909
+ type: 'vim_move_word_end',
910
+ payload: { count: 1 },
911
+ });
912
+ expect(eResult).toHaveOnlyValidCharacters();
913
+ expect(eResult.cursorCol).toBe(1); // End of 中文
914
+ eResult = handleVimAction(eResult, {
915
+ type: 'vim_move_word_end',
916
+ payload: { count: 1 },
917
+ });
918
+ expect(eResult.cursorCol).toBe(5); // End of test
919
+ });
920
+ it('should handle strings starting with Arabic characters', () => {
921
+ const state = createTestState(['مرحباhelloسلام'], 0, 0);
922
+ // Test 'w' command - when at start of non-Latin word, w moves to next word
923
+ let wResult = handleVimAction(state, {
924
+ type: 'vim_move_word_forward',
925
+ payload: { count: 1 },
926
+ });
927
+ expect(wResult).toHaveOnlyValidCharacters();
928
+ expect(wResult.cursorCol).toBe(5); // Start of 'hello'
929
+ wResult = handleVimAction(wResult, {
930
+ type: 'vim_move_word_forward',
931
+ payload: { count: 1 },
932
+ });
933
+ expect(wResult.cursorCol).toBe(10); // Start of 'سلام'
934
+ // Test 'b' command from end
935
+ const bState = createTestState(['مرحباhelloسلام'], 0, 13);
936
+ let bResult = handleVimAction(bState, {
937
+ type: 'vim_move_word_backward',
938
+ payload: { count: 1 },
939
+ });
940
+ expect(bResult).toHaveOnlyValidCharacters();
941
+ expect(bResult.cursorCol).toBe(10); // Start of سلام
942
+ bResult = handleVimAction(bResult, {
943
+ type: 'vim_move_word_backward',
944
+ payload: { count: 1 },
945
+ });
946
+ expect(bResult.cursorCol).toBe(5); // Start of hello
947
+ });
948
+ });
949
+ });
950
+ });
951
+ //# sourceMappingURL=vim-buffer-actions.test.js.map