diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 04683485..adc47c00 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -92,6 +92,12 @@ var cmdLine = new layout.singleLineEditor(consoleEl); cmdLine.editor = env.editor; env.editor.cmdLine = cmdLine; +env.editor.showCommandLine = function(val) { + this.cmdLine.focus(); + if (typeof val == "string") + this.cmdLine.setValue(val, 1); +}; + /** * This demonstrates how you can define commands and bind shortcuts to them. */ diff --git a/lib/ace/document.js b/lib/ace/document.js index fb3734a2..a60ed0e9 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -247,7 +247,8 @@ var Document = function(text) { if (position.row >= length) { position.row = Math.max(0, length - 1); position.column = this.getLine(length-1).length; - } + } else if (position.row < 0) + position.row = 0; return position; }; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 33c3d447..92a10add 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -103,7 +103,9 @@ var Editor = function(renderer, session) { * **/ this.setKeyboardHandler = function(keyboardHandler) { - if (typeof keyboardHandler == "string" && keyboardHandler) { + if (!keyboardHandler) { + this.keyBinding.setKeyboardHandler(null); + } else if (typeof keyboardHandler == "string") { this.$keybindingId = keyboardHandler; var _self = this; config.loadModule(["keybinding", keyboardHandler], function(module) { @@ -1279,16 +1281,15 @@ var Editor = function(renderer, session) { _numberRx.lastIndex = 0 var s = this.session.getLine(row) - while(_numberRx.lastIndex < column - 1 ){ + while (_numberRx.lastIndex < column) { var m = _numberRx.exec(s) if(m.index <= column && m.index+m[0].length >= column){ var number = { value: m[0], start: m.index, end: m.index+m[0].length - } - return number + return number; } } return null; diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 7fb93b0f..7e47eb2c 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -91,28 +91,29 @@ exports.handler.attach = function(editor) { editor.session.$emacsMark = null; - exports.markMode = function() { - return editor.session.$emacsMark; + editor.emacsMarkMode = function() { + return this.session.$emacsMark; } - exports.setMarkMode = function(p) { - editor.session.$emacsMark = p; + editor.setEmacsMarkMode = function(p) { + this.session.$emacsMark = p; } - editor.on("click",$resetMarkMode); + editor.on("click", $resetMarkMode); editor.on("changeSession",$kbSessionChange); editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates; editor.setStyle("emacs-mode"); editor.commands.addCommands(commands); exports.handler.platform = editor.commands.platform; + editor.$emacsModeHandler = this; }; exports.handler.detach = function(editor) { delete editor.renderer.screenToTextCoordinates; editor.session.$selectLongWords = $formerLongWords; editor.session.$useEmacsStyleLineStart = $formerLineStart; - editor.removeEventListener("click",$resetMarkMode); - editor.removeEventListener("changeSession",$kbSessionChange); + editor.removeEventListener("click", $resetMarkMode); + editor.removeEventListener("changeSession", $kbSessionChange); editor.unsetStyle("emacs-mode"); editor.commands.removeCommands(commands); }; @@ -166,9 +167,10 @@ exports.handler.bindKey = function(key, command) { exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { + var editor = data.editor; // insertstring data.count times if (hashId == -1) { - exports.setMarkMode(null); + editor.setEmacsMarkMode(null); if (data.count) { var str = Array(data.count + 1).join(key); data.count = null; @@ -223,7 +225,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { args = command.args; if (command.command) command = command.command; if (command === "goorselect") { - command = exports.markMode() ? args[1] : args[0]; + command = editor.emacsMarkMode() ? args[1] : args[0]; args = null; } } @@ -232,9 +234,9 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { if (command === "insertstring" || command === "splitline" || command === "togglecomment") { - exports.setMarkMode(null); + editor.setEmacsMarkMode(null); } - command = this.commands[command] || data.editor.commands.commands[command]; + command = this.commands[command] || editor.commands.commands[command]; } if (!command.readonly && !command.isYank) @@ -330,10 +332,10 @@ exports.emacsKeys = { "C-/|C-x u|S-C--|C-z": "undo", "S-C-/|S-C-x u|C--|S-C-z": "redo", //infinite undo? // vertical editing - "C-x r": "selectRectangularRegion" - + "C-x r": "selectRectangularRegion", + "M-x": {command: "focusCommandLine", args: "M-x "} // todo - // "M-x" "C-x C-t" "M-t" "M-c" "F11" "C-M- "M-q" + // "C-x C-t" "M-t" "M-c" "F11" "C-M- "M-q" }; @@ -365,21 +367,20 @@ exports.handler.addCommands({ // "goto" commands become "select" commands. // Any insertion or mouse click resets mark-mode. // setMark twice in a row at the same place resets markmode - var markMode = exports.markMode(); + var markMode = editor.emacsMarkMode(); if (markMode) { var cp = editor.getCursorPosition(); if (editor.selection.isEmpty() && - markMode.row == cp.row && markMode.column == cp.column) { - exports.setMarkMode(null); + markMode.row == cp.row && markMode.column == cp.column) { + editor.setEmacsMarkMode(null); // console.log("Mark mode off"); return; } } // turn on mark mode markMode = editor.getCursorPosition(); - exports.setMarkMode(markMode); + editor.setEmacsMarkMode(markMode); editor.selection.setSelectionAnchor(markMode.row, markMode.column); - }, exchangePointAndMark: { exec: function(editor) { @@ -407,7 +408,7 @@ exports.handler.addCommands({ multiselectAction: "forEach" }, killLine: function(editor) { - exports.setMarkMode(null); + editor.setEmacsMarkMode(null); var pos = editor.getCursorPosition(); if (pos.column == 0 && editor.session.doc.getLine(pos.row).length == 0) { @@ -448,7 +449,11 @@ exports.handler.addCommands({ }, keyboardQuit: function(editor) { editor.selection.clearSelection(); - exports.setMarkMode(null); + editor.setEmacsMarkMode(null); + }, + focusCommandLine: function(editor, arg) { + if (editor.showCommandLine) + editor.showCommandLine(arg); } }); diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js index 3078c2d0..e559ff7b 100644 --- a/lib/ace/keyboard/keybinding.js +++ b/lib/ace/keyboard/keybinding.js @@ -50,11 +50,12 @@ var KeyBinding = function(editor) { }; this.setKeyboardHandler = function(kb) { - if (this.$handlers[this.$handlers.length - 1] == kb) + var h = this.$handlers; + if (h[h.length - 1] == kb) return; - while (this.$handlers[1]) - this.removeKeyboardHandler(this.$handlers[1]); + while (h[h.length - 1] && h[h.length - 1] != this.defaultHandler) + this.removeKeyboardHandler(h[h.length - 1]); this.addKeyboardHandler(kb, 1); }; diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index a081350c..b3c12049 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -32,6 +32,7 @@ define(function(require, exports, module) { "never use strict"; +var lang = require("../../lib/lang"); var util = require("./maps/util"); var motions = require("./maps/motions"); var operators = require("./maps/operators"); @@ -89,6 +90,8 @@ var actions = exports.actions = { param: true, fn: function(editor, range, count, param) { if (param && param.length) { + if (param.length > 1) + param = param == "return" ? "\n" : param == "tab" ? "\t" : param; repeat(function() { editor.insert(param); }, count || 1); editor.navigateLeft(); } @@ -207,13 +210,14 @@ var actions = exports.actions = { editor.setOverwrite(false); if (defaultReg.isLine) { var pos = editor.getCursorPosition(); - var lines = defaultReg.text.split("\n"); - editor.session.getDocument().insertLines(pos.row + 1, lines); + pos.column = editor.session.getLine(pos.row).length; + var text = lang.stringRepeat("\n" + defaultReg.text, count || 1); + editor.session.insert(pos, text); editor.moveCursorTo(pos.row + 1, 0); } else { editor.navigateRight(); - editor.insert(defaultReg.text); + editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); editor.navigateLeft(); } editor.setOverwrite(true); @@ -227,12 +231,13 @@ var actions = exports.actions = { if (defaultReg.isLine) { var pos = editor.getCursorPosition(); - var lines = defaultReg.text.split("\n"); - editor.session.getDocument().insertLines(pos.row, lines); - editor.moveCursorTo(pos.row, 0); + pos.column = 0; + var text = lang.stringRepeat(defaultReg.text + "\n", count || 1); + editor.session.insert(pos, text); + editor.moveCursorToPosition(pos); } else { - editor.insert(defaultReg.text); + editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); } editor.setOverwrite(true); editor.selection.clearSelection(); @@ -280,17 +285,23 @@ var actions = exports.actions = { }, ":": { fn: function(editor, range, count, param) { - // not implemented + var val = ":"; + if (count > 1) + val = ".,.+" + count + val; + if (editor.showCommandLine) + editor.showCommandLine(val); } }, "/": { fn: function(editor, range, count, param) { - // not implemented + if (editor.showCommandLine) + editor.showCommandLine("/"); } }, "?": { fn: function(editor, range, count, param) { - // not implemented + if (editor.showCommandLine) + editor.showCommandLine("?"); } }, ".": { @@ -327,15 +338,19 @@ var inputBuffer = exports.inputBuffer = { lastInsertCommands: [], push: function(editor, ch, keyId) { + var status = this.status; var isKeyHandled = true; this.idle = false; var wObj = this.waitingForParam; + if (/^numpad\d+$/i.test(ch)) + ch = ch.substr(6); + if (wObj) { this.exec(editor, wObj, ch); } // If input is a number (that doesn't start with 0) else if (!(ch === "0" && !this.currentCount.length) && - (ch.match(/^\d+$/) && this.isAccepting(NUMBER))) { + (/^\d+$/.test(ch) && this.isAccepting(NUMBER))) { // Assuming that ch is always of type String, and not Number this.currentCount += ch; this.currentCmd = NUMBER; @@ -389,6 +404,7 @@ var inputBuffer = exports.inputBuffer = { this.idle = false; } else if (this.operator) { + this.operator.count = this.getCount(); this.exec(editor, { operator: this.operator }, ch); } else { @@ -402,10 +418,9 @@ var inputBuffer = exports.inputBuffer = { this.status = this.currentCount; } else if (this.status) { this.status = ""; - } else { - return isKeyHandled; } - editor._emit("changeStatus"); + if (this.status != status) + editor._emit("changeStatus"); return isKeyHandled; }, diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index c143d218..3611386b 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -138,6 +138,8 @@ function find(editor, needle, dir) { var Range = require("../../../range").Range; +var LAST_SEARCH_MOTION = {}; + module.exports = { "w": new Motion(function(editor) { var str = new StringStream(editor); @@ -381,7 +383,9 @@ module.exports = { "f": new Motion({ param: true, handlesCount: true, - getPos: function(editor, range, count, param, isSel) { + getPos: function(editor, range, count, param, isSel, isRepeat) { + if (!isRepeat) + LAST_SEARCH_MOTION = {ch: "f", param: param}; var cursor = editor.getCursorPosition(); var column = util.getRightNthChar(editor, cursor, param, count || 1); @@ -394,7 +398,9 @@ module.exports = { "F": new Motion({ param: true, handlesCount: true, - getPos: function(editor, range, count, param, isSel) { + getPos: function(editor, range, count, param, isSel, isRepeat) { + if (!isRepeat) + LAST_SEARCH_MOTION = {ch: "F", param: param}; var cursor = editor.getCursorPosition(); var column = util.getLeftNthChar(editor, cursor, param, count || 1); @@ -407,10 +413,15 @@ module.exports = { "t": new Motion({ param: true, handlesCount: true, - getPos: function(editor, range, count, param, isSel) { + getPos: function(editor, range, count, param, isSel, isRepeat) { + if (!isRepeat) + LAST_SEARCH_MOTION = {ch: "t", param: param}; var cursor = editor.getCursorPosition(); var column = util.getRightNthChar(editor, cursor, param, count || 1); + if (isRepeat && column == 0 && !(count > 1)) + var column = util.getRightNthChar(editor, cursor, param, 2); + if (typeof column === "number") { cursor.column += column + (isSel ? 1 : 0); return cursor; @@ -420,16 +431,46 @@ module.exports = { "T": new Motion({ param: true, handlesCount: true, - getPos: function(editor, range, count, param, isSel) { + getPos: function(editor, range, count, param, isSel, isRepeat) { + if (!isRepeat) + LAST_SEARCH_MOTION = {ch: "T", param: param}; var cursor = editor.getCursorPosition(); var column = util.getLeftNthChar(editor, cursor, param, count || 1); + if (isRepeat && column == 0 && !(count > 1)) + var column = util.getLeftNthChar(editor, cursor, param, 2); + if (typeof column === "number") { cursor.column -= column; return cursor; } } }), + ";": new Motion({ + handlesCount: true, + getPos: function(editor, range, count, param, isSel) { + var ch = LAST_SEARCH_MOTION.ch; + if (!ch) + return; + return module.exports[ch].getPos( + editor, range, count, LAST_SEARCH_MOTION.param, isSel, true + ); + } + }), + ",": new Motion({ + handlesCount: true, + getPos: function(editor, range, count, param, isSel) { + var ch = LAST_SEARCH_MOTION.ch; + if (!ch) + return; + var up = ch.toUpperCase(); + ch = ch === up ? ch.toLowerCase() : up; + + return module.exports[ch].getPos( + editor, range, count, LAST_SEARCH_MOTION.param, isSel, true + ); + } + }), "^": { nav: function(editor) { @@ -614,7 +655,7 @@ module.exports = { }; module.exports.backspace = module.exports.left = module.exports.h; -module.exports.right = module.exports.l; +module.exports.space = module.exports.return = module.exports.right = module.exports.l; module.exports.up = module.exports.k; module.exports.down = module.exports.j; module.exports.pagedown = module.exports["ctrl-d"]; diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js index 702abc6e..067562a0 100644 --- a/lib/ace/keyboard/vim/maps/operators.js +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -30,7 +30,7 @@ define(function(require, exports, module) { -"never use strict"; +"use strict"; var util = require("./util"); var registers = require("../registers"); @@ -58,7 +58,6 @@ module.exports = { var selRange = editor.getSelectionRange(); // check if end of the document was reached if (!selRange.isMultiLine()) { - lastLineReached = true var row = selRange.start.row - 1; var col = editor.session.getLine(row).length selRange.setStart(row, col);