indent guides

This commit is contained in:
nightwing 2012-07-15 23:32:20 +04:00
commit f73e38625b
5 changed files with 163 additions and 71 deletions

View file

@ -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);
});

View file

@ -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

View file

@ -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("<span class='ace_invisible'>"
+ this.TAB_CHAR
+ new Array(i).join("&#160;")
+ Array(i).join("&#160;")
+ "</span>");
} else {
tabStr.push(new Array(i+1).join("&#160;"));
}
}
if (this.displayIndentGuides) {
this.$indentGuideRe = /\s\S| \t|\t |\s$/;
var className = "ace_indent-guide";
var content = Array(this.tabSize + 1).join("&#160;");
var tabContent = content;
if (this.showInvisibles) {
className += " ace_invisible";
tabContent = this.TAB_CHAR + content.substr(6);
}
this.$tabStrings[" "] = "<span class='" + className + "' >" + content + "</span>";
this.$tabStrings["\t"] = "<span class='" + className + "' >" + tabContent + "</span>";
}
};
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("<div class='ace_line_group'>")
// 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("</div>"); // 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("<div class='ace_line' style='height:",
this.config.lineHeight, "px",
"'>"
);
}
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("</div>",
"<div class='ace_line' style='height:",
this.config.lineHeight, "px",
"'>"
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(
"<div class='ace_line' style='height:", this.config.lineHeight, "px'>"
);
}
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("<span class='ace_invisible'>" + this.EOL_CHAR + "</span>");
else
stringBuilder.push("<span class='ace_invisible'>" + this.EOF_CHAR + "</span>");
if (foldLine)
row = foldLine.end.row
stringBuilder.push(
"<span class='ace_invisible'>",
row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR,
"</span>"
);
}
if (!onlyContents)
stringBuilder.push("</div>");
};
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() {

View file

@ -154,6 +154,9 @@ MockRenderer.prototype.getScrollTopRow = function() {
MockRenderer.prototype.draw = function() {
};
MockRenderer.prototype.onChangeTabSize = function(startRow, endRow) {
};
MockRenderer.prototype.updateLines = function(startRow, endRow) {
};

View file

@ -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;
/**