proper display of tabs and add option to show invisible characters
This commit is contained in:
parent
cd27cb7554
commit
61f0cb63d9
5 changed files with 164 additions and 14 deletions
|
|
@ -31,6 +31,10 @@
|
||||||
background: black;
|
background: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.line .invisible {
|
||||||
|
color: rgb(191, 191, 191);
|
||||||
|
}
|
||||||
|
|
||||||
.line .keyword {
|
.line .keyword {
|
||||||
color: blue;
|
color: blue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,20 @@ ace.Editor = function(renderer, doc) {
|
||||||
return this.$highlightActiveLine;
|
return this.$highlightActiveLine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.$showInvisibles = true;
|
||||||
|
this.setShowInvisibles = function(showInvisibles) {
|
||||||
|
showInvisibles = !!showInvisibles;
|
||||||
|
if (this.$showInvisibles == showInvisibles) return;
|
||||||
|
|
||||||
|
this.$showInvisibles = showInvisibles;
|
||||||
|
this.renderer.setShowInvisibles(showInvisibles);
|
||||||
|
this.renderer.draw();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getShowInvisibles = function() {
|
||||||
|
return this.showInvisibles;
|
||||||
|
};
|
||||||
|
|
||||||
this.removeRight = function() {
|
this.removeRight = function() {
|
||||||
if (this.selection.isEmpty()) {
|
if (this.selection.isEmpty()) {
|
||||||
this.selection.selectRight();
|
this.selection.selectRight();
|
||||||
|
|
|
||||||
|
|
@ -44,13 +44,19 @@ ace.VirtualRenderer = function(container) {
|
||||||
this.lines = doc.lines;
|
this.lines = doc.lines;
|
||||||
this.doc = doc;
|
this.doc = doc;
|
||||||
this.markerLayer.setDocument(doc);
|
this.markerLayer.setDocument(doc);
|
||||||
this.textLayer.setTabSize(doc.getTabSize());
|
this.textLayer.setDocument(doc);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setTokenizer = function(tokenizer) {
|
this.setTokenizer = function(tokenizer) {
|
||||||
this.textLayer.setTokenizer(tokenizer);
|
this.textLayer.setTokenizer(tokenizer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.$showInvisibles = true;
|
||||||
|
this.setShowInvisibles = function(showInvisibles) {
|
||||||
|
this.$showInvisibles = showInvisibles;
|
||||||
|
this.textLayer.setShowInvisibles(showInvisibles);
|
||||||
|
};
|
||||||
|
|
||||||
this.getContainerElement = function() {
|
this.getContainerElement = function() {
|
||||||
return this.container;
|
return this.container;
|
||||||
};
|
};
|
||||||
|
|
@ -117,7 +123,10 @@ ace.VirtualRenderer = function(container) {
|
||||||
var offset = this.scrollTop % this.lineHeight;
|
var offset = this.scrollTop % this.lineHeight;
|
||||||
var minHeight = this.scroller.clientHeight + offset;
|
var minHeight = this.scroller.clientHeight + offset;
|
||||||
|
|
||||||
var longestLine = Math.max(this.scroller.clientWidth, Math.round(this.doc.getWidth() * this.characterWidth));
|
var charCount = this.doc.getWidth();
|
||||||
|
if (this.$showInvisibles)
|
||||||
|
charCount += 1;
|
||||||
|
var longestLine = Math.max(this.scroller.clientWidth, Math.round(charCount * this.characterWidth));
|
||||||
|
|
||||||
var lineCount = Math.ceil(minHeight / this.lineHeight);
|
var lineCount = Math.ceil(minHeight / this.lineHeight);
|
||||||
var firstRow = Math.round((this.scrollTop - offset) / this.lineHeight);
|
var firstRow = Math.round((this.scrollTop - offset) / this.lineHeight);
|
||||||
|
|
@ -150,6 +159,8 @@ ace.VirtualRenderer = function(container) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addMarker = function(range, clazz, type) {
|
this.addMarker = function(range, clazz, type) {
|
||||||
|
range.start = this.$documentToScreenPosition(range.start);
|
||||||
|
range.end = this.$documentToScreenPosition(range.end);
|
||||||
return this.markerLayer.addMarker(range, clazz, type);
|
return this.markerLayer.addMarker(range, clazz, type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -158,10 +169,63 @@ ace.VirtualRenderer = function(container) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateCursor = function(position) {
|
this.updateCursor = function(position) {
|
||||||
this.cursorLayer.setCursor(position);
|
this.cursorLayer.setCursor(this.$documentToScreenPosition(position));
|
||||||
this.cursorLayer.update(this.layerConfig);
|
this.cursorLayer.update(this.layerConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.$documentToScreenPosition = function(pos) {
|
||||||
|
return {
|
||||||
|
row: pos.row,
|
||||||
|
column: this.$documentToScreenColumn(pos.row, pos.column)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$documentToScreenColumn = function(row, docColumn) {
|
||||||
|
var tabSize = this.doc.getTabSize();
|
||||||
|
|
||||||
|
var screenColumn = 0;
|
||||||
|
var remaining = docColumn;
|
||||||
|
|
||||||
|
var line = this.doc.getLine(row).split(/\t/g);
|
||||||
|
for (var i=0; i<line.length; i++) {
|
||||||
|
var len = line[i].length;
|
||||||
|
if (remaining > len) {
|
||||||
|
remaining -= (len + 1);
|
||||||
|
screenColumn += len + tabSize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
screenColumn += remaining;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return screenColumn;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$screenToDocumentColumn = function(row, screenColumn) {
|
||||||
|
var tabSize = this.doc.getTabSize();
|
||||||
|
|
||||||
|
var docColumn = 0;
|
||||||
|
var remaining = screenColumn;
|
||||||
|
|
||||||
|
var line = this.doc.getLine(row).split(/\t/g);
|
||||||
|
for (var i=0; i<line.length; i++) {
|
||||||
|
var len = line[i].length;
|
||||||
|
if (remaining >= len + tabSize) {
|
||||||
|
remaining -= (len + tabSize);
|
||||||
|
docColumn += (len + 1);
|
||||||
|
}
|
||||||
|
else if (remaining > len){
|
||||||
|
docColumn += len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
docColumn += remaining;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return docColumn;
|
||||||
|
};
|
||||||
|
|
||||||
this.hideCursor = function() {
|
this.hideCursor = function() {
|
||||||
this.cursorLayer.hideCursor();
|
this.cursorLayer.hideCursor();
|
||||||
};
|
};
|
||||||
|
|
@ -230,7 +294,7 @@ ace.VirtualRenderer = function(container) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
row : row,
|
row : row,
|
||||||
column : col
|
column : this.$screenToDocumentColumn(row, col)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,14 @@ ace.layer.Text = function(parentEl) {
|
||||||
parentEl.appendChild(this.element);
|
parentEl.appendChild(this.element);
|
||||||
|
|
||||||
this.$measureSizes();
|
this.$measureSizes();
|
||||||
this.$tabString = " ";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
// this.ENTER_CHAR = "¶";
|
||||||
|
this.ENTER_CHAR = "¬";
|
||||||
|
this.TAB_CHAR = "‣";
|
||||||
|
|
||||||
this.setTokenizer = function(tokenizer) {
|
this.setTokenizer = function(tokenizer) {
|
||||||
this.tokenizer = tokenizer;
|
this.tokenizer = tokenizer;
|
||||||
};
|
};
|
||||||
|
|
@ -44,11 +47,27 @@ ace.layer.Text = function(parentEl) {
|
||||||
this.element.removeChild(measureNode);
|
this.element.removeChild(measureNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setTabSize = function(tabSize) {
|
this.setDocument = function(doc) {
|
||||||
this.$tabString = new Array(tabSize+1).join(" ");
|
this.doc = doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$showInvisibles = true;
|
||||||
|
this.setShowInvisibles = function(showInvisibles) {
|
||||||
|
this.$showInvisibles = showInvisibles;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$computeTabString = function() {
|
||||||
|
var tabSize = this.doc.getTabSize();
|
||||||
|
if (this.$showInvisibles) {
|
||||||
|
this.$tabString = "<span class='invisible'>" + this.TAB_CHAR + new Array(tabSize).join(" ") + "</span>";
|
||||||
|
} else {
|
||||||
|
this.$tabString = new Array(tabSize+1).join(" ");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateLines = function(layerConfig, firstRow, lastRow) {
|
this.updateLines = function(layerConfig, firstRow, lastRow) {
|
||||||
|
this.$computeTabString();
|
||||||
|
|
||||||
var first = Math.max(firstRow, layerConfig.firstRow);
|
var first = Math.max(firstRow, layerConfig.firstRow);
|
||||||
var last = Math.min(lastRow, layerConfig.lastRow);
|
var last = Math.min(lastRow, layerConfig.lastRow);
|
||||||
|
|
||||||
|
|
@ -64,6 +83,8 @@ ace.layer.Text = function(parentEl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update = function(config) {
|
this.update = function(config) {
|
||||||
|
this.$computeTabString();
|
||||||
|
|
||||||
var html = [];
|
var html = [];
|
||||||
for ( var i = config.firstRow; i <= config.lastRow; i++) {
|
for ( var i = config.firstRow; i <= config.lastRow; i++) {
|
||||||
html.push("<div class='line' style='height:" + this.lineHeight + "px;", "width:",
|
html.push("<div class='line' style='height:" + this.lineHeight + "px;", "width:",
|
||||||
|
|
@ -82,20 +103,20 @@ ace.layer.Text = function(parentEl) {
|
||||||
var output = token.value
|
var output = token.value
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
.replace(/</g, "<")
|
.replace(/</g, "<")
|
||||||
//.replace(/\t/g, "‣ ")
|
.replace(/ /g, " ")
|
||||||
.replace(/\t/g, this.$tabString)
|
.replace(/\t/g, this.$tabString);
|
||||||
.replace(/\s/g, " ");
|
// TODO: proper space matching!
|
||||||
|
|
||||||
if (token.type !== "text") {
|
if (token.type !== "text") {
|
||||||
stringBuilder.push("<span class='", token.type, "'>", output,
|
stringBuilder.push("<span class='", token.type, "'>", output, "</span>");
|
||||||
"</span>");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
stringBuilder.push(output);
|
stringBuilder.push(output);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// TODO: show invisibles
|
|
||||||
//stringBuilder.push("¶");
|
if (this.$showInvisibles)
|
||||||
|
stringBuilder.push("<span class='invisible'>" + this.ENTER_CHAR + "</span>");
|
||||||
};
|
};
|
||||||
|
|
||||||
}).call(ace.layer.Text.prototype);
|
}).call(ace.layer.Text.prototype);
|
||||||
47
test/VirtualRendererTest.js
Normal file
47
test/VirtualRendererTest.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
var VirtualRendererTest = new TestCase("VirtualRendererTest", {
|
||||||
|
|
||||||
|
"test: convert document to screen coordinates" : function() {
|
||||||
|
var el = document.createElement("div");
|
||||||
|
var renderer = new ace.VirtualRenderer(el);
|
||||||
|
|
||||||
|
var doc = new ace.Document("01234\t567890\t1234");
|
||||||
|
doc.setTabSize(4);
|
||||||
|
renderer.setDocument(doc);
|
||||||
|
|
||||||
|
assertEquals(0, renderer.$documentToScreenColumn(0, 0));
|
||||||
|
assertEquals(4, renderer.$documentToScreenColumn(0, 4));
|
||||||
|
assertEquals(5, renderer.$documentToScreenColumn(0, 5));
|
||||||
|
assertEquals(9, renderer.$documentToScreenColumn(0, 6));
|
||||||
|
assertEquals(15, renderer.$documentToScreenColumn(0, 12));
|
||||||
|
assertEquals(19, renderer.$documentToScreenColumn(0, 13));
|
||||||
|
|
||||||
|
doc.setTabSize(2);
|
||||||
|
|
||||||
|
assertEquals(0, renderer.$documentToScreenColumn(0, 0));
|
||||||
|
assertEquals(4, renderer.$documentToScreenColumn(0, 4));
|
||||||
|
assertEquals(5, renderer.$documentToScreenColumn(0, 5));
|
||||||
|
assertEquals(7, renderer.$documentToScreenColumn(0, 6));
|
||||||
|
assertEquals(13, renderer.$documentToScreenColumn(0, 12));
|
||||||
|
assertEquals(15, renderer.$documentToScreenColumn(0, 13));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: convert screen to document coordinates" : function() {
|
||||||
|
var el = document.createElement("div");
|
||||||
|
var renderer = new ace.VirtualRenderer(el);
|
||||||
|
|
||||||
|
var doc = new ace.Document("01234\t567890\t1234");
|
||||||
|
doc.setTabSize(4);
|
||||||
|
renderer.setDocument(doc);
|
||||||
|
|
||||||
|
assertEquals(0, renderer.$screenToDocumentColumn(0, 0));
|
||||||
|
assertEquals(4, renderer.$screenToDocumentColumn(0, 4));
|
||||||
|
assertEquals(5, renderer.$screenToDocumentColumn(0, 5));
|
||||||
|
assertEquals(5, renderer.$screenToDocumentColumn(0, 6));
|
||||||
|
assertEquals(5, renderer.$screenToDocumentColumn(0, 7));
|
||||||
|
assertEquals(5, renderer.$screenToDocumentColumn(0, 8));
|
||||||
|
assertEquals(6, renderer.$screenToDocumentColumn(0, 9));
|
||||||
|
assertEquals(12, renderer.$screenToDocumentColumn(0, 15));
|
||||||
|
assertEquals(13, renderer.$screenToDocumentColumn(0, 19));
|
||||||
|
}
|
||||||
|
// change tab size after setDocument (for text layer)
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue