luca 0.9.9 → 0.9.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (604) hide show
  1. data/CHANGELOG +9 -153
  2. data/Gemfile +1 -10
  3. data/Gemfile.lock +40 -113
  4. data/Guardfile +25 -3
  5. data/README.md +57 -54
  6. data/ROADMAP +32 -15
  7. data/Rakefile +75 -24
  8. data/app.rb +46 -12
  9. data/{app/assets → assets}/images/glyphicons-halflings-white.png +0 -0
  10. data/{app/assets → assets}/images/glyphicons-halflings.png +0 -0
  11. data/assets/javascripts/dependencies/backbone-min.js +37 -0
  12. data/assets/javascripts/dependencies/backbone-query.min.js +1 -0
  13. data/assets/javascripts/dependencies/bootstrap.min.js +7 -0
  14. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-coffeescript.js +0 -0
  15. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-css.js +0 -0
  16. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-html.js +0 -0
  17. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-javascript.js +0 -0
  18. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-less.js +0 -0
  19. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror-vim.js +0 -0
  20. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/codemirror.js +0 -0
  21. data/assets/javascripts/dependencies/coffee-script.js +12189 -0
  22. data/{spec → assets}/javascripts/dependencies/jasmine-html.js +0 -0
  23. data/{spec → assets}/javascripts/dependencies/jasmine.js +0 -0
  24. data/assets/javascripts/dependencies/jquery.js +4 -0
  25. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/modal.js +0 -0
  26. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/modernizr.min.js +0 -0
  27. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/prettify.js +0 -0
  28. data/{spec → assets}/javascripts/dependencies/sinon.js +0 -0
  29. data/{vendor/assets/javascripts → assets/javascripts/dependencies}/spin-min.js +0 -0
  30. data/assets/javascripts/dependencies/underscore-min.js +31 -0
  31. data/assets/javascripts/dependencies/underscore-string.min.js +14 -0
  32. data/assets/javascripts/dependencies.coffee +5 -0
  33. data/assets/javascripts/luca/index.coffee +1 -0
  34. data/assets/javascripts/luca-templates.js +1 -0
  35. data/assets/javascripts/luca-ui-base.coffee +1 -0
  36. data/assets/javascripts/luca-ui-bootstrap.js +1 -0
  37. data/assets/javascripts/luca-ui-development-tools.coffee +9 -0
  38. data/assets/javascripts/luca-ui-full.js +3 -0
  39. data/assets/javascripts/luca-ui-spec.coffee +2 -0
  40. data/assets/javascripts/luca-ui.js +3 -0
  41. data/assets/javascripts/sandbox/application.coffee +57 -0
  42. data/assets/javascripts/sandbox/config.coffee +7 -0
  43. data/assets/javascripts/sandbox/router.coffee +24 -0
  44. data/assets/javascripts/sandbox/templates/builder/component_list.luca +1 -0
  45. data/assets/javascripts/sandbox/templates/builder.luca +2 -0
  46. data/assets/javascripts/sandbox/templates/main.luca +53 -0
  47. data/assets/javascripts/sandbox/templates/sandbox/docs_index.luca +1 -0
  48. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +8 -0
  49. data/assets/javascripts/sandbox/templates/sandbox/readme.luca +30 -0
  50. data/assets/javascripts/sandbox/templates/sandbox.luca +1 -0
  51. data/assets/javascripts/sandbox/views/builder/builder_canvas.coffee +3 -0
  52. data/assets/javascripts/sandbox/views/builder/builder_editor.coffee +6 -0
  53. data/assets/javascripts/sandbox/views/builder/component_list.coffee +38 -0
  54. data/assets/javascripts/sandbox/views/builder/project_browser.coffee +14 -0
  55. data/assets/javascripts/sandbox/views/builder.coffee +133 -0
  56. data/assets/javascripts/sandbox/views/docs_controller.coffee +7 -0
  57. data/assets/javascripts/sandbox/views/inspector/instance_filter.coffee +18 -0
  58. data/{app/assets/stylesheets/luca/containers/modal_view.scss → assets/javascripts/sandbox/views/inspector/instance_list.coffee} +0 -0
  59. data/assets/javascripts/sandbox/views/inspector.coffee +11 -0
  60. data/assets/javascripts/sandbox/views/top_navigation.coffee +4 -0
  61. data/assets/javascripts/sandbox.coffee +7 -0
  62. data/assets/javascripts/spec-dependencies.coffee +4 -0
  63. data/assets/stylesheets/bootstrap-responsive.min.css +2 -0
  64. data/assets/stylesheets/bootstrap.min.css +727 -0
  65. data/{vendor/assets → assets}/stylesheets/codemirror-blackboard.css +0 -0
  66. data/{vendor/assets → assets}/stylesheets/codemirror-monokai.css +0 -0
  67. data/{vendor/assets → assets}/stylesheets/codemirror.css +0 -0
  68. data/{vendor/assets → assets}/stylesheets/jasmine.css +0 -0
  69. data/assets/stylesheets/luca-ui-bootstrap.css +4 -0
  70. data/assets/stylesheets/luca-ui-development-tools.css +5 -0
  71. data/assets/stylesheets/luca-ui-full.css +3 -0
  72. data/assets/stylesheets/luca-ui-spec.css +3 -0
  73. data/assets/stylesheets/luca-ui.css +3 -0
  74. data/assets/stylesheets/prettify.css +40 -0
  75. data/assets/stylesheets/sandbox/builder.scss +79 -0
  76. data/assets/stylesheets/sandbox/sandbox.scss +18 -0
  77. data/assets/stylesheets/sandbox.css +3 -0
  78. data/assets/stylesheets/themes/amelia-bootstrap.css +826 -0
  79. data/assets/stylesheets/themes/slate-bootstrap.css +797 -0
  80. data/assets/stylesheets/themes/superhero-bootstrap.css +830 -0
  81. data/config.ru +2 -1
  82. data/docs/{old/application.md → application.md} +0 -0
  83. data/docs/{old/collection.md → collection.md} +0 -0
  84. data/docs/{old/collection_manager.md → collection_manager.md} +0 -0
  85. data/docs/{old/container_philosophy.md → container_philosophy.md} +0 -0
  86. data/docs/{old/event_binding_helpers.md → event_binding_helpers.md} +0 -0
  87. data/docs/{old/method_caching_and_computed_properties.md → method_caching_and_computed_properties.md} +0 -0
  88. data/docs/{old/view.md → view.md} +0 -0
  89. data/lib/luca/code_browser.rb +55 -0
  90. data/lib/luca/command_line.rb +69 -0
  91. data/lib/luca/rails/engine.rb +0 -12
  92. data/lib/luca/rails/version.rb +2 -1
  93. data/lib/luca/rails.rb +3 -5
  94. data/lib/luca/template.rb +0 -2
  95. data/lib/luca.rb +1 -25
  96. data/luca.gemspec +7 -16
  97. data/site/assets/bootstrap.min.js +7 -0
  98. data/site/assets/dependencies.js +94 -0
  99. data/site/assets/glyphicons-halflings-white.png +0 -0
  100. data/site/assets/glyphicons-halflings.png +0 -0
  101. data/site/assets/luca-ui-bootstrap.css +1331 -0
  102. data/site/assets/luca-ui-bootstrap.js +9 -0
  103. data/site/assets/luca-ui-development-tools.css +234 -0
  104. data/site/assets/luca-ui-development-tools.js +18561 -0
  105. data/site/assets/luca-ui-development-tools.min.js +15 -0
  106. data/site/assets/luca-ui-full.min.js +8 -0
  107. data/site/assets/luca-ui.min.js +4 -0
  108. data/site/assets/sandbox.css +62 -0
  109. data/site/assets/sandbox.js +469 -0
  110. data/site/docs/application.html +41 -0
  111. data/site/docs/caching.html +43 -0
  112. data/site/docs/collection.html +75 -0
  113. data/site/docs/collection_manager.html +71 -0
  114. data/site/docs/containers.html +118 -0
  115. data/site/docs/events.html +153 -0
  116. data/site/docs/view.html +128 -0
  117. data/site/img/glyphicons-halflings-white.png +0 -0
  118. data/site/img/glyphicons-halflings.png +0 -0
  119. data/site/index.html +20 -0
  120. data/site/source-map.js +1 -0
  121. data/spec/{javascripts/components → components}/application_spec.coffee +0 -0
  122. data/spec/{javascripts/components → components}/collection_loader_view_spec.coffee +0 -0
  123. data/{site/source/app/assets/javascripts/docs/lib/util.coffee → spec/components/controller_spec.coffee} +0 -0
  124. data/spec/{javascripts/components → components}/fields/checkbox_array_spec.coffee +0 -0
  125. data/spec/components/form_view_spec.coffee +80 -0
  126. data/spec/{javascripts/components → components}/grid_view_spec.coffee +0 -0
  127. data/spec/{javascripts/components → components}/record_manager_spec.coffee +0 -0
  128. data/spec/{javascripts/components → components}/template_spec.coffee +0 -0
  129. data/spec/containers/card_view_spec.coffee +50 -0
  130. data/{site/source/app/assets/javascripts/docs/views/components/code_editor/index.coffee → spec/containers/column_view_spec.coffee} +0 -0
  131. data/spec/{javascripts/containers → containers}/modal_view_spec.coffee +0 -0
  132. data/spec/{javascripts/containers → containers}/panel_view_spec.coffee +0 -0
  133. data/spec/{javascripts/components/pagination_control_spec.coffee → containers/split_view_spec.coffee} +0 -0
  134. data/spec/{javascripts/containers → containers}/tab_view_spec.coffee +0 -0
  135. data/spec/{javascripts/containers → containers}/viewport_spec.coffee +0 -0
  136. data/spec/{javascripts/core → core}/collection_spec.coffee +1 -26
  137. data/spec/core/container_spec.coffee +66 -0
  138. data/spec/{javascripts/concerns/paginatable_spec.coffee → core/field_spec.coffee} +0 -0
  139. data/spec/{javascripts/core → core}/model_spec.coffee +3 -26
  140. data/spec/{javascripts/core → core}/observer_spec.coffee +0 -0
  141. data/spec/{javascripts/core → core}/view_spec.coffee +38 -114
  142. data/spec/{javascripts/core/framework_spec.coffee → framework_spec.coffee} +2 -31
  143. data/spec/{javascripts/helper.coffee → helper.coffee} +0 -0
  144. data/spec/{javascripts/managers → managers}/collection_manager_spec.coffee +0 -0
  145. data/spec/{javascripts/managers → managers}/socket_manager_spec.coffee +0 -0
  146. data/src/components/application.coffee +318 -0
  147. data/src/components/base_toolbar.coffee +15 -0
  148. data/{app/assets/javascripts/luca → src}/components/collection_loader_view.coffee +1 -3
  149. data/src/components/collection_view.coffee +131 -0
  150. data/src/components/controller.coffee +63 -0
  151. data/src/components/fields/button_field.coffee +38 -0
  152. data/{app/assets/javascripts/luca → src}/components/fields/checkbox_array.coffee +7 -18
  153. data/{app/assets/javascripts/luca → src}/components/fields/checkbox_field.coffee +10 -19
  154. data/{app/assets/javascripts/luca → src}/components/fields/file_upload_field.coffee +4 -5
  155. data/src/components/fields/hidden_field.coffee +12 -0
  156. data/src/components/fields/label_field.coffee +14 -0
  157. data/src/components/fields/select_field.coffee +97 -0
  158. data/src/components/fields/text_area_field.coffee +40 -0
  159. data/{app/assets/javascripts/luca → src}/components/fields/text_field.coffee +5 -16
  160. data/src/components/fields/type_ahead_field.coffee +22 -0
  161. data/src/components/form_button_toolbar.coffee +25 -0
  162. data/src/components/form_view.coffee +275 -0
  163. data/src/components/grid_view.coffee +269 -0
  164. data/src/components/index.coffee +1 -0
  165. data/src/components/load_mask.coffee +3 -0
  166. data/src/components/nav_bar.coffee +22 -0
  167. data/src/components/page_controller.coffee +2 -0
  168. data/{app/assets/javascripts/luca → src}/components/record_manager.coffee +0 -0
  169. data/{app/assets/javascripts/luca → src}/components/router.coffee +1 -6
  170. data/src/components/table_view.coffee +53 -0
  171. data/src/components/template.coffee +5 -0
  172. data/src/components/toolbar_dialog.coffee +25 -0
  173. data/src/containers/card_view.coffee +93 -0
  174. data/src/containers/column_view.coffee +42 -0
  175. data/{app/assets/javascripts/luca → src}/containers/modal_view.coffee +9 -9
  176. data/src/containers/page_view.coffee +2 -0
  177. data/{app/assets/javascripts/luca → src}/containers/panel_toolbar.coffee +84 -89
  178. data/src/containers/panel_view.coffee +23 -0
  179. data/src/containers/split_view.coffee +8 -0
  180. data/{app/assets/javascripts/luca → src}/containers/tab_view.coffee +21 -27
  181. data/{app/assets/javascripts/luca → src}/containers/viewport.coffee +18 -14
  182. data/{app/assets/javascripts/luca → src}/core/collection.coffee +51 -97
  183. data/src/core/container.coffee +374 -0
  184. data/{tutorials/component-definitions.md → src/core/core.coffee} +0 -0
  185. data/src/core/field.coffee +79 -0
  186. data/src/core/model.coffee +43 -0
  187. data/{app/assets/javascripts/luca → src}/core/observer.coffee +0 -0
  188. data/src/core/panel.coffee +118 -0
  189. data/{app/assets/javascripts/luca/core/registry → src/core}/registry.coffee +43 -63
  190. data/{app/assets/javascripts/luca/util → src/core}/script_loader.coffee +0 -0
  191. data/src/core/view.coffee +264 -0
  192. data/src/define.coffee +110 -0
  193. data/{app/assets/javascripts/luca → src}/framework.coffee +103 -98
  194. data/src/index.coffee +23 -0
  195. data/{app/assets/javascripts/luca → src}/managers/collection_manager.coffee +13 -37
  196. data/src/managers/socket_manager.coffee +54 -0
  197. data/{app/assets/javascripts/luca/concerns → src/modules}/deferrable.coffee +4 -5
  198. data/src/modules/grid_layout.coffee +3 -0
  199. data/{app/assets/javascripts/luca/concerns/loadmaskable.coffee → src/modules/load_mask.coffee} +11 -23
  200. data/{app/assets/javascripts/luca/concerns → src/modules}/local_storage.coffee +0 -0
  201. data/src/plugins/development_tool_helpers.coffee +21 -0
  202. data/src/plugins/events.coffee +54 -0
  203. data/src/samples/definition.coffee +49 -0
  204. data/{tutorials/component-definitions/01_intro.md → src/stylesheets/base.scss} +0 -0
  205. data/{app/assets/stylesheets/luca → src/stylesheets}/components/checkbox_array.scss +1 -1
  206. data/src/stylesheets/components/form_view.scss +59 -0
  207. data/{app/assets/stylesheets/luca → src/stylesheets}/components/grid_view.scss +0 -0
  208. data/{app/assets/stylesheets/luca → src/stylesheets}/components/load_mask.scss +0 -0
  209. data/src/stylesheets/components/toolbar.scss +0 -0
  210. data/{app/assets/stylesheets/luca → src/stylesheets}/components/viewport.scss +4 -1
  211. data/src/stylesheets/containers/container.scss +16 -0
  212. data/src/stylesheets/containers/modal_view.scss +0 -0
  213. data/{app/assets/stylesheets/luca → src/stylesheets}/containers/panels.scss +0 -0
  214. data/{app/assets/stylesheets/luca → src/stylesheets}/containers/tab_view.scss +5 -5
  215. data/{app/assets/stylesheets/luca → src/stylesheets}/normalize.scss +0 -0
  216. data/src/stylesheets/tools/class_browser.scss +32 -0
  217. data/src/stylesheets/tools/code_editor.scss +24 -0
  218. data/src/stylesheets/tools/component_tester.scss +26 -0
  219. data/{app/assets/stylesheets/luca/development → src/stylesheets/tools}/console.scss +0 -0
  220. data/src/templates/components/bootstrap_form_controls.luca +7 -0
  221. data/src/templates/components/collection_loader_view.luca +5 -0
  222. data/src/templates/components/form_alert +0 -0
  223. data/src/templates/components/form_alert.luca +3 -0
  224. data/src/templates/components/grid_view.luca +7 -0
  225. data/src/templates/components/grid_view_empty_text.luca +3 -0
  226. data/src/templates/components/load_mask.luca +3 -0
  227. data/src/templates/components/nav_bar.luca +2 -0
  228. data/src/templates/containers/basic.luca +1 -0
  229. data/src/templates/containers/tab_selector_container.luca +8 -0
  230. data/src/templates/containers/tab_view.luca +2 -0
  231. data/src/templates/containers/toolbar_wrapper.luca +1 -0
  232. data/src/templates/fields/button_field.luca +2 -0
  233. data/src/templates/fields/button_field_link.luca +5 -0
  234. data/src/templates/fields/checkbox_array.luca +4 -0
  235. data/src/templates/fields/checkbox_array_item.luca +4 -0
  236. data/src/templates/fields/checkbox_field.luca +9 -0
  237. data/src/templates/fields/file_upload_field.luca +8 -0
  238. data/src/templates/fields/hidden_field.luca +1 -0
  239. data/src/templates/fields/select_field.luca +8 -0
  240. data/src/templates/fields/text_area_field.luca +8 -0
  241. data/src/templates/fields/text_field.luca +17 -0
  242. data/src/templates/sample/contents.luca +1 -0
  243. data/src/templates/sample/welcome.luca +1 -0
  244. data/src/templates/table_view.luca +4 -0
  245. data/src/tools/application_inspector.coffee +2 -0
  246. data/src/tools/code_editor.coffee +258 -0
  247. data/{app/assets/javascripts/luca/development → src/tools}/code_mirror_field.coffee +1 -2
  248. data/src/tools/coffee_script_editor.coffee +82 -0
  249. data/src/tools/collection_inspector.coffee +4 -0
  250. data/src/tools/collections/components.coffee +59 -0
  251. data/src/tools/collections/instances.coffee +15 -0
  252. data/src/tools/component_tester.coffee +462 -0
  253. data/{app/assets/javascripts/luca/development → src/tools}/console.coffee +24 -55
  254. data/src/tools/models/components.coffee +25 -0
  255. data/src/tools/models/instance.coffee +2 -0
  256. data/src/tools/templates/component_tester/help.luca +14 -0
  257. data/{app/assets/javascripts/luca/util/luca.coffee → src/util.coffee} +6 -90
  258. data/vendor/assets/javascripts/luca-ui-development-tools.js +18533 -0
  259. data/vendor/assets/javascripts/luca-ui-development-tools.min.js +15 -0
  260. data/vendor/assets/javascripts/luca-ui-full.js +4806 -0
  261. data/vendor/assets/javascripts/luca-ui-full.min.js +8 -0
  262. data/vendor/assets/javascripts/luca-ui-templates.js +24 -0
  263. data/vendor/assets/javascripts/luca-ui.js +1703 -4394
  264. data/vendor/assets/javascripts/luca-ui.min.js +4 -5
  265. data/vendor/assets/luca-ui/base.css +85 -0
  266. data/vendor/assets/luca-ui/components/application.js +91 -0
  267. data/vendor/assets/luca-ui/components/base_toolbar.js +23 -0
  268. data/vendor/assets/luca-ui/components/controller.js +38 -0
  269. data/vendor/assets/luca-ui/components/fields/button_field.js +45 -0
  270. data/vendor/assets/luca-ui/components/fields/checkbox_field.js +43 -0
  271. data/vendor/assets/luca-ui/components/fields/file_upload_field.js +20 -0
  272. data/vendor/assets/luca-ui/components/fields/hidden_field.js +20 -0
  273. data/vendor/assets/luca-ui/components/fields/select_field.js +97 -0
  274. data/vendor/assets/luca-ui/components/fields/text_area_field.js +48 -0
  275. data/vendor/assets/luca-ui/components/fields/text_field.js +46 -0
  276. data/vendor/assets/luca-ui/components/fields/type_ahead_field.js +13 -0
  277. data/vendor/assets/luca-ui/components/form_button_toolbar.js +32 -0
  278. data/vendor/assets/luca-ui/components/form_view.css +32 -0
  279. data/vendor/assets/luca-ui/components/form_view.js +207 -0
  280. data/{app/assets/stylesheets/luca/components/table_view.scss → vendor/assets/luca-ui/components/grid_view.css} +51 -60
  281. data/vendor/assets/luca-ui/components/grid_view.js +202 -0
  282. data/vendor/assets/luca-ui/components/record_manager.js +207 -0
  283. data/vendor/assets/luca-ui/components/router.js +36 -0
  284. data/vendor/assets/luca-ui/components/template.js +26 -0
  285. data/vendor/assets/luca-ui/components/toolbar.css +11 -0
  286. data/vendor/assets/luca-ui/containers/card_view.js +98 -0
  287. data/vendor/assets/luca-ui/containers/column_view.js +52 -0
  288. data/vendor/assets/luca-ui/containers/container.css +3 -0
  289. data/vendor/assets/luca-ui/containers/modal_view.css +0 -0
  290. data/vendor/assets/luca-ui/containers/modal_view.js +87 -0
  291. data/vendor/assets/luca-ui/containers/panel_view.js +34 -0
  292. data/vendor/assets/luca-ui/containers/split_view.js +13 -0
  293. data/vendor/assets/luca-ui/containers/tab_view.css +16 -0
  294. data/vendor/assets/luca-ui/containers/tab_view.js +80 -0
  295. data/vendor/assets/luca-ui/containers/viewport.js +18 -0
  296. data/vendor/assets/luca-ui/core/collection.js +221 -0
  297. data/vendor/assets/luca-ui/core/container.js +205 -0
  298. data/vendor/assets/luca-ui/core/field.js +59 -0
  299. data/vendor/assets/luca-ui/core/observer.js +42 -0
  300. data/vendor/assets/luca-ui/core/view.js +127 -0
  301. data/vendor/assets/luca-ui/framework.js +110 -0
  302. data/vendor/assets/luca-ui/index.js +5 -0
  303. data/vendor/assets/luca-ui/managers/collection_manager.js +98 -0
  304. data/vendor/assets/luca-ui/managers/socket_manager.js +52 -0
  305. data/vendor/assets/luca-ui/modules/deferrable.js +21 -0
  306. data/vendor/assets/luca-ui/modules/local_storage.js +81 -0
  307. data/vendor/assets/luca-ui/normalize.css +359 -0
  308. data/vendor/assets/luca-ui/stylesheets/base.css +85 -0
  309. data/vendor/assets/luca-ui/stylesheets/components/form_view.css +32 -0
  310. data/vendor/assets/luca-ui/stylesheets/components/grid_view.css +76 -0
  311. data/vendor/assets/luca-ui/stylesheets/components/toolbar.css +11 -0
  312. data/vendor/assets/luca-ui/stylesheets/containers/container.css +3 -0
  313. data/vendor/assets/luca-ui/stylesheets/containers/modal_view.css +0 -0
  314. data/vendor/assets/luca-ui/stylesheets/containers/tab_view.css +16 -0
  315. data/vendor/assets/luca-ui/stylesheets/normalize.css +359 -0
  316. data/vendor/assets/luca-ui/templates/components/bootstrap_form_controls.js +4 -0
  317. data/vendor/assets/luca-ui/templates/components/form_view.js +4 -0
  318. data/vendor/assets/luca-ui/templates/components/grid_view.js +4 -0
  319. data/vendor/assets/luca-ui/templates/components/grid_view_empty_text.js +4 -0
  320. data/vendor/assets/luca-ui/templates/containers/basic.js +4 -0
  321. data/vendor/assets/luca-ui/templates/containers/tab_selector_container.js +4 -0
  322. data/vendor/assets/luca-ui/templates/containers/tab_view.js +4 -0
  323. data/vendor/assets/luca-ui/templates/containers/toolbar_wrapper.js +4 -0
  324. data/vendor/assets/luca-ui/templates/fields/button_field.js +4 -0
  325. data/vendor/assets/luca-ui/templates/fields/button_field_link.js +4 -0
  326. data/vendor/assets/luca-ui/templates/fields/checkbox_field.js +4 -0
  327. data/vendor/assets/luca-ui/templates/fields/file_upload_field.js +4 -0
  328. data/vendor/assets/luca-ui/templates/fields/hidden_field.js +4 -0
  329. data/vendor/assets/luca-ui/templates/fields/select_field.js +4 -0
  330. data/vendor/assets/luca-ui/templates/fields/text_area_field.js +4 -0
  331. data/vendor/assets/luca-ui/templates/fields/text_field.js +4 -0
  332. data/vendor/assets/luca-ui/templates/sample/contents.js +4 -0
  333. data/vendor/assets/luca-ui/templates/sample/welcome.js +4 -0
  334. data/vendor/assets/stylesheets/luca-ui.css +471 -68
  335. data/views/jasmine.erb +2 -2
  336. metadata +328 -496
  337. data/app/assets/javascripts/luca/basic.coffee +0 -8
  338. data/app/assets/javascripts/luca/components/application.coffee +0 -530
  339. data/app/assets/javascripts/luca/components/collection_view.coffee +0 -211
  340. data/app/assets/javascripts/luca/components/controller.coffee +0 -176
  341. data/app/assets/javascripts/luca/components/fields/base.coffee +0 -144
  342. data/app/assets/javascripts/luca/components/fields/button_field.coffee +0 -91
  343. data/app/assets/javascripts/luca/components/fields/hidden_field.coffee +0 -11
  344. data/app/assets/javascripts/luca/components/fields/label_field.coffee +0 -12
  345. data/app/assets/javascripts/luca/components/fields/select_field.coffee +0 -159
  346. data/app/assets/javascripts/luca/components/fields/text_area_field.coffee +0 -57
  347. data/app/assets/javascripts/luca/components/fields/type_ahead_field.coffee +0 -20
  348. data/app/assets/javascripts/luca/components/form_view.coffee +0 -421
  349. data/app/assets/javascripts/luca/components/grid_layout_view.coffee +0 -42
  350. data/app/assets/javascripts/luca/components/index.coffee +0 -7
  351. data/app/assets/javascripts/luca/components/load_mask.coffee +0 -8
  352. data/app/assets/javascripts/luca/components/multi_collection_view.coffee +0 -92
  353. data/app/assets/javascripts/luca/components/nav_bar.coffee +0 -80
  354. data/app/assets/javascripts/luca/components/page.coffee +0 -70
  355. data/app/assets/javascripts/luca/components/pagination_control.coffee +0 -104
  356. data/app/assets/javascripts/luca/components/simple_collection_view.coffee +0 -10
  357. data/app/assets/javascripts/luca/components/table_view.coffee +0 -93
  358. data/app/assets/javascripts/luca/components/table_view_scrollable.coffee +0 -23
  359. data/app/assets/javascripts/luca/concerns/application_event_bindings.coffee +0 -19
  360. data/app/assets/javascripts/luca/concerns/collection_event_bindings.coffee +0 -47
  361. data/app/assets/javascripts/luca/concerns/development_tool_helpers.coffee +0 -30
  362. data/app/assets/javascripts/luca/concerns/dom_helpers.coffee +0 -61
  363. data/app/assets/javascripts/luca/concerns/enhanced_properties.coffee +0 -23
  364. data/app/assets/javascripts/luca/concerns/filterable.coffee +0 -101
  365. data/app/assets/javascripts/luca/concerns/form_model_bindings.coffee +0 -20
  366. data/app/assets/javascripts/luca/concerns/grid_layout.coffee +0 -15
  367. data/app/assets/javascripts/luca/concerns/modal_view.coffee +0 -63
  368. data/app/assets/javascripts/luca/concerns/model_presenter.coffee +0 -23
  369. data/app/assets/javascripts/luca/concerns/paginatable.coffee +0 -79
  370. data/app/assets/javascripts/luca/concerns/query_collection_bindings.coffee +0 -52
  371. data/app/assets/javascripts/luca/concerns/sortable.coffee +0 -69
  372. data/app/assets/javascripts/luca/concerns/state_model.coffee +0 -58
  373. data/app/assets/javascripts/luca/concerns/templating.coffee +0 -13
  374. data/app/assets/javascripts/luca/config.coffee +0 -54
  375. data/app/assets/javascripts/luca/containers/card_view.coffee +0 -178
  376. data/app/assets/javascripts/luca/containers/container.coffee +0 -711
  377. data/app/assets/javascripts/luca/containers/index.coffee +0 -2
  378. data/app/assets/javascripts/luca/containers/page_controller.coffee +0 -25
  379. data/app/assets/javascripts/luca/core/events.coffee +0 -114
  380. data/app/assets/javascripts/luca/core/index.coffee +0 -12
  381. data/app/assets/javascripts/luca/core/model.coffee +0 -56
  382. data/app/assets/javascripts/luca/core/panel.coffee +0 -124
  383. data/app/assets/javascripts/luca/core/registry/component_definition.coffee +0 -319
  384. data/app/assets/javascripts/luca/core/registry/concerns.coffee +0 -70
  385. data/app/assets/javascripts/luca/core/registry/index.coffee +0 -4
  386. data/app/assets/javascripts/luca/core/registry/meta_data.coffee +0 -99
  387. data/app/assets/javascripts/luca/core/templates.coffee +0 -51
  388. data/app/assets/javascripts/luca/core/view.coffee +0 -383
  389. data/app/assets/javascripts/luca/dependencies.coffee +0 -9
  390. data/app/assets/javascripts/luca/development/code_sync_manager.coffee +0 -173
  391. data/app/assets/javascripts/luca/development/component.coffee +0 -76
  392. data/app/assets/javascripts/luca/development/components.coffee +0 -57
  393. data/app/assets/javascripts/luca/development/index.coffee +0 -5
  394. data/app/assets/javascripts/luca/index.coffee +0 -9
  395. data/app/assets/javascripts/luca/managers/index.coffee +0 -2
  396. data/app/assets/javascripts/luca/managers/socket_manager.coffee +0 -89
  397. data/app/assets/javascripts/luca/templates/components/bootstrap_form_controls.jst.ejs +0 -10
  398. data/app/assets/javascripts/luca/templates/components/collection_loader_view.jst.ejs +0 -6
  399. data/app/assets/javascripts/luca/templates/components/form_alert.jst.ejs +0 -4
  400. data/app/assets/javascripts/luca/templates/components/grid_view.jst.ejs +0 -11
  401. data/app/assets/javascripts/luca/templates/components/grid_view_empty_text.jst.ejs +0 -3
  402. data/app/assets/javascripts/luca/templates/components/load_mask.jst.ejs +0 -5
  403. data/app/assets/javascripts/luca/templates/components/nav_bar.jst.ejs +0 -19
  404. data/app/assets/javascripts/luca/templates/components/pagination.jst.ejs +0 -10
  405. data/app/assets/javascripts/luca/templates/components/table_view.jst.ejs +0 -4
  406. data/app/assets/javascripts/luca/templates/containers/basic.jst.ejs +0 -1
  407. data/app/assets/javascripts/luca/templates/containers/tab_selector_container.jst.ejs +0 -12
  408. data/app/assets/javascripts/luca/templates/containers/tab_view.jst.ejs +0 -2
  409. data/app/assets/javascripts/luca/templates/containers/toolbar_wrapper.jst.ejs +0 -1
  410. data/app/assets/javascripts/luca/templates/fields/button_field.jst.ejs +0 -2
  411. data/app/assets/javascripts/luca/templates/fields/button_field_link.jst.ejs +0 -6
  412. data/app/assets/javascripts/luca/templates/fields/checkbox_array.jst.ejs +0 -4
  413. data/app/assets/javascripts/luca/templates/fields/checkbox_array_item.jst.ejs +0 -3
  414. data/app/assets/javascripts/luca/templates/fields/checkbox_field.jst.ejs +0 -10
  415. data/app/assets/javascripts/luca/templates/fields/file_upload_field.jst.ejs +0 -10
  416. data/app/assets/javascripts/luca/templates/fields/hidden_field.jst.ejs +0 -1
  417. data/app/assets/javascripts/luca/templates/fields/select_field.jst.ejs +0 -11
  418. data/app/assets/javascripts/luca/templates/fields/text_area_field.jst.ejs +0 -11
  419. data/app/assets/javascripts/luca/templates/fields/text_field.jst.ejs +0 -16
  420. data/app/assets/javascripts/luca/util/deprecations.coffee +0 -18
  421. data/app/assets/javascripts/luca/util/index.coffee +0 -4
  422. data/app/assets/javascripts/luca/util/keybindings.coffee +0 -24
  423. data/app/assets/javascripts/luca/util/logging.coffee +0 -30
  424. data/app/assets/javascripts/luca-ui.js +0 -1
  425. data/app/assets/stylesheets/luca/components/form_view.scss +0 -7
  426. data/app/assets/stylesheets/luca/containers/container.scss +0 -19
  427. data/app/assets/stylesheets/luca/development/index.css +0 -3
  428. data/app/assets/stylesheets/luca/index.css +0 -4
  429. data/bin/luca +0 -14
  430. data/docs/framework.json +0 -1
  431. data/docs/luca-framework-documentation.js +0 -1
  432. data/lib/generators/luca/application/application_generator.rb +0 -75
  433. data/lib/generators/luca/application/templates/controller.rb +0 -4
  434. data/lib/generators/luca/application/templates/index.html.erb +0 -19
  435. data/lib/generators/luca/application/templates/index.html.haml +0 -7
  436. data/lib/generators/luca/application/templates/javascripts/application.coffee +0 -18
  437. data/lib/generators/luca/application/templates/javascripts/collection_manager.coffee +0 -2
  438. data/lib/generators/luca/application/templates/javascripts/config.coffee +0 -3
  439. data/lib/generators/luca/application/templates/javascripts/dependencies.coffee +0 -3
  440. data/lib/generators/luca/application/templates/javascripts/home.jst.ejs +0 -2
  441. data/lib/generators/luca/application/templates/javascripts/index.coffee +0 -15
  442. data/lib/generators/luca/application/templates/javascripts/router.coffee +0 -4
  443. data/lib/guard/luca.rb +0 -84
  444. data/lib/luca/asset_compiler.rb +0 -117
  445. data/lib/luca/cli/generate.rb +0 -37
  446. data/lib/luca/cli/server.rb +0 -20
  447. data/lib/luca/cli/sync.rb +0 -40
  448. data/lib/luca/cli/watch.rb +0 -16
  449. data/lib/luca/cli.rb +0 -68
  450. data/lib/luca/collection/endpoint.rb +0 -38
  451. data/lib/luca/collection/file_backend.rb +0 -121
  452. data/lib/luca/collection/redis_backend.rb +0 -153
  453. data/lib/luca/collection.rb +0 -64
  454. data/lib/luca/compiled_asset.rb +0 -61
  455. data/lib/luca/component_definition.rb +0 -356
  456. data/lib/luca/luca_application.rb +0 -258
  457. data/lib/luca/project.rb +0 -73
  458. data/lib/luca/project_harness.rb +0 -96
  459. data/lib/luca/server.rb +0 -7
  460. data/lib/luca/stylesheet.rb +0 -35
  461. data/lib/luca/template_asset.rb +0 -64
  462. data/lib/luca/version.rb +0 -3
  463. data/lib/luca/watcher.rb +0 -72
  464. data/lib/railties/luca/tasks.rake +0 -38
  465. data/site/.bundle/config +0 -2
  466. data/site/.gitignore +0 -5
  467. data/site/.rvmrc +0 -1
  468. data/site/CHANGELOG.md +0 -41
  469. data/site/DOCS.md +0 -41
  470. data/site/Gemfile +0 -8
  471. data/site/Gemfile.lock +0 -134
  472. data/site/LICENSE.md +0 -19
  473. data/site/config.rb +0 -84
  474. data/site/helpers/site_helpers.rb +0 -20
  475. data/site/html5bp-docs/README.md +0 -38
  476. data/site/html5bp-docs/contribute.md +0 -104
  477. data/site/html5bp-docs/crossdomain.md +0 -21
  478. data/site/html5bp-docs/css.md +0 -135
  479. data/site/html5bp-docs/extend.md +0 -507
  480. data/site/html5bp-docs/faq.md +0 -77
  481. data/site/html5bp-docs/htaccess.md +0 -323
  482. data/site/html5bp-docs/html.md +0 -170
  483. data/site/html5bp-docs/js.md +0 -31
  484. data/site/html5bp-docs/misc.md +0 -25
  485. data/site/html5bp-docs/usage.md +0 -109
  486. data/site/readme.md +0 -47
  487. data/site/source/.htaccess +0 -540
  488. data/site/source/404.html +0 -157
  489. data/site/source/app/assets/javascripts/dependencies.js.coffee +0 -6
  490. data/site/source/app/assets/javascripts/docs/application.coffee +0 -64
  491. data/site/source/app/assets/javascripts/docs/collections/docs_documentation.coffee +0 -17
  492. data/site/source/app/assets/javascripts/docs/collections/github_repositories.coffee +0 -7
  493. data/site/source/app/assets/javascripts/docs/collections/index.coffee +0 -1
  494. data/site/source/app/assets/javascripts/docs/collections/luca_documentation.coffee +0 -17
  495. data/site/source/app/assets/javascripts/docs/collections/public_gists.coffee +0 -4
  496. data/site/source/app/assets/javascripts/docs/config.coffee +0 -5
  497. data/site/source/app/assets/javascripts/docs/index.coffee +0 -12
  498. data/site/source/app/assets/javascripts/docs/lib/router.coffee +0 -3
  499. data/site/source/app/assets/javascripts/docs/models/component.coffee +0 -99
  500. data/site/source/app/assets/javascripts/docs/models/github_repository.coffee +0 -3
  501. data/site/source/app/assets/javascripts/docs/models/index.coffee +0 -1
  502. data/site/source/app/assets/javascripts/docs/templates/component_documentation.jst.ejs +0 -55
  503. data/site/source/app/assets/javascripts/docs/templates/examples_browser/overview.jst.ejs +0 -4
  504. data/site/source/app/assets/javascripts/docs/templates/examples_browser/selector.jst.ejs +0 -11
  505. data/site/source/app/assets/javascripts/docs/templates/github_repository.jst.ejs +0 -4
  506. data/site/source/app/assets/javascripts/docs/templates/layouts/main.jst.ejs +0 -4
  507. data/site/source/app/assets/javascripts/docs/templates/left_navigation.jst.ejs +0 -5
  508. data/site/source/app/assets/javascripts/docs/templates/pages/getting_started.jst.ejs +0 -78
  509. data/site/source/app/assets/javascripts/docs/templates/pages/home.jst.ejs +0 -57
  510. data/site/source/app/assets/javascripts/docs/views/components/code_editor.coffee +0 -45
  511. data/site/source/app/assets/javascripts/docs/views/components/component_documentation.coffee +0 -72
  512. data/site/source/app/assets/javascripts/docs/views/index.coffee +0 -3
  513. data/site/source/app/assets/javascripts/docs/views/pages/browse_source/details.coffee +0 -37
  514. data/site/source/app/assets/javascripts/docs/views/pages/browse_source/list.coffee +0 -31
  515. data/site/source/app/assets/javascripts/docs/views/pages/browse_source.coffee +0 -46
  516. data/site/source/app/assets/javascripts/docs/views/pages/component_editor.coffee +0 -10
  517. data/site/source/app/assets/javascripts/docs/views/pages/examples_browser/docs.coffee +0 -12
  518. data/site/source/app/assets/javascripts/docs/views/pages/examples_browser/source.coffee +0 -13
  519. data/site/source/app/assets/javascripts/docs/views/pages/examples_browser.coffee +0 -102
  520. data/site/source/app/assets/javascripts/docs/views/pages/home.coffee +0 -10
  521. data/site/source/app/assets/javascripts/docs/views/views/api_browser/index.coffee +0 -43
  522. data/site/source/app/assets/javascripts/docs/views/views/collection_view_examples/grid_layout_view_example.coffee +0 -14
  523. data/site/source/app/assets/javascripts/docs/views/views/collection_view_examples/table_view_example.coffee +0 -39
  524. data/site/source/app/assets/javascripts/docs/views/views/form_view_examples/basic_example.coffee +0 -38
  525. data/site/source/app/assets/javascripts/docs/views/views/form_view_examples/complex_layout.coffee +0 -110
  526. data/site/source/app/assets/javascripts/docs/views/views/top_navigation.coffee +0 -6
  527. data/site/source/app/assets/javascripts/docs-docs.js +0 -1
  528. data/site/source/app/assets/javascripts/luca-docs.js +0 -1
  529. data/site/source/app/assets/javascripts/luca-framework-documentation.js +0 -1
  530. data/site/source/app/assets/javascripts/site.js.coffee +0 -4
  531. data/site/source/app/assets/javascripts/vendor/codemirror.js +0 -4786
  532. data/site/source/app/assets/javascripts/vendor/coffeescript.js +0 -346
  533. data/site/source/app/assets/javascripts/vendor/css.js +0 -465
  534. data/site/source/app/assets/javascripts/vendor/htmlmixed.js +0 -84
  535. data/site/source/app/assets/javascripts/vendor/javascript.js +0 -422
  536. data/site/source/app/assets/javascripts/vendor/js-beautify.js +0 -1353
  537. data/site/source/app/assets/javascripts/vendor/modernizr-2.6.1.min.js +0 -4
  538. data/site/source/app/assets/javascripts/vendor/vim.js +0 -2511
  539. data/site/source/app/assets/stylesheets/docs/api-browser.css.scss +0 -5
  540. data/site/source/app/assets/stylesheets/docs/application.css.scss +0 -35
  541. data/site/source/app/assets/stylesheets/docs/browse-source.css.scss +0 -5
  542. data/site/source/app/assets/stylesheets/docs/scrollable-table.css.scss +0 -5
  543. data/site/source/app/assets/stylesheets/site.css.scss +0 -2
  544. data/site/source/app/assets/stylesheets/vendor/codemirror.css +0 -240
  545. data/site/source/app/assets/stylesheets/vendor/prettify-tomorrow-night-bright.css +0 -160
  546. data/site/source/app/assets/stylesheets/vendor/twilight.css +0 -26
  547. data/site/source/crossdomain.xml +0 -15
  548. data/site/source/documentation.html.haml +0 -1
  549. data/site/source/favicon_base.png +0 -0
  550. data/site/source/humans.txt +0 -15
  551. data/site/source/images/background.png +0 -0
  552. data/site/source/images/middleman.png +0 -0
  553. data/site/source/index.html.haml +0 -1
  554. data/site/source/layouts/layout.haml +0 -55
  555. data/site/source/readme.md +0 -63
  556. data/site/source/robots.txt +0 -3
  557. data/spec/javascripts/components/collection_view_spec.coffee +0 -59
  558. data/spec/javascripts/components/controller_spec.coffee +0 -62
  559. data/spec/javascripts/components/form_view_spec.coffee +0 -162
  560. data/spec/javascripts/components/multi_collection_view_spec.coffee +0 -5
  561. data/spec/javascripts/components/table_view_spec.coffee +0 -17
  562. data/spec/javascripts/concerns/collection_event_bindings_spec.coffee +0 -15
  563. data/spec/javascripts/concerns/dom_helpers_spec.coffee +0 -16
  564. data/spec/javascripts/concerns/filterable_spec.coffee +0 -25
  565. data/spec/javascripts/concerns/model_presenter_spec.coffee +0 -31
  566. data/spec/javascripts/concerns/state_model_spec.coffee +0 -55
  567. data/spec/javascripts/containers/card_view_spec.coffee +0 -108
  568. data/spec/javascripts/core/concerns_spec.coffee +0 -88
  569. data/spec/javascripts/core/container_spec.coffee +0 -287
  570. data/spec/javascripts/core/define_spec.coffee +0 -116
  571. data/spec/javascripts/core/events_spec.coffee +0 -26
  572. data/spec/javascripts/core/field_spec.coffee +0 -4
  573. data/spec/javascripts/core/util_spec.coffee +0 -24
  574. data/spec/javascripts/dependencies/index.coffee +0 -3
  575. data/spec/lib/component_definition_spec.rb +0 -63
  576. data/spec/lib/input_compiler_spec.rb +0 -9
  577. data/spec/lib/luca_application_spec.rb +0 -30
  578. data/spec/support/fixtures/application.coffee +0 -45
  579. data/spec/support/fixtures/component.coffee +0 -34
  580. data/tutorials/component-driven-design.md +0 -140
  581. data/tutorials/structure-of-a-project.md +0 -63
  582. data/vendor/assets/javascripts/backbone-ext.js +0 -21
  583. data/vendor/assets/javascripts/backbone-min.js +0 -42
  584. data/vendor/assets/javascripts/backbone-query.min.js +0 -1
  585. data/vendor/assets/javascripts/bootstrap.min.js +0 -7
  586. data/vendor/assets/javascripts/codemirror-ui.js +0 -503
  587. data/vendor/assets/javascripts/hogan.js +0 -707
  588. data/vendor/assets/javascripts/inflections.js +0 -656
  589. data/vendor/assets/javascripts/jasmine-html.js +0 -190
  590. data/vendor/assets/javascripts/jasmine.js +0 -2476
  591. data/vendor/assets/javascripts/jquery.js +0 -5
  592. data/vendor/assets/javascripts/keymaster.min.js +0 -4
  593. data/vendor/assets/javascripts/luca-dependencies.min.js +0 -8
  594. data/vendor/assets/javascripts/luca-development.min.js +0 -1
  595. data/vendor/assets/javascripts/luca-spec.js +0 -11
  596. data/vendor/assets/javascripts/luca.full.min.js +0 -12
  597. data/vendor/assets/javascripts/luca.min.js +0 -5
  598. data/vendor/assets/javascripts/sinon.js +0 -3469
  599. data/vendor/assets/javascripts/underscore-min.js +0 -1
  600. data/vendor/assets/javascripts/underscore-string.min.js +0 -1
  601. data/vendor/assets/stylesheets/bootstrap-responsive.min.css +0 -9
  602. data/vendor/assets/stylesheets/bootstrap.min.css +0 -9
  603. data/vendor/assets/stylesheets/luca-components.css +0 -202
  604. data/vendor/assets/stylesheets/luca-development.css +0 -23
@@ -1,2511 +0,0 @@
1
- /**
2
- * Supported keybindings:
3
- *
4
- * Motion:
5
- * h, j, k, l
6
- * e, E, w, W, b, B, ge, gE
7
- * f<character>, F<character>, t<character>, T<character>
8
- * $, ^, 0
9
- * gg, G
10
- * %
11
- * '<character>, `<character>
12
- *
13
- * Operator:
14
- * d, y, c
15
- * dd, yy, cc
16
- * g~, g~g~
17
- * >, <, >>, <<
18
- *
19
- * Operator-Motion:
20
- * x, X, D, Y, C, ~
21
- *
22
- * Action:
23
- * a, i, s, A, I, S, o, O
24
- * J
25
- * u, Ctrl-r
26
- * m<character>
27
- * r<character>
28
- *
29
- * Modes:
30
- * ESC - leave insert mode, visual mode, and clear input state.
31
- * Ctrl-[, Ctrl-c - same as ESC.
32
- *
33
- * Registers: unamed, -, a-z, A-Z, 0-9
34
- * (Does not respect the special case for number registers when delete
35
- * operator is made with these commands: %, (, ), , /, ?, n, N, {, } )
36
- * TODO: Implement the remaining registers.
37
- * Marks: a-z, A-Z, and 0-9
38
- * TODO: Implement the remaining special marks. They have more complex
39
- * behavior.
40
- *
41
- * Code structure:
42
- * 1. Default keymap
43
- * 2. Variable declarations and short basic helpers
44
- * 3. Instance (External API) implementation
45
- * 4. Internal state tracking objects (input state, counter) implementation
46
- * and instanstiation
47
- * 5. Key handler (the main command dispatcher) implementation
48
- * 6. Motion, operator, and action implementations
49
- * 7. Helper functions for the key handler, motions, operators, and actions
50
- * 8. Set up Vim to work as a keymap for CodeMirror.
51
- */
52
-
53
- (function() {
54
- 'use strict';
55
-
56
- var defaultKeymap = [
57
- // Key to key mapping. This goes first to make it possible to override
58
- // existing mappings.
59
- { keys: ['Left'], type: 'keyToKey', toKeys: ['h'] },
60
- { keys: ['Right'], type: 'keyToKey', toKeys: ['l'] },
61
- { keys: ['Up'], type: 'keyToKey', toKeys: ['k'] },
62
- { keys: ['Down'], type: 'keyToKey', toKeys: ['j'] },
63
- { keys: ['Space'], type: 'keyToKey', toKeys: ['l'] },
64
- { keys: ['Backspace'], type: 'keyToKey', toKeys: ['h'] },
65
- { keys: ['Ctrl-Space'], type: 'keyToKey', toKeys: ['W'] },
66
- { keys: ['Ctrl-Backspace'], type: 'keyToKey', toKeys: ['B'] },
67
- { keys: ['Shift-Space'], type: 'keyToKey', toKeys: ['w'] },
68
- { keys: ['Shift-Backspace'], type: 'keyToKey', toKeys: ['b'] },
69
- { keys: ['Ctrl-n'], type: 'keyToKey', toKeys: ['j'] },
70
- { keys: ['Ctrl-p'], type: 'keyToKey', toKeys: ['k'] },
71
- { keys: ['Ctrl-['], type: 'keyToKey', toKeys: ['Esc'] },
72
- { keys: ['Ctrl-c'], type: 'keyToKey', toKeys: ['Esc'] },
73
- { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'] },
74
- { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'] },
75
- { keys: ['Home'], type: 'keyToKey', toKeys: ['0'] },
76
- { keys: ['End'], type: 'keyToKey', toKeys: ['$'] },
77
- { keys: ['PageUp'], type: 'keyToKey', toKeys: ['Ctrl-b'] },
78
- { keys: ['PageDown'], type: 'keyToKey', toKeys: ['Ctrl-f'] },
79
- // Motions
80
- { keys: ['h'], type: 'motion',
81
- motion: 'moveByCharacters',
82
- motionArgs: { forward: false }},
83
- { keys: ['l'], type: 'motion',
84
- motion: 'moveByCharacters',
85
- motionArgs: { forward: true }},
86
- { keys: ['j'], type: 'motion',
87
- motion: 'moveByLines',
88
- motionArgs: { forward: true, linewise: true }},
89
- { keys: ['k'], type: 'motion',
90
- motion: 'moveByLines',
91
- motionArgs: { forward: false, linewise: true }},
92
- { keys: ['w'], type: 'motion',
93
- motion: 'moveByWords',
94
- motionArgs: { forward: true, wordEnd: false }},
95
- { keys: ['W'], type: 'motion',
96
- motion: 'moveByWords',
97
- motionArgs: { forward: true, wordEnd: false, bigWord: true }},
98
- { keys: ['e'], type: 'motion',
99
- motion: 'moveByWords',
100
- motionArgs: { forward: true, wordEnd: true, inclusive: true }},
101
- { keys: ['E'], type: 'motion',
102
- motion: 'moveByWords',
103
- motionArgs: { forward: true, wordEnd: true, bigWord: true,
104
- inclusive: true }},
105
- { keys: ['b'], type: 'motion',
106
- motion: 'moveByWords',
107
- motionArgs: { forward: false, wordEnd: false }},
108
- { keys: ['B'], type: 'motion',
109
- motion: 'moveByWords',
110
- motionArgs: { forward: false, wordEnd: false, bigWord: true }},
111
- { keys: ['g', 'e'], type: 'motion',
112
- motion: 'moveByWords',
113
- motionArgs: { forward: false, wordEnd: true, inclusive: true }},
114
- { keys: ['g', 'E'], type: 'motion',
115
- motion: 'moveByWords',
116
- motionArgs: { forward: false, wordEnd: true, bigWord: true,
117
- inclusive: true }},
118
- { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
119
- motionArgs: { forward: false }},
120
- { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
121
- motionArgs: { forward: true }},
122
- { keys: ['Ctrl-f'], type: 'motion',
123
- motion: 'moveByPage', motionArgs: { forward: true }},
124
- { keys: ['Ctrl-b'], type: 'motion',
125
- motion: 'moveByPage', motionArgs: { forward: false }},
126
- { keys: ['g', 'g'], type: 'motion',
127
- motion: 'moveToLineOrEdgeOfDocument',
128
- motionArgs: { forward: false, explicitRepeat: true, linewise: true }},
129
- { keys: ['G'], type: 'motion',
130
- motion: 'moveToLineOrEdgeOfDocument',
131
- motionArgs: { forward: true, explicitRepeat: true, linewise: true }},
132
- { keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' },
133
- { keys: ['^'], type: 'motion',
134
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
135
- { keys: ['$'], type: 'motion',
136
- motion: 'moveToEol',
137
- motionArgs: { inclusive: true }},
138
- { keys: ['%'], type: 'motion',
139
- motion: 'moveToMatchedSymbol',
140
- motionArgs: { inclusive: true }},
141
- { keys: ['f', 'character'], type: 'motion',
142
- motion: 'moveToCharacter',
143
- motionArgs: { forward: true , inclusive: true }},
144
- { keys: ['F', 'character'], type: 'motion',
145
- motion: 'moveToCharacter',
146
- motionArgs: { forward: false }},
147
- { keys: ['t', 'character'], type: 'motion',
148
- motion: 'moveTillCharacter',
149
- motionArgs: { forward: true, inclusive: true }},
150
- { keys: ['T', 'character'], type: 'motion',
151
- motion: 'moveTillCharacter',
152
- motionArgs: { forward: false }},
153
- { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark' },
154
- { keys: ['`', 'character'], type: 'motion', motion: 'goToMark' },
155
- { keys: ['|'], type: 'motion',
156
- motion: 'moveToColumn',
157
- motionArgs: { }},
158
- // Operators
159
- { keys: ['d'], type: 'operator', operator: 'delete' },
160
- { keys: ['y'], type: 'operator', operator: 'yank' },
161
- { keys: ['c'], type: 'operator', operator: 'change',
162
- operatorArgs: { enterInsertMode: true } },
163
- { keys: ['>'], type: 'operator', operator: 'indent',
164
- operatorArgs: { indentRight: true }},
165
- { keys: ['<'], type: 'operator', operator: 'indent',
166
- operatorArgs: { indentRight: false }},
167
- { keys: ['g', '~'], type: 'operator', operator: 'swapcase' },
168
- { keys: ['n'], type: 'motion', motion: 'findNext' },
169
- { keys: ['N'], type: 'motion', motion: 'findPrev' },
170
- // Operator-Motion dual commands
171
- { keys: ['x'], type: 'operatorMotion', operator: 'delete',
172
- motion: 'moveByCharacters', motionArgs: { forward: true },
173
- operatorMotionArgs: { visualLine: false }},
174
- { keys: ['X'], type: 'operatorMotion', operator: 'delete',
175
- motion: 'moveByCharacters', motionArgs: { forward: false },
176
- operatorMotionArgs: { visualLine: true }},
177
- { keys: ['D'], type: 'operatorMotion', operator: 'delete',
178
- motion: 'moveToEol', motionArgs: { inclusive: true },
179
- operatorMotionArgs: { visualLine: true }},
180
- { keys: ['Y'], type: 'operatorMotion', operator: 'yank',
181
- motion: 'moveToEol', motionArgs: { inclusive: true },
182
- operatorMotionArgs: { visualLine: true }},
183
- { keys: ['C'], type: 'operatorMotion',
184
- operator: 'change', operatorArgs: { enterInsertMode: true },
185
- motion: 'moveToEol', motionArgs: { inclusive: true },
186
- operatorMotionArgs: { visualLine: true }},
187
- { keys: ['~'], type: 'operatorMotion', operator: 'swapcase',
188
- motion: 'moveByCharacters', motionArgs: { forward: true }},
189
- // Actions
190
- { keys: ['a'], type: 'action', action: 'enterInsertMode',
191
- actionArgs: { insertAt: 'charAfter' }},
192
- { keys: ['A'], type: 'action', action: 'enterInsertMode',
193
- actionArgs: { insertAt: 'eol' }},
194
- { keys: ['i'], type: 'action', action: 'enterInsertMode' },
195
- { keys: ['I'], type: 'action', action: 'enterInsertMode',
196
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
197
- { keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode',
198
- actionArgs: { after: true }},
199
- { keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode',
200
- actionArgs: { after: false }},
201
- { keys: ['v'], type: 'action', action: 'toggleVisualMode' },
202
- { keys: ['V'], type: 'action', action: 'toggleVisualMode',
203
- actionArgs: { linewise: true }},
204
- { keys: ['J'], type: 'action', action: 'joinLines' },
205
- { keys: ['p'], type: 'action', action: 'paste',
206
- actionArgs: { after: true }},
207
- { keys: ['P'], type: 'action', action: 'paste',
208
- actionArgs: { after: false }},
209
- { keys: ['r', 'character'], type: 'action', action: 'replace' },
210
- { keys: ['u'], type: 'action', action: 'undo' },
211
- { keys: ['Ctrl-r'], type: 'action', action: 'redo' },
212
- { keys: ['m', 'character'], type: 'action', action: 'setMark' },
213
- { keys: ['\"', 'character'], type: 'action', action: 'setRegister' },
214
- { keys: [',', '/'], type: 'action', action: 'clearSearchHighlight' },
215
- // Text object motions
216
- { keys: ['a', 'character'], type: 'motion',
217
- motion: 'textObjectManipulation' },
218
- { keys: ['i', 'character'], type: 'motion',
219
- motion: 'textObjectManipulation',
220
- motionArgs: { textObjectInner: true }},
221
- // Search
222
- { keys: ['/'], type: 'search',
223
- searchArgs: { forward: true, querySrc: 'prompt' }},
224
- { keys: ['?'], type: 'search',
225
- searchArgs: { forward: false, querySrc: 'prompt' }},
226
- { keys: ['*'], type: 'search',
227
- searchArgs: { forward: true, querySrc: 'wordUnderCursor' }},
228
- { keys: ['#'], type: 'search',
229
- searchArgs: { forward: false, querySrc: 'wordUnderCursor' }},
230
- // Ex command
231
- { keys: [':'], type: 'ex' }
232
- ];
233
-
234
- var Vim = function() {
235
- var alphabetRegex = /[A-Za-z]/;
236
- var numberRegex = /[\d]/;
237
- var whiteSpaceRegex = /\s/;
238
- var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)];
239
- function makeKeyRange(start, size) {
240
- var keys = [];
241
- for (var i = start; i < start + size; i++) {
242
- keys.push(String.fromCharCode(i));
243
- }
244
- return keys;
245
- }
246
- var upperCaseAlphabet = makeKeyRange(65, 26);
247
- var lowerCaseAlphabet = makeKeyRange(97, 26);
248
- var numbers = makeKeyRange(48, 10);
249
- var SPECIAL_SYMBOLS = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'';
250
- var specialSymbols = SPECIAL_SYMBOLS.split('');
251
- var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
252
- 'Esc', 'Home', 'End', 'PageUp', 'PageDown'];
253
- var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
254
- numbers).concat(['<', '>']);
255
- var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
256
- numbers).concat('-\"'.split(''));
257
-
258
- function isAlphabet(k) {
259
- return alphabetRegex.test(k);
260
- }
261
- function isLine(cm, line) {
262
- return line >= 0 && line < cm.lineCount();
263
- }
264
- function isLowerCase(k) {
265
- return (/^[a-z]$/).test(k);
266
- }
267
- function isMatchableSymbol(k) {
268
- return '()[]{}'.indexOf(k) != -1;
269
- }
270
- function isNumber(k) {
271
- return numberRegex.test(k);
272
- }
273
- function isUpperCase(k) {
274
- return (/^[A-Z]$/).test(k);
275
- }
276
- function isAlphanumeric(k) {
277
- return (/^[\w]$/).test(k);
278
- }
279
- function isWhiteSpace(k) {
280
- return whiteSpaceRegex.test(k);
281
- }
282
- function isWhiteSpaceString(k) {
283
- return (/^\s*$/).test(k);
284
- }
285
- function inRangeInclusive(x, start, end) {
286
- return x >= start && x <= end;
287
- }
288
- function inArray(val, arr) {
289
- for (var i = 0; i < arr.length; i++) {
290
- if (arr[i] == val) {
291
- return true;
292
- }
293
- }
294
- return false;
295
- }
296
-
297
- // Global Vim state. Call getVimGlobalState to get and initialize.
298
- var vimGlobalState;
299
- function getVimGlobalState() {
300
- if (!vimGlobalState) {
301
- vimGlobalState = {
302
- // The current search query.
303
- searchQuery: null,
304
- // Whether we are searching backwards.
305
- searchIsReversed: false,
306
- registerController: new RegisterController({})
307
- };
308
- }
309
- return vimGlobalState;
310
- }
311
- function getVimState(cm) {
312
- if (!cm.vimState) {
313
- // Store instance state in the CodeMirror object.
314
- cm.vimState = {
315
- inputState: new InputState(),
316
- // When using jk for navigation, if you move from a longer line to a
317
- // shorter line, the cursor may clip to the end of the shorter line.
318
- // If j is pressed again and cursor goes to the next line, the
319
- // cursor should go back to its horizontal position on the longer
320
- // line if it can. This is to keep track of the horizontal position.
321
- lastHPos: -1,
322
- // The last motion command run. Cleared if a non-motion command gets
323
- // executed in between.
324
- lastMotion: null,
325
- marks: {},
326
- visualMode: false,
327
- // If we are in visual line mode. No effect if visualMode is false.
328
- visualLine: false
329
- };
330
- }
331
- return cm.vimState;
332
- }
333
-
334
- var vimApi= {
335
- buildKeyMap: function() {
336
- // TODO: Convert keymap into dictionary format for fast lookup.
337
- },
338
- // Testing hook, though it might be useful to expose the register
339
- // controller anyways.
340
- getRegisterController: function() {
341
- return getVimGlobalState().registerController;
342
- },
343
- // Testing hook.
344
- clearVimGlobalState_: function() {
345
- vimGlobalState = null;
346
- },
347
- map: function(lhs, rhs) {
348
- // Add user defined key bindings.
349
- exCommandDispatcher.map(lhs, rhs);
350
- },
351
- // Initializes vim state variable on the CodeMirror object. Should only be
352
- // called lazily by handleKey or for testing.
353
- maybeInitState: function(cm) {
354
- getVimState(cm);
355
- },
356
- // This is the outermost function called by CodeMirror, after keys have
357
- // been mapped to their Vim equivalents.
358
- handleKey: function(cm, key) {
359
- var command;
360
- var vim = getVimState(cm);
361
- if (key == 'Esc') {
362
- // Clear input state and get back to normal mode.
363
- vim.inputState.reset();
364
- if (vim.visualMode) {
365
- exitVisualMode(cm, vim);
366
- }
367
- return;
368
- }
369
- if (vim.visualMode &&
370
- cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
371
- // The selection was cleared. Exit visual mode.
372
- exitVisualMode(cm, vim);
373
- }
374
- if (!vim.visualMode &&
375
- !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
376
- vim.visualMode = true;
377
- vim.visualLine = false;
378
- }
379
- if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) {
380
- // Have to special case 0 since it's both a motion and a number.
381
- command = commandDispatcher.matchCommand(key, defaultKeymap, vim);
382
- }
383
- if (!command) {
384
- if (isNumber(key)) {
385
- // Increment count unless count is 0 and key is 0.
386
- vim.inputState.pushRepeatDigit(key);
387
- }
388
- return;
389
- }
390
- if (command.type == 'keyToKey') {
391
- // TODO: prevent infinite recursion.
392
- for (var i = 0; i < command.toKeys.length; i++) {
393
- this.handleKey(cm, command.toKeys[i]);
394
- }
395
- } else {
396
- commandDispatcher.processCommand(cm, vim, command);
397
- }
398
- }
399
- };
400
-
401
- // Represents the current input state.
402
- function InputState() {
403
- this.reset();
404
- }
405
- InputState.prototype.reset = function() {
406
- this.prefixRepeat = [];
407
- this.motionRepeat = [];
408
-
409
- this.operator = null;
410
- this.operatorArgs = null;
411
- this.motion = null;
412
- this.motionArgs = null;
413
- this.keyBuffer = []; // For matching multi-key commands.
414
- this.registerName = null; // Defaults to the unamed register.
415
- };
416
- InputState.prototype.pushRepeatDigit = function(n) {
417
- if (!this.operator) {
418
- this.prefixRepeat = this.prefixRepeat.concat(n);
419
- } else {
420
- this.motionRepeat = this.motionRepeat.concat(n);
421
- }
422
- };
423
- InputState.prototype.getRepeat = function() {
424
- var repeat = 0;
425
- if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
426
- repeat = 1;
427
- if (this.prefixRepeat.length > 0) {
428
- repeat *= parseInt(this.prefixRepeat.join(''), 10);
429
- }
430
- if (this.motionRepeat.length > 0) {
431
- repeat *= parseInt(this.motionRepeat.join(''), 10);
432
- }
433
- }
434
- return repeat;
435
- };
436
-
437
- /*
438
- * Register stores information about copy and paste registers. Besides
439
- * text, a register must store whether it is linewise (i.e., when it is
440
- * pasted, should it insert itself into a new line, or should the text be
441
- * inserted at the cursor position.)
442
- */
443
- function Register(text, linewise) {
444
- this.clear();
445
- if (text) {
446
- this.set(text, linewise);
447
- }
448
- }
449
- Register.prototype = {
450
- set: function(text, linewise) {
451
- this.text = text;
452
- this.linewise = !!linewise;
453
- },
454
- append: function(text, linewise) {
455
- // if this register has ever been set to linewise, use linewise.
456
- if (linewise || this.linewise) {
457
- this.text += '\n' + text;
458
- this.linewise = true;
459
- } else {
460
- this.text += text;
461
- }
462
- },
463
- clear: function() {
464
- this.text = '';
465
- this.linewise = false;
466
- },
467
- toString: function() { return this.text; }
468
- };
469
-
470
- /*
471
- * vim registers allow you to keep many independent copy and paste buffers.
472
- * See http://usevim.com/2012/04/13/registers/ for an introduction.
473
- *
474
- * RegisterController keeps the state of all the registers. An initial
475
- * state may be passed in. The unnamed register '"' will always be
476
- * overridden.
477
- */
478
- function RegisterController(registers) {
479
- this.registers = registers;
480
- this.unamedRegister = registers['\"'] = new Register();
481
- }
482
- RegisterController.prototype = {
483
- pushText: function(registerName, operator, text, linewise) {
484
- // Lowercase and uppercase registers refer to the same register.
485
- // Uppercase just means append.
486
- var register = this.isValidRegister(registerName) ?
487
- this.getRegister(registerName) : null;
488
- // if no register/an invalid register was specified, things go to the
489
- // default registers
490
- if (!register) {
491
- switch (operator) {
492
- case 'yank':
493
- // The 0 register contains the text from the most recent yank.
494
- this.registers['0'] = new Register(text, linewise);
495
- break;
496
- case 'delete':
497
- case 'change':
498
- if (text.indexOf('\n') == -1) {
499
- // Delete less than 1 line. Update the small delete register.
500
- this.registers['-'] = new Register(text, linewise);
501
- } else {
502
- // Shift down the contents of the numbered registers and put the
503
- // deleted text into register 1.
504
- this.shiftNumericRegisters_();
505
- this.registers['1'] = new Register(text, linewise);
506
- }
507
- break;
508
- }
509
- // Make sure the unnamed register is set to what just happened
510
- this.unamedRegister.set(text, linewise);
511
- return;
512
- }
513
-
514
- // If we've gotten to this point, we've actually specified a register
515
- var append = isUpperCase(registerName);
516
- if (append) {
517
- register.append(text, linewise);
518
- // The unamed register always has the same value as the last used
519
- // register.
520
- this.unamedRegister.append(text, linewise);
521
- } else {
522
- register.set(text, linewise);
523
- this.unamedRegister.set(text, linewise);
524
- }
525
- },
526
- // Gets the register named @name. If one of @name doesn't already exist,
527
- // create it. If @name is invalid, return the unamedRegister.
528
- getRegister: function(name) {
529
- if (!this.isValidRegister(name)) {
530
- return this.unamedRegister;
531
- }
532
- name = name.toLowerCase();
533
- if (!this.registers[name]) {
534
- this.registers[name] = new Register();
535
- }
536
- return this.registers[name];
537
- },
538
- isValidRegister: function(name) {
539
- return name && inArray(name, validRegisters);
540
- },
541
- shiftNumericRegisters_: function() {
542
- for (var i = 9; i >= 2; i--) {
543
- this.registers[i] = this.getRegister('' + (i - 1));
544
- }
545
- }
546
- };
547
-
548
- var commandDispatcher = {
549
- matchCommand: function(key, keyMap, vim) {
550
- var inputState = vim.inputState;
551
- var keys = inputState.keyBuffer.concat(key);
552
- for (var i = 0; i < keyMap.length; i++) {
553
- var command = keyMap[i];
554
- if (matchKeysPartial(keys, command.keys)) {
555
- if (keys.length < command.keys.length) {
556
- // Matches part of a multi-key command. Buffer and wait for next
557
- // stroke.
558
- inputState.keyBuffer.push(key);
559
- return null;
560
- } else {
561
- if (inputState.operator && command.type == 'action') {
562
- // Ignore matched action commands after an operator. Operators
563
- // only operate on motions. This check is really for text
564
- // objects since aW, a[ etcs conflicts with a.
565
- continue;
566
- }
567
- // Matches whole comand. Return the command.
568
- if (command.keys[keys.length - 1] == 'character') {
569
- inputState.selectedCharacter = keys[keys.length - 1];
570
- }
571
- inputState.keyBuffer = [];
572
- return command;
573
- }
574
- }
575
- }
576
- // Clear the buffer since there are no partial matches.
577
- inputState.keyBuffer = [];
578
- return null;
579
- },
580
- processCommand: function(cm, vim, command) {
581
- switch (command.type) {
582
- case 'motion':
583
- this.processMotion(cm, vim, command);
584
- break;
585
- case 'operator':
586
- this.processOperator(cm, vim, command);
587
- break;
588
- case 'operatorMotion':
589
- this.processOperatorMotion(cm, vim, command);
590
- break;
591
- case 'action':
592
- this.processAction(cm, vim, command);
593
- break;
594
- case 'search':
595
- this.processSearch(cm, vim, command);
596
- break;
597
- case 'ex':
598
- case 'keyToEx':
599
- this.processEx(cm, vim, command);
600
- break;
601
- default:
602
- break;
603
- }
604
- },
605
- processMotion: function(cm, vim, command) {
606
- vim.inputState.motion = command.motion;
607
- vim.inputState.motionArgs = copyArgs(command.motionArgs);
608
- this.evalInput(cm, vim);
609
- },
610
- processOperator: function(cm, vim, command) {
611
- var inputState = vim.inputState;
612
- if (inputState.operator) {
613
- if (inputState.operator == command.operator) {
614
- // Typing an operator twice like 'dd' makes the operator operate
615
- // linewise
616
- inputState.motion = 'expandToLine';
617
- inputState.motionArgs = { linewise: true };
618
- this.evalInput(cm, vim);
619
- return;
620
- } else {
621
- // 2 different operators in a row doesn't make sense.
622
- inputState.reset();
623
- }
624
- }
625
- inputState.operator = command.operator;
626
- inputState.operatorArgs = copyArgs(command.operatorArgs);
627
- if (vim.visualMode) {
628
- // Operating on a selection in visual mode. We don't need a motion.
629
- this.evalInput(cm, vim);
630
- }
631
- },
632
- processOperatorMotion: function(cm, vim, command) {
633
- var visualMode = vim.visualMode;
634
- var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
635
- if (operatorMotionArgs) {
636
- // Operator motions may have special behavior in visual mode.
637
- if (visualMode && operatorMotionArgs.visualLine) {
638
- vim.visualLine = true;
639
- }
640
- }
641
- this.processOperator(cm, vim, command);
642
- if (!visualMode) {
643
- this.processMotion(cm, vim, command);
644
- }
645
- },
646
- processAction: function(cm, vim, command) {
647
- var inputState = vim.inputState;
648
- var repeat = inputState.getRepeat();
649
- var repeatIsExplicit = !!repeat;
650
- var actionArgs = copyArgs(command.actionArgs) || {};
651
- if (inputState.selectedCharacter) {
652
- actionArgs.selectedCharacter = inputState.selectedCharacter;
653
- }
654
- // Actions may or may not have motions and operators. Do these first.
655
- if (command.operator) {
656
- this.processOperator(cm, vim, command);
657
- }
658
- if (command.motion) {
659
- this.processMotion(cm, vim, command);
660
- }
661
- if (command.motion || command.operator) {
662
- this.evalInput(cm, vim);
663
- }
664
- actionArgs.repeat = repeat || 1;
665
- actionArgs.repeatIsExplicit = repeatIsExplicit;
666
- actionArgs.registerName = inputState.registerName;
667
- inputState.reset();
668
- vim.lastMotion = null,
669
- actions[command.action](cm, actionArgs, vim);
670
- },
671
- processSearch: function(cm, vim, command) {
672
- if (!cm.getSearchCursor) {
673
- // Search depends on SearchCursor.
674
- return;
675
- }
676
- var forward = command.searchArgs.forward;
677
- getSearchState(cm).setReversed(!forward);
678
- var promptPrefix = (forward) ? '/' : '?';
679
- function handleQuery(query, ignoreCase, smartCase) {
680
- updateSearchQuery(cm, query, ignoreCase, smartCase);
681
- commandDispatcher.processMotion(cm, vim, {
682
- type: 'motion',
683
- motion: 'findNext'
684
- });
685
- }
686
- function onPromptClose(query) {
687
- handleQuery(query, true /** ignoreCase */, true /** smartCase */);
688
- }
689
- switch (command.searchArgs.querySrc) {
690
- case 'prompt':
691
- showPrompt(cm, onPromptClose, promptPrefix, searchPromptDesc);
692
- break;
693
- case 'wordUnderCursor':
694
- var word = expandWordUnderCursor(cm, false /** inclusive */,
695
- true /** forward */, false /** bigWord */,
696
- true /** noSymbol */);
697
- var isKeyword = true;
698
- if (!word) {
699
- word = expandWordUnderCursor(cm, false /** inclusive */,
700
- true /** forward */, false /** bigWord */,
701
- false /** noSymbol */);
702
- isKeyword = false;
703
- }
704
- if (!word) {
705
- return;
706
- }
707
- var query = cm.getLine(word.start.line).substring(word.start.ch,
708
- word.end.ch + 1);
709
- if (isKeyword) {
710
- query = '\\b' + query + '\\b';
711
- } else {
712
- query = escapeRegex(query);
713
- }
714
- cm.setCursor(word.start);
715
- handleQuery(query, true /** ignoreCase */, false /** smartCase */);
716
- break;
717
- }
718
- },
719
- processEx: function(cm, vim, command) {
720
- function onPromptClose(input) {
721
- exCommandDispatcher.processCommand(cm, input);
722
- }
723
- if (command.type == 'keyToEx') {
724
- // Handle user defined Ex to Ex mappings
725
- exCommandDispatcher.processCommand(cm, command.exArgs.input);
726
- } else {
727
- if (vim.visualMode) {
728
- showPrompt(cm, onPromptClose, ':', undefined, '\'<,\'>');
729
- } else {
730
- showPrompt(cm, onPromptClose, ':');
731
- }
732
- }
733
- },
734
- evalInput: function(cm, vim) {
735
- // If the motion comand is set, execute both the operator and motion.
736
- // Otherwise return.
737
- var inputState = vim.inputState;
738
- var motion = inputState.motion;
739
- var motionArgs = inputState.motionArgs || {};
740
- var operator = inputState.operator;
741
- var operatorArgs = inputState.operatorArgs || {};
742
- var registerName = inputState.registerName;
743
- var selectionEnd = cm.getCursor('head');
744
- var selectionStart = cm.getCursor('anchor');
745
- // The difference between cur and selection cursors are that cur is
746
- // being operated on and ignores that there is a selection.
747
- var curStart = copyCursor(selectionEnd);
748
- var curOriginal = copyCursor(curStart);
749
- var curEnd;
750
- var repeat;
751
- if (motionArgs.repeat !== undefined) {
752
- // If motionArgs specifies a repeat, that takes precedence over the
753
- // input state's repeat. Used by Ex mode and can be user defined.
754
- repeat = inputState.motionArgs.repeat;
755
- } else {
756
- repeat = inputState.getRepeat();
757
- }
758
- if (repeat > 0 && motionArgs.explicitRepeat) {
759
- motionArgs.repeatIsExplicit = true;
760
- } else if (motionArgs.noRepeat ||
761
- (!motionArgs.explicitRepeat && repeat === 0)) {
762
- repeat = 1;
763
- motionArgs.repeatIsExplicit = false;
764
- }
765
- if (inputState.selectedCharacter) {
766
- // If there is a character input, stick it in all of the arg arrays.
767
- motionArgs.selectedCharacter = operatorArgs.selectedCharacter =
768
- inputState.selectedCharacter;
769
- }
770
- motionArgs.repeat = repeat;
771
- inputState.reset();
772
- if (motion) {
773
- var motionResult = motions[motion](cm, motionArgs, vim);
774
- vim.lastMotion = motions[motion];
775
- if (!motionResult) {
776
- return;
777
- }
778
- if (motionResult instanceof Array) {
779
- curStart = motionResult[0];
780
- curEnd = motionResult[1];
781
- } else {
782
- curEnd = motionResult;
783
- }
784
- // TODO: Handle null returns from motion commands better.
785
- if (!curEnd) {
786
- curEnd = { ch: curStart.ch, line: curStart.line };
787
- }
788
- if (vim.visualMode) {
789
- // Check if the selection crossed over itself. Will need to shift
790
- // the start point if that happened.
791
- if (cursorIsBefore(selectionStart, selectionEnd) &&
792
- (cursorEqual(selectionStart, curEnd) ||
793
- cursorIsBefore(curEnd, selectionStart))) {
794
- // The end of the selection has moved from after the start to
795
- // before the start. We will shift the start right by 1.
796
- selectionStart.ch += 1;
797
- } else if (cursorIsBefore(selectionEnd, selectionStart) &&
798
- (cursorEqual(selectionStart, curEnd) ||
799
- cursorIsBefore(selectionStart, curEnd))) {
800
- // The opposite happened. We will shift the start left by 1.
801
- selectionStart.ch -= 1;
802
- }
803
- selectionEnd = curEnd;
804
- if (vim.visualLine) {
805
- if (cursorIsBefore(selectionStart, selectionEnd)) {
806
- selectionStart.ch = 0;
807
- selectionEnd.ch = lineLength(cm, selectionEnd.line);
808
- } else {
809
- selectionEnd.ch = 0;
810
- selectionStart.ch = lineLength(cm, selectionStart.line);
811
- }
812
- }
813
- // Need to set the cursor to clear the selection. Otherwise,
814
- // CodeMirror can't figure out that we changed directions...
815
- cm.setCursor(selectionStart);
816
- cm.setSelection(selectionStart, selectionEnd);
817
- updateMark(cm, vim, '<',
818
- cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
819
- : selectionEnd);
820
- updateMark(cm, vim, '>',
821
- cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
822
- : selectionStart);
823
- } else if (!operator) {
824
- curEnd = clipCursorToContent(cm, curEnd);
825
- cm.setCursor(curEnd.line, curEnd.ch);
826
- }
827
- }
828
-
829
- if (operator) {
830
- var inverted = false;
831
- vim.lastMotion = null;
832
- operatorArgs.repeat = repeat; // Indent in visual mode needs this.
833
- if (vim.visualMode) {
834
- curStart = selectionStart;
835
- curEnd = selectionEnd;
836
- motionArgs.inclusive = true;
837
- }
838
- // Swap start and end if motion was backward.
839
- if (cursorIsBefore(curEnd, curStart)) {
840
- var tmp = curStart;
841
- curStart = curEnd;
842
- curEnd = tmp;
843
- inverted = true;
844
- }
845
- if (motionArgs.inclusive && !(vim.visualMode && inverted)) {
846
- // Move the selection end one to the right to include the last
847
- // character.
848
- curEnd.ch++;
849
- }
850
- var linewise = motionArgs.linewise ||
851
- (vim.visualMode && vim.visualLine);
852
- if (linewise) {
853
- // Expand selection to entire line.
854
- expandSelectionToLine(cm, curStart, curEnd);
855
- } else if (motionArgs.forward) {
856
- // Clip to trailing newlines only if we the motion goes forward.
857
- clipToLine(cm, curStart, curEnd);
858
- }
859
- operatorArgs.registerName = registerName;
860
- // Keep track of linewise as it affects how paste and change behave.
861
- operatorArgs.linewise = linewise;
862
- operators[operator](cm, operatorArgs, vim, curStart,
863
- curEnd, curOriginal);
864
- if (vim.visualMode) {
865
- exitVisualMode(cm, vim);
866
- }
867
- if (operatorArgs.enterInsertMode) {
868
- actions.enterInsertMode(cm);
869
- }
870
- }
871
- }
872
- };
873
-
874
- /**
875
- * typedef {Object{line:number,ch:number}} Cursor An object containing the
876
- * position of the cursor.
877
- */
878
- // All of the functions below return Cursor objects.
879
- var motions = {
880
- expandToLine: function(cm, motionArgs) {
881
- // Expands forward to end of line, and then to next line if repeat is
882
- // >1. Does not handle backward motion!
883
- var cur = cm.getCursor();
884
- return { line: cur.line + motionArgs.repeat - 1, ch: Infinity };
885
- },
886
- findNext: function(cm, motionArgs, vim) {
887
- return findNext(cm, false /** prev */, motionArgs.repeat);
888
- },
889
- findPrev: function(cm, motionArgs, vim) {
890
- return findNext(cm, true /** prev */, motionArgs.repeat);
891
- },
892
- goToMark: function(cm, motionArgs, vim) {
893
- var mark = vim.marks[motionArgs.selectedCharacter];
894
- if (mark) {
895
- return mark.find();
896
- }
897
- return null;
898
- },
899
- moveByCharacters: function(cm, motionArgs) {
900
- var cur = cm.getCursor();
901
- var repeat = motionArgs.repeat;
902
- var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
903
- return { line: cur.line, ch: ch };
904
- },
905
- moveByLines: function(cm, motionArgs, vim) {
906
- var endCh = cm.getCursor().ch;
907
- // Depending what our last motion was, we may want to do different
908
- // things. If our last motion was moving vertically, we want to
909
- // preserve the HPos from our last horizontal move. If our last motion
910
- // was going to the end of a line, moving vertically we should go to
911
- // the end of the line, etc.
912
- switch (vim.lastMotion) {
913
- case this.moveByLines:
914
- case this.moveToColumn:
915
- case this.moveToEol:
916
- endCh = vim.lastHPos;
917
- break;
918
- default:
919
- vim.lastHPos = endCh;
920
- }
921
- var cur = cm.getCursor();
922
- var repeat = motionArgs.repeat;
923
- var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
924
- if (line < 0 || line > cm.lineCount() - 1) {
925
- return null;
926
- }
927
- return { line: line, ch: endCh };
928
- },
929
- moveByPage: function(cm, motionArgs) {
930
- // CodeMirror only exposes functions that move the cursor page down, so
931
- // doing this bad hack to move the cursor and move it back. evalInput
932
- // will move the cursor to where it should be in the end.
933
- var curStart = cm.getCursor();
934
- var repeat = motionArgs.repeat;
935
- cm.moveV((motionArgs.forward ? repeat : -repeat), 'page');
936
- var curEnd = cm.getCursor();
937
- cm.setCursor(curStart);
938
- return curEnd;
939
- },
940
- moveByParagraph: function(cm, motionArgs) {
941
- var line = cm.getCursor().line;
942
- var repeat = motionArgs.repeat;
943
- var inc = motionArgs.forward ? 1 : -1;
944
- for (var i = 0; i < repeat; i++) {
945
- if ((!motionArgs.forward && line === 0) ||
946
- (motionArgs.forward && line == cm.lineCount() - 1)) {
947
- break;
948
- }
949
- line += inc;
950
- while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) {
951
- line += inc;
952
- }
953
- }
954
- return { line: line, ch: 0 };
955
- },
956
- moveByWords: function(cm, motionArgs) {
957
- return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
958
- !!motionArgs.wordEnd, !!motionArgs.bigWord);
959
- },
960
- moveTillCharacter: function(cm, motionArgs) {
961
- var repeat = motionArgs.repeat;
962
- var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
963
- motionArgs.selectedCharacter);
964
- var increment = motionArgs.forward ? -1 : 1;
965
- curEnd.ch += increment;
966
- return curEnd;
967
- },
968
- moveToCharacter: function(cm, motionArgs) {
969
- var repeat = motionArgs.repeat;
970
- return moveToCharacter(cm, repeat, motionArgs.forward,
971
- motionArgs.selectedCharacter);
972
- },
973
- moveToColumn: function(cm, motionArgs, vim) {
974
- var repeat = motionArgs.repeat;
975
- // repeat is equivalent to which column we want to move to!
976
- vim.lastHPos = repeat - 1;
977
- return moveToColumn(cm, repeat);
978
- },
979
- moveToEol: function(cm, motionArgs, vim) {
980
- var cur = cm.getCursor();
981
- vim.lastHPos = Infinity;
982
- return { line: cur.line + motionArgs.repeat - 1, ch: Infinity };
983
- },
984
- moveToFirstNonWhiteSpaceCharacter: function(cm) {
985
- // Go to the start of the line where the text begins, or the end for
986
- // whitespace-only lines
987
- var cursor = cm.getCursor();
988
- var line = cm.getLine(cursor.line);
989
- return { line: cursor.line,
990
- ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)) };
991
- },
992
- moveToMatchedSymbol: function(cm, motionArgs) {
993
- var cursor = cm.getCursor();
994
- var symbol = cm.getLine(cursor.line).charAt(cursor.ch);
995
- if (isMatchableSymbol(symbol)) {
996
- return findMatchedSymbol(cm, cm.getCursor(), motionArgs.symbol);
997
- } else {
998
- return cursor;
999
- }
1000
- },
1001
- moveToStartOfLine: function(cm) {
1002
- var cursor = cm.getCursor();
1003
- return { line: cursor.line, ch: 0 };
1004
- },
1005
- moveToLineOrEdgeOfDocument: function(cm, motionArgs) {
1006
- var lineNum = motionArgs.forward ? cm.lineCount() - 1 : 0;
1007
- if (motionArgs.repeatIsExplicit) {
1008
- lineNum = motionArgs.repeat - 1;
1009
- }
1010
- return { line: lineNum,
1011
- ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) };
1012
- },
1013
- textObjectManipulation: function(cm, motionArgs) {
1014
- var character = motionArgs.selectedCharacter;
1015
- // Inclusive is the difference between a and i
1016
- // TODO: Instead of using the additional text object map to perform text
1017
- // object operations, merge the map into the defaultKeyMap and use
1018
- // motionArgs to define behavior. Define separate entries for 'aw',
1019
- // 'iw', 'a[', 'i[', etc.
1020
- var inclusive = !motionArgs.textObjectInner;
1021
- if (!textObjects[character]) {
1022
- // No text object defined for this, don't move.
1023
- return null;
1024
- }
1025
- var tmp = textObjects[character](cm, inclusive);
1026
- var start = tmp.start;
1027
- var end = tmp.end;
1028
- return [start, end];
1029
- }
1030
- };
1031
-
1032
- var operators = {
1033
- change: function(cm, operatorArgs, vim, curStart, curEnd) {
1034
- getVimGlobalState().registerController.pushText(
1035
- operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd),
1036
- operatorArgs.linewise);
1037
- if (operatorArgs.linewise) {
1038
- // Delete starting at the first nonwhitespace character of the first
1039
- // line, instead of from the start of the first line. This way we get
1040
- // an indent when we get into insert mode. This behavior isn't quite
1041
- // correct because we should treat this as a completely new line, and
1042
- // indent should be whatever codemirror thinks is the right indent.
1043
- // But cm.indentLine doesn't seem work on empty lines.
1044
- // TODO: Fix the above.
1045
- curStart.ch =
1046
- findFirstNonWhiteSpaceCharacter(cm.getLine(curStart.line));
1047
- // Insert an additional newline so that insert mode can start there.
1048
- // curEnd should be on the first character of the new line.
1049
- cm.replaceRange('\n', curStart, curEnd);
1050
- } else {
1051
- cm.replaceRange('', curStart, curEnd);
1052
- }
1053
- cm.setCursor(curStart);
1054
- },
1055
- // delete is a javascript keyword.
1056
- 'delete': function(cm, operatorArgs, vim, curStart, curEnd) {
1057
- getVimGlobalState().registerController.pushText(
1058
- operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd),
1059
- operatorArgs.linewise);
1060
- cm.replaceRange('', curStart, curEnd);
1061
- if (operatorArgs.linewise) {
1062
- cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
1063
- } else {
1064
- cm.setCursor(curStart);
1065
- }
1066
- },
1067
- indent: function(cm, operatorArgs, vim, curStart, curEnd) {
1068
- var startLine = curStart.line;
1069
- var endLine = curEnd.line;
1070
- // In visual mode, n> shifts the selection right n times, instead of
1071
- // shifting n lines right once.
1072
- var repeat = (vim.visualMode) ? operatorArgs.repeat : 1;
1073
- if (operatorArgs.linewise) {
1074
- // The only way to delete a newline is to delete until the start of
1075
- // the next line, so in linewise mode evalInput will include the next
1076
- // line. We don't want this in indent, so we go back a line.
1077
- endLine--;
1078
- }
1079
- for (var i = startLine; i <= endLine; i++) {
1080
- for (var j = 0; j < repeat; j++) {
1081
- cm.indentLine(i, operatorArgs.indentRight);
1082
- }
1083
- }
1084
- cm.setCursor(curStart);
1085
- cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
1086
- },
1087
- swapcase: function(cm, operatorArgs, vim, curStart, curEnd, curOriginal) {
1088
- var toSwap = cm.getRange(curStart, curEnd);
1089
- var swapped = '';
1090
- for (var i = 0; i < toSwap.length; i++) {
1091
- var character = toSwap.charAt(i);
1092
- swapped += isUpperCase(character) ? character.toLowerCase() :
1093
- character.toUpperCase();
1094
- }
1095
- cm.replaceRange(swapped, curStart, curEnd);
1096
- cm.setCursor(curOriginal);
1097
- },
1098
- yank: function(cm, operatorArgs, vim, curStart, curEnd, curOriginal) {
1099
- getVimGlobalState().registerController.pushText(
1100
- operatorArgs.registerName, 'yank',
1101
- cm.getRange(curStart, curEnd), operatorArgs.linewise);
1102
- cm.setCursor(curOriginal);
1103
- }
1104
- };
1105
-
1106
- var actions = {
1107
- clearSearchHighlight: clearSearchHighlight,
1108
- enterInsertMode: function(cm, actionArgs) {
1109
- var insertAt = (actionArgs) ? actionArgs.insertAt : null;
1110
- if (insertAt == 'eol') {
1111
- var cursor = cm.getCursor();
1112
- cursor = { line: cursor.line, ch: lineLength(cm, cursor.line) };
1113
- cm.setCursor(cursor);
1114
- } else if (insertAt == 'charAfter') {
1115
- cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
1116
- }
1117
- cm.setOption('keyMap', 'vim-insert');
1118
- },
1119
- toggleVisualMode: function(cm, actionArgs, vim) {
1120
- var repeat = actionArgs.repeat;
1121
- var curStart = cm.getCursor();
1122
- var curEnd;
1123
- // TODO: The repeat should actually select number of characters/lines
1124
- // equal to the repeat times the size of the previous visual
1125
- // operation.
1126
- if (!vim.visualMode) {
1127
- vim.visualMode = true;
1128
- vim.visualLine = !!actionArgs.linewise;
1129
- if (vim.visualLine) {
1130
- curStart.ch = 0;
1131
- curEnd = clipCursorToContent(cm, {
1132
- line: curStart.line + repeat - 1,
1133
- ch: lineLength(cm, curStart.line)
1134
- }, true /** includeLineBreak */);
1135
- } else {
1136
- curEnd = clipCursorToContent(cm, {
1137
- line: curStart.line,
1138
- ch: curStart.ch + repeat
1139
- }, true /** includeLineBreak */);
1140
- }
1141
- // Make the initial selection.
1142
- if (!actionArgs.repeatIsExplicit && !vim.visualLine) {
1143
- // This is a strange case. Here the implicit repeat is 1. The
1144
- // following commands lets the cursor hover over the 1 character
1145
- // selection.
1146
- cm.setCursor(curEnd);
1147
- cm.setSelection(curEnd, curStart);
1148
- } else {
1149
- cm.setSelection(curStart, curEnd);
1150
- }
1151
- } else {
1152
- curStart = cm.getCursor('anchor');
1153
- curEnd = cm.getCursor('head');
1154
- if (!vim.visualLine && actionArgs.linewise) {
1155
- // Shift-V pressed in characterwise visual mode. Switch to linewise
1156
- // visual mode instead of exiting visual mode.
1157
- vim.visualLine = true;
1158
- curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
1159
- lineLength(cm, curStart.line);
1160
- curEnd.ch = cursorIsBefore(curStart, curEnd) ?
1161
- lineLength(cm, curEnd.line) : 0;
1162
- cm.setSelection(curStart, curEnd);
1163
- } else if (vim.visualLine && !actionArgs.linewise) {
1164
- // v pressed in linewise visual mode. Switch to characterwise visual
1165
- // mode instead of exiting visual mode.
1166
- vim.visualLine = false;
1167
- } else {
1168
- exitVisualMode(cm, vim);
1169
- }
1170
- }
1171
- updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
1172
- : curEnd);
1173
- updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
1174
- : curStart);
1175
- },
1176
- joinLines: function(cm, actionArgs, vim) {
1177
- var curStart, curEnd;
1178
- if (vim.visualMode) {
1179
- curStart = cm.getCursor('anchor');
1180
- curEnd = cm.getCursor('head');
1181
- curEnd.ch = lineLength(cm, curEnd.line) - 1;
1182
- } else {
1183
- // Repeat is the number of lines to join. Minimum 2 lines.
1184
- var repeat = Math.max(actionArgs.repeat, 2);
1185
- curStart = cm.getCursor();
1186
- curEnd = clipCursorToContent(cm, { line: curStart.line + repeat - 1,
1187
- ch: Infinity });
1188
- }
1189
- var finalCh = 0;
1190
- cm.operation(function() {
1191
- for (var i = curStart.line; i < curEnd.line; i++) {
1192
- finalCh = lineLength(cm, curStart.line);
1193
- var tmp = { line: curStart.line + 1,
1194
- ch: lineLength(cm, curStart.line + 1) };
1195
- var text = cm.getRange(curStart, tmp);
1196
- text = text.replace(/\n\s*/g, ' ');
1197
- cm.replaceRange(text, curStart, tmp);
1198
- }
1199
- var curFinalPos = { line: curStart.line, ch: finalCh };
1200
- cm.setCursor(curFinalPos);
1201
- });
1202
- },
1203
- newLineAndEnterInsertMode: function(cm, actionArgs) {
1204
- var insertAt = cm.getCursor();
1205
- if (insertAt.line === 0 && !actionArgs.after) {
1206
- // Special case for inserting newline before start of document.
1207
- cm.replaceRange('\n', { line: 0, ch: 0 });
1208
- cm.setCursor(0, 0);
1209
- } else {
1210
- insertAt.line = (actionArgs.after) ? insertAt.line :
1211
- insertAt.line - 1;
1212
- insertAt.ch = lineLength(cm, insertAt.line);
1213
- cm.setCursor(insertAt);
1214
- var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||
1215
- CodeMirror.commands.newlineAndIndent;
1216
- newlineFn(cm);
1217
- }
1218
- this.enterInsertMode(cm);
1219
- },
1220
- paste: function(cm, actionArgs, vim) {
1221
- var cur = cm.getCursor();
1222
- var register = getVimGlobalState().registerController.getRegister(
1223
- actionArgs.registerName);
1224
- if (!register.text) {
1225
- return;
1226
- }
1227
- for (var text = '', i = 0; i < actionArgs.repeat; i++) {
1228
- text += register.text;
1229
- }
1230
- var linewise = register.linewise;
1231
- if (linewise) {
1232
- if (actionArgs.after) {
1233
- // Move the newline at the end to the start instead, and paste just
1234
- // before the newline character of the line we are on right now.
1235
- text = '\n' + text.slice(0, text.length - 1);
1236
- cur.ch = lineLength(cm, cur.line);
1237
- } else {
1238
- cur.ch = 0;
1239
- }
1240
- } else {
1241
- cur.ch += actionArgs.after ? 1 : 0;
1242
- }
1243
- cm.replaceRange(text, cur);
1244
- // Now fine tune the cursor to where we want it.
1245
- var curPosFinal;
1246
- var idx;
1247
- if (linewise && actionArgs.after) {
1248
- curPosFinal = { line: cur.line + 1,
1249
- ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)) };
1250
- } else if (linewise && !actionArgs.after) {
1251
- curPosFinal = { line: cur.line,
1252
- ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)) };
1253
- } else if (!linewise && actionArgs.after) {
1254
- idx = cm.indexFromPos(cur);
1255
- curPosFinal = cm.posFromIndex(idx + text.length - 1);
1256
- } else {
1257
- idx = cm.indexFromPos(cur);
1258
- curPosFinal = cm.posFromIndex(idx + text.length);
1259
- }
1260
- cm.setCursor(curPosFinal);
1261
- },
1262
- undo: function(cm, actionArgs) {
1263
- repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
1264
- },
1265
- redo: function(cm, actionArgs) {
1266
- repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
1267
- },
1268
- setRegister: function(cm, actionArgs, vim) {
1269
- vim.inputState.registerName = actionArgs.selectedCharacter;
1270
- },
1271
- setMark: function(cm, actionArgs, vim) {
1272
- var markName = actionArgs.selectedCharacter;
1273
- updateMark(cm, vim, markName, cm.getCursor());
1274
- },
1275
- replace: function(cm, actionArgs) {
1276
- var replaceWith = actionArgs.selectedCharacter;
1277
- var curStart = cm.getCursor();
1278
- var line = cm.getLine(curStart.line);
1279
- var replaceTo = curStart.ch + actionArgs.repeat;
1280
- if (replaceTo > line.length) {
1281
- return;
1282
- }
1283
- var curEnd = { line: curStart.line, ch: replaceTo };
1284
- var replaceWithStr = '';
1285
- for (var i = 0; i < curEnd.ch - curStart.ch; i++) {
1286
- replaceWithStr += replaceWith;
1287
- }
1288
- cm.replaceRange(replaceWithStr, curStart, curEnd);
1289
- cm.setCursor(offsetCursor(curEnd, 0, -1));
1290
- }
1291
- };
1292
-
1293
- var textObjects = {
1294
- // TODO: lots of possible exceptions that can be thrown here. Try da(
1295
- // outside of a () block.
1296
- // TODO: implement text objects for the reverse like }. Should just be
1297
- // an additional mapping after moving to the defaultKeyMap.
1298
- 'w': function(cm, inclusive) {
1299
- return expandWordUnderCursor(cm, inclusive, true /** forward */,
1300
- false /** bigWord */);
1301
- },
1302
- 'W': function(cm, inclusive) {
1303
- return expandWordUnderCursor(cm, inclusive,
1304
- true /** forward */, true /** bigWord */);
1305
- },
1306
- '{': function(cm, inclusive) {
1307
- return selectCompanionObject(cm, '}', inclusive);
1308
- },
1309
- '(': function(cm, inclusive) {
1310
- return selectCompanionObject(cm, ')', inclusive);
1311
- },
1312
- '[': function(cm, inclusive) {
1313
- return selectCompanionObject(cm, ']', inclusive);
1314
- },
1315
- '\'': function(cm, inclusive) {
1316
- return findBeginningAndEnd(cm, "'", inclusive);
1317
- },
1318
- '\"': function(cm, inclusive) {
1319
- return findBeginningAndEnd(cm, '"', inclusive);
1320
- }
1321
- };
1322
-
1323
- /*
1324
- * Below are miscellaneous utility functions used by vim.js
1325
- */
1326
-
1327
- /**
1328
- * Clips cursor to ensure that:
1329
- * 0 <= cur.ch < lineLength
1330
- * AND
1331
- * 0 <= cur.line < lineCount
1332
- * If includeLineBreak is true, then allow cur.ch == lineLength.
1333
- */
1334
- function clipCursorToContent(cm, cur, includeLineBreak) {
1335
- var line = Math.min(Math.max(0, cur.line), cm.lineCount() - 1);
1336
- var maxCh = lineLength(cm, line) - 1;
1337
- maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
1338
- var ch = Math.min(Math.max(0, cur.ch), maxCh);
1339
- return { line: line, ch: ch };
1340
- }
1341
- // Merge arguments in place, for overriding arguments.
1342
- function mergeArgs(to, from) {
1343
- for (var prop in from) {
1344
- if (from.hasOwnProperty(prop)) {
1345
- to[prop] = from[prop];
1346
- }
1347
- }
1348
- }
1349
- function copyArgs(args) {
1350
- var ret = {};
1351
- for (var prop in args) {
1352
- if (args.hasOwnProperty(prop)) {
1353
- ret[prop] = args[prop];
1354
- }
1355
- }
1356
- return ret;
1357
- }
1358
- function offsetCursor(cur, offsetLine, offsetCh) {
1359
- return { line: cur.line + offsetLine, ch: cur.ch + offsetCh };
1360
- }
1361
- function arrayEq(a1, a2) {
1362
- if (a1.length != a2.length) {
1363
- return false;
1364
- }
1365
- for (var i = 0; i < a1.length; i++) {
1366
- if (a1[i] != a2[i]) {
1367
- return false;
1368
- }
1369
- }
1370
- return true;
1371
- }
1372
- function matchKeysPartial(pressed, mapped) {
1373
- for (var i = 0; i < pressed.length; i++) {
1374
- // 'character' means any character. For mark, register commads, etc.
1375
- if (pressed[i] != mapped[i] && mapped[i] != 'character') {
1376
- return false;
1377
- }
1378
- }
1379
- return true;
1380
- }
1381
- function arrayIsSubsetFromBeginning(small, big) {
1382
- for (var i = 0; i < small.length; i++) {
1383
- if (small[i] != big[i]) {
1384
- return false;
1385
- }
1386
- }
1387
- return true;
1388
- }
1389
- function repeatFn(cm, fn, repeat) {
1390
- return function() {
1391
- for (var i = 0; i < repeat; i++) {
1392
- fn(cm);
1393
- }
1394
- };
1395
- }
1396
- function copyCursor(cur) {
1397
- return { line: cur.line, ch: cur.ch };
1398
- }
1399
- function cursorEqual(cur1, cur2) {
1400
- return cur1.ch == cur2.ch && cur1.line == cur2.line;
1401
- }
1402
- function cursorIsBefore(cur1, cur2) {
1403
- if (cur1.line < cur2.line) {
1404
- return true;
1405
- } else if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
1406
- return true;
1407
- }
1408
- return false;
1409
- }
1410
- function lineLength(cm, lineNum) {
1411
- return cm.getLine(lineNum).length;
1412
- }
1413
- function reverse(s){
1414
- return s.split("").reverse().join("");
1415
- }
1416
- function trim(s) {
1417
- if (s.trim) {
1418
- return s.trim();
1419
- } else {
1420
- return s.replace(/^\s+|\s+$/g, '');
1421
- }
1422
- }
1423
- function escapeRegex(s) {
1424
- return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, "\\$1");
1425
- }
1426
-
1427
- function exitVisualMode(cm, vim) {
1428
- vim.visualMode = false;
1429
- vim.visualLine = false;
1430
- var selectionStart = cm.getCursor('anchor');
1431
- var selectionEnd = cm.getCursor('head');
1432
- if (!cursorEqual(selectionStart, selectionEnd)) {
1433
- // Clear the selection and set the cursor only if the selection has not
1434
- // already been cleared. Otherwise we risk moving the cursor somewhere
1435
- // it's not supposed to be.
1436
- cm.setCursor(clipCursorToContent(cm, selectionEnd));
1437
- }
1438
- }
1439
-
1440
- // Remove any trailing newlines from the selection. For
1441
- // example, with the caret at the start of the last word on the line,
1442
- // 'dw' should word, but not the newline, while 'w' should advance the
1443
- // caret to the first character of the next line.
1444
- function clipToLine(cm, curStart, curEnd) {
1445
- var selection = cm.getRange(curStart, curEnd);
1446
- var lines = selection.split('\n');
1447
- if (lines.length > 1 && isWhiteSpaceString(lines.pop())) {
1448
- curEnd.line--;
1449
- curEnd.ch = lineLength(cm, curEnd.line);
1450
- }
1451
- }
1452
-
1453
- // Expand the selection to line ends.
1454
- function expandSelectionToLine(cm, curStart, curEnd) {
1455
- curStart.ch = 0;
1456
- curEnd.ch = 0;
1457
- curEnd.line++;
1458
- }
1459
-
1460
- function findFirstNonWhiteSpaceCharacter(text) {
1461
- if (!text) {
1462
- return 0;
1463
- }
1464
- var firstNonWS = text.search(/\S/);
1465
- return firstNonWS == -1 ? text.length : firstNonWS;
1466
- }
1467
-
1468
- function expandWordUnderCursor(cm, inclusive, forward, bigWord, noSymbol) {
1469
- var cur = cm.getCursor();
1470
- var line = cm.getLine(cur.line);
1471
- var idx = cur.ch;
1472
-
1473
- // Seek to first word or non-whitespace character, depending on if
1474
- // noSymbol is true.
1475
- var textAfterIdx = line.substring(idx);
1476
- var firstMatchedChar;
1477
- if (noSymbol) {
1478
- firstMatchedChar = textAfterIdx.search(/\w/);
1479
- } else {
1480
- firstMatchedChar = textAfterIdx.search(/\S/);
1481
- }
1482
- if (firstMatchedChar == -1) {
1483
- return null;
1484
- }
1485
- idx += firstMatchedChar;
1486
- textAfterIdx = line.substring(idx);
1487
- var textBeforeIdx = line.substring(0, idx);
1488
-
1489
- var matchRegex;
1490
- // Greedy matchers for the "word" we are trying to expand.
1491
- if (bigWord) {
1492
- matchRegex = /^\S+/;
1493
- } else {
1494
- if ((/\w/).test(line.charAt(idx))) {
1495
- matchRegex = /^\w+/;
1496
- } else {
1497
- matchRegex = /^[^\w\s]+/;
1498
- }
1499
- }
1500
-
1501
- var wordAfterRegex = matchRegex.exec(textAfterIdx);
1502
- var wordStart = idx;
1503
- var wordEnd = idx + wordAfterRegex[0].length - 1;
1504
- // TODO: Find a better way to do this. It will be slow on very long lines.
1505
- var wordBeforeRegex = matchRegex.exec(reverse(textBeforeIdx));
1506
- if (wordBeforeRegex) {
1507
- wordStart -= wordBeforeRegex[0].length;
1508
- }
1509
-
1510
- if (inclusive) {
1511
- wordEnd++;
1512
- }
1513
-
1514
- return { start: { line: cur.line, ch: wordStart },
1515
- end: { line: cur.line, ch: wordEnd }};
1516
- }
1517
-
1518
- /*
1519
- * Returns the boundaries of the next word. If the cursor in the middle of
1520
- * the word, then returns the boundaries of the current word, starting at
1521
- * the cursor. If the cursor is at the start/end of a word, and we are going
1522
- * forward/backward, respectively, find the boundaries of the next word.
1523
- *
1524
- * @param {CodeMirror} cm CodeMirror object.
1525
- * @param {Cursor} cur The cursor position.
1526
- * @param {boolean} forward True to search forward. False to search
1527
- * backward.
1528
- * @param {boolean} bigWord True if punctuation count as part of the word.
1529
- * False if only [a-zA-Z0-9] characters count as part of the word.
1530
- * @return {Object{from:number, to:number, line: number}} The boundaries of
1531
- * the word, or null if there are no more words.
1532
- */
1533
- // TODO: Treat empty lines (with no whitespace) as words.
1534
- function findWord(cm, cur, forward, bigWord) {
1535
- var lineNum = cur.line;
1536
- var pos = cur.ch;
1537
- var line = cm.getLine(lineNum);
1538
- var dir = forward ? 1 : -1;
1539
- var regexps = bigWord ? bigWordRegexp : wordRegexp;
1540
-
1541
- while (true) {
1542
- var stop = (dir > 0) ? line.length : -1;
1543
- var wordStart = stop, wordEnd = stop;
1544
- // Find bounds of next word.
1545
- while (pos != stop) {
1546
- var foundWord = false;
1547
- for (var i = 0; i < regexps.length && !foundWord; ++i) {
1548
- if (regexps[i].test(line.charAt(pos))) {
1549
- wordStart = pos;
1550
- // Advance to end of word.
1551
- while (pos != stop && regexps[i].test(line.charAt(pos))) {
1552
- pos += dir;
1553
- }
1554
- wordEnd = pos;
1555
- foundWord = wordStart != wordEnd;
1556
- if (wordStart == cur.ch && lineNum == cur.line &&
1557
- wordEnd == wordStart + dir) {
1558
- // We started at the end of a word. Find the next one.
1559
- continue;
1560
- } else {
1561
- return {
1562
- from: Math.min(wordStart, wordEnd + 1),
1563
- to: Math.max(wordStart, wordEnd),
1564
- line: lineNum };
1565
- }
1566
- }
1567
- }
1568
- if (!foundWord) {
1569
- pos += dir;
1570
- }
1571
- }
1572
- // Advance to next/prev line.
1573
- lineNum += dir;
1574
- if (!isLine(cm, lineNum)) {
1575
- return null;
1576
- }
1577
- line = cm.getLine(lineNum);
1578
- pos = (dir > 0) ? 0 : line.length;
1579
- }
1580
- // Should never get here.
1581
- throw 'The impossible happened.';
1582
- }
1583
-
1584
- /**
1585
- * @param {CodeMirror} cm CodeMirror object.
1586
- * @param {int} repeat Number of words to move past.
1587
- * @param {boolean} forward True to search forward. False to search
1588
- * backward.
1589
- * @param {boolean} wordEnd True to move to end of word. False to move to
1590
- * beginning of word.
1591
- * @param {boolean} bigWord True if punctuation count as part of the word.
1592
- * False if only alphabet characters count as part of the word.
1593
- * @return {Cursor} The position the cursor should move to.
1594
- */
1595
- function moveToWord(cm, repeat, forward, wordEnd, bigWord) {
1596
- var cur = cm.getCursor();
1597
- for (var i = 0; i < repeat; i++) {
1598
- var startCh = cur.ch, startLine = cur.line, word;
1599
- var movedToNextWord = false;
1600
- while (!movedToNextWord) {
1601
- // Search and advance.
1602
- word = findWord(cm, cur, forward, bigWord);
1603
- movedToNextWord = true;
1604
- if (word) {
1605
- // Move to the word we just found. If by moving to the word we end
1606
- // up in the same spot, then move an extra character and search
1607
- // again.
1608
- cur.line = word.line;
1609
- if (forward && wordEnd) {
1610
- // 'e'
1611
- cur.ch = word.to - 1;
1612
- } else if (forward && !wordEnd) {
1613
- // 'w'
1614
- if (inRangeInclusive(cur.ch, word.from, word.to) &&
1615
- word.line == startLine) {
1616
- // Still on the same word. Go to the next one.
1617
- movedToNextWord = false;
1618
- cur.ch = word.to - 1;
1619
- } else {
1620
- cur.ch = word.from;
1621
- }
1622
- } else if (!forward && wordEnd) {
1623
- // 'ge'
1624
- if (inRangeInclusive(cur.ch, word.from, word.to) &&
1625
- word.line == startLine) {
1626
- // still on the same word. Go to the next one.
1627
- movedToNextWord = false;
1628
- cur.ch = word.from;
1629
- } else {
1630
- cur.ch = word.to;
1631
- }
1632
- } else if (!forward && !wordEnd) {
1633
- // 'b'
1634
- cur.ch = word.from;
1635
- }
1636
- } else {
1637
- // No more words to be found. Move to the end.
1638
- if (forward) {
1639
- return { line: cur.line, ch: lineLength(cm, cur.line) };
1640
- } else {
1641
- return { line: cur.line, ch: 0 };
1642
- }
1643
- }
1644
- }
1645
- }
1646
- return cur;
1647
- }
1648
-
1649
- function moveToCharacter(cm, repeat, forward, character) {
1650
- var cur = cm.getCursor();
1651
- var start = cur.ch;
1652
- var idx;
1653
- for (var i = 0; i < repeat; i ++) {
1654
- var line = cm.getLine(cur.line);
1655
- idx = charIdxInLine(start, line, character, forward, true);
1656
- if (idx == -1) {
1657
- return cur;
1658
- }
1659
- start = idx;
1660
- }
1661
- return { line: cm.getCursor().line, ch: idx };
1662
- }
1663
-
1664
- function moveToColumn(cm, repeat) {
1665
- // repeat is always >= 1, so repeat - 1 always corresponds
1666
- // to the column we want to go to.
1667
- var line = cm.getCursor().line;
1668
- return clipCursorToContent(cm, { line: line, ch: repeat - 1 });
1669
- }
1670
-
1671
- function updateMark(cm, vim, markName, pos) {
1672
- if (!inArray(markName, validMarks)) {
1673
- return;
1674
- }
1675
- if (vim.marks[markName]) {
1676
- vim.marks[markName].clear();
1677
- }
1678
- vim.marks[markName] = cm.setBookmark(pos);
1679
- }
1680
-
1681
- function charIdxInLine(start, line, character, forward, includeChar) {
1682
- // Search for char in line.
1683
- // motion_options: {forward, includeChar}
1684
- // If includeChar = true, include it too.
1685
- // If forward = true, search forward, else search backwards.
1686
- // If char is not found on this line, do nothing
1687
- var idx;
1688
- if (forward) {
1689
- idx = line.indexOf(character, start + 1);
1690
- if (idx != -1 && !includeChar) {
1691
- idx -= 1;
1692
- }
1693
- } else {
1694
- idx = line.lastIndexOf(character, start - 1);
1695
- if (idx != -1 && !includeChar) {
1696
- idx += 1;
1697
- }
1698
- }
1699
- return idx;
1700
- }
1701
-
1702
- function findMatchedSymbol(cm, cur, symb) {
1703
- var line = cur.line;
1704
- symb = symb ? symb : cm.getLine(line).charAt(cur.ch);
1705
-
1706
- // Are we at the opening or closing char
1707
- var forwards = inArray(symb, ['(', '[', '{']);
1708
-
1709
- var reverseSymb = ({
1710
- '(': ')', ')': '(',
1711
- '[': ']', ']': '[',
1712
- '{': '}', '}': '{'})[symb];
1713
-
1714
- // Couldn't find a matching symbol, abort
1715
- if (!reverseSymb) {
1716
- return cur;
1717
- }
1718
-
1719
- // set our increment to move forward (+1) or backwards (-1)
1720
- // depending on which bracket we're matching
1721
- var increment = ({'(': 1, '{': 1, '[': 1})[symb] || -1;
1722
- var depth = 1, nextCh = symb, index = cur.ch, lineText = cm.getLine(line);
1723
- // Simple search for closing paren--just count openings and closings till
1724
- // we find our match
1725
- // TODO: use info from CodeMirror to ignore closing brackets in comments
1726
- // and quotes, etc.
1727
- while (nextCh && depth > 0) {
1728
- index += increment;
1729
- nextCh = lineText.charAt(index);
1730
- if (!nextCh) {
1731
- line += increment;
1732
- index = 0;
1733
- lineText = cm.getLine(line) || '';
1734
- nextCh = lineText.charAt(index);
1735
- }
1736
- if (nextCh === symb) {
1737
- depth++;
1738
- } else if (nextCh === reverseSymb) {
1739
- depth--;
1740
- }
1741
- }
1742
-
1743
- if (nextCh) {
1744
- return { line: line, ch: index };
1745
- }
1746
- return cur;
1747
- }
1748
-
1749
- function selectCompanionObject(cm, revSymb, inclusive) {
1750
- var cur = cm.getCursor();
1751
-
1752
- var end = findMatchedSymbol(cm, cur, revSymb);
1753
- var start = findMatchedSymbol(cm, end);
1754
- start.ch += inclusive ? 1 : 0;
1755
- end.ch += inclusive ? 0 : 1;
1756
-
1757
- return { start: start, end: end };
1758
- }
1759
-
1760
- function regexLastIndexOf(string, pattern, startIndex) {
1761
- for (var i = !startIndex ? string.length : startIndex;
1762
- i >= 0; --i) {
1763
- if (pattern.test(string.charAt(i))) {
1764
- return i;
1765
- }
1766
- }
1767
- return -1;
1768
- }
1769
-
1770
- // Takes in a symbol and a cursor and tries to simulate text objects that
1771
- // have identical opening and closing symbols
1772
- // TODO support across multiple lines
1773
- function findBeginningAndEnd(cm, symb, inclusive) {
1774
- var cur = cm.getCursor();
1775
- var line = cm.getLine(cur.line);
1776
- var chars = line.split('');
1777
- var start, end, i, len;
1778
- var firstIndex = chars.indexOf(symb);
1779
-
1780
- // the decision tree is to always look backwards for the beginning first,
1781
- // but if the cursor is in front of the first instance of the symb,
1782
- // then move the cursor forward
1783
- if (cur.ch < firstIndex) {
1784
- cur.ch = firstIndex;
1785
- // Why is this line even here???
1786
- // cm.setCursor(cur.line, firstIndex+1);
1787
- }
1788
- // otherwise if the cursor is currently on the closing symbol
1789
- else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
1790
- end = cur.ch; // assign end to the current cursor
1791
- --cur.ch; // make sure to look backwards
1792
- }
1793
-
1794
- // if we're currently on the symbol, we've got a start
1795
- if (chars[cur.ch] == symb && !end) {
1796
- start = cur.ch + 1; // assign start to ahead of the cursor
1797
- } else {
1798
- // go backwards to find the start
1799
- for (i = cur.ch; i > -1 && !start; i--) {
1800
- if (chars[i] == symb) {
1801
- start = i + 1;
1802
- }
1803
- }
1804
- }
1805
-
1806
- // look forwards for the end symbol
1807
- if (start && !end) {
1808
- for (i = start, len = chars.length; i < len && !end; i++) {
1809
- if (chars[i] == symb) {
1810
- end = i;
1811
- }
1812
- }
1813
- }
1814
-
1815
- // nothing found
1816
- if (!start || !end) {
1817
- return { start: cur, end: cur };
1818
- }
1819
-
1820
- // include the symbols
1821
- if (inclusive) {
1822
- --start; ++end;
1823
- }
1824
-
1825
- return {
1826
- start: { line: cur.line, ch: start },
1827
- end: { line: cur.line, ch: end }
1828
- };
1829
- }
1830
-
1831
- // Search functions
1832
- function SearchState() {
1833
- // Highlighted text that match the query.
1834
- this.marked = null;
1835
- }
1836
- SearchState.prototype = {
1837
- getQuery: function() {
1838
- return getVimGlobalState().query;
1839
- },
1840
- setQuery: function(query) {
1841
- getVimGlobalState().query = query;
1842
- },
1843
- getMarked: function() {
1844
- return this.marked;
1845
- },
1846
- setMarked: function(marked) {
1847
- this.marked = marked;
1848
- },
1849
- getOverlay: function() {
1850
- return this.searchOverlay;
1851
- },
1852
- setOverlay: function(overlay) {
1853
- this.searchOverlay = overlay;
1854
- },
1855
- isReversed: function() {
1856
- return getVimGlobalState().isReversed;
1857
- },
1858
- setReversed: function(reversed) {
1859
- getVimGlobalState().isReversed = reversed;
1860
- }
1861
- };
1862
- function getSearchState(cm) {
1863
- var vim = getVimState(cm);
1864
- return vim.searchState_ || (vim.searchState_ = new SearchState());
1865
- }
1866
- function dialog(cm, text, shortText, callback, initialValue) {
1867
- if (cm.openDialog) {
1868
- cm.openDialog(text, callback, { bottom: true, value: initialValue });
1869
- }
1870
- else {
1871
- callback(prompt(shortText, ""));
1872
- }
1873
- }
1874
- function findUnescapedSlashes(str) {
1875
- var escapeNextChar = false;
1876
- var slashes = [];
1877
- for (var i = 0; i < str.length; i++) {
1878
- var c = str.charAt(i);
1879
- if (!escapeNextChar && c == '/') {
1880
- slashes.push(i);
1881
- }
1882
- escapeNextChar = (c == '\\');
1883
- }
1884
- return slashes;
1885
- }
1886
- /**
1887
- * Extract the regular expression from the query and return a Regexp object.
1888
- * Returns null if the query is blank.
1889
- * If ignoreCase is passed in, the Regexp object will have the 'i' flag set.
1890
- * If smartCase is passed in, and the query contains upper case letters,
1891
- * then ignoreCase is overridden, and the 'i' flag will not be set.
1892
- * If the query contains the /i in the flag part of the regular expression,
1893
- * then both ignoreCase and smartCase are ignored, and 'i' will be passed
1894
- * through to the Regex object.
1895
- */
1896
- function parseQuery(cm, query, ignoreCase, smartCase) {
1897
- // First try to extract regex + flags from the input. If no flags found,
1898
- // extract just the regex. IE does not accept flags directly defined in
1899
- // the regex string in the form /regex/flags
1900
- var slashes = findUnescapedSlashes(query);
1901
- var regexPart;
1902
- var forceIgnoreCase;
1903
- if (!slashes.length) {
1904
- // Query looks like 'regexp'
1905
- regexPart = query;
1906
- } else {
1907
- // Query looks like 'regexp/...'
1908
- regexPart = query.substring(0, slashes[0]);
1909
- var flagsPart = query.substring(slashes[0]);
1910
- forceIgnoreCase = (flagsPart.indexOf('i') != -1);
1911
- }
1912
- if (!regexPart) {
1913
- return null;
1914
- }
1915
- if (smartCase) {
1916
- ignoreCase = (/^[^A-Z]*$/).test(regexPart);
1917
- }
1918
- try {
1919
- var regexp = new RegExp(regexPart,
1920
- (ignoreCase || forceIgnoreCase) ? 'i' : undefined);
1921
- return regexp;
1922
- } catch (e) {
1923
- showConfirm(cm, 'Invalid regex: ' + regexPart);
1924
- }
1925
- }
1926
- function showConfirm(cm, text) {
1927
- if (cm.openConfirm) {
1928
- cm.openConfirm('<span style="color: red">' + text +
1929
- '</span> <button type="button">OK</button>', function() {},
1930
- {bottom: true});
1931
- } else {
1932
- alert(text);
1933
- }
1934
- }
1935
- function makePrompt(prefix, desc) {
1936
- var raw = '';
1937
- if (prefix) {
1938
- raw += '<span style="font-family: monospace">' + prefix + '</span>';
1939
- }
1940
- raw += '<input type="text"/> ' +
1941
- '<span style="color: #888">';
1942
- if (desc) {
1943
- raw += '<span style="color: #888">';
1944
- raw += desc;
1945
- raw += '</span>';
1946
- }
1947
- return raw;
1948
- }
1949
- var searchPromptDesc = '(Javascript regexp)';
1950
- function showPrompt(cm, onPromptClose, prefix, desc, initialValue) {
1951
- var shortText = (prefix || '') + ' ' + (desc || '');
1952
- dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose,
1953
- initialValue);
1954
- }
1955
- function regexEqual(r1, r2) {
1956
- if (r1 instanceof RegExp && r2 instanceof RegExp) {
1957
- var props = ["global", "multiline", "ignoreCase", "source"];
1958
- for (var i = 0; i < props.length; i++) {
1959
- var prop = props[i];
1960
- if (r1[prop] !== r2[prop]) {
1961
- return(false);
1962
- }
1963
- }
1964
- return(true);
1965
- }
1966
- return(false);
1967
- }
1968
- function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
1969
- cm.operation(function() {
1970
- var state = getSearchState(cm);
1971
- if (!rawQuery) {
1972
- return;
1973
- }
1974
- var query = parseQuery(cm, rawQuery, !!ignoreCase, !!smartCase);
1975
- if (!query) {
1976
- return;
1977
- }
1978
- if (regexEqual(query, state.getQuery())) {
1979
- return;
1980
- }
1981
- clearSearchHighlight(cm);
1982
- highlightSearchMatches(cm, query);
1983
- state.setQuery(query);
1984
- });
1985
- }
1986
- function searchOverlay(query) {
1987
- return {
1988
- token: function(stream) {
1989
- var match = stream.match(query, false);
1990
- if (match) {
1991
- if (!stream.sol()) {
1992
- // Backtrack 1 to match \b
1993
- stream.backUp(1);
1994
- if (!query.exec(stream.next() + match[0])) {
1995
- stream.next();
1996
- return null;
1997
- }
1998
- }
1999
- stream.match(query);
2000
- return "searching";
2001
- }
2002
- while (!stream.eol()) {
2003
- stream.next();
2004
- if (stream.match(query, false)) break;
2005
- }
2006
- },
2007
- query: query
2008
- };
2009
- }
2010
- function highlightSearchMatches(cm, query) {
2011
- if (cm.addOverlay) {
2012
- var overlay = getSearchState(cm).getOverlay();
2013
- if (!overlay || query != overlay.query) {
2014
- if (overlay) {
2015
- cm.removeOverlay(overlay);
2016
- }
2017
- overlay = searchOverlay(query);
2018
- cm.addOverlay(overlay);
2019
- getSearchState(cm).setOverlay(overlay);
2020
- }
2021
- } else {
2022
- // TODO: Highlight only text inside the viewport. Highlighting everything
2023
- // is inefficient and expensive.
2024
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
2025
- var marked = [];
2026
- for (var cursor = cm.getSearchCursor(query);
2027
- cursor.findNext();) {
2028
- marked.push(cm.markText(cursor.from(), cursor.to(),
2029
- { className: 'cm-searching' }));
2030
- }
2031
- getSearchState(cm).setMarked(marked);
2032
- }
2033
- }
2034
- }
2035
- function findNext(cm, prev, repeat) {
2036
- return cm.operation(function() {
2037
- var state = getSearchState(cm);
2038
- var query = state.getQuery();
2039
- if (!query) {
2040
- return;
2041
- }
2042
- if (!state.getMarked()) {
2043
- highlightSearchMatches(cm, query);
2044
- }
2045
- var pos = cm.getCursor();
2046
- // If search is initiated with ? instead of /, negate direction.
2047
- prev = (state.isReversed()) ? !prev : prev;
2048
- if (!prev) {
2049
- pos.ch += 1;
2050
- }
2051
- var cursor = cm.getSearchCursor(query, pos);
2052
- for (var i = 0; i < repeat; i++) {
2053
- if (!cursor.find(prev)) {
2054
- // SearchCursor may have returned null because it hit EOF, wrap
2055
- // around and try again.
2056
- cursor = cm.getSearchCursor(query,
2057
- (prev) ? { line: cm.lineCount() - 1} : {line: 0, ch: 0} );
2058
- if (!cursor.find(prev)) {
2059
- return;
2060
- }
2061
- }
2062
- }
2063
- return cursor.from();
2064
- });}
2065
- function clearSearchHighlight(cm) {
2066
- if (cm.addOverlay) {
2067
- cm.removeOverlay(getSearchState(cm).getOverlay());
2068
- getSearchState(cm).setOverlay(null);
2069
- } else {
2070
- cm.operation(function() {
2071
- var state = getSearchState(cm);
2072
- if (!state.getQuery()) {
2073
- return;
2074
- }
2075
- var marked = state.getMarked();
2076
- if (!marked) {
2077
- return;
2078
- }
2079
- for (var i = 0; i < marked.length; ++i) {
2080
- marked[i].clear();
2081
- }
2082
- state.setMarked(null);
2083
- });
2084
- }
2085
- }
2086
- /**
2087
- * Check if pos is in the specified range, INCLUSIVE.
2088
- * Range can be specified with 1 or 2 arguments.
2089
- * If the first range argument is an array, treat it as an array of line
2090
- * numbers. Match pos against any of the lines.
2091
- * If the first range argument is a number,
2092
- * if there is only 1 range argument, check if pos has the same line
2093
- * number
2094
- * if there are 2 range arguments, then check if pos is in between the two
2095
- * range arguments.
2096
- */
2097
- function isInRange(pos, start, end) {
2098
- if (typeof pos != 'number') {
2099
- // Assume it is a cursor position. Get the line number.
2100
- pos = pos.line;
2101
- }
2102
- if (start instanceof Array) {
2103
- return inArray(pos, start);
2104
- } else {
2105
- if (end) {
2106
- return (pos >= start && pos <= end);
2107
- } else {
2108
- return pos == start;
2109
- }
2110
- }
2111
- }
2112
-
2113
- // Ex command handling
2114
- // Care must be taken when adding to the default Ex command map. For any
2115
- // pair of commands that have a shared prefix, at least one of their
2116
- // shortNames must not match the prefix of the other command.
2117
- var defaultExCommandMap = [
2118
- { name: 'map', type: 'builtIn' },
2119
- { name: 'write', shortName: 'w', type: 'builtIn' },
2120
- { name: 'undo', shortName: 'u', type: 'builtIn' },
2121
- { name: 'redo', shortName: 'red', type: 'builtIn' },
2122
- { name: 'substitute', shortName: 's', type: 'builtIn'}
2123
- ];
2124
- Vim.ExCommandDispatcher = function() {
2125
- this.buildCommandMap_();
2126
- };
2127
- Vim.ExCommandDispatcher.prototype = {
2128
- processCommand: function(cm, input) {
2129
- var inputStream = new CodeMirror.StringStream(input);
2130
- var params = {};
2131
- params.input = input;
2132
- try {
2133
- this.parseInput_(cm, inputStream, params);
2134
- } catch(e) {
2135
- showConfirm(cm, e);
2136
- return;
2137
- }
2138
- var commandName;
2139
- if (!params.commandName) {
2140
- // If only a line range is defined, move to the line.
2141
- if (params.line !== undefined) {
2142
- commandName = 'move';
2143
- }
2144
- } else {
2145
- var command = this.matchCommand_(params.commandName);
2146
- if (command) {
2147
- commandName = command.name;
2148
- this.parseCommandArgs_(inputStream, params, command);
2149
- if (command.type == 'exToKey') {
2150
- // Handle Ex to Key mapping.
2151
- for (var i = 0; i < command.toKeys.length; i++) {
2152
- vim.handleKey(cm, command.toKeys[i]);
2153
- }
2154
- return;
2155
- } else if (command.type == 'exToEx') {
2156
- // Handle Ex to Ex mapping.
2157
- this.processCommand(cm, command.toInput);
2158
- return;
2159
- }
2160
- }
2161
- }
2162
- if (!commandName) {
2163
- showConfirm(cm, 'Not an editor command ":' + input + '"');
2164
- return;
2165
- }
2166
- exCommands[commandName](cm, params);
2167
- },
2168
- parseInput_: function(cm, inputStream, result) {
2169
- inputStream.eatWhile(':');
2170
- // Parse range.
2171
- if (inputStream.eat('%')) {
2172
- result.line = 0;
2173
- result.lineEnd = cm.lineCount() - 1;
2174
- } else {
2175
- result.line = this.parseLineSpec_(cm, inputStream);
2176
- if (result.line !== undefined && inputStream.eat(',')) {
2177
- result.lineEnd = this.parseLineSpec_(cm, inputStream);
2178
- }
2179
- }
2180
-
2181
- // Parse command name.
2182
- var commandMatch = inputStream.match(/^(\w+)/);
2183
- if (commandMatch) {
2184
- result.commandName = commandMatch[1];
2185
- } else {
2186
- result.commandName = inputStream.match(/.*/)[0];
2187
- }
2188
-
2189
- return result;
2190
- },
2191
- parseLineSpec_: function(cm, inputStream) {
2192
- var numberMatch = inputStream.match(/^(\d+)/);
2193
- if (numberMatch) {
2194
- return parseInt(numberMatch[1], 10) - 1;
2195
- }
2196
- switch (inputStream.next()) {
2197
- case '.':
2198
- return cm.getCursor().line;
2199
- case '$':
2200
- return cm.lineCount() - 1;
2201
- case '\'':
2202
- var mark = getVimState(cm).marks[inputStream.next()];
2203
- if (mark && mark.find()) {
2204
- return mark.find().line;
2205
- } else {
2206
- throw "Mark not set";
2207
- }
2208
- break;
2209
- default:
2210
- inputStream.backUp(1);
2211
- return cm.getCursor().line;
2212
- }
2213
- },
2214
- parseCommandArgs_: function(inputStream, params, command) {
2215
- if (inputStream.eol()) {
2216
- return;
2217
- }
2218
- params.argString = inputStream.match(/.*/)[0];
2219
- // Parse command-line arguments
2220
- var delim = command.argDelimiter || /\s+/;
2221
- var args = trim(params.argString).split(delim);
2222
- if (args.length && args[0]) {
2223
- params.args = args;
2224
- }
2225
- },
2226
- matchCommand_: function(commandName) {
2227
- // Return the command in the command map that matches the shortest
2228
- // prefix of the passed in command name. The match is guaranteed to be
2229
- // unambiguous if the defaultExCommandMap's shortNames are set up
2230
- // correctly. (see @code{defaultExCommandMap}).
2231
- for (var i = commandName.length; i > 0; i--) {
2232
- var prefix = commandName.substring(0, i);
2233
- if (this.commandMap_[prefix]) {
2234
- var command = this.commandMap_[prefix];
2235
- if (command.name.indexOf(commandName) === 0) {
2236
- return command;
2237
- }
2238
- }
2239
- }
2240
- return null;
2241
- },
2242
- buildCommandMap_: function() {
2243
- this.commandMap_ = {};
2244
- for (var i = 0; i < defaultExCommandMap.length; i++) {
2245
- var command = defaultExCommandMap[i];
2246
- var key = command.shortName || command.name;
2247
- this.commandMap_[key] = command;
2248
- }
2249
- },
2250
- map: function(lhs, rhs) {
2251
- if (lhs != ':' && lhs.charAt(0) == ':') {
2252
- var commandName = lhs.substring(1);
2253
- if (rhs != ':' && rhs.charAt(0) == ':') {
2254
- // Ex to Ex mapping
2255
- this.commandMap_[commandName] = {
2256
- name: commandName,
2257
- type: 'exToEx',
2258
- toInput: rhs.substring(1)
2259
- };
2260
- } else {
2261
- // Ex to key mapping
2262
- this.commandMap_[commandName] = {
2263
- name: commandName,
2264
- type: 'exToKey',
2265
- toKeys: parseKeyString(rhs)
2266
- };
2267
- }
2268
- } else {
2269
- if (rhs != ':' && rhs.charAt(0) == ':') {
2270
- // Key to Ex mapping.
2271
- defaultKeymap.unshift({
2272
- keys: parseKeyString(lhs),
2273
- type: 'keyToEx',
2274
- exArgs: { input: rhs.substring(1) }});
2275
- } else {
2276
- // Key to key mapping
2277
- defaultKeymap.unshift({
2278
- keys: parseKeyString(lhs),
2279
- type: 'keyToKey',
2280
- toKeys: parseKeyString(rhs)
2281
- });
2282
- }
2283
- }
2284
- }
2285
- };
2286
-
2287
- // Converts a key string sequence of the form a<C-w>bd<Left> into Vim's
2288
- // keymap representation.
2289
- function parseKeyString(str) {
2290
- var idx = 0;
2291
- var keys = [];
2292
- while (idx < str.length) {
2293
- if (str.charAt(idx) != '<') {
2294
- keys.push(str.charAt(idx));
2295
- idx++;
2296
- continue;
2297
- }
2298
- // Vim key notation here means desktop Vim key-notation.
2299
- // See :help key-notation in desktop Vim.
2300
- var vimKeyNotationStart = ++idx;
2301
- while (str.charAt(idx++) != '>') {}
2302
- var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1);
2303
- var match = (/^C-(.+)$/).exec(vimKeyNotation);
2304
- if (match) {
2305
- var key;
2306
- switch (match[1]) {
2307
- case 'BS':
2308
- key = 'Backspace';
2309
- break;
2310
- case 'CR':
2311
- key = 'Enter';
2312
- break;
2313
- case 'Del':
2314
- key = 'Delete';
2315
- break;
2316
- default:
2317
- key = match[1];
2318
- break;
2319
- }
2320
- keys.push('Ctrl-' + key);
2321
- }
2322
- }
2323
- return keys;
2324
- }
2325
-
2326
- var exCommands = {
2327
- map: function(cm, params) {
2328
- var mapArgs = params.args;
2329
- if (!mapArgs || mapArgs.length < 2) {
2330
- if (cm) {
2331
- showConfirm(cm, 'Invalid mapping: ' + params.input);
2332
- }
2333
- return;
2334
- }
2335
- exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm);
2336
- },
2337
- move: function(cm, params) {
2338
- commandDispatcher.processMotion(cm, getVimState(cm), {
2339
- motion: 'moveToLineOrEdgeOfDocument',
2340
- motionArgs: { forward: false, explicitRepeat: true,
2341
- linewise: true, repeat: params.line }});
2342
- },
2343
- substitute: function(cm, params) {
2344
- var argString = params.argString;
2345
- var slashes = findUnescapedSlashes(argString);
2346
- if (slashes[0] !== 0) {
2347
- showConfirm(cm, 'Substitutions should be of the form ' +
2348
- ':s/pattern/replace/');
2349
- return;
2350
- }
2351
- var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
2352
- var replacePart = '';
2353
- var flagsPart;
2354
- var count;
2355
- if (slashes[1]) {
2356
- replacePart = argString.substring(slashes[1] + 1, slashes[2]);
2357
- }
2358
- if (slashes[2]) {
2359
- // After the 3rd slash, we can have flags followed by a space followed
2360
- // by count.
2361
- var trailing = argString.substring(slashes[2] + 1).split(' ');
2362
- flagsPart = trailing[0];
2363
- count = parseInt(trailing[1]);
2364
- }
2365
- if (flagsPart) {
2366
- regexPart = regexPart + '/' + flagsPart;
2367
- }
2368
- if (regexPart) {
2369
- // If regex part is empty, then use the previous query. Otherwise use
2370
- // the regex part as the new query.
2371
- updateSearchQuery(cm, regexPart, true /** ignoreCase */,
2372
- true /** smartCase */);
2373
- }
2374
- var state = getSearchState(cm);
2375
- var query = state.getQuery();
2376
- var lineStart = params.line || 0;
2377
- var lineEnd = params.lineEnd || lineStart;
2378
- if (count) {
2379
- lineStart = lineEnd;
2380
- lineEnd = lineStart + count - 1;
2381
- }
2382
- var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
2383
- function doReplace() {
2384
- for (var cursor = cm.getSearchCursor(query, startPos);
2385
- cursor.findNext() &&
2386
- isInRange(cursor.from(), lineStart, lineEnd);) {
2387
- var text = cm.getRange(cursor.from(), cursor.to());
2388
- var newText = text.replace(query, replacePart);
2389
- cursor.replace(newText);
2390
- }
2391
- var vim = getVimState(cm);
2392
- if (vim.visualMode) {
2393
- exitVisualMode(cm, vim);
2394
- }
2395
- }
2396
- if (cm.compoundChange) {
2397
- // Only exists in v2
2398
- cm.compoundChange(doReplace);
2399
- } else {
2400
- cm.operation(doReplace);
2401
- }
2402
- },
2403
- redo: CodeMirror.commands.redo,
2404
- undo: CodeMirror.commands.undo,
2405
- write: function(cm) {
2406
- if (CodeMirror.commands.save) {
2407
- // If a save command is defined, call it.
2408
- CodeMirror.commands.save(cm);
2409
- } else {
2410
- // Saves to text area if no save command is defined.
2411
- cm.save();
2412
- }
2413
- }
2414
- };
2415
-
2416
- var exCommandDispatcher = new Vim.ExCommandDispatcher();
2417
-
2418
- // Register Vim with CodeMirror
2419
- function buildVimKeyMap() {
2420
- /**
2421
- * Handle the raw key event from CodeMirror. Translate the
2422
- * Shift + key modifier to the resulting letter, while preserving other
2423
- * modifers.
2424
- */
2425
- // TODO: Figure out a way to catch capslock.
2426
- function handleKeyEvent_(cm, key, modifier) {
2427
- if (isUpperCase(key)) {
2428
- // Convert to lower case if shift is not the modifier since the key
2429
- // we get from CodeMirror is always upper case.
2430
- if (modifier == 'Shift') {
2431
- modifier = null;
2432
- }
2433
- else {
2434
- key = key.toLowerCase();
2435
- }
2436
- }
2437
- if (modifier) {
2438
- // Vim will parse modifier+key combination as a single key.
2439
- key = modifier + '-' + key;
2440
- }
2441
- vim.handleKey(cm, key);
2442
- }
2443
-
2444
- // Closure to bind CodeMirror, key, modifier.
2445
- function keyMapper(key, modifier) {
2446
- return function(cm) {
2447
- handleKeyEvent_(cm, key, modifier);
2448
- };
2449
- }
2450
-
2451
- var modifiers = ['Shift', 'Ctrl'];
2452
- var keyMap = {
2453
- 'nofallthrough': true,
2454
- 'style': 'fat-cursor'
2455
- };
2456
- function bindKeys(keys, modifier) {
2457
- for (var i = 0; i < keys.length; i++) {
2458
- var key = keys[i];
2459
- if (!modifier && inArray(key, specialSymbols)) {
2460
- // Wrap special symbols with '' because that's how CodeMirror binds
2461
- // them.
2462
- key = "'" + key + "'";
2463
- }
2464
- if (modifier) {
2465
- keyMap[modifier + '-' + key] = keyMapper(keys[i], modifier);
2466
- } else {
2467
- keyMap[key] = keyMapper(keys[i]);
2468
- }
2469
- }
2470
- }
2471
- bindKeys(upperCaseAlphabet);
2472
- bindKeys(upperCaseAlphabet, 'Shift');
2473
- bindKeys(upperCaseAlphabet, 'Ctrl');
2474
- bindKeys(specialSymbols);
2475
- bindKeys(specialSymbols, 'Ctrl');
2476
- bindKeys(numbers);
2477
- bindKeys(numbers, 'Ctrl');
2478
- bindKeys(specialKeys);
2479
- bindKeys(specialKeys, 'Ctrl');
2480
- return keyMap;
2481
- }
2482
- CodeMirror.keyMap.vim = buildVimKeyMap();
2483
-
2484
- function exitInsertMode(cm) {
2485
- cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
2486
- cm.setOption('keyMap', 'vim');
2487
- }
2488
-
2489
- CodeMirror.keyMap['vim-insert'] = {
2490
- // TODO: override navigation keys so that Esc will cancel automatic
2491
- // indentation from o, O, i_<CR>
2492
- 'Esc': exitInsertMode,
2493
- 'Ctrl-[': exitInsertMode,
2494
- 'Ctrl-C': exitInsertMode,
2495
- 'Ctrl-N': 'autocomplete',
2496
- 'Ctrl-P': 'autocomplete',
2497
- 'Enter': function(cm) {
2498
- var fn = CodeMirror.commands.newlineAndIndentContinueComment ||
2499
- CodeMirror.commands.newlineAndIndent;
2500
- fn(cm);
2501
- },
2502
- fallthrough: ['default']
2503
- };
2504
-
2505
- return vimApi;
2506
- };
2507
- // Initialize Vim and make it available as an API.
2508
- var vim = Vim();
2509
- CodeMirror.Vim = vim;
2510
- }
2511
- )();