diff --git a/demo/demo.js b/demo/demo.js index 542b6087..0437c78c 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -84,6 +84,7 @@ exports.launch = function(env) { } docs.plain = new EditSession(loreIpsum); docs.plain.setUseWrapMode(true); + docs.plain.setWrapLimitRange(80, 80) docs.plain.setMode(new TextMode()); docs.plain.setUndoManager(new UndoManager()); @@ -189,7 +190,7 @@ exports.launch = function(env) { if (!doc.getUseWrapMode()) { wrapModeEl.value = "off"; } else { - wrapModeEl.value = doc.getWrapLimit(); + wrapModeEl.value = doc.getWrapLimitRange().min || "free"; } env.editor.focus(); }); @@ -220,12 +221,17 @@ exports.launch = function(env) { break; case "40": session.setUseWrapMode(true); - session.setWrapLimit(40); + session.setWrapLimitRange(40, 40); renderer.setPrintMarginColumn(40); break; case "80": session.setUseWrapMode(true); - session.setWrapLimit(80); + session.setWrapLimitRange(80, 80); + renderer.setPrintMarginColumn(80); + break; + case "free": + session.setUseWrapMode(true); + session.setWrapLimitRange(null, null); renderer.setPrintMarginColumn(80); break; } diff --git a/editor.html b/editor.html index c8d0ebec..d4557f3b 100644 --- a/editor.html +++ b/editor.html @@ -97,6 +97,7 @@ + diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index d06147ae..98f382bc 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -642,6 +642,10 @@ var EditSession = function(text, mode) { // WRAPMODE this.$wrapLimit = 80; this.$useWrapMode = false; + this.$wrapLimitRange = { + min : null, + max : null + }; this.setUseWrapMode = function(useWrapMode) { if (useWrapMode != this.$useWrapMode) { @@ -666,21 +670,61 @@ var EditSession = function(text, mode) { return this.$useWrapMode; }; - this.setWrapLimit = function(wrapLimit) { - if (wrapLimit != this.$wrapLimit) { - this.$wrapLimit = wrapLimit; + // Allow the wrap limit to move freely between min and max. Either + // parameter can be null to allow the wrap limit to be unconstrained + // in that direction. Or set both parameters to the same number to pin + // the limit to that value. + this.setWrapLimitRange = function(min, max) { + if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { + this.$wrapLimitRange.min = min; + this.$wrapLimitRange.max = max; this.$modified = true; - if (this.$useWrapMode) { - this.$updateWrapData(0, this.getLength() - 1); - } + // This will force a recalculation of the wrap limit this._dispatchEvent("changeWrapMode"); } }; + // This should generally only be called by the renderer when a resize + // is detected. + this.adjustWrapLimit = function(desiredLimit) { + var wrapLimit = this.$constrainWrapLimit(desiredLimit); + if (wrapLimit != this.$wrapLimit && wrapLimit > 0) { + this.$wrapLimit = wrapLimit; + this.$modified = true; + if (this.$useWrapMode) { + this.$updateWrapData(0, this.getLength() - 1); + this._dispatchEvent("changeWrapLimit"); + } + return true; + } + return false; + }; + + this.$constrainWrapLimit = function(wrapLimit) { + var min = this.$wrapLimitRange.min; + if (min) + wrapLimit = Math.max(min, wrapLimit); + + var max = this.$wrapLimitRange.max; + if (max) + wrapLimit = Math.min(max, wrapLimit); + + // What would a limit of 0 even mean? + return Math.max(1, wrapLimit); + }; + this.getWrapLimit = function() { return this.$wrapLimit; }; + this.getWrapLimitRange = function() { + // Avoid unexpected mutation by returning a copy + return { + min : this.$wrapLimitRange.min, + max : this.$wrapLimitRange.max + }; + }; + this.$updateWrapDataOnChange = function(e) { if (!this.$useWrapMode) { return; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index a0669372..685ee10a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -126,6 +126,7 @@ var Editor =function(renderer, session) { this.session.removeEventListener("change", this.$onDocumentChange); this.session.removeEventListener("changeMode", this.$onDocumentModeChange); this.session.removeEventListener("changeTabSize", this.$onDocumentChangeTabSize); + this.session.removeEventListener("changeWrapLimit", this.$onDocumentChangeWrapLimit); this.session.removeEventListener("changeWrapMode", this.$onDocumentChangeWrapMode); this.session.removeEventListener("changeBreakpoint", this.$onDocumentChangeBreakpoint); this.session.removeEventListener("changeAnnotation", this.$onDocumentChangeAnnotation); @@ -149,6 +150,9 @@ var Editor =function(renderer, session) { this.$onDocumentChangeTabSize = this.renderer.updateText.bind(this.renderer); session.addEventListener("changeTabSize", this.$onDocumentChangeTabSize); + this.$onDocumentChangeWrapLimit = this.onDocumentChangeWrapLimit.bind(this); + session.addEventListener("changeWrapLimit", this.$onDocumentChangeWrapLimit); + this.$onDocumentChangeWrapMode = this.onDocumentChangeWrapMode.bind(this); session.addEventListener("changeWrapMode", this.$onDocumentChangeWrapMode); @@ -348,11 +352,15 @@ var Editor =function(renderer, session) { this.renderer.setTokenizer(this.bgTokenizer); }; - this.onDocumentChangeWrapMode = function() { + this.onDocumentChangeWrapLimit = function() { this.renderer.updateCursor(this.getCursorPosition(), this.$overwrite); this.renderer.updateFull(); }; + this.onDocumentChangeWrapMode = function() { + this.renderer.onResize(true); + }; + this.getCopyText = function() { if (!this.selection.isEmpty()) { return this.session.getTextRange(this.getSelectionRange()); diff --git a/lib/ace/keyboard/state_handler.js b/lib/ace/keyboard/state_handler.js index b887cd4d..5861ad23 100644 --- a/lib/ace/keyboard/state_handler.js +++ b/lib/ace/keyboard/state_handler.js @@ -197,7 +197,7 @@ StateHandler.prototype = { // 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 senese. - if (hashId != 0 && (key == "" || key == String.fromCharCode(0))) { + if (hashId != 0 && (key == "" || String.fromCharCode(0))) { return null; } diff --git a/lib/ace/test/edit_session_test.js b/lib/ace/test/edit_session_test.js index de6d8f39..3ab24e94 100644 --- a/lib/ace/test/edit_session_test.js +++ b/lib/ace/test/edit_session_test.js @@ -169,14 +169,14 @@ var Test = { var wrapLimit = 12; var session = new EditSession(["foo bar foo bar"]); session.setUseWrapMode(true); - session.setWrapLimit(12); + session.setWrapLimitRange(12, 12); assert.position(session.documentToScreenPosition(0, 11), 0, 11); assert.position(session.documentToScreenPosition(0, 12), 1, 0); session = new EditSession(["ぁぁa"]); session.setUseWrapMode(true); - session.setWrapLimit(2); + session.setWrapLimitRange(2, 2); assert.position(session.documentToScreenPosition(0, 1), 1, 0); assert.position(session.documentToScreenPosition(0, 2), 2, 0); assert.position(session.documentToScreenPosition(0, 4), 2, 1); @@ -202,7 +202,7 @@ var Test = { var wrapLimit = 12; var session = new EditSession(["foo bar foo bar"]); session.setUseWrapMode(true); - session.setWrapLimit(12); + session.setWrapLimitRange(12, 12); assert.position(session.screenToDocumentPosition(1, 0), 0, 12); assert.position(session.screenToDocumentPosition(0, 11), 0, 11); @@ -378,7 +378,7 @@ var Test = { var document = session.getDocument(); session.setUseWrapMode(true); - session.setWrapLimit(3); + session.setWrapLimitRange(3, 3); // Test if wrapData is there and was computed. assert.equal(session.$wrapData.length, 2); diff --git a/lib/ace/theme/twilight.js b/lib/ace/theme/twilight.js index 600ba519..2fe50e40 100644 --- a/lib/ace/theme/twilight.js +++ b/lib/ace/theme/twilight.js @@ -89,6 +89,9 @@ define(function(require, exports, module) { .ace-twilight.normal-mode .ace_cursor.ace_overwrite {\ border: 1px solid #FFE300;\ background: #766B13;\ +}\ +.ace-twilight.normal-mode .ace_cursor-layer {\ + z-index: 0;\ }\ \ .ace-twilight .ace_marker-layer .ace_selection {\ diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index a432bd3f..c7fa8d63 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -210,6 +210,13 @@ var VirtualRenderer = function(container, theme) { var gutterWidth = this.showGutter ? this.$gutter.offsetWidth : 0; this.scroller.style.left = gutterWidth + "px"; this.scroller.style.width = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) + "px"; + + if (this.session.getUseWrapMode()) { + var limit = Math.floor(this.scroller.clientWidth / this.characterWidth); + if (this.session.adjustWrapLimit(limit) || force) { + changes = changes | this.CHANGE_FULL; + } + } } this.$size.scrollerWidth = this.scroller.clientWidth;