diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js
index 9e3062f5..a5ed08da 100644
--- a/demo/kitchen-sink/demo.js
+++ b/demo/kitchen-sink/demo.js
@@ -242,6 +242,10 @@ var docs = [
new WrappedDoc(
"latex", "LaTeX",
require("ace/requirejs/text!./docs/latex.tex")
+ ),
+ new Doc(
+ "markdown", "International Text",
+ require("ace/requirejs/text!./docs/international.md")
)
];
@@ -336,6 +340,8 @@ function updateUIEditorOptions() {
selectStyleEl.checked = editor.getSelectionStyle() == "line";
themeEl.value = editor.getTheme();
highlightActiveEl.checked = editor.getHighlightActiveLine();
+
+ editor.setShowInvisibles(true);
showHiddenEl.checked = editor.getShowInvisibles();
showGutterEl.checked = editor.renderer.getShowGutter();
showPrintMarginEl.checked = editor.renderer.getShowPrintMargin();
diff --git a/demo/kitchen-sink/docs/international.md b/demo/kitchen-sink/docs/international.md
new file mode 100644
index 00000000..92646875
--- /dev/null
+++ b/demo/kitchen-sink/docs/international.md
@@ -0,0 +1,7 @@
+Pinyin Simplified
+-----------------
+反对方山东队但是上
+
+Thai
+----
+อักษรไทย
\ No newline at end of file
diff --git a/kitchen-sink.html b/kitchen-sink.html
index ee49cd28..f3e1463c 100644
--- a/kitchen-sink.html
+++ b/kitchen-sink.html
@@ -75,6 +75,7 @@
+
diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js
index cbdcc1c9..db79a67b 100644
--- a/lib/ace/edit_session.js
+++ b/lib/ace/edit_session.js
@@ -1150,7 +1150,6 @@ var EditSession = function(text, mode) {
// "Tokens"
var CHAR = 1,
- CHAR_EXT = 2,
PLACEHOLDER_START = 3,
PLACEHOLDER_BODY = 4,
PUNCTUATION = 9,
@@ -1177,10 +1176,6 @@ var EditSession = function(text, mode) {
// Get all the TAB_SPACEs.
replace(/12/g, function() {
len -= 1;
- }).
- // Get all the CHAR_EXT/multipleWidth characters.
- replace(/2/g, function() {
- len -= 1;
});
lastDocSplit += len;
@@ -1267,7 +1262,7 @@ var EditSession = function(text, mode) {
// === ELSE ===
split = lastSplit + wrapLimit;
- // The split is inside of a CHAR or CHAR_EXT token and no space
+ // The split is inside of a CHAR token and no space
// around -> force a split.
addSplit(split);
}
@@ -1299,10 +1294,6 @@ var EditSession = function(text, mode) {
arr.push(SPACE);
} else if((c > 39 && c < 48) || (c > 57 && c < 64)) {
arr.push(PUNCTUATION);
- }
- // full width characters
- else if (c >= 0x1100 && isFullWidth(c)) {
- arr.push(CHAR, CHAR_EXT);
} else {
arr.push(CHAR);
}
@@ -1336,12 +1327,8 @@ var EditSession = function(text, mode) {
if (c == 9) {
screenColumn += this.getScreenTabSize(screenColumn);
}
- // full width characters
- else if (c >= 0x1100 && isFullWidth(c)) {
- screenColumn += 2;
- } else {
- screenColumn += 1;
- }
+ screenColumn += 1;
+
if (screenColumn > maxScreenColumn) {
break
}
@@ -1650,45 +1637,6 @@ var EditSession = function(text, mode) {
return screenRows;
}
- // For every keystroke this gets called once per char in the whole doc!!
- // Wouldn't hurt to make it a bit faster for c >= 0x1100
- function isFullWidth(c) {
- if (c < 0x1100)
- return false;
- return c >= 0x1100 && c <= 0x115F ||
- c >= 0x11A3 && c <= 0x11A7 ||
- c >= 0x11FA && c <= 0x11FF ||
- c >= 0x2329 && c <= 0x232A ||
- c >= 0x2E80 && c <= 0x2E99 ||
- c >= 0x2E9B && c <= 0x2EF3 ||
- c >= 0x2F00 && c <= 0x2FD5 ||
- c >= 0x2FF0 && c <= 0x2FFB ||
- c >= 0x3000 && c <= 0x303E ||
- c >= 0x3041 && c <= 0x3096 ||
- c >= 0x3099 && c <= 0x30FF ||
- c >= 0x3105 && c <= 0x312D ||
- c >= 0x3131 && c <= 0x318E ||
- c >= 0x3190 && c <= 0x31BA ||
- c >= 0x31C0 && c <= 0x31E3 ||
- c >= 0x31F0 && c <= 0x321E ||
- c >= 0x3220 && c <= 0x3247 ||
- c >= 0x3250 && c <= 0x32FE ||
- c >= 0x3300 && c <= 0x4DBF ||
- c >= 0x4E00 && c <= 0xA48C ||
- c >= 0xA490 && c <= 0xA4C6 ||
- c >= 0xA960 && c <= 0xA97C ||
- c >= 0xAC00 && c <= 0xD7A3 ||
- c >= 0xD7B0 && c <= 0xD7C6 ||
- c >= 0xD7CB && c <= 0xD7FB ||
- c >= 0xF900 && c <= 0xFAFF ||
- c >= 0xFE10 && c <= 0xFE19 ||
- c >= 0xFE30 && c <= 0xFE52 ||
- c >= 0xFE54 && c <= 0xFE66 ||
- c >= 0xFE68 && c <= 0xFE6B ||
- c >= 0xFF01 && c <= 0xFF60 ||
- c >= 0xFFE0 && c <= 0xFFE6;
- };
-
}).call(EditSession.prototype);
require("./edit_session/folding").Folding.call(EditSession.prototype);
diff --git a/lib/ace/layer/cursor.js b/lib/ace/layer/cursor.js
index 8dec094d..68d81cac 100644
--- a/lib/ace/layer/cursor.js
+++ b/lib/ace/layer/cursor.js
@@ -102,14 +102,15 @@ var Cursor = function(parentEl) {
var position = this.session.selection.getCursor();
var pos = this.session.documentToScreenPosition(position);
- var cursorLeft = Math.round(this.$padding +
- pos.column * this.config.characterWidth);
- var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) *
- this.config.lineHeight;
+ var textWidth = this.config.textWidth(pos.row, pos.column);
+ var cursorLeft = Math.round(this.$padding + textWidth);
+ var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) * this.config.lineHeight;
+ var cursorWidth = (this.config.textWidth(pos.row, pos.column + 1) - textWidth) || this.config.characterWidth;
return {
left : cursorLeft,
- top : cursorTop
+ top : cursorTop,
+ width: cursorWidth
};
};
@@ -120,7 +121,7 @@ var Cursor = function(parentEl) {
this.cursor.style.left = this.pixelPos.left + "px";
this.cursor.style.top = this.pixelPos.top + "px";
- this.cursor.style.width = config.characterWidth + "px";
+ this.cursor.style.width = this.pixelPos.width + "px";
this.cursor.style.height = config.lineHeight + "px";
var overwrite = this.session.getOverwrite()
diff --git a/lib/ace/layer/marker.js b/lib/ace/layer/marker.js
index ce8a5222..8133a8a9 100644
--- a/lib/ace/layer/marker.js
+++ b/lib/ace/layer/marker.js
@@ -81,9 +81,7 @@ var Marker = function(parentEl) {
range = range.toScreenRange(this.session);
if (marker.renderer) {
var top = this.$getTop(range.start.row, config);
- var left = Math.round(
- this.$padding + range.start.column * config.characterWidth
- );
+ var left = this.$padding + config.textWidth(range.start.row, range.start.column);
marker.renderer(html, range, left, top, config);
}
else if (range.isMultiLine()) {
@@ -143,11 +141,10 @@ var Marker = function(parentEl) {
// from selection start to the end of the line
var padding = type === "background" ? 0 : this.$padding;
var height = layerConfig.lineHeight;
- var width = Math.round(layerConfig.width - (range.start.column * layerConfig.characterWidth));
+ var textWidth = layerConfig.textWidth(range.start.row, range.start.column);
+ var left = padding + textWidth;
+ var width = Math.round(layerConfig.width - textWidth);
var top = this.$getTop(range.start.row, layerConfig);
- var left = Math.round(
- padding + range.start.column * layerConfig.characterWidth
- );
stringBuilder.push(
"
" + 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);
@@ -376,11 +368,6 @@ var Text = function(parentEl) {
} else {
return " ";
}
- } else {
- screenColumn += 1;
- return "" + c + "";
}
};
@@ -398,6 +385,101 @@ var Text = function(parentEl) {
}
return screenColumn + value.length;
};
+
+ this.$measureText = function(tokens, column) {
+ // build HTML for tokens
+ var stringBuilder = [];
+ var len = 0;
+ var i = 0;
+ var screenColumn = 0; // TODO
+ while (len < column && i < tokens.length) {
+ var token = tokens[i++];
+
+ var self = this;
+ var replaceReg = /\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])/g;
+ var replaceFunc = function(c, a, b, tabIdx, idx4) {
+ if (c.charCodeAt(0) == 32) {
+ return new Array(c.length+1).join(" ");
+ } 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.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 {
+ return " ";
+ }
+ }
+ };
+
+ var value = token.value;
+
+ // truncate once length is larger than 'column'
+ len += value.length;
+ if (len > column)
+ value = value.substring(0, value.length - (len - column));
+
+ var output = value.replace(replaceReg, replaceFunc);
+
+
+ if (!this.$textToken[token.type]) {
+ var classes = "ace_" + token.type.replace(/\./g, " ace_");
+ stringBuilder.push("", output, "");
+ }
+ else {
+ stringBuilder.push(output);
+ }
+ }
+
+ // render line off screen
+ var el = document.createElement("div");
+ var style = el.style;
+ style.position = "absolute";
+ style.top = "-1000px";
+ el.className = "ace_line";
+ el.innerHTML = stringBuilder.join("");
+ this.element.appendChild(el);
+
+ // measure pixel length
+ var width = el.offsetWidth;
+ this.element.removeChild(el);
+
+ return width;
+ }
+
+ this.textWidth = function(row, column) {
+ //return this.$characterSize.width * column;
+ var line = this.session.getTokens(row, row)[0];
+
+ // cache in tokens object
+ // this way the cache gets invalidated automatically when the tokens change
+ if (line.widthCache && line.widthCache[column]) {
+ // invalidate if font size has changed
+ if (line.widthCache.rowHeight == this.getLineHeight()) {
+ return line.widthCache[column];
+ }
+ else
+ delete line.widthCache;
+ }
+
+ var width = this.$measureText(line.tokens, column);
+
+ if (!line.widthCache)
+ line.widthCache = { rowHeight: this.getLineHeight() };
+
+ line.widthCache[column] = width;
+
+ return width;
+ };
this.$renderLineCore = function(stringBuilder, lastRow, tokens, splits, onlyContents) {
var chars = 0;
diff --git a/lib/ace/theme/comic.js b/lib/ace/theme/comic.js
new file mode 100644
index 00000000..1ab63b74
--- /dev/null
+++ b/lib/ace/theme/comic.js
@@ -0,0 +1,264 @@
+/* ***** 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):
+ * Michael Schwartz
+ *
+ * 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) {
+
+exports.cssText = ".ace-vibrant-ink .ace_editor {\
+ border: 2px solid rgb(159, 159, 159);\
+}\
+\
+.ace-vibrant-ink .ace_editor.ace_focus {\
+ border: 2px solid #327fbd;\
+}\
+\
+.ace-vibrant-ink .ace_text-layer {\
+ font-family: 'Comic Sans MS';\
+}\
+\
+.ace-vibrant-ink .ace_gutter {\
+ width: 50px;\
+ background: #e8e8e8;\
+ color: #333;\
+ overflow : hidden;\
+}\
+\
+.ace-vibrant-ink .ace_gutter-layer {\
+ width: 100%;\
+ text-align: right;\
+}\
+\
+.ace-vibrant-ink .ace_gutter-layer .ace_gutter-cell {\
+ padding-right: 6px;\
+}\
+\
+.ace-vibrant-ink .ace_print_margin {\
+ width: 1px;\
+ background: #e8e8e8;\
+}\
+\
+.ace-vibrant-ink .ace_scroller {\
+ background-color: #0F0F0F;\
+}\
+\
+.ace-vibrant-ink .ace_text-layer {\
+ cursor: text;\
+ color: #FFFFFF;\
+}\
+\
+.ace-vibrant-ink .ace_cursor {\
+ border-left: 2px solid #FFFFFF;\
+}\
+\
+.ace-vibrant-ink .ace_cursor.ace_overwrite {\
+ border-left: 0px;\
+ border-bottom: 1px solid #FFFFFF;\
+}\
+ \
+.ace-vibrant-ink .ace_marker-layer .ace_selection {\
+ background: #6699CC;\
+}\
+\
+.ace-vibrant-ink .ace_marker-layer .ace_step {\
+ background: rgb(198, 219, 174);\
+}\
+\
+.ace-vibrant-ink .ace_marker-layer .ace_bracket {\
+ margin: -1px 0 0 -1px;\
+ border: 1px solid #99CC99;\
+}\
+\
+.ace-vibrant-ink .ace_marker-layer .ace_active_line {\
+ background: #333333;\
+}\
+\
+ \
+.ace-vibrant-ink .ace_invisible {\
+ color: #404040;\
+}\
+\
+.ace-vibrant-ink .ace_keyword {\
+ color:#FF6600;\
+ font-weight: bold;\
+}\
+\
+.ace-vibrant-ink .ace_keyword.ace_operator {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_constant {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_constant.ace_language {\
+ color:#339999;\
+}\
+\
+.ace-vibrant-ink .ace_constant.ace_library {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_constant.ace_numeric {\
+ color:#99CC99;\
+}\
+\
+.ace-vibrant-ink .ace_invalid {\
+ color:#CCFF33;\
+ background-color:#000000;\
+}\
+\
+.ace-vibrant-ink .ace_invalid.ace_illegal {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_invalid.ace_deprecated {\
+ color:#CCFF33;\
+ background-color:#000000;\
+}\
+\
+.ace-vibrant-ink .ace_support {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_support.ace_function {\
+ color:#FFCC00;\
+}\
+\
+.ace-vibrant-ink .ace_function.ace_buildin {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_string {\
+ color:#66FF00;\
+}\
+\
+.ace-vibrant-ink .ace_string.ace_regexp {\
+ color:#44B4CC;\
+}\
+\
+.ace-vibrant-ink .ace_comment {\
+ color:#9933CC;\
+ font-style: italic;\
+}\
+\
+.ace-vibrant-ink .ace_comment.ace_doc {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_comment.ace_doc.ace_tag {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_variable {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_variable.ace_language {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_xml_pe {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_meta {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_meta.ace_tag {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_meta.ace_tag.ace_input {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_entity.ace_other.ace_attribute-name {\
+ font-style:italic;\
+color:#99CC99;\
+}\
+\
+.ace-vibrant-ink .ace_entity.ace_name {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_entity.ace_name.ace_function {\
+ color:#FFCC00;\
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_underline {\
+ text-decoration:underline;\
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading.ace_1 {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading.ace_2 {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading.ace_3 {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading.ace_4 {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading.ace_5 {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_heading.ace_6 {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_markup.ace_list {\
+ \
+}\
+\
+.ace-vibrant-ink .ace_collab.ace_user1 {\
+ \
+}";
+
+exports.cssClass = "ace-vibrant-ink";
+
+});
diff --git a/lib/ace/theme/textmate.js b/lib/ace/theme/textmate.js
index 845b1041..329ba6df 100644
--- a/lib/ace/theme/textmate.js
+++ b/lib/ace/theme/textmate.js
@@ -79,6 +79,7 @@ exports.cssText = ".ace-tm .ace_editor {\
\
.ace-tm .ace_line .ace_keyword {\
color: blue;\
+ font-weight:bold;\
}\
\
.ace-tm .ace_line .ace_constant.ace_buildin {\
diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js
index 536096b1..d82c3551 100644
--- a/lib/ace/virtual_renderer.js
+++ b/lib/ace/virtual_renderer.js
@@ -136,6 +136,7 @@ var VirtualRenderer = function(container, theme) {
lastRow : 0,
lineHeight : 1,
characterWidth : 1,
+ textWidth: function() { return 1; },
minHeight : 1,
maxHeight : 1,
offset : 0,
@@ -523,6 +524,7 @@ var VirtualRenderer = function(container, theme) {
lastRow : lastRow,
lineHeight : this.lineHeight,
characterWidth : this.characterWidth,
+ textWidth: this.$textLayer.textWidth.bind(this.$textLayer),
minHeight : minHeight,
maxHeight : maxHeight,
offset : offset,
@@ -720,14 +722,48 @@ var VirtualRenderer = function(container, theme) {
// todo: handle horizontal scrolling
};
+ this.$findColumn = function(row, width) {
+ // binary search to find the screen column
+ var min = 0;
+ var max = this.session.getLine(row).length;
+
+ while (true) {
+ if (max <= 0)
+ return null;
+
+ // if the range has length one pick the closes character
+ if (max-min == 1) {
+ var wMin = this.$textLayer.textWidth(row, min);
+ var wMax = this.$textLayer.textWidth(row, max);
+
+ if (Math.abs(wMin-width) < Math.abs(wMax-width))
+ return min;
+ else
+ return max;
+ }
+ else {
+ // same as Math.floor((max-min)/2) but faster
+ var pivot = min + ((max - min) >> 1);
+ var w = this.$textLayer.textWidth(row, pivot);
+ if (w == width)
+ return pivot;
+ else if (w > width)
+ max = pivot;
+ else
+ min = pivot;
+ }
+ }
+ };
+
this.screenToTextCoordinates = function(pageX, pageY) {
var canvasPos = this.scroller.getBoundingClientRect();
- var col = Math.round((pageX + this.scroller.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft())
- / this.characterWidth);
var row = Math.floor((pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop())
/ this.lineHeight);
+ var width = pageX + this.scroller.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft();
+ var col = this.$findColumn(row, width);
+
return this.session.screenToDocumentPosition(row, Math.max(col, 0));
};