maglev-database-explorer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (688) hide show
  1. data/MIT-LICENSE +21 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/maglev-database-explorer/application.js +17 -0
  5. data/app/assets/javascripts/maglev_database_explorer/def.js +2 -0
  6. data/app/assets/stylesheets/maglev-database-explorer/application.css +13 -0
  7. data/app/assets/stylesheets/maglev_database_explorer/def.css +4 -0
  8. data/app/controllers/maglev_database_explorer/application_controller.rb +4 -0
  9. data/app/controllers/maglev_database_explorer/code_controller.rb +101 -0
  10. data/app/controllers/maglev_database_explorer/object_controller.rb +60 -0
  11. data/app/controllers/maglev_database_explorer/object_explorer_controller.rb +9 -0
  12. data/app/helpers/maglev_database_explorer/application_helper.rb +4 -0
  13. data/app/views/layouts/maglev-database-explorer/application.html.erb +14 -0
  14. data/app/views/maglev_database_explorer/object_explorer/_workspace.html.erb +28 -0
  15. data/app/views/maglev_database_explorer/object_explorer/index.html.erb +92 -0
  16. data/config/routes.rb +15 -0
  17. data/lib/maglev-database-explorer.rb +17 -0
  18. data/lib/maglev-database-explorer/code_evaluation.rb +36 -0
  19. data/lib/maglev-database-explorer/database_views.rb +29 -0
  20. data/lib/maglev-database-explorer/database_views/abstract_dictionary.rb +69 -0
  21. data/lib/maglev-database-explorer/database_views/abstract_exception.rb +25 -0
  22. data/lib/maglev-database-explorer/database_views/array.rb +28 -0
  23. data/lib/maglev-database-explorer/database_views/boolean.rb +12 -0
  24. data/lib/maglev-database-explorer/database_views/class.rb +46 -0
  25. data/lib/maglev-database-explorer/database_views/exception.rb +3 -0
  26. data/lib/maglev-database-explorer/database_views/execblock.rb +5 -0
  27. data/lib/maglev-database-explorer/database_views/fixnum.rb +5 -0
  28. data/lib/maglev-database-explorer/database_views/float.rb +6 -0
  29. data/lib/maglev-database-explorer/database_views/globals.rb +2 -0
  30. data/lib/maglev-database-explorer/database_views/gsnmethod.rb +29 -0
  31. data/lib/maglev-database-explorer/database_views/hash.rb +5 -0
  32. data/lib/maglev-database-explorer/database_views/maglev_record.rb +43 -0
  33. data/lib/maglev-database-explorer/database_views/module.rb +104 -0
  34. data/lib/maglev-database-explorer/database_views/nilclass.rb +6 -0
  35. data/lib/maglev-database-explorer/database_views/object.rb +77 -0
  36. data/lib/maglev-database-explorer/database_views/proc.rb +4 -0
  37. data/lib/maglev-database-explorer/database_views/repository.rb +4 -0
  38. data/lib/maglev-database-explorer/database_views/ruby_workspace.rb +6 -0
  39. data/lib/maglev-database-explorer/database_views/smalltalk_classes.rb +35 -0
  40. data/lib/maglev-database-explorer/database_views/smalltalk_classes.st +60 -0
  41. data/lib/maglev-database-explorer/database_views/string.rb +27 -0
  42. data/lib/maglev-database-explorer/database_views/symbol.rb +33 -0
  43. data/lib/maglev-database-explorer/database_views/system.rb +48 -0
  44. data/lib/maglev-database-explorer/database_views/thread.rb +101 -0
  45. data/lib/maglev-database-explorer/debug_server.rb +22 -0
  46. data/lib/maglev-database-explorer/engine.rb +23 -0
  47. data/lib/maglev-database-explorer/engine_symlinks.rb +19 -0
  48. data/lib/maglev-database-explorer/halt.rb +33 -0
  49. data/lib/maglev-database-explorer/ruby_workspace.rb +31 -0
  50. data/lib/maglev-database-explorer/version.rb +3 -0
  51. data/lib/tasks/maglev-database-explorer_tasks.rake +4 -0
  52. data/public/LICENSE +35 -0
  53. data/public/amber/CHANGELOG +65 -0
  54. data/public/amber/LICENSE +22 -0
  55. data/public/amber/Makefile +53 -0
  56. data/public/amber/README.md +30 -0
  57. data/public/amber/bin/amber +3 -0
  58. data/public/amber/bin/amberc +352 -0
  59. data/public/amber/bin/nodecompile.js +33 -0
  60. data/public/amber/bin/server +3 -0
  61. data/public/amber/bin/server.bat +3 -0
  62. data/public/amber/css/amber-normalize.css +468 -0
  63. data/public/amber/css/amber-normalize.less +501 -0
  64. data/public/amber/css/amber.css +539 -0
  65. data/public/amber/css/documentation.css +84 -0
  66. data/public/amber/css/profstef.css +75 -0
  67. data/public/amber/css/style.css +313 -0
  68. data/public/amber/documentation.html +37 -0
  69. data/public/amber/examples/Makefile +23 -0
  70. data/public/amber/examples/README +4 -0
  71. data/public/amber/examples/android/helloamber/AndroidManifest.xml +19 -0
  72. data/public/amber/examples/android/helloamber/HelloAmber.st +13 -0
  73. data/public/amber/examples/android/helloamber/Makefile +56 -0
  74. data/public/amber/examples/android/helloamber/README.md +80 -0
  75. data/public/amber/examples/android/helloamber/ant.properties +17 -0
  76. data/public/amber/examples/android/helloamber/assets/index.html +15 -0
  77. data/public/amber/examples/android/helloamber/assets/jquery-1.7.2.min.js +4 -0
  78. data/public/amber/examples/android/helloamber/build.xml +83 -0
  79. data/public/amber/examples/android/helloamber/local.properties +10 -0
  80. data/public/amber/examples/android/helloamber/proguard-project.txt +20 -0
  81. data/public/amber/examples/android/helloamber/project.properties +14 -0
  82. data/public/amber/examples/android/helloamber/res/layout/main.xml +6 -0
  83. data/public/amber/examples/android/helloamber/res/values/strings.xml +4 -0
  84. data/public/amber/examples/android/helloamber/src/org/amberlang/android/helloamber/HelloAmber.java +59 -0
  85. data/public/amber/examples/myproject/index.html +16 -0
  86. data/public/amber/examples/nodejs/README +9 -0
  87. data/public/amber/examples/nodejs/benchfib/Benchfib.st +124 -0
  88. data/public/amber/examples/nodejs/benchfib/Makefile +8 -0
  89. data/public/amber/examples/nodejs/benchfib/benchfib +1 -0
  90. data/public/amber/examples/nodejs/hello/Hello.st +9 -0
  91. data/public/amber/examples/nodejs/hello/Makefile +8 -0
  92. data/public/amber/examples/nodejs/hello/README +13 -0
  93. data/public/amber/examples/nodejs/hello/hello +1 -0
  94. data/public/amber/examples/nodejs/meta/Makefile +8 -0
  95. data/public/amber/examples/nodejs/meta/MyScript.st +27 -0
  96. data/public/amber/examples/nodejs/meta/meta +1 -0
  97. data/public/amber/examples/nodejs/pystone/Makefile +8 -0
  98. data/public/amber/examples/nodejs/pystone/Pystone.st +306 -0
  99. data/public/amber/examples/nodejs/pystone/pystone +1 -0
  100. data/public/amber/examples/nodejs/trivialserver/Makefile +8 -0
  101. data/public/amber/examples/nodejs/trivialserver/TrivialServer.st +51 -0
  102. data/public/amber/examples/nodejs/trivialserver/trivial +1 -0
  103. data/public/amber/examples/presentation/esug2011/images/asterix.png +0 -0
  104. data/public/amber/examples/presentation/esug2011/images/background_box.png +0 -0
  105. data/public/amber/examples/presentation/esug2011/images/background_header.png +0 -0
  106. data/public/amber/examples/presentation/esug2011/images/balloon.jpg +0 -0
  107. data/public/amber/examples/presentation/esug2011/images/balloon_header.png +0 -0
  108. data/public/amber/examples/presentation/esug2011/images/devices.jpg +0 -0
  109. data/public/amber/examples/presentation/esug2011/images/enyo.png +0 -0
  110. data/public/amber/examples/presentation/esug2011/images/ide_star_wars.png +0 -0
  111. data/public/amber/examples/presentation/esug2011/images/nodejs.png +0 -0
  112. data/public/amber/examples/presentation/esug2011/images/terminal.png +0 -0
  113. data/public/amber/examples/presentation/esug2011/images/webos.png +0 -0
  114. data/public/amber/examples/presentation/fosdem2012/images/amber.png +0 -0
  115. data/public/amber/examples/presentation/fosdem2012/images/ambrhino.jpg +0 -0
  116. data/public/amber/examples/presentation/fosdem2012/images/nodejs.png +0 -0
  117. data/public/amber/examples/presentation/fosdem2012/images/pharo.png +0 -0
  118. data/public/amber/examples/presentation/fosdem2012/images/php.gif +0 -0
  119. data/public/amber/examples/presentation/fosdem2012/images/rails.png +0 -0
  120. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/arrow-next.png +0 -0
  121. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/arrow-prev.png +0 -0
  122. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/closedhand.cur +0 -0
  123. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/openhand.cur +0 -0
  124. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/shadow-top-back.png +0 -0
  125. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/shadow-top-forward.png +0 -0
  126. data/public/amber/examples/presentation/fosdem2012/lib/booklet/images/shadow.png +0 -0
  127. data/public/amber/examples/presentation/fosdem2012/lib/booklet/jquery.booklet.1.2.0.css +100 -0
  128. data/public/amber/examples/presentation/fosdem2012/lib/booklet/jquery.booklet.1.2.0.js +1232 -0
  129. data/public/amber/examples/presentation/fosdem2012/lib/booklet/jquery.booklet.1.2.0.min.js +13 -0
  130. data/public/amber/examples/presentation/fosdem2012/lib/booklet/jquery.easing.1.3.js +38 -0
  131. data/public/amber/examples/presentation/index.html +17 -0
  132. data/public/amber/examples/presentation/js/Presentation.deploy.js +1922 -0
  133. data/public/amber/examples/presentation/js/Presentation.js +2712 -0
  134. data/public/amber/examples/presentation/st/Presentation.st +1996 -0
  135. data/public/amber/examples/trysmalltalk/index.html +18 -0
  136. data/public/amber/examples/trysmalltalk/js/TrySmalltalk.deploy.js +837 -0
  137. data/public/amber/examples/trysmalltalk/js/TrySmalltalk.js +1206 -0
  138. data/public/amber/examples/trysmalltalk/st/TrySmalltalk.st +964 -0
  139. data/public/amber/examples/twitterwall/css/twitterwall.css +19 -0
  140. data/public/amber/examples/twitterwall/index.html +35 -0
  141. data/public/amber/examples/twitterwall/js/TwitterWall.deploy.js +116 -0
  142. data/public/amber/examples/twitterwall/js/TwitterWall.js +146 -0
  143. data/public/amber/examples/twitterwall/st/TwitterWall.st +91 -0
  144. data/public/amber/examples/webos/README +11 -0
  145. data/public/amber/examples/webos/eris/DuckQwaq.wav +0 -0
  146. data/public/amber/examples/webos/eris/EnyoAmber.st +36 -0
  147. data/public/amber/examples/webos/eris/Eris.css +0 -0
  148. data/public/amber/examples/webos/eris/Eris.st +86 -0
  149. data/public/amber/examples/webos/eris/Makefile +37 -0
  150. data/public/amber/examples/webos/eris/README +3 -0
  151. data/public/amber/examples/webos/eris/appinfo.json +10 -0
  152. data/public/amber/examples/webos/eris/depends.js +4 -0
  153. data/public/amber/examples/webos/eris/index.html +12 -0
  154. data/public/amber/examples/webos/helloamber/EnyoAmber.st +36 -0
  155. data/public/amber/examples/webos/helloamber/HelloAmber.st +86 -0
  156. data/public/amber/examples/webos/helloamber/Makefile +38 -0
  157. data/public/amber/examples/webos/helloamber/README +5 -0
  158. data/public/amber/examples/webos/helloamber/appinfo.json +10 -0
  159. data/public/amber/examples/webos/helloamber/depends.js +3 -0
  160. data/public/amber/examples/webos/helloamber/index.html +12 -0
  161. data/public/amber/favicon.ico +0 -0
  162. data/public/amber/images/amber.png +0 -0
  163. data/public/amber/images/amber.svg +706 -0
  164. data/public/amber/images/amber_small.png +0 -0
  165. data/public/amber/images/off.amber.png +0 -0
  166. data/public/amber/images/off.png +0 -0
  167. data/public/amber/images/offHover.amber.png +0 -0
  168. data/public/amber/images/offHover.png +0 -0
  169. data/public/amber/images/presentation.png +0 -0
  170. data/public/amber/images/profstef.png +0 -0
  171. data/public/amber/images/sprite.amber.png +0 -0
  172. data/public/amber/images/sprite.png +0 -0
  173. data/public/amber/images/tinylogo.amber.png +0 -0
  174. data/public/amber/images/tinylogo.png +0 -0
  175. data/public/amber/images/twitterwall.png +0 -0
  176. data/public/amber/index.html +55 -0
  177. data/public/amber/js/Additional-Examples.deploy.js +15 -0
  178. data/public/amber/js/Additional-Examples.js +21 -0
  179. data/public/amber/js/Benchfib.deploy.js +132 -0
  180. data/public/amber/js/Benchfib.js +167 -0
  181. data/public/amber/js/Canvas.deploy.js +2446 -0
  182. data/public/amber/js/Canvas.js +3547 -0
  183. data/public/amber/js/Compiler-Tests.deploy.js +97 -0
  184. data/public/amber/js/Compiler-Tests.js +137 -0
  185. data/public/amber/js/Compiler.deploy.js +1877 -0
  186. data/public/amber/js/Compiler.js +2622 -0
  187. data/public/amber/js/Documentation.deploy.js +961 -0
  188. data/public/amber/js/Documentation.js +1376 -0
  189. data/public/amber/js/Examples.deploy.js +53 -0
  190. data/public/amber/js/Examples.js +73 -0
  191. data/public/amber/js/IDE.deploy.js +3557 -0
  192. data/public/amber/js/IDE.js +5002 -0
  193. data/public/amber/js/Kernel-Announcements.deploy.js +107 -0
  194. data/public/amber/js/Kernel-Announcements.js +152 -0
  195. data/public/amber/js/Kernel-Classes.deploy.js +774 -0
  196. data/public/amber/js/Kernel-Classes.js +1095 -0
  197. data/public/amber/js/Kernel-Collections.deploy.js +3121 -0
  198. data/public/amber/js/Kernel-Collections.js +4427 -0
  199. data/public/amber/js/Kernel-Exceptions.deploy.js +244 -0
  200. data/public/amber/js/Kernel-Exceptions.js +349 -0
  201. data/public/amber/js/Kernel-Methods.deploy.js +573 -0
  202. data/public/amber/js/Kernel-Methods.js +807 -0
  203. data/public/amber/js/Kernel-Objects.deploy.js +2877 -0
  204. data/public/amber/js/Kernel-Objects.js +4107 -0
  205. data/public/amber/js/Kernel-Tests.deploy.js +1513 -0
  206. data/public/amber/js/Kernel-Tests.js +2053 -0
  207. data/public/amber/js/Kernel-Transcript.deploy.js +142 -0
  208. data/public/amber/js/Kernel-Transcript.js +202 -0
  209. data/public/amber/js/Maglev-Core.deploy.js +2967 -0
  210. data/public/amber/js/Maglev-Core.js +4202 -0
  211. data/public/amber/js/Maglev-Database-Explorer.deploy.js +4426 -0
  212. data/public/amber/js/Maglev-Database-Explorer.js +6162 -0
  213. data/public/amber/js/Maglev-Vendor.deploy.js +350 -0
  214. data/public/amber/js/Maglev-Vendor.js +465 -0
  215. data/public/amber/js/README.md +5 -0
  216. data/public/amber/js/SUnit.deploy.js +351 -0
  217. data/public/amber/js/SUnit.js +501 -0
  218. data/public/amber/js/amber.js +273 -0
  219. data/public/amber/js/boot.js +602 -0
  220. data/public/amber/js/compat.js +22 -0
  221. data/public/amber/js/init.js +9 -0
  222. data/public/amber/js/lib/CodeMirror/LICENSE +19 -0
  223. data/public/amber/js/lib/CodeMirror/active-line.js +39 -0
  224. data/public/amber/js/lib/CodeMirror/amber.css +21 -0
  225. data/public/amber/js/lib/CodeMirror/codemirror.css +67 -0
  226. data/public/amber/js/lib/CodeMirror/codemirror.js +2144 -0
  227. data/public/amber/js/lib/CodeMirror/smalltalk.js +134 -0
  228. data/public/amber/js/lib/jQuery/jquery-1.4.4.min.js +167 -0
  229. data/public/amber/js/lib/jQuery/jquery-1.6.4.min.js +4 -0
  230. data/public/amber/js/lib/jQuery/jquery-ui-1.8.16.custom.min.js +791 -0
  231. data/public/amber/js/lib/jQuery/jquery.textarea.js +267 -0
  232. data/public/amber/js/lib/peg-0.6.2.min.js +2 -0
  233. data/public/amber/js/lib/showdown.js +419 -0
  234. data/public/amber/js/parser.js +4222 -0
  235. data/public/amber/js/parser.pegjs +223 -0
  236. data/public/amber/learn.html +40 -0
  237. data/public/amber/repl/Makefile +8 -0
  238. data/public/amber/repl/REPL.js +124 -0
  239. data/public/amber/repl/REPL.st +56 -0
  240. data/public/amber/repl/amber.js +18085 -0
  241. data/public/amber/server/FileServer.st +576 -0
  242. data/public/amber/server/Makefile +8 -0
  243. data/public/amber/server/server.js +13049 -0
  244. data/public/amber/st/Benchfib.st +124 -0
  245. data/public/amber/st/Canvas.st +968 -0
  246. data/public/amber/st/Compiler-Tests.st +471 -0
  247. data/public/amber/st/Compiler.st +1445 -0
  248. data/public/amber/st/Documentation.st +758 -0
  249. data/public/amber/st/Examples.st +38 -0
  250. data/public/amber/st/IDE.st +2404 -0
  251. data/public/amber/st/Kernel-Announcements.st +61 -0
  252. data/public/amber/st/Kernel-Classes.st +462 -0
  253. data/public/amber/st/Kernel-Collections.st +1611 -0
  254. data/public/amber/st/Kernel-Exceptions.st +124 -0
  255. data/public/amber/st/Kernel-Methods.st +291 -0
  256. data/public/amber/st/Kernel-Objects.st +1587 -0
  257. data/public/amber/st/Kernel-Tests.st +953 -0
  258. data/public/amber/st/Kernel-Transcript.st +70 -0
  259. data/public/amber/st/Maglev-Core.st +1659 -0
  260. data/public/amber/st/Maglev-Database-Explorer.st +3085 -0
  261. data/public/amber/st/Maglev-Vendor.st +213 -0
  262. data/public/amber/st/Makefile +104 -0
  263. data/public/amber/st/README.md +4 -0
  264. data/public/amber/st/SUnit.st +172 -0
  265. data/public/css/bootstrap-combined.no-icons.min.css +731 -0
  266. data/public/css/bootstrap.css +6811 -0
  267. data/public/css/jquery-ui.min.css +5 -0
  268. data/public/css/smalltalk_code.css +42 -0
  269. data/public/css/style.css +209 -0
  270. data/public/images/maglev-logo.gif +0 -0
  271. data/public/images/rails_tiny.png +0 -0
  272. data/public/images/ruby.png +0 -0
  273. data/public/images/ruby_smalltalk_bridge.png +0 -0
  274. data/public/images/smalltalk.png +0 -0
  275. data/public/libs/bootstrap.min.js +7 -0
  276. data/public/libs/codemirror/CONTRIBUTING.md +72 -0
  277. data/public/libs/codemirror/LICENSE +23 -0
  278. data/public/libs/codemirror/README.md +11 -0
  279. data/public/libs/codemirror/addon/dialog/dialog.css +32 -0
  280. data/public/libs/codemirror/addon/dialog/dialog.js +80 -0
  281. data/public/libs/codemirror/addon/display/placeholder.js +54 -0
  282. data/public/libs/codemirror/addon/edit/closebrackets.js +54 -0
  283. data/public/libs/codemirror/addon/edit/closetag.js +86 -0
  284. data/public/libs/codemirror/addon/edit/continuecomment.js +44 -0
  285. data/public/libs/codemirror/addon/edit/continuelist.js +25 -0
  286. data/public/libs/codemirror/addon/edit/matchbrackets.js +82 -0
  287. data/public/libs/codemirror/addon/fold/brace-fold.js +31 -0
  288. data/public/libs/codemirror/addon/fold/foldcode.js +32 -0
  289. data/public/libs/codemirror/addon/fold/indent-fold.js +11 -0
  290. data/public/libs/codemirror/addon/fold/xml-fold.js +64 -0
  291. data/public/libs/codemirror/addon/hint/html-hint.js +582 -0
  292. data/public/libs/codemirror/addon/hint/javascript-hint.js +142 -0
  293. data/public/libs/codemirror/addon/hint/pig-hint.js +117 -0
  294. data/public/libs/codemirror/addon/hint/python-hint.js +93 -0
  295. data/public/libs/codemirror/addon/hint/show-hint.css +38 -0
  296. data/public/libs/codemirror/addon/hint/show-hint.js +180 -0
  297. data/public/libs/codemirror/addon/hint/xml-hint.js +118 -0
  298. data/public/libs/codemirror/addon/lint/javascript-lint.js +127 -0
  299. data/public/libs/codemirror/addon/lint/json-lint.js +14 -0
  300. data/public/libs/codemirror/addon/lint/lint.css +96 -0
  301. data/public/libs/codemirror/addon/lint/lint.js +197 -0
  302. data/public/libs/codemirror/addon/mode/loadmode.js +51 -0
  303. data/public/libs/codemirror/addon/mode/multiplex.js +95 -0
  304. data/public/libs/codemirror/addon/mode/overlay.js +59 -0
  305. data/public/libs/codemirror/addon/runmode/colorize.js +29 -0
  306. data/public/libs/codemirror/addon/runmode/runmode-standalone.js +130 -0
  307. data/public/libs/codemirror/addon/runmode/runmode.js +52 -0
  308. data/public/libs/codemirror/addon/runmode/runmode.node.js +89 -0
  309. data/public/libs/codemirror/addon/search/match-highlighter.js +60 -0
  310. data/public/libs/codemirror/addon/search/search.js +131 -0
  311. data/public/libs/codemirror/addon/search/searchcursor.js +133 -0
  312. data/public/libs/codemirror/addon/selection/active-line.js +39 -0
  313. data/public/libs/codemirror/addon/selection/mark-selection.js +34 -0
  314. data/public/libs/codemirror/bin/compress +91 -0
  315. data/public/libs/codemirror/bin/lint +11 -0
  316. data/public/libs/codemirror/demo/activeline.html +70 -0
  317. data/public/libs/codemirror/demo/bidi.html +61 -0
  318. data/public/libs/codemirror/demo/btree.html +87 -0
  319. data/public/libs/codemirror/demo/buffers.html +98 -0
  320. data/public/libs/codemirror/demo/changemode.html +50 -0
  321. data/public/libs/codemirror/demo/closebrackets.html +63 -0
  322. data/public/libs/codemirror/demo/closetag.html +37 -0
  323. data/public/libs/codemirror/demo/complete.html +70 -0
  324. data/public/libs/codemirror/demo/emacs.html +60 -0
  325. data/public/libs/codemirror/demo/folding.html +69 -0
  326. data/public/libs/codemirror/demo/fullscreen.html +147 -0
  327. data/public/libs/codemirror/demo/html5complete.html +92 -0
  328. data/public/libs/codemirror/demo/indentwrap.html +49 -0
  329. data/public/libs/codemirror/demo/lint.html +90 -0
  330. data/public/libs/codemirror/demo/loadmode.html +40 -0
  331. data/public/libs/codemirror/demo/marker.html +59 -0
  332. data/public/libs/codemirror/demo/markselection.html +36 -0
  333. data/public/libs/codemirror/demo/matchhighlighter.html +38 -0
  334. data/public/libs/codemirror/demo/multiplex.html +60 -0
  335. data/public/libs/codemirror/demo/mustache.html +59 -0
  336. data/public/libs/codemirror/demo/placeholder.html +36 -0
  337. data/public/libs/codemirror/demo/preview.html +76 -0
  338. data/public/libs/codemirror/demo/resize.html +49 -0
  339. data/public/libs/codemirror/demo/runmode.html +50 -0
  340. data/public/libs/codemirror/demo/search.html +85 -0
  341. data/public/libs/codemirror/demo/spanaffectswrapping_shim.html +73 -0
  342. data/public/libs/codemirror/demo/theme.html +89 -0
  343. data/public/libs/codemirror/demo/variableheight.html +52 -0
  344. data/public/libs/codemirror/demo/vim.html +65 -0
  345. data/public/libs/codemirror/demo/visibletabs.html +53 -0
  346. data/public/libs/codemirror/demo/widget.html +74 -0
  347. data/public/libs/codemirror/demo/xmlcomplete.html +81 -0
  348. data/public/libs/codemirror/doc/baboon.png +0 -0
  349. data/public/libs/codemirror/doc/baboon_vector.svg +153 -0
  350. data/public/libs/codemirror/doc/compress.html +200 -0
  351. data/public/libs/codemirror/doc/docs.css +167 -0
  352. data/public/libs/codemirror/doc/internals.html +505 -0
  353. data/public/libs/codemirror/doc/manual.html +1897 -0
  354. data/public/libs/codemirror/doc/modes.html +94 -0
  355. data/public/libs/codemirror/doc/oldrelease.html +492 -0
  356. data/public/libs/codemirror/doc/realworld.html +100 -0
  357. data/public/libs/codemirror/doc/reporting.html +60 -0
  358. data/public/libs/codemirror/doc/upgrade_v2.2.html +98 -0
  359. data/public/libs/codemirror/doc/upgrade_v3.html +227 -0
  360. data/public/libs/codemirror/index.html +487 -0
  361. data/public/libs/codemirror/keymap/emacs.js +30 -0
  362. data/public/libs/codemirror/keymap/vim.js +3044 -0
  363. data/public/libs/codemirror/lib/codemirror.css +253 -0
  364. data/public/libs/codemirror/lib/codemirror.js +5585 -0
  365. data/public/libs/codemirror/mode/apl/apl.js +160 -0
  366. data/public/libs/codemirror/mode/apl/index.html +61 -0
  367. data/public/libs/codemirror/mode/asterisk/asterisk.js +183 -0
  368. data/public/libs/codemirror/mode/asterisk/index.html +142 -0
  369. data/public/libs/codemirror/mode/clike/clike.js +302 -0
  370. data/public/libs/codemirror/mode/clike/index.html +103 -0
  371. data/public/libs/codemirror/mode/clike/scala.html +767 -0
  372. data/public/libs/codemirror/mode/clojure/clojure.js +222 -0
  373. data/public/libs/codemirror/mode/clojure/index.html +76 -0
  374. data/public/libs/codemirror/mode/coffeescript/LICENSE +22 -0
  375. data/public/libs/codemirror/mode/coffeescript/coffeescript.js +346 -0
  376. data/public/libs/codemirror/mode/coffeescript/index.html +728 -0
  377. data/public/libs/codemirror/mode/commonlisp/commonlisp.js +101 -0
  378. data/public/libs/codemirror/mode/commonlisp/index.html +165 -0
  379. data/public/libs/codemirror/mode/css/css.js +567 -0
  380. data/public/libs/codemirror/mode/css/index.html +58 -0
  381. data/public/libs/codemirror/mode/css/scss.html +145 -0
  382. data/public/libs/codemirror/mode/css/scss_test.js +80 -0
  383. data/public/libs/codemirror/mode/css/test.js +113 -0
  384. data/public/libs/codemirror/mode/d/d.js +205 -0
  385. data/public/libs/codemirror/mode/d/index.html +262 -0
  386. data/public/libs/codemirror/mode/diff/diff.js +32 -0
  387. data/public/libs/codemirror/mode/diff/index.html +105 -0
  388. data/public/libs/codemirror/mode/ecl/ecl.js +192 -0
  389. data/public/libs/codemirror/mode/ecl/index.html +39 -0
  390. data/public/libs/codemirror/mode/erlang/erlang.js +463 -0
  391. data/public/libs/codemirror/mode/erlang/index.html +64 -0
  392. data/public/libs/codemirror/mode/gas/gas.js +326 -0
  393. data/public/libs/codemirror/mode/gas/index.html +57 -0
  394. data/public/libs/codemirror/mode/gfm/gfm.js +96 -0
  395. data/public/libs/codemirror/mode/gfm/index.html +74 -0
  396. data/public/libs/codemirror/mode/gfm/test.js +112 -0
  397. data/public/libs/codemirror/mode/go/go.js +165 -0
  398. data/public/libs/codemirror/mode/go/index.html +74 -0
  399. data/public/libs/codemirror/mode/groovy/groovy.js +210 -0
  400. data/public/libs/codemirror/mode/groovy/index.html +73 -0
  401. data/public/libs/codemirror/mode/haskell/haskell.js +242 -0
  402. data/public/libs/codemirror/mode/haskell/index.html +62 -0
  403. data/public/libs/codemirror/mode/haxe/haxe.js +429 -0
  404. data/public/libs/codemirror/mode/haxe/index.html +90 -0
  405. data/public/libs/codemirror/mode/htmlembedded/htmlembedded.js +73 -0
  406. data/public/libs/codemirror/mode/htmlembedded/index.html +49 -0
  407. data/public/libs/codemirror/mode/htmlmixed/htmlmixed.js +104 -0
  408. data/public/libs/codemirror/mode/htmlmixed/index.html +73 -0
  409. data/public/libs/codemirror/mode/http/http.js +98 -0
  410. data/public/libs/codemirror/mode/http/index.html +32 -0
  411. data/public/libs/codemirror/mode/javascript/index.html +92 -0
  412. data/public/libs/codemirror/mode/javascript/javascript.js +467 -0
  413. data/public/libs/codemirror/mode/javascript/typescript.html +48 -0
  414. data/public/libs/codemirror/mode/jinja2/index.html +38 -0
  415. data/public/libs/codemirror/mode/jinja2/jinja2.js +42 -0
  416. data/public/libs/codemirror/mode/less/index.html +741 -0
  417. data/public/libs/codemirror/mode/less/less.js +266 -0
  418. data/public/libs/codemirror/mode/livescript/LICENSE +23 -0
  419. data/public/libs/codemirror/mode/livescript/index.html +446 -0
  420. data/public/libs/codemirror/mode/livescript/livescript.js +267 -0
  421. data/public/libs/codemirror/mode/livescript/livescript.ls +266 -0
  422. data/public/libs/codemirror/mode/lua/index.html +74 -0
  423. data/public/libs/codemirror/mode/lua/lua.js +140 -0
  424. data/public/libs/codemirror/mode/markdown/index.html +344 -0
  425. data/public/libs/codemirror/mode/markdown/markdown.js +526 -0
  426. data/public/libs/codemirror/mode/markdown/test.js +636 -0
  427. data/public/libs/codemirror/mode/meta.js +75 -0
  428. data/public/libs/codemirror/mode/mirc/index.html +149 -0
  429. data/public/libs/codemirror/mode/mirc/mirc.js +177 -0
  430. data/public/libs/codemirror/mode/ntriples/index.html +33 -0
  431. data/public/libs/codemirror/mode/ntriples/ntriples.js +170 -0
  432. data/public/libs/codemirror/mode/ocaml/index.html +131 -0
  433. data/public/libs/codemirror/mode/ocaml/ocaml.js +113 -0
  434. data/public/libs/codemirror/mode/pascal/LICENSE +7 -0
  435. data/public/libs/codemirror/mode/pascal/index.html +48 -0
  436. data/public/libs/codemirror/mode/pascal/pascal.js +94 -0
  437. data/public/libs/codemirror/mode/perl/LICENSE +19 -0
  438. data/public/libs/codemirror/mode/perl/index.html +62 -0
  439. data/public/libs/codemirror/mode/perl/perl.js +816 -0
  440. data/public/libs/codemirror/mode/php/index.html +51 -0
  441. data/public/libs/codemirror/mode/php/php.js +129 -0
  442. data/public/libs/codemirror/mode/pig/index.html +42 -0
  443. data/public/libs/codemirror/mode/pig/pig.js +171 -0
  444. data/public/libs/codemirror/mode/properties/index.html +41 -0
  445. data/public/libs/codemirror/mode/properties/properties.js +63 -0
  446. data/public/libs/codemirror/mode/python/LICENSE.txt +21 -0
  447. data/public/libs/codemirror/mode/python/index.html +135 -0
  448. data/public/libs/codemirror/mode/python/python.js +340 -0
  449. data/public/libs/codemirror/mode/q/index.html +131 -0
  450. data/public/libs/codemirror/mode/q/q.js +124 -0
  451. data/public/libs/codemirror/mode/r/LICENSE +24 -0
  452. data/public/libs/codemirror/mode/r/index.html +74 -0
  453. data/public/libs/codemirror/mode/r/r.js +141 -0
  454. data/public/libs/codemirror/mode/rpm/changes/changes.js +19 -0
  455. data/public/libs/codemirror/mode/rpm/changes/index.html +53 -0
  456. data/public/libs/codemirror/mode/rpm/spec/index.html +99 -0
  457. data/public/libs/codemirror/mode/rpm/spec/spec.css +5 -0
  458. data/public/libs/codemirror/mode/rpm/spec/spec.js +66 -0
  459. data/public/libs/codemirror/mode/rst/LICENSE.txt +21 -0
  460. data/public/libs/codemirror/mode/rst/index.html +524 -0
  461. data/public/libs/codemirror/mode/rst/rst.js +550 -0
  462. data/public/libs/codemirror/mode/ruby/LICENSE +24 -0
  463. data/public/libs/codemirror/mode/ruby/index.html +173 -0
  464. data/public/libs/codemirror/mode/ruby/ruby.js +197 -0
  465. data/public/libs/codemirror/mode/rust/index.html +48 -0
  466. data/public/libs/codemirror/mode/rust/rust.js +432 -0
  467. data/public/libs/codemirror/mode/sass/index.html +54 -0
  468. data/public/libs/codemirror/mode/sass/sass.js +349 -0
  469. data/public/libs/codemirror/mode/scheme/index.html +65 -0
  470. data/public/libs/codemirror/mode/scheme/scheme.js +230 -0
  471. data/public/libs/codemirror/mode/shell/index.html +51 -0
  472. data/public/libs/codemirror/mode/shell/shell.js +118 -0
  473. data/public/libs/codemirror/mode/sieve/LICENSE +19 -0
  474. data/public/libs/codemirror/mode/sieve/index.html +81 -0
  475. data/public/libs/codemirror/mode/sieve/sieve.js +183 -0
  476. data/public/libs/codemirror/mode/smalltalk/index.html +57 -0
  477. data/public/libs/codemirror/mode/smalltalk/smalltalk.js +141 -0
  478. data/public/libs/codemirror/mode/smarty/index.html +83 -0
  479. data/public/libs/codemirror/mode/smarty/smarty.js +148 -0
  480. data/public/libs/codemirror/mode/sparql/index.html +42 -0
  481. data/public/libs/codemirror/mode/sparql/sparql.js +143 -0
  482. data/public/libs/codemirror/mode/sql/index.html +68 -0
  483. data/public/libs/codemirror/mode/sql/sql.js +268 -0
  484. data/public/libs/codemirror/mode/stex/index.html +98 -0
  485. data/public/libs/codemirror/mode/stex/stex.js +246 -0
  486. data/public/libs/codemirror/mode/stex/test.js +117 -0
  487. data/public/libs/codemirror/mode/tcl/index.html +129 -0
  488. data/public/libs/codemirror/mode/tcl/tcl.js +131 -0
  489. data/public/libs/codemirror/mode/tiddlywiki/index.html +142 -0
  490. data/public/libs/codemirror/mode/tiddlywiki/tiddlywiki.css +14 -0
  491. data/public/libs/codemirror/mode/tiddlywiki/tiddlywiki.js +353 -0
  492. data/public/libs/codemirror/mode/tiki/index.html +81 -0
  493. data/public/libs/codemirror/mode/tiki/tiki.css +26 -0
  494. data/public/libs/codemirror/mode/tiki/tiki.js +308 -0
  495. data/public/libs/codemirror/mode/turtle/index.html +39 -0
  496. data/public/libs/codemirror/mode/turtle/turtle.js +145 -0
  497. data/public/libs/codemirror/mode/vb/LICENSE.txt +21 -0
  498. data/public/libs/codemirror/mode/vb/index.html +88 -0
  499. data/public/libs/codemirror/mode/vb/vb.js +259 -0
  500. data/public/libs/codemirror/mode/vbscript/index.html +42 -0
  501. data/public/libs/codemirror/mode/vbscript/vbscript.js +26 -0
  502. data/public/libs/codemirror/mode/velocity/index.html +103 -0
  503. data/public/libs/codemirror/mode/velocity/velocity.js +144 -0
  504. data/public/libs/codemirror/mode/verilog/index.html +121 -0
  505. data/public/libs/codemirror/mode/verilog/verilog.js +182 -0
  506. data/public/libs/codemirror/mode/xml/index.html +45 -0
  507. data/public/libs/codemirror/mode/xml/xml.js +328 -0
  508. data/public/libs/codemirror/mode/xquery/LICENSE +20 -0
  509. data/public/libs/codemirror/mode/xquery/index.html +221 -0
  510. data/public/libs/codemirror/mode/xquery/test.js +64 -0
  511. data/public/libs/codemirror/mode/xquery/xquery.js +450 -0
  512. data/public/libs/codemirror/mode/yaml/index.html +68 -0
  513. data/public/libs/codemirror/mode/yaml/yaml.js +95 -0
  514. data/public/libs/codemirror/mode/z80/index.html +39 -0
  515. data/public/libs/codemirror/mode/z80/z80.js +85 -0
  516. data/public/libs/codemirror/package.json +21 -0
  517. data/public/libs/codemirror/test/doc_test.js +329 -0
  518. data/public/libs/codemirror/test/driver.js +138 -0
  519. data/public/libs/codemirror/test/index.html +182 -0
  520. data/public/libs/codemirror/test/lint/acorn.js +1593 -0
  521. data/public/libs/codemirror/test/lint/lint.js +112 -0
  522. data/public/libs/codemirror/test/lint/parse-js.js +1372 -0
  523. data/public/libs/codemirror/test/lint/walk.js +216 -0
  524. data/public/libs/codemirror/test/mode_test.css +10 -0
  525. data/public/libs/codemirror/test/mode_test.js +192 -0
  526. data/public/libs/codemirror/test/phantom_driver.js +31 -0
  527. data/public/libs/codemirror/test/run.js +33 -0
  528. data/public/libs/codemirror/test/test.js +1400 -0
  529. data/public/libs/codemirror/test/vim_test.js +1688 -0
  530. data/public/libs/codemirror/theme/ambiance-mobile.css +5 -0
  531. data/public/libs/codemirror/theme/ambiance.css +75 -0
  532. data/public/libs/codemirror/theme/blackboard.css +25 -0
  533. data/public/libs/codemirror/theme/cobalt.css +18 -0
  534. data/public/libs/codemirror/theme/eclipse.css +25 -0
  535. data/public/libs/codemirror/theme/elegant.css +10 -0
  536. data/public/libs/codemirror/theme/erlang-dark.css +21 -0
  537. data/public/libs/codemirror/theme/lesser-dark.css +44 -0
  538. data/public/libs/codemirror/theme/midnight.css +52 -0
  539. data/public/libs/codemirror/theme/monokai.css +28 -0
  540. data/public/libs/codemirror/theme/neat.css +9 -0
  541. data/public/libs/codemirror/theme/night.css +21 -0
  542. data/public/libs/codemirror/theme/rubyblue.css +21 -0
  543. data/public/libs/codemirror/theme/solarized.css +207 -0
  544. data/public/libs/codemirror/theme/twilight.css +26 -0
  545. data/public/libs/codemirror/theme/vibrant-ink.css +27 -0
  546. data/public/libs/codemirror/theme/xq-dark.css +46 -0
  547. data/public/libs/codemirror/theme/xq-light.css +43 -0
  548. data/public/libs/font-awesome/css/font-awesome-ie7.css +983 -0
  549. data/public/libs/font-awesome/css/font-awesome-ie7.min.css +24 -0
  550. data/public/libs/font-awesome/css/font-awesome.css +1268 -0
  551. data/public/libs/font-awesome/css/font-awesome.min.css +24 -0
  552. data/public/libs/font-awesome/font/FontAwesome.otf +0 -0
  553. data/public/libs/font-awesome/font/fontawesome-webfont.eot +0 -0
  554. data/public/libs/font-awesome/font/fontawesome-webfont.svg +339 -0
  555. data/public/libs/font-awesome/font/fontawesome-webfont.ttf +0 -0
  556. data/public/libs/font-awesome/font/fontawesome-webfont.woff +0 -0
  557. data/public/libs/font-awesome/less/bootstrap.less +78 -0
  558. data/public/libs/font-awesome/less/core.less +132 -0
  559. data/public/libs/font-awesome/less/extras.less +79 -0
  560. data/public/libs/font-awesome/less/font-awesome-ie7.less +413 -0
  561. data/public/libs/font-awesome/less/font-awesome.less +32 -0
  562. data/public/libs/font-awesome/less/icons.less +330 -0
  563. data/public/libs/font-awesome/less/mixins.less +34 -0
  564. data/public/libs/font-awesome/less/path.less +15 -0
  565. data/public/libs/font-awesome/less/variables.less +9 -0
  566. data/public/libs/jquery-1.10.0.min.js +6 -0
  567. data/public/libs/jquery-ui.min.js +12 -0
  568. data/public/libs/jquery.jsPlumb-1.4.0-all.js +9571 -0
  569. data/public/libs/jquery.livequery.js +226 -0
  570. data/public/libs/jsPlumbInitializer.js +72 -0
  571. data/public/libs/jstree/README.txt +10 -0
  572. data/public/libs/jstree/_demo/_dump.sql +20 -0
  573. data/public/libs/jstree/_demo/_inc/__mysql_errors.log +0 -0
  574. data/public/libs/jstree/_demo/_inc/class._database.php +146 -0
  575. data/public/libs/jstree/_demo/_inc/class._database_i.php +152 -0
  576. data/public/libs/jstree/_demo/_inc/class.tree.php +602 -0
  577. data/public/libs/jstree/_demo/_install.txt +6 -0
  578. data/public/libs/jstree/_demo/config.php +14 -0
  579. data/public/libs/jstree/_demo/file.png +0 -0
  580. data/public/libs/jstree/_demo/folder.png +0 -0
  581. data/public/libs/jstree/_demo/index.html +461 -0
  582. data/public/libs/jstree/_demo/root.png +0 -0
  583. data/public/libs/jstree/_demo/server.php +69 -0
  584. data/public/libs/jstree/_docs/!style.css +48 -0
  585. data/public/libs/jstree/_docs/_drive.png +0 -0
  586. data/public/libs/jstree/_docs/_html_data.html +2 -0
  587. data/public/libs/jstree/_docs/_json_data.json +4 -0
  588. data/public/libs/jstree/_docs/_search_data.json +6 -0
  589. data/public/libs/jstree/_docs/_search_result.json +1 -0
  590. data/public/libs/jstree/_docs/_xml_flat.xml +12 -0
  591. data/public/libs/jstree/_docs/_xml_nest.xml +18 -0
  592. data/public/libs/jstree/_docs/checkbox.html +171 -0
  593. data/public/libs/jstree/_docs/contextmenu.html +121 -0
  594. data/public/libs/jstree/_docs/cookies.html +97 -0
  595. data/public/libs/jstree/_docs/core.html +689 -0
  596. data/public/libs/jstree/_docs/crrm.html +316 -0
  597. data/public/libs/jstree/_docs/dnd.html +199 -0
  598. data/public/libs/jstree/_docs/hotkeys.html +82 -0
  599. data/public/libs/jstree/_docs/html_data.html +175 -0
  600. data/public/libs/jstree/_docs/index.html +86 -0
  601. data/public/libs/jstree/_docs/json_data.html +249 -0
  602. data/public/libs/jstree/_docs/languages.html +152 -0
  603. data/public/libs/jstree/_docs/logo.png +0 -0
  604. data/public/libs/jstree/_docs/search.html +153 -0
  605. data/public/libs/jstree/_docs/sort.html +85 -0
  606. data/public/libs/jstree/_docs/syntax/!script.js +2232 -0
  607. data/public/libs/jstree/_docs/syntax/!style.css +511 -0
  608. data/public/libs/jstree/_docs/syntax/clipboard.swf +0 -0
  609. data/public/libs/jstree/_docs/syntax/help.png +0 -0
  610. data/public/libs/jstree/_docs/syntax/magnifier.png +0 -0
  611. data/public/libs/jstree/_docs/syntax/page_white_code.png +0 -0
  612. data/public/libs/jstree/_docs/syntax/page_white_copy.png +0 -0
  613. data/public/libs/jstree/_docs/syntax/printer.png +0 -0
  614. data/public/libs/jstree/_docs/syntax/wrapping.png +0 -0
  615. data/public/libs/jstree/_docs/themeroller.html +107 -0
  616. data/public/libs/jstree/_docs/themes.html +127 -0
  617. data/public/libs/jstree/_docs/types.html +178 -0
  618. data/public/libs/jstree/_docs/ui.html +197 -0
  619. data/public/libs/jstree/_docs/unique.html +85 -0
  620. data/public/libs/jstree/_docs/xml_data.html +218 -0
  621. data/public/libs/jstree/_lib/jquery.cookie.js +96 -0
  622. data/public/libs/jstree/_lib/jquery.hotkeys.js +99 -0
  623. data/public/libs/jstree/_lib/jquery.js +5 -0
  624. data/public/libs/jstree/jquery.jstree.js +4564 -0
  625. data/public/libs/jstree/themes/apple/bg.jpg +0 -0
  626. data/public/libs/jstree/themes/apple/d.png +0 -0
  627. data/public/libs/jstree/themes/apple/dot_for_ie.gif +0 -0
  628. data/public/libs/jstree/themes/apple/style.css +61 -0
  629. data/public/libs/jstree/themes/apple/throbber.gif +0 -0
  630. data/public/libs/jstree/themes/classic/d.gif +0 -0
  631. data/public/libs/jstree/themes/classic/d.png +0 -0
  632. data/public/libs/jstree/themes/classic/dot_for_ie.gif +0 -0
  633. data/public/libs/jstree/themes/classic/style.css +77 -0
  634. data/public/libs/jstree/themes/classic/throbber.gif +0 -0
  635. data/public/libs/jstree/themes/default-rtl/d.gif +0 -0
  636. data/public/libs/jstree/themes/default-rtl/d.png +0 -0
  637. data/public/libs/jstree/themes/default-rtl/dots.gif +0 -0
  638. data/public/libs/jstree/themes/default-rtl/style.css +84 -0
  639. data/public/libs/jstree/themes/default-rtl/throbber.gif +0 -0
  640. data/public/libs/jstree/themes/default/d.gif +0 -0
  641. data/public/libs/jstree/themes/default/d.png +0 -0
  642. data/public/libs/jstree/themes/default/style.css +74 -0
  643. data/public/libs/jstree/themes/default/throbber.gif +0 -0
  644. data/test/dummy/README.rdoc +261 -0
  645. data/test/dummy/Rakefile +7 -0
  646. data/test/dummy/app/assets/javascripts/application.js +15 -0
  647. data/test/dummy/app/assets/javascripts/demo_app.js +2 -0
  648. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  649. data/test/dummy/app/assets/stylesheets/demo_app.css +4 -0
  650. data/test/dummy/app/controllers/application_controller.rb +3 -0
  651. data/test/dummy/app/controllers/demo_app_controller.rb +6 -0
  652. data/test/dummy/app/helpers/application_helper.rb +2 -0
  653. data/test/dummy/app/helpers/demo_app_helper.rb +2 -0
  654. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  655. data/test/dummy/config.ru +4 -0
  656. data/test/dummy/config/application.rb +59 -0
  657. data/test/dummy/config/boot.rb +10 -0
  658. data/test/dummy/config/database.yml +25 -0
  659. data/test/dummy/config/environment.rb +5 -0
  660. data/test/dummy/config/environments/development.rb +37 -0
  661. data/test/dummy/config/environments/production.rb +67 -0
  662. data/test/dummy/config/environments/test.rb +37 -0
  663. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  664. data/test/dummy/config/initializers/inflections.rb +15 -0
  665. data/test/dummy/config/initializers/mime_types.rb +5 -0
  666. data/test/dummy/config/initializers/secret_token.rb +7 -0
  667. data/test/dummy/config/initializers/session_store.rb +8 -0
  668. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  669. data/test/dummy/config/locales/en.yml +5 -0
  670. data/test/dummy/config/routes.rb +6 -0
  671. data/test/dummy/db/test.sqlite3 +0 -0
  672. data/test/dummy/log/development.log +17606 -0
  673. data/test/dummy/public/404.html +26 -0
  674. data/test/dummy/public/422.html +26 -0
  675. data/test/dummy/public/500.html +25 -0
  676. data/test/dummy/public/favicon.ico +0 -0
  677. data/test/dummy/script/rails +6 -0
  678. data/test/dummy/test/functional/demo_app_controller_test.rb +7 -0
  679. data/test/dummy/test/unit/helpers/demo_app_helper_test.rb +4 -0
  680. data/test/dummy/tmp/cache/assets/D26/980/sprockets%2Fe04a37a0fa7f26c1183a1a4436e2a9c1 +0 -0
  681. data/test/dummy/tmp/cache/assets/D32/4F0/sprockets%2Fbd5441f3593e4ea157222202f3fddad6 +0 -0
  682. data/test/dummy/tmp/pids/server.pid +1 -0
  683. data/test/functional/maglev_database_explorer/def_controller_test.rb +9 -0
  684. data/test/integration/navigation_test.rb +10 -0
  685. data/test/maglev-database-explorer_test.rb +7 -0
  686. data/test/test_helper.rb +15 -0
  687. data/test/unit/helpers/maglev_database_explorer/def_helper_test.rb +6 -0
  688. metadata +813 -0
@@ -0,0 +1,30 @@
1
+ // TODO number prefixes
2
+ (function() {
3
+ // Really primitive kill-ring implementation.
4
+ var killRing = [];
5
+ function addToRing(str) {
6
+ killRing.push(str);
7
+ if (killRing.length > 50) killRing.shift();
8
+ }
9
+ function getFromRing() { return killRing[killRing.length - 1] || ""; }
10
+ function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
11
+
12
+ CodeMirror.keyMap.emacs = {
13
+ "Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
14
+ "Ctrl-W": function(cm) {addToRing(cm.getSelection()); cm.replaceSelection("");},
15
+ "Ctrl-Alt-W": function(cm) {addToRing(cm.getSelection()); cm.replaceSelection("");},
16
+ "Alt-W": function(cm) {addToRing(cm.getSelection());},
17
+ "Ctrl-Y": function(cm) {cm.replaceSelection(getFromRing());},
18
+ "Alt-Y": function(cm) {cm.replaceSelection(popFromRing());},
19
+ "Ctrl-/": "undo", "Shift-Ctrl--": "undo", "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
20
+ "Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace",
21
+ "Ctrl-Z": "undo", "Cmd-Z": "undo", "Alt-/": "autocomplete", "Alt-V": "goPageUp",
22
+ "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
23
+ fallthrough: ["basic", "emacsy"]
24
+ };
25
+
26
+ CodeMirror.keyMap["emacs-Ctrl-X"] = {
27
+ "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": "undo", "K": "close",
28
+ auto: "emacs", nofallthrough: true
29
+ };
30
+ })();
@@ -0,0 +1,3044 @@
1
+ /**
2
+ * Supported keybindings:
3
+ *
4
+ * Motion:
5
+ * h, j, k, l
6
+ * gj, gk
7
+ * e, E, w, W, b, B, ge, gE
8
+ * f<character>, F<character>, t<character>, T<character>
9
+ * $, ^, 0, -, +, _
10
+ * gg, G
11
+ * %
12
+ * '<character>, `<character>
13
+ *
14
+ * Operator:
15
+ * d, y, c
16
+ * dd, yy, cc
17
+ * g~, g~g~
18
+ * >, <, >>, <<
19
+ *
20
+ * Operator-Motion:
21
+ * x, X, D, Y, C, ~
22
+ *
23
+ * Action:
24
+ * a, i, s, A, I, S, o, O
25
+ * zz, z., z<CR>, zt, zb, z-
26
+ * J
27
+ * u, Ctrl-r
28
+ * m<character>
29
+ * r<character>
30
+ *
31
+ * Modes:
32
+ * ESC - leave insert mode, visual mode, and clear input state.
33
+ * Ctrl-[, Ctrl-c - same as ESC.
34
+ *
35
+ * Registers: unamed, -, a-z, A-Z, 0-9
36
+ * (Does not respect the special case for number registers when delete
37
+ * operator is made with these commands: %, (, ), , /, ?, n, N, {, } )
38
+ * TODO: Implement the remaining registers.
39
+ * Marks: a-z, A-Z, and 0-9
40
+ * TODO: Implement the remaining special marks. They have more complex
41
+ * behavior.
42
+ *
43
+ * Code structure:
44
+ * 1. Default keymap
45
+ * 2. Variable declarations and short basic helpers
46
+ * 3. Instance (External API) implementation
47
+ * 4. Internal state tracking objects (input state, counter) implementation
48
+ * and instanstiation
49
+ * 5. Key handler (the main command dispatcher) implementation
50
+ * 6. Motion, operator, and action implementations
51
+ * 7. Helper functions for the key handler, motions, operators, and actions
52
+ * 8. Set up Vim to work as a keymap for CodeMirror.
53
+ */
54
+
55
+ (function() {
56
+ 'use strict';
57
+
58
+ var defaultKeymap = [
59
+ // Key to key mapping. This goes first to make it possible to override
60
+ // existing mappings.
61
+ { keys: ['Left'], type: 'keyToKey', toKeys: ['h'] },
62
+ { keys: ['Right'], type: 'keyToKey', toKeys: ['l'] },
63
+ { keys: ['Up'], type: 'keyToKey', toKeys: ['k'] },
64
+ { keys: ['Down'], type: 'keyToKey', toKeys: ['j'] },
65
+ { keys: ['Space'], type: 'keyToKey', toKeys: ['l'] },
66
+ { keys: ['Backspace'], type: 'keyToKey', toKeys: ['h'] },
67
+ { keys: ['Ctrl-Space'], type: 'keyToKey', toKeys: ['W'] },
68
+ { keys: ['Ctrl-Backspace'], type: 'keyToKey', toKeys: ['B'] },
69
+ { keys: ['Shift-Space'], type: 'keyToKey', toKeys: ['w'] },
70
+ { keys: ['Shift-Backspace'], type: 'keyToKey', toKeys: ['b'] },
71
+ { keys: ['Ctrl-n'], type: 'keyToKey', toKeys: ['j'] },
72
+ { keys: ['Ctrl-p'], type: 'keyToKey', toKeys: ['k'] },
73
+ { keys: ['Ctrl-['], type: 'keyToKey', toKeys: ['Esc'] },
74
+ { keys: ['Ctrl-c'], type: 'keyToKey', toKeys: ['Esc'] },
75
+ { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'] },
76
+ { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'] },
77
+ { keys: ['Home'], type: 'keyToKey', toKeys: ['0'] },
78
+ { keys: ['End'], type: 'keyToKey', toKeys: ['$'] },
79
+ { keys: ['PageUp'], type: 'keyToKey', toKeys: ['Ctrl-b'] },
80
+ { keys: ['PageDown'], type: 'keyToKey', toKeys: ['Ctrl-f'] },
81
+ // Motions
82
+ { keys: ['H'], type: 'motion',
83
+ motion: 'moveToTopLine',
84
+ motionArgs: { linewise: true }},
85
+ { keys: ['M'], type: 'motion',
86
+ motion: 'moveToMiddleLine',
87
+ motionArgs: { linewise: true }},
88
+ { keys: ['L'], type: 'motion',
89
+ motion: 'moveToBottomLine',
90
+ motionArgs: { linewise: true }},
91
+ { keys: ['h'], type: 'motion',
92
+ motion: 'moveByCharacters',
93
+ motionArgs: { forward: false }},
94
+ { keys: ['l'], type: 'motion',
95
+ motion: 'moveByCharacters',
96
+ motionArgs: { forward: true }},
97
+ { keys: ['j'], type: 'motion',
98
+ motion: 'moveByLines',
99
+ motionArgs: { forward: true, linewise: true }},
100
+ { keys: ['k'], type: 'motion',
101
+ motion: 'moveByLines',
102
+ motionArgs: { forward: false, linewise: true }},
103
+ { keys: ['g','j'], type: 'motion',
104
+ motion: 'moveByDisplayLines',
105
+ motionArgs: { forward: true }},
106
+ { keys: ['g','k'], type: 'motion',
107
+ motion: 'moveByDisplayLines',
108
+ motionArgs: { forward: false }},
109
+ { keys: ['w'], type: 'motion',
110
+ motion: 'moveByWords',
111
+ motionArgs: { forward: true, wordEnd: false }},
112
+ { keys: ['W'], type: 'motion',
113
+ motion: 'moveByWords',
114
+ motionArgs: { forward: true, wordEnd: false, bigWord: true }},
115
+ { keys: ['e'], type: 'motion',
116
+ motion: 'moveByWords',
117
+ motionArgs: { forward: true, wordEnd: true, inclusive: true }},
118
+ { keys: ['E'], type: 'motion',
119
+ motion: 'moveByWords',
120
+ motionArgs: { forward: true, wordEnd: true, bigWord: true,
121
+ inclusive: true }},
122
+ { keys: ['b'], type: 'motion',
123
+ motion: 'moveByWords',
124
+ motionArgs: { forward: false, wordEnd: false }},
125
+ { keys: ['B'], type: 'motion',
126
+ motion: 'moveByWords',
127
+ motionArgs: { forward: false, wordEnd: false, bigWord: true }},
128
+ { keys: ['g', 'e'], type: 'motion',
129
+ motion: 'moveByWords',
130
+ motionArgs: { forward: false, wordEnd: true, inclusive: true }},
131
+ { keys: ['g', 'E'], type: 'motion',
132
+ motion: 'moveByWords',
133
+ motionArgs: { forward: false, wordEnd: true, bigWord: true,
134
+ inclusive: true }},
135
+ { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
136
+ motionArgs: { forward: false }},
137
+ { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
138
+ motionArgs: { forward: true }},
139
+ { keys: ['Ctrl-f'], type: 'motion',
140
+ motion: 'moveByPage', motionArgs: { forward: true }},
141
+ { keys: ['Ctrl-b'], type: 'motion',
142
+ motion: 'moveByPage', motionArgs: { forward: false }},
143
+ { keys: ['Ctrl-d'], type: 'motion',
144
+ motion: 'moveByScroll',
145
+ motionArgs: { forward: true, explicitRepeat: true }},
146
+ { keys: ['Ctrl-u'], type: 'motion',
147
+ motion: 'moveByScroll',
148
+ motionArgs: { forward: false, explicitRepeat: true }},
149
+ { keys: ['g', 'g'], type: 'motion',
150
+ motion: 'moveToLineOrEdgeOfDocument',
151
+ motionArgs: { forward: false, explicitRepeat: true, linewise: true }},
152
+ { keys: ['G'], type: 'motion',
153
+ motion: 'moveToLineOrEdgeOfDocument',
154
+ motionArgs: { forward: true, explicitRepeat: true, linewise: true }},
155
+ { keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' },
156
+ { keys: ['^'], type: 'motion',
157
+ motion: 'moveToFirstNonWhiteSpaceCharacter' },
158
+ { keys: ['+'], type: 'motion',
159
+ motion: 'moveByLines',
160
+ motionArgs: { forward: true, toFirstChar:true }},
161
+ { keys: ['-'], type: 'motion',
162
+ motion: 'moveByLines',
163
+ motionArgs: { forward: false, toFirstChar:true }},
164
+ { keys: ['_'], type: 'motion',
165
+ motion: 'moveByLines',
166
+ motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
167
+ { keys: ['$'], type: 'motion',
168
+ motion: 'moveToEol',
169
+ motionArgs: { inclusive: true }},
170
+ { keys: ['%'], type: 'motion',
171
+ motion: 'moveToMatchedSymbol',
172
+ motionArgs: { inclusive: true }},
173
+ { keys: ['f', 'character'], type: 'motion',
174
+ motion: 'moveToCharacter',
175
+ motionArgs: { forward: true , inclusive: true }},
176
+ { keys: ['F', 'character'], type: 'motion',
177
+ motion: 'moveToCharacter',
178
+ motionArgs: { forward: false }},
179
+ { keys: ['t', 'character'], type: 'motion',
180
+ motion: 'moveTillCharacter',
181
+ motionArgs: { forward: true, inclusive: true }},
182
+ { keys: ['T', 'character'], type: 'motion',
183
+ motion: 'moveTillCharacter',
184
+ motionArgs: { forward: false }},
185
+ { keys: [';'], type: 'motion', motion: 'repeatLastCharacterSearch',
186
+ motionArgs: { forward: true }},
187
+ { keys: [','], type: 'motion', motion: 'repeatLastCharacterSearch',
188
+ motionArgs: { forward: false }},
189
+ { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark' },
190
+ { keys: ['`', 'character'], type: 'motion', motion: 'goToMark' },
191
+ { keys: [']', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
192
+ { keys: ['[', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
193
+ { keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
194
+ { keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
195
+ { keys: [']', 'character'], type: 'motion',
196
+ motion: 'moveToSymbol',
197
+ motionArgs: { forward: true}},
198
+ { keys: ['[', 'character'], type: 'motion',
199
+ motion: 'moveToSymbol',
200
+ motionArgs: { forward: false}},
201
+ { keys: ['|'], type: 'motion',
202
+ motion: 'moveToColumn',
203
+ motionArgs: { }},
204
+ // Operators
205
+ { keys: ['d'], type: 'operator', operator: 'delete' },
206
+ { keys: ['y'], type: 'operator', operator: 'yank' },
207
+ { keys: ['c'], type: 'operator', operator: 'change',
208
+ operatorArgs: { enterInsertMode: true } },
209
+ { keys: ['>'], type: 'operator', operator: 'indent',
210
+ operatorArgs: { indentRight: true }},
211
+ { keys: ['<'], type: 'operator', operator: 'indent',
212
+ operatorArgs: { indentRight: false }},
213
+ { keys: ['g', '~'], type: 'operator', operator: 'swapcase' },
214
+ { keys: ['n'], type: 'motion', motion: 'findNext',
215
+ motionArgs: { forward: true }},
216
+ { keys: ['N'], type: 'motion', motion: 'findNext',
217
+ motionArgs: { forward: false }},
218
+ // Operator-Motion dual commands
219
+ { keys: ['x'], type: 'operatorMotion', operator: 'delete',
220
+ motion: 'moveByCharacters', motionArgs: { forward: true },
221
+ operatorMotionArgs: { visualLine: false }},
222
+ { keys: ['X'], type: 'operatorMotion', operator: 'delete',
223
+ motion: 'moveByCharacters', motionArgs: { forward: false },
224
+ operatorMotionArgs: { visualLine: true }},
225
+ { keys: ['D'], type: 'operatorMotion', operator: 'delete',
226
+ motion: 'moveToEol', motionArgs: { inclusive: true },
227
+ operatorMotionArgs: { visualLine: true }},
228
+ { keys: ['Y'], type: 'operatorMotion', operator: 'yank',
229
+ motion: 'moveToEol', motionArgs: { inclusive: true },
230
+ operatorMotionArgs: { visualLine: true }},
231
+ { keys: ['C'], type: 'operatorMotion',
232
+ operator: 'change', operatorArgs: { enterInsertMode: true },
233
+ motion: 'moveToEol', motionArgs: { inclusive: true },
234
+ operatorMotionArgs: { visualLine: true }},
235
+ { keys: ['~'], type: 'operatorMotion', operator: 'swapcase',
236
+ motion: 'moveByCharacters', motionArgs: { forward: true }},
237
+ // Actions
238
+ { keys: ['a'], type: 'action', action: 'enterInsertMode',
239
+ actionArgs: { insertAt: 'charAfter' }},
240
+ { keys: ['A'], type: 'action', action: 'enterInsertMode',
241
+ actionArgs: { insertAt: 'eol' }},
242
+ { keys: ['i'], type: 'action', action: 'enterInsertMode' },
243
+ { keys: ['I'], type: 'action', action: 'enterInsertMode',
244
+ motion: 'moveToFirstNonWhiteSpaceCharacter' },
245
+ { keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode',
246
+ actionArgs: { after: true }},
247
+ { keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode',
248
+ actionArgs: { after: false }},
249
+ { keys: ['v'], type: 'action', action: 'toggleVisualMode' },
250
+ { keys: ['V'], type: 'action', action: 'toggleVisualMode',
251
+ actionArgs: { linewise: true }},
252
+ { keys: ['J'], type: 'action', action: 'joinLines' },
253
+ { keys: ['p'], type: 'action', action: 'paste',
254
+ actionArgs: { after: true }},
255
+ { keys: ['P'], type: 'action', action: 'paste',
256
+ actionArgs: { after: false }},
257
+ { keys: ['r', 'character'], type: 'action', action: 'replace' },
258
+ { keys: ['u'], type: 'action', action: 'undo' },
259
+ { keys: ['Ctrl-r'], type: 'action', action: 'redo' },
260
+ { keys: ['m', 'character'], type: 'action', action: 'setMark' },
261
+ { keys: ['\"', 'character'], type: 'action', action: 'setRegister' },
262
+ { keys: ['z', 'z'], type: 'action', action: 'scrollToCursor',
263
+ actionArgs: { position: 'center' }},
264
+ { keys: ['z', '.'], type: 'action', action: 'scrollToCursor',
265
+ actionArgs: { position: 'center' },
266
+ motion: 'moveToFirstNonWhiteSpaceCharacter' },
267
+ { keys: ['z', 't'], type: 'action', action: 'scrollToCursor',
268
+ actionArgs: { position: 'top' }},
269
+ { keys: ['z', 'Enter'], type: 'action', action: 'scrollToCursor',
270
+ actionArgs: { position: 'top' },
271
+ motion: 'moveToFirstNonWhiteSpaceCharacter' },
272
+ { keys: ['z', '-'], type: 'action', action: 'scrollToCursor',
273
+ actionArgs: { position: 'bottom' }},
274
+ { keys: ['z', 'b'], type: 'action', action: 'scrollToCursor',
275
+ actionArgs: { position: 'bottom' },
276
+ motion: 'moveToFirstNonWhiteSpaceCharacter' },
277
+ { keys: ['.'], type: 'action', action: 'repeatLastEdit' },
278
+ { keys: ['Ctrl-a'], type: 'action', action: 'incrementNumberToken',
279
+ actionArgs: {increase: true, backtrack: false}},
280
+ { keys: ['Ctrl-x'], type: 'action', action: 'incrementNumberToken',
281
+ actionArgs: {increase: false, backtrack: false}},
282
+ // Text object motions
283
+ { keys: ['a', 'character'], type: 'motion',
284
+ motion: 'textObjectManipulation' },
285
+ { keys: ['i', 'character'], type: 'motion',
286
+ motion: 'textObjectManipulation',
287
+ motionArgs: { textObjectInner: true }},
288
+ // Search
289
+ { keys: ['/'], type: 'search',
290
+ searchArgs: { forward: true, querySrc: 'prompt' }},
291
+ { keys: ['?'], type: 'search',
292
+ searchArgs: { forward: false, querySrc: 'prompt' }},
293
+ { keys: ['*'], type: 'search',
294
+ searchArgs: { forward: true, querySrc: 'wordUnderCursor' }},
295
+ { keys: ['#'], type: 'search',
296
+ searchArgs: { forward: false, querySrc: 'wordUnderCursor' }},
297
+ // Ex command
298
+ { keys: [':'], type: 'ex' }
299
+ ];
300
+
301
+ var Vim = function() {
302
+ var alphabetRegex = /[A-Za-z]/;
303
+ var numberRegex = /[\d]/;
304
+ var whiteSpaceRegex = /\s/;
305
+ var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)];
306
+ function makeKeyRange(start, size) {
307
+ var keys = [];
308
+ for (var i = start; i < start + size; i++) {
309
+ keys.push(String.fromCharCode(i));
310
+ }
311
+ return keys;
312
+ }
313
+ var upperCaseAlphabet = makeKeyRange(65, 26);
314
+ var lowerCaseAlphabet = makeKeyRange(97, 26);
315
+ var numbers = makeKeyRange(48, 10);
316
+ var SPECIAL_SYMBOLS = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'';
317
+ var specialSymbols = SPECIAL_SYMBOLS.split('');
318
+ var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
319
+ 'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
320
+ var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
321
+ numbers).concat(['<', '>']);
322
+ var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
323
+ numbers).concat('-\"'.split(''));
324
+
325
+ function isAlphabet(k) {
326
+ return alphabetRegex.test(k);
327
+ }
328
+ function isLine(cm, line) {
329
+ return line >= cm.firstLine() && line <= cm.lastLine();
330
+ }
331
+ function isLowerCase(k) {
332
+ return (/^[a-z]$/).test(k);
333
+ }
334
+ function isMatchableSymbol(k) {
335
+ return '()[]{}'.indexOf(k) != -1;
336
+ }
337
+ function isNumber(k) {
338
+ return numberRegex.test(k);
339
+ }
340
+ function isUpperCase(k) {
341
+ return (/^[A-Z]$/).test(k);
342
+ }
343
+ function isAlphanumeric(k) {
344
+ return (/^[\w]$/).test(k);
345
+ }
346
+ function isWhiteSpace(k) {
347
+ return whiteSpaceRegex.test(k);
348
+ }
349
+ function isWhiteSpaceString(k) {
350
+ return (/^\s*$/).test(k);
351
+ }
352
+ function inRangeInclusive(x, start, end) {
353
+ return x >= start && x <= end;
354
+ }
355
+ function inArray(val, arr) {
356
+ for (var i = 0; i < arr.length; i++) {
357
+ if (arr[i] == val) {
358
+ return true;
359
+ }
360
+ }
361
+ return false;
362
+ }
363
+
364
+ // Global Vim state. Call getVimGlobalState to get and initialize.
365
+ var vimGlobalState;
366
+ function getVimGlobalState() {
367
+ if (!vimGlobalState) {
368
+ vimGlobalState = {
369
+ // The current search query.
370
+ searchQuery: null,
371
+ // Whether we are searching backwards.
372
+ searchIsReversed: false,
373
+ // Recording latest f, t, F or T motion command.
374
+ lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
375
+ registerController: new RegisterController({})
376
+ };
377
+ }
378
+ return vimGlobalState;
379
+ }
380
+ function getVimState(cm) {
381
+ if (!cm.vimState) {
382
+ // Store instance state in the CodeMirror object.
383
+ cm.vimState = {
384
+ inputState: new InputState(),
385
+ // When using jk for navigation, if you move from a longer line to a
386
+ // shorter line, the cursor may clip to the end of the shorter line.
387
+ // If j is pressed again and cursor goes to the next line, the
388
+ // cursor should go back to its horizontal position on the longer
389
+ // line if it can. This is to keep track of the horizontal position.
390
+ lastHPos: -1,
391
+ // Doing the same with screen-position for gj/gk
392
+ lastHSPos: -1,
393
+ // The last motion command run. Cleared if a non-motion command gets
394
+ // executed in between.
395
+ lastMotion: null,
396
+ marks: {},
397
+ visualMode: false,
398
+ // If we are in visual line mode. No effect if visualMode is false.
399
+ visualLine: false
400
+ };
401
+ }
402
+ return cm.vimState;
403
+ }
404
+
405
+ var vimApi= {
406
+ buildKeyMap: function() {
407
+ // TODO: Convert keymap into dictionary format for fast lookup.
408
+ },
409
+ // Testing hook, though it might be useful to expose the register
410
+ // controller anyways.
411
+ getRegisterController: function() {
412
+ return getVimGlobalState().registerController;
413
+ },
414
+ // Testing hook.
415
+ clearVimGlobalState_: function() {
416
+ vimGlobalState = null;
417
+ },
418
+ map: function(lhs, rhs) {
419
+ // Add user defined key bindings.
420
+ exCommandDispatcher.map(lhs, rhs);
421
+ },
422
+ defineEx: function(name, prefix, func){
423
+ if (name.indexOf(prefix) === 0) {
424
+ exCommands[name]=func;
425
+ exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
426
+ }else throw new Error("(Vim.defineEx) \""+prefix+"\" is not a prefix of \""+name+"\", command not registered");
427
+ },
428
+ // Initializes vim state variable on the CodeMirror object. Should only be
429
+ // called lazily by handleKey or for testing.
430
+ maybeInitState: function(cm) {
431
+ getVimState(cm);
432
+ },
433
+ // This is the outermost function called by CodeMirror, after keys have
434
+ // been mapped to their Vim equivalents.
435
+ handleKey: function(cm, key) {
436
+ var command;
437
+ var vim = getVimState(cm);
438
+ if (key == 'Esc') {
439
+ // Clear input state and get back to normal mode.
440
+ vim.inputState = new InputState();
441
+ if (vim.visualMode) {
442
+ exitVisualMode(cm, vim);
443
+ }
444
+ return;
445
+ }
446
+ if (vim.visualMode &&
447
+ cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
448
+ // The selection was cleared. Exit visual mode.
449
+ exitVisualMode(cm, vim);
450
+ }
451
+ if (!vim.visualMode &&
452
+ !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
453
+ vim.visualMode = true;
454
+ vim.visualLine = false;
455
+ }
456
+ if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) {
457
+ // Have to special case 0 since it's both a motion and a number.
458
+ command = commandDispatcher.matchCommand(key, defaultKeymap, vim);
459
+ }
460
+ if (!command) {
461
+ if (isNumber(key)) {
462
+ // Increment count unless count is 0 and key is 0.
463
+ vim.inputState.pushRepeatDigit(key);
464
+ }
465
+ return;
466
+ }
467
+ if (command.type == 'keyToKey') {
468
+ // TODO: prevent infinite recursion.
469
+ for (var i = 0; i < command.toKeys.length; i++) {
470
+ this.handleKey(cm, command.toKeys[i]);
471
+ }
472
+ } else {
473
+ commandDispatcher.processCommand(cm, vim, command);
474
+ }
475
+ }
476
+ };
477
+
478
+ // Represents the current input state.
479
+ function InputState() {
480
+ this.prefixRepeat = [];
481
+ this.motionRepeat = [];
482
+
483
+ this.operator = null;
484
+ this.operatorArgs = null;
485
+ this.motion = null;
486
+ this.motionArgs = null;
487
+ this.keyBuffer = []; // For matching multi-key commands.
488
+ this.registerName = null; // Defaults to the unamed register.
489
+ }
490
+ InputState.prototype.pushRepeatDigit = function(n) {
491
+ if (!this.operator) {
492
+ this.prefixRepeat = this.prefixRepeat.concat(n);
493
+ } else {
494
+ this.motionRepeat = this.motionRepeat.concat(n);
495
+ }
496
+ };
497
+ InputState.prototype.getRepeat = function() {
498
+ var repeat = 0;
499
+ if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
500
+ repeat = 1;
501
+ if (this.prefixRepeat.length > 0) {
502
+ repeat *= parseInt(this.prefixRepeat.join(''), 10);
503
+ }
504
+ if (this.motionRepeat.length > 0) {
505
+ repeat *= parseInt(this.motionRepeat.join(''), 10);
506
+ }
507
+ }
508
+ return repeat;
509
+ };
510
+
511
+ /*
512
+ * Register stores information about copy and paste registers. Besides
513
+ * text, a register must store whether it is linewise (i.e., when it is
514
+ * pasted, should it insert itself into a new line, or should the text be
515
+ * inserted at the cursor position.)
516
+ */
517
+ function Register(text, linewise) {
518
+ this.clear();
519
+ if (text) {
520
+ this.set(text, linewise);
521
+ }
522
+ }
523
+ Register.prototype = {
524
+ set: function(text, linewise) {
525
+ this.text = text;
526
+ this.linewise = !!linewise;
527
+ },
528
+ append: function(text, linewise) {
529
+ // if this register has ever been set to linewise, use linewise.
530
+ if (linewise || this.linewise) {
531
+ this.text += '\n' + text;
532
+ this.linewise = true;
533
+ } else {
534
+ this.text += text;
535
+ }
536
+ },
537
+ clear: function() {
538
+ this.text = '';
539
+ this.linewise = false;
540
+ },
541
+ toString: function() { return this.text; }
542
+ };
543
+
544
+ /*
545
+ * vim registers allow you to keep many independent copy and paste buffers.
546
+ * See http://usevim.com/2012/04/13/registers/ for an introduction.
547
+ *
548
+ * RegisterController keeps the state of all the registers. An initial
549
+ * state may be passed in. The unnamed register '"' will always be
550
+ * overridden.
551
+ */
552
+ function RegisterController(registers) {
553
+ this.registers = registers;
554
+ this.unamedRegister = registers['\"'] = new Register();
555
+ }
556
+ RegisterController.prototype = {
557
+ pushText: function(registerName, operator, text, linewise) {
558
+ // Lowercase and uppercase registers refer to the same register.
559
+ // Uppercase just means append.
560
+ var register = this.isValidRegister(registerName) ?
561
+ this.getRegister(registerName) : null;
562
+ // if no register/an invalid register was specified, things go to the
563
+ // default registers
564
+ if (!register) {
565
+ switch (operator) {
566
+ case 'yank':
567
+ // The 0 register contains the text from the most recent yank.
568
+ this.registers['0'] = new Register(text, linewise);
569
+ break;
570
+ case 'delete':
571
+ case 'change':
572
+ if (text.indexOf('\n') == -1) {
573
+ // Delete less than 1 line. Update the small delete register.
574
+ this.registers['-'] = new Register(text, linewise);
575
+ } else {
576
+ // Shift down the contents of the numbered registers and put the
577
+ // deleted text into register 1.
578
+ this.shiftNumericRegisters_();
579
+ this.registers['1'] = new Register(text, linewise);
580
+ }
581
+ break;
582
+ }
583
+ // Make sure the unnamed register is set to what just happened
584
+ this.unamedRegister.set(text, linewise);
585
+ return;
586
+ }
587
+
588
+ // If we've gotten to this point, we've actually specified a register
589
+ var append = isUpperCase(registerName);
590
+ if (append) {
591
+ register.append(text, linewise);
592
+ // The unamed register always has the same value as the last used
593
+ // register.
594
+ this.unamedRegister.append(text, linewise);
595
+ } else {
596
+ register.set(text, linewise);
597
+ this.unamedRegister.set(text, linewise);
598
+ }
599
+ },
600
+ // Gets the register named @name. If one of @name doesn't already exist,
601
+ // create it. If @name is invalid, return the unamedRegister.
602
+ getRegister: function(name) {
603
+ if (!this.isValidRegister(name)) {
604
+ return this.unamedRegister;
605
+ }
606
+ name = name.toLowerCase();
607
+ if (!this.registers[name]) {
608
+ this.registers[name] = new Register();
609
+ }
610
+ return this.registers[name];
611
+ },
612
+ isValidRegister: function(name) {
613
+ return name && inArray(name, validRegisters);
614
+ },
615
+ shiftNumericRegisters_: function() {
616
+ for (var i = 9; i >= 2; i--) {
617
+ this.registers[i] = this.getRegister('' + (i - 1));
618
+ }
619
+ }
620
+ };
621
+
622
+ var commandDispatcher = {
623
+ matchCommand: function(key, keyMap, vim) {
624
+ var inputState = vim.inputState;
625
+ var keys = inputState.keyBuffer.concat(key);
626
+ for (var i = 0; i < keyMap.length; i++) {
627
+ var command = keyMap[i];
628
+ if (matchKeysPartial(keys, command.keys)) {
629
+ if (keys.length < command.keys.length) {
630
+ // Matches part of a multi-key command. Buffer and wait for next
631
+ // stroke.
632
+ inputState.keyBuffer.push(key);
633
+ return null;
634
+ } else {
635
+ if (inputState.operator && command.type == 'action') {
636
+ // Ignore matched action commands after an operator. Operators
637
+ // only operate on motions. This check is really for text
638
+ // objects since aW, a[ etcs conflicts with a.
639
+ continue;
640
+ }
641
+ // Matches whole comand. Return the command.
642
+ if (command.keys[keys.length - 1] == 'character') {
643
+ inputState.selectedCharacter = keys[keys.length - 1];
644
+ if(inputState.selectedCharacter.length>1){
645
+ switch(inputState.selectedCharacter){
646
+ case "Enter":
647
+ inputState.selectedCharacter='\n';
648
+ break;
649
+ case "Space":
650
+ inputState.selectedCharacter=' ';
651
+ break;
652
+ default:
653
+ continue;
654
+ }
655
+ }
656
+ }
657
+ inputState.keyBuffer = [];
658
+ return command;
659
+ }
660
+ }
661
+ }
662
+ // Clear the buffer since there are no partial matches.
663
+ inputState.keyBuffer = [];
664
+ return null;
665
+ },
666
+ processCommand: function(cm, vim, command) {
667
+ vim.inputState.repeatOverride = command.repeatOverride;
668
+ switch (command.type) {
669
+ case 'motion':
670
+ this.processMotion(cm, vim, command);
671
+ break;
672
+ case 'operator':
673
+ this.processOperator(cm, vim, command);
674
+ break;
675
+ case 'operatorMotion':
676
+ this.processOperatorMotion(cm, vim, command);
677
+ break;
678
+ case 'action':
679
+ this.processAction(cm, vim, command);
680
+ break;
681
+ case 'search':
682
+ this.processSearch(cm, vim, command);
683
+ break;
684
+ case 'ex':
685
+ case 'keyToEx':
686
+ this.processEx(cm, vim, command);
687
+ break;
688
+ default:
689
+ break;
690
+ }
691
+ },
692
+ processMotion: function(cm, vim, command) {
693
+ vim.inputState.motion = command.motion;
694
+ vim.inputState.motionArgs = copyArgs(command.motionArgs);
695
+ this.evalInput(cm, vim);
696
+ },
697
+ processOperator: function(cm, vim, command) {
698
+ var inputState = vim.inputState;
699
+ if (inputState.operator) {
700
+ if (inputState.operator == command.operator) {
701
+ // Typing an operator twice like 'dd' makes the operator operate
702
+ // linewise
703
+ inputState.motion = 'expandToLine';
704
+ inputState.motionArgs = { linewise: true };
705
+ this.evalInput(cm, vim);
706
+ return;
707
+ } else {
708
+ // 2 different operators in a row doesn't make sense.
709
+ vim.inputState = new InputState();
710
+ }
711
+ }
712
+ inputState.operator = command.operator;
713
+ inputState.operatorArgs = copyArgs(command.operatorArgs);
714
+ if (vim.visualMode) {
715
+ // Operating on a selection in visual mode. We don't need a motion.
716
+ this.evalInput(cm, vim);
717
+ }
718
+ },
719
+ processOperatorMotion: function(cm, vim, command) {
720
+ var visualMode = vim.visualMode;
721
+ var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
722
+ if (operatorMotionArgs) {
723
+ // Operator motions may have special behavior in visual mode.
724
+ if (visualMode && operatorMotionArgs.visualLine) {
725
+ vim.visualLine = true;
726
+ }
727
+ }
728
+ this.processOperator(cm, vim, command);
729
+ if (!visualMode) {
730
+ this.processMotion(cm, vim, command);
731
+ }
732
+ },
733
+ processAction: function(cm, vim, command) {
734
+ var inputState = vim.inputState;
735
+ var repeat = inputState.getRepeat();
736
+ var repeatIsExplicit = !!repeat;
737
+ var actionArgs = copyArgs(command.actionArgs) || {};
738
+ if (inputState.selectedCharacter) {
739
+ actionArgs.selectedCharacter = inputState.selectedCharacter;
740
+ }
741
+ // Actions may or may not have motions and operators. Do these first.
742
+ if (command.operator) {
743
+ this.processOperator(cm, vim, command);
744
+ }
745
+ if (command.motion) {
746
+ this.processMotion(cm, vim, command);
747
+ }
748
+ if (command.motion || command.operator) {
749
+ this.evalInput(cm, vim);
750
+ }
751
+ actionArgs.repeat = repeat || 1;
752
+ actionArgs.repeatIsExplicit = repeatIsExplicit;
753
+ actionArgs.registerName = inputState.registerName;
754
+ vim.inputState = new InputState();
755
+ vim.lastMotion = null,
756
+ actions[command.action](cm, actionArgs, vim);
757
+ },
758
+ processSearch: function(cm, vim, command) {
759
+ if (!cm.getSearchCursor) {
760
+ // Search depends on SearchCursor.
761
+ return;
762
+ }
763
+ var forward = command.searchArgs.forward;
764
+ getSearchState(cm).setReversed(!forward);
765
+ var promptPrefix = (forward) ? '/' : '?';
766
+ var originalQuery = getSearchState(cm).getQuery();
767
+ var originalScrollPos = cm.getScrollInfo();
768
+ function handleQuery(query, ignoreCase, smartCase) {
769
+ try {
770
+ updateSearchQuery(cm, query, ignoreCase, smartCase);
771
+ } catch (e) {
772
+ showConfirm(cm, 'Invalid regex: ' + regexPart);
773
+ return;
774
+ }
775
+ commandDispatcher.processMotion(cm, vim, {
776
+ type: 'motion',
777
+ motion: 'findNext',
778
+ motionArgs: { forward: true }
779
+ });
780
+ }
781
+ function onPromptClose(query) {
782
+ cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
783
+ handleQuery(query, true /** ignoreCase */, true /** smartCase */);
784
+ }
785
+ function onPromptKeyUp(e, query) {
786
+ var parsedQuery;
787
+ try {
788
+ parsedQuery = updateSearchQuery(cm, query,
789
+ true /** ignoreCase */, true /** smartCase */)
790
+ } catch (e) {
791
+ // Swallow bad regexes for incremental search.
792
+ }
793
+ if (parsedQuery) {
794
+ cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);
795
+ } else {
796
+ clearSearchHighlight(cm);
797
+ cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
798
+ }
799
+ }
800
+ function onPromptKeyDown(e, query, close) {
801
+ var keyName = CodeMirror.keyName(e);
802
+ if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
803
+ updateSearchQuery(cm, originalQuery);
804
+ clearSearchHighlight(cm);
805
+ cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
806
+
807
+ CodeMirror.e_stop(e);
808
+ close();
809
+ cm.focus();
810
+ }
811
+ }
812
+ switch (command.searchArgs.querySrc) {
813
+ case 'prompt':
814
+ showPrompt(cm, {
815
+ onClose: onPromptClose,
816
+ prefix: promptPrefix,
817
+ desc: searchPromptDesc,
818
+ onKeyUp: onPromptKeyUp,
819
+ onKeyDown: onPromptKeyDown
820
+ });
821
+ break;
822
+ case 'wordUnderCursor':
823
+ var word = expandWordUnderCursor(cm, false /** inclusive */,
824
+ true /** forward */, false /** bigWord */,
825
+ true /** noSymbol */);
826
+ var isKeyword = true;
827
+ if (!word) {
828
+ word = expandWordUnderCursor(cm, false /** inclusive */,
829
+ true /** forward */, false /** bigWord */,
830
+ false /** noSymbol */);
831
+ isKeyword = false;
832
+ }
833
+ if (!word) {
834
+ return;
835
+ }
836
+ var query = cm.getLine(word.start.line).substring(word.start.ch,
837
+ word.end.ch + 1);
838
+ if (isKeyword) {
839
+ query = '\\b' + query + '\\b';
840
+ } else {
841
+ query = escapeRegex(query);
842
+ }
843
+ cm.setCursor(word.start);
844
+ handleQuery(query, true /** ignoreCase */, false /** smartCase */);
845
+ break;
846
+ }
847
+ },
848
+ processEx: function(cm, vim, command) {
849
+ function onPromptClose(input) {
850
+ exCommandDispatcher.processCommand(cm, input);
851
+ }
852
+ function onPromptKeyDown(e, input, close) {
853
+ var keyName = CodeMirror.keyName(e);
854
+ if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
855
+ CodeMirror.e_stop(e);
856
+ close();
857
+ cm.focus();
858
+ }
859
+ }
860
+ if (command.type == 'keyToEx') {
861
+ // Handle user defined Ex to Ex mappings
862
+ exCommandDispatcher.processCommand(cm, command.exArgs.input);
863
+ } else {
864
+ if (vim.visualMode) {
865
+ showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
866
+ onKeyDown: onPromptKeyDown});
867
+ } else {
868
+ showPrompt(cm, { onClose: onPromptClose, prefix: ':',
869
+ onKeyDown: onPromptKeyDown});
870
+ }
871
+ }
872
+ },
873
+ evalInput: function(cm, vim) {
874
+ // If the motion comand is set, execute both the operator and motion.
875
+ // Otherwise return.
876
+ var inputState = vim.inputState;
877
+ var motion = inputState.motion;
878
+ var motionArgs = inputState.motionArgs || {};
879
+ var operator = inputState.operator;
880
+ var operatorArgs = inputState.operatorArgs || {};
881
+ var registerName = inputState.registerName;
882
+ var selectionEnd = cm.getCursor('head');
883
+ var selectionStart = cm.getCursor('anchor');
884
+ // The difference between cur and selection cursors are that cur is
885
+ // being operated on and ignores that there is a selection.
886
+ var curStart = copyCursor(selectionEnd);
887
+ var curOriginal = copyCursor(curStart);
888
+ var curEnd;
889
+ var repeat;
890
+ if (operator) {
891
+ this.recordLastEdit(cm, vim, inputState);
892
+ }
893
+ if (inputState.repeatOverride !== undefined) {
894
+ // If repeatOverride is specified, that takes precedence over the
895
+ // input state's repeat. Used by Ex mode and can be user defined.
896
+ repeat = inputState.repeatOverride;
897
+ } else {
898
+ repeat = inputState.getRepeat();
899
+ }
900
+ if (repeat > 0 && motionArgs.explicitRepeat) {
901
+ motionArgs.repeatIsExplicit = true;
902
+ } else if (motionArgs.noRepeat ||
903
+ (!motionArgs.explicitRepeat && repeat === 0)) {
904
+ repeat = 1;
905
+ motionArgs.repeatIsExplicit = false;
906
+ }
907
+ if (inputState.selectedCharacter) {
908
+ // If there is a character input, stick it in all of the arg arrays.
909
+ motionArgs.selectedCharacter = operatorArgs.selectedCharacter =
910
+ inputState.selectedCharacter;
911
+ }
912
+ motionArgs.repeat = repeat;
913
+ vim.inputState = new InputState();
914
+ if (motion) {
915
+ var motionResult = motions[motion](cm, motionArgs, vim);
916
+ vim.lastMotion = motions[motion];
917
+ if (!motionResult) {
918
+ return;
919
+ }
920
+ if (motionResult instanceof Array) {
921
+ curStart = motionResult[0];
922
+ curEnd = motionResult[1];
923
+ } else {
924
+ curEnd = motionResult;
925
+ }
926
+ // TODO: Handle null returns from motion commands better.
927
+ if (!curEnd) {
928
+ curEnd = { ch: curStart.ch, line: curStart.line };
929
+ }
930
+ if (vim.visualMode) {
931
+ // Check if the selection crossed over itself. Will need to shift
932
+ // the start point if that happened.
933
+ if (cursorIsBefore(selectionStart, selectionEnd) &&
934
+ (cursorEqual(selectionStart, curEnd) ||
935
+ cursorIsBefore(curEnd, selectionStart))) {
936
+ // The end of the selection has moved from after the start to
937
+ // before the start. We will shift the start right by 1.
938
+ selectionStart.ch += 1;
939
+ } else if (cursorIsBefore(selectionEnd, selectionStart) &&
940
+ (cursorEqual(selectionStart, curEnd) ||
941
+ cursorIsBefore(selectionStart, curEnd))) {
942
+ // The opposite happened. We will shift the start left by 1.
943
+ selectionStart.ch -= 1;
944
+ }
945
+ selectionEnd = curEnd;
946
+ if (vim.visualLine) {
947
+ if (cursorIsBefore(selectionStart, selectionEnd)) {
948
+ selectionStart.ch = 0;
949
+ selectionEnd.ch = lineLength(cm, selectionEnd.line);
950
+ } else {
951
+ selectionEnd.ch = 0;
952
+ selectionStart.ch = lineLength(cm, selectionStart.line);
953
+ }
954
+ }
955
+ cm.setSelection(selectionStart, selectionEnd);
956
+ updateMark(cm, vim, '<',
957
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
958
+ : selectionEnd);
959
+ updateMark(cm, vim, '>',
960
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
961
+ : selectionStart);
962
+ } else if (!operator) {
963
+ curEnd = clipCursorToContent(cm, curEnd);
964
+ cm.setCursor(curEnd.line, curEnd.ch);
965
+ }
966
+ }
967
+
968
+ if (operator) {
969
+ var inverted = false;
970
+ vim.lastMotion = null;
971
+ operatorArgs.repeat = repeat; // Indent in visual mode needs this.
972
+ if (vim.visualMode) {
973
+ curStart = selectionStart;
974
+ curEnd = selectionEnd;
975
+ motionArgs.inclusive = true;
976
+ }
977
+ // Swap start and end if motion was backward.
978
+ if (cursorIsBefore(curEnd, curStart)) {
979
+ var tmp = curStart;
980
+ curStart = curEnd;
981
+ curEnd = tmp;
982
+ inverted = true;
983
+ }
984
+ if (motionArgs.inclusive && !(vim.visualMode && inverted)) {
985
+ // Move the selection end one to the right to include the last
986
+ // character.
987
+ curEnd.ch++;
988
+ }
989
+ var linewise = motionArgs.linewise ||
990
+ (vim.visualMode && vim.visualLine);
991
+ if (linewise) {
992
+ // Expand selection to entire line.
993
+ expandSelectionToLine(cm, curStart, curEnd);
994
+ } else if (motionArgs.forward) {
995
+ // Clip to trailing newlines only if we the motion goes forward.
996
+ clipToLine(cm, curStart, curEnd);
997
+ }
998
+ operatorArgs.registerName = registerName;
999
+ // Keep track of linewise as it affects how paste and change behave.
1000
+ operatorArgs.linewise = linewise;
1001
+ operators[operator](cm, operatorArgs, vim, curStart,
1002
+ curEnd, curOriginal);
1003
+ if (vim.visualMode) {
1004
+ exitVisualMode(cm, vim);
1005
+ }
1006
+ if (operatorArgs.enterInsertMode) {
1007
+ actions.enterInsertMode(cm);
1008
+ }
1009
+ }
1010
+ },
1011
+ recordLastEdit: function(cm, vim, inputState) {
1012
+ vim.lastEdit = inputState;
1013
+ }
1014
+ };
1015
+
1016
+ /**
1017
+ * typedef {Object{line:number,ch:number}} Cursor An object containing the
1018
+ * position of the cursor.
1019
+ */
1020
+ // All of the functions below return Cursor objects.
1021
+ var motions = {
1022
+ moveToTopLine: function(cm, motionArgs) {
1023
+ var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;
1024
+ return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line)) };
1025
+ },
1026
+ moveToMiddleLine: function(cm) {
1027
+ var range = getUserVisibleLines(cm);
1028
+ var line = Math.floor((range.top + range.bottom) * 0.5);
1029
+ return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line)) };
1030
+ },
1031
+ moveToBottomLine: function(cm, motionArgs) {
1032
+ var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;
1033
+ return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line)) };
1034
+ },
1035
+ expandToLine: function(cm, motionArgs) {
1036
+ // Expands forward to end of line, and then to next line if repeat is
1037
+ // >1. Does not handle backward motion!
1038
+ var cur = cm.getCursor();
1039
+ return { line: cur.line + motionArgs.repeat - 1, ch: Infinity };
1040
+ },
1041
+ findNext: function(cm, motionArgs, vim) {
1042
+ var state = getSearchState(cm);
1043
+ var query = state.getQuery();
1044
+ if (!query) {
1045
+ return;
1046
+ }
1047
+ var prev = !motionArgs.forward;
1048
+ // If search is initiated with ? instead of /, negate direction.
1049
+ prev = (state.isReversed()) ? !prev : prev;
1050
+ highlightSearchMatches(cm, query);
1051
+ return findNext(cm, prev/** prev */, query, motionArgs.repeat);
1052
+ },
1053
+ goToMark: function(cm, motionArgs, vim) {
1054
+ var mark = vim.marks[motionArgs.selectedCharacter];
1055
+ if (mark) {
1056
+ return mark.find();
1057
+ }
1058
+ return null;
1059
+ },
1060
+ jumpToMark: function(cm, motionArgs, vim) {
1061
+ var best = cm.getCursor();
1062
+ for (var i = 0; i < motionArgs.repeat; i++) {
1063
+ var cursor = best;
1064
+ for (var key in vim.marks) {
1065
+ if (!isLowerCase(key)) {
1066
+ continue;
1067
+ }
1068
+ var mark = vim.marks[key].find();
1069
+ var isWrongDirection = (motionArgs.forward) ?
1070
+ cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark)
1071
+
1072
+ if (isWrongDirection) {
1073
+ continue;
1074
+ }
1075
+ if (motionArgs.linewise && (mark.line == cursor.line)) {
1076
+ continue;
1077
+ }
1078
+
1079
+ var equal = cursorEqual(cursor, best);
1080
+ var between = (motionArgs.forward) ?
1081
+ cusrorIsBetween(cursor, mark, best) :
1082
+ cusrorIsBetween(best, mark, cursor);
1083
+
1084
+ if (equal || between) {
1085
+ best = mark;
1086
+ }
1087
+ }
1088
+ }
1089
+
1090
+ if (motionArgs.linewise) {
1091
+ // Vim places the cursor on the first non-whitespace character of
1092
+ // the line if there is one, else it places the cursor at the end
1093
+ // of the line, regardless of whether a mark was found.
1094
+ best.ch = findFirstNonWhiteSpaceCharacter(cm.getLine(best.line));
1095
+ }
1096
+ return best;
1097
+ },
1098
+ moveByCharacters: function(cm, motionArgs) {
1099
+ var cur = cm.getCursor();
1100
+ var repeat = motionArgs.repeat;
1101
+ var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
1102
+ return { line: cur.line, ch: ch };
1103
+ },
1104
+ moveByLines: function(cm, motionArgs, vim) {
1105
+ var cur = cm.getCursor();
1106
+ var endCh = cur.ch;
1107
+ // Depending what our last motion was, we may want to do different
1108
+ // things. If our last motion was moving vertically, we want to
1109
+ // preserve the HPos from our last horizontal move. If our last motion
1110
+ // was going to the end of a line, moving vertically we should go to
1111
+ // the end of the line, etc.
1112
+ switch (vim.lastMotion) {
1113
+ case this.moveByLines:
1114
+ case this.moveByDisplayLines:
1115
+ case this.moveByScroll:
1116
+ case this.moveToColumn:
1117
+ case this.moveToEol:
1118
+ endCh = vim.lastHPos;
1119
+ break;
1120
+ default:
1121
+ vim.lastHPos = endCh;
1122
+ }
1123
+ var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0);
1124
+ var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
1125
+ if (line < cm.firstLine() || line > cm.lastLine() ) {
1126
+ return null;
1127
+ }
1128
+ if(motionArgs.toFirstChar){
1129
+ endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
1130
+ vim.lastHPos = endCh;
1131
+ }
1132
+ vim.lastHSPos = cm.charCoords({line:line, ch:endCh},"div").left;
1133
+ return { line: line, ch: endCh };
1134
+ },
1135
+ moveByDisplayLines: function(cm, motionArgs, vim) {
1136
+ var cur = cm.getCursor();
1137
+ switch (vim.lastMotion) {
1138
+ case this.moveByDisplayLines:
1139
+ case this.moveByScroll:
1140
+ case this.moveByLines:
1141
+ case this.moveToColumn:
1142
+ case this.moveToEol:
1143
+ break;
1144
+ default:
1145
+ vim.lastHSPos = cm.charCoords(cur,"div").left;
1146
+ }
1147
+ var repeat = motionArgs.repeat;
1148
+ var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),"line",vim.lastHSPos);
1149
+ if (res.hitSide) {
1150
+ if (motionArgs.forward) {
1151
+ var lastCharCoords = cm.charCoords(res, 'div');
1152
+ var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };
1153
+ var res = cm.coordsChar(goalCoords, 'div');
1154
+ } else {
1155
+ var resCoords = cm.charCoords({ line: cm.firstLine(), ch: 0}, 'div');
1156
+ resCoords.left = vim.lastHSPos;
1157
+ res = cm.coordsChar(resCoords, 'div');
1158
+ }
1159
+ }
1160
+ vim.lastHPos = res.ch;
1161
+ return res;
1162
+ },
1163
+ moveByPage: function(cm, motionArgs) {
1164
+ // CodeMirror only exposes functions that move the cursor page down, so
1165
+ // doing this bad hack to move the cursor and move it back. evalInput
1166
+ // will move the cursor to where it should be in the end.
1167
+ var curStart = cm.getCursor();
1168
+ var repeat = motionArgs.repeat;
1169
+ cm.moveV((motionArgs.forward ? repeat : -repeat), 'page');
1170
+ var curEnd = cm.getCursor();
1171
+ cm.setCursor(curStart);
1172
+ return curEnd;
1173
+ },
1174
+ moveByParagraph: function(cm, motionArgs) {
1175
+ var line = cm.getCursor().line;
1176
+ var repeat = motionArgs.repeat;
1177
+ var inc = motionArgs.forward ? 1 : -1;
1178
+ for (var i = 0; i < repeat; i++) {
1179
+ if ((!motionArgs.forward && line === cm.firstLine() ) ||
1180
+ (motionArgs.forward && line == cm.lastLine())) {
1181
+ break;
1182
+ }
1183
+ line += inc;
1184
+ while (line !== cm.firstLine() && line != cm.lastLine() && cm.getLine(line)) {
1185
+ line += inc;
1186
+ }
1187
+ }
1188
+ return { line: line, ch: 0 };
1189
+ },
1190
+ moveByScroll: function(cm, motionArgs, vim) {
1191
+ var globalState = getVimGlobalState();
1192
+ var scrollbox = cm.getScrollInfo();
1193
+ var curEnd = null;
1194
+ var repeat = motionArgs.repeat;
1195
+ if (!repeat) {
1196
+ repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
1197
+ }
1198
+ var orig = cm.charCoords(cm.getCursor(), 'local');
1199
+ motionArgs.repeat = repeat;
1200
+ var curEnd = motions.moveByDisplayLines(cm, motionArgs, vim);
1201
+ if (!curEnd) {
1202
+ return null;
1203
+ }
1204
+ var dest = cm.charCoords(curEnd, 'local');
1205
+ cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
1206
+ return curEnd;
1207
+ },
1208
+ moveByWords: function(cm, motionArgs) {
1209
+ return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
1210
+ !!motionArgs.wordEnd, !!motionArgs.bigWord);
1211
+ },
1212
+ moveTillCharacter: function(cm, motionArgs) {
1213
+ var repeat = motionArgs.repeat;
1214
+ var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
1215
+ motionArgs.selectedCharacter);
1216
+ var increment = motionArgs.forward ? -1 : 1;
1217
+ recordLastCharacterSearch(increment, motionArgs);
1218
+ if(!curEnd)return cm.getCursor();
1219
+ curEnd.ch += increment;
1220
+ return curEnd;
1221
+ },
1222
+ moveToCharacter: function(cm, motionArgs) {
1223
+ var repeat = motionArgs.repeat;
1224
+ recordLastCharacterSearch(0, motionArgs);
1225
+ return moveToCharacter(cm, repeat, motionArgs.forward,
1226
+ motionArgs.selectedCharacter) || cm.getCursor();
1227
+ },
1228
+ moveToSymbol: function(cm, motionArgs) {
1229
+ var repeat = motionArgs.repeat;
1230
+ return findSymbol(cm, repeat, motionArgs.forward,
1231
+ motionArgs.selectedCharacter) || cm.getCursor();
1232
+ },
1233
+ moveToColumn: function(cm, motionArgs, vim) {
1234
+ var repeat = motionArgs.repeat;
1235
+ // repeat is equivalent to which column we want to move to!
1236
+ vim.lastHPos = repeat - 1;
1237
+ vim.lastHSPos = cm.charCoords(cm.getCursor(),"div").left;
1238
+ return moveToColumn(cm, repeat);
1239
+ },
1240
+ moveToEol: function(cm, motionArgs, vim) {
1241
+ var cur = cm.getCursor();
1242
+ vim.lastHPos = Infinity;
1243
+ var retval={ line: cur.line + motionArgs.repeat - 1, ch: Infinity }
1244
+ var end=cm.clipPos(retval);
1245
+ end.ch--;
1246
+ vim.lastHSPos = cm.charCoords(end,"div").left;
1247
+ return retval;
1248
+ },
1249
+ moveToFirstNonWhiteSpaceCharacter: function(cm) {
1250
+ // Go to the start of the line where the text begins, or the end for
1251
+ // whitespace-only lines
1252
+ var cursor = cm.getCursor();
1253
+ return { line: cursor.line,
1254
+ ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)) };
1255
+ },
1256
+ moveToMatchedSymbol: function(cm, motionArgs) {
1257
+ var cursor = cm.getCursor();
1258
+ var symbol = cm.getLine(cursor.line).charAt(cursor.ch);
1259
+ if (isMatchableSymbol(symbol)) {
1260
+ return findMatchedSymbol(cm, cm.getCursor(), motionArgs.symbol);
1261
+ } else {
1262
+ return cursor;
1263
+ }
1264
+ },
1265
+ moveToStartOfLine: function(cm) {
1266
+ var cursor = cm.getCursor();
1267
+ return { line: cursor.line, ch: 0 };
1268
+ },
1269
+ moveToLineOrEdgeOfDocument: function(cm, motionArgs) {
1270
+ var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
1271
+ if (motionArgs.repeatIsExplicit) {
1272
+ lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
1273
+ }
1274
+ return { line: lineNum,
1275
+ ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) };
1276
+ },
1277
+ textObjectManipulation: function(cm, motionArgs) {
1278
+ var character = motionArgs.selectedCharacter;
1279
+ // Inclusive is the difference between a and i
1280
+ // TODO: Instead of using the additional text object map to perform text
1281
+ // object operations, merge the map into the defaultKeyMap and use
1282
+ // motionArgs to define behavior. Define separate entries for 'aw',
1283
+ // 'iw', 'a[', 'i[', etc.
1284
+ var inclusive = !motionArgs.textObjectInner;
1285
+ if (!textObjects[character]) {
1286
+ // No text object defined for this, don't move.
1287
+ return null;
1288
+ }
1289
+ var tmp = textObjects[character](cm, inclusive);
1290
+ var start = tmp.start;
1291
+ var end = tmp.end;
1292
+ return [start, end];
1293
+ },
1294
+ repeatLastCharacterSearch: function(cm, motionArgs) {
1295
+ var lastSearch = getVimGlobalState().lastChararacterSearch;
1296
+ var repeat = motionArgs.repeat;
1297
+ var forward = motionArgs.forward === lastSearch.forward;
1298
+ var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);
1299
+ cm.moveH(-increment, 'char');
1300
+ motionArgs.inclusive = forward ? true : false;
1301
+ var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
1302
+ if (!curEnd) {
1303
+ cm.moveH(increment, 'char')
1304
+ return cm.getCursor();
1305
+ }
1306
+ curEnd.ch += increment;
1307
+ return curEnd;
1308
+ }
1309
+ };
1310
+
1311
+ var operators = {
1312
+ change: function(cm, operatorArgs, vim, curStart, curEnd) {
1313
+ getVimGlobalState().registerController.pushText(
1314
+ operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd),
1315
+ operatorArgs.linewise);
1316
+ if (operatorArgs.linewise) {
1317
+ // Delete starting at the first nonwhitespace character of the first
1318
+ // line, instead of from the start of the first line. This way we get
1319
+ // an indent when we get into insert mode. This behavior isn't quite
1320
+ // correct because we should treat this as a completely new line, and
1321
+ // indent should be whatever codemirror thinks is the right indent.
1322
+ // But cm.indentLine doesn't seem work on empty lines.
1323
+ // TODO: Fix the above.
1324
+ curStart.ch =
1325
+ findFirstNonWhiteSpaceCharacter(cm.getLine(curStart.line));
1326
+ // Insert an additional newline so that insert mode can start there.
1327
+ // curEnd should be on the first character of the new line.
1328
+ cm.replaceRange('\n', curStart, curEnd);
1329
+ } else {
1330
+ cm.replaceRange('', curStart, curEnd);
1331
+ }
1332
+ cm.setCursor(curStart);
1333
+ },
1334
+ // delete is a javascript keyword.
1335
+ 'delete': function(cm, operatorArgs, vim, curStart, curEnd) {
1336
+ getVimGlobalState().registerController.pushText(
1337
+ operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd),
1338
+ operatorArgs.linewise);
1339
+ cm.replaceRange('', curStart, curEnd);
1340
+ if (operatorArgs.linewise) {
1341
+ cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
1342
+ } else {
1343
+ cm.setCursor(curStart);
1344
+ }
1345
+ },
1346
+ indent: function(cm, operatorArgs, vim, curStart, curEnd) {
1347
+ var startLine = curStart.line;
1348
+ var endLine = curEnd.line;
1349
+ // In visual mode, n> shifts the selection right n times, instead of
1350
+ // shifting n lines right once.
1351
+ var repeat = (vim.visualMode) ? operatorArgs.repeat : 1;
1352
+ if (operatorArgs.linewise) {
1353
+ // The only way to delete a newline is to delete until the start of
1354
+ // the next line, so in linewise mode evalInput will include the next
1355
+ // line. We don't want this in indent, so we go back a line.
1356
+ endLine--;
1357
+ }
1358
+ for (var i = startLine; i <= endLine; i++) {
1359
+ for (var j = 0; j < repeat; j++) {
1360
+ cm.indentLine(i, operatorArgs.indentRight);
1361
+ }
1362
+ }
1363
+ cm.setCursor(curStart);
1364
+ cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
1365
+ },
1366
+ swapcase: function(cm, operatorArgs, vim, curStart, curEnd, curOriginal) {
1367
+ var toSwap = cm.getRange(curStart, curEnd);
1368
+ var swapped = '';
1369
+ for (var i = 0; i < toSwap.length; i++) {
1370
+ var character = toSwap.charAt(i);
1371
+ swapped += isUpperCase(character) ? character.toLowerCase() :
1372
+ character.toUpperCase();
1373
+ }
1374
+ cm.replaceRange(swapped, curStart, curEnd);
1375
+ cm.setCursor(curOriginal);
1376
+ },
1377
+ yank: function(cm, operatorArgs, vim, curStart, curEnd, curOriginal) {
1378
+ getVimGlobalState().registerController.pushText(
1379
+ operatorArgs.registerName, 'yank',
1380
+ cm.getRange(curStart, curEnd), operatorArgs.linewise);
1381
+ cm.setCursor(curOriginal);
1382
+ }
1383
+ };
1384
+
1385
+ var actions = {
1386
+ scrollToCursor: function(cm, actionArgs) {
1387
+ var lineNum = cm.getCursor().line;
1388
+ var heightProp = window.getComputedStyle(cm.getScrollerElement()).
1389
+ getPropertyValue('height');
1390
+ var height = parseInt(heightProp);
1391
+ var y = cm.charCoords({line: lineNum, ch: 0}, "local").top;
1392
+ var halfHeight = parseInt(height) / 2;
1393
+ switch (actionArgs.position) {
1394
+ case 'center': y = y - (height / 2) + 10;
1395
+ break;
1396
+ case 'bottom': y = y - height;
1397
+ break;
1398
+ case 'top': break;
1399
+ }
1400
+ cm.scrollTo(null, y);
1401
+ // The calculations are slightly off, use scrollIntoView to nudge the
1402
+ // view into the right place.
1403
+ cm.scrollIntoView();
1404
+ },
1405
+ enterInsertMode: function(cm, actionArgs) {
1406
+ var insertAt = (actionArgs) ? actionArgs.insertAt : null;
1407
+ if (insertAt == 'eol') {
1408
+ var cursor = cm.getCursor();
1409
+ cursor = { line: cursor.line, ch: lineLength(cm, cursor.line) };
1410
+ cm.setCursor(cursor);
1411
+ } else if (insertAt == 'charAfter') {
1412
+ cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
1413
+ }
1414
+ cm.setOption('keyMap', 'vim-insert');
1415
+ },
1416
+ toggleVisualMode: function(cm, actionArgs, vim) {
1417
+ var repeat = actionArgs.repeat;
1418
+ var curStart = cm.getCursor();
1419
+ var curEnd;
1420
+ // TODO: The repeat should actually select number of characters/lines
1421
+ // equal to the repeat times the size of the previous visual
1422
+ // operation.
1423
+ if (!vim.visualMode) {
1424
+ vim.visualMode = true;
1425
+ vim.visualLine = !!actionArgs.linewise;
1426
+ if (vim.visualLine) {
1427
+ curStart.ch = 0;
1428
+ curEnd = clipCursorToContent(cm, {
1429
+ line: curStart.line + repeat - 1,
1430
+ ch: lineLength(cm, curStart.line)
1431
+ }, true /** includeLineBreak */);
1432
+ } else {
1433
+ curEnd = clipCursorToContent(cm, {
1434
+ line: curStart.line,
1435
+ ch: curStart.ch + repeat
1436
+ }, true /** includeLineBreak */);
1437
+ }
1438
+ // Make the initial selection.
1439
+ if (!actionArgs.repeatIsExplicit && !vim.visualLine) {
1440
+ // This is a strange case. Here the implicit repeat is 1. The
1441
+ // following commands lets the cursor hover over the 1 character
1442
+ // selection.
1443
+ cm.setCursor(curEnd);
1444
+ cm.setSelection(curEnd, curStart);
1445
+ } else {
1446
+ cm.setSelection(curStart, curEnd);
1447
+ }
1448
+ } else {
1449
+ curStart = cm.getCursor('anchor');
1450
+ curEnd = cm.getCursor('head');
1451
+ if (!vim.visualLine && actionArgs.linewise) {
1452
+ // Shift-V pressed in characterwise visual mode. Switch to linewise
1453
+ // visual mode instead of exiting visual mode.
1454
+ vim.visualLine = true;
1455
+ curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
1456
+ lineLength(cm, curStart.line);
1457
+ curEnd.ch = cursorIsBefore(curStart, curEnd) ?
1458
+ lineLength(cm, curEnd.line) : 0;
1459
+ cm.setSelection(curStart, curEnd);
1460
+ } else if (vim.visualLine && !actionArgs.linewise) {
1461
+ // v pressed in linewise visual mode. Switch to characterwise visual
1462
+ // mode instead of exiting visual mode.
1463
+ vim.visualLine = false;
1464
+ } else {
1465
+ exitVisualMode(cm, vim);
1466
+ }
1467
+ }
1468
+ updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
1469
+ : curEnd);
1470
+ updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
1471
+ : curStart);
1472
+ },
1473
+ joinLines: function(cm, actionArgs, vim) {
1474
+ var curStart, curEnd;
1475
+ if (vim.visualMode) {
1476
+ curStart = cm.getCursor('anchor');
1477
+ curEnd = cm.getCursor('head');
1478
+ curEnd.ch = lineLength(cm, curEnd.line) - 1;
1479
+ } else {
1480
+ // Repeat is the number of lines to join. Minimum 2 lines.
1481
+ var repeat = Math.max(actionArgs.repeat, 2);
1482
+ curStart = cm.getCursor();
1483
+ curEnd = clipCursorToContent(cm, { line: curStart.line + repeat - 1,
1484
+ ch: Infinity });
1485
+ }
1486
+ var finalCh = 0;
1487
+ cm.operation(function() {
1488
+ for (var i = curStart.line; i < curEnd.line; i++) {
1489
+ finalCh = lineLength(cm, curStart.line);
1490
+ var tmp = { line: curStart.line + 1,
1491
+ ch: lineLength(cm, curStart.line + 1) };
1492
+ var text = cm.getRange(curStart, tmp);
1493
+ text = text.replace(/\n\s*/g, ' ');
1494
+ cm.replaceRange(text, curStart, tmp);
1495
+ }
1496
+ var curFinalPos = { line: curStart.line, ch: finalCh };
1497
+ cm.setCursor(curFinalPos);
1498
+ });
1499
+ },
1500
+ newLineAndEnterInsertMode: function(cm, actionArgs) {
1501
+ var insertAt = cm.getCursor();
1502
+ if (insertAt.line === cm.firstLine() && !actionArgs.after) {
1503
+ // Special case for inserting newline before start of document.
1504
+ cm.replaceRange('\n', { line: cm.firstLine(), ch: 0 });
1505
+ cm.setCursor(cm.firstLine(), 0);
1506
+ } else {
1507
+ insertAt.line = (actionArgs.after) ? insertAt.line :
1508
+ insertAt.line - 1;
1509
+ insertAt.ch = lineLength(cm, insertAt.line);
1510
+ cm.setCursor(insertAt);
1511
+ var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||
1512
+ CodeMirror.commands.newlineAndIndent;
1513
+ newlineFn(cm);
1514
+ }
1515
+ this.enterInsertMode(cm);
1516
+ },
1517
+ paste: function(cm, actionArgs, vim) {
1518
+ var cur = cm.getCursor();
1519
+ var register = getVimGlobalState().registerController.getRegister(
1520
+ actionArgs.registerName);
1521
+ if (!register.text) {
1522
+ return;
1523
+ }
1524
+ for (var text = '', i = 0; i < actionArgs.repeat; i++) {
1525
+ text += register.text;
1526
+ }
1527
+ var linewise = register.linewise;
1528
+ if (linewise) {
1529
+ if (actionArgs.after) {
1530
+ // Move the newline at the end to the start instead, and paste just
1531
+ // before the newline character of the line we are on right now.
1532
+ text = '\n' + text.slice(0, text.length - 1);
1533
+ cur.ch = lineLength(cm, cur.line);
1534
+ } else {
1535
+ cur.ch = 0;
1536
+ }
1537
+ } else {
1538
+ cur.ch += actionArgs.after ? 1 : 0;
1539
+ }
1540
+ cm.replaceRange(text, cur);
1541
+ // Now fine tune the cursor to where we want it.
1542
+ var curPosFinal;
1543
+ var idx;
1544
+ if (linewise && actionArgs.after) {
1545
+ curPosFinal = { line: cur.line + 1,
1546
+ ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)) };
1547
+ } else if (linewise && !actionArgs.after) {
1548
+ curPosFinal = { line: cur.line,
1549
+ ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)) };
1550
+ } else if (!linewise && actionArgs.after) {
1551
+ idx = cm.indexFromPos(cur);
1552
+ curPosFinal = cm.posFromIndex(idx + text.length - 1);
1553
+ } else {
1554
+ idx = cm.indexFromPos(cur);
1555
+ curPosFinal = cm.posFromIndex(idx + text.length);
1556
+ }
1557
+ cm.setCursor(curPosFinal);
1558
+ },
1559
+ undo: function(cm, actionArgs) {
1560
+ repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
1561
+ },
1562
+ redo: function(cm, actionArgs) {
1563
+ repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
1564
+ },
1565
+ setRegister: function(cm, actionArgs, vim) {
1566
+ vim.inputState.registerName = actionArgs.selectedCharacter;
1567
+ },
1568
+ setMark: function(cm, actionArgs, vim) {
1569
+ var markName = actionArgs.selectedCharacter;
1570
+ updateMark(cm, vim, markName, cm.getCursor());
1571
+ },
1572
+ replace: function(cm, actionArgs, vim) {
1573
+ var replaceWith = actionArgs.selectedCharacter;
1574
+ var curStart = cm.getCursor();
1575
+ var replaceTo;
1576
+ var curEnd;
1577
+ if(vim.visualMode){
1578
+ curStart=cm.getCursor('start');
1579
+ curEnd=cm.getCursor('end');
1580
+ // workaround to catch the character under the cursor
1581
+ // existing workaround doesn't cover actions
1582
+ curEnd=cm.clipPos({line: curEnd.line, ch: curEnd.ch+1});
1583
+ }else{
1584
+ var line = cm.getLine(curStart.line);
1585
+ replaceTo = curStart.ch + actionArgs.repeat;
1586
+ if (replaceTo > line.length) {
1587
+ replaceTo=line.length;
1588
+ }
1589
+ curEnd = { line: curStart.line, ch: replaceTo };
1590
+ }
1591
+ if(replaceWith=='\n'){
1592
+ if(!vim.visualMode) cm.replaceRange('', curStart, curEnd);
1593
+ // special case, where vim help says to replace by just one line-break
1594
+ (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
1595
+ }else {
1596
+ var replaceWithStr=cm.getRange(curStart, curEnd);
1597
+ //replace all characters in range by selected, but keep linebreaks
1598
+ replaceWithStr=replaceWithStr.replace(/[^\n]/g,replaceWith);
1599
+ cm.replaceRange(replaceWithStr, curStart, curEnd);
1600
+ if(vim.visualMode){
1601
+ cm.setCursor(curStart);
1602
+ exitVisualMode(cm,vim);
1603
+ }else{
1604
+ cm.setCursor(offsetCursor(curEnd, 0, -1));
1605
+ }
1606
+ }
1607
+ },
1608
+ incrementNumberToken: function(cm, actionArgs, vim) {
1609
+ var cur = cm.getCursor();
1610
+ var lineStr = cm.getLine(cur.line);
1611
+ var re = /-?\d+/g;
1612
+ var match;
1613
+ var start;
1614
+ var end;
1615
+ var numberStr;
1616
+ var token;
1617
+ while ((match = re.exec(lineStr)) !== null) {
1618
+ token = match[0];
1619
+ start = match.index;
1620
+ end = start + token.length;
1621
+ if(cur.ch < end)break;
1622
+ }
1623
+ if(!actionArgs.backtrack && (end <= cur.ch))return;
1624
+ if (token) {
1625
+ var increment = actionArgs.increase ? 1 : -1;
1626
+ var number = parseInt(token) + (increment * actionArgs.repeat);
1627
+ var from = {ch:start, line:cur.line};
1628
+ var to = {ch:end, line:cur.line};
1629
+ numberStr = number.toString();
1630
+ cm.replaceRange(numberStr, from, to);
1631
+ } else {
1632
+ return;
1633
+ }
1634
+ cm.setCursor({line: cur.line, ch: start + numberStr.length - 1});
1635
+ },
1636
+ repeatLastEdit: function(cm, actionArgs, vim) {
1637
+ // TODO: Make this repeat insert mode changes.
1638
+ var lastEdit = vim.lastEdit;
1639
+ if (lastEdit) {
1640
+ if (actionArgs.repeat && actionArgs.repeatIsExplicit) {
1641
+ vim.lastEdit.repeatOverride = actionArgs.repeat;
1642
+ }
1643
+ var currentInputState = vim.inputState;
1644
+ vim.inputState = vim.lastEdit;
1645
+ commandDispatcher.evalInput(cm, vim);
1646
+ vim.inputState = currentInputState;
1647
+ }
1648
+ }
1649
+ };
1650
+
1651
+ var textObjects = {
1652
+ // TODO: lots of possible exceptions that can be thrown here. Try da(
1653
+ // outside of a () block.
1654
+ // TODO: implement text objects for the reverse like }. Should just be
1655
+ // an additional mapping after moving to the defaultKeyMap.
1656
+ 'w': function(cm, inclusive) {
1657
+ return expandWordUnderCursor(cm, inclusive, true /** forward */,
1658
+ false /** bigWord */);
1659
+ },
1660
+ 'W': function(cm, inclusive) {
1661
+ return expandWordUnderCursor(cm, inclusive,
1662
+ true /** forward */, true /** bigWord */);
1663
+ },
1664
+ '{': function(cm, inclusive) {
1665
+ return selectCompanionObject(cm, '}', inclusive);
1666
+ },
1667
+ '(': function(cm, inclusive) {
1668
+ return selectCompanionObject(cm, ')', inclusive);
1669
+ },
1670
+ '[': function(cm, inclusive) {
1671
+ return selectCompanionObject(cm, ']', inclusive);
1672
+ },
1673
+ '\'': function(cm, inclusive) {
1674
+ return findBeginningAndEnd(cm, "'", inclusive);
1675
+ },
1676
+ '\"': function(cm, inclusive) {
1677
+ return findBeginningAndEnd(cm, '"', inclusive);
1678
+ }
1679
+ };
1680
+
1681
+ /*
1682
+ * Below are miscellaneous utility functions used by vim.js
1683
+ */
1684
+
1685
+ /**
1686
+ * Clips cursor to ensure that line is within the buffer's range
1687
+ * If includeLineBreak is true, then allow cur.ch == lineLength.
1688
+ */
1689
+ function clipCursorToContent(cm, cur, includeLineBreak) {
1690
+ var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );
1691
+ var maxCh = lineLength(cm, line) - 1;
1692
+ maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
1693
+ var ch = Math.min(Math.max(0, cur.ch), maxCh);
1694
+ return { line: line, ch: ch };
1695
+ }
1696
+ // Merge arguments in place, for overriding arguments.
1697
+ function mergeArgs(to, from) {
1698
+ for (var prop in from) {
1699
+ if (from.hasOwnProperty(prop)) {
1700
+ to[prop] = from[prop];
1701
+ }
1702
+ }
1703
+ }
1704
+ function copyArgs(args) {
1705
+ var ret = {};
1706
+ for (var prop in args) {
1707
+ if (args.hasOwnProperty(prop)) {
1708
+ ret[prop] = args[prop];
1709
+ }
1710
+ }
1711
+ return ret;
1712
+ }
1713
+ function offsetCursor(cur, offsetLine, offsetCh) {
1714
+ return { line: cur.line + offsetLine, ch: cur.ch + offsetCh };
1715
+ }
1716
+ function arrayEq(a1, a2) {
1717
+ if (a1.length != a2.length) {
1718
+ return false;
1719
+ }
1720
+ for (var i = 0; i < a1.length; i++) {
1721
+ if (a1[i] != a2[i]) {
1722
+ return false;
1723
+ }
1724
+ }
1725
+ return true;
1726
+ }
1727
+ function matchKeysPartial(pressed, mapped) {
1728
+ for (var i = 0; i < pressed.length; i++) {
1729
+ // 'character' means any character. For mark, register commads, etc.
1730
+ if (pressed[i] != mapped[i] && mapped[i] != 'character') {
1731
+ return false;
1732
+ }
1733
+ }
1734
+ return true;
1735
+ }
1736
+ function arrayIsSubsetFromBeginning(small, big) {
1737
+ for (var i = 0; i < small.length; i++) {
1738
+ if (small[i] != big[i]) {
1739
+ return false;
1740
+ }
1741
+ }
1742
+ return true;
1743
+ }
1744
+ function repeatFn(cm, fn, repeat) {
1745
+ return function() {
1746
+ for (var i = 0; i < repeat; i++) {
1747
+ fn(cm);
1748
+ }
1749
+ };
1750
+ }
1751
+ function copyCursor(cur) {
1752
+ return { line: cur.line, ch: cur.ch };
1753
+ }
1754
+ function cursorEqual(cur1, cur2) {
1755
+ return cur1.ch == cur2.ch && cur1.line == cur2.line;
1756
+ }
1757
+ function cursorIsBefore(cur1, cur2) {
1758
+ if (cur1.line < cur2.line) {
1759
+ return true;
1760
+ } else if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
1761
+ return true;
1762
+ }
1763
+ return false;
1764
+ }
1765
+ function cusrorIsBetween(cur1, cur2, cur3) {
1766
+ // returns true if cur2 is between cur1 and cur3.
1767
+ var cur1before2 = cursorIsBefore(cur1, cur2);
1768
+ var cur2before3 = cursorIsBefore(cur2, cur3);
1769
+ return cur1before2 && cur2before3;
1770
+ }
1771
+ function lineLength(cm, lineNum) {
1772
+ return cm.getLine(lineNum).length;
1773
+ }
1774
+ function reverse(s){
1775
+ return s.split("").reverse().join("");
1776
+ }
1777
+ function trim(s) {
1778
+ if (s.trim) {
1779
+ return s.trim();
1780
+ } else {
1781
+ return s.replace(/^\s+|\s+$/g, '');
1782
+ }
1783
+ }
1784
+ function escapeRegex(s) {
1785
+ return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, "\\$1");
1786
+ }
1787
+
1788
+ function exitVisualMode(cm, vim) {
1789
+ vim.visualMode = false;
1790
+ vim.visualLine = false;
1791
+ var selectionStart = cm.getCursor('anchor');
1792
+ var selectionEnd = cm.getCursor('head');
1793
+ if (!cursorEqual(selectionStart, selectionEnd)) {
1794
+ // Clear the selection and set the cursor only if the selection has not
1795
+ // already been cleared. Otherwise we risk moving the cursor somewhere
1796
+ // it's not supposed to be.
1797
+ cm.setCursor(clipCursorToContent(cm, selectionEnd));
1798
+ }
1799
+ }
1800
+
1801
+ // Remove any trailing newlines from the selection. For
1802
+ // example, with the caret at the start of the last word on the line,
1803
+ // 'dw' should word, but not the newline, while 'w' should advance the
1804
+ // caret to the first character of the next line.
1805
+ function clipToLine(cm, curStart, curEnd) {
1806
+ var selection = cm.getRange(curStart, curEnd);
1807
+ var lines = selection.split('\n');
1808
+ if (lines.length > 1 && isWhiteSpaceString(lines.pop())) {
1809
+ curEnd.line--;
1810
+ curEnd.ch = lineLength(cm, curEnd.line);
1811
+ }
1812
+ }
1813
+
1814
+ // Expand the selection to line ends.
1815
+ function expandSelectionToLine(cm, curStart, curEnd) {
1816
+ curStart.ch = 0;
1817
+ curEnd.ch = 0;
1818
+ curEnd.line++;
1819
+ }
1820
+
1821
+ function findFirstNonWhiteSpaceCharacter(text) {
1822
+ if (!text) {
1823
+ return 0;
1824
+ }
1825
+ var firstNonWS = text.search(/\S/);
1826
+ return firstNonWS == -1 ? text.length : firstNonWS;
1827
+ }
1828
+
1829
+ function expandWordUnderCursor(cm, inclusive, forward, bigWord, noSymbol) {
1830
+ var cur = cm.getCursor();
1831
+ var line = cm.getLine(cur.line);
1832
+ var idx = cur.ch;
1833
+
1834
+ // Seek to first word or non-whitespace character, depending on if
1835
+ // noSymbol is true.
1836
+ var textAfterIdx = line.substring(idx);
1837
+ var firstMatchedChar;
1838
+ if (noSymbol) {
1839
+ firstMatchedChar = textAfterIdx.search(/\w/);
1840
+ } else {
1841
+ firstMatchedChar = textAfterIdx.search(/\S/);
1842
+ }
1843
+ if (firstMatchedChar == -1) {
1844
+ return null;
1845
+ }
1846
+ idx += firstMatchedChar;
1847
+ textAfterIdx = line.substring(idx);
1848
+ var textBeforeIdx = line.substring(0, idx);
1849
+
1850
+ var matchRegex;
1851
+ // Greedy matchers for the "word" we are trying to expand.
1852
+ if (bigWord) {
1853
+ matchRegex = /^\S+/;
1854
+ } else {
1855
+ if ((/\w/).test(line.charAt(idx))) {
1856
+ matchRegex = /^\w+/;
1857
+ } else {
1858
+ matchRegex = /^[^\w\s]+/;
1859
+ }
1860
+ }
1861
+
1862
+ var wordAfterRegex = matchRegex.exec(textAfterIdx);
1863
+ var wordStart = idx;
1864
+ var wordEnd = idx + wordAfterRegex[0].length - 1;
1865
+ // TODO: Find a better way to do this. It will be slow on very long lines.
1866
+ var wordBeforeRegex = matchRegex.exec(reverse(textBeforeIdx));
1867
+ if (wordBeforeRegex) {
1868
+ wordStart -= wordBeforeRegex[0].length;
1869
+ }
1870
+
1871
+ if (inclusive) {
1872
+ wordEnd++;
1873
+ }
1874
+
1875
+ return { start: { line: cur.line, ch: wordStart },
1876
+ end: { line: cur.line, ch: wordEnd }};
1877
+ }
1878
+
1879
+ function recordLastCharacterSearch(increment, args) {
1880
+ var vimGlobalState = getVimGlobalState();
1881
+ vimGlobalState.lastChararacterSearch.increment = increment;
1882
+ vimGlobalState.lastChararacterSearch.forward = args.forward;
1883
+ vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter;
1884
+ }
1885
+
1886
+ var symbolToMode = {
1887
+ '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',
1888
+ '[': 'section', ']': 'section',
1889
+ '*': 'comment', '/': 'comment',
1890
+ 'm': 'method', 'M': 'method',
1891
+ '#': 'preprocess'
1892
+ };
1893
+ var findSymbolModes = {
1894
+ bracket: {
1895
+ isComplete: function(state) {
1896
+ if (state.nextCh === state.symb) {
1897
+ state.depth++;
1898
+ if(state.depth >= 1)return true;
1899
+ } else if (state.nextCh === state.reverseSymb) {
1900
+ state.depth--;
1901
+ }
1902
+ return false;
1903
+ }
1904
+ },
1905
+ section: {
1906
+ init: function(state) {
1907
+ state.curMoveThrough = true;
1908
+ state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';
1909
+ },
1910
+ isComplete: function(state) {
1911
+ return state.index === 0 && state.nextCh === state.symb;
1912
+ }
1913
+ },
1914
+ comment: {
1915
+ isComplete: function(state) {
1916
+ var found = state.lastCh === '*' && state.nextCh === '/';
1917
+ state.lastCh = state.nextCh;
1918
+ return found;
1919
+ }
1920
+ },
1921
+ // TODO: The original Vim implementation only operates on level 1 and 2.
1922
+ // The current implementation doesn't check for code block level and
1923
+ // therefore it operates on any levels.
1924
+ method: {
1925
+ init: function(state) {
1926
+ state.symb = (state.symb === 'm' ? '{' : '}');
1927
+ state.reverseSymb = state.symb === '{' ? '}' : '{';
1928
+ },
1929
+ isComplete: function(state) {
1930
+ if(state.nextCh === state.symb)return true;
1931
+ return false;
1932
+ }
1933
+ },
1934
+ preprocess: {
1935
+ init: function(state) {
1936
+ state.index = 0;
1937
+ },
1938
+ isComplete: function(state) {
1939
+ if (state.nextCh === '#') {
1940
+ var token = state.lineText.match(/#(\w+)/)[1];
1941
+ if (token === 'endif') {
1942
+ if (state.forward && state.depth === 0) {
1943
+ return true;
1944
+ }
1945
+ state.depth++;
1946
+ } else if (token === 'if') {
1947
+ if (!state.forward && state.depth === 0) {
1948
+ return true;
1949
+ }
1950
+ state.depth--;
1951
+ }
1952
+ if(token === 'else' && state.depth === 0)return true;
1953
+ }
1954
+ return false;
1955
+ }
1956
+ }
1957
+ };
1958
+ function findSymbol(cm, repeat, forward, symb) {
1959
+ var cur = cm.getCursor();
1960
+ var increment = forward ? 1 : -1;
1961
+ var endLine = forward ? cm.lineCount() : -1;
1962
+ var curCh = cur.ch;
1963
+ var line = cur.line;
1964
+ var lineText = cm.getLine(line);
1965
+ var state = {
1966
+ lineText: lineText,
1967
+ nextCh: lineText.charAt(curCh),
1968
+ lastCh: null,
1969
+ index: curCh,
1970
+ symb: symb,
1971
+ reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],
1972
+ forward: forward,
1973
+ depth: 0,
1974
+ curMoveThrough: false
1975
+ };
1976
+ var mode = symbolToMode[symb];
1977
+ if(!mode)return cur;
1978
+ var init = findSymbolModes[mode].init;
1979
+ var isComplete = findSymbolModes[mode].isComplete;
1980
+ if(init)init(state);
1981
+ while (line !== endLine && repeat) {
1982
+ state.index += increment;
1983
+ state.nextCh = state.lineText.charAt(state.index);
1984
+ if (!state.nextCh) {
1985
+ line += increment;
1986
+ state.lineText = cm.getLine(line) || '';
1987
+ if (increment > 0) {
1988
+ state.index = 0;
1989
+ } else {
1990
+ var lineLen = state.lineText.length;
1991
+ state.index = (lineLen > 0) ? (lineLen-1) : 0;
1992
+ }
1993
+ state.nextCh = state.lineText.charAt(state.index);
1994
+ }
1995
+ if (isComplete(state)) {
1996
+ cur.line = line;
1997
+ cur.ch = state.index;
1998
+ repeat--;
1999
+ }
2000
+ }
2001
+ if (state.nextCh || state.curMoveThrough) {
2002
+ return { line: line, ch: state.index };
2003
+ }
2004
+ return cur;
2005
+ }
2006
+
2007
+ /*
2008
+ * Returns the boundaries of the next word. If the cursor in the middle of
2009
+ * the word, then returns the boundaries of the current word, starting at
2010
+ * the cursor. If the cursor is at the start/end of a word, and we are going
2011
+ * forward/backward, respectively, find the boundaries of the next word.
2012
+ *
2013
+ * @param {CodeMirror} cm CodeMirror object.
2014
+ * @param {Cursor} cur The cursor position.
2015
+ * @param {boolean} forward True to search forward. False to search
2016
+ * backward.
2017
+ * @param {boolean} bigWord True if punctuation count as part of the word.
2018
+ * False if only [a-zA-Z0-9] characters count as part of the word.
2019
+ * @return {Object{from:number, to:number, line: number}} The boundaries of
2020
+ * the word, or null if there are no more words.
2021
+ */
2022
+ // TODO: Treat empty lines (with no whitespace) as words.
2023
+ function findWord(cm, cur, forward, bigWord) {
2024
+ var lineNum = cur.line;
2025
+ var pos = cur.ch;
2026
+ var line = cm.getLine(lineNum);
2027
+ var dir = forward ? 1 : -1;
2028
+ var regexps = bigWord ? bigWordRegexp : wordRegexp;
2029
+
2030
+ while (true) {
2031
+ var stop = (dir > 0) ? line.length : -1;
2032
+ var wordStart = stop, wordEnd = stop;
2033
+ // Find bounds of next word.
2034
+ while (pos != stop) {
2035
+ var foundWord = false;
2036
+ for (var i = 0; i < regexps.length && !foundWord; ++i) {
2037
+ if (regexps[i].test(line.charAt(pos))) {
2038
+ wordStart = pos;
2039
+ // Advance to end of word.
2040
+ while (pos != stop && regexps[i].test(line.charAt(pos))) {
2041
+ pos += dir;
2042
+ }
2043
+ wordEnd = pos;
2044
+ foundWord = wordStart != wordEnd;
2045
+ if (wordStart == cur.ch && lineNum == cur.line &&
2046
+ wordEnd == wordStart + dir) {
2047
+ // We started at the end of a word. Find the next one.
2048
+ continue;
2049
+ } else {
2050
+ return {
2051
+ from: Math.min(wordStart, wordEnd + 1),
2052
+ to: Math.max(wordStart, wordEnd),
2053
+ line: lineNum };
2054
+ }
2055
+ }
2056
+ }
2057
+ if (!foundWord) {
2058
+ pos += dir;
2059
+ }
2060
+ }
2061
+ // Advance to next/prev line.
2062
+ lineNum += dir;
2063
+ if (!isLine(cm, lineNum)) {
2064
+ return null;
2065
+ }
2066
+ line = cm.getLine(lineNum);
2067
+ pos = (dir > 0) ? 0 : line.length;
2068
+ }
2069
+ // Should never get here.
2070
+ throw 'The impossible happened.';
2071
+ }
2072
+
2073
+ /**
2074
+ * @param {CodeMirror} cm CodeMirror object.
2075
+ * @param {int} repeat Number of words to move past.
2076
+ * @param {boolean} forward True to search forward. False to search
2077
+ * backward.
2078
+ * @param {boolean} wordEnd True to move to end of word. False to move to
2079
+ * beginning of word.
2080
+ * @param {boolean} bigWord True if punctuation count as part of the word.
2081
+ * False if only alphabet characters count as part of the word.
2082
+ * @return {Cursor} The position the cursor should move to.
2083
+ */
2084
+ function moveToWord(cm, repeat, forward, wordEnd, bigWord) {
2085
+ var cur = cm.getCursor();
2086
+ for (var i = 0; i < repeat; i++) {
2087
+ var startCh = cur.ch, startLine = cur.line, word;
2088
+ var movedToNextWord = false;
2089
+ while (!movedToNextWord) {
2090
+ // Search and advance.
2091
+ word = findWord(cm, cur, forward, bigWord);
2092
+ movedToNextWord = true;
2093
+ if (word) {
2094
+ // Move to the word we just found. If by moving to the word we end
2095
+ // up in the same spot, then move an extra character and search
2096
+ // again.
2097
+ cur.line = word.line;
2098
+ if (forward && wordEnd) {
2099
+ // 'e'
2100
+ cur.ch = word.to - 1;
2101
+ } else if (forward && !wordEnd) {
2102
+ // 'w'
2103
+ if (inRangeInclusive(cur.ch, word.from, word.to) &&
2104
+ word.line == startLine) {
2105
+ // Still on the same word. Go to the next one.
2106
+ movedToNextWord = false;
2107
+ cur.ch = word.to - 1;
2108
+ } else {
2109
+ cur.ch = word.from;
2110
+ }
2111
+ } else if (!forward && wordEnd) {
2112
+ // 'ge'
2113
+ if (inRangeInclusive(cur.ch, word.from, word.to) &&
2114
+ word.line == startLine) {
2115
+ // still on the same word. Go to the next one.
2116
+ movedToNextWord = false;
2117
+ cur.ch = word.from;
2118
+ } else {
2119
+ cur.ch = word.to;
2120
+ }
2121
+ } else if (!forward && !wordEnd) {
2122
+ // 'b'
2123
+ cur.ch = word.from;
2124
+ }
2125
+ } else {
2126
+ // No more words to be found. Move to the end.
2127
+ if (forward) {
2128
+ return { line: cur.line, ch: lineLength(cm, cur.line) };
2129
+ } else {
2130
+ return { line: cur.line, ch: 0 };
2131
+ }
2132
+ }
2133
+ }
2134
+ }
2135
+ return cur;
2136
+ }
2137
+
2138
+ function moveToCharacter(cm, repeat, forward, character) {
2139
+ var cur = cm.getCursor();
2140
+ var start = cur.ch;
2141
+ var idx;
2142
+ for (var i = 0; i < repeat; i ++) {
2143
+ var line = cm.getLine(cur.line);
2144
+ idx = charIdxInLine(start, line, character, forward, true);
2145
+ if (idx == -1) {
2146
+ return null;
2147
+ }
2148
+ start = idx;
2149
+ }
2150
+ return { line: cm.getCursor().line, ch: idx };
2151
+ }
2152
+
2153
+ function moveToColumn(cm, repeat) {
2154
+ // repeat is always >= 1, so repeat - 1 always corresponds
2155
+ // to the column we want to go to.
2156
+ var line = cm.getCursor().line;
2157
+ return clipCursorToContent(cm, { line: line, ch: repeat - 1 });
2158
+ }
2159
+
2160
+ function updateMark(cm, vim, markName, pos) {
2161
+ if (!inArray(markName, validMarks)) {
2162
+ return;
2163
+ }
2164
+ if (vim.marks[markName]) {
2165
+ vim.marks[markName].clear();
2166
+ }
2167
+ vim.marks[markName] = cm.setBookmark(pos);
2168
+ }
2169
+
2170
+ function charIdxInLine(start, line, character, forward, includeChar) {
2171
+ // Search for char in line.
2172
+ // motion_options: {forward, includeChar}
2173
+ // If includeChar = true, include it too.
2174
+ // If forward = true, search forward, else search backwards.
2175
+ // If char is not found on this line, do nothing
2176
+ var idx;
2177
+ if (forward) {
2178
+ idx = line.indexOf(character, start + 1);
2179
+ if (idx != -1 && !includeChar) {
2180
+ idx -= 1;
2181
+ }
2182
+ } else {
2183
+ idx = line.lastIndexOf(character, start - 1);
2184
+ if (idx != -1 && !includeChar) {
2185
+ idx += 1;
2186
+ }
2187
+ }
2188
+ return idx;
2189
+ }
2190
+
2191
+ function findMatchedSymbol(cm, cur, symb) {
2192
+ var line = cur.line;
2193
+ symb = symb ? symb : cm.getLine(line).charAt(cur.ch);
2194
+
2195
+ var reverseSymb = ({
2196
+ '(': ')', ')': '(',
2197
+ '[': ']', ']': '[',
2198
+ '{': '}', '}': '{'})[symb];
2199
+
2200
+ // Couldn't find a matching symbol, abort
2201
+ if (!reverseSymb) {
2202
+ return cur;
2203
+ }
2204
+
2205
+ // set our increment to move forward (+1) or backwards (-1)
2206
+ // depending on which bracket we're matching
2207
+ var increment = ({'(': 1, '{': 1, '[': 1})[symb] || -1;
2208
+ var endLine = increment === 1 ? cm.lineCount() : -1;
2209
+ var depth = 1, nextCh = symb, index = cur.ch, lineText = cm.getLine(line);
2210
+ // Simple search for closing paren--just count openings and closings till
2211
+ // we find our match
2212
+ // TODO: use info from CodeMirror to ignore closing brackets in comments
2213
+ // and quotes, etc.
2214
+ while (line !== endLine && depth > 0) {
2215
+ index += increment;
2216
+ nextCh = lineText.charAt(index);
2217
+ if (!nextCh) {
2218
+ line += increment;
2219
+ lineText = cm.getLine(line) || '';
2220
+ if (increment > 0) {
2221
+ index = 0;
2222
+ } else {
2223
+ var lineLen = lineText.length;
2224
+ index = (lineLen > 0) ? (lineLen-1) : 0;
2225
+ }
2226
+ nextCh = lineText.charAt(index);
2227
+ }
2228
+ if (nextCh === symb) {
2229
+ depth++;
2230
+ } else if (nextCh === reverseSymb) {
2231
+ depth--;
2232
+ }
2233
+ }
2234
+
2235
+ if (nextCh) {
2236
+ return { line: line, ch: index };
2237
+ }
2238
+ return cur;
2239
+ }
2240
+
2241
+ function selectCompanionObject(cm, revSymb, inclusive) {
2242
+ var cur = cm.getCursor();
2243
+
2244
+ var end = findMatchedSymbol(cm, cur, revSymb);
2245
+ var start = findMatchedSymbol(cm, end);
2246
+ start.ch += inclusive ? 1 : 0;
2247
+ end.ch += inclusive ? 0 : 1;
2248
+
2249
+ return { start: start, end: end };
2250
+ }
2251
+
2252
+ function regexLastIndexOf(string, pattern, startIndex) {
2253
+ for (var i = !startIndex ? string.length : startIndex;
2254
+ i >= 0; --i) {
2255
+ if (pattern.test(string.charAt(i))) {
2256
+ return i;
2257
+ }
2258
+ }
2259
+ return -1;
2260
+ }
2261
+
2262
+ // Takes in a symbol and a cursor and tries to simulate text objects that
2263
+ // have identical opening and closing symbols
2264
+ // TODO support across multiple lines
2265
+ function findBeginningAndEnd(cm, symb, inclusive) {
2266
+ var cur = cm.getCursor();
2267
+ var line = cm.getLine(cur.line);
2268
+ var chars = line.split('');
2269
+ var start, end, i, len;
2270
+ var firstIndex = chars.indexOf(symb);
2271
+
2272
+ // the decision tree is to always look backwards for the beginning first,
2273
+ // but if the cursor is in front of the first instance of the symb,
2274
+ // then move the cursor forward
2275
+ if (cur.ch < firstIndex) {
2276
+ cur.ch = firstIndex;
2277
+ // Why is this line even here???
2278
+ // cm.setCursor(cur.line, firstIndex+1);
2279
+ }
2280
+ // otherwise if the cursor is currently on the closing symbol
2281
+ else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
2282
+ end = cur.ch; // assign end to the current cursor
2283
+ --cur.ch; // make sure to look backwards
2284
+ }
2285
+
2286
+ // if we're currently on the symbol, we've got a start
2287
+ if (chars[cur.ch] == symb && !end) {
2288
+ start = cur.ch + 1; // assign start to ahead of the cursor
2289
+ } else {
2290
+ // go backwards to find the start
2291
+ for (i = cur.ch; i > -1 && !start; i--) {
2292
+ if (chars[i] == symb) {
2293
+ start = i + 1;
2294
+ }
2295
+ }
2296
+ }
2297
+
2298
+ // look forwards for the end symbol
2299
+ if (start && !end) {
2300
+ for (i = start, len = chars.length; i < len && !end; i++) {
2301
+ if (chars[i] == symb) {
2302
+ end = i;
2303
+ }
2304
+ }
2305
+ }
2306
+
2307
+ // nothing found
2308
+ if (!start || !end) {
2309
+ return { start: cur, end: cur };
2310
+ }
2311
+
2312
+ // include the symbols
2313
+ if (inclusive) {
2314
+ --start; ++end;
2315
+ }
2316
+
2317
+ return {
2318
+ start: { line: cur.line, ch: start },
2319
+ end: { line: cur.line, ch: end }
2320
+ };
2321
+ }
2322
+
2323
+ // Search functions
2324
+ function SearchState() {}
2325
+ SearchState.prototype = {
2326
+ getQuery: function() {
2327
+ return getVimGlobalState().query;
2328
+ },
2329
+ setQuery: function(query) {
2330
+ getVimGlobalState().query = query;
2331
+ },
2332
+ getOverlay: function() {
2333
+ return this.searchOverlay;
2334
+ },
2335
+ setOverlay: function(overlay) {
2336
+ this.searchOverlay = overlay;
2337
+ },
2338
+ isReversed: function() {
2339
+ return getVimGlobalState().isReversed;
2340
+ },
2341
+ setReversed: function(reversed) {
2342
+ getVimGlobalState().isReversed = reversed;
2343
+ }
2344
+ };
2345
+ function getSearchState(cm) {
2346
+ var vim = getVimState(cm);
2347
+ return vim.searchState_ || (vim.searchState_ = new SearchState());
2348
+ }
2349
+ function dialog(cm, template, shortText, onClose, options) {
2350
+ if (cm.openDialog) {
2351
+ cm.openDialog(template, onClose, { bottom: true, value: options.value,
2352
+ onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp });
2353
+ }
2354
+ else {
2355
+ callback(prompt(shortText, ""));
2356
+ }
2357
+ }
2358
+ function findUnescapedSlashes(str) {
2359
+ var escapeNextChar = false;
2360
+ var slashes = [];
2361
+ for (var i = 0; i < str.length; i++) {
2362
+ var c = str.charAt(i);
2363
+ if (!escapeNextChar && c == '/') {
2364
+ slashes.push(i);
2365
+ }
2366
+ escapeNextChar = (c == '\\');
2367
+ }
2368
+ return slashes;
2369
+ }
2370
+ /**
2371
+ * Extract the regular expression from the query and return a Regexp object.
2372
+ * Returns null if the query is blank.
2373
+ * If ignoreCase is passed in, the Regexp object will have the 'i' flag set.
2374
+ * If smartCase is passed in, and the query contains upper case letters,
2375
+ * then ignoreCase is overridden, and the 'i' flag will not be set.
2376
+ * If the query contains the /i in the flag part of the regular expression,
2377
+ * then both ignoreCase and smartCase are ignored, and 'i' will be passed
2378
+ * through to the Regex object.
2379
+ */
2380
+ function parseQuery(cm, query, ignoreCase, smartCase) {
2381
+ // Check if the query is already a regex.
2382
+ if (query instanceof RegExp) { return query; }
2383
+ // First try to extract regex + flags from the input. If no flags found,
2384
+ // extract just the regex. IE does not accept flags directly defined in
2385
+ // the regex string in the form /regex/flags
2386
+ var slashes = findUnescapedSlashes(query);
2387
+ var regexPart;
2388
+ var forceIgnoreCase;
2389
+ if (!slashes.length) {
2390
+ // Query looks like 'regexp'
2391
+ regexPart = query;
2392
+ } else {
2393
+ // Query looks like 'regexp/...'
2394
+ regexPart = query.substring(0, slashes[0]);
2395
+ var flagsPart = query.substring(slashes[0]);
2396
+ forceIgnoreCase = (flagsPart.indexOf('i') != -1);
2397
+ }
2398
+ if (!regexPart) {
2399
+ return null;
2400
+ }
2401
+ if (smartCase) {
2402
+ ignoreCase = (/^[^A-Z]*$/).test(regexPart);
2403
+ }
2404
+ var regexp = new RegExp(regexPart,
2405
+ (ignoreCase || forceIgnoreCase) ? 'i' : undefined);
2406
+ return regexp;
2407
+ }
2408
+ function showConfirm(cm, text) {
2409
+ if (cm.openConfirm) {
2410
+ cm.openConfirm('<span style="color: red">' + text +
2411
+ '</span> <button type="button">OK</button>', function() {},
2412
+ {bottom: true});
2413
+ } else {
2414
+ alert(text);
2415
+ }
2416
+ }
2417
+ function makePrompt(prefix, desc) {
2418
+ var raw = '';
2419
+ if (prefix) {
2420
+ raw += '<span style="font-family: monospace">' + prefix + '</span>';
2421
+ }
2422
+ raw += '<input type="text"/> ' +
2423
+ '<span style="color: #888">';
2424
+ if (desc) {
2425
+ raw += '<span style="color: #888">';
2426
+ raw += desc;
2427
+ raw += '</span>';
2428
+ }
2429
+ return raw;
2430
+ }
2431
+ var searchPromptDesc = '(Javascript regexp)';
2432
+ function showPrompt(cm, options) {
2433
+ var shortText = (options.prefix || '') + ' ' + (options.desc || '');
2434
+ var prompt = makePrompt(options.prefix, options.desc);
2435
+ dialog(cm, prompt, shortText, options.onClose, options);
2436
+ }
2437
+ function regexEqual(r1, r2) {
2438
+ if (r1 instanceof RegExp && r2 instanceof RegExp) {
2439
+ var props = ["global", "multiline", "ignoreCase", "source"];
2440
+ for (var i = 0; i < props.length; i++) {
2441
+ var prop = props[i];
2442
+ if (r1[prop] !== r2[prop]) {
2443
+ return(false);
2444
+ }
2445
+ }
2446
+ return(true);
2447
+ }
2448
+ return(false);
2449
+ }
2450
+ // Returns true if the query is valid.
2451
+ function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
2452
+ if (!rawQuery) {
2453
+ return;
2454
+ }
2455
+ var state = getSearchState(cm);
2456
+ var query = parseQuery(cm, rawQuery, !!ignoreCase, !!smartCase);
2457
+ if (!query) {
2458
+ return;
2459
+ }
2460
+ highlightSearchMatches(cm, query);
2461
+ if (regexEqual(query, state.getQuery())) {
2462
+ return query;
2463
+ }
2464
+ state.setQuery(query);
2465
+ return query;
2466
+ }
2467
+ function searchOverlay(query) {
2468
+ if (query.source.charAt(0) == '^') {
2469
+ var matchSol = true;
2470
+ }
2471
+ return {
2472
+ token: function(stream) {
2473
+ if (matchSol && !stream.sol()) {
2474
+ stream.skipToEnd();
2475
+ return;
2476
+ }
2477
+ var match = stream.match(query, false);
2478
+ if (match) {
2479
+ if (match[0].length == 0) {
2480
+ // Matched empty string, skip to next.
2481
+ stream.next();
2482
+ return;
2483
+ }
2484
+ if (!stream.sol()) {
2485
+ // Backtrack 1 to match \b
2486
+ stream.backUp(1);
2487
+ if (!query.exec(stream.next() + match[0])) {
2488
+ stream.next();
2489
+ return null;
2490
+ }
2491
+ }
2492
+ stream.match(query);
2493
+ return "searching";
2494
+ }
2495
+ while (!stream.eol()) {
2496
+ stream.next();
2497
+ if (stream.match(query, false)) break;
2498
+ }
2499
+ },
2500
+ query: query
2501
+ };
2502
+ }
2503
+ function highlightSearchMatches(cm, query) {
2504
+ var overlay = getSearchState(cm).getOverlay();
2505
+ if (!overlay || query != overlay.query) {
2506
+ if (overlay) {
2507
+ cm.removeOverlay(overlay);
2508
+ }
2509
+ overlay = searchOverlay(query);
2510
+ cm.addOverlay(overlay);
2511
+ getSearchState(cm).setOverlay(overlay);
2512
+ }
2513
+ }
2514
+ function findNext(cm, prev, query, repeat) {
2515
+ if (repeat === undefined) { repeat = 1; }
2516
+ return cm.operation(function() {
2517
+ var pos = cm.getCursor();
2518
+ if (!prev) {
2519
+ pos.ch += 1;
2520
+ }
2521
+ var cursor = cm.getSearchCursor(query, pos);
2522
+ for (var i = 0; i < repeat; i++) {
2523
+ if (!cursor.find(prev)) {
2524
+ // SearchCursor may have returned null because it hit EOF, wrap
2525
+ // around and try again.
2526
+ cursor = cm.getSearchCursor(query,
2527
+ (prev) ? { line: cm.lastLine() } : {line: cm.firstLine(), ch: 0} );
2528
+ if (!cursor.find(prev)) {
2529
+ return;
2530
+ }
2531
+ }
2532
+ }
2533
+ return cursor.from();
2534
+ });}
2535
+ function clearSearchHighlight(cm) {
2536
+ cm.removeOverlay(getSearchState(cm).getOverlay());
2537
+ getSearchState(cm).setOverlay(null);
2538
+ }
2539
+ /**
2540
+ * Check if pos is in the specified range, INCLUSIVE.
2541
+ * Range can be specified with 1 or 2 arguments.
2542
+ * If the first range argument is an array, treat it as an array of line
2543
+ * numbers. Match pos against any of the lines.
2544
+ * If the first range argument is a number,
2545
+ * if there is only 1 range argument, check if pos has the same line
2546
+ * number
2547
+ * if there are 2 range arguments, then check if pos is in between the two
2548
+ * range arguments.
2549
+ */
2550
+ function isInRange(pos, start, end) {
2551
+ if (typeof pos != 'number') {
2552
+ // Assume it is a cursor position. Get the line number.
2553
+ pos = pos.line;
2554
+ }
2555
+ if (start instanceof Array) {
2556
+ return inArray(pos, start);
2557
+ } else {
2558
+ if (end) {
2559
+ return (pos >= start && pos <= end);
2560
+ } else {
2561
+ return pos == start;
2562
+ }
2563
+ }
2564
+ }
2565
+ function getUserVisibleLines(cm) {
2566
+ var scrollInfo = cm.getScrollInfo();
2567
+ var occludeTorleranceTop = 6;
2568
+ var occludeTorleranceBottom = 10;
2569
+ var from = cm.coordsChar({left:0, top: occludeTorleranceTop}, 'local');
2570
+ var bottomY = scrollInfo.clientHeight - occludeTorleranceBottom;
2571
+ var to = cm.coordsChar({left:0, top: bottomY}, 'local');
2572
+ return {top: from.line, bottom: to.line};
2573
+ }
2574
+
2575
+ // Ex command handling
2576
+ // Care must be taken when adding to the default Ex command map. For any
2577
+ // pair of commands that have a shared prefix, at least one of their
2578
+ // shortNames must not match the prefix of the other command.
2579
+ var defaultExCommandMap = [
2580
+ { name: 'map', type: 'builtIn' },
2581
+ { name: 'write', shortName: 'w', type: 'builtIn' },
2582
+ { name: 'undo', shortName: 'u', type: 'builtIn' },
2583
+ { name: 'redo', shortName: 'red', type: 'builtIn' },
2584
+ { name: 'substitute', shortName: 's', type: 'builtIn'},
2585
+ { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'},
2586
+ { name: 'delmarks', shortName: 'delm', type: 'builtin'}
2587
+ ];
2588
+ Vim.ExCommandDispatcher = function() {
2589
+ this.buildCommandMap_();
2590
+ };
2591
+ Vim.ExCommandDispatcher.prototype = {
2592
+ processCommand: function(cm, input) {
2593
+ var inputStream = new CodeMirror.StringStream(input);
2594
+ var params = {};
2595
+ params.input = input;
2596
+ try {
2597
+ this.parseInput_(cm, inputStream, params);
2598
+ } catch(e) {
2599
+ showConfirm(cm, e);
2600
+ return;
2601
+ }
2602
+ var commandName;
2603
+ if (!params.commandName) {
2604
+ // If only a line range is defined, move to the line.
2605
+ if (params.line !== undefined) {
2606
+ commandName = 'move';
2607
+ }
2608
+ } else {
2609
+ var command = this.matchCommand_(params.commandName);
2610
+ if (command) {
2611
+ commandName = command.name;
2612
+ this.parseCommandArgs_(inputStream, params, command);
2613
+ if (command.type == 'exToKey') {
2614
+ // Handle Ex to Key mapping.
2615
+ for (var i = 0; i < command.toKeys.length; i++) {
2616
+ vim.handleKey(cm, command.toKeys[i]);
2617
+ }
2618
+ return;
2619
+ } else if (command.type == 'exToEx') {
2620
+ // Handle Ex to Ex mapping.
2621
+ this.processCommand(cm, command.toInput);
2622
+ return;
2623
+ }
2624
+ }
2625
+ }
2626
+ if (!commandName) {
2627
+ showConfirm(cm, 'Not an editor command ":' + input + '"');
2628
+ return;
2629
+ }
2630
+ exCommands[commandName](cm, params);
2631
+ },
2632
+ parseInput_: function(cm, inputStream, result) {
2633
+ inputStream.eatWhile(':');
2634
+ // Parse range.
2635
+ if (inputStream.eat('%')) {
2636
+ result.line = cm.firstLine();
2637
+ result.lineEnd = cm.lastLine();
2638
+ } else {
2639
+ result.line = this.parseLineSpec_(cm, inputStream);
2640
+ if (result.line !== undefined && inputStream.eat(',')) {
2641
+ result.lineEnd = this.parseLineSpec_(cm, inputStream);
2642
+ }
2643
+ }
2644
+
2645
+ // Parse command name.
2646
+ var commandMatch = inputStream.match(/^(\w+)/);
2647
+ if (commandMatch) {
2648
+ result.commandName = commandMatch[1];
2649
+ } else {
2650
+ result.commandName = inputStream.match(/.*/)[0];
2651
+ }
2652
+
2653
+ return result;
2654
+ },
2655
+ parseLineSpec_: function(cm, inputStream) {
2656
+ var numberMatch = inputStream.match(/^(\d+)/);
2657
+ if (numberMatch) {
2658
+ return parseInt(numberMatch[1], 10) - 1;
2659
+ }
2660
+ switch (inputStream.next()) {
2661
+ case '.':
2662
+ return cm.getCursor().line;
2663
+ case '$':
2664
+ return cm.lastLine();
2665
+ case '\'':
2666
+ var mark = getVimState(cm).marks[inputStream.next()];
2667
+ if (mark && mark.find()) {
2668
+ return mark.find().line;
2669
+ } else {
2670
+ throw "Mark not set";
2671
+ }
2672
+ break;
2673
+ default:
2674
+ inputStream.backUp(1);
2675
+ return cm.getCursor().line;
2676
+ }
2677
+ },
2678
+ parseCommandArgs_: function(inputStream, params, command) {
2679
+ if (inputStream.eol()) {
2680
+ return;
2681
+ }
2682
+ params.argString = inputStream.match(/.*/)[0];
2683
+ // Parse command-line arguments
2684
+ var delim = command.argDelimiter || /\s+/;
2685
+ var args = trim(params.argString).split(delim);
2686
+ if (args.length && args[0]) {
2687
+ params.args = args;
2688
+ }
2689
+ },
2690
+ matchCommand_: function(commandName) {
2691
+ // Return the command in the command map that matches the shortest
2692
+ // prefix of the passed in command name. The match is guaranteed to be
2693
+ // unambiguous if the defaultExCommandMap's shortNames are set up
2694
+ // correctly. (see @code{defaultExCommandMap}).
2695
+ for (var i = commandName.length; i > 0; i--) {
2696
+ var prefix = commandName.substring(0, i);
2697
+ if (this.commandMap_[prefix]) {
2698
+ var command = this.commandMap_[prefix];
2699
+ if (command.name.indexOf(commandName) === 0) {
2700
+ return command;
2701
+ }
2702
+ }
2703
+ }
2704
+ return null;
2705
+ },
2706
+ buildCommandMap_: function() {
2707
+ this.commandMap_ = {};
2708
+ for (var i = 0; i < defaultExCommandMap.length; i++) {
2709
+ var command = defaultExCommandMap[i];
2710
+ var key = command.shortName || command.name;
2711
+ this.commandMap_[key] = command;
2712
+ }
2713
+ },
2714
+ map: function(lhs, rhs) {
2715
+ if (lhs != ':' && lhs.charAt(0) == ':') {
2716
+ var commandName = lhs.substring(1);
2717
+ if (rhs != ':' && rhs.charAt(0) == ':') {
2718
+ // Ex to Ex mapping
2719
+ this.commandMap_[commandName] = {
2720
+ name: commandName,
2721
+ type: 'exToEx',
2722
+ toInput: rhs.substring(1)
2723
+ };
2724
+ } else {
2725
+ // Ex to key mapping
2726
+ this.commandMap_[commandName] = {
2727
+ name: commandName,
2728
+ type: 'exToKey',
2729
+ toKeys: parseKeyString(rhs)
2730
+ };
2731
+ }
2732
+ } else {
2733
+ if (rhs != ':' && rhs.charAt(0) == ':') {
2734
+ // Key to Ex mapping.
2735
+ defaultKeymap.unshift({
2736
+ keys: parseKeyString(lhs),
2737
+ type: 'keyToEx',
2738
+ exArgs: { input: rhs.substring(1) }});
2739
+ } else {
2740
+ // Key to key mapping
2741
+ defaultKeymap.unshift({
2742
+ keys: parseKeyString(lhs),
2743
+ type: 'keyToKey',
2744
+ toKeys: parseKeyString(rhs)
2745
+ });
2746
+ }
2747
+ }
2748
+ }
2749
+ };
2750
+
2751
+ // Converts a key string sequence of the form a<C-w>bd<Left> into Vim's
2752
+ // keymap representation.
2753
+ function parseKeyString(str) {
2754
+ var idx = 0;
2755
+ var keys = [];
2756
+ while (idx < str.length) {
2757
+ if (str.charAt(idx) != '<') {
2758
+ keys.push(str.charAt(idx));
2759
+ idx++;
2760
+ continue;
2761
+ }
2762
+ // Vim key notation here means desktop Vim key-notation.
2763
+ // See :help key-notation in desktop Vim.
2764
+ var vimKeyNotationStart = ++idx;
2765
+ while (str.charAt(idx++) != '>') {}
2766
+ var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1);
2767
+ var mod='';
2768
+ var match = (/^C-(.+)$/).exec(vimKeyNotation);
2769
+ if (match) {
2770
+ mod='Ctrl-';
2771
+ vimKeyNotation=match[1];
2772
+ }
2773
+ var key;
2774
+ switch (vimKeyNotation) {
2775
+ case 'BS':
2776
+ key = 'Backspace';
2777
+ break;
2778
+ case 'CR':
2779
+ key = 'Enter';
2780
+ break;
2781
+ case 'Del':
2782
+ key = 'Delete';
2783
+ break;
2784
+ default:
2785
+ key = vimKeyNotation;
2786
+ break;
2787
+ }
2788
+ keys.push(mod + key);
2789
+ }
2790
+ return keys;
2791
+ }
2792
+
2793
+ var exCommands = {
2794
+ map: function(cm, params) {
2795
+ var mapArgs = params.args;
2796
+ if (!mapArgs || mapArgs.length < 2) {
2797
+ if (cm) {
2798
+ showConfirm(cm, 'Invalid mapping: ' + params.input);
2799
+ }
2800
+ return;
2801
+ }
2802
+ exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm);
2803
+ },
2804
+ move: function(cm, params) {
2805
+ commandDispatcher.processCommand(cm, getVimState(cm), {
2806
+ type: 'motion',
2807
+ motion: 'moveToLineOrEdgeOfDocument',
2808
+ motionArgs: { forward: false, explicitRepeat: true,
2809
+ linewise: true },
2810
+ repeatOverride: params.line+1});
2811
+ },
2812
+ substitute: function(cm, params) {
2813
+ var argString = params.argString;
2814
+ var slashes = findUnescapedSlashes(argString);
2815
+ if (slashes[0] !== 0) {
2816
+ showConfirm(cm, 'Substitutions should be of the form ' +
2817
+ ':s/pattern/replace/');
2818
+ return;
2819
+ }
2820
+ var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
2821
+ var replacePart = '';
2822
+ var flagsPart;
2823
+ var count;
2824
+ if (slashes[1]) {
2825
+ replacePart = argString.substring(slashes[1] + 1, slashes[2]);
2826
+ }
2827
+ if (slashes[2]) {
2828
+ // After the 3rd slash, we can have flags followed by a space followed
2829
+ // by count.
2830
+ var trailing = argString.substring(slashes[2] + 1).split(' ');
2831
+ flagsPart = trailing[0];
2832
+ count = parseInt(trailing[1]);
2833
+ }
2834
+ if (flagsPart) {
2835
+ regexPart = regexPart + '/' + flagsPart;
2836
+ }
2837
+ if (regexPart) {
2838
+ // If regex part is empty, then use the previous query. Otherwise use
2839
+ // the regex part as the new query.
2840
+ try {
2841
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
2842
+ true /** smartCase */);
2843
+ } catch (e) {
2844
+ showConfirm(cm, 'Invalid regex: ' + regexPart);
2845
+ return;
2846
+ }
2847
+ }
2848
+ var state = getSearchState(cm);
2849
+ var query = state.getQuery();
2850
+ var lineStart = params.line || cm.firstLine();
2851
+ var lineEnd = params.lineEnd || lineStart;
2852
+ if (count) {
2853
+ lineStart = lineEnd;
2854
+ lineEnd = lineStart + count - 1;
2855
+ }
2856
+ var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
2857
+ function doReplace() {
2858
+ for (var cursor = cm.getSearchCursor(query, startPos);
2859
+ cursor.findNext() &&
2860
+ isInRange(cursor.from(), lineStart, lineEnd);) {
2861
+ var text = cm.getRange(cursor.from(), cursor.to());
2862
+ var newText = text.replace(query, replacePart);
2863
+ cursor.replace(newText);
2864
+ }
2865
+ var vim = getVimState(cm);
2866
+ if (vim.visualMode) {
2867
+ exitVisualMode(cm, vim);
2868
+ }
2869
+ }
2870
+ cm.operation(doReplace);
2871
+ },
2872
+ redo: CodeMirror.commands.redo,
2873
+ undo: CodeMirror.commands.undo,
2874
+ write: function(cm) {
2875
+ if (CodeMirror.commands.save) {
2876
+ // If a save command is defined, call it.
2877
+ CodeMirror.commands.save(cm);
2878
+ } else {
2879
+ // Saves to text area if no save command is defined.
2880
+ cm.save();
2881
+ }
2882
+ },
2883
+ nohlsearch: function(cm) {
2884
+ clearSearchHighlight(cm);
2885
+ },
2886
+ delmarks: function(cm, params) {
2887
+ if (!params.argString || !params.argString.trim()) {
2888
+ showConfirm(cm, 'Argument required');
2889
+ return;
2890
+ }
2891
+
2892
+ var state = getVimState(cm);
2893
+ var stream = new CodeMirror.StringStream(params.argString.trim());
2894
+ while (!stream.eol()) {
2895
+ stream.eatSpace();
2896
+
2897
+ // Record the streams position at the beginning of the loop for use
2898
+ // in error messages.
2899
+ var count = stream.pos;
2900
+
2901
+ if (!stream.match(/[a-zA-Z]/, false)) {
2902
+ showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
2903
+ return;
2904
+ }
2905
+
2906
+ var sym = stream.next();
2907
+ // Check if this symbol is part of a range
2908
+ if (stream.match('-', true)) {
2909
+ // This symbol is part of a range.
2910
+
2911
+ // The range must terminate at an alphabetic character.
2912
+ if (!stream.match(/[a-zA-Z]/, false)) {
2913
+ showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
2914
+ return;
2915
+ }
2916
+
2917
+ var startMark = sym;
2918
+ var finishMark = stream.next();
2919
+ // The range must terminate at an alphabetic character which
2920
+ // shares the same case as the start of the range.
2921
+ if (isLowerCase(startMark) && isLowerCase(finishMark) ||
2922
+ isUpperCase(startMark) && isUpperCase(finishMark)) {
2923
+ var start = startMark.charCodeAt(0);
2924
+ var finish = finishMark.charCodeAt(0);
2925
+ if (start >= finish) {
2926
+ showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
2927
+ return;
2928
+ }
2929
+
2930
+ // Because marks are always ASCII values, and we have
2931
+ // determined that they are the same case, we can use
2932
+ // their char codes to iterate through the defined range.
2933
+ for (var j = 0; j <= finish - start; j++) {
2934
+ var mark = String.fromCharCode(start + j);
2935
+ delete state.marks[mark];
2936
+ }
2937
+ } else {
2938
+ showConfirm(cm, 'Invalid argument: ' + startMark + "-");
2939
+ return;
2940
+ }
2941
+ } else {
2942
+ // This symbol is a valid mark, and is not part of a range.
2943
+ delete state.marks[sym];
2944
+ }
2945
+ }
2946
+ }
2947
+ };
2948
+
2949
+ var exCommandDispatcher = new Vim.ExCommandDispatcher();
2950
+
2951
+ // Register Vim with CodeMirror
2952
+ function buildVimKeyMap() {
2953
+ /**
2954
+ * Handle the raw key event from CodeMirror. Translate the
2955
+ * Shift + key modifier to the resulting letter, while preserving other
2956
+ * modifers.
2957
+ */
2958
+ // TODO: Figure out a way to catch capslock.
2959
+ function handleKeyEvent_(cm, key, modifier) {
2960
+ if (isUpperCase(key)) {
2961
+ // Convert to lower case if shift is not the modifier since the key
2962
+ // we get from CodeMirror is always upper case.
2963
+ if (modifier == 'Shift') {
2964
+ modifier = null;
2965
+ }
2966
+ else {
2967
+ key = key.toLowerCase();
2968
+ }
2969
+ }
2970
+ if (modifier) {
2971
+ // Vim will parse modifier+key combination as a single key.
2972
+ key = modifier + '-' + key;
2973
+ }
2974
+ vim.handleKey(cm, key);
2975
+ }
2976
+
2977
+ // Closure to bind CodeMirror, key, modifier.
2978
+ function keyMapper(key, modifier) {
2979
+ return function(cm) {
2980
+ handleKeyEvent_(cm, key, modifier);
2981
+ };
2982
+ }
2983
+
2984
+ var modifiers = ['Shift', 'Ctrl'];
2985
+ var keyMap = {
2986
+ 'nofallthrough': true,
2987
+ 'style': 'fat-cursor'
2988
+ };
2989
+ function bindKeys(keys, modifier) {
2990
+ for (var i = 0; i < keys.length; i++) {
2991
+ var key = keys[i];
2992
+ if (!modifier && inArray(key, specialSymbols)) {
2993
+ // Wrap special symbols with '' because that's how CodeMirror binds
2994
+ // them.
2995
+ key = "'" + key + "'";
2996
+ }
2997
+ if (modifier) {
2998
+ keyMap[modifier + '-' + key] = keyMapper(keys[i], modifier);
2999
+ } else {
3000
+ keyMap[key] = keyMapper(keys[i]);
3001
+ }
3002
+ }
3003
+ }
3004
+ bindKeys(upperCaseAlphabet);
3005
+ bindKeys(upperCaseAlphabet, 'Shift');
3006
+ bindKeys(upperCaseAlphabet, 'Ctrl');
3007
+ bindKeys(specialSymbols);
3008
+ bindKeys(specialSymbols, 'Ctrl');
3009
+ bindKeys(numbers);
3010
+ bindKeys(numbers, 'Ctrl');
3011
+ bindKeys(specialKeys);
3012
+ bindKeys(specialKeys, 'Ctrl');
3013
+ return keyMap;
3014
+ }
3015
+ CodeMirror.keyMap.vim = buildVimKeyMap();
3016
+
3017
+ function exitInsertMode(cm) {
3018
+ cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
3019
+ cm.setOption('keyMap', 'vim');
3020
+ }
3021
+
3022
+ CodeMirror.keyMap['vim-insert'] = {
3023
+ // TODO: override navigation keys so that Esc will cancel automatic
3024
+ // indentation from o, O, i_<CR>
3025
+ 'Esc': exitInsertMode,
3026
+ 'Ctrl-[': exitInsertMode,
3027
+ 'Ctrl-C': exitInsertMode,
3028
+ 'Ctrl-N': 'autocomplete',
3029
+ 'Ctrl-P': 'autocomplete',
3030
+ 'Enter': function(cm) {
3031
+ var fn = CodeMirror.commands.newlineAndIndentContinueComment ||
3032
+ CodeMirror.commands.newlineAndIndent;
3033
+ fn(cm);
3034
+ },
3035
+ fallthrough: ['default']
3036
+ };
3037
+
3038
+ return vimApi;
3039
+ };
3040
+ // Initialize Vim and make it available as an API.
3041
+ var vim = Vim();
3042
+ CodeMirror.Vim = vim;
3043
+ }
3044
+ )();