diff --git a/build_support/style.css b/build_support/style.css
index b452add7..43f46d0e 100644
--- a/build_support/style.css
+++ b/build_support/style.css
@@ -1,3 +1,5 @@
+@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);
+
body {
margin:0;
padding:0;
diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js
index 9d272bd6..bcd240ff 100644
--- a/demo/kitchen-sink/demo.js
+++ b/demo/kitchen-sink/demo.js
@@ -301,7 +301,8 @@ split.on("focus", function(editor) {
});
env.split = split;
window.env = env;
-window.ace = env.editor;
+window.editor = window.ace = env.editor;
+env.editor.setAnimatedScroll(true);
var docEl = document.getElementById("doc");
var modeEl = document.getElementById("mode");
@@ -395,10 +396,28 @@ function saveOption(el, val) {
}
}
+event.addListener(themeEl, "mouseover", function(e){
+ this.desiredValue = e.target.value;
+ if (!this.$timer)
+ this.$timer = setTimeout(this.updateTheme);
+})
+
+event.addListener(themeEl, "mouseout", function(e){
+ this.desiredValue = null;
+ if (!this.$timer)
+ this.$timer = setTimeout(this.updateTheme, 20);
+})
+
+themeEl.updateTheme = function(){
+ env.split.setTheme(themeEl.desiredValue || themeEl.selectedValue);
+ themeEl.$timer = null;
+}
+
bindDropdown("theme", function(value) {
if (!value)
return;
env.editor.setTheme(value);
+ themeEl.selectedValue = value;
});
bindDropdown("keybinding", function(value) {
@@ -480,6 +499,9 @@ bindCheckbox("enable_behaviours", function(checked) {
env.editor.setBehavioursEnabled(checked);
});
+bindCheckbox("fade_fold_widgets", function(checked) {
+ env.editor.setFadeFoldWidgets(checked);
+});
var secondSession = null;
bindDropdown("split", function(value) {
diff --git a/demo/kitchen-sink/styles.css b/demo/kitchen-sink/styles.css
index 74c53b90..e71e7e53 100644
--- a/demo/kitchen-sink/styles.css
+++ b/demo/kitchen-sink/styles.css
@@ -1,3 +1,5 @@
+@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);
+
html {
height: 100%;
width: 100%;
diff --git a/doc/wiki b/doc/wiki
index d2a65d0a..cc5ccde4 160000
--- a/doc/wiki
+++ b/doc/wiki
@@ -1 +1 @@
-Subproject commit d2a65d0addc2e5ab922bbff9cb6022a4652b4f13
+Subproject commit cc5ccde4565f7b21c81345f47e941e011e94b810
diff --git a/kitchen-sink.html b/kitchen-sink.html
index bf37f827..5fc01e17 100644
--- a/kitchen-sink.html
+++ b/kitchen-sink.html
@@ -200,6 +200,14 @@
+
+ |
+
+ |
+
+
+ |
+
diff --git a/lib/ace/commands/command_manager.js b/lib/ace/commands/command_manager.js
index a6c800f5..b919dbc1 100644
--- a/lib/ace/commands/command_manager.js
+++ b/lib/ace/commands/command_manager.js
@@ -31,7 +31,7 @@ var CommandManager = function(platform, commands) {
this.addCommands(commands);
this.setDefaultHandler("exec", function(e) {
- e.command.exec(e.editor, e.args || {});
+ return e.command.exec(e.editor, e.args || {});
});
};
@@ -51,8 +51,18 @@ oop.inherits(CommandManager, HashHandler);
if (editor && editor.$readOnly && !command.readOnly)
return false;
- this._emit("exec", {editor: editor, command: command, args: args});
- return true;
+ try {
+ var retvalue = this._emit("exec", {
+ editor: editor,
+ command: command,
+ args: args
+ });
+ } catch (e) {
+ window.console && window.console.log(e);
+ return true;
+ }
+
+ return retvalue === false ? false : true;
};
this.toggleRecording = function() {
diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js
index f109b336..d12478a3 100644
--- a/lib/ace/commands/default_commands.js
+++ b/lib/ace/commands/default_commands.js
@@ -311,7 +311,7 @@ exports.commands = [{
multiSelectAction: "forEach"
}, {
name: "togglecomment",
- bindKey: bindKey("Ctrl-7", "Command-7"),
+ bindKey: bindKey("Ctrl-/", "Command-/"),
exec: function(editor) { editor.toggleCommentLines(); },
multiSelectAction: "forEach"
}, {
diff --git a/lib/ace/commands/multi_select_commands.js b/lib/ace/commands/multi_select_commands.js
index 71f0a3e5..cce05705 100644
--- a/lib/ace/commands/multi_select_commands.js
+++ b/lib/ace/commands/multi_select_commands.js
@@ -84,16 +84,17 @@ exports.defaultCommands = [{
exec: function(editor) { editor.multiSelect.splitIntoLines(); },
bindKey: {win: "Ctrl-Shift-L", mac: "Ctrl-Shift-L"},
readonly: true
-}];
-
-// commands active in multiselect mode
-exports.multiEditCommands = [{
+}, {
name: "singleSelection",
bindKey: "esc",
exec: function(editor) { editor.exitMultiSelectMode(); },
- readonly: true
+ readonly: true,
+ isAvailable: function(editor) {return editor.inMultiSelectMode}
}];
+// commands active in multiselect mode
+exports.multiEditCommands = {"singleSelection": "esc"};
+
var HashHandler = require("../keyboard/hash_handler").HashHandler;
exports.keyboardHandler = new HashHandler(exports.multiEditCommands);
diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css
index 83c21130..5a544076 100644
--- a/lib/ace/css/editor.css
+++ b/lib/ace/css/editor.css
@@ -1,4 +1,3 @@
-@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);
.ace_editor {
position: absolute;
@@ -131,6 +130,7 @@
.ace_text-layer {
color: black;
+ font: inherit !important;
}
.ace_cjk {
@@ -179,10 +179,6 @@
z-index: 2;
}
-.ace_gutter .ace_gutter_active_line{
- background-color : #dcdcdc;
-}
-
.ace_marker-layer .ace_selected_word {
position: absolute;
z-index: 4;
@@ -289,3 +285,25 @@
background-color: #FFB4B4;
border-color: #DE5555;
}
+
+.ace_fade-fold-widgets .ace_fold-widget {
+ -moz-transition: 0.5s opacity;
+ -webkit-transition: 0.5s opacity;
+ -o-transition: 0.5s opacity;
+ -ms-transition: 0.5s opacity;
+ transition: 0.5s opacity;
+ opacity: 0;
+}
+.ace_fade-fold-widgets:hover .ace_fold-widget {
+ -moz-transition-duration: 0.05s;
+ -webkit-transition-duration: 0.05s;
+ -o-transition-duration: 0.05s;
+ -ms-transition-duration: 0.05s;
+ transition-duration: 0.05s;
+ -moz-transition-delay: 0.2s;
+ -webkit-transition-delay: 0.2s;
+ -o-transition-delay: 0.2s;
+ -ms-transition-delay: 0.2s;
+ transition-delay: 0.2s;
+ opacity:1;
+}
diff --git a/lib/ace/document.js b/lib/ace/document.js
index f984ee81..bc6ac32e 100644
--- a/lib/ace/document.js
+++ b/lib/ace/document.js
@@ -257,10 +257,9 @@ var Document = function(text) {
range.end.column);
}
else {
- var lines = [];
- lines.push(this.$lines[range.start.row].substring(range.start.column));
- lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));
- lines.push(this.$lines[range.end.row].substring(0, range.end.column));
+ var lines = this.getLines(range.start.row+1, range.end.row-1);
+ lines.unshift((this.$lines[range.start.row] || "").substring(range.start.column));
+ lines.push((this.$lines[range.end.row] || "").substring(0, range.end.column));
return lines.join(this.getNewLineCharacter());
}
};
diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js
index 12fa89ce..8a57bcda 100644
--- a/lib/ace/edit_session.js
+++ b/lib/ace/edit_session.js
@@ -93,10 +93,7 @@ var EditSession = function(text, mode) {
}
this.selection = new Selection(this);
- if (mode)
- this.setMode(mode);
- else
- this.setMode(new TextMode());
+ this.setMode(mode);
};
@@ -766,7 +763,7 @@ var EditSession = function(text, mode) {
this._loadMode = function(mode, callback) {
if (this.$modes[mode])
return callback(this.$modes[mode]);
-
+
var _self = this;
var module;
try {
@@ -774,16 +771,17 @@ var EditSession = function(text, mode) {
} catch (e) {};
if (module)
return done(module);
-
+
fetch(function() {
require([mode], done);
});
-
+
function done(module) {
if (_self.$modes[mode])
return callback(_self.$modes[mode]);
-
+
_self.$modes[mode] = new module.Mode();
+ _self.$modes[mode].$id = mode;
_self._emit("loadmode", {
name: mode,
mode: _self.$modes[mode]
@@ -794,40 +792,48 @@ var EditSession = function(text, mode) {
function fetch(callback) {
if (!config.get("packaged"))
return callback();
-
+
var base = mode.split("/").pop();
var filename = config.get("modePath") + "/mode-" + base + config.get("suffix");
net.loadScript(filename, callback);
}
};
+ /**
+ * EditSession.setMode(mode)
+ * - mode (TextMode): Set a new text mode
+ *
+ * Sets a new text mode for the `EditSession`. This method also emits the `'changeMode'` event. If a [[BackgroundTokenizer `BackgroundTokenizer`]] is set, the `'tokenizerUpdate'` event is also emitted.
+ *
+ **/
this.$mode = null;
- this.$origMode = null;
- /**
- * EditSession.setMode(mode)
- * - mode (TextMode): Set a new text mode
- *
- * Sets a new text mode for the `EditSession`. This method also emits the `'changeMode'` event. If a [[BackgroundTokenizer `BackgroundTokenizer`]] is set, the `'tokenizerUpdate'` event is also emitted.
- *
- **/
+ this.$modeId = null;
this.setMode = function(mode) {
- this.$origMode = mode;
-
// load on demand
if (typeof mode === "string") {
+ if (this.$modeId == mode)
+ return;
+
+ this.$modeId = mode;
var _self = this;
this._loadMode(mode, function(module) {
- if (_self.$origMode !== mode)
+ if (_self.$modeId !== mode)
return;
-
+
_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;
this.$mode = mode;
-
+ this.$modeId = mode.$id;
this.$stopWorker();
@@ -901,7 +907,7 @@ var EditSession = function(text, mode) {
this.getMode = function() {
return this.$mode;
};
-
+
this.$scrollTop = 0;
/**
* EditSession.setScrollTop(scrollTop)
@@ -926,7 +932,7 @@ var EditSession = function(text, mode) {
this.getScrollTop = function() {
return this.$scrollTop;
};
-
+
this.$scrollLeft = 0;
/**
* EditSession.setScrollLeft(scrollLeft)
@@ -1419,7 +1425,7 @@ var EditSession = function(text, mode) {
range.start.column
);
}
-
+
var len = this.doc.getLength() - 1;
if (range.end.row > len) {
range.end.row = len;
diff --git a/lib/ace/edit_session/fold_line.js b/lib/ace/edit_session/fold_line.js
index 7676c4a1..291fc80f 100644
--- a/lib/ace/edit_session/fold_line.js
+++ b/lib/ace/edit_session/fold_line.js
@@ -177,7 +177,9 @@ function FoldLine(foldData, folds) {
&& fold.start.column != column
&& fold.start.row != row)
{
- throw "Moving characters inside of a fold should never be reached";
+ //throwing here breaks whole editor
+ //@todo properly handle this
+ window.console && window.console.log(row, column, fold);
} else if (fold.start.row == row) {
folds = this.folds;
var i = folds.indexOf(fold);
diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index b80a232b..b0342377 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -78,6 +78,7 @@ var Editor = function(renderer, session) {
this.container = container;
this.renderer = renderer;
+ this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands);
this.textInput = new TextInput(renderer.getTextAreaContainer(), this);
this.keyBinding = new KeyBinding(this);
@@ -94,7 +95,6 @@ var Editor = function(renderer, session) {
wrap: true
});
- this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands);
this.setSession(session || new EditSession(""));
};
@@ -458,6 +458,7 @@ var Editor = function(renderer, session) {
this.$highlightBrackets();
this.$updateHighlightActiveLine();
+ this.$updateHighlightGutterLine();
};
/** internal, hide
@@ -470,13 +471,10 @@ var Editor = function(renderer, session) {
if (session.$highlightLineMarker)
session.removeMarker(session.$highlightLineMarker);
- if (typeof this.$lastrow == "number")
- this.renderer.removeGutterDecoration(this.$lastrow, "ace_gutter_active_line");
session.$highlightLineMarker = null;
- this.$lastrow = null;
- if (this.getHighlightActiveLine()) {
+ if (this.$highlightActiveLine) {
var cursor = this.getCursorPosition(),
foldLine = this.session.getFoldLine(cursor.row);
@@ -489,11 +487,24 @@ var Editor = function(renderer, session) {
}
session.$highlightLineMarker = session.addMarker(range, "ace_active_line", "background");
}
-
- this.renderer.addGutterDecoration(this.$lastrow = cursor.row, "ace_gutter_active_line");
- }
};
+ /** internal, hide
+ * Editor.$updateHighlightGutterLine()
+ *
+ *
+ **/
+ this.$updateHighlightGutterLine = function(){
+ if (typeof this.$lastrow == "number")
+ this.renderer.removeGutterDecoration(this.$lastrow, "ace_gutter_active_line");
+
+ this.$lastrow = null;
+
+ if (this.$highlightGutterLine)
+ this.renderer.addGutterDecoration(
+ this.$lastrow = this.getCursorPosition().row, "ace_gutter_active_line");
+ }
+
/**
* Editor@onSelectionChange(e)
* - e (Object): Contains a single property, `data`, which has the delta of changes
@@ -514,6 +525,7 @@ var Editor = function(renderer, session) {
session.$selectionMarker = session.addMarker(range, "ace_selection", style);
} else {
this.$updateHighlightActiveLine();
+ this.$updateHighlightGutterLine();
}
if (this.$highlightSelectedWord)
@@ -592,6 +604,7 @@ var Editor = function(renderer, session) {
// Update the active line marker as due to folding changes the current
// line range on the screen might have changed.
this.$updateHighlightActiveLine();
+ this.$updateHighlightGutterLine();
// TODO: This might be too much updating. Okay for now.
this.renderer.updateFull();
};
@@ -610,15 +623,34 @@ var Editor = function(renderer, session) {
return text;
};
+ /**
+ * Editor.onCopy()
+ *
+ * Called whenever a text "copy" happens.
+ **/
+ this.onCopy = function() {
+ this.commands.exec("copy", this);
+ };
+
/**
* Editor.onCut()
*
- * Emitted whenever a text "cut" happens.
+ * called whenever a text "cut" happens.
**/
this.onCut = function() {
this.commands.exec("cut", this);
};
+ /**
+ * Editor.onPaste()
+ *
+ * called whenever a text "paste" happens.
+ **/
+ this.onPaste = function(text) {
+ this._emit("paste", text);
+ this.insert(text);
+ };
+
/**
* Editor.insert(text)
* - text (String): The new text to add
@@ -724,11 +756,8 @@ var Editor = function(renderer, session) {
*
* Emitted when text is entered.
**/
- this.onTextInput = function(text, pasted) {
- if (pasted)
- this._emit("paste", text);
-
- this.keyBinding.onTextInput(text, pasted);
+ this.onTextInput = function(text) {
+ this.keyBinding.onTextInput(text);
};
/**
@@ -844,7 +873,8 @@ var Editor = function(renderer, session) {
*
**/
this.setHighlightActiveLine = function(shouldHighlight) {
- if (this.$highlightActiveLine == shouldHighlight) return;
+ if (this.$highlightActiveLine == shouldHighlight)
+ return;
this.$highlightActiveLine = shouldHighlight;
this.$updateHighlightActiveLine();
@@ -859,6 +889,19 @@ var Editor = function(renderer, session) {
return this.$highlightActiveLine;
};
+ this.$highlightGutterLine = true;
+ this.setHighlightGutterLine = function(shouldHighlightGutterLine) {
+ if (this.$highlightGutterLine == shouldHighlightGutterLine)
+ return;
+
+ this.$highlightGutterLine = shouldHighlightGutterLine;
+ this.$updateHighlightGutterLine();
+ };
+
+ this.getHighlightGutterLine = function() {
+ return this.$highlightGutterLine;
+ };
+
this.$highlightSelectedWord = true;
/**
* Editor.setHighlightSelectedWord(shouldHighlight)
@@ -1013,6 +1056,14 @@ var Editor = function(renderer, session) {
return this.renderer.$gutterLayer.getShowFoldWidgets();
};
+ this.setFadeFoldWidgets = function(show) {
+ this.renderer.setFadeFoldWidgets(show);
+ };
+
+ this.getFadeFoldWidgets = function() {
+ return this.renderer.getFadeFoldWidgets();
+ };
+
/**
* Editor.remove(dir)
* - dir (String): The direction of the deletion to occur, either "left" or "right"
@@ -1022,7 +1073,7 @@ var Editor = function(renderer, session) {
**/
this.remove = function(dir) {
if (this.selection.isEmpty()){
- if(dir == "left")
+ if (dir == "left")
this.selection.selectLeft();
else
this.selection.selectRight();
@@ -1447,15 +1498,29 @@ var Editor = function(renderer, session) {
return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1;
};
- this.$getPageDownRow = function() {
- return this.renderer.getScrollBottomRow();
- };
+ this.$moveByPage = function(dir, select) {
+ var renderer = this.renderer;
+ var config = this.renderer.layerConfig;
+ var rows = dir * Math.floor(config.height / config.lineHeight);
- this.$getPageUpRow = function() {
- var firstRow = this.renderer.getScrollTopRow();
- var lastRow = this.renderer.getScrollBottomRow();
+ this.$blockScrolling++;
+ if (select == true) {
+ this.selection.$moveSelection(function(){
+ this.moveCursorBy(rows, 0);
+ });
+ } else if (select == false) {
+ this.selection.moveCursorBy(rows, 0);
+ this.selection.clearSelection();
+ }
+ this.$blockScrolling--;
- return firstRow - (lastRow - firstRow);
+ var scrollTop = renderer.scrollTop;
+
+ renderer.scrollBy(0, rows * config.lineHeight);
+ if (select != null)
+ renderer.scrollCursorIntoView(null, 0.5);
+
+ renderer.animateScrolling(scrollTop);
};
/**
@@ -1464,14 +1529,7 @@ var Editor = function(renderer, session) {
* Selects the text from the current position of the document until where a "page down" finishes.
**/
this.selectPageDown = function() {
- var row = this.$getPageDownRow() + Math.floor(this.$getVisibleRowCount() / 2);
-
- this.scrollPageDown();
-
- var selection = this.getSelection();
- var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());
- var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);
- selection.selectTo(dest.row, dest.column);
+ this.$moveByPage(1, true);
};
/**
@@ -1480,15 +1538,7 @@ var Editor = function(renderer, session) {
* Selects the text from the current position of the document until where a "page up" finishes.
**/
this.selectPageUp = function() {
- var visibleRows = this.renderer.getScrollTopRow() - this.renderer.getScrollBottomRow();
- var row = this.$getPageUpRow() + Math.round(visibleRows / 2);
-
- this.scrollPageUp();
-
- var selection = this.getSelection();
- var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());
- var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);
- selection.selectTo(dest.row, dest.column);
+ this.$moveByPage(-1, true);
};
/**
@@ -1497,11 +1547,7 @@ var Editor = function(renderer, session) {
* Shifts the document to wherever "page down" is, as well as moving the cursor position.
**/
this.gotoPageDown = function() {
- var row = this.$getPageDownRow();
- var column = this.getCursorPositionScreen().column;
-
- this.scrollToRow(row);
- this.getSelection().moveCursorToScreen(row, column);
+ this.$moveByPage(1, false);
};
/**
@@ -1510,11 +1556,7 @@ var Editor = function(renderer, session) {
* Shifts the document to wherever "page up" is, as well as moving the cursor position.
**/
this.gotoPageUp = function() {
- var row = this.$getPageUpRow();
- var column = this.getCursorPositionScreen().column;
-
- this.scrollToRow(row);
- this.getSelection().moveCursorToScreen(row, column);
+ this.$moveByPage(-1, false);
};
/**
@@ -1523,7 +1565,7 @@ var Editor = function(renderer, session) {
* Scrolls the document to wherever "page down" is, without changing the cursor position.
**/
this.scrollPageDown = function() {
- this.scrollToRow(this.$getPageDownRow());
+ this.$moveByPage(1);
};
/**
@@ -1532,7 +1574,7 @@ var Editor = function(renderer, session) {
* Scrolls the document to wherever "page up" is, without changing the cursor position.
**/
this.scrollPageUp = function() {
- this.renderer.scrollToRow(this.$getPageUpRow());
+ this.$moveByPage(-1);
};
/** related to: VirtualRenderer.scrollToRow
@@ -1550,11 +1592,13 @@ var Editor = function(renderer, session) {
* Editor.scrollToLine(line, center)
* - line (Number): The line to scroll to
* - center (Boolean): If `true`
+ * - animate (Boolean): If `true` animates scrolling
+ * - callback (Function): Function to be called when the animation has finished
*
- * TODO scrollsa to line, if center == true, puts line in middle of screen or attempts to)
+ * TODO scrolls a to line, if center == true, puts line in middle of screen or attempts to)
**/
- this.scrollToLine = function(line, center) {
- this.renderer.scrollToLine(line, center);
+ this.scrollToLine = function(line, center, animate, callback) {
+ this.renderer.scrollToLine(line, center, animate, callback);
};
/**
@@ -1672,19 +1716,21 @@ var Editor = function(renderer, session) {
* Editor.gotoLine(lineNumber, column)
* - lineNumber (Number): The line number to go to
* - column (Number): A column number to go to
+ * - animate (Boolean): If `true` animates scolling
*
* Moves the cursor to the specified line number, and also into the indiciated column.
*
**/
- this.gotoLine = function(lineNumber, column) {
+ this.gotoLine = function(lineNumber, column, animate) {
this.selection.clearSelection();
this.session.unfold({row: lineNumber - 1, column: column || 0});
this.$blockScrolling += 1;
- this.moveCursorTo(lineNumber-1, column || 0);
+ this.moveCursorTo(lineNumber - 1, column || 0);
this.$blockScrolling -= 1;
- if (!this.isRowFullyVisible(this.getCursorPosition().row))
- this.scrollToLine(lineNumber, true);
+
+ if (!this.isRowFullyVisible(lineNumber - 1))
+ this.scrollToLine(lineNumber - 1, true, animate);
};
/** related to: Editor.moveCursorTo
@@ -1790,8 +1836,10 @@ var Editor = function(renderer, session) {
* Moves the cursor to the end of the current file. Note that this does de-select the current selection.
**/
this.navigateFileEnd = function() {
+ var scrollTop = this.renderer.scrollTop;
this.selection.moveCursorFileEnd();
this.clearSelection();
+ this.renderer.animateScrolling(scrollTop);
};
/**
@@ -1800,8 +1848,10 @@ var Editor = function(renderer, session) {
* Moves the cursor to the start of the current file. Note that this does de-select the current selection.
**/
this.navigateFileStart = function() {
+ var scrollTop = this.renderer.scrollTop;
this.selection.moveCursorFileStart();
this.clearSelection();
+ this.renderer.animateScrolling(scrollTop);
};
/**
@@ -1910,44 +1960,45 @@ var Editor = function(renderer, session) {
* Editor.find(needle, options)
* - needle (String): The text to search for
* - options (Object): An object defining various search properties
+ * - animate (Boolean): If `true` animate scrolling
*
* Attempts to find `needle` within the document. For more information on `options`, see [[Search `Search`]].
**/
- this.find = function(needle, options) {
+ this.find = function(needle, options, animate) {
this.clearSelection();
options = options || {};
options.needle = needle;
this.$search.set(options);
- this.$find();
+ this.$find(false, animate);
};
/** related to: Editor.find
* Editor.findNext(options)
+ * - options (Object): search options
+ * - animate (Boolean): If `true` animate scrolling
*
* Performs another search for `needle` in the document. For more information on `options`, see [[Search `Search`]].
**/
- this.findNext = function(options) {
+ this.findNext = function(options, animate) {
options = options || {};
- if (typeof options.backwards == "undefined")
- options.backwards = false;
this.$search.set(options);
- this.$find();
+ this.$find(false, animate);
};
/** related to: Editor.find
* Editor.findPrevious(options)
+ * - options (Object): search options
+ * - animate (Boolean): If `true` animate scrolling
*
* Performs a search for `needle` backwards. For more information on `options`, see [[Search `Search`]].
**/
- this.findPrevious = function(options) {
+ this.findPrevious = function(options, animate) {
options = options || {};
- if (typeof options.backwards == "undefined")
- options.backwards = true;
this.$search.set(options);
- this.$find();
+ this.$find(true, animate);
};
- this.$find = function(backwards) {
+ this.$find = function(backwards, animate) {
if (!this.selection.isEmpty())
this.$search.set({needle: this.session.getTextRange(this.getSelectionRange())});
@@ -1956,24 +2007,14 @@ var Editor = function(renderer, session) {
var range = this.$search.find(this.session);
if (range) {
- this.session.unfold(range);
-
this.$blockScrolling += 1;
+ this.session.unfold(range);
this.selection.setSelectionRange(range);
this.$blockScrolling -= 1;
- if (this.getAnimatedScroll()) {
- var cursor = this.getCursorPosition();
- if (!this.isRowFullyVisible(cursor.row))
- this.scrollToLine(cursor.row, true);
-
- //@todo scroll X
- //if (!this.isColumnFullyVisible(cursor.column))
- //this.scrollToRow(cursor.column);
- }
- else {
- this.renderer.scrollSelectionIntoView(range.start, range.end);
- }
+ var scrollTop = this.renderer.scrollTop;
+ this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5);
+ this.renderer.animateScrolling(scrollTop);
}
};
@@ -1983,7 +2024,10 @@ var Editor = function(renderer, session) {
* {:UndoManager.undo}
**/
this.undo = function() {
+ this.$blockScrolling++;
this.session.getUndoManager().undo();
+ this.$blockScrolling--;
+ this.renderer.scrollCursorIntoView(null, 0.5);
};
/** related to: UndoManager.redo
@@ -1992,7 +2036,10 @@ var Editor = function(renderer, session) {
* {:UndoManager.redo}
**/
this.redo = function() {
+ this.$blockScrolling++;
this.session.getUndoManager().redo();
+ this.$blockScrolling--;
+ this.renderer.scrollCursorIntoView(null, 0.5);
};
/**
diff --git a/lib/ace/editor_navigation_test.js b/lib/ace/editor_navigation_test.js
index 93eaa2c2..9a715445 100644
--- a/lib/ace/editor_navigation_test.js
+++ b/lib/ace/editor_navigation_test.js
@@ -82,7 +82,7 @@ module.exports = {
editor.navigateTo(0, 0);
editor.gotoLine(101);
assert.position(editor.getCursorPosition(), 100, 0);
- assert.equal(editor.getFirstVisibleRow(), 90);
+ assert.equal(editor.getFirstVisibleRow(), 89);
editor.navigateTo(100, 0);
editor.gotoLine(11);
@@ -102,7 +102,7 @@ module.exports = {
editor.navigateTo(0, 0);
editor.gotoLine(191);
assert.position(editor.getCursorPosition(), 190, 0);
- assert.equal(editor.getFirstVisibleRow(), 180);
+ assert.equal(editor.getFirstVisibleRow(), 179);
editor.navigateTo(0, 0);
editor.gotoLine(196);
diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js
index c4f14181..53a9c9c4 100644
--- a/lib/ace/keyboard/hash_handler.js
+++ b/lib/ace/keyboard/hash_handler.js
@@ -78,6 +78,18 @@ function HashHandler(config, platform) {
}
};
+ this.bindKey = function(key, command) {
+ if(!key)
+ return;
+
+ var ckb = this.commmandKeyBinding;
+ key.split("|").forEach(function(keyPart) {
+ var binding = this.parseKeys(keyPart, command);
+ var hashId = binding.hashId;
+ (ckb[hashId] || (ckb[hashId] = {}))[binding.key] = command;
+ }, this);
+ };
+
this.addCommands = function(commands) {
commands && Object.keys(commands).forEach(function(name) {
var command = commands[name];
@@ -100,18 +112,6 @@ function HashHandler(config, platform) {
}, this);
};
- this.bindKey = function(key, command) {
- if(!key)
- return;
-
- var ckb = this.commmandKeyBinding;
- key.split("|").forEach(function(keyPart) {
- var binding = parseKeys(keyPart, command);
- var hashId = binding.hashId;
- (ckb[hashId] || (ckb[hashId] = {}))[binding.key] = command;
- });
- };
-
this.bindKeys = function(keyList) {
Object.keys(keyList).forEach(function(key) {
this.bindKey(key, keyList[key]);
@@ -127,10 +127,10 @@ function HashHandler(config, platform) {
this.bindKey(key, command);
};
- function parseKeys(keys, val, ret) {
+ this.parseKeys = function(keys, val) {
var key;
var hashId = 0;
- var parts = splitSafe(keys.toLowerCase());
+ var parts = keys.toLowerCase().trim().split(/\s*\-\s*/);
for (var i = 0, l = parts.length; i < l; i++) {
if (keyUtil.KEY_MODS[parts[i]])
@@ -143,17 +143,12 @@ function HashHandler(config, platform) {
key: key,
hashId: hashId
};
- }
-
- function splitSafe(s) {
- return (s.trim()
- .split(new RegExp("[\\s ]*\\-[\\s ]*", "g"), 999));
- }
+ };
this.findKeyCommand = function findKeyCommand(hashId, keyString) {
var ckbr = this.commmandKeyBinding;
return ckbr[hashId] && ckbr[hashId][keyString.toLowerCase()];
- }
+ };
this.handleKeyboard = function(data, hashId, keyString, keyCode) {
return {
@@ -164,4 +159,4 @@ function HashHandler(config, platform) {
}).call(HashHandler.prototype)
exports.HashHandler = HashHandler;
-});
\ No newline at end of file
+});
diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js
index 6e6656d5..79fb7aa9 100644
--- a/lib/ace/keyboard/keybinding.js
+++ b/lib/ace/keyboard/keybinding.js
@@ -47,15 +47,27 @@ require("../commands/default_commands");
var KeyBinding = function(editor) {
this.$editor = editor;
this.$data = { };
- this.$handlers = [this];
+ this.$handlers = [];
+ this.setDefaultHandler(editor.commands);
};
(function() {
+ this.setDefaultHandler = function(keyboardHandler) {
+ this.removeKeyboardHandler(this.$defaultHandler);
+ this.$defaultHandler = keyboardHandler;
+ if (keyboardHandler)
+ this.$handlers.unshift(keyboardHandler);
+ this.$data = { };
+ };
+
this.setKeyboardHandler = function(keyboardHandler) {
if (this.$handlers[this.$handlers.length - 1] == keyboardHandler)
return;
this.$data = { };
- this.$handlers = keyboardHandler ? [this, keyboardHandler] : [this];
+ this.$handlers = [];
+ this.setDefaultHandler(this.$defaultHandler);
+ if (keyboardHandler)
+ this.$handlers.push(keyboardHandler);
};
this.addKeyboardHandler = function(keyboardHandler) {
@@ -93,7 +105,7 @@ var KeyBinding = function(editor) {
// allow keyboardHandler to consume keys
if (toExecute.command != "null")
- success = commands.exec(toExecute.command, this.$editor, toExecute.args);
+ success = commands.exec(toExecute.command, this.$editor, toExecute.args, e);
else
success = true;
@@ -103,20 +115,14 @@ var KeyBinding = function(editor) {
return success;
};
- this.handleKeyboard = function(data, hashId, keyString) {
- return {
- command: this.$editor.commands.findKeyCommand(hashId, keyString)
- };
- };
-
this.onCommandKey = function(e, hashId, keyCode) {
var keyString = keyUtil.keyCodeToString(keyCode);
this.$callKeyboardHandlers(hashId, keyString, keyCode, e);
};
- this.onTextInput = function(text, pasted) {
+ this.onTextInput = function(text) {
var success = false;
- if (!pasted && text.length == 1)
+ if (text.length == 1)
success = this.$callKeyboardHandlers(0, text);
if (!success)
this.$editor.commands.exec("insertstring", this.$editor, text);
diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js
index bb917f5b..10f12865 100644
--- a/lib/ace/keyboard/textinput.js
+++ b/lib/ace/keyboard/textinput.js
@@ -72,13 +72,18 @@ var TextInput = function(parentNode, host) {
if (!copied) {
var value = valueToSend || text.value;
if (value) {
- if (value.charCodeAt(value.length-1) == PLACEHOLDER.charCodeAt(0)) {
- value = value.slice(0, -1);
- if (value)
- host.onTextInput(value, pasted);
+ if (value.length > 1) {
+ if (value.charAt(0) == PLACEHOLDER)
+ value = value.substr(1);
+ else if (value.charAt(value.length - 1) == PLACEHOLDER)
+ value = value.slice(0, -1);
}
- else {
- host.onTextInput(value, pasted);
+
+ if (value && value != PLACEHOLDER) {
+ if (pasted)
+ host.onPaste(value);
+ else
+ host.onTextInput(value);
}
// If editor is no longer focused we quit immediately, since
@@ -99,7 +104,7 @@ var TextInput = function(parentNode, host) {
var onTextInput = function(e) {
setTimeout(function () {
if (!inCompostion)
- sendText(e.data);
+ sendText(e.data);
}, 0);
};
@@ -155,6 +160,7 @@ var TextInput = function(parentNode, host) {
};
event.addCommandKeyListener(text, host.onCommandKey.bind(host));
+
if (useragent.isOldIE) {
var keytable = { 13:1, 27:1 };
event.addListener(text, "keyup", function (e) {
diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js
index d4d39bc0..e4069659 100644
--- a/lib/ace/layer/text.js
+++ b/lib/ace/layer/text.js
@@ -390,21 +390,18 @@ var Text = function(parentEl) {
this.$renderToken = function(stringBuilder, screenColumn, token, value) {
var self = this;
- var replaceReg = /\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g;
+ var replaceReg = /\t|&|<|( +)|([\u0000-\u0019\u00a0\u2000-\u200b\u2028\u2029\u3000])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g;
var replaceFunc = function(c, a, b, tabIdx, idx4) {
- if (c.charCodeAt(0) == 32) {
+ if (a) {
return new Array(c.length+1).join(" ");
+ } else if (c == "&") {
+ return useragent.isOldGecko ? "&" : "&";
+ } else if (c == "<") {
+ return "<";
} else if (c == "\t") {
var tabSize = self.session.getScreenTabSize(screenColumn + tabIdx);
screenColumn += tabSize - 1;
return self.$tabStrings[tabSize];
- } else if (c == "&") {
- if (useragent.isOldGecko)
- return "&";
- else
- return "&";
- } else if (c == "<") {
- return "<";
} else if (c == "\u3000") {
// U+3000 is both invisible AND full-width, so must be handled uniquely
var classToUse = self.showInvisibles ? "ace_cjk ace_invisible" : "ace_cjk";
@@ -413,13 +410,11 @@ var Text = function(parentEl) {
return "" + space + "";
- } else if (c.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)) {
- if (self.showInvisibles) {
- var space = new Array(c.length+1).join(self.SPACE_CHAR);
- return "" + space + "";
- } else {
+ } else if (b) {
+ if (self.showInvisibles)
+ return "" + self.SPACE_CHAR + "";
+ else
return " ";
- }
} else {
screenColumn += 1;
return " Void
+ * VirtualRenderer.scrollCursorIntoView(cursor, offset) -> Void
*
* Scrolls the cursor into the first visibile area of the editor
**/
- this.scrollCursorIntoView = function(cursor) {
+ this.scrollCursorIntoView = function(cursor, offset) {
// the editor is not visible
if (this.$size.scrollerHeight === 0)
return;
@@ -900,10 +914,12 @@ var VirtualRenderer = function(container, theme) {
var top = pos.top;
if (this.scrollTop > top) {
+ if (offset)
+ top -= offset * this.$size.scrollerHeight;
this.session.setScrollTop(top);
- }
-
- if (this.scrollTop + this.$size.scrollerHeight < top + this.lineHeight) {
+ } else if (this.scrollTop + this.$size.scrollerHeight < top + this.lineHeight) {
+ if (offset)
+ top += offset * this.$size.scrollerHeight;
this.session.setScrollTop(top + this.lineHeight - this.$size.scrollerHeight);
}
@@ -913,9 +929,7 @@ var VirtualRenderer = function(container, theme) {
if (left < this.$padding + 2 * this.layerConfig.characterWidth)
left = 0;
this.session.setScrollLeft(left);
- }
-
- if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) {
+ } else if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) {
this.session.setScrollLeft(Math.round(left + this.characterWidth - this.$size.scrollerWidth));
}
};
@@ -966,36 +980,70 @@ var VirtualRenderer = function(container, theme) {
this.session.setScrollTop(row * this.lineHeight);
};
+ this.STEPS = 8;
+ this.$calcSteps = function(fromValue, toValue){
+ var i = 0;
+ var l = this.STEPS;
+ var steps = [];
+
+ var func = function(t, x_min, dx) {
+ return dx * (Math.pow(t - 1, 3) + 1) + x_min;
+ };
+
+ for (i = 0; i < l; ++i)
+ steps.push(func(i / this.STEPS, fromValue, toValue - fromValue));
+
+ return steps;
+ };
+
/**
- * VirtualRenderer.scrollToLine(line, center) -> Void
+ * VirtualRenderer.scrollToLine(line, center, animate, callback) -> Void
* - line (Number): A line number
* - center (Boolean): If `true`, centers the editor the to indicated line
- *
+ * - animate (Boolean): If `true` animates scrolling
+ * - callback (Function): Function to be called after the animation has finished
+ *
* Gracefully scrolls the editor to the row indicated.
**/
- this.scrollToLine = function(line, center) {
+ this.scrollToLine = function(line, center, animate, callback) {
var pos = this.$cursorLayer.getPixelPosition({row: line, column: 0});
var offset = pos.top;
if (center)
offset -= this.$size.scrollerHeight / 2;
- if (this.$animatedScroll && Math.abs(offset - this.scrollTop) < 10000) {
- var _self = this;
- var steps = _self.$calcSteps(this.scrollTop, offset);
-
- clearInterval(this.$timer);
- this.$timer = setInterval(function() {
- _self.session.setScrollTop(steps.shift());
-
- if (!steps.length)
- clearInterval(_self.$timer);
- }, 10);
- }
- else {
+ var initialScroll = this.scrollTop;
this.session.setScrollTop(offset);
- }
+ if (animate !== false)
+ this.animateScrolling(initialScroll, callback);
};
+ this.animateScrolling = function(fromValue, callback) {
+ var toValue = this.scrollTop;
+ if (this.$animatedScroll && Math.abs(fromValue - toValue) < 100000) {
+ var _self = this;
+ var steps = _self.$calcSteps(fromValue, toValue);
+ this.$inScrollAnimation = true;
+
+ clearInterval(this.$timer);
+
+ _self.session.setScrollTop(steps.shift());
+ this.$timer = setInterval(function() {
+ if (steps.length) {
+ _self.session.setScrollTop(steps.shift());
+ // trick session to think it's already scrolled to not loose toValue
+ _self.session.$scrollTop = toValue;
+ } else {
+ this.$inScrollAnimation = false;
+ clearInterval(_self.$timer);
+
+ _self.session.$scrollTop = -1;
+ _self.session.setScrollTop(toValue);
+ callback && callback();
+ }
+ }, 10);
+ }
+ };
+
/**
* VirtualRenderer.scrollToY(scrollTop) -> Number
* - scrollTop (Number): The position to scroll to