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;
|
||||
}
|
||||
|
||||
.line .invisible {
|
||||
color: rgb(191, 191, 191);
|
||||
}
|
||||
|
||||
.line .keyword {
|
||||
color: blue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -338,6 +338,20 @@ ace.Editor = function(renderer, doc) {
|
|||
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() {
|
||||
if (this.selection.isEmpty()) {
|
||||
this.selection.selectRight();
|
||||
|
|
|
|||
|
|
@ -44,13 +44,19 @@ ace.VirtualRenderer = function(container) {
|
|||
this.lines = doc.lines;
|
||||
this.doc = doc;
|
||||
this.markerLayer.setDocument(doc);
|
||||
this.textLayer.setTabSize(doc.getTabSize());
|
||||
this.textLayer.setDocument(doc);
|
||||
};
|
||||
|
||||
this.setTokenizer = function(tokenizer) {
|
||||
this.textLayer.setTokenizer(tokenizer);
|
||||
};
|
||||
|
||||
this.$showInvisibles = true;
|
||||
this.setShowInvisibles = function(showInvisibles) {
|
||||
this.$showInvisibles = showInvisibles;
|
||||
this.textLayer.setShowInvisibles(showInvisibles);
|
||||
};
|
||||
|
||||
this.getContainerElement = function() {
|
||||
return this.container;
|
||||
};
|
||||
|
|
@ -117,7 +123,10 @@ ace.VirtualRenderer = function(container) {
|
|||
var offset = this.scrollTop % this.lineHeight;
|
||||
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 firstRow = Math.round((this.scrollTop - offset) / this.lineHeight);
|
||||
|
|
@ -150,6 +159,8 @@ ace.VirtualRenderer = function(container) {
|
|||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
@ -158,10 +169,63 @@ ace.VirtualRenderer = function(container) {
|
|||
};
|
||||
|
||||
this.updateCursor = function(position) {
|
||||
this.cursorLayer.setCursor(position);
|
||||
this.cursorLayer.setCursor(this.$documentToScreenPosition(position));
|
||||
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.cursorLayer.hideCursor();
|
||||
};
|
||||
|
|
@ -230,7 +294,7 @@ ace.VirtualRenderer = function(container) {
|
|||
|
||||
return {
|
||||
row : row,
|
||||
column : col
|
||||
column : this.$screenToDocumentColumn(row, col)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ ace.layer.Text = function(parentEl) {
|
|||
parentEl.appendChild(this.element);
|
||||
|
||||
this.$measureSizes();
|
||||
this.$tabString = " ";
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
// this.ENTER_CHAR = "¶";
|
||||
this.ENTER_CHAR = "¬";
|
||||
this.TAB_CHAR = "‣";
|
||||
|
||||
this.setTokenizer = function(tokenizer) {
|
||||
this.tokenizer = tokenizer;
|
||||
};
|
||||
|
|
@ -44,11 +47,27 @@ ace.layer.Text = function(parentEl) {
|
|||
this.element.removeChild(measureNode);
|
||||
};
|
||||
|
||||
this.setTabSize = function(tabSize) {
|
||||
this.$tabString = new Array(tabSize+1).join(" ");
|
||||
this.setDocument = function(doc) {
|
||||
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.$computeTabString();
|
||||
|
||||
var first = Math.max(firstRow, layerConfig.firstRow);
|
||||
var last = Math.min(lastRow, layerConfig.lastRow);
|
||||
|
||||
|
|
@ -64,6 +83,8 @@ ace.layer.Text = function(parentEl) {
|
|||
};
|
||||
|
||||
this.update = function(config) {
|
||||
this.$computeTabString();
|
||||
|
||||
var html = [];
|
||||
for ( var i = config.firstRow; i <= config.lastRow; i++) {
|
||||
html.push("<div class='line' style='height:" + this.lineHeight + "px;", "width:",
|
||||
|
|
@ -82,20 +103,20 @@ ace.layer.Text = function(parentEl) {
|
|||
var output = token.value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
//.replace(/\t/g, "‣ ")
|
||||
.replace(/\t/g, this.$tabString)
|
||||
.replace(/\s/g, " ");
|
||||
.replace(/ /g, " ")
|
||||
.replace(/\t/g, this.$tabString);
|
||||
// TODO: proper space matching!
|
||||
|
||||
if (token.type !== "text") {
|
||||
stringBuilder.push("<span class='", token.type, "'>", output,
|
||||
"</span>");
|
||||
stringBuilder.push("<span class='", token.type, "'>", output, "</span>");
|
||||
}
|
||||
else {
|
||||
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);
|
||||
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