diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 9ae70f4f..4bc1c35e 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -407,7 +407,7 @@ event.addListener(container, "drop", function(e) { if (window.FileReader) { var reader = new FileReader(); reader.onload = function() { - var mode = getModeFromPath(file.name); + var mode = modelist.getModeFromPath(file.name); env.editor.session.doc.setValue(reader.result); modeEl.value = mode.name; diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 2952b80a..d6c54b72 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -50,9 +50,13 @@ var HashHandler = require("./hash_handler").HashHandler; exports.handler = new HashHandler(); var initialized = false; +var $formerLongWords; +var $formerLineStart; + 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;\ @@ -79,16 +83,65 @@ exports.handler.attach = function(editor) { }', 'emacsMode' ); } + // in emacs, gotowordleft/right should not count a space as a word.. + $formerLongWords = editor.session.$selectLongWords; + editor.session.$selectLongWords = true; + // CTRL-A should go to actual beginning of line + $formerLineStart = editor.session.$useEmacsStyleLineStart; + editor.session.$useEmacsStyleLineStart = true; + + editor.session.$emacsMark = null; + + exports.markMode = function() { + return editor.session.$emacsMark; + } + + exports.setMarkMode = function(p) { + editor.session.$emacsMark = p; + } + + editor.on("click",$resetMarkMode); + + editor.on("changeSession",$kbSessionChange); editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates; + editor.setStyle("emacs-mode"); + }; exports.handler.detach = function(editor) { + delete editor.renderer.screenToTextCoordinates; + + editor.session.$selectLongWords = $formerLongWords; + editor.session.$useEmacsStyleLineStart = $formerLineStart; + + + editor.removeEventListener("click",$resetMarkMode); + editor.removeEventListener("changeSession",$kbSessionChange); + editor.unsetStyle("emacs-mode"); }; +var $kbSessionChange = function(e) { + if (e.oldSession) { + e.oldSession.$selectLongWords = $formerLongWords; + e.oldSession.$useEmacsStyleLineStart = $formerLineStart; + } + + $formerLongWords = e.session.$selectLongWords; + e.session.$selectLongWords = true; + $formerLineStart = e.session.$useEmacsStyleLineStart; + e.session.$useEmacsStyleLineStart = true; + + if (!e.session.hasOwnProperty('$emacsMark')) + e.session.$emacsMark = null; +} + +var $resetMarkMode = function(e) { + e.editor.session.$emacsMark = null; +} var keys = require("../lib/keys").KEY_MODS; var eMods = { @@ -119,6 +172,7 @@ exports.handler.bindKey = function(key, command) { exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { if (hashId == -1) { + exports.setMarkMode(null); if (data.count) { var str = Array(data.count + 1).join(key); data.count = null; @@ -162,9 +216,21 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { if (typeof command != "string") { var args = command.args; command = command.command; + if (command == "goorselect") { + command = args[0]; + if (exports.markMode()) { + command = args[1]; + } + args = null; + } } if (typeof command == "string") { + if (command == "insertstring" || + command == "splitline" || + command == "togglecomment") { + exports.setMarkMode(null); + } command = this.commands[command] || data.editor.commands.commands[command]; } @@ -190,16 +256,16 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { 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", + "Up|C-p" : {command: "goorselect", args: ["golineup","selectup"]}, + "Down|C-n" : {command: "goorselect", args: ["golinedown","selectdown"]}, + "Left|C-b" : {command: "goorselect", args: ["gotoleft","selectleft"]}, + "Right|C-f" : {command: "goorselect", args: ["gotoright","selectright"]}, + "C-Left|M-b" : {command: "goorselect", args: ["gotowordleft","selectwordleft"]}, + "C-Right|M-f" : {command: "goorselect", args: ["gotowordright","selectwordright"]}, + "Home|C-a" : {command: "goorselect", args: ["gotolinestart","selecttolinestart"]}, + "End|C-e" : {command: "goorselect", args: ["gotolineend","selecttolineend"]}, + "C-Home|S-M-,": {command: "goorselect", args: ["gotostart","selecttostart"]}, + "C-End|S-M-." : {command: "goorselect", args: ["gotoend","selecttoend"]}, // selection "S-Up|S-C-p" : "selectup", @@ -219,10 +285,10 @@ exports.emacsKeys = { "C-x C-p": "selectall", // todo fix these - "C-Down": "gotopagedown", - "C-Up": "gotopageup", - "PageDown|C-v": "gotopagedown", - "PageUp|M-v": "gotopageup", + "C-Down": {command: "goorselect", args: ["gotopagedown","selectpagedown"]}, + "C-Up": {command: "goorselect", args: ["gotopageup","selectpageup"]}, + "PageDown|C-v": {command: "goorselect", args: ["gotopagedown","selectpagedown"]}, + "PageUp|M-v": {command: "goorselect", args: ["gotopageup","selectpageup"]}, "S-C-Down": "selectpagedown", "S-C-Up": "selectpageup", "C-s": "findnext", @@ -247,16 +313,15 @@ exports.emacsKeys = { "C-w": "killRegion", "M-w": "killRingSave", - "C-Space": "setMark", "C-x C-x": "exchangePointAndMark", "C-t": "transposeletters", - - "M-u": "touppercase", + "M-u": "touppercase", // Doesn't work "M-l": "tolowercase", - "M-/": "autocomplete", - "C-u": "universalArgument", + "M-/": "autocomplete", // Doesn't work + "C-u": "universalArgument", + "M-;": "togglecomment", "C-/|C-x u|S-C--|C-z": "undo", @@ -289,11 +354,37 @@ exports.handler.addCommands({ selectRectangularRegion: function(editor) { editor.multiSelect.toggleBlockSelection(); }, - setMark: function() { + setMark: function(editor) { + // Emulate emacs highlighting behaviour in transient-mark-mode. + // Sets mark-mode and clears current selection. + // When mark is set, keyboard cursor movement commands become + // selection modification commands. That is, + // "goto" commands become "select" commands. + // Any insertion or mouse click resets mark-mode. + // setMark twice in a row at the same place resets markmode + var markMode = exports.markMode(); + + if (markMode) { + + cp = editor.getCursorPosition(); + if (editor.selection.isEmpty() && + markMode.row == cp.row && markMode.column == cp.column) { + exports.setMarkMode(null); + // console.log("Mark mode off"); + return; + } + } + + // turn on mark mode + markMode = editor.getCursorPosition(); + exports.setMarkMode(markMode); + editor.selection.setSelectionAnchor(markMode.row, markMode.column); + }, exchangePointAndMark: { exec: function(editor) { var range = editor.selection.getRange(); + editor.selection.setSelectionRange(range, !editor.selection.isBackwards()); }, readonly: true, @@ -317,7 +408,22 @@ exports.handler.addCommands({ multiselectAction: "forEach" }, killLine: function(editor) { - editor.selection.selectLine(); + exports.setMarkMode(null); + var pos = editor.getCursorPosition(); + + if (pos.column == 0 && + editor.session.doc.getLine(pos.row).length == 0) { + // If an already empty line is killed, remove + // the line entirely + editor.selection.selectLine(); + } else { + // otherwise just remove from the current cursor position + // to the end (but don't delete the selection if it's before + // the cursor) + editor.clearSelection(); + editor.selection.selectLineEnd(); + + } var range = editor.getSelectionRange(); var text = editor.session.getTextRange(range); exports.killRing.add(text); diff --git a/lib/ace/selection.js b/lib/ace/selection.js index d80f9662..dd5435f1 100644 --- a/lib/ace/selection.js +++ b/lib/ace/selection.js @@ -517,17 +517,10 @@ var Selection = function(session) { ); var leadingSpace = beforeCursor.match(/^\s*/); - if (leadingSpace[0].length == column) { - this.moveCursorTo( - firstColumnPosition.row, firstColumnPosition.column - ); - } - else { - this.moveCursorTo( - firstColumnPosition.row, - firstColumnPosition.column + leadingSpace[0].length - ); - } + // TODO find better way for emacs mode to override selection behaviors + if (leadingSpace[0].length != column && !this.session.$useEmacsStyleLineStart) + firstColumnPosition.column += leadingSpace[0].length; + this.moveCursorToPosition(firstColumnPosition); }; /**