diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js
index 5a375412..7a2f2e76 100644
--- a/demo/kitchen-sink/demo.js
+++ b/demo/kitchen-sink/demo.js
@@ -286,7 +286,7 @@ env.editor.commands.addCommands([{
name: "gotoline",
bindKey: {win: "Ctrl-L", mac: "Command-L"},
exec: function(editor, line) {
- if (typeof needle == "object") {
+ if (typeof line == "object") {
var arg = this.name + " " + editor.getCursorPosition().row;
editor.cmdLine.setValue(arg, 1)
editor.cmdLine.focus()
@@ -328,7 +328,7 @@ cmdLine.commands.bindKeys({
},
})
-cmdLine.commands.removeCommands(["find", "goToLine", "findAll", "replace", "replaceAll"])
+cmdLine.commands.removeCommands(["find", "gotoline", "findall", "replace", "replaceall"])
/**
* This demonstrates how you can define commands and bind shortcuts to them.
@@ -557,6 +557,10 @@ bindCheckbox("show_hidden", function(checked) {
env.editor.setShowInvisibles(checked);
});
+bindCheckbox("display_indent_guides", function(checked) {
+ env.editor.setDisplayIndentGuides(checked);
+});
+
bindCheckbox("show_gutter", function(checked) {
env.editor.renderer.setShowGutter(checked);
});
diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index 5b387f98..476a08c6 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -165,7 +165,7 @@ var Editor = function(renderer, session) {
this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this);
session.addEventListener("tokenizerUpdate", this.$onTokenizerUpdate);
- this.$onChangeTabSize = this.renderer.updateText.bind(this.renderer);
+ this.$onChangeTabSize = this.renderer.onChangeTabSize.bind(this.renderer);
session.addEventListener("changeTabSize", this.$onChangeTabSize);
this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this);
@@ -218,6 +218,7 @@ var Editor = function(renderer, session) {
this.onChangeBreakpoint();
this.onChangeAnnotation();
this.session.getUseWrapMode() && this.renderer.adjustWrapLimit();
+ this.renderer.onChangeTabSize();
this.renderer.updateFull();
this._emit("changeSession", {
@@ -988,9 +989,6 @@ var Editor = function(renderer, session) {
* If `showInvisibiles` is set to `true`, invisible characters—like spaces or new lines—are show in the editor.
**/
this.setShowInvisibles = function(showInvisibles) {
- if (this.getShowInvisibles() == showInvisibles)
- return;
-
this.renderer.setShowInvisibles(showInvisibles);
};
@@ -1003,6 +1001,14 @@ var Editor = function(renderer, session) {
return this.renderer.getShowInvisibles();
};
+ this.setDisplayIndentGuides = function(display) {
+ this.renderer.setDisplayIndentGuides(display);
+ };
+
+ this.getDisplayIndentGuides = function() {
+ return this.renderer.getDisplayIndentGuides();
+ };
+
/**
* Editor.setShowPrintMargin(showPrintMargin)
* - showPrintMargin (Boolean): Specifies whether or not to show the print margin
diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js
index e96dff28..6c0f736a 100644
--- a/lib/ace/layer/text.js
+++ b/lib/ace/layer/text.js
@@ -204,28 +204,52 @@ var Text = function(parentEl) {
return false;
this.showInvisibles = showInvisibles;
+ this.$computeTabString();
+ return true;
+ };
+
+ this.displayIndentGuides = true;
+ this.setDisplayIndentGuides = function(display) {
+ if (this.displayIndentGuides == display)
+ return false;
+
+ this.displayIndentGuides = display;
+ this.$computeTabString();
return true;
};
this.$tabStrings = [];
+ this.onChangeTabSize =
this.$computeTabString = function() {
var tabSize = this.session.getTabSize();
+ this.tabSize = tabSize;
var tabStr = this.$tabStrings = [0];
for (var i = 1; i < tabSize + 1; i++) {
if (this.showInvisibles) {
tabStr.push(""
+ this.TAB_CHAR
- + new Array(i).join(" ")
+ + Array(i).join(" ")
+ "");
} else {
tabStr.push(new Array(i+1).join(" "));
}
}
+ if (this.displayIndentGuides) {
+ this.$indentGuideRe = /\s\S| \t|\t |\s$/;
+ var className = "ace_indent-guide";
+ var content = Array(this.tabSize + 1).join(" ");
+ var tabContent = content;
+ if (this.showInvisibles) {
+ className += " ace_invisible";
+ tabContent = this.TAB_CHAR + content.substr(6);
+ }
+ this.$tabStrings[" "] = "" + content + "";
+ this.$tabStrings["\t"] = "" + tabContent + "";
+ }
};
this.updateLines = function(config, firstRow, lastRow) {
- this.$computeTabString();
// Due to wrap line changes there can be new lines if e.g.
// the line to updated wrapped in the meantime.
if (this.config.lastRow != config.lastRow ||
@@ -253,22 +277,32 @@ var Text = function(parentEl) {
lineElementsIdx ++;
}
- for (var i=first; i<=last; i++) {
+ var row = first;
+ var foldLine = this.session.getNextFoldLine(row);
+ var foldStart = foldLine ? foldLine.start.row : Infinity;
+
+ while (true) {
+ if (row > foldStart) {
+ row = foldLine.end.row+1;
+ foldLine = this.session.getNextFoldLine(row, foldLine);
+ foldStart = foldLine ? foldLine.start.row :Infinity;
+ }
+ if (row > last)
+ break;
+
var lineElement = lineElements[lineElementsIdx++];
- if (!lineElement)
- continue;
-
- var html = [];
- var tokens = this.session.getTokens(i);
- this.$renderLine(html, i, tokens, !this.$useLineGroups());
- lineElement = dom.setInnerHtml(lineElement, html.join(""));
-
- i = this.session.getRowFoldEnd(i);
+ if (lineElement) {
+ var html = [];
+ this.$renderLine(
+ html, row, !this.$useLineGroups(), row == foldStart ? foldLine : false
+ );
+ dom.setInnerHtml(lineElement, html.join(""));
+ }
+ row++;
}
};
this.scrollLines = function(config) {
- this.$computeTabString();
var oldConfig = this.config;
this.config = config;
@@ -321,8 +355,7 @@ var Text = function(parentEl) {
var html = [];
// Get the tokens per line as there might be some lines in between
// beeing folded.
- var tokens = this.session.getTokens(row);
- this.$renderLine(html, row, tokens, false);
+ this.$renderLine(html, row, false, row == foldStart ? foldLine : false);
// don't use setInnerHtml since we are working with an empty DIV
container.innerHTML = html.join("");
@@ -341,7 +374,6 @@ var Text = function(parentEl) {
};
this.update = function(config) {
- this.$computeTabString();
this.config = config;
var html = [];
@@ -363,10 +395,7 @@ var Text = function(parentEl) {
if (this.$useLineGroups())
html.push("
")
- // Get the tokens per line as there might be some lines in between
- // beeing folded.
- var tokens = this.session.getTokens(row);
- this.$renderLine(html, row, tokens, false);
+ this.$renderLine(html, row, false, row == foldStart ? foldLine : false);
if (this.$useLineGroups())
html.push("
"); // end the line group
@@ -429,38 +458,44 @@ var Text = function(parentEl) {
return screenColumn + value.length;
};
- this.$renderLineCore = function(stringBuilder, lastRow, tokens, splits, onlyContents) {
+ this.renderIndentGuide = function(stringBuilder, value) {
+ var cols = value.search(this.$indentGuideRe);
+ if (cols <= 0)
+ return value;
+ if (value[0] == " ") {
+ cols -= cols % this.tabSize;
+ stringBuilder.push(Array(cols/this.tabSize + 1).join(this.$tabStrings[" "]));
+ return value.substr(cols);
+ } else if (value[0] == "\t") {
+ stringBuilder.push(Array(cols + 1).join(this.$tabStrings["\t"]));
+ return value.substr(cols);
+ }
+ return value;
+ };
+
+ this.$renderWrappedLine = function(stringBuilder, tokens, splits, onlyContents) {
var chars = 0;
var split = 0;
- var splitChars;
+ var splitChars = splits[0];
var screenColumn = 0;
- var self = this;
-
- if (!splits || splits.length == 0)
- splitChars = Number.MAX_VALUE;
- else
- splitChars = splits[0];
-
- if (!onlyContents) {
- stringBuilder.push(""
- );
- }
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
var value = token.value;
+ if (i == 0 && this.displayIndentGuides) {
+ chars = value.length;
+ value = this.renderIndentGuide(stringBuilder, value);
+ if (!value)
+ continue;
+ chars -= value.length;
+ }
if (chars + value.length < splitChars) {
- screenColumn = self.$renderToken(
- stringBuilder, screenColumn, token, value
- );
+ screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value);
chars += value.length;
- }
- else {
+ } else {
while (chars + value.length >= splitChars) {
- screenColumn = self.$renderToken(
+ screenColumn = this.$renderToken(
stringBuilder, screenColumn,
token, value.substring(0, splitChars - chars)
);
@@ -470,8 +505,7 @@ var Text = function(parentEl) {
if (!onlyContents) {
stringBuilder.push("
",
""
+ this.config.lineHeight, "px'>"
);
}
@@ -481,37 +515,70 @@ var Text = function(parentEl) {
}
if (value.length != 0) {
chars += value.length;
- screenColumn = self.$renderToken(
+ screenColumn = this.$renderToken(
stringBuilder, screenColumn, token, value
);
}
}
}
+ };
+
+ this.$renderSimpleLine = function(stringBuilder, tokens) {
+ var screenColumn = 0;
+ var token = tokens[0];
+ var value = token.value;
+ if (this.displayIndentGuides)
+ value = this.renderIndentGuide(stringBuilder, value);
+ if (value)
+ screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value);
+ for (var i = 1; i < tokens.length; i++) {
+ token = tokens[i];
+ value = token.value;
+ screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value);
+ }
+ };
+
+ // row is either first row of foldline or not in fold
+ this.$renderLine = function(stringBuilder, row, onlyContents, foldLine) {
+ if (!foldLine && foldLine != false)
+ foldLine = this.session.getFoldLine(row);
+
+ if (foldLine)
+ var tokens = this.$getFoldLineTokens(row, foldLine);
+ else
+ var tokens = this.session.getTokens(row);
+
+
+ if (!onlyContents) {
+ stringBuilder.push(
+ "
"
+ );
+ }
+
+ if (tokens.length) {
+ var splits = this.session.getRowSplitData(row);
+ if (splits && splits.length)
+ this.$renderWrappedLine(stringBuilder, tokens, splits, onlyContents);
+ else
+ this.$renderSimpleLine(stringBuilder, tokens);
+ }
if (this.showInvisibles) {
- if (lastRow !== this.session.getLength() - 1)
- stringBuilder.push("" + this.EOL_CHAR + "");
- else
- stringBuilder.push("" + this.EOF_CHAR + "");
+ if (foldLine)
+ row = foldLine.end.row
+
+ stringBuilder.push(
+ "",
+ row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR,
+ ""
+ );
}
if (!onlyContents)
stringBuilder.push("
");
};
- this.$renderLine = function(stringBuilder, row, tokens, onlyContents) {
- // Check if the line to render is folded or not. If not, things are
- // simple, otherwise, we need to fake some things...
- if (!this.session.isRowFolded(row)) {
- var splits = this.session.getRowSplitData(row);
- this.$renderLineCore(stringBuilder, row, tokens, splits, onlyContents);
- } else {
- this.$renderFoldLine(stringBuilder, row, tokens, onlyContents);
- }
- };
-
- this.$renderFoldLine = function(stringBuilder, row, tokens, onlyContents) {
+ this.$getFoldLineTokens = function(row, foldLine) {
var session = this.session;
- var foldLine = session.getFoldLine(row);
var renderTokens = [];
function addTokens(tokens, from, to) {
@@ -552,6 +619,7 @@ var Text = function(parentEl) {
}
}
+ var tokens = session.getTokens(row);
foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) {
if (placeholder) {
renderTokens.push({
@@ -567,9 +635,7 @@ var Text = function(parentEl) {
}
}, foldLine.end.row, this.session.getLine(foldLine.end.row).length);
- // splits for foldline are stored at its' first row
- var splits = this.session.$useWrapMode ? this.session.$wrapData[row] : null;
- this.$renderLineCore(stringBuilder, row, renderTokens, splits, onlyContents);
+ return renderTokens;
};
this.$useLineGroups = function() {
diff --git a/lib/ace/test/mockrenderer.js b/lib/ace/test/mockrenderer.js
index 4e5c515d..2ba4231d 100644
--- a/lib/ace/test/mockrenderer.js
+++ b/lib/ace/test/mockrenderer.js
@@ -154,6 +154,9 @@ MockRenderer.prototype.getScrollTopRow = function() {
MockRenderer.prototype.draw = function() {
};
+MockRenderer.prototype.onChangeTabSize = function(startRow, endRow) {
+};
+
MockRenderer.prototype.updateLines = function(startRow, endRow) {
};
diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js
index d9a0c241..bba5ddf7 100644
--- a/lib/ace/virtual_renderer.js
+++ b/lib/ace/virtual_renderer.js
@@ -104,7 +104,6 @@ var VirtualRenderer = function(container, theme) {
this.$gutterLayer = new GutterLayer(this.$gutter);
this.$gutterLayer.on("changeGutterWidth", this.onResize.bind(this, true));
- this.setFadeFoldWidgets(true);
this.$markerBack = new MarkerLayer(this.content);
@@ -249,6 +248,11 @@ var VirtualRenderer = function(container, theme) {
this.$loop.schedule(this.CHANGE_LINES);
};
+ this.onChangeTabSize = function() {
+ this.$loop.schedule(this.CHANGE_TEXT | this.CHANGE_MARKER);
+ this.$textLayer.onChangeTabSize();
+ };
+
/**
* VirtualRenderer.updateText() -> Void
*
@@ -388,6 +392,15 @@ var VirtualRenderer = function(container, theme) {
return this.$textLayer.showInvisibles;
};
+ this.getDisplayIndentGuides = function() {
+ return this.$textLayer.displayIndentGuides;
+ };
+
+ this.setDisplayIndentGuides = function(display) {
+ if (this.$textLayer.setDisplayIndentGuides(display))
+ this.$loop.schedule(this.CHANGE_TEXT);
+ };
+
this.$showPrintMargin = true;
/**