Show line numbers on the status bar of view-source in combination with mouse clicks, caret browsing (hit F7), goto line (Ctrl+L), and selection movements. Patch by Christian Schmidt <bugzilla@christian.schmidt.name>, b=15364, r=neil, sr=bz

This commit is contained in:
rbs%maths.uq.edu.au 2003-07-16 07:01:32 +00:00
parent 6b158bac83
commit 2f2a264bdd
5 changed files with 220 additions and 67 deletions

View File

@ -1510,7 +1510,7 @@ function BrowserViewSourceOfURL(url, charset, pageCookie)
// try to open a view-source window while inheriting the charset (if any)
openDialog("chrome://navigator/content/viewSource.xul",
"_blank",
"scrollbars,resizable,chrome,dialog=no",
"all,dialog=no",
url, charset, pageCookie);
}

View File

@ -78,4 +78,7 @@
</vbox>
<statusbar id="status-bar" class="chromeclass-status">
<statusbarpanel id="statusbar-line-col" label="" flex="1"/>
</statusbar>
</window>

View File

@ -22,7 +22,10 @@
*/
const pageLoaderIface = Components.interfaces.nsIWebPageDescriptor;
const nsISelectionPrivate = Components.interfaces.nsISelectionPrivate;
const nsISelectionController = Components.interfaces.nsISelectionController;
var gBrowser = null;
var gViewSourceBundle = null;
var gPrefs = null;
var gLastLineFound = '';
@ -35,6 +38,16 @@ try {
} catch (ex) {
}
var gSelectionListener = {
timeout: 0,
notifySelectionChanged: function(doc, sel, reason)
{
// Coalesce notifications within 100ms intervals.
if (!this.timeout)
this.timeout = setTimeout(updateStatusBar, 100);
}
}
function onLoadViewSource()
{
viewSource(window.arguments[0]);
@ -48,6 +61,22 @@ function getBrowser()
return gBrowser;
}
function getSelectionController()
{
return getBrowser().docShell
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsISelectionDisplay)
.QueryInterface(nsISelectionController);
}
function getViewSourceBundle()
{
if (!gViewSourceBundle)
gViewSourceBundle = document.getElementById("viewSourceBundle");
return gViewSourceBundle;
}
function viewSource(url)
{
if (!url)
@ -145,6 +174,7 @@ function viewSource(url)
}
window._content.focus();
return true;
}
@ -158,6 +188,11 @@ function onLoadContent()
gGoToLine = 0;
}
document.getElementById('cmd_goToLine').removeAttribute('disabled');
// Register a listener so that we can show the caret position on the status bar.
window._content.getSelection()
.QueryInterface(nsISelectionPrivate)
.addSelectionListener(gSelectionListener);
}
function onUnloadContent()
@ -195,7 +230,7 @@ function ViewSourceGoToLine()
{
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var viewSourceBundle = document.getElementById('viewSourceBundle');
var viewSourceBundle = getViewSourceBundle();
var input = {value:gLastLineFound};
for (;;) {
@ -241,46 +276,179 @@ function goToLine(line)
// the first line in the pre element is number 123.
// Do binary search to find the pre element containing the line.
//
var pre, curLine;
var pre;
for (var lbound = 0, ubound = viewsource.childNodes.length; ; ) {
var middle = (lbound + ubound) >> 1;
pre = viewsource.childNodes[middle];
curLine = parseInt(pre.id.substring(4));
var firstLine = parseInt(pre.id.substring(4));
if (lbound == ubound - 1) {
break;
}
if (line >= curLine) {
if (line >= firstLine) {
lbound = middle;
} else {
ubound = middle;
}
}
var range = null;
var result = {};
var found = findLocation(pre, line, null, -1, false, result);
if (!found) {
return false;
}
var selection = window._content.getSelection();
selection.removeAllRanges();
// In our case, the range's startOffset is after "\n" on the previous line.
// Tune the selection at the beginning of the next line and do some tweaking
// to position the focusNode and the caret at the beginning of the line.
selection.QueryInterface(nsISelectionPrivate)
.interlinePosition = true;
selection.addRange(result.range);
if (!selection.isCollapsed) {
selection.collapseToEnd();
var offset = result.range.startOffset;
var node = result.range.startContainer;
if (offset < node.data.length) {
// The same text node spans across the "\n", just focus where we were.
selection.extend(node, offset);
}
else {
// There is another tag just after the "\n", hook there. We need
// to focus a safe point because there are edgy cases such as
// <span>...\n</span><span>...</span> vs.
// <span>...\n<span>...</span></span><span>...</span>
node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
selection.extend(node, 0);
}
}
var selCon = getSelectionController();
selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
selCon.setCaretEnabled(true);
selCon.setCaretVisibilityDuringSelection(true);
// Scroll the beginning of the line into view.
selCon.scrollSelectionIntoView(
nsISelectionController.SELECTION_NORMAL,
nsISelectionController.SELECTION_FOCUS_REGION,
true);
gLastLineFound = line;
document.getElementById("statusbar-line-col").label = getViewSourceBundle()
.getFormattedString("statusBarLineCol", [line, 1]);
return true;
}
function updateStatusBar()
{
// Reset the coalesce flag.
gSelectionListener.timeout = 0;
var statusBarField = document.getElementById("statusbar-line-col");
var selection = window._content.getSelection();
if (!selection.focusNode) {
statusBarField.label = '';
return;
}
if (selection.focusNode.nodeType != Node.TEXT_NODE) {
return;
}
var selCon = getSelectionController();
selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
selCon.setCaretEnabled(true);
selCon.setCaretVisibilityDuringSelection(true);
var interlinePosition = selection
.QueryInterface(nsISelectionPrivate).interlinePosition;
var result = {};
findLocation(null, -1,
selection.focusNode, selection.focusOffset, interlinePosition, result);
statusBarField.label = getViewSourceBundle()
.getFormattedString("statusBarLineCol", [result.line, result.col]);
}
//
// Loops through the text lines in the pre element. The arguments are either
// (pre, line) or (node, offset, interlinePosition). result is an out
// argument. If (pre, line) are specified (and node == null), result.range is
// a range spanning the specified line. If the (node, offset,
// interlinePosition) are specified, result.line and result.col are the line
// and column number of the specified offset in the specified node relative to
// the whole file.
//
function findLocation(pre, line, node, offset, interlinePosition, result)
{
if (node && !pre) {
//
// Look upwards to find the current pre element.
//
for (pre = node;
pre.nodeName != "PRE";
pre = pre.parentNode);
}
//
// The source document is made up of a number of pre elements with
// id attributes in the format <pre id="line123">, meaning that
// the first line in the pre element is number 123.
//
var curLine = parseInt(pre.id.substring(4));
//
// Walk through each of the text nodes and count newlines.
//
var treewalker = document.createTreeWalker(pre, NodeFilter.SHOW_TEXT, null, false);
var treewalker = window._content.document
.createTreeWalker(pre, NodeFilter.SHOW_TEXT, null, false);
//
// The column number of the first character in the current text node.
//
var firstCol = 1;
var found = false;
for (var textNode = treewalker.firstChild();
textNode && curLine <= line + 1;
textNode = treewalker.nextNode()) {
textNode && !found;
textNode = treewalker.nextNode()) {
//
// \r is not a valid character in the DOM, so we only check for \n.
//
var lineArray = textNode.data.split(/\n/);
var lastLineInNode = curLine + lineArray.length - 1;
if (lastLineInNode < line) {
//
// Check if we can skip the text node without further inspection.
//
if (node ? (textNode != node) : (lastLineInNode < line)) {
if (lineArray.length > 1) {
firstCol = 1;
}
firstCol += lineArray[lineArray.length - 1].length;
curLine = lastLineInNode;
continue;
}
for (var i = 0, curPos = 0;
//
// curPos is the offset within the current text node of the first
// character in the current line.
//
for (var i = 0, curPos = 0;
i < lineArray.length;
curPos += lineArray[i++].length + 1) {
@ -288,66 +456,48 @@ function goToLine(line)
curLine++;
}
if (curLine == line && !range) {
range = document.createRange();
range.setStart(textNode, curPos);
if (node) {
if (offset >= curPos && offset <= curPos + lineArray[i].length) {
//
// If we are right after the \n of a line and interlinePosition is
// false, the caret looks as if it were at the end of the previous
// line, so we display that line and column instead.
//
if (i > 0 && offset == curPos && !interlinePosition) {
result.line = curPos - lineArray[i - 1].length - 1;
result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
//
// This will always be overridden later, except when we look for
// the very last line in the file (this is the only line that does
// not end with \n).
//
range.setEndAfter(pre.lastChild);
} else {
result.line = curLine;
result.col = (i == 0 ? firstCol : 1) + offset - curPos;
}
found = true;
} else if (curLine == line + 1) {
range.setEnd(textNode, curPos);
curLine++;
break;
break;
}
} else {
if (curLine == line && !("range" in result)) {
result.range = document.createRange();
result.range.setStart(textNode, curPos);
//
// This will always be overridden later, except when we look for
// the very last line in the file (this is the only line that does
// not end with \n).
//
result.range.setEndAfter(pre.lastChild);
} else if (curLine == line + 1) {
result.range.setEnd(textNode, curPos - 1);
found = true;
break;
}
}
}
}
if (!range) {
return false;
}
var selection = window._content.getSelection();
selection.removeAllRanges();
var selCon = getBrowser().docShell
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsISelectionDisplay)
.QueryInterface(Components.interfaces.nsISelectionController);
selCon.setDisplaySelection(
Components.interfaces.nsISelectionController.SELECTION_ON);
selCon.setCaretEnabled(true);
// In our case, the range's startOffset is after "\n" on the previous line.
// Set "hintright" to tune the selection at the beginning of the next line.
selection.QueryInterface(Components.interfaces.nsISelectionPrivate)
.interlinePosition = true;
selection.addRange(range);
// If it is a blank line, collapse to make the caret show up.
// (work-around to bug 156175)
if (range.endContainer == range.startContainer &&
range.endOffset - range.startOffset == 1) {
// note: by construction, there is just a "\n" in-bewteen
selection.collapseToStart();
}
// Scroll the beginning of the line into view.
selCon.scrollSelectionIntoView(
Components.interfaces.nsISelectionController.SELECTION_NORMAL,
Components.interfaces.nsISelectionController.SELECTION_ANCHOR_REGION,
true);
gLastLineFound = line;
return true;
return found;
}
//function to toggle long-line wrapping and set the view_source.wrap_long_lines

View File

@ -4,4 +4,4 @@ invalidInputTitle = Invalid input
invalidInputText = The line number entered is invalid.
outOfRangeTitle = Line not found
outOfRangeText = The specified line was not found.
statusBarLineCol = Line %1$S, Col %2$S

View File

@ -394,7 +394,7 @@
var line = getAttribute("line");
window.openDialog(
"chrome://navigator/content/viewSource.xul", "_blank",
"scrollbars,resizable,chrome,dialog=no", url, null, null, line);
"all,dialog=no", url, null, null, line);
]]></handler>
</handlers>
</binding>