From 9f57a4f99de715e5e1e1106fe652fe2ebab6f82e Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 3 May 2012 17:35:59 +0400 Subject: [PATCH 01/47] tweak mousehandler --- lib/ace/mouse/default_gutter_handler.js | 10 +++++++--- lib/ace/mouse/default_handlers.js | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/ace/mouse/default_gutter_handler.js b/lib/ace/mouse/default_gutter_handler.js index d03e18ce..a43ec16a 100644 --- a/lib/ace/mouse/default_gutter_handler.js +++ b/lib/ace/mouse/default_gutter_handler.js @@ -52,10 +52,14 @@ function GutterHandler(mouseHandler) { var row = e.getDocumentPosition().row; var selection = editor.session.selection; - selection.moveCursorTo(row, 0); - selection.selectLine(); + if (e.getShiftKey()) { + selection.selectTo(row, 0); + } else { + selection.moveCursorTo(row, 0); + selection.selectLine(); + mouseHandler.$clickSelection = selection.getRange(); + } - mouseHandler.$clickSelection = selection.getRange(); mouseHandler.captureMouse(e, "selectByLines"); }); } diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index 34d6ef1b..48fe88d7 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -297,7 +297,7 @@ function DefaultHandlers(mouseHandler) { editor.selectAll(); this.$clickSelection = editor.getSelectionRange(); - this.setState("select"); + this.setState("null"); }; this.onScroll = function(ev) { From e664a3b73844865d5b2e85776173d60afe9d50ba Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 3 May 2012 18:02:15 +0400 Subject: [PATCH 02/47] do not allow time window with no mode set on session while loading mode --- lib/ace/edit_session.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 32070ce4..b563b534 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -772,6 +772,10 @@ var EditSession = function(text, mode) { if (module) return done(module); + // set mode to text until loading is finished + if (!this.$mode) + this.$setModePlaceholder(); + fetch(function() { require([mode], done); }); @@ -799,6 +803,27 @@ var EditSession = function(text, mode) { } }; + this.$setModePlaceholder = function() { + var textModeId = "ace/mode/text" + if (!this.$modes[textModeId]) + this.$modes[textModeId] = new TextMode(); + + var tokenizer = this.$modes[textModeId].getTokenizer(); + + if (!this.bgTokenizer) { + this.bgTokenizer = new BackgroundTokenizer(tokenizer); + var _self = this; + this.bgTokenizer.addEventListener("update", function(e) { + _self._emit("tokenizerUpdate", e); + }); + } else { + this.bgTokenizer.setTokenizer(tokenizer); + } + + this.tokenRe = this.$modes[textModeId].tokenRe; + this.nonTokenRe = this.$modes[textModeId].nonTokenRe; + }; + /** * EditSession.setMode(mode) * - mode (TextMode): Set a new text mode @@ -809,6 +834,7 @@ var EditSession = function(text, mode) { this.$mode = null; this.$modeId = null; this.setMode = function(mode) { + mode = mode || "ace/mode/text"; // load on demand if (typeof mode === "string") { if (this.$modeId == mode) @@ -823,12 +849,6 @@ var EditSession = function(text, mode) { _self.setMode(module); }); return; - } else if (mode == null) { - mode = "ace/mode/text" - this.$modeId = mode; - this.$modes[mode] = this.$modes[mode] || (new TextMode()); - this.setMode(this.$modes[mode]); - return; } if (this.$mode === mode) return; From 81b87d5c6060dffaa7c89684ec4c68ab6f6ff9ec Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 3 May 2012 18:08:06 +0400 Subject: [PATCH 03/47] add timeout to highlight word --- lib/ace/editor.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 85d7076b..af6690ff 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -509,8 +509,11 @@ var Editor = function(renderer, session) { this.$updateHighlightActiveLine(); } - if (this.$highlightSelectedWord) - this.session.getMode().highlightSelection(this); + if (this.$highlightSelectedWord && !this.$wordHighlightTimer) + this.$wordHighlightTimer = setTimeout(function(self) { + self.session.$mode.highlightSelection(self); + self.$wordHighlightTimer = null; + }, 30, this); }; /** From f67fc8c9eb8ed1e32b69f6610ec96e8ad19e2e39 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 13 Dec 2011 18:55:21 +0400 Subject: [PATCH 04/47] change directory structure of keybindings --- Makefile.dryice.js | 2 +- demo/kitchen-sink/demo.js | 4 ++-- lib/ace/keyboard/{keybinding => }/emacs.js | 2 +- lib/ace/keyboard/{keybinding => }/vim.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename lib/ace/keyboard/{keybinding => }/emacs.js (99%) rename lib/ace/keyboard/{keybinding => }/vim.js (98%) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index f3345648..c64a8629 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -373,7 +373,7 @@ function buildAce(aceProject, options) { source: [ { project: cloneProject(project), - require: [ 'ace/keyboard/keybinding/' + keybinding ] + require: [ 'ace/keyboard/' + keybinding ] } ], filter: filters, diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index bcd240ff..a0f98e1f 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -50,8 +50,8 @@ var theme = require("ace/theme/textmate"); var EditSession = require("ace/edit_session").EditSession; var UndoManager = require("ace/undomanager").UndoManager; -var vim = require("ace/keyboard/keybinding/vim").Vim; -var emacs = require("ace/keyboard/keybinding/emacs").Emacs; +var vim = require("ace/keyboard/vim").handler; +var emacs = require("ace/keyboard/emacs").handler; var HashHandler = require("ace/keyboard/hash_handler").HashHandler; var modesByName; diff --git a/lib/ace/keyboard/keybinding/emacs.js b/lib/ace/keyboard/emacs.js similarity index 99% rename from lib/ace/keyboard/keybinding/emacs.js rename to lib/ace/keyboard/emacs.js index 1c4619fd..e912f589 100644 --- a/lib/ace/keyboard/keybinding/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -145,6 +145,6 @@ var emacsState = { ] }; -exports.Emacs = new StateHandler(emacsState); +exports.handler = new StateHandler(emacsState); }); diff --git a/lib/ace/keyboard/keybinding/vim.js b/lib/ace/keyboard/vim.js similarity index 98% rename from lib/ace/keyboard/keybinding/vim.js rename to lib/ace/keyboard/vim.js index 6cc5d912..78367c2d 100644 --- a/lib/ace/keyboard/keybinding/vim.js +++ b/lib/ace/keyboard/vim.js @@ -133,6 +133,6 @@ var vimStates = { ] }; -exports.Vim = new StateHandler(vimStates); +exports.handler = new StateHandler(vimStates); }); From adf7420b85811a87a41fda9d8a8dd0f129e0f3c4 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 13 Dec 2011 18:58:35 +0400 Subject: [PATCH 05/47] import vim keybinding from cloud9 --- lib/ace/keyboard/vim/commands.js | 500 +++++++++++++++++++++++++ lib/ace/keyboard/vim/keyboard.js | 92 +++++ lib/ace/keyboard/vim/maps/aliases.js | 67 ++++ lib/ace/keyboard/vim/maps/motions.js | 285 ++++++++++++++ lib/ace/keyboard/vim/maps/operators.js | 154 ++++++++ lib/ace/keyboard/vim/maps/util.js | 117 ++++++ lib/ace/keyboard/vim/registers.js | 13 + 7 files changed, 1228 insertions(+) create mode 100644 lib/ace/keyboard/vim/commands.js create mode 100644 lib/ace/keyboard/vim/keyboard.js create mode 100644 lib/ace/keyboard/vim/maps/aliases.js create mode 100644 lib/ace/keyboard/vim/maps/motions.js create mode 100644 lib/ace/keyboard/vim/maps/operators.js create mode 100644 lib/ace/keyboard/vim/maps/util.js create mode 100644 lib/ace/keyboard/vim/registers.js diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js new file mode 100644 index 00000000..a952047f --- /dev/null +++ b/lib/ace/keyboard/vim/commands.js @@ -0,0 +1,500 @@ +/** + * Vim mode for the Cloud9 IDE + * + * @author Sergi Mansilla + * @copyright 2011, Ajax.org B.V. + * @license GPLv3 + */ + +define(function(require, exports, module) { + +"use strict"; + +var Editors = require("ext/editors/editors"); +var util = require("ext/vim/maps/util"); +var motions = require("ext/vim/maps/motions"); +var operators = require("ext/vim/maps/operators"); +var alias = require("ext/vim/maps/aliases"); +var registers = require("ext/vim/registers"); + +var NUMBER = 1; +var OPERATOR = 2; +var MOTION = 3; +var ACTION = 4; + +//var NORMAL_MODE = 0; +//var INSERT_MODE = 1; +//var VISUAL_MODE = 2; +//getSelectionLead + +exports.searchStore = { + current: "", + options: { + needle: "", + backwards: false, + wrap: true, + caseSensitive: false, + wholeWord: false, + regExp: false + } +}; + +var repeat = function repeat(fn, count, args) { + count = parseInt(count); + while (0 < count--) + fn.apply(this, args); +}; + +var actions = { + "z": { + param: true, + fn: function(editor, range, count, param) { + switch (param) { + case "z": + editor.centerSelection(); + break; + case "t": + editor.scrollToRow(editor.getCursorPosition().row); + break; + } + } + }, + "r": { + param: true, + fn: function(editor, range, count, param) { + param = util.toRealChar(param); + if (param && param.length) { + repeat(function() { editor.insert(param); }, count || 1); + editor.navigateLeft(); + } + } + }, + // "~" HACK + "shift-`": { + fn: function(editor, range, count) { + repeat(function() { + var pos = editor.getCursorPosition(); + var line = editor.session.getLine(pos.row); + var ch = line[pos.column]; + editor.insert(toggleCase(ch)); + }, count || 1); + } + }, + "*": { + fn: function(editor, range, count, param) { + editor.selection.selectWord(); + editor.findNext(); + var cursor = editor.selection.getCursor(); + var range = editor.session.getWordRange(cursor.row, cursor.column); + editor.selection.setSelectionRange(range, true); + } + }, + "#": { + fn: function(editor, range, count, param) { + editor.selection.selectWord(); + editor.findPrevious(); + var cursor = editor.selection.getCursor(); + var range = editor.session.getWordRange(cursor.row, cursor.column); + editor.selection.setSelectionRange(range, true); + } + }, + "n": { + fn: function(editor, range, count, param) { + editor.findNext(editor.getLastSearchOptions()); + editor.selection.clearSelection(); + //editor.navigateWordLeft(); + } + }, + "shift-n": { + fn: function(editor, range, count, param) { + editor.findPrevious(editor.getLastSearchOptions()); + editor.selection.clearSelection(); + //editor.navigateWordLeft(); + } + }, + "v": { + fn: function(editor, range, count, param) { + editor.selection.selectRight(); + util.onVisualMode = true; + util.onVisualLineMode = false; + var cursor = document.getElementsByClassName("ace_cursor")[0]; + cursor.style.display = "none"; + } + }, + "shift-v": { + fn: function(editor, range, count, param) { + util.onVisualLineMode = true; + //editor.selection.selectLine(); + //editor.selection.selectLeft(); + var row = editor.getCursorPosition().row; + editor.selection.clearSelection(); + editor.selection.moveCursorTo(row, 0); + editor.selection.selectLineEnd(); + editor.selection.visualLineStart = row; + } + }, + "shift-y": { + fn: function(editor, range, count, param) { + util.copyLine(editor); + } + }, + "p": { + fn: function(editor, range, count, param) { + var defaultReg = registers._default; + + editor.setOverwrite(false); + if (defaultReg.isLine) { + var pos = editor.getCursorPosition(); + var lines = defaultReg.text.split("\n"); + editor.session.getDocument().insertLines(pos.row + 1, lines); + editor.moveCursorTo(pos.row + 1, 0); + } + else { + editor.navigateRight(); + editor.insert(defaultReg.text); + editor.navigateLeft(); + } + editor.setOverwrite(true); + editor.selection.clearSelection(); + } + }, + "shift-p": { + fn: function(editor, range, count, param) { + var defaultReg = registers._default; + editor.setOverwrite(false); + + 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); + } + else { + editor.insert(defaultReg.text); + } + editor.setOverwrite(true); + editor.selection.clearSelection(); + } + }, + "shift-j": { + fn: function(editor, range, count, param) { + var pos = editor.getCursorPosition(); + + if (editor.session.getLength() === pos.row + 1) + return; + + var nextLine = editor.session.getLine(pos.row + 1); + var cleanLine = /^\s*(.*)$/.exec(nextLine)[1]; + + editor.navigateDown(); + editor.removeLines(); + + if (editor.session.getLength() > editor.getCursorPosition().row + 1) + editor.navigateUp(); + + editor.navigateLineEnd(); + editor.insert(" " + (cleanLine || "")); + editor.moveCursorTo(pos.row, pos.column); + + } + }, + "u": { + fn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + for (var i = 0; i < count; i++) { + editor.undo(); + } + editor.selection.clearSelection(); + } + }, + "ctrl-r": { + fn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + for (var i = 0; i < count; i++) { + editor.redo(); + } + editor.selection.clearSelection(); + } + }, + ":": { + fn: function(editor, range, count, param) { + editor.blur(); + txtConsoleInput.focus(); + txtConsoleInput.setValue(":"); + } + }, + "/": { + fn: function(editor, range, count, param) { + editor.blur(); + txtConsoleInput.focus(); + txtConsoleInput.setValue("/"); + } + }, + ".": { + fn: function(editor, range, count, param) { + var previous = inputBuffer.previous; + util.onInsertReplaySequence = inputBuffer.lastInsertCommands; + inputBuffer.exec(editor, previous.action, previous.param); + } + } +}; + +var inputBuffer = exports.inputBuffer = { + accepting: [NUMBER, OPERATOR, MOTION, ACTION], + currentCmd: null, + //currentMode: 0, + currentCount: "", + + // Types + operator: null, + motion: null, + + lastInsertCommands: [], + + push: function(editor, char, keyId) { + if (char && char.length > 1) { // There is a modifier key + if (!char[char.length - 1].match(/[A-za-z]/) && keyId) // It is a letter + char = keyId; + } + + this.idle = false; + var wObj = this.waitingForParam; + if (wObj) { + this.exec(editor, wObj, char); + } + // If input is a number (that doesn't start with 0) + else if (!(char === "0" && !this.currentCount.length) && + (char.match(/^\d+$/) && this.isAccepting(NUMBER))) { + // Assuming that char is always of type String, and not Number + this.currentCount += char; + this.currentCmd = NUMBER; + this.accepting = [NUMBER, OPERATOR, MOTION, ACTION]; + } + else if (!this.operator && this.isAccepting(OPERATOR) && operators[char]) { + this.operator = { + char: char, + count: this.getCount() + }; + this.currentCmd = OPERATOR; + this.accepting = [NUMBER, MOTION, ACTION]; + this.exec(editor, { operator: this.operator }); + } + else if (motions[char] && this.isAccepting(MOTION)) { + this.currentCmd = MOTION; + + var ctx = { + operator: this.operator, + motion: { + char: char, + count: this.getCount() + } + }; + + if (motions[char].param) + this.waitForParam(ctx); + else + this.exec(editor, ctx); + } + else if (alias[char] && this.isAccepting(MOTION)) { + alias[char].operator.count = this.getCount(); + this.exec(editor, alias[char]); + } + else if (actions[char] && this.isAccepting(ACTION)) { + var actionObj = { + action: { + fn: actions[char].fn, + count: this.getCount() + } + }; + + if (actions[char].param) { + this.waitForParam(actionObj); + } + else { + this.exec(editor, actionObj); + } + } + else if (this.operator) { + this.exec(editor, { operator: this.operator }, char); + } + else { + this.reset(); + } + }, + + waitForParam: function(cmd) { + this.waitingForParam = cmd; + }, + + getCount: function() { + var count = this.currentCount; + this.currentCount = ""; + return count; + }, + + exec: function(editor, action, param) { + var m = action.motion; + var o = action.operator; + var a = action.action; + + if(o) { + this.previous = { + action: action, + param: param + }; + } + + if (o && !editor.selection.isEmpty()) { + if (operators[o.char].selFn) { + operators[o.char].selFn(editor, editor.getSelectionRange(), o.count, param); + this.reset(); + } + return; + } + + // There is an operator, but no motion or action. We try to pass the + // current char to the operator to see if it responds to it (an example + // of this is the 'dd' operator). + else if (!m && !a && o && param) { + operators[o.char].fn(editor, null, o.count, param); + this.reset(); + } + else if (m) { + var run = function(fn) { + if (fn && typeof fn === "function") { // There should always be a motion + if (m.count) + repeat(fn, m.count, [editor, null, m.count, param]); + else + fn(editor, null, m.count, param); + } + }; + + var motionObj = motions[m.char]; + var selectable = motionObj.sel; + + if (!o) { + if ((util.onVisualMode || util.onVisualLineMode) && selectable) + run(motionObj.sel); + else + run(motionObj.nav); + } + else if (selectable) { + repeat(function() { + run(motionObj.sel); + operators[o.char].fn(editor, editor.getSelectionRange(), o.count, param); + }, o.count || 1); + } + this.reset(); + } + else if (a) { + a.fn(editor, editor.getSelectionRange(), a.count, param); + this.reset(); + } + handleCursorMove(); + }, + + isAccepting: function(type) { + return this.accepting.indexOf(type) !== -1; + }, + + reset: function() { + this.operator = null; + this.motion = null; + this.currentCount = ""; + this.accepting = [NUMBER, OPERATOR, MOTION, ACTION]; + this.idle = true; + this.waitingForParam = null; + } +}; + +function setPreviousCommand(fn) { + inputBuffer.previous = { action: { action: { fn: fn } } }; +} + +exports.commands = { + start: { + exec: function start(editor) { + util.insertMode(editor); + setPreviousCommand(start); + } + }, + startBeginning: { + exec: function startBeginning(editor) { + editor.navigateLineStart(); + util.insertMode(editor); + setPreviousCommand(startBeginning); + } + }, + // Stop Insert mode as soon as possible. Works like typing in + // insert mode. + stop: { + exec: function stop(editor) { + inputBuffer.reset(); + util.onVisualMode = false; + util.onVisualLineMode = false; + inputBuffer.lastInsertCommands = util.normalMode(editor); + } + }, + append: { + exec: function append(editor) { + var pos = editor.getCursorPosition(); + var lineLen = editor.session.getLine(pos.row).length; + if (lineLen) + editor.navigateRight(); + util.insertMode(editor); + setPreviousCommand(append); + } + }, + appendEnd: { + exec: function appendEnd(editor) { + editor.navigateLineEnd(); + util.insertMode(editor); + setPreviousCommand(appendEnd); + } + } +}; + +var handleCursorMove = exports.onCursorMove = function() { + var editor = Editors.currentEditor.amlEditor.$editor; + + if(util.currentMode === 'insert' || handleCursorMove.running) + return; + else if(!editor.selection.isEmpty()) { + handleCursorMove.running = true; + if(util.onVisualLineMode) { + var originRow = editor.selection.visualLineStart; + var cursorRow = editor.getCursorPosition().row; + if(originRow <= cursorRow) { + var endLine = editor.session.getLine(cursorRow); + editor.selection.clearSelection(); + editor.selection.moveCursorTo(originRow, 0); + editor.selection.selectTo(cursorRow, endLine.length); + } else { + var endLine = editor.session.getLine(originRow); + editor.selection.clearSelection(); + editor.selection.moveCursorTo(originRow, endLine.length); + editor.selection.selectTo(cursorRow, 0); + } + } + handleCursorMove.running = false; + return; + } + else { + handleCursorMove.running = true; + var pos = editor.getCursorPosition(); + var lineLen = editor.session.getLine(pos.row).length; + + if (lineLen && pos.column === lineLen) + editor.navigateLeft(); + handleCursorMove.running = false; + } +}; + +function toggleCase(ch) { + if(ch.toUpperCase() === ch) + return ch.toLowerCase(); + else + return ch.toUpperCase(); +} + +}); \ No newline at end of file diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js new file mode 100644 index 00000000..4786b5eb --- /dev/null +++ b/lib/ace/keyboard/vim/keyboard.js @@ -0,0 +1,92 @@ + +define(function(require, exports, module) { + +"use strict"; + +var StateHandler = require("ace/keyboard/state_handler").StateHandler; +var cmds = require("ext/vim/commands"); +var editors = require("ext/editors/editors"); + +var matchChar = function(buffer, hashId, key, symbolicName, keyId) { + // If no command keys are pressed, then catch the input. + // If only the shift key is pressed and a character key, then + // catch that input as well. + // Otherwise, we let the input got through. + var matched = ((hashId === 0) || (((hashId === 1) || (hashId === 4)) && key.length === 1)); + //console.log("INFO", arguments) + + if (matched) { + if (keyId) { + keyId = String.fromCharCode(parseInt(keyId.replace("U+", "0x"), 10)); + } + + var editor = editors.currentEditor.amlEditor.$editor; + editor.commands.addCommand({ + name: "builder", + exec: function(editor) { + cmds.inputBuffer.push.call(cmds.inputBuffer, editor, symbolicName, keyId); + } + }); + } + return matched; +}; + +var inIdleState = function() { + if (cmds.inputBuffer.idle) { + return true; + } + return false; +}; + +var states = exports.states = { + start: [ // normal mode + { + key: "esc", + exec: "stop", + then: "start" + }, + { + regex: "^i$", + match: inIdleState, + exec: "start", + then: "insertMode" + }, + { + regex: "^shift-i$", + match: inIdleState, + exec: "startBeginning", + then: "insertMode" + }, + { + regex: "^a$", + match: inIdleState, + exec: "append", + then: "insertMode" + }, + { + regex: "^shift-a$", + match: inIdleState, + exec: "appendEnd", + then: "insertMode" + }, + { + // The rest of input will be processed here + match: matchChar, + exec: "builder" + } + ], + insertMode: [ + { + key: "esc", + exec: "stop", + then: "start" + }, + { + key: "backspace", + exec: "backspace" + } + ] +}; + +exports.handler = new StateHandler(states); +}); diff --git a/lib/ace/keyboard/vim/maps/aliases.js b/lib/ace/keyboard/vim/maps/aliases.js new file mode 100644 index 00000000..35ceb93f --- /dev/null +++ b/lib/ace/keyboard/vim/maps/aliases.js @@ -0,0 +1,67 @@ +"use strict" + +define(function(require, exports, module) { +module.exports = { + "x": { + operator: { + char: "d", + count: 1 + }, + motion: { + char: "l", + count: 1 + } + }, + "shift-x": { + operator: { + char: "d", + count: 1 + }, + motion: { + char: "h", + count: 1 + } + }, + "shift-d": { + operator: { + char: "d", + count: 1 + }, + motion: { + char: "$", + count: 1 + } + }, + "shift-c": { + operator: { + char: "c", + count: 1 + }, + motion: { + char: "$", + count: 1 + } + }, + "s": { + operator: { + char: "c", + count: 1 + }, + motion: { + char: "l", + count: 1 + } + }, + "shift-s": { + operator: { + char: "c", + count: 1 + }, + motion: { + char: "l", + count: 1 + } + } +}; +}); + diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js new file mode 100644 index 00000000..3bf66aa4 --- /dev/null +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -0,0 +1,285 @@ +"use strict" + +define(function(require, exports, module) { + +var util = require("ext/vim/maps/util"); + +var keepScrollPosition = function(editor, fn) { + var scrollTopRow = editor.renderer.getScrollTopRow(); + var initialRow = editor.getCursorPosition().row; + var diff = initialRow - scrollTopRow; + fn && fn.call(editor); + editor.renderer.scrollToRow(editor.getCursorPosition().row - diff); +}; + +module.exports = { + "w": { + nav: function(editor) { + editor.navigateWordRight(); + }, + sel: function(editor) { + editor.selection.selectWordRight(); + } + }, + "b": { + nav: function(editor) { + editor.navigateWordLeft(); + }, + sel: function(editor) { + editor.selection.selectWordLeft(); + } + }, + "l": { + nav: function(editor) { + editor.navigateRight(); + }, + sel: function(editor) { + var pos = editor.getCursorPosition(); + var col = pos.column; + var lineLen = editor.session.getLine(pos.row).length; + + // Solving the behavior at the end of the line due to the + // different 0 index-based colum positions in ACE. + if (lineLen && col !== lineLen) //In selection mode you can select the newline + editor.selection.selectRight(); + } + }, + "h": { + nav: function(editor) { + var pos = editor.getCursorPosition(); + if (pos.column > 0) + editor.navigateLeft(); + }, + sel: function(editor) { + var pos = editor.getCursorPosition(); + if (pos.column > 0) + editor.selection.selectLeft(); + } + }, + "k": { + nav: function(editor) { + editor.navigateUp(); + }, + sel: function(editor) { + editor.selection.selectUp(); + } + }, + "j": { + nav: function(editor) { + editor.navigateDown(); + }, + sel: function(editor) { + editor.selection.selectDown(); + } + }, + "i": { + param: true, + sel: function(editor, range, count, param) { + switch (param) { + case "w": + editor.selection.selectWord(); + } + } + }, + "a": { + param: true, + sel: function(editor, range, count, param) { + switch (param) { + case "w": + editor.selection.selectAWord(); + } + } + }, + "f": { + param: true, + nav: function(editor, range, count, param) { + count = parseInt(count, 10) || 1; + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getRightNthChar(editor, cursor, param, count); + + if (typeof column === "number") { + ed.selection.clearSelection(); // Why does it select in the first place? + ed.moveCursorTo(cursor.row, column + cursor.column + 1); + } + }, + sel: function(editor, range, count, param) { + count = parseInt(count, 10) || 1; + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getRightNthChar(editor, cursor, param, count); + + if (typeof column === "number") { + ed.moveCursorTo(cursor.row, column + cursor.column + 1); + } + } + }, + "t": { + param: true, + nav: function(editor, range, count, param) { + count = parseInt(count, 10) || 1; + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getRightNthChar(editor, cursor, param, count); + + if (typeof column === "number") { + ed.selection.clearSelection(); // Why does it select in the first place? + ed.moveCursorTo(cursor.row, column + cursor.column); + } + }, + sel: function(editor, range, count, param) { + count = parseInt(count, 10) || 1; + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getRightNthChar(editor, cursor, param, count); + + if (typeof column === "number") { + ed.moveCursorTo(cursor.row, column + cursor.column); + } + } + }, + "^": { + nav: function(editor) { + editor.navigateLineStart(); + }, + sel: function(editor) { + editor.selection.selectLineStart(); + } + }, + "$": { + nav: function(editor) { + editor.navigateLineEnd(); + }, + sel: function(editor) { + editor.selection.selectLineEnd(); + } + }, + "0": { + nav: function(editor) { + var ed = editor; + ed.navigateTo(ed.selection.selectionLead.row, 0); + }, + sel: function(editor) { + var ed = editor; + ed.selectTo(ed.selection.selectionLead.row, 0); + } + }, + "shift-g": { + nav: function(editor, range, count, param) { + count = parseInt(count, 10); + if (!count && count !== 0) { // Stupid JS + count = editor.session.getLength(); + } + editor.gotoLine(count); + }, + sel: function(editor, range, count, param) { + count = parseInt(count, 10); + if (!count && count !== 0) { // Stupid JS + count = editor.session.getLength(); + } + editor.selection.selectTo(count, 0); + } + }, + "ctrl-d": { + nav: function(editor, range, count, param) { + editor.selection.clearSelection(); + keepScrollPosition(editor, editor.gotoPageDown); + }, + sel: function(editor, range, count, param) { + keepScrollPosition(editor, editor.selectPageDown); + } + }, + "ctrl-u": { + nav: function(editor, range, count, param) { + editor.selection.clearSelection(); + keepScrollPosition(editor, editor.gotoPageUp); + + }, + sel: function(editor, range, count, param) { + keepScrollPosition(editor, editor.selectPageUp); + } + }, + "g": { + param: true, + nav: function(editor, range, count, param) { + switch(param) { + case "m": + console.log("Middle line"); + break; + case "e": + console.log("End of prev word"); + break; + case "g": + editor.gotoLine(count || 0); + } + }, + sel: function(editor, range, count, param) { + switch(param) { + case "m": + console.log("Middle line"); + break; + case "e": + console.log("End of prev word"); + break; + case "g": + editor.selection.selectTo(count || 0, 0); + } + } + }, + "o": { + nav: function(editor, range, count, param) { + count = count || 1; + var content = ""; + while (0 < count--) + content += "\n"; + + if (content.length) { + editor.navigateLineEnd() + editor.insert(content); + util.insertMode(editor); + } + } + }, + "shift-o": { + nav: function(editor, range, count, param) { + var row = editor.getCursorPosition().row; + count = count || 1; + var content = ""; + while (0 < count--) + content += "\n"; + + if (content.length) { + if(row > 0) { + editor.navigateUp(); + editor.navigateLineEnd() + editor.insert(content); + } else { + editor.session.insert({row: 0, column: 0}, content); + editor.navigateUp(); + } + util.insertMode(editor); + } + } + }, + "%": { + nav: function(editor, range, count, param) { + var cursor = editor.getCursorPosition(); + var match = editor.session.findMatchingBracket({ + row: cursor.row, + column: cursor.column + 1 + }); + + if (match) + editor.moveCursorTo(match.row, match.column); + } + } +}; + +module.exports.backspace = module.exports.left = module.exports.h; +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"]; +module.exports.pageup = module.exports["ctrl-u"]; + +}); diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js new file mode 100644 index 00000000..a946d4c8 --- /dev/null +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -0,0 +1,154 @@ +define(function(require, exports, module) { + +"use strict"; + +var util = require("ext/vim/maps/util"); +var registers = require("ext/vim/registers"); + +module.exports = { + "d": { + selFn: function(editor, range, count, param) { + registers._default.text = editor.getCopyText(); + registers._default.isLine = util.onVisualLineMode; + if(util.onVisualLineMode) + editor.removeLines(); + else + editor.session.remove(range); + util.normalMode(editor); + }, + fn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + switch (param) { + case "d": + registers._default.text = ""; + registers._default.isLine = true; + for (var i=0; i": { + selFn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + for (var i = 0; i < count; i++) { + editor.indent(); + } + util.normalMode(editor); + }, + fn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + switch (param) { + case ">": + var pos = editor.getCursorPosition(); + editor.selection.selectLine(); + for (var i = 0; i < count - 1; i++) { + editor.selection.moveCursorDown(); + } + editor.indent(); + editor.selection.clearSelection(); + editor.moveCursorToPosition(pos); + editor.navigateLineEnd(); + editor.navigateLineStart(); + break; + } + } + }, + "<": { + selFn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + for (var i = 0; i < count; i++) { + editor.blockOutdent(); + } + util.normalMode(editor); + }, + fn: function(editor, range, count, param) { + count = parseInt(count || 1, 10); + switch (param) { + case "<": + var pos = editor.getCursorPosition(); + editor.selection.selectLine(); + for (var i = 0; i < count - 1; i++) { + editor.selection.moveCursorDown(); + } + editor.blockOutdent(); + editor.selection.clearSelection(); + editor.moveCursorToPosition(pos); + editor.navigateLineEnd(); + editor.navigateLineStart(); + break; + } + } + } +}; +}); diff --git a/lib/ace/keyboard/vim/maps/util.js b/lib/ace/keyboard/vim/maps/util.js new file mode 100644 index 00000000..18348125 --- /dev/null +++ b/lib/ace/keyboard/vim/maps/util.js @@ -0,0 +1,117 @@ +"use strict"; + +define(function(require, exports, module) { +var registers = require("ext/vim/registers"); + +module.exports = { + onVisualMode: false, + onVisualLineMode: false, + currentMode: 'normal', + insertMode: function(editor) { + var _self = this; + var theme = editor && editor.getTheme() || "ace/theme/textmate"; + + require(["require", theme], function (require) { + var isDarkTheme = require(theme).isDark; + + _self.currentMode = 'insert'; + // Switch editor to insert mode + editor.unsetStyle('insert-mode'); + + var cursor = document.getElementsByClassName("ace_cursor")[0]; + if (cursor) { + cursor.style.display = null; + cursor.style.backgroundColor = null; + cursor.style.opacity = null; + cursor.style.border = null; + cursor.style.borderLeftColor = isDarkTheme? "#eeeeee" : "#333333"; + cursor.style.borderLeftStyle = "solid"; + cursor.style.borderLeftWidth = "2px"; + } + + editor.setOverwrite(false); + editor.keyBinding.$data.buffer = ""; + editor.keyBinding.$data.state = "insertMode"; + _self.onVisualMode = false; + _self.onVisualLineMode = false; + if(_self.onInsertReplaySequence) { + // Ok, we're apparently replaying ("."), so let's do it + editor.commands.macro = _self.onInsertReplaySequence; + editor.commands.replay(editor); + _self.onInsertReplaySequence = null; + _self.normalMode(editor); + } else { + // Record any movements, insertions in insert mode + if(!editor.commands.recording) + editor.commands.toggleRecording(); + } + }); + }, + normalMode: function(editor) { + // Switch editor to normal mode + this.currentMode = 'normal'; + + editor.setStyle('normal-mode'); + editor.clearSelection(); + + var cursor = document.getElementsByClassName("ace_cursor")[0]; + if (cursor) { + cursor.style.display = null; + cursor.style.backgroundColor = "red"; + cursor.style.opacity = ".5"; + cursor.style.border = "0"; + } + + var pos; + if (!editor.getOverwrite()) { + pos = editor.getCursorPosition(); + if (pos.column > 0) + editor.navigateLeft(); + } + editor.setOverwrite(true); + editor.keyBinding.$data.buffer = ""; + editor.keyBinding.$data.state = "start"; + this.onVisualMode = false; + this.onVisualLineMode = false; + // Save recorded keystrokes + if(editor.commands.recording) { + editor.commands.toggleRecording(); + return editor.commands.macro; + } + else { + return []; + } + }, + getRightNthChar: function(editor, cursor, char, n) { + var line = editor.getSession().getLine(cursor.row); + var matches = line.substr(cursor.column + 1).split(char); + + return n < matches.length ? matches.slice(0, n).join(char).length : 0; + }, + getLeftNthChar: function(editor, cursor, char, n) { + var line = editor.getSession().getLine(cursor.row); + var matches = line.substr(0, cursor.column + 1).split(char); + + return n < matches.length ? matches.slice(-1 * n).join(char).length + 1: 0; + }, + toRealChar: function(char) { + if (char.length === 1) + return char; + + if (/^shift-./.test(char)) + return char[char.length - 1].toUpperCase(); + else + return ""; + }, + copyLine: function(editor) { + var pos = editor.getCursorPosition(); + editor.selection.clearSelection(); + editor.moveCursorTo(pos.row, pos.column); + editor.selection.selectLine(); + registers._default.isLine = true; + registers._default.text = editor.getCopyText().replace(/\n$/, ""); + editor.selection.clearSelection(); + editor.moveCursorTo(pos.row, pos.column); + } +}; +}); diff --git a/lib/ace/keyboard/vim/registers.js b/lib/ace/keyboard/vim/registers.js new file mode 100644 index 00000000..24b98314 --- /dev/null +++ b/lib/ace/keyboard/vim/registers.js @@ -0,0 +1,13 @@ + +define(function(require, exports, module) { + +"use strict"; + +module.exports = { + _default: { + text: "", + isLine: false + } +}; + +}); From 1a5ce521886f369d3cc102c60f527d1974a5af21 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 13 Dec 2011 19:15:30 +0400 Subject: [PATCH 06/47] adapt vim mode to work with ace --- lib/ace/keyboard/emacs.js | 4 +- lib/ace/keyboard/vim.js | 147 +++---------------------- lib/ace/keyboard/vim/commands.js | 24 ++-- lib/ace/keyboard/vim/keyboard.js | 32 +++--- lib/ace/keyboard/vim/maps/aliases.js | 2 +- lib/ace/keyboard/vim/maps/motions.js | 4 +- lib/ace/keyboard/vim/maps/operators.js | 6 +- lib/ace/keyboard/vim/maps/util.js | 4 +- lib/ace/keyboard/vim/registers.js | 2 +- 9 files changed, 54 insertions(+), 171 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index e912f589..5f9fbe8d 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -38,8 +38,8 @@ define(function(require, exports, module) { "use strict"; -var StateHandler = require("../state_handler").StateHandler; -var matchCharacterOnly = require("../state_handler").matchCharacterOnly; +var StateHandler = require("./state_handler").StateHandler; +var matchCharacterOnly = require("./state_handler").matchCharacterOnly; var emacsState = { start: [ diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 78367c2d..f1392ba9 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -1,138 +1,27 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Skywriter. - * - * The Initial Developer of the Original Code is - * Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Julian Viereck (julian.viereck@gmail.com) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - define(function(require, exports, module) { "use strict"; -var StateHandler = require("../state_handler").StateHandler; -var matchCharacterOnly = require("../state_handler").matchCharacterOnly; +"never use strict"; -var vimcommand = function(key, exec, then) { - return { - regex: [ "([0-9]*)", key ], - exec: exec, - params: [ - { - name: "times", - match: 1, - type: "number", - defaultValue: 1 - } - ], - then: then - } -} +var commands = require("./vim/commands"); +var util = require("./vim/maps/util"); -var vimStates = { - start: [ - { - key: "i", - then: "insertMode" - }, - { - key: "d", - then: "deleteMode" - }, - { - key: "a", - exec: "gotoright", - then: "insertMode" - }, - { - key: "shift-i", - exec: "gotolinestart", - then: "insertMode" - }, - { - key: "shift-a", - exec: "gotolineend", - then: "insertMode" - }, - { - key: "shift-c", - exec: "removetolineend", - then: "insertMode" - }, - { - key: "shift-r", - exec: "overwrite", - then: "replaceMode" - }, - vimcommand("(k|up)", "golineup"), - vimcommand("(j|down)", "golinedown"), - vimcommand("(l|right)", "gotoright"), - vimcommand("(h|left)", "gotoleft"), - { - key: "shift-g", - exec: "gotoend" - }, - vimcommand("b", "gotowordleft"), - vimcommand("e", "gotowordright"), - vimcommand("x", "del"), - vimcommand("shift-x", "backspace"), - vimcommand("shift-d", "removetolineend"), - vimcommand("u", "undo"), // [count] on this may/may not work, depending on browser implementation... - { - comment: "Catch some keyboard input to stop it here", - match: matchCharacterOnly - } - ], - insertMode: [ - { - key: "esc", - then: "start" - } - ], - replaceMode: [ - { - key: "esc", - exec: "overwrite", - then: "start" - } - ], - deleteMode: [ - { - key: "d", - exec: "removeline", - then: "start" - } - ] +exports.handler = require("./vim/keyboard").handler; + +exports.onCursorMove = function() { + commands.onCursorMove(); + onCursorMove.scheduled = false; }; -exports.handler = new StateHandler(vimStates); +exports.attach = function(editor){ + editor.on("click", exports.onCursorMove); + if (util.currentMode !== "insert") + commands.coreCommands.stop.exec(editor); +}; + +exports.detach = function(editor){ + editor.removeListener("click", exports.onCursorMove); + commands.coreCommands.start.exec(editor); +}; }); diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index a952047f..5e1a1912 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -8,14 +8,13 @@ define(function(require, exports, module) { -"use strict"; +"never use strict"; -var Editors = require("ext/editors/editors"); -var util = require("ext/vim/maps/util"); -var motions = require("ext/vim/maps/motions"); -var operators = require("ext/vim/maps/operators"); -var alias = require("ext/vim/maps/aliases"); -var registers = require("ext/vim/registers"); +var util = require("./maps/util"); +var motions = require("./maps/motions"); +var operators = require("./maps/operators"); +var alias = require("./maps/aliases"); +var registers = require("./registers"); var NUMBER = 1; var OPERATOR = 2; @@ -390,7 +389,7 @@ var inputBuffer = exports.inputBuffer = { a.fn(editor, editor.getSelectionRange(), a.count, param); this.reset(); } - handleCursorMove(); + handleCursorMove(editor); }, isAccepting: function(type) { @@ -411,7 +410,7 @@ function setPreviousCommand(fn) { inputBuffer.previous = { action: { action: { fn: fn } } }; } -exports.commands = { +exports.coreCommands = { start: { exec: function start(editor) { util.insertMode(editor); @@ -451,12 +450,13 @@ exports.commands = { util.insertMode(editor); setPreviousCommand(appendEnd); } + }, + builder: { + exec: function builder(editor) {} } }; -var handleCursorMove = exports.onCursorMove = function() { - var editor = Editors.currentEditor.amlEditor.$editor; - +var handleCursorMove = exports.onCursorMove = function(editor) { if(util.currentMode === 'insert' || handleCursorMove.running) return; else if(!editor.selection.isEmpty()) { diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js index 4786b5eb..a51a5e7a 100644 --- a/lib/ace/keyboard/vim/keyboard.js +++ b/lib/ace/keyboard/vim/keyboard.js @@ -1,11 +1,11 @@ define(function(require, exports, module) { -"use strict"; +"never use strict"; var StateHandler = require("ace/keyboard/state_handler").StateHandler; -var cmds = require("ext/vim/commands"); -var editors = require("ext/editors/editors"); +var cmds = require("./commands"); +var coreCommands = cmds.coreCommands; var matchChar = function(buffer, hashId, key, symbolicName, keyId) { // If no command keys are pressed, then catch the input. @@ -20,13 +20,9 @@ var matchChar = function(buffer, hashId, key, symbolicName, keyId) { keyId = String.fromCharCode(parseInt(keyId.replace("U+", "0x"), 10)); } - var editor = editors.currentEditor.amlEditor.$editor; - editor.commands.addCommand({ - name: "builder", - exec: function(editor) { - cmds.inputBuffer.push.call(cmds.inputBuffer, editor, symbolicName, keyId); - } - }); + coreCommands.builder.exec = function(editor) { + cmds.inputBuffer.push.call(cmds.inputBuffer, editor, symbolicName, keyId); + } } return matched; }; @@ -42,43 +38,43 @@ var states = exports.states = { start: [ // normal mode { key: "esc", - exec: "stop", + exec: coreCommands.stop, then: "start" }, { regex: "^i$", match: inIdleState, - exec: "start", + exec: coreCommands.start, then: "insertMode" }, { regex: "^shift-i$", match: inIdleState, - exec: "startBeginning", + exec: coreCommands.startBeginning, then: "insertMode" }, { regex: "^a$", match: inIdleState, - exec: "append", + exec: coreCommands.append, then: "insertMode" }, { regex: "^shift-a$", match: inIdleState, - exec: "appendEnd", + exec: coreCommands.appendEnd, then: "insertMode" }, { // The rest of input will be processed here match: matchChar, - exec: "builder" + exec: coreCommands.builder } ], insertMode: [ { key: "esc", - exec: "stop", + exec: coreCommands.stop, then: "start" }, { @@ -89,4 +85,4 @@ var states = exports.states = { }; exports.handler = new StateHandler(states); -}); +}); \ No newline at end of file diff --git a/lib/ace/keyboard/vim/maps/aliases.js b/lib/ace/keyboard/vim/maps/aliases.js index 35ceb93f..34b6d971 100644 --- a/lib/ace/keyboard/vim/maps/aliases.js +++ b/lib/ace/keyboard/vim/maps/aliases.js @@ -1,4 +1,4 @@ -"use strict" +"never use strict" define(function(require, exports, module) { module.exports = { diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index 3bf66aa4..89b77a18 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -1,8 +1,8 @@ -"use strict" +"never use strict" define(function(require, exports, module) { -var util = require("ext/vim/maps/util"); +var util = require("./util"); var keepScrollPosition = function(editor, fn) { var scrollTopRow = editor.renderer.getScrollTopRow(); diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js index a946d4c8..2b332dce 100644 --- a/lib/ace/keyboard/vim/maps/operators.js +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -1,9 +1,9 @@ define(function(require, exports, module) { -"use strict"; +"never use strict"; -var util = require("ext/vim/maps/util"); -var registers = require("ext/vim/registers"); +var util = require("./util"); +var registers = require("../registers"); module.exports = { "d": { diff --git a/lib/ace/keyboard/vim/maps/util.js b/lib/ace/keyboard/vim/maps/util.js index 18348125..60e120cf 100644 --- a/lib/ace/keyboard/vim/maps/util.js +++ b/lib/ace/keyboard/vim/maps/util.js @@ -1,7 +1,5 @@ -"use strict"; - define(function(require, exports, module) { -var registers = require("ext/vim/registers"); +var registers = require("../registers"); module.exports = { onVisualMode: false, diff --git a/lib/ace/keyboard/vim/registers.js b/lib/ace/keyboard/vim/registers.js index 24b98314..05fb765c 100644 --- a/lib/ace/keyboard/vim/registers.js +++ b/lib/ace/keyboard/vim/registers.js @@ -1,7 +1,7 @@ define(function(require, exports, module) { -"use strict"; +"never use strict"; module.exports = { _default: { From 76cbc3f543811b24b22bd90417dd8258ede90cf7 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 13 Dec 2011 19:38:04 +0400 Subject: [PATCH 07/47] add support for keyboardHandler.attach/detach --- lib/ace/keyboard/keybinding.js | 41 +++++++++++++++++++++------------- lib/ace/keyboard/vim.js | 11 ++++----- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js index 79fb7aa9..d1c9a0cf 100644 --- a/lib/ace/keyboard/keybinding.js +++ b/lib/ace/keyboard/keybinding.js @@ -42,7 +42,6 @@ define(function(require, exports, module) { var keyUtil = require("../lib/keys"); var event = require("../lib/event"); -require("../commands/default_commands"); var KeyBinding = function(editor) { this.$editor = editor; @@ -52,34 +51,44 @@ var KeyBinding = function(editor) { }; (function() { - this.setDefaultHandler = function(keyboardHandler) { + this.setDefaultHandler = function(kb) { this.removeKeyboardHandler(this.$defaultHandler); - this.$defaultHandler = keyboardHandler; - if (keyboardHandler) - this.$handlers.unshift(keyboardHandler); - this.$data = { }; + this.$defaultHandler = kb; + this.addKeyboardHandler(kb, 0); + this.$data = {editor: this.$editor}; }; - this.setKeyboardHandler = function(keyboardHandler) { - if (this.$handlers[this.$handlers.length - 1] == keyboardHandler) + this.setKeyboardHandler = function(kb) { + if (this.$handlers[this.$handlers.length - 1] == kb) return; - this.$data = { }; + this.$handlers = []; this.setDefaultHandler(this.$defaultHandler); - if (keyboardHandler) - this.$handlers.push(keyboardHandler); + this.addKeyboardHandler(kb, 1); }; - this.addKeyboardHandler = function(keyboardHandler) { - this.removeKeyboardHandler(keyboardHandler); - this.$handlers.push(keyboardHandler); + this.addKeyboardHandler = function(kb, pos) { + if (!kb) + return; + var i = this.$handlers.indexOf(kb); + if (i != -1) + this.$handlers.splice(i, 1); + + if (pos == undefined) + this.$handlers.push(kb); + else + this.$handlers.splice(pos, 0, kb); + + if (i == -1 && kb.attach) + kb.attach(this.$editor); }; - this.removeKeyboardHandler = function(keyboardHandler) { - var i = this.$handlers.indexOf(keyboardHandler); + this.removeKeyboardHandler = function(kb) { + var i = this.$handlers.indexOf(kb); if (i == -1) return false; this.$handlers.splice(i, 1); + kb.detach && kb.detach(this.$editor); return true; }; diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index f1392ba9..102414e8 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -8,20 +8,21 @@ var util = require("./vim/maps/util"); exports.handler = require("./vim/keyboard").handler; -exports.onCursorMove = function() { - commands.onCursorMove(); - onCursorMove.scheduled = false; +exports.onCursorMove = function(e) { + commands.onCursorMove(e.editor); + exports.onCursorMove.scheduled = false; }; -exports.attach = function(editor){ +exports.handler.attach = function(editor) { editor.on("click", exports.onCursorMove); if (util.currentMode !== "insert") commands.coreCommands.stop.exec(editor); }; -exports.detach = function(editor){ +exports.handler.detach = function(editor) { editor.removeListener("click", exports.onCursorMove); commands.coreCommands.start.exec(editor); + util.currentMode = "normal"; }; }); From 7edd5724efa15d83ec76be92c4ad31e10bcc8204 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 11:18:50 +0400 Subject: [PATCH 08/47] update to latest vim mode from cloud9 --- lib/ace/keyboard/vim/commands.js | 76 ++++++++++++++++++---------- lib/ace/keyboard/vim/keyboard.js | 2 +- lib/ace/keyboard/vim/maps/motions.js | 26 +++++++++- lib/ace/keyboard/vim/maps/util.js | 4 +- 4 files changed, 77 insertions(+), 31 deletions(-) diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index 5e1a1912..3312f4a1 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -16,15 +16,11 @@ var operators = require("./maps/operators"); var alias = require("./maps/aliases"); var registers = require("./registers"); -var NUMBER = 1; +var NUMBER = 1; var OPERATOR = 2; -var MOTION = 3; -var ACTION = 4; - -//var NORMAL_MODE = 0; -//var INSERT_MODE = 1; -//var VISUAL_MODE = 2; -//getSelectionLead +var MOTION = 3; +var ACTION = 4; +var HMARGIN = 8; // Minimum amount of line separation between margins; exports.searchStore = { current: "", @@ -39,11 +35,34 @@ exports.searchStore = { }; var repeat = function repeat(fn, count, args) { - count = parseInt(count); + count = parseInt(count, 10); while (0 < count--) fn.apply(this, args); }; +var toggleCase = function toggleCase(ch) { + if (ch.toUpperCase() === ch) + return ch.toLowerCase(); + else + return ch.toUpperCase(); +}; + +var ensureScrollMargin = function(editor) { + setTimeout(function() { + var curPos = editor.getCursorPosition().row; + var topRow = editor.renderer.layerConfig.firstRow; + var linesToBottom = editor.renderer.layerConfig.lastRow - curPos; + var linesToTop = curPos - topRow; + + if (linesToBottom >= 0 && linesToBottom < HMARGIN) { + editor.scrollToRow(topRow + (HMARGIN - linesToBottom)); + } + else if (linesToTop >= 0 && linesToTop < HMARGIN) { + editor.scrollToRow(topRow - (HMARGIN - linesToTop)); + } + }, 20); // Delay introduced to ensure scroll after async find operation. +}; + var actions = { "z": { param: true, @@ -83,8 +102,9 @@ var actions = { fn: function(editor, range, count, param) { editor.selection.selectWord(); editor.findNext(); + ensureScrollMargin(editor); var cursor = editor.selection.getCursor(); - var range = editor.session.getWordRange(cursor.row, cursor.column); + range = editor.session.getWordRange(cursor.row, cursor.column); editor.selection.setSelectionRange(range, true); } }, @@ -92,23 +112,32 @@ var actions = { fn: function(editor, range, count, param) { editor.selection.selectWord(); editor.findPrevious(); + ensureScrollMargin(editor); var cursor = editor.selection.getCursor(); - var range = editor.session.getWordRange(cursor.row, cursor.column); + range = editor.session.getWordRange(cursor.row, cursor.column); editor.selection.setSelectionRange(range, true); } }, "n": { fn: function(editor, range, count, param) { - editor.findNext(editor.getLastSearchOptions()); + var options = editor.getLastSearchOptions(); + options.backwards = false; + + editor.findNext(options); + + ensureScrollMargin(editor); editor.selection.clearSelection(); - //editor.navigateWordLeft(); } }, "shift-n": { fn: function(editor, range, count, param) { - editor.findPrevious(editor.getLastSearchOptions()); + var options = editor.getLastSearchOptions(); + options.backwards = true; + + editor.navigateWordLeft(); + editor.findPrevious(options); + ensureScrollMargin(editor); editor.selection.clearSelection(); - //editor.navigateWordLeft(); } }, "v": { @@ -231,9 +260,10 @@ var actions = { }, ".": { fn: function(editor, range, count, param) { - var previous = inputBuffer.previous; util.onInsertReplaySequence = inputBuffer.lastInsertCommands; - inputBuffer.exec(editor, previous.action, previous.param); + var previous = inputBuffer.previous; + if (previous) // If there is a previous action + inputBuffer.exec(editor, previous.action, previous.param); } } }; @@ -336,7 +366,7 @@ var inputBuffer = exports.inputBuffer = { var o = action.operator; var a = action.action; - if(o) { + if (o) { this.previous = { action: action, param: param @@ -489,12 +519,4 @@ var handleCursorMove = exports.onCursorMove = function(editor) { handleCursorMove.running = false; } }; - -function toggleCase(ch) { - if(ch.toUpperCase() === ch) - return ch.toLowerCase(); - else - return ch.toUpperCase(); -} - -}); \ No newline at end of file +}); diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js index a51a5e7a..08b02714 100644 --- a/lib/ace/keyboard/vim/keyboard.js +++ b/lib/ace/keyboard/vim/keyboard.js @@ -85,4 +85,4 @@ var states = exports.states = { }; exports.handler = new StateHandler(states); -}); \ No newline at end of file +}); diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index 89b77a18..1c9ec02a 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -1,4 +1,4 @@ -"never use strict" +"use strict" define(function(require, exports, module) { @@ -114,6 +114,30 @@ module.exports = { } } }, + "shift-f": { + param: true, + nav: function(editor, range, count, param) { + count = parseInt(count, 10) || 1; + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getLeftNthChar(editor, cursor, param, count); + + if (typeof column === "number") { + ed.selection.clearSelection(); // Why does it select in the first place? + ed.moveCursorTo(cursor.row, cursor.column - column + 1); + } + }, + sel: function(editor, range, count, param) { + count = parseInt(count, 10) || 1; + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getLeftNthChar(editor, cursor, param, count); + + if (typeof column === "number") { + ed.moveCursorTo(cursor.row, cursor.column - column + 1); + } + } + }, "t": { param: true, nav: function(editor, range, count, param) { diff --git a/lib/ace/keyboard/vim/maps/util.js b/lib/ace/keyboard/vim/maps/util.js index 60e120cf..ae280aac 100644 --- a/lib/ace/keyboard/vim/maps/util.js +++ b/lib/ace/keyboard/vim/maps/util.js @@ -88,9 +88,9 @@ module.exports = { }, getLeftNthChar: function(editor, cursor, char, n) { var line = editor.getSession().getLine(cursor.row); - var matches = line.substr(0, cursor.column + 1).split(char); + var matches = line.substr(0, cursor.column).split(char); - return n < matches.length ? matches.slice(-1 * n).join(char).length + 1: 0; + return n < matches.length ? matches.slice(-1 * n).join(char).length + 2 : 0; }, toRealChar: function(char) { if (char.length === 1) From a80828bab53ba7ea758afbbcaef8c69fa9213a66 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 11:26:10 +0400 Subject: [PATCH 09/47] better handling of token types in bracket_match.js --- lib/ace/edit_session/bracket_match.js | 42 +++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/ace/edit_session/bracket_match.js b/lib/ace/edit_session/bracket_match.js index e3add27a..8144462a 100644 --- a/lib/ace/edit_session/bracket_match.js +++ b/lib/ace/edit_session/bracket_match.js @@ -96,21 +96,24 @@ function BracketMatch() { "}": "{" }; - this.$findOpeningBracket = function(bracket, position) { + this.$findOpeningBracket = function(bracket, position, typeRe) { var openBracket = this.$brackets[bracket]; var depth = 1; var iterator = new TokenIterator(this, position.row, position.column); var token = iterator.getCurrentToken(); - if (!token) return null; + if (!token) + token = iterator.stepForward(); + if (!token) + return - // token.type contains a period-delimited list of token identifiers - // (e.g.: "constant.numeric" or "paren.lparen"). Create a pattern that - // matches any token containing the same identifiers or a subset. In - // addition, if token.type includes "rparen", then also match "lparen". - // So if type.token is "paren.rparen", then typeRe will match "lparen.paren". - var typeRe = new RegExp("(\\.?" + - token.type.replace(".", "|").replace("rparen", "lparen|rparen") + ")+"); + if (!typeRe){ + typeRe = new RegExp( + "(\\.?" + + token.type.replace(".", "\\.").replace("rparen", ".paren") + + ")+" + ); + } // Start searching in token, just before the character at position.column var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2; @@ -149,21 +152,24 @@ function BracketMatch() { return null; }; - this.$findClosingBracket = function(bracket, position) { + this.$findClosingBracket = function(bracket, position, typeRe) { var closingBracket = this.$brackets[bracket]; var depth = 1; var iterator = new TokenIterator(this, position.row, position.column); var token = iterator.getCurrentToken(); - if (!token) return null; + if (!token) + token = iterator.stepForward(); + if (!token) + return - // token.type contains a period-delimited list of token identifiers - // (e.g.: "constant.numeric" or "paren.lparen"). Create a pattern that - // matches any token containing the same identifiers or a subset. In - // addition, if token.type includes "lparen", then also match "rparen". - // So if type.token is "lparen.paren", then typeRe will match "paren.rparen". - var typeRe = new RegExp("(\\.?" + - token.type.replace(".", "|").replace("lparen", "lparen|rparen") + ")+"); + if (!typeRe){ + typeRe = new RegExp( + "(\\.?" + + token.type.replace(".", "\\.").replace("lparen", ".paren") + + ")+" + ); + } // Start searching in token, after the character at position.column var valueIndex = position.column - iterator.getCurrentTokenColumn(); From f9c8f7c11b72ba5da24b444bbcbfe186a8133cab Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 12:55:26 +0400 Subject: [PATCH 10/47] allow passing regexp as a needle for search --- lib/ace/search.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ace/search.js b/lib/ace/search.js index 846f3905..e6a6f922 100644 --- a/lib/ace/search.js +++ b/lib/ace/search.js @@ -275,6 +275,9 @@ Search.SELECTION = 2; }; this.$assembleRegExp = function() { + if (typeof this.$options.needle != 'string') + return this.$options.needle; + if (this.$options.regExp) { var needle = this.$options.needle; } else { From 0c7b310ecccd36bda76564ab8e86db4093c841e4 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 12:55:53 +0400 Subject: [PATCH 11/47] add alignCursor to virtual_renderer --- lib/ace/virtual_renderer.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index fdff0160..6735adcb 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -1011,6 +1011,16 @@ var VirtualRenderer = function(container, theme) { this.session.setScrollTop(row * this.lineHeight); }; + this.alignCursor = function(cursor, alignment) { + if (typeof cursor == "number") + cursor = {row: cursor, column: 0}; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + var offset = pos.top - this.$size.scrollerHeight * (alignment || 0); + + this.session.setScrollTop(offset); + }; + this.STEPS = 8; this.$calcSteps = function(fromValue, toValue){ var i = 0; From a2a304390700e44141c014c52c47bf2344394dc6 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 14:28:10 +0400 Subject: [PATCH 12/47] manual merge of ace++ branch --- lib/ace/keyboard/keybinding.js | 6 +- lib/ace/keyboard/state_handler.js | 2 + lib/ace/keyboard/vim.js | 5 +- lib/ace/keyboard/vim/commands.js | 162 ++++++----- lib/ace/keyboard/vim/keyboard.js | 121 +++----- lib/ace/keyboard/vim/maps/aliases.js | 13 +- lib/ace/keyboard/vim/maps/motions.js | 388 +++++++++++++++++++++---- lib/ace/keyboard/vim/maps/operators.js | 114 ++++---- lib/ace/keyboard/vim/maps/util.js | 110 ++++--- lib/ace/lib/keys.js | 2 +- lib/ace/mode/behaviour/cstyle.js | 2 +- 11 files changed, 599 insertions(+), 326 deletions(-) diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js index d1c9a0cf..1fe10538 100644 --- a/lib/ace/keyboard/keybinding.js +++ b/lib/ace/keyboard/keybinding.js @@ -116,7 +116,7 @@ var KeyBinding = function(editor) { if (toExecute.command != "null") success = commands.exec(toExecute.command, this.$editor, toExecute.args, e); else - success = true; + success = toExecute.stopEvent == true; if (success && e) event.stopEvent(e); @@ -130,9 +130,7 @@ var KeyBinding = function(editor) { }; this.onTextInput = function(text) { - var success = false; - if (text.length == 1) - success = this.$callKeyboardHandlers(0, text); + var success = this.$callKeyboardHandlers(-1, text); if (!success) this.$editor.commands.exec("insertstring", this.$editor, text); }; diff --git a/lib/ace/keyboard/state_handler.js b/lib/ace/keyboard/state_handler.js index 8e93ba30..c7efe552 100644 --- a/lib/ace/keyboard/state_handler.js +++ b/lib/ace/keyboard/state_handler.js @@ -202,6 +202,8 @@ StateHandler.prototype = { * This function is called by keyBinding. */ handleKeyboard: function(data, hashId, key, keyCode, e) { + if (hashId == -1) + hashId = 0 // If we pressed any command key but no other key, then ignore the input. // Otherwise "shift-" is added to the buffer, and later on "shift-g" // which results in "shift-shift-g" which doesn't make sense. diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 102414e8..0f73e764 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -1,5 +1,4 @@ define(function(require, exports, module) { -"use strict"; "never use strict"; @@ -9,7 +8,7 @@ var util = require("./vim/maps/util"); exports.handler = require("./vim/keyboard").handler; exports.onCursorMove = function(e) { - commands.onCursorMove(e.editor); + commands.onCursorMove(e.editor, e); exports.onCursorMove.scheduled = false; }; @@ -21,7 +20,7 @@ exports.handler.attach = function(editor) { exports.handler.detach = function(editor) { editor.removeListener("click", exports.onCursorMove); - commands.coreCommands.start.exec(editor); + util.noMode(editor); util.currentMode = "normal"; }; diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index 3312f4a1..77a30ac6 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -35,7 +35,6 @@ exports.searchStore = { }; var repeat = function repeat(fn, count, args) { - count = parseInt(count, 10); while (0 < count--) fn.apply(this, args); }; @@ -48,19 +47,22 @@ var toggleCase = function toggleCase(ch) { }; var ensureScrollMargin = function(editor) { - setTimeout(function() { - var curPos = editor.getCursorPosition().row; - var topRow = editor.renderer.layerConfig.firstRow; - var linesToBottom = editor.renderer.layerConfig.lastRow - curPos; - var linesToTop = curPos - topRow; + var renderer = editor.renderer + var pos = renderer.$cursorLayer.getPixelPosition(); - if (linesToBottom >= 0 && linesToBottom < HMARGIN) { - editor.scrollToRow(topRow + (HMARGIN - linesToBottom)); - } - else if (linesToTop >= 0 && linesToTop < HMARGIN) { - editor.scrollToRow(topRow - (HMARGIN - linesToTop)); - } - }, 20); // Delay introduced to ensure scroll after async find operation. + var top = pos.top; + + var margin = HMARGIN * renderer.layerConfig.lineHeight; + if (2 * margin > renderer.$size.scrollerHeight) + margin = renderer.$size.scrollerHeight / 2 + + if (renderer.scrollTop > top - margin) { + renderer.session.setScrollTop(top - margin); + } + + if (renderer.scrollTop + renderer.$size.scrollerHeight < top + margin + renderer.lineHeight) { + renderer.session.setScrollTop(top + margin + renderer.lineHeight - renderer.$size.scrollerHeight); + } }; var actions = { @@ -69,10 +71,13 @@ var actions = { fn: function(editor, range, count, param) { switch (param) { case "z": - editor.centerSelection(); + editor.alignCursor(null, 0.5); break; case "t": - editor.scrollToRow(editor.getCursorPosition().row); + editor.alignCursor(null, 0); + break; + case "b": + editor.alignCursor(null, 1); break; } } @@ -80,21 +85,30 @@ var actions = { "r": { param: true, fn: function(editor, range, count, param) { - param = util.toRealChar(param); if (param && param.length) { repeat(function() { editor.insert(param); }, count || 1); editor.navigateLeft(); } } }, - // "~" HACK - "shift-`": { + "R": { + fn: function(editor, range, count, param) { + util.insertMode(editor); + editor.setOverwrite(true); + } + }, + "~": { fn: function(editor, range, count) { repeat(function() { - var pos = editor.getCursorPosition(); - var line = editor.session.getLine(pos.row); - var ch = line[pos.column]; - editor.insert(toggleCase(ch)); + var range = editor.selection.getRange(); + if (range.isEmpty()) + range.end.column++ + var text = editor.session.getTextRange(range) + var toggled = text.toUpperCase() + if (toggled == text) + editor.navigateRight() + else + editor.session.replace(range, toggled); }, count || 1); } }, @@ -103,9 +117,8 @@ var actions = { editor.selection.selectWord(); editor.findNext(); ensureScrollMargin(editor); - var cursor = editor.selection.getCursor(); - range = editor.session.getWordRange(cursor.row, cursor.column); - editor.selection.setSelectionRange(range, true); + var r = editor.selection.getRange(); + editor.selection.setSelectionRange(r, true); } }, "#": { @@ -113,9 +126,8 @@ var actions = { editor.selection.selectWord(); editor.findPrevious(); ensureScrollMargin(editor); - var cursor = editor.selection.getCursor(); - range = editor.session.getWordRange(cursor.row, cursor.column); - editor.selection.setSelectionRange(range, true); + var r = editor.selection.getRange(); + editor.selection.setSelectionRange(r, true); } }, "n": { @@ -123,35 +135,39 @@ var actions = { var options = editor.getLastSearchOptions(); options.backwards = false; + editor.selection.moveCursorRight(); + editor.selection.clearSelection(); editor.findNext(options); ensureScrollMargin(editor); - editor.selection.clearSelection(); + var r = editor.selection.getRange(); + r.end.row = r.start.row; + r.end.column = r.start.column + editor.selection.setSelectionRange(r, true); } }, - "shift-n": { + "N": { fn: function(editor, range, count, param) { var options = editor.getLastSearchOptions(); options.backwards = true; - editor.navigateWordLeft(); editor.findPrevious(options); ensureScrollMargin(editor); - editor.selection.clearSelection(); + var r = editor.selection.getRange(); + r.end.row = r.start.row; + r.end.column = r.start.column + editor.selection.setSelectionRange(r, true); } }, "v": { fn: function(editor, range, count, param) { editor.selection.selectRight(); - util.onVisualMode = true; - util.onVisualLineMode = false; - var cursor = document.getElementsByClassName("ace_cursor")[0]; - cursor.style.display = "none"; - } + util.visualMode(editor, false); + }, + acceptsMotion: true }, - "shift-v": { + "V": { fn: function(editor, range, count, param) { - util.onVisualLineMode = true; //editor.selection.selectLine(); //editor.selection.selectLeft(); var row = editor.getCursorPosition().row; @@ -159,9 +175,12 @@ var actions = { editor.selection.moveCursorTo(row, 0); editor.selection.selectLineEnd(); editor.selection.visualLineStart = row; - } + + util.visualMode(editor, true); + }, + acceptsMotion: true }, - "shift-y": { + "Y": { fn: function(editor, range, count, param) { util.copyLine(editor); } @@ -186,7 +205,7 @@ var actions = { editor.selection.clearSelection(); } }, - "shift-p": { + "P": { fn: function(editor, range, count, param) { var defaultReg = registers._default; editor.setOverwrite(false); @@ -204,26 +223,26 @@ var actions = { editor.selection.clearSelection(); } }, - "shift-j": { + "J": { fn: function(editor, range, count, param) { - var pos = editor.getCursorPosition(); + var session = editor.session + range = editor.getSelectionRange(); + var pos = {row: range.start.row, column: range.start.column}; + count = count || range.end.row - range.start.row; + var maxRow = Math.min(pos.row + (count || 1), session.getLength() - 1); - if (editor.session.getLength() === pos.row + 1) - return; + range.start.column = session.getLine(pos.row).length; + range.end.column = session.getLine(maxRow).length; + range.end.row = maxRow; - var nextLine = editor.session.getLine(pos.row + 1); - var cleanLine = /^\s*(.*)$/.exec(nextLine)[1]; + var text = "" + for (var i = pos.row; i < maxRow; i++) { + var nextLine = session.getLine(i + 1); + text += " " + /^\s*(.*)$/.exec(nextLine)[1] || ""; + } - editor.navigateDown(); - editor.removeLines(); - - if (editor.session.getLength() > editor.getCursorPosition().row + 1) - editor.navigateUp(); - - editor.navigateLineEnd(); - editor.insert(" " + (cleanLine || "")); + session.replace(range, text); editor.moveCursorTo(pos.row, pos.column); - } }, "u": { @@ -281,11 +300,6 @@ var inputBuffer = exports.inputBuffer = { lastInsertCommands: [], push: function(editor, char, keyId) { - if (char && char.length > 1) { // There is a modifier key - if (!char[char.length - 1].match(/[A-za-z]/) && keyId) // It is a letter - char = keyId; - } - this.idle = false; var wObj = this.waitingForParam; if (wObj) { @@ -342,6 +356,9 @@ var inputBuffer = exports.inputBuffer = { else { this.exec(editor, actionObj); } + + if (actions[char].acceptsMotion) + this.idle = false; } else if (this.operator) { this.exec(editor, { operator: this.operator }, char); @@ -358,7 +375,7 @@ var inputBuffer = exports.inputBuffer = { getCount: function() { var count = this.currentCount; this.currentCount = ""; - return count; + return count && parseInt(count, 10); }, exec: function(editor, action, param) { @@ -366,6 +383,9 @@ var inputBuffer = exports.inputBuffer = { var o = action.operator; var a = action.action; + if (!param) + param = action.param; + if (o) { this.previous = { action: action, @@ -391,7 +411,7 @@ var inputBuffer = exports.inputBuffer = { else if (m) { var run = function(fn) { if (fn && typeof fn === "function") { // There should always be a motion - if (m.count) + if (m.count && !motionObj.handlesCount) repeat(fn, m.count, [editor, null, m.count, param]); else fn(editor, null, m.count, param); @@ -480,18 +500,15 @@ exports.coreCommands = { util.insertMode(editor); setPreviousCommand(appendEnd); } - }, - builder: { - exec: function builder(editor) {} } }; -var handleCursorMove = exports.onCursorMove = function(editor) { - if(util.currentMode === 'insert' || handleCursorMove.running) +var handleCursorMove = exports.onCursorMove = function(editor, e) { + if (util.currentMode === 'insert' || handleCursorMove.running) return; else if(!editor.selection.isEmpty()) { handleCursorMove.running = true; - if(util.onVisualLineMode) { + if (util.onVisualLineMode) { var originRow = editor.selection.visualLineStart; var cursorRow = editor.getCursorPosition().row; if(originRow <= cursorRow) { @@ -510,6 +527,11 @@ var handleCursorMove = exports.onCursorMove = function(editor) { return; } else { + if (e && (util.onVisualLineMode || util.onVisualMode)) { + editor.selection.clearSelection(); + util.normalMode(editor) + } + handleCursorMove.running = true; var pos = editor.getCursorPosition(); var lineLen = editor.session.getLine(pos.row).length; diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js index 08b02714..fab668d0 100644 --- a/lib/ace/keyboard/vim/keyboard.js +++ b/lib/ace/keyboard/vim/keyboard.js @@ -1,88 +1,61 @@ define(function(require, exports, module) { -"never use strict"; -var StateHandler = require("ace/keyboard/state_handler").StateHandler; +var keyUtil = require("../../lib/keys"); var cmds = require("./commands"); var coreCommands = cmds.coreCommands; -var matchChar = function(buffer, hashId, key, symbolicName, keyId) { - // If no command keys are pressed, then catch the input. - // If only the shift key is pressed and a character key, then - // catch that input as well. - // Otherwise, we let the input got through. - var matched = ((hashId === 0) || (((hashId === 1) || (hashId === 4)) && key.length === 1)); - //console.log("INFO", arguments) - if (matched) { - if (keyId) { - keyId = String.fromCharCode(parseInt(keyId.replace("U+", "0x"), 10)); - } +var startCommands = { + 'i': { + command: coreCommands.start + }, + 'I': { + command: coreCommands.startBeginning, + }, + 'a': { + command: coreCommands.append + }, + 'A': { + command: coreCommands.appendEnd + } +}; - coreCommands.builder.exec = function(editor) { - cmds.inputBuffer.push.call(cmds.inputBuffer, editor, symbolicName, keyId); +exports.handler = { + handleKeyboard: function(data, hashId, key, keyCode, e) { + // ignore command keys (shift, ctrl etc.) + // Otherwise "shift-" is added to the buffer, and later on "shift-g" + // which results in "shift-shift-g" which doesn't make sense. + if (hashId != 0 && (key == "" || key == "\x00")) + return null; + + if (hashId == 1) + key = 'ctrl-' + key; + + if (data.state == 'start') { + if (hashId == -1 || hashId == 1) { + if (cmds.inputBuffer.idle && startCommands[key]) + return startCommands[key]; + + return { command: { + exec: function(editor) {cmds.inputBuffer.push(editor, key);} + } }; + } // wait for input + else if (key.length === 1 && (hashId == 0 || hashId === 4)) { //no modifier || shift + return {command: "null", stopEvent: false} + } else if (key == 'esc') { + return {command: coreCommands.stop}; + } + } else { + if (key == 'esc' || key == 'ctrl-[') { + data.state = 'start' + return {command: coreCommands.stop} + } } } - return matched; -}; +} -var inIdleState = function() { - if (cmds.inputBuffer.idle) { - return true; - } - return false; -}; -var states = exports.states = { - start: [ // normal mode - { - key: "esc", - exec: coreCommands.stop, - then: "start" - }, - { - regex: "^i$", - match: inIdleState, - exec: coreCommands.start, - then: "insertMode" - }, - { - regex: "^shift-i$", - match: inIdleState, - exec: coreCommands.startBeginning, - then: "insertMode" - }, - { - regex: "^a$", - match: inIdleState, - exec: coreCommands.append, - then: "insertMode" - }, - { - regex: "^shift-a$", - match: inIdleState, - exec: coreCommands.appendEnd, - then: "insertMode" - }, - { - // The rest of input will be processed here - match: matchChar, - exec: coreCommands.builder - } - ], - insertMode: [ - { - key: "esc", - exec: coreCommands.stop, - then: "start" - }, - { - key: "backspace", - exec: "backspace" - } - ] -}; -exports.handler = new StateHandler(states); -}); +}); \ No newline at end of file diff --git a/lib/ace/keyboard/vim/maps/aliases.js b/lib/ace/keyboard/vim/maps/aliases.js index 34b6d971..ac495dcd 100644 --- a/lib/ace/keyboard/vim/maps/aliases.js +++ b/lib/ace/keyboard/vim/maps/aliases.js @@ -12,7 +12,7 @@ module.exports = { count: 1 } }, - "shift-x": { + "X": { operator: { char: "d", count: 1 @@ -22,7 +22,7 @@ module.exports = { count: 1 } }, - "shift-d": { + "D": { operator: { char: "d", count: 1 @@ -32,7 +32,7 @@ module.exports = { count: 1 } }, - "shift-c": { + "C": { operator: { char: "c", count: 1 @@ -52,15 +52,12 @@ module.exports = { count: 1 } }, - "shift-s": { + "S": { operator: { char: "c", count: 1 }, - motion: { - char: "l", - count: 1 - } + param: "c" } }; }); diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index 1c9ec02a..42972870 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -1,4 +1,4 @@ -"use strict" +"never use strict" define(function(require, exports, module) { @@ -12,23 +12,199 @@ var keepScrollPosition = function(editor, fn) { editor.renderer.scrollToRow(editor.getCursorPosition().row - diff); }; +function Motion(getRange, type){ + if (type == 'extend') + var extend = true + else + var reverse = type + + this.nav = function(editor) { + var r = getRange(editor); + if (!r) + return + if (!r.end) + var a = r + else if (reverse) + var a = r.start + else + var a = r.end + + editor.clearSelection() + editor.moveCursorTo(a.row, a.column); + } + this.sel = function(editor){ + var r = getRange(editor); + if (!r) + return + if (extend) + return editor.selection.setSelectionRange(r); + + if (!r.end) + var a = r + else if (reverse) + var a = r.start + else + var a = r.end + + editor.selection.selectTo(a.row, a.column); + } +} + +var nonWordRe = /[\s.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/; +var wordSeparatorRe = /[.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/; +var whiteRe = /\s/; +var StringStream = function(editor, cursor) { + var sel = editor.selection + this.range = sel.getRange() + cursor = cursor || sel.selectionLead + this.row = cursor.row; + this.col = cursor.column; + var line = editor.session.getLine(this.row); + var maxRow = editor.session.getLength() + this.ch = line[this.col] || '\n' + this.skippedLines = 0 + + this.next = function() { + this.ch = line[++this.col] || this.handleNewLine(1) + //this.debug() + return this.ch + } + this.prev = function() { + this.ch = line[--this.col] || this.handleNewLine(-1) + //this.debug() + return this.ch + } + this.peek = function(dir) { + var ch = line[this.col + dir] + if (ch) + return ch + if (dir == -1) + return '\n' + if (this.col == line.length - 1) + return '\n' + return editor.session.getLine(this.row + 1)[0] || '\n' + } + + this.handleNewLine = function(dir) { + if (dir == 1){ + if (this.col == line.length) + return '\n' + if (this.row == maxRow - 1) + return '' + this.col = 0 + this.row ++ + line = editor.session.getLine(this.row) + this.skippedLines++ + return line[0] || '\n' + } + if (dir == -1) { + if (this.row == 0) + return '' + this.row -- + line = editor.session.getLine(this.row) + this.col = line.length; + this.skippedLines-- + return '\n' + } + } + this.debug = function() { + console.log(line.substring(0, this.col)+'|'+this.ch+'\''+this.col+'\''+line.substr(this.col+1)) + } +} + +var Search = require("ace/search").Search; +var search = new Search(); + +function find(editor, needle, dir) { + search.$options.needle = needle + search.$options.backwards = dir == -1 + return search.find(editor.session) +} +var lang = require("ace/lib/lang"); +var Range = require("ace/range").Range; + module.exports = { - "w": { - nav: function(editor) { - editor.navigateWordRight(); - }, - sel: function(editor) { - editor.selection.selectWordRight(); + "w": new Motion(function(editor) { + var str = new StringStream(editor) + + if (str.ch && wordSeparatorRe.test(str.ch)) { + while (str.ch && wordSeparatorRe.test(str.ch)) + str.next() + } else { + while (str.ch && !nonWordRe.test(str.ch)) + str.next() } - }, - "b": { - nav: function(editor) { - editor.navigateWordLeft(); - }, - sel: function(editor) { - editor.selection.selectWordLeft(); + while (str.ch && whiteRe.test(str.ch) && str.skippedLines < 2) + str.next() + + str.skippedLines == 2 && str.prev() + return {column: str.col, row: str.row} + }), + "W": new Motion(function(editor) { + var str = new StringStream(editor) + while(str.ch && !(whiteRe.test(str.ch) && !whiteRe.test(str.peek(1))) && str.skippedLines < 2) + str.next() + if (str.skippedLines == 2) + str.prev() + else + str.next() + + return {column: str.col, row: str.row} + }), + "b": new Motion(function(editor) { + var str = new StringStream(editor) + + str.prev() + while (str.ch && whiteRe.test(str.ch) && str.skippedLines > -2) + str.prev() + + if (str.ch && wordSeparatorRe.test(str.ch)) { + while (str.ch && wordSeparatorRe.test(str.ch)) + str.prev() + } else { + while (str.ch && !nonWordRe.test(str.ch)) + str.prev() } - }, + str.ch && str.next(); + return {column: str.col, row: str.row} + }), + "B": new Motion(function(editor) { + var str = new StringStream(editor) + str.prev() + while(str.ch && !(!whiteRe.test(str.ch) && whiteRe.test(str.peek(-1))) && str.skippedLines > -2) + str.prev() + + if (str.skippedLines == -2) + str.next() + + return {column: str.col, row: str.row} + }, true), + "e": new Motion(function(editor) { + var str = new StringStream(editor) + + str.next() + while (str.ch && whiteRe.test(str.ch)) + str.next() + + if (str.ch && wordSeparatorRe.test(str.ch)) { + while (str.ch && wordSeparatorRe.test(str.ch)) + str.next() + } else { + while (str.ch && !nonWordRe.test(str.ch)) + str.next() + } + str.ch && str.prev() + return {column: str.col, row: str.row} + }), + "E": new Motion(function(editor) { + var str = new StringStream(editor) + str.next() + while(str.ch && !(!whiteRe.test(str.ch) && whiteRe.test(str.peek(1)))) + str.next() + + return {column: str.col, row: str.row} + }), + "l": { nav: function(editor) { editor.navigateRight(); @@ -72,12 +248,41 @@ module.exports = { editor.selection.selectDown(); } }, + "i": { param: true, sel: function(editor, range, count, param) { switch (param) { case "w": editor.selection.selectWord(); + break; + case "W": + editor.selection.selectAWord(); + break; + case "(": + case "{": + case "[": + var cursor = editor.getCursorPosition() + var end = editor.session.$findClosingBracket(param, cursor, /paren/) + if (!end) + return + var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/) + if (!start) + return + start.column ++; + editor.selection.setSelectionRange(Range.fromPoints(start, end)) + break + case "'": + case "\"": + case "/": + var end = find(editor, param, 1) + if (!end) + return + var start = find(editor, param, -1) + if (!start) + return + editor.selection.setSelectionRange(Range.fromPoints(start.end, end.start)) + break } } }, @@ -87,16 +292,46 @@ module.exports = { switch (param) { case "w": editor.selection.selectAWord(); + break; + case "W": + editor.selection.selectAWord(); + break; + case "(": + case "{": + case "[": + var cursor = editor.getCursorPosition() + var end = editor.session.$findClosingBracket(param, cursor, /paren/) + if (!end) + return + var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/) + if (!start) + return + end.column ++; + editor.selection.setSelectionRange(Range.fromPoints(start, end)) + break + case "'": + case "\"": + case "/": + var end = find(editor, param, 1) + if (!end) + return + var start = find(editor, param, -1) + if (!start) + return + end.column ++; + editor.selection.setSelectionRange(Range.fromPoints(start.start, end.end)) + break } } }, + "f": { param: true, + handlesCount: true, nav: function(editor, range, count, param) { - count = parseInt(count, 10) || 1; var ed = editor; var cursor = ed.getCursorPosition(); - var column = util.getRightNthChar(editor, cursor, param, count); + var column = util.getRightNthChar(editor, cursor, param, count || 1); if (typeof column === "number") { ed.selection.clearSelection(); // Why does it select in the first place? @@ -104,18 +339,18 @@ module.exports = { } }, sel: function(editor, range, count, param) { - count = parseInt(count, 10) || 1; var ed = editor; var cursor = ed.getCursorPosition(); - var column = util.getRightNthChar(editor, cursor, param, count); + var column = util.getRightNthChar(editor, cursor, param, count || 1); if (typeof column === "number") { ed.moveCursorTo(cursor.row, column + cursor.column + 1); } } }, - "shift-f": { + "F": { param: true, + handlesCount: true, nav: function(editor, range, count, param) { count = parseInt(count, 10) || 1; var ed = editor; @@ -124,27 +359,26 @@ module.exports = { if (typeof column === "number") { ed.selection.clearSelection(); // Why does it select in the first place? - ed.moveCursorTo(cursor.row, cursor.column - column + 1); + ed.moveCursorTo(cursor.row, cursor.column - column - 1); } }, sel: function(editor, range, count, param) { - count = parseInt(count, 10) || 1; var ed = editor; var cursor = ed.getCursorPosition(); - var column = util.getLeftNthChar(editor, cursor, param, count); + var column = util.getLeftNthChar(editor, cursor, param, count || 1); if (typeof column === "number") { - ed.moveCursorTo(cursor.row, cursor.column - column + 1); + ed.moveCursorTo(cursor.row, cursor.column - column - 1); } } }, "t": { param: true, + handlesCount: true, nav: function(editor, range, count, param) { - count = parseInt(count, 10) || 1; var ed = editor; var cursor = ed.getCursorPosition(); - var column = util.getRightNthChar(editor, cursor, param, count); + var column = util.getRightNthChar(editor, cursor, param, count || 1); if (typeof column === "number") { ed.selection.clearSelection(); // Why does it select in the first place? @@ -152,16 +386,39 @@ module.exports = { } }, sel: function(editor, range, count, param) { - count = parseInt(count, 10) || 1; var ed = editor; var cursor = ed.getCursorPosition(); - var column = util.getRightNthChar(editor, cursor, param, count); + var column = util.getRightNthChar(editor, cursor, param, count || 1); if (typeof column === "number") { ed.moveCursorTo(cursor.row, column + cursor.column); } } }, + "T": { + param: true, + handlesCount: true, + nav: function(editor, range, count, param) { + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getLeftNthChar(editor, cursor, param, count || 1); + + if (typeof column === "number") { + ed.selection.clearSelection(); // Why does it select in the first place? + ed.moveCursorTo(cursor.row, -column + cursor.column); + } + }, + sel: function(editor, range, count, param) { + var ed = editor; + var cursor = ed.getCursorPosition(); + var column = util.getLeftNthChar(editor, cursor, param, count || 1); + + if (typeof column === "number") { + ed.moveCursorTo(cursor.row, -column + cursor.column); + } + } + }, + "^": { nav: function(editor) { editor.navigateLineStart(); @@ -188,41 +445,20 @@ module.exports = { ed.selectTo(ed.selection.selectionLead.row, 0); } }, - "shift-g": { + "G": { nav: function(editor, range, count, param) { - count = parseInt(count, 10); if (!count && count !== 0) { // Stupid JS count = editor.session.getLength(); } editor.gotoLine(count); }, sel: function(editor, range, count, param) { - count = parseInt(count, 10); if (!count && count !== 0) { // Stupid JS count = editor.session.getLength(); } editor.selection.selectTo(count, 0); } }, - "ctrl-d": { - nav: function(editor, range, count, param) { - editor.selection.clearSelection(); - keepScrollPosition(editor, editor.gotoPageDown); - }, - sel: function(editor, range, count, param) { - keepScrollPosition(editor, editor.selectPageDown); - } - }, - "ctrl-u": { - nav: function(editor, range, count, param) { - editor.selection.clearSelection(); - keepScrollPosition(editor, editor.gotoPageUp); - - }, - sel: function(editor, range, count, param) { - keepScrollPosition(editor, editor.selectPageUp); - } - }, "g": { param: true, nav: function(editor, range, count, param) { @@ -235,6 +471,10 @@ module.exports = { break; case "g": editor.gotoLine(count || 0); + case "u": + editor.gotoLine(count || 0); + case "U": + editor.gotoLine(count || 0); } }, sel: function(editor, range, count, param) { @@ -264,7 +504,7 @@ module.exports = { } } }, - "shift-o": { + "O": { nav: function(editor, range, count, param) { var row = editor.getCursorPosition().row; count = count || 1; @@ -285,18 +525,42 @@ module.exports = { } } }, - "%": { - nav: function(editor, range, count, param) { - var cursor = editor.getCursorPosition(); - var match = editor.session.findMatchingBracket({ - row: cursor.row, - column: cursor.column + 1 - }); - - if (match) - editor.moveCursorTo(match.row, match.column); + "%": new Motion(function(editor){ + var brRe = /[\[\]{}()]/g; + var cursor = editor.getCursorPosition(); + var ch = editor.session.getLine(cursor.row)[cursor.column] + if (!brRe.test(ch)){ + var range = find(editor, brRe) + if (!range) + return + cursor = range.start } - } + var match = editor.session.findMatchingBracket({ + row: cursor.row, + column: cursor.column + 1 + }); + + return match + }), + "ctrl-d": { + nav: function(editor, range, count, param) { + editor.selection.clearSelection(); + keepScrollPosition(editor, editor.gotoPageDown); + }, + sel: function(editor, range, count, param) { + keepScrollPosition(editor, editor.selectPageDown); + } + }, + "ctrl-u": { + nav: function(editor, range, count, param) { + editor.selection.clearSelection(); + keepScrollPosition(editor, editor.gotoPageUp); + + }, + sel: function(editor, range, count, param) { + keepScrollPosition(editor, editor.selectPageUp); + } + }, }; module.exports.backspace = module.exports.left = module.exports.h; diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js index 2b332dce..2dd67ee7 100644 --- a/lib/ace/keyboard/vim/maps/operators.js +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -7,63 +7,65 @@ var registers = require("../registers"); module.exports = { "d": { - selFn: function(editor, range, count, param) { - registers._default.text = editor.getCopyText(); - registers._default.isLine = util.onVisualLineMode; - if(util.onVisualLineMode) - editor.removeLines(); - else - editor.session.remove(range); - util.normalMode(editor); - }, - fn: function(editor, range, count, param) { - count = parseInt(count || 1, 10); - switch (param) { - case "d": - registers._default.text = ""; - registers._default.isLine = true; - for (var i=0; i": { selFn: function(editor, range, count, param) { - count = parseInt(count || 1, 10); + count = count || 1; for (var i = 0; i < count; i++) { editor.indent(); } @@ -126,14 +128,14 @@ module.exports = { }, "<": { selFn: function(editor, range, count, param) { - count = parseInt(count || 1, 10); + count = count || 1; for (var i = 0; i < count; i++) { editor.blockOutdent(); } util.normalMode(editor); }, fn: function(editor, range, count, param) { - count = parseInt(count || 1, 10); + count = count || 1; switch (param) { case "<": var pos = editor.getCursorPosition(); diff --git a/lib/ace/keyboard/vim/maps/util.js b/lib/ace/keyboard/vim/maps/util.js index ae280aac..1956aec3 100644 --- a/lib/ace/keyboard/vim/maps/util.js +++ b/lib/ace/keyboard/vim/maps/util.js @@ -1,78 +1,75 @@ define(function(require, exports, module) { var registers = require("../registers"); +var dom = require("ace/lib/dom"); +dom.importCssString('.insert-mode. ace_cursor{\ + border-left: 2px solid #333333;\ +}\ +.ace_dark.insert-mode .ace_cursor{\ + border-left: 2px solid #eeeeee;\ +}\ +.normal-mode .ace_cursor{\ + border: 0!important;\ + background-color: red;\ + opacity: 0.5;\ +}', 'vimMode'); + module.exports = { onVisualMode: false, onVisualLineMode: false, currentMode: 'normal', + noMode: function(editor) { + editor.unsetStyle('insert-mode'); + editor.unsetStyle('normal-mode'); + if (editor.commands.recording) + editor.commands.toggleRecording(); + }, insertMode: function(editor) { - var _self = this; - var theme = editor && editor.getTheme() || "ace/theme/textmate"; + this.currentMode = 'insert'; + // Switch editor to insert mode + editor.setStyle('insert-mode'); + editor.unsetStyle('normal-mode'); - require(["require", theme], function (require) { - var isDarkTheme = require(theme).isDark; - _self.currentMode = 'insert'; - // Switch editor to insert mode - editor.unsetStyle('insert-mode'); - - var cursor = document.getElementsByClassName("ace_cursor")[0]; - if (cursor) { - cursor.style.display = null; - cursor.style.backgroundColor = null; - cursor.style.opacity = null; - cursor.style.border = null; - cursor.style.borderLeftColor = isDarkTheme? "#eeeeee" : "#333333"; - cursor.style.borderLeftStyle = "solid"; - cursor.style.borderLeftWidth = "2px"; - } - - editor.setOverwrite(false); - editor.keyBinding.$data.buffer = ""; - editor.keyBinding.$data.state = "insertMode"; - _self.onVisualMode = false; - _self.onVisualLineMode = false; - if(_self.onInsertReplaySequence) { - // Ok, we're apparently replaying ("."), so let's do it - editor.commands.macro = _self.onInsertReplaySequence; - editor.commands.replay(editor); - _self.onInsertReplaySequence = null; - _self.normalMode(editor); - } else { - // Record any movements, insertions in insert mode - if(!editor.commands.recording) - editor.commands.toggleRecording(); - } - }); + editor.setOverwrite(false); + editor.keyBinding.$data.buffer = ""; + editor.keyBinding.$data.state = "insertMode"; + this.onVisualMode = false; + this.onVisualLineMode = false; + if(this.onInsertReplaySequence) { + // Ok, we're apparently replaying ("."), so let's do it + editor.commands.macro = this.onInsertReplaySequence; + editor.commands.replay(editor); + this.onInsertReplaySequence = null; + this.normalMode(editor); + } else { + // Record any movements, insertions in insert mode + if(!editor.commands.recording) + editor.commands.toggleRecording(); + } }, normalMode: function(editor) { // Switch editor to normal mode this.currentMode = 'normal'; + editor.unsetStyle('insert-mode'); editor.setStyle('normal-mode'); editor.clearSelection(); - var cursor = document.getElementsByClassName("ace_cursor")[0]; - if (cursor) { - cursor.style.display = null; - cursor.style.backgroundColor = "red"; - cursor.style.opacity = ".5"; - cursor.style.border = "0"; - } - var pos; if (!editor.getOverwrite()) { pos = editor.getCursorPosition(); if (pos.column > 0) editor.navigateLeft(); } + editor.setOverwrite(true); editor.keyBinding.$data.buffer = ""; editor.keyBinding.$data.state = "start"; this.onVisualMode = false; this.onVisualLineMode = false; // Save recorded keystrokes - if(editor.commands.recording) { + if (editor.commands.recording) { editor.commands.toggleRecording(); return editor.commands.macro; } @@ -80,17 +77,36 @@ module.exports = { return []; } }, + visualMode: function(editor, lineMode) { + if ( + (this.onVisualLineMode && lineMode) + || (this.onVisualMode && !lineMode) + ) { + this.normalMode(editor); + return; + } + + editor.setStyle('insert-mode'); + editor.unsetStyle('normal-mode'); + + if (lineMode) { + this.onVisualLineMode = true; + } else { + this.onVisualMode = true; + this.onVisualLineMode = false; + } + }, getRightNthChar: function(editor, cursor, char, n) { var line = editor.getSession().getLine(cursor.row); var matches = line.substr(cursor.column + 1).split(char); - return n < matches.length ? matches.slice(0, n).join(char).length : 0; + return n < matches.length ? matches.slice(0, n).join(char).length : null; }, getLeftNthChar: function(editor, cursor, char, n) { var line = editor.getSession().getLine(cursor.row); var matches = line.substr(0, cursor.column).split(char); - return n < matches.length ? matches.slice(-1 * n).join(char).length + 2 : 0; + return n < matches.length ? matches.slice(-1 * n).join(char).length : null; }, toRealChar: function(char) { if (char.length === 1) diff --git a/lib/ace/lib/keys.js b/lib/ace/lib/keys.js index ef231039..30719ce1 100644 --- a/lib/ace/lib/keys.js +++ b/lib/ace/lib/keys.js @@ -102,7 +102,7 @@ var Keys = (function() { 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.', 188: ',', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\', - 221: ']', 222: '\"' + 221: ']', 222: '\'' } }; diff --git a/lib/ace/mode/behaviour/cstyle.js b/lib/ace/mode/behaviour/cstyle.js index 3db838ad..f180fa39 100644 --- a/lib/ace/mode/behaviour/cstyle.js +++ b/lib/ace/mode/behaviour/cstyle.js @@ -40,7 +40,7 @@ define(function(require, exports, module) { "use strict"; var oop = require("../../lib/oop"); -var Behaviour = require('../behaviour').Behaviour; +var Behaviour = require("../behaviour").Behaviour; var CstyleBehaviour = function () { From ab50850be6684de0baaadbf84c2dd5ed40e0c95d Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 14:58:42 +0400 Subject: [PATCH 13/47] add stupid semicolons; --- lib/ace/keyboard/vim/commands.js | 29 ++-- lib/ace/keyboard/vim/keyboard.js | 14 +- lib/ace/keyboard/vim/maps/motions.js | 190 +++++++++++++-------------- 3 files changed, 112 insertions(+), 121 deletions(-) diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index 77a30ac6..ca1cf54f 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -39,22 +39,15 @@ var repeat = function repeat(fn, count, args) { fn.apply(this, args); }; -var toggleCase = function toggleCase(ch) { - if (ch.toUpperCase() === ch) - return ch.toLowerCase(); - else - return ch.toUpperCase(); -}; - var ensureScrollMargin = function(editor) { - var renderer = editor.renderer + var renderer = editor.renderer; var pos = renderer.$cursorLayer.getPixelPosition(); var top = pos.top; var margin = HMARGIN * renderer.layerConfig.lineHeight; if (2 * margin > renderer.$size.scrollerHeight) - margin = renderer.$size.scrollerHeight / 2 + margin = renderer.$size.scrollerHeight / 2; if (renderer.scrollTop > top - margin) { renderer.session.setScrollTop(top - margin); @@ -102,11 +95,11 @@ var actions = { repeat(function() { var range = editor.selection.getRange(); if (range.isEmpty()) - range.end.column++ - var text = editor.session.getTextRange(range) - var toggled = text.toUpperCase() + range.end.column++; + var text = editor.session.getTextRange(range); + var toggled = text.toUpperCase(); if (toggled == text) - editor.navigateRight() + editor.navigateRight(); else editor.session.replace(range, toggled); }, count || 1); @@ -142,7 +135,7 @@ var actions = { ensureScrollMargin(editor); var r = editor.selection.getRange(); r.end.row = r.start.row; - r.end.column = r.start.column + r.end.column = r.start.column; editor.selection.setSelectionRange(r, true); } }, @@ -155,7 +148,7 @@ var actions = { ensureScrollMargin(editor); var r = editor.selection.getRange(); r.end.row = r.start.row; - r.end.column = r.start.column + r.end.column = r.start.column; editor.selection.setSelectionRange(r, true); } }, @@ -225,7 +218,7 @@ var actions = { }, "J": { fn: function(editor, range, count, param) { - var session = editor.session + var session = editor.session; range = editor.getSelectionRange(); var pos = {row: range.start.row, column: range.start.column}; count = count || range.end.row - range.start.row; @@ -235,7 +228,7 @@ var actions = { range.end.column = session.getLine(maxRow).length; range.end.row = maxRow; - var text = "" + var text = ""; for (var i = pos.row; i < maxRow; i++) { var nextLine = session.getLine(i + 1); text += " " + /^\s*(.*)$/.exec(nextLine)[1] || ""; @@ -529,7 +522,7 @@ var handleCursorMove = exports.onCursorMove = function(editor, e) { else { if (e && (util.onVisualLineMode || util.onVisualMode)) { editor.selection.clearSelection(); - util.normalMode(editor) + util.normalMode(editor); } handleCursorMove.running = true; diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js index fab668d0..d184a6d3 100644 --- a/lib/ace/keyboard/vim/keyboard.js +++ b/lib/ace/keyboard/vim/keyboard.js @@ -12,7 +12,7 @@ var startCommands = { command: coreCommands.start }, 'I': { - command: coreCommands.startBeginning, + command: coreCommands.startBeginning }, 'a': { command: coreCommands.append @@ -42,20 +42,18 @@ exports.handler = { exec: function(editor) {cmds.inputBuffer.push(editor, key);} } }; } // wait for input - else if (key.length === 1 && (hashId == 0 || hashId === 4)) { //no modifier || shift - return {command: "null", stopEvent: false} + else if (key.length == 1 && (hashId == 0 || hashId == 4)) { //no modifier || shift + return {command: "null", stopEvent: false}; } else if (key == 'esc') { return {command: coreCommands.stop}; } } else { if (key == 'esc' || key == 'ctrl-[') { - data.state = 'start' - return {command: coreCommands.stop} + data.state = 'start'; + return {command: coreCommands.stop}; } } } -} - - +}; }); \ No newline at end of file diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index 42972870..f65ac015 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -14,37 +14,37 @@ var keepScrollPosition = function(editor, fn) { function Motion(getRange, type){ if (type == 'extend') - var extend = true + var extend = true; else - var reverse = type + var reverse = type; this.nav = function(editor) { var r = getRange(editor); if (!r) - return + return; if (!r.end) - var a = r + var a = r; else if (reverse) - var a = r.start + var a = r.start; else - var a = r.end + var a = r.end; - editor.clearSelection() + editor.clearSelection(); editor.moveCursorTo(a.row, a.column); } this.sel = function(editor){ var r = getRange(editor); if (!r) - return + return; if (extend) return editor.selection.setSelectionRange(r); if (!r.end) - var a = r + var a = r; else if (reverse) - var a = r.start + var a = r.start; else - var a = r.end + var a = r.end; editor.selection.selectTo(a.row, a.column); } @@ -54,61 +54,61 @@ var nonWordRe = /[\s.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/; var wordSeparatorRe = /[.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/; var whiteRe = /\s/; var StringStream = function(editor, cursor) { - var sel = editor.selection - this.range = sel.getRange() - cursor = cursor || sel.selectionLead + var sel = editor.selection; + this.range = sel.getRange(); + cursor = cursor || sel.selectionLead; this.row = cursor.row; this.col = cursor.column; var line = editor.session.getLine(this.row); var maxRow = editor.session.getLength() this.ch = line[this.col] || '\n' - this.skippedLines = 0 + this.skippedLines = 0; this.next = function() { - this.ch = line[++this.col] || this.handleNewLine(1) + this.ch = line[++this.col] || this.handleNewLine(1); //this.debug() - return this.ch + return this.ch; } this.prev = function() { - this.ch = line[--this.col] || this.handleNewLine(-1) + this.ch = line[--this.col] || this.handleNewLine(-1); //this.debug() - return this.ch + return this.ch; } this.peek = function(dir) { - var ch = line[this.col + dir] + var ch = line[this.col + dir]; if (ch) - return ch + return ch; if (dir == -1) - return '\n' + return '\n'; if (this.col == line.length - 1) - return '\n' - return editor.session.getLine(this.row + 1)[0] || '\n' + return '\n'; + return editor.session.getLine(this.row + 1)[0] || '\n'; } this.handleNewLine = function(dir) { if (dir == 1){ if (this.col == line.length) - return '\n' + return '\n'; if (this.row == maxRow - 1) - return '' - this.col = 0 - this.row ++ - line = editor.session.getLine(this.row) - this.skippedLines++ - return line[0] || '\n' + return ''; + this.col = 0; + this.row ++; + line = editor.session.getLine(this.row); + this.skippedLines++; + return line[0] || '\n'; } if (dir == -1) { if (this.row == 0) - return '' - this.row -- - line = editor.session.getLine(this.row) + return ''; + this.row --; + line = editor.session.getLine(this.row); this.col = line.length; - this.skippedLines-- - return '\n' + this.skippedLines--; + return '\n'; } } this.debug = function() { - console.log(line.substring(0, this.col)+'|'+this.ch+'\''+this.col+'\''+line.substr(this.col+1)) + console.log(line.substring(0, this.col)+'|'+this.ch+'\''+this.col+'\''+line.substr(this.col+1)); } } @@ -116,93 +116,93 @@ var Search = require("ace/search").Search; var search = new Search(); function find(editor, needle, dir) { - search.$options.needle = needle - search.$options.backwards = dir == -1 - return search.find(editor.session) + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(editor.session); } -var lang = require("ace/lib/lang"); + var Range = require("ace/range").Range; module.exports = { "w": new Motion(function(editor) { - var str = new StringStream(editor) + var str = new StringStream(editor); if (str.ch && wordSeparatorRe.test(str.ch)) { while (str.ch && wordSeparatorRe.test(str.ch)) - str.next() + str.next(); } else { while (str.ch && !nonWordRe.test(str.ch)) - str.next() + str.next(); } while (str.ch && whiteRe.test(str.ch) && str.skippedLines < 2) - str.next() + str.next(); - str.skippedLines == 2 && str.prev() - return {column: str.col, row: str.row} + str.skippedLines == 2 && str.prev(); + return {column: str.col, row: str.row}; }), "W": new Motion(function(editor) { - var str = new StringStream(editor) + var str = new StringStream(editor); while(str.ch && !(whiteRe.test(str.ch) && !whiteRe.test(str.peek(1))) && str.skippedLines < 2) - str.next() + str.next(); if (str.skippedLines == 2) - str.prev() + str.prev(); else - str.next() + str.next(); return {column: str.col, row: str.row} }), "b": new Motion(function(editor) { - var str = new StringStream(editor) + var str = new StringStream(editor); - str.prev() + str.prev(); while (str.ch && whiteRe.test(str.ch) && str.skippedLines > -2) - str.prev() + str.prev(); if (str.ch && wordSeparatorRe.test(str.ch)) { while (str.ch && wordSeparatorRe.test(str.ch)) - str.prev() + str.prev(); } else { while (str.ch && !nonWordRe.test(str.ch)) - str.prev() + str.prev(); } str.ch && str.next(); - return {column: str.col, row: str.row} + return {column: str.col, row: str.row}; }), "B": new Motion(function(editor) { var str = new StringStream(editor) - str.prev() + str.prev(); while(str.ch && !(!whiteRe.test(str.ch) && whiteRe.test(str.peek(-1))) && str.skippedLines > -2) - str.prev() + str.prev(); if (str.skippedLines == -2) - str.next() + str.next(); - return {column: str.col, row: str.row} + return {column: str.col, row: str.row}; }, true), "e": new Motion(function(editor) { - var str = new StringStream(editor) + var str = new StringStream(editor); - str.next() + str.next(); while (str.ch && whiteRe.test(str.ch)) - str.next() + str.next(); if (str.ch && wordSeparatorRe.test(str.ch)) { while (str.ch && wordSeparatorRe.test(str.ch)) - str.next() + str.next(); } else { while (str.ch && !nonWordRe.test(str.ch)) - str.next() + str.next(); } - str.ch && str.prev() - return {column: str.col, row: str.row} + str.ch && str.prev(); + return {column: str.col, row: str.row}; }), "E": new Motion(function(editor) { - var str = new StringStream(editor) - str.next() + var str = new StringStream(editor); + str.next(); while(str.ch && !(!whiteRe.test(str.ch) && whiteRe.test(str.peek(1)))) - str.next() + str.next(); - return {column: str.col, row: str.row} + return {column: str.col, row: str.row}; }), "l": { @@ -265,10 +265,10 @@ module.exports = { var cursor = editor.getCursorPosition() var end = editor.session.$findClosingBracket(param, cursor, /paren/) if (!end) - return + return; var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/) if (!start) - return + return; start.column ++; editor.selection.setSelectionRange(Range.fromPoints(start, end)) break @@ -277,10 +277,10 @@ module.exports = { case "/": var end = find(editor, param, 1) if (!end) - return + return; var start = find(editor, param, -1) if (!start) - return + return; editor.selection.setSelectionRange(Range.fromPoints(start.end, end.start)) break } @@ -299,28 +299,28 @@ module.exports = { case "(": case "{": case "[": - var cursor = editor.getCursorPosition() - var end = editor.session.$findClosingBracket(param, cursor, /paren/) + var cursor = editor.getCursorPosition(); + var end = editor.session.$findClosingBracket(param, cursor, /paren/); if (!end) - return - var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/) + return; + var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/); if (!start) - return + return; end.column ++; - editor.selection.setSelectionRange(Range.fromPoints(start, end)) - break + editor.selection.setSelectionRange(Range.fromPoints(start, end)); + break; case "'": case "\"": case "/": - var end = find(editor, param, 1) + var end = find(editor, param, 1); if (!end) - return - var start = find(editor, param, -1) + return; + var start = find(editor, param, -1); if (!start) - return + return; end.column ++; - editor.selection.setSelectionRange(Range.fromPoints(start.start, end.end)) - break + editor.selection.setSelectionRange(Range.fromPoints(start.start, end.end)); + break; } } }, @@ -528,19 +528,19 @@ module.exports = { "%": new Motion(function(editor){ var brRe = /[\[\]{}()]/g; var cursor = editor.getCursorPosition(); - var ch = editor.session.getLine(cursor.row)[cursor.column] - if (!brRe.test(ch)){ - var range = find(editor, brRe) + var ch = editor.session.getLine(cursor.row)[cursor.column]; + if (!brRe.test(ch)) { + var range = find(editor, brRe); if (!range) - return - cursor = range.start + return; + cursor = range.start; } var match = editor.session.findMatchingBracket({ row: cursor.row, column: cursor.column + 1 }); - return match + return match; }), "ctrl-d": { nav: function(editor, range, count, param) { From 4923280c8921169967c117aedd8193e9a6d6c114 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Apr 2012 15:18:18 +0400 Subject: [PATCH 14/47] consistently use "paren.lparen" for all modes --- lib/ace/mode/pgsql_highlight_rules.js | 2 +- lib/ace/mode/python_highlight_rules.js | 2 +- lib/ace/mode/sh_highlight_rules.js | 2 +- lib/ace/mode/sql_highlight_rules.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ace/mode/pgsql_highlight_rules.js b/lib/ace/mode/pgsql_highlight_rules.js index d15c94da..ca381b14 100755 --- a/lib/ace/mode/pgsql_highlight_rules.js +++ b/lib/ace/mode/pgsql_highlight_rules.js @@ -424,7 +424,7 @@ var PgsqlHighlightRules = function() { "\\?\\||\\?\\|\\||@|@\\-@|@>|@@|@@@|\\^|\\||\\|\\&>|\\|/|\\|>>|\\|\\||\\|\\|/|~|~\\*|~<=~|~<~|" + "~=|~>=~|~>~|~~|~~\\*" }, { - token : "lparen.paren", + token : "paren.lparen", regex : "[\\(]" }, { token : "paren.rparen", diff --git a/lib/ace/mode/python_highlight_rules.js b/lib/ace/mode/python_highlight_rules.js index 7e00e339..b9e5e48c 100644 --- a/lib/ace/mode/python_highlight_rules.js +++ b/lib/ace/mode/python_highlight_rules.js @@ -146,7 +146,7 @@ var PythonHighlightRules = function() { token : "keyword.operator", regex : "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|=" }, { - token : "lparen.paren", + token : "paren.lparen", regex : "[\\[\\(\\{]" }, { token : "paren.rparen", diff --git a/lib/ace/mode/sh_highlight_rules.js b/lib/ace/mode/sh_highlight_rules.js index dbfdc969..d416ab7a 100644 --- a/lib/ace/mode/sh_highlight_rules.js +++ b/lib/ace/mode/sh_highlight_rules.js @@ -127,7 +127,7 @@ var ShHighlightRules = function() { token : "keyword.operator", regex : "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!=" }, { - token : "lparen.paren", + token : "paren.lparen", regex : "[\\[\\(\\{]" }, { token : "paren.rparen", diff --git a/lib/ace/mode/sql_highlight_rules.js b/lib/ace/mode/sql_highlight_rules.js index e80d6648..dc099edc 100644 --- a/lib/ace/mode/sql_highlight_rules.js +++ b/lib/ace/mode/sql_highlight_rules.js @@ -70,7 +70,7 @@ var SqlHighlightRules = function() { token : "keyword.operator", regex : "\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|=" }, { - token : "lparen.paren", + token : "paren.lparen", regex : "[\\(]" }, { token : "paren.rparen", From 674cc0d3faecd39103cc5e18eda5462218693b6b Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 23 Apr 2012 17:47:17 +0400 Subject: [PATCH 15/47] properly remove old keybindings when adding new one --- lib/ace/keyboard/keybinding.js | 5 +++-- lib/ace/keyboard/vim/maps/util.js | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js index 1fe10538..cea77f50 100644 --- a/lib/ace/keyboard/keybinding.js +++ b/lib/ace/keyboard/keybinding.js @@ -62,8 +62,9 @@ var KeyBinding = function(editor) { if (this.$handlers[this.$handlers.length - 1] == kb) return; - this.$handlers = []; - this.setDefaultHandler(this.$defaultHandler); + while (this.$handlers[1]) + this.removeKeyboardHandler(this.$handlers[1]); + this.addKeyboardHandler(kb, 1); }; diff --git a/lib/ace/keyboard/vim/maps/util.js b/lib/ace/keyboard/vim/maps/util.js index 1956aec3..54e66681 100644 --- a/lib/ace/keyboard/vim/maps/util.js +++ b/lib/ace/keyboard/vim/maps/util.js @@ -23,6 +23,7 @@ module.exports = { editor.unsetStyle('normal-mode'); if (editor.commands.recording) editor.commands.toggleRecording(); + editor.setOverwrite(false); }, insertMode: function(editor) { this.currentMode = 'insert'; From 100d078ba320a168923688f792c0b77ec0f7dc60 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 24 Apr 2012 12:37:13 +0400 Subject: [PATCH 16/47] add license comments --- lib/ace/keyboard/vim.js | 41 ++++++++++++++++++++++-- lib/ace/keyboard/vim/commands.js | 44 ++++++++++++++++++++++---- lib/ace/keyboard/vim/keyboard.js | 39 ++++++++++++++++++++++- lib/ace/keyboard/vim/maps/aliases.js | 39 ++++++++++++++++++++++- lib/ace/keyboard/vim/maps/motions.js | 40 ++++++++++++++++++++++- lib/ace/keyboard/vim/maps/operators.js | 37 ++++++++++++++++++++++ lib/ace/keyboard/vim/registers.js | 36 +++++++++++++++++++++ 7 files changed, 264 insertions(+), 12 deletions(-) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 0f73e764..663812be 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -1,6 +1,43 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * Harutyun Amirjanyan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + define(function(require, exports, module) { - -"never use strict"; +"use strict"; var commands = require("./vim/commands"); var util = require("./vim/maps/util"); diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index ca1cf54f..9f802a42 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -1,11 +1,41 @@ -/** - * Vim mode for the Cloud9 IDE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * - * @author Sergi Mansilla - * @copyright 2011, Ajax.org B.V. - * @license GPLv3 - */ - + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * Harutyun Amirjanyan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + define(function(require, exports, module) { "never use strict"; diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js index d184a6d3..a252adfe 100644 --- a/lib/ace/keyboard/vim/keyboard.js +++ b/lib/ace/keyboard/vim/keyboard.js @@ -1,4 +1,41 @@ - +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * Harutyun Amirjanyan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + define(function(require, exports, module) { diff --git a/lib/ace/keyboard/vim/maps/aliases.js b/lib/ace/keyboard/vim/maps/aliases.js index ac495dcd..6708a5c3 100644 --- a/lib/ace/keyboard/vim/maps/aliases.js +++ b/lib/ace/keyboard/vim/maps/aliases.js @@ -1,4 +1,41 @@ -"never use strict" +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict" define(function(require, exports, module) { module.exports = { diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index f65ac015..ec97b298 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -1,4 +1,42 @@ -"never use strict" +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * Harutyun Amirjanyan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict" define(function(require, exports, module) { diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js index 2dd67ee7..14ff9fe9 100644 --- a/lib/ace/keyboard/vim/maps/operators.js +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -1,3 +1,40 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + define(function(require, exports, module) { "never use strict"; diff --git a/lib/ace/keyboard/vim/registers.js b/lib/ace/keyboard/vim/registers.js index 05fb765c..95fecdf2 100644 --- a/lib/ace/keyboard/vim/registers.js +++ b/lib/ace/keyboard/vim/registers.js @@ -1,3 +1,39 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sergi Mansilla + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { From 59358495f5b964a0ed5e21676e6e4e4e95bf74cd Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 27 Apr 2012 23:39:24 +0400 Subject: [PATCH 17/47] simplify vim keyboard handler --- lib/ace/keyboard/vim.js | 82 +++++++++++++++++++++------ lib/ace/keyboard/vim/keyboard.js | 96 -------------------------------- 2 files changed, 66 insertions(+), 112 deletions(-) delete mode 100644 lib/ace/keyboard/vim/keyboard.js diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 663812be..74bb1dd2 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -35,30 +35,80 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ - + define(function(require, exports, module) { "use strict"; -var commands = require("./vim/commands"); +var keyUtil = require("../lib/keys"); +var cmds = require("./vim/commands"); +var coreCommands = cmds.coreCommands; var util = require("./vim/maps/util"); -exports.handler = require("./vim/keyboard").handler; +var startCommands = { + 'i': { + command: coreCommands.start + }, + 'I': { + command: coreCommands.startBeginning + }, + 'a': { + command: coreCommands.append + }, + 'A': { + command: coreCommands.appendEnd + } +}; + +exports.handler = { + handleKeyboard: function(data, hashId, key, keyCode, e) { + // ignore command keys (shift, ctrl etc.) + // Otherwise "shift-" is added to the buffer, and later on "shift-g" + // which results in "shift-shift-g" which doesn't make sense. + if (hashId != 0 && (key == "" || key == "\x00")) + return null; + + if (hashId == 1) + key = 'ctrl-' + key; + + if (data.state == 'start') { + if (hashId == -1 || hashId == 1) { + if (cmds.inputBuffer.idle && startCommands[key]) + return startCommands[key]; + + return { command: { + exec: function(editor) {cmds.inputBuffer.push(editor, key);} + } }; + } // wait for input + else if (key.length == 1 && (hashId == 0 || hashId == 4)) { //no modifier || shift + return {command: "null", stopEvent: false}; + } else if (key == 'esc') { + return {command: coreCommands.stop}; + } + } else { + if (key == 'esc' || key == 'ctrl-[') { + data.state = 'start'; + return {command: coreCommands.stop}; + } + } + }, + + attach: function(editor) { + editor.on("click", exports.onCursorMove); + if (util.currentMode !== "insert") + cmds.coreCommands.stop.exec(editor); + }, + + detach: function(editor) { + editor.removeListener("click", exports.onCursorMove); + util.noMode(editor); + util.currentMode = "normal"; + } +}; + exports.onCursorMove = function(e) { - commands.onCursorMove(e.editor, e); + cmds.onCursorMove(e.editor, e); exports.onCursorMove.scheduled = false; }; -exports.handler.attach = function(editor) { - editor.on("click", exports.onCursorMove); - if (util.currentMode !== "insert") - commands.coreCommands.stop.exec(editor); -}; - -exports.handler.detach = function(editor) { - editor.removeListener("click", exports.onCursorMove); - util.noMode(editor); - util.currentMode = "normal"; -}; - }); diff --git a/lib/ace/keyboard/vim/keyboard.js b/lib/ace/keyboard/vim/keyboard.js deleted file mode 100644 index a252adfe..00000000 --- a/lib/ace/keyboard/vim/keyboard.js +++ /dev/null @@ -1,96 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Ajax.org Code Editor (ACE). - * - * The Initial Developer of the Original Code is - * Ajax.org B.V. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Sergi Mansilla - * Harutyun Amirjanyan - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -define(function(require, exports, module) { - - -var keyUtil = require("../../lib/keys"); -var cmds = require("./commands"); -var coreCommands = cmds.coreCommands; - - -var startCommands = { - 'i': { - command: coreCommands.start - }, - 'I': { - command: coreCommands.startBeginning - }, - 'a': { - command: coreCommands.append - }, - 'A': { - command: coreCommands.appendEnd - } -}; - -exports.handler = { - handleKeyboard: function(data, hashId, key, keyCode, e) { - // ignore command keys (shift, ctrl etc.) - // Otherwise "shift-" is added to the buffer, and later on "shift-g" - // which results in "shift-shift-g" which doesn't make sense. - if (hashId != 0 && (key == "" || key == "\x00")) - return null; - - if (hashId == 1) - key = 'ctrl-' + key; - - if (data.state == 'start') { - if (hashId == -1 || hashId == 1) { - if (cmds.inputBuffer.idle && startCommands[key]) - return startCommands[key]; - - return { command: { - exec: function(editor) {cmds.inputBuffer.push(editor, key);} - } }; - } // wait for input - else if (key.length == 1 && (hashId == 0 || hashId == 4)) { //no modifier || shift - return {command: "null", stopEvent: false}; - } else if (key == 'esc') { - return {command: coreCommands.stop}; - } - } else { - if (key == 'esc' || key == 'ctrl-[') { - data.state = 'start'; - return {command: coreCommands.stop}; - } - } - } -}; - -}); \ No newline at end of file From 5a9e6d4cb9565ebae8fb6ffb5d8fcf68d34bfad0 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 27 Apr 2012 23:46:21 +0400 Subject: [PATCH 18/47] ctrl-f: pagedown ctrl-b: pageup --- lib/ace/keyboard/vim.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 74bb1dd2..9e6d5bd4 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -56,7 +56,13 @@ var startCommands = { }, 'A': { command: coreCommands.appendEnd - } + }, + 'ctrl-f': { + command: "gotopagedown" + }, + 'ctrl-b': { + command: "gotopageup" + }, }; exports.handler = { From 48450d1ab90b19ec3ad665b15b918daa0bbeabec Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 28 Apr 2012 10:58:23 +0400 Subject: [PATCH 19/47] vim ctrl-w in insert mode --- lib/ace/keyboard/vim.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 9e6d5bd4..a94e9dc8 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -94,6 +94,8 @@ exports.handler = { if (key == 'esc' || key == 'ctrl-[') { data.state = 'start'; return {command: coreCommands.stop}; + } else if (key == "ctrl-w") { + return {command: "removewordleft"}; } } }, From b1c5129e5a3a804aa224c73b2cbde34b2deb856a Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 29 Apr 2012 23:29:35 +0400 Subject: [PATCH 20/47] emacs mode --- lib/ace/keyboard/emacs.js | 427 ++++++++++++++++++++++-------- lib/ace/keyboard/hash_handler.js | 7 +- lib/ace/keyboard/keybinding.js | 2 +- lib/ace/keyboard/vim.js | 2 +- lib/ace/keyboard/vim/maps/util.js | 2 +- 5 files changed, 331 insertions(+), 109 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 5f9fbe8d..e2c65e9d 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -20,6 +20,7 @@ * * Contributor(s): * Julian Viereck (julian.viereck@gmail.com) + * Harutyun Amirjanyan (harutyun@c9.io) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -38,113 +39,329 @@ define(function(require, exports, module) { "use strict"; -var StateHandler = require("./state_handler").StateHandler; -var matchCharacterOnly = require("./state_handler").matchCharacterOnly; +var dom = require("../lib/dom"); -var emacsState = { - start: [ - { - key: "ctrl-x", - then: "c-x" - }, - { - regex: [ "(?:command-([0-9]*))*", "(down|ctrl-n)" ], - exec: "golinedown", - params: [ - { - name: "times", - match: 1, - type: "number", - defaultValue: 1 - } - ] - }, - { - regex: [ "(?:command-([0-9]*))*", "(right|ctrl-f)" ], - exec: "gotoright", - params: [ - { - name: "times", - match: 1, - type: "number", - defaultValue: 1 - } - ] - }, - { - regex: [ "(?:command-([0-9]*))*", "(up|ctrl-p)" ], - exec: "golineup", - params: [ - { - name: "times", - match: 1, - type: "number", - defaultValue: 1 - } - ] - }, - { - regex: [ "(?:command-([0-9]*))*", "(left|ctrl-b)" ], - exec: "gotoleft", - params: [ - { - name: "times", - match: 1, - type: "number", - defaultValue: 1 - } - ] - }, - { - comment: "This binding matches all printable characters except numbers as long as they are no numbers and print them n times.", - regex: [ "(?:command-([0-9]*))", "([^0-9]+)*" ], - match: matchCharacterOnly, - exec: "inserttext", - params: [ - { - name: "times", - match: 1, - type: "number", - defaultValue: "1" - }, - { - name: "text", - match: 2 - } - ] - }, - { - comment: "This binding matches numbers as long as there is no meta_number in the buffer.", - regex: [ "(command-[0-9]*)*", "([0-9]+)" ], - match: matchCharacterOnly, - disallowMatches: [ 1 ], - exec: "inserttext", - params: [ - { - name: "text", - match: 2, - type: "text" - } - ] - }, - { - regex: [ "command-([0-9]*)", "(command-[0-9]|[0-9])" ], - comment: "Stops execution if the regex /meta_[0-9]+/ matches to avoid resetting the buffer." - } - ], - "c-x": [ - { - key: "ctrl-g", - then: "start" - }, - { - key: "ctrl-s", - exec: "save", - then: "start" - } - ] +var screenToTextBlockCoordinates = function(pageX, pageY) { + var canvasPos = this.scroller.getBoundingClientRect(); + + var col = Math.floor( + (pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth + ); + var row = Math.floor( + (pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight + ); + + return this.session.screenToDocumentPosition(row, col); +}; + +var HashHandler = require("./hash_handler").HashHandler; +exports.handler = new HashHandler(); + +var initialized = false; +exports.handler.attach = function(editor) { + if (!initialized) { + initialized = true; + dom.importCssString('\ + .emacs-mode .ace_cursor{\ + border: 2px rgba(50,250,50,0.8) solid!important;\ + -moz-box-sizing: border-box!important;\ + box-sizing: border-box!important;\ + background-color: rgba(0,250,0,0.9);\ + opacity: 0.5;\ + }\ + .emacs-mode .ace_cursor.ace_hidden{\ + opacity: 1;\ + background-color: transparent;\ + }\ + .emacs-mode .ace_cursor.ace_overwrite {\ + opacity: 1;\ + background-color: transparent;\ + border-width: 0 0 2px 2px !important;\ + }\ + .emacs-mode .ace_text-layer {\ + z-index: 4\ + }\ + .emacs-mode .ace_cursor-layer {\ + z-index: 2\ + }', 'emacsMode' + ); + } + + editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates; + editor.setStyle("emacs-mode"); +}; + +exports.handler.detach = function(editor) { + delete editor.renderer.screenToTextCoordinates; + editor.unsetStyle("emacs-mode"); +}; + + +var keys = require("../lib/keys").KEY_MODS; +var eMods = { + C: "ctrl", S: "shift", M: "alt" +}; +["S-C-M", "S-C", "S-M", "C-M", "S", "C", "M"].forEach(function(c) { + var hashId = 0; + c.split("-").forEach(function(c){ + hashId = hashId | keys[eMods[c]]; + }); + eMods[hashId] = c.toLowerCase() + "-"; +}); + +exports.handler.bindKey = function(key, command) { + if (!key) + return; + + var ckb = this.commmandKeyBinding; + key.split("|").forEach(function(keyPart) { + keyPart = keyPart.toLowerCase(); + ckb[keyPart] = command; + keyPart = keyPart.split(" ")[0]; + if (!ckb[keyPart]) + ckb[keyPart] = "null"; + }, this); +}; + + +exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { + if (hashId == -1) { + if (data.count) { + var str = Array(data.count + 1).join(key); + data.count = null; + return {command: "insertstring", args: str}; + } + } + + if (key == "\x00") + return; + + var modifier = eMods[hashId]; + if (modifier == "c-" || data.universalArgument) { + var count = parseInt(key[key.length - 1]); + if (count) { + data.count = count; + return {command: "null"}; + } + } + data.universalArgument = false; + + if (modifier) + key = modifier + key; + + if (data.keyChain) + key = data.keyChain += " " + key; + + var command = this.commmandKeyBinding[key]; + data.keyChain = command == "null" ? key : ""; + + if (!command) + return; + + if (command == "null") + return {command: "null"}; + + if (command == "universalArgument") { + data.universalArgument = true; + return {command: "null"}; + } + + if (typeof command != "string") { + var args = command.args; + command = command.command; + } + + if (typeof command == "string") { + command = this.commands[command] || data.editor.commands.commands[command]; + } + + if (!command.readonly && !command.isYank) + data.lastCommand = null; + + if (data.count) { + var count = data.count; + data.count = 0; + return { + args: args, + command: { + exec: function(editor, args) { + for (var i = 0; i < count; i++) + command.exec(editor, args); + } + } + }; + } + + return {command: command, args: args}; +}; + +exports.emacsKeys = { + // movement + "Up|C-p" : "golineup", + "Down|C-n" : "golinedown", + "Left|C-b" : "gotoleft", + "Right|C-f" : "gotoright", + "C-Left|M-b" : "gotowordleft", + "C-Right|M-f" : "gotowordright", + "Home|C-a" : "gotolinestart", + "End|C-e" : "gotolineend", + "C-Home|S-M-,": "gotostart", + "C-End|S-M-." : "gotoend", + + // selection + "S-Up|S-C-p" : "selectup", + "S-Down|S-C-n" : "selectdown", + "S-Left|S-C-b" : "selectleft", + "S-Right|S-C-f" : "selectright", + "S-C-Left|S-M-b" : "selectwordleft", + "S-C-Right|S-M-f" : "selectwordright", + "S-Home|S-C-a" : "selecttolinestart", + "S-End|S-C-e" : "selecttolineend", + "S-C-Home" : "selecttostart", + "S-C-End" : "selecttoend", + + "C-l|M-s" : "centerselection", + "M-g": "gotoline", + "C-x C-p": "selectall", + + // todo fix these + "C-Down": "gotopagedown", + "C-Up": "gotopageup", + "PageDown|C-v": "gotopagedown", + "PageUp|M-v": "gotopageup", + "S-C-Down": "selectpagedown", + "S-C-Up": "selectpageup", + "C-s": "findnext", + "C-r": "findprevious", + "M-C-s": "findnext", + "M-C-r": "findprevious", + "S-M-5": "replace", + + // basic editing + "Backspace": "backspace", + "Delete|C-d": "del", + "Return|C-m": {command: "insertstring", args: "\n"}, // "newline" + "C-o": "splitline", + + "M-d|C-Delete": {command: "killWord", args: "right"}, + "C-Backspace|M-Backspace|M-Delete": {command: "killWord", args: "left"}, + "C-k": "killLine", + + "C-y|S-Delete": "yank", + "M-y": "yankRotate", + "C-g": "keyboardQuit", + + "C-w": "killRegion", + "M-w": "killRingSave", + + "C-Space": "setMark", + "C-x C-x": "exchangePointAndMark", + + "C-t": "transposeletters", + + "M-u": "touppercase", + "M-l": "tolowercase", + "M-/": "autocomplete", + "C-u": "universalArgument", + "M-;": "togglecomment", + + "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" + + // todo + // "M-x" "C-x C-t" "M-t" "M-c" "F11" "C-M- "M-q" +}; + + +exports.handler.bindKeys(exports.emacsKeys); + +exports.handler.addCommands({ + selectRectangularRegion: function(editor) { + editor.multiSelect.toggleBlockSelection(); + }, + setMark: function() { + }, + exchangePointAndMark: { + exec: function(editor) { + var range = editor.selection.getRange(); + editor.selection.setSelectionRange(range, !editor.selection.isBackwards()); + }, + readonly: true, + multiselectAction: "forEach" + }, + killWord: { + exec: function(editor, dir) { + editor.clearSelection(); + if (dir == "left") + editor.selection.selectWordLeft(); + else + editor.selection.selectWordRight(); + + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + exports.killRing.add(text); + + editor.session.remove(range); + editor.clearSelection(); + }, + multiselectAction: "forEach" + }, + killLine: function(editor) { + editor.selection.selectLine(); + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + exports.killRing.add(text); + + editor.session.remove(range); + editor.clearSelection(); + }, + yank: function(editor) { + editor.onPaste(exports.killRing.get()); + editor.keyBinding.$data.lastCommand = "yank"; + }, + yankRotate: function(editor) { + if (editor.keyBinding.$data.lastCommand != "yank") + return; + + editor.undo(); + editor.onPaste(exports.killRing.rotate()); + editor.keyBinding.$data.lastCommand = "yank"; + }, + killRegion: function(editor) { + exports.killRing.add(editor.getCopyText()); + editor.cut(); + }, + killRingSave: function(editor) { + exports.killRing.add(editor.getCopyText()); + } +}); + +var commands = exports.handler.commands; +commands.yank.isYank = true; +commands.yankRotate.isYank = true; + +exports.killRing = { + $data: [], + add: function(str) { + str && this.$data.push(str); + if (this.$data.length > 30) + this.$data.shift(); + }, + get: function() { + return this.$data[this.$data.length - 1] || ""; + }, + pop: function() { + if (this.$data.length > 1) + this.$data.pop(); + return this.get(); + }, + rotate: function() { + this.$data.unshift(this.$data.pop()); + return this.get(); + } }; -exports.handler = new StateHandler(emacsState); }); diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index 53a9c9c4..0db64573 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -127,7 +127,7 @@ function HashHandler(config, platform) { this.bindKey(key, command); }; - this.parseKeys = function(keys, val) { + this.parseKeys = function(keys) { var key; var hashId = 0; var parts = keys.toLowerCase().trim().split(/\s*\-\s*/); @@ -138,6 +138,11 @@ function HashHandler(config, platform) { else key = parts[i] || "-"; //when empty, the splitSafe removed a '-' } + + if (parts[0] == "text" && parts.length == 2) { + hashId = -1; + key = parts[1]; + } return { key: key, diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js index cea77f50..2c336438 100644 --- a/lib/ace/keyboard/keybinding.js +++ b/lib/ace/keyboard/keybinding.js @@ -117,7 +117,7 @@ var KeyBinding = function(editor) { if (toExecute.command != "null") success = commands.exec(toExecute.command, this.$editor, toExecute.args, e); else - success = toExecute.stopEvent == true; + success = toExecute.passEvent != true; if (success && e) event.stopEvent(e); diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index a94e9dc8..e5698913 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -86,7 +86,7 @@ exports.handler = { } }; } // wait for input else if (key.length == 1 && (hashId == 0 || hashId == 4)) { //no modifier || shift - return {command: "null", stopEvent: false}; + return {command: "null"}; } else if (key == 'esc') { return {command: coreCommands.stop}; } diff --git a/lib/ace/keyboard/vim/maps/util.js b/lib/ace/keyboard/vim/maps/util.js index 54e66681..fb79f39e 100644 --- a/lib/ace/keyboard/vim/maps/util.js +++ b/lib/ace/keyboard/vim/maps/util.js @@ -1,7 +1,7 @@ define(function(require, exports, module) { var registers = require("../registers"); -var dom = require("ace/lib/dom"); +var dom = require("../../../lib/dom"); dom.importCssString('.insert-mode. ace_cursor{\ border-left: 2px solid #333333;\ }\ From 2a347a35c91d6ee027a1b8a709d0a80edbaaff28 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 1 May 2012 14:37:16 +0400 Subject: [PATCH 21/47] tabs to spaces --- lib/ace/keyboard/hash_handler.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index 0db64573..d3d4f524 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -138,11 +138,11 @@ function HashHandler(config, platform) { else key = parts[i] || "-"; //when empty, the splitSafe removed a '-' } - - if (parts[0] == "text" && parts.length == 2) { - hashId = -1; - key = parts[1]; - } + + if (parts[0] == "text" && parts.length == 2) { + hashId = -1; + key = parts[1]; + } return { key: key, From 25c9c553c6e3e778f7ee90c42a9ca9f6915def37 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 1 May 2012 15:43:54 +0400 Subject: [PATCH 22/47] fix vim mode +cleanup --- lib/ace/keyboard/vim.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index e5698913..26016d34 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -45,22 +45,22 @@ var coreCommands = cmds.coreCommands; var util = require("./vim/maps/util"); var startCommands = { - 'i': { + "i": { command: coreCommands.start }, - 'I': { + "I": { command: coreCommands.startBeginning }, - 'a': { + "a": { command: coreCommands.append }, - 'A': { + "A": { command: coreCommands.appendEnd }, - 'ctrl-f': { + "ctrl-f": { command: "gotopagedown" }, - 'ctrl-b': { + "ctrl-b": { command: "gotopageup" }, }; @@ -68,15 +68,13 @@ var startCommands = { exports.handler = { handleKeyboard: function(data, hashId, key, keyCode, e) { // ignore command keys (shift, ctrl etc.) - // Otherwise "shift-" is added to the buffer, and later on "shift-g" - // which results in "shift-shift-g" which doesn't make sense. if (hashId != 0 && (key == "" || key == "\x00")) return null; if (hashId == 1) - key = 'ctrl-' + key; + key = "ctrl-" + key; - if (data.state == 'start') { + if (data.state == "start") { if (hashId == -1 || hashId == 1) { if (cmds.inputBuffer.idle && startCommands[key]) return startCommands[key]; @@ -86,13 +84,13 @@ exports.handler = { } }; } // wait for input else if (key.length == 1 && (hashId == 0 || hashId == 4)) { //no modifier || shift - return {command: "null"}; - } else if (key == 'esc') { + return {command: "null", passEvent: true}; + } else if (key == "esc") { return {command: coreCommands.stop}; } } else { - if (key == 'esc' || key == 'ctrl-[') { - data.state = 'start'; + if (key == "esc" || key == "ctrl-[") { + data.state = "start"; return {command: coreCommands.stop}; } else if (key == "ctrl-w") { return {command: "removewordleft"}; From 4abb08fa92d4a016afe90f098406ae1cdc346c02 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 3 May 2012 18:50:30 +0400 Subject: [PATCH 23/47] fix setPlaceholderMode --- lib/ace/edit_session.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index b563b534..3f1810cc 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -808,7 +808,8 @@ var EditSession = function(text, mode) { if (!this.$modes[textModeId]) this.$modes[textModeId] = new TextMode(); - var tokenizer = this.$modes[textModeId].getTokenizer(); + this.$mode = this.$modes[textModeId]; + var tokenizer = this.$mode.getTokenizer(); if (!this.bgTokenizer) { this.bgTokenizer = new BackgroundTokenizer(tokenizer); @@ -819,9 +820,10 @@ var EditSession = function(text, mode) { } else { this.bgTokenizer.setTokenizer(tokenizer); } + this.bgTokenizer.setDocument(this.getDocument()); - this.tokenRe = this.$modes[textModeId].tokenRe; - this.nonTokenRe = this.$modes[textModeId].nonTokenRe; + this.tokenRe = this.$mode.tokenRe; + this.nonTokenRe = this.$mode.nonTokenRe; }; /** From 6bb805b40f8e3389a16be6bb1683f6b00411e084 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 3 May 2012 19:28:20 +0400 Subject: [PATCH 24/47] simplify config --- Makefile.dryice.js | 49 +++++++++++++++++++++++++++++------------ kitchen-sink.html | 2 +- lib/ace/ace.js | 6 +++-- lib/ace/config.js | 5 +---- lib/ace/edit_session.js | 6 ++--- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index c64a8629..88edab5c 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -43,7 +43,7 @@ var copy = require('dryice').copy; var ACE_HOME = __dirname; function main(args) { - var target; + var target = "minimal"; if (args.length == 3) { target = args[2]; // Check if 'target' contains some allowed value. @@ -52,12 +52,13 @@ function main(args) { } } - if (!target) { + if (target == "help") { console.log("--- Ace Dryice Build Tool ---"); console.log(""); console.log("Options:"); + console.log(" minimal Runs minimal build of Ace"); console.log(" normal Runs embedded build of Ace"); - console.log(" demo Runs demo build of Ace"); + console.log(" demo Runs demo build of Ace"); console.log(" bm Runs bookmarklet build of Ace"); process.exit(0); } @@ -70,6 +71,15 @@ function main(args) { textPluginPattern: /^ace\/requirejs\/text!/ }; + if (target == "minimal") { + buildAce(aceProject, { + compress: false, + noconflict: false, + suffix: "", + compat: true, + name: "ace" + }); + } if (target == "normal") { ace(aceProject); } @@ -113,14 +123,14 @@ function ace(aceProject) { buildAce(aceProject, { compress: false, noconflict: false, - suffix: "-uncompressed.js", + suffix: "-uncompressed", compat: true, name: "ace" }); buildAce(aceProject, { compress: false, noconflict: true, - suffix: "-uncompressed-noconflict.js", + suffix: "-uncompressed-noconflict", compat: true, name: "ace", workers: [] @@ -130,7 +140,7 @@ function ace(aceProject) { buildAce(aceProject, { compress: true, noconflict: false, - suffix: ".js", + suffix: "", compat: true, name: "ace", workers: [] @@ -138,7 +148,7 @@ function ace(aceProject) { buildAce(aceProject, { compress: true, noconflict: true, - suffix: "-noconflict.js", + suffix: "-noconflict", compat: true, name: "ace", workers: [] @@ -199,7 +209,7 @@ function demo(aceProject) { noconflict: false, compat: false, name: "kitchen-sink", - suffix: "-uncompressed.js", + suffix: "", keybindings: [] }); } @@ -213,7 +223,7 @@ function buildAce(aceProject, options) { requires: null, compress: false, noconflict: false, - suffix: ".js", + suffix: "", name: "ace", compat: true, modes: [ @@ -252,6 +262,15 @@ function buildAce(aceProject, options) { var exportFilter = exportAce(options.ns, options.exportModule); } + // remove use strict + filters.push(function(text) { + return text.replace(/['"]use strict['"];/g, ""); + }) + // remove redundant comments + filters.push(function(text) { + return text.replace(/(;)\s*\/\*[\d\D]*?\*\//g, "$1"); + }) + if (options.compress) filters.push(copy.filter.uglifyjs); @@ -279,7 +298,7 @@ function buildAce(aceProject, options) { copy({ source: ace, filter: exportFilter ? filters.concat(exportFilter) : filters, - dest: targetDir + '/' + name + suffix + dest: targetDir + suffix + '/' + name + ".js" }); if (options.compat) { @@ -292,7 +311,7 @@ function buildAce(aceProject, options) { } ], filter: filters, - dest: targetDir + "/" + name + "-compat" + suffix + dest: targetDir + suffix + "/" + name + "-compat.js" }); } @@ -309,7 +328,7 @@ function buildAce(aceProject, options) { } ], filter: filters, - dest: targetDir + "/mode-" + mode + suffix + dest: targetDir + suffix + "/mode-" + mode + ".js" }); }); @@ -324,7 +343,7 @@ function buildAce(aceProject, options) { require: ["ace/theme/" + theme] }], filter: filters, - dest: targetDir + "/theme-" + theme + suffix + dest: targetDir + suffix + "/theme-" + theme + ".js" }); }); @@ -377,7 +396,7 @@ function buildAce(aceProject, options) { } ], filter: filters, - dest: "build/src/keybinding-" + keybinding + suffix + dest: targetDir + suffix + "/keybinding-" + keybinding + ".js" }); }); } @@ -423,10 +442,12 @@ function exportAce(ns, module, requireBase) { var template = function() { (function() { REQUIRE_NS.require(["MODULE"], function(a) { + a.config.init(); if (!window.NS) window.NS = {}; for (var key in a) if (a.hasOwnProperty(key)) NS[key] = a[key]; + require("./config").init(); }); })(); }; diff --git a/kitchen-sink.html b/kitchen-sink.html index 5fc01e17..b3528cc8 100644 --- a/kitchen-sink.html +++ b/kitchen-sink.html @@ -227,7 +227,7 @@