vim.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Distributed under the BSD license:
  3. *
  4. * Copyright (c) 2010, Ajax.org B.V.
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of Ajax.org B.V. nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * ***** END LICENSE BLOCK ***** */
  30. define(function(require, exports, module) {
  31. "use strict";
  32. var cmds = require("./vim/commands");
  33. var coreCommands = cmds.coreCommands;
  34. var util = require("./vim/maps/util");
  35. var useragent = require("../lib/useragent");
  36. var startCommands = {
  37. "i": {
  38. command: coreCommands.start
  39. },
  40. "I": {
  41. command: coreCommands.startBeginning
  42. },
  43. "a": {
  44. command: coreCommands.append
  45. },
  46. "A": {
  47. command: coreCommands.appendEnd
  48. },
  49. "ctrl-f": {
  50. command: "gotopagedown"
  51. },
  52. "ctrl-b": {
  53. command: "gotopageup"
  54. }
  55. };
  56. exports.handler = {
  57. $id: "ace/keyboard/vim",
  58. // workaround for j not repeating with `defaults write -g ApplePressAndHoldEnabled -bool true`
  59. handleMacRepeat: function(data, hashId, key) {
  60. if (hashId == -1) {
  61. // record key
  62. data.inputChar = key;
  63. data.lastEvent = "input";
  64. } else if (data.inputChar && data.$lastHash == hashId && data.$lastKey == key) {
  65. // check for repeated keypress
  66. if (data.lastEvent == "input") {
  67. data.lastEvent = "input1";
  68. } else if (data.lastEvent == "input1") {
  69. // simulate textinput
  70. return true;
  71. }
  72. } else {
  73. // reset
  74. data.$lastHash = hashId;
  75. data.$lastKey = key;
  76. data.lastEvent = "keypress";
  77. }
  78. },
  79. // on mac, with some keyboard layouts (e.g swedish) ^ starts composition, we don't need it in normal mode
  80. updateMacCompositionHandlers: function(editor, enable) {
  81. var onCompositionUpdateOverride = function(text) {
  82. if (util.currentMode !== "insert") {
  83. var el = this.textInput.getElement();
  84. el.blur();
  85. el.focus();
  86. el.value = text;
  87. } else {
  88. this.onCompositionUpdateOrig(text);
  89. }
  90. };
  91. var onCompositionStartOverride = function(text) {
  92. if (util.currentMode === "insert") {
  93. this.onCompositionStartOrig(text);
  94. }
  95. }
  96. if (enable) {
  97. if (!editor.onCompositionUpdateOrig) {
  98. editor.onCompositionUpdateOrig = editor.onCompositionUpdate;
  99. editor.onCompositionUpdate = onCompositionUpdateOverride;
  100. editor.onCompositionStartOrig = editor.onCompositionStart;
  101. editor.onCompositionStart = onCompositionStartOverride;
  102. }
  103. } else {
  104. if (editor.onCompositionUpdateOrig) {
  105. editor.onCompositionUpdate = editor.onCompositionUpdateOrig;
  106. editor.onCompositionUpdateOrig = null;
  107. editor.onCompositionStart = editor.onCompositionStartOrig;
  108. editor.onCompositionStartOrig = null;
  109. }
  110. }
  111. },
  112. handleKeyboard: function(data, hashId, key, keyCode, e) {
  113. // ignore command keys (shift, ctrl etc.)
  114. if (hashId != 0 && (key == "" || key == "\x00"))
  115. return null;
  116. var editor = data.editor;
  117. if (hashId == 1)
  118. key = "ctrl-" + key;
  119. if (key == "ctrl-c") {
  120. if (!useragent.isMac && editor.getCopyText()) {
  121. editor.once("copy", function() {
  122. if (data.state == "start")
  123. coreCommands.stop.exec(editor);
  124. else
  125. editor.selection.clearSelection();
  126. });
  127. return {command: "null", passEvent: true};
  128. }
  129. return {command: coreCommands.stop};
  130. } else if ((key == "esc" && hashId == 0) || key == "ctrl-[") {
  131. return {command: coreCommands.stop};
  132. } else if (data.state == "start") {
  133. if (useragent.isMac && this.handleMacRepeat(data, hashId, key)) {
  134. hashId = -1;
  135. key = data.inputChar;
  136. }
  137. if (hashId == -1 || hashId == 1 || hashId == 0 && key.length > 1) {
  138. if (cmds.inputBuffer.idle && startCommands[key])
  139. return startCommands[key];
  140. cmds.inputBuffer.push(editor, key);
  141. return {command: "null", passEvent: false};
  142. } // if no modifier || shift: wait for input.
  143. else if (key.length == 1 && (hashId == 0 || hashId == 4)) {
  144. return {command: "null", passEvent: true};
  145. } else if (key == "esc" && hashId == 0) {
  146. return {command: coreCommands.stop};
  147. }
  148. } else {
  149. if (key == "ctrl-w") {
  150. return {command: "removewordleft"};
  151. }
  152. }
  153. },
  154. attach: function(editor) {
  155. editor.on("click", exports.onCursorMove);
  156. if (util.currentMode !== "insert")
  157. cmds.coreCommands.stop.exec(editor);
  158. editor.$vimModeHandler = this;
  159. this.updateMacCompositionHandlers(editor, true);
  160. },
  161. detach: function(editor) {
  162. editor.removeListener("click", exports.onCursorMove);
  163. util.noMode(editor);
  164. util.currentMode = "normal";
  165. this.updateMacCompositionHandlers(editor, false);
  166. },
  167. actions: cmds.actions,
  168. getStatusText: function() {
  169. if (util.currentMode == "insert")
  170. return "INSERT";
  171. if (util.onVisualMode)
  172. return (util.onVisualLineMode ? "VISUAL LINE " : "VISUAL ") + cmds.inputBuffer.status;
  173. return cmds.inputBuffer.status;
  174. }
  175. };
  176. exports.onCursorMove = function(e) {
  177. cmds.onCursorMove(e.editor, e);
  178. exports.onCursorMove.scheduled = false;
  179. };
  180. });