diff --git a/demo/kitchen-sink/statusbar.js b/demo/kitchen-sink/statusbar.js index 946ae1d4..57620036 100644 --- a/demo/kitchen-sink/statusbar.js +++ b/demo/kitchen-sink/statusbar.js @@ -9,14 +9,14 @@ var StatusBar = function(editor, parentNode) { this.element.style.cssText = "color: gray; position:absolute; right:0; border-left:1px solid"; parentNode.appendChild(this.element); - var statusUpdate = lang.deferredCall(function(){ + var statusUpdate = lang.delayedCall(function(){ this.updateStatus(editor) }.bind(this)); editor.on("changeStatus", function() { - statusUpdate.schedule(50); + statusUpdate.schedule(100); }); editor.on("changeSelection", function() { - statusUpdate.schedule(50); + statusUpdate.schedule(100); }); }; diff --git a/lib/ace/config.js b/lib/ace/config.js index b242e990..bdd6a162 100644 --- a/lib/ace/config.js +++ b/lib/ace/config.js @@ -169,4 +169,93 @@ function deHyphenate(str) { return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); }); } +function capitalize(str) { + return str[0].toUpperCase() + str.substr(1); +} + + +var optionsProvider = { + setOptions: function(optList) { + Object.keys(optList).forEach(function(key) { + this.setOption(key, optList[key]); + }, this); + }, + getOptions: function(a) { + var b = {}; + Object.keys(a).forEach(function(key) { + b[key] = this.getOption(key); + }, this); + return b; + }, + setOption: function(name, value) { + if (this["$" + name] === value) + return; + var opt = this.$options[name]; + if (!opt) + return undefined; + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].setOption(name, value); + + if (!opt.handlesSet) + this["$" + name] = value; + if (opt && opt.set) + opt.set.call(this, value); + }, + getOption: function(name) { + var opt = this.$options[name]; + if (!opt) + return undefined; + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].getOption(name); + return opt && opt.get ? opt.get.call(this) : this["$" + name]; + } +}; + +var defaultOptions = {}; +/* + * option {name, value, initialValue, setterName, set, get } + */ +exports.defineOptions = function(obj, path, options) { + if (!obj.$options) + defaultOptions[path] = obj.$options = {}; + + Object.keys(options).forEach(function(key) { + var opt = options[key]; + if (typeof opt == "string") + opt = {forwardTo: opt}; + + opt.name || (opt.name = key); + obj.$options[opt.name] = opt; + if ("initialValue" in opt) + obj["$" + opt.name] = opt.initialValue; + + opt.setterName = capitalize(opt.name) + if (opt.setterName) { + addSetters(obj, opt.name, opt.setterName); + } + }); + + // implement option provider interface + oop.implement(obj, optionsProvider); + + return this; +}; + +function addSetters(obj, name, setterName) { + obj["set" + setterName] = function(value) { + return this.setOption(name, value) + }; + obj["get" + setterName] = function() { + return this.getOption(name) + }; +}; + +exports.resetOptions = function(obj) { + Object.keys(obj.$options).forEach(function(key) { + var opt = obj.$options[key]; + if ("value" in opt) + obj.setOption(key, opt.value); + }); +}; + }); diff --git a/lib/ace/config_test.js b/lib/ace/config_test.js index 9d0c4697..0e4f5449 100644 --- a/lib/ace/config_test.js +++ b/lib/ace/config_test.js @@ -40,7 +40,7 @@ var assert = require("./test/assertions"); module.exports = { - "test path resolution" : function() { + "test: path resolution" : function() { config.set("packaged", "true"); var url = config.moduleUrl("kr_theme", "theme"); assert.equal(url, "theme-kr.js"); @@ -61,6 +61,53 @@ module.exports = { assert.equal(url, "a/b1.js"); assert.equal(); + }, + "test: define options" : function() { + var o = {}; + config.defineOptions(o, "test_object", { + opt1: { + set: function(val) { + this.x = val; + }, + value: 7, + }, + initialValue: { + set: function(val) { + this.x = val; + }, + initialValue: 8, + }, + opt2: { + get: function(val) { + return this.x; + } + }, + forwarded: "model" + }); + o.model = {}; + config.defineOptions(o.model, "model", { + forwarded: {value: 1} + }); + + config.resetOptions(o); + config.resetOptions(o.model); + assert.equal(o.getOption("opt1"), 7); + assert.equal(o.getOption("opt2"), 7); + o.setOption("opt1", 8); + assert.equal(o.getOption("opt1"), 8); + assert.equal(o.getOption("opt2"), 8); + + assert.equal(o.getOption("forwarded"), 1); + + assert.equal(o.getOption("new"), undefined); + o.setOption("new", 0); + assert.equal(o.getOption("new"), undefined); + + + assert.equal(o.getOption("initialValue"), 8); + o.setOption("initialValue", 7); + assert.equal(o.getOption("opt2"), 7); + } }; diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 9ac41352..81afdb4e 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -42,6 +42,7 @@ var Range = require("./range").Range; var Document = require("./document").Document; var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer; var SearchHighlight = require("./search_highlight").SearchHighlight; +var config = require("./config"); /** * @@ -175,6 +176,9 @@ var EditSession = function(text, mode) { this.selection = new Selection(this); this.setMode(mode); + + config.resetOptions(this); + config._emit("session", this); }; @@ -452,50 +456,26 @@ var EditSession = function(text, mode) { } }; - this.$useSoftTabs = true; /** * Pass `true` to enable the use of soft tabs. Soft tabs means you're using spaces instead of the tab character (`'\t'`). * @param {Boolean} useSoftTabs Value indicating whether or not to use soft tabs * - * - * **/ - this.setUseSoftTabs = function(useSoftTabs) { - if (this.$useSoftTabs === useSoftTabs) return; - - this.$useSoftTabs = useSoftTabs; - }; /** * Returns `true` if soft tabs are being used, `false` otherwise. * @returns {Boolean} **/ - this.getUseSoftTabs = function() { - return this.$useSoftTabs; - }; - this.$tabSize = 4; /** * Set the number of spaces that define a soft tab; for example, passing in `4` transforms the soft tabs to be equivalent to four spaces. This function also emits the `changeTabSize` event. * @param {Number} tabSize The new tab size * * **/ - this.setTabSize = function(tabSize) { - if (isNaN(tabSize) || this.$tabSize === tabSize) return; - - this.$modified = true; - this.$rowLengthCache = []; - this.$tabSize = tabSize; - this._emit("changeTabSize"); - }; - /** * Returns the current tab size. **/ - this.getTabSize = function() { - return this.$tabSize; - }; /** * Returns `true` if the character at the position is a soft tab. @@ -864,31 +844,14 @@ var EditSession = function(text, mode) { return this.doc.getNewLineMode(); }; - this.$useWorker = true; - /** * Identifies if you want to use a worker for the `EditSession`. * @param {Boolean} useWorker Set to `true` to use a worker * **/ - this.setUseWorker = function(useWorker) { - if (this.$useWorker == useWorker) - return; - - this.$useWorker = useWorker; - - this.$stopWorker(); - if (useWorker) - this.$startWorker(); - }; - /** * Returns `true` if workers are being used. **/ - this.getUseWorker = function() { - return this.$useWorker; - }; - /** * Reloads all the tokens on the current session. This function calls [[BackgroundTokenizer.start `BackgroundTokenizer.start ()`]] to all the rows; it also emits the `'tokenizerUpdate'` event. **/ @@ -2403,5 +2366,57 @@ var EditSession = function(text, mode) { require("./edit_session/folding").Folding.call(EditSession.prototype); require("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype); +config.defineOptions(EditSession.prototype, "session", { + wrap: { + set: function(value) { + if (!value || value == "off") + value = false; + else if (value == "free") + value = true; + else if (typeof value == "string") + value = parseInt(value, 10) || false; + + if (!value) { + this.setUseWrapMode(false); + } else { + var col = typeof value == "number" && value; + this.setUseWrapMode(true); + this.setWrapLimitRange(value, value); + } + this.$wrap = value; + }, + get: function() { + return this.getUseWrapMode() ? this.getWrapLimitRange().min || "free" : "off"; + }, + handlesSet: true + }, + firstLineNumber: { + set: function() {this._emit("changeBreakpoint");}, + initialValue: 1 + }, + useWorker: { + set: function(useWorker) { + this.$useWorker = useWorker; + + this.$stopWorker(); + if (useWorker) + this.$startWorker(); + }, + initialValue: true + }, + useSoftTabs: {initialValue: true}, + tabSize: { + set: function(tabSize) { + if (isNaN(tabSize) || this.$tabSize === tabSize) return; + + this.$modified = true; + this.$rowLengthCache = []; + this.$tabSize = tabSize; + this._emit("changeTabSize"); + }, + initialValue: 4 + } +}); + exports.EditSession = EditSession; }); diff --git a/lib/ace/editor.js b/lib/ace/editor.js index cbd0ef71..7ab2e564 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -88,6 +88,8 @@ var Editor = function(renderer, session) { }); this.setSession(session || new EditSession("")); + config.resetOptions(this); + config._emit("editor", this); }; (function(){ @@ -807,18 +809,10 @@ var Editor = function(renderer, session) { * * **/ - this.setScrollSpeed = function(speed) { - this.$mouseHandler.setScrollSpeed(speed); - }; - /** * Returns the value indicating how fast the mouse scroll speed is (in milliseconds). * @returns {Number} **/ - this.getScrollSpeed = function() { - return this.$mouseHandler.getScrollSpeed(); - }; - /** * Sets the delay (in milliseconds) of the mouse drag. * @param {Number} dragDelay A value indicating the new delay @@ -826,19 +820,10 @@ var Editor = function(renderer, session) { * * **/ - this.setDragDelay = function(dragDelay) { - this.$mouseHandler.setDragDelay(dragDelay); - }; - /** * Returns the current mouse drag delay. * @returns {Number} **/ - this.getDragDelay = function() { - return this.$mouseHandler.getDragDelay(); - }; - - this.$selectionStyle = "line"; /** * Emitted when the selection style changes, via [[Editor.setSelectionStyle]]. @@ -860,133 +845,49 @@ var Editor = function(renderer, session) { * * **/ - this.setSelectionStyle = function(style) { - if (this.$selectionStyle == style) return; - - this.$selectionStyle = style; - this.onSelectionChange(); - this._emit("changeSelectionStyle", {data: style}); - }; - /** * Returns the current selection style. * @returns {String} **/ - this.getSelectionStyle = function() { - return this.$selectionStyle; - }; - - this.$highlightActiveLine = true; - /** * Determines whether or not the current line should be highlighted. * @param {Boolean} shouldHighlight Set to `true` to highlight the current line * **/ - this.setHighlightActiveLine = function(shouldHighlight) { - if (this.$highlightActiveLine == shouldHighlight) - return; - - this.$highlightActiveLine = shouldHighlight; - this.$updateHighlightActiveLine(); - }; - /** * Returns `true` if current lines are always highlighted. * @return {Boolean} **/ - this.getHighlightActiveLine = function() { - return this.$highlightActiveLine; - }; - - this.$highlightGutterLine = true; - this.setHighlightGutterLine = function(shouldHighlight) { - if (this.$highlightGutterLine == shouldHighlight) - return; - - this.renderer.setHighlightGutterLine(shouldHighlight); - this.$highlightGutterLine = shouldHighlight; - }; - - this.getHighlightGutterLine = function() { - return this.$highlightGutterLine; - }; - - this.$highlightSelectedWord = true; /** * Determines if the currently selected word should be highlighted. * @param {Boolean} shouldHighlight Set to `true` to highlight the currently selected word * * **/ - this.setHighlightSelectedWord = function(shouldHighlight) { - if (this.$highlightSelectedWord == shouldHighlight) - return; - - this.$highlightSelectedWord = shouldHighlight; - this.$onSelectionChange(); - }; - /** * Returns `true` if currently highlighted words are to be highlighted. * @returns {Boolean} **/ - this.getHighlightSelectedWord = function() { - return this.$highlightSelectedWord; - }; - - this.setAnimatedScroll = function(shouldAnimate){ - this.renderer.setAnimatedScroll(shouldAnimate); - }; - - this.getAnimatedScroll = function(){ - return this.renderer.getAnimatedScroll(); - }; - /** * If `showInvisibiles` is set to `true`, invisible characters—like spaces or new lines—are show in the editor. * @param {Boolean} showInvisibles Specifies whether or not to show invisible characters * * **/ - this.setShowInvisibles = function(showInvisibles) { - this.renderer.setShowInvisibles(showInvisibles); - }; - /** * Returns `true` if invisible characters are being shown. * @returns {Boolean} **/ - this.getShowInvisibles = function() { - return this.renderer.getShowInvisibles(); - }; - - this.setDisplayIndentGuides = function(display) { - this.renderer.setDisplayIndentGuides(display); - }; - - this.getDisplayIndentGuides = function() { - return this.renderer.getDisplayIndentGuides(); - }; - /** * If `showPrintMargin` is set to `true`, the print margin is shown in the editor. * @param {Boolean} showPrintMargin Specifies whether or not to show the print margin * * **/ - this.setShowPrintMargin = function(showPrintMargin) { - this.renderer.setShowPrintMargin(showPrintMargin); - }; - /** * Returns `true` if the print margin is being shown. * @returns {Boolean} **/ - this.getShowPrintMargin = function() { - return this.renderer.getShowPrintMargin(); - }; - /** * Sets the column defining where the print margin should be. * @param {Number} showPrintMargin Specifies the new print margin @@ -994,61 +895,26 @@ var Editor = function(renderer, session) { * * **/ - this.setPrintMarginColumn = function(showPrintMargin) { - this.renderer.setPrintMarginColumn(showPrintMargin); - }; - /** * Returns the column number of where the print margin is. * @returns {Number} **/ - this.getPrintMarginColumn = function() { - return this.renderer.getPrintMarginColumn(); - }; - - this.$readOnly = false; /** * If `readOnly` is true, then the editor is set to read-only mode, and none of the content can change. * @param {Boolean} readOnly Specifies whether the editor can be modified or not * * **/ - this.setReadOnly = function(readOnly) { - this.$readOnly = readOnly; - this.textInput.setReadOnly(readOnly); - this.renderer.$cursorLayer.setBlinking(!readOnly); - }; - /** * Returns `true` if the editor is set to read-only mode. * @returns {Boolean} **/ - this.getReadOnly = function() { - return this.$readOnly; - }; - - this.$modeBehaviours = true; - /** * Specifies whether to use behaviors or not. ["Behaviors" in this case is the auto-pairing of special characters, like quotation marks, parenthesis, or brackets.]{: #BehaviorsDef} * @param {Boolean} enabled Enables or disables behaviors * * **/ - this.setBehavioursEnabled = function (enabled) { - this.$modeBehaviours = enabled; - }; - - /** - * Returns `true` if the behaviors are currently enabled. {:BehaviorsDef} - * - * @returns {Boolean} - **/ - this.getBehavioursEnabled = function () { - return this.$modeBehaviours; - }; - - this.$modeWrapBehaviours = true; /** * Specifies whether to use wrapping behaviors or not, i.e. automatically wrapping the selection with characters such as brackets @@ -1056,49 +922,19 @@ var Editor = function(renderer, session) { * @param {Boolean} enabled Enables or disables wrapping behaviors * **/ - this.setWrapBehavioursEnabled = function (enabled) { - this.$modeWrapBehaviours = enabled; - }; - /** * Returns `true` if the wrapping behaviors are currently enabled. **/ - this.getWrapBehavioursEnabled = function () { - return this.$modeWrapBehaviours; - }; /** * Indicates whether the fold widgets are shown or not. * @param {Boolean} show Specifies whether the fold widgets are shown * - * **/ - this.setShowFoldWidgets = function(show) { - var gutter = this.renderer.$gutterLayer; - if (gutter.getShowFoldWidgets() == show) - return; - - this.renderer.$gutterLayer.setShowFoldWidgets(show); - this.$showFoldWidgets = show; - this.renderer.updateFull(); - }; - /** * Returns `true` if the fold widgets are shown. * @return {Boolean} **/ - this.getShowFoldWidgets = function() { - return this.renderer.$gutterLayer.getShowFoldWidgets(); - }; - - this.setFadeFoldWidgets = function(show) { - this.renderer.setFadeFoldWidgets(show); - }; - - this.getFadeFoldWidgets = function() { - return this.renderer.getFadeFoldWidgets(); - }; - /** * Removes words of text from the editor. A "word" is defined as a string of characters bookended by whitespace. * @param {String} dir The direction of the deletion to occur, either "left" or "right" @@ -2172,5 +2008,45 @@ var Editor = function(renderer, session) { }).call(Editor.prototype); + +config.defineOptions(Editor.prototype, "editor", { + selectionStyle: { + set: function(style) { + this.onSelectionChange(); + this._emit("changeSelectionStyle", {data: style}); + }, + initialValue: "line", + }, + highlightActiveLine: { + set: function() {this.$updateHighlightActiveLine();}, + initialValue: true + }, + highlightSelectedWord: { + set: function(shouldHighlight) {this.$onSelectionChange();}, + initialValue: true + }, + readOnly: { + set: function(readOnly) { + this.textInput.setReadOnly(readOnly); + this.renderer.$cursorLayer.setBlinking(!readOnly); + }, + initialValue: false + }, + behavioursEnabled: {initialValue: true}, + wrapBehavioursEnabled: {initialValue: true}, + + highlightGutterLine: "renderer", + animatedScroll: "renderer", + showInvisibles: "renderer", + showPrintMargin: "renderer", + printMarginColumn: "renderer", + fadeFoldWidgets: "renderer", + showFoldWidgets: "renderer", + displayIndentGuides: "renderer", + scrollSpeed: "$mouseHandler", + dragDelay: "$mouseHandler", + focusTimout: "$mouseHandler" +}); + exports.Editor = Editor; }); diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index 92001c02..9f0d7128 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -31,7 +31,7 @@ define(function(require, exports, module) { "use strict"; -var keyUtil = require("../lib/keys"); +var keyUtil = require("../lib/keys"); function HashHandler(config, platform) { this.platform = platform; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 99fec7b6..109d6a67 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -126,6 +126,7 @@ var Gutter = function(parentEl) { var foldWidgets = this.$showFoldWidgets && this.session.foldWidgets; var breakpoints = this.session.$breakpoints; var decorations = this.session.$decorations; + var firstLineNumber = this.session.$firstLineNumber; var lastLineNumber = 0; while (true) { @@ -142,7 +143,7 @@ var Gutter = function(parentEl) { "