mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-23 18:02:27 +01:00
Beginnings of bettter incsearch support
This commit is contained in:
@@ -28,6 +28,8 @@ the terms of any one of the MPL, the GPL or the LGPL.
|
|||||||
|
|
||||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
const Point = new Struct("x", "y");
|
||||||
|
|
||||||
function Buffer() //{{{
|
function Buffer() //{{{
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1278,17 +1280,13 @@ function Buffer() //{{{
|
|||||||
|
|
||||||
if (!selection)
|
if (!selection)
|
||||||
{
|
{
|
||||||
var selectionController = getBrowser().docShell
|
let selController = this.selectionController;
|
||||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
let caretmode = selController.getCaretEnabled();
|
||||||
.getInterface(Components.interfaces.nsISelectionDisplay)
|
selController.setCaretEnabled(true);
|
||||||
.QueryInterface(Components.interfaces.nsISelectionController);
|
selController.wordMove(false, false);
|
||||||
|
selController.wordMove(true, true);
|
||||||
var caretmode = selectionController.getCaretEnabled();
|
|
||||||
selectionController.setCaretEnabled(true);
|
|
||||||
selectionController.wordMove(false, false);
|
|
||||||
selectionController.wordMove(true, true);
|
|
||||||
selection = window.content.getSelection().toString();
|
selection = window.content.getSelection().toString();
|
||||||
selectionController.setCaretEnabled(caretmode);
|
selController.setCaretEnabled(caretmode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return selection;
|
return selection;
|
||||||
@@ -1481,6 +1479,11 @@ function Buffer() //{{{
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get selectionController() getBrowser().docShell
|
||||||
|
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Components.interfaces.nsISelectionDisplay)
|
||||||
|
.QueryInterface(Components.interfaces.nsISelectionController),
|
||||||
|
|
||||||
saveLink: function (elem, skipPrompt)
|
saveLink: function (elem, skipPrompt)
|
||||||
{
|
{
|
||||||
var doc = elem.ownerDocument;
|
var doc = elem.ownerDocument;
|
||||||
|
|||||||
@@ -149,8 +149,16 @@ function Completion() //{{{
|
|||||||
// Things we can dereference
|
// Things we can dereference
|
||||||
if (["object", "string", "function"].indexOf(typeof obj) == -1)
|
if (["object", "string", "function"].indexOf(typeof obj) == -1)
|
||||||
continue;
|
continue;
|
||||||
|
/* Try harder.
|
||||||
if (/^\[XPCNativeWrapper /.test(obj))
|
if (/^\[XPCNativeWrapper /.test(obj))
|
||||||
obj = obj.wrappedJSObject;
|
obj = obj.wrappedJSObject;
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (obj.wrappedJSObject)
|
||||||
|
obj = obj.wrappedJSObject;
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
|
||||||
for (let [k, v] in this.iter(obj))
|
for (let [k, v] in this.iter(obj))
|
||||||
compl.push([k, v]);
|
compl.push([k, v]);
|
||||||
@@ -381,7 +389,7 @@ function Completion() //{{{
|
|||||||
let statement = get(frame, 0, STATEMENTS) || 0; // Current statement.
|
let statement = get(frame, 0, STATEMENTS) || 0; // Current statement.
|
||||||
let prev = statement;
|
let prev = statement;
|
||||||
let obj;
|
let obj;
|
||||||
for (let [i, dot] in Iterator(get(frame)[DOTS]))
|
for (let [i, dot] in Iterator(get(frame)[DOTS].concat(stop)))
|
||||||
{
|
{
|
||||||
if (dot < statement)
|
if (dot < statement)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
182
content/find.js
182
content/find.js
@@ -1,4 +1,4 @@
|
|||||||
/***** BEGIN LICENSE BLOCK ***** {{{
|
/***** B/GIN LICENSE BLOCK ***** {{{
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
The contents of this file are subject to the Mozilla Public License Version
|
||||||
@@ -54,6 +54,7 @@ function Search() //{{{
|
|||||||
var lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
|
var lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
|
||||||
var caseSensitive = false; // search string is case sensitive
|
var caseSensitive = false; // search string is case sensitive
|
||||||
var linksOnly = false; // search is limited to link text only
|
var linksOnly = false; // search is limited to link text only
|
||||||
|
var rangeFind;
|
||||||
|
|
||||||
// Event handlers for search - closure is needed
|
// Event handlers for search - closure is needed
|
||||||
liberator.registerCallback("change", modes.SEARCH_FORWARD, function (command) { search.searchKeyPressed(command); });
|
liberator.registerCallback("change", modes.SEARCH_FORWARD, function (command) { search.searchKeyPressed(command); });
|
||||||
@@ -109,6 +110,129 @@ function Search() //{{{
|
|||||||
searchString = pattern;
|
searchString = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RangeFind(matchCase, backward)
|
||||||
|
{
|
||||||
|
var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"]
|
||||||
|
.createInstance()
|
||||||
|
.QueryInterface(Components.interfaces.nsIFind);
|
||||||
|
finder.caseSensitive = matchCase;
|
||||||
|
var sel = buffer.selectionController;
|
||||||
|
var doc = content.document;
|
||||||
|
var win = content;
|
||||||
|
|
||||||
|
var pageRange = doc.createRange();
|
||||||
|
pageRange.setStartBefore(doc.body);
|
||||||
|
pageRange.setEndAfter(doc.body);
|
||||||
|
var pageStart = pageRange.cloneRange();
|
||||||
|
var pageEnd = pageRange.cloneRange();
|
||||||
|
pageStart.collapse(true);
|
||||||
|
pageEnd.collapse(false);
|
||||||
|
|
||||||
|
// This doesn't work yet.
|
||||||
|
this.resetCaret = function ()
|
||||||
|
{
|
||||||
|
let equal = function (r1, r2) !r1.compareToRange(Range.START_TO_START, r2) && !r1.compareToRange(Range.END_TO_END, r2);
|
||||||
|
let selection = win.getSelection();
|
||||||
|
if (selection.rangeCount == 0)
|
||||||
|
selection.addRange(pageStart);
|
||||||
|
function getLines()
|
||||||
|
{
|
||||||
|
let orig = selection.getRangeAt(0);
|
||||||
|
function getRanges(forward)
|
||||||
|
{
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(orig);
|
||||||
|
let cur = orig;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var last = cur;
|
||||||
|
sel.lineMove(forward, false);
|
||||||
|
cur = selection.getRangeAt(0);
|
||||||
|
if(equal(cur, last))
|
||||||
|
break;
|
||||||
|
yield cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield orig;
|
||||||
|
for(let range in getRanges(true))
|
||||||
|
yield range;
|
||||||
|
for(let range in getRanges(false))
|
||||||
|
yield range;
|
||||||
|
}
|
||||||
|
for (let range in getLines())
|
||||||
|
{
|
||||||
|
if (sel.checkVisibility(range.startContainer, range.startOffset, range.startOffset))
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var start = new Point(win.pageXOffset, win.pageYOffset);
|
||||||
|
let selection = sel.getSelection(sel.SELECTION_NORMAL);
|
||||||
|
var startRange = selection.rangeCount ? selection.getRangeAt(0) : pageStart;
|
||||||
|
startRange.collapse(true);
|
||||||
|
|
||||||
|
var lastString = "";
|
||||||
|
var lastRange;
|
||||||
|
var forward;
|
||||||
|
|
||||||
|
this.__defineGetter__("searchString", function () lastString);
|
||||||
|
this.__defineGetter__("backward", function () finder.findBackwards);
|
||||||
|
|
||||||
|
this.search = function (word, reverse)
|
||||||
|
{
|
||||||
|
finder.findBackwards = reverse ? !backward : backward;
|
||||||
|
let again = word == null;
|
||||||
|
if (again)
|
||||||
|
word = lastString;
|
||||||
|
if (!matchCase)
|
||||||
|
word = word.toLowerCase();
|
||||||
|
|
||||||
|
if (word == "")
|
||||||
|
var range = startRange;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lastRange)
|
||||||
|
{
|
||||||
|
if (again)
|
||||||
|
lastRange.collapse(this.backward);
|
||||||
|
else if (word.indexOf(lastString) != 0 || this.backward)
|
||||||
|
lastRange = null;
|
||||||
|
else
|
||||||
|
lastRange.collapse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var range = finder.Find(word, pageRange,
|
||||||
|
lastRange || startRange,
|
||||||
|
pageEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastString = word;
|
||||||
|
if (range == null)
|
||||||
|
{
|
||||||
|
liberator.dump(this.wrapped);
|
||||||
|
if (this.wrapped)
|
||||||
|
return null;
|
||||||
|
this.wrapped = true;
|
||||||
|
lastRange = this.backward ? pageEnd : pageStart;
|
||||||
|
return this.search(again ? null : word, reverse);
|
||||||
|
}
|
||||||
|
this.wrapped = false;
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
sel.scrollSelectionIntoView(sel.SELECTION_NORMAL, 0, false);
|
||||||
|
lastRange = range.cloneRange();
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cancel = function ()
|
||||||
|
{
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(startRange);
|
||||||
|
win.scrollTo(start.x, start.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Stolen from toolkit.jar in Firefox, for the time being. The private
|
/* Stolen from toolkit.jar in Firefox, for the time being. The private
|
||||||
* methods were unstable, and changed. The new version is not remotely
|
* methods were unstable, and changed. The new version is not remotely
|
||||||
* compatible with what we do.
|
* compatible with what we do.
|
||||||
@@ -328,19 +452,17 @@ function Search() //{{{
|
|||||||
backwards = false;
|
backwards = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.find("", backwards);
|
||||||
// TODO: focus the top of the currently visible screen
|
// TODO: focus the top of the currently visible screen
|
||||||
},
|
},
|
||||||
|
|
||||||
// Finds text in a page
|
// Finds text in a page
|
||||||
// TODO: backwards seems impossible i fear :(
|
|
||||||
find: function (str, backwards)
|
find: function (str, backwards)
|
||||||
{
|
{
|
||||||
var fastFind = getBrowser().fastFind;
|
|
||||||
|
|
||||||
processUserPattern(str);
|
processUserPattern(str);
|
||||||
|
|
||||||
fastFind.caseSensitive = caseSensitive;
|
rangeFind = new RangeFind(caseSensitive, backwards);
|
||||||
found = fastFind.find(searchString, linksOnly) != Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND;
|
found = rangeFind.search(searchString);
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + searchPattern); }, 0);
|
setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + searchPattern); }, 0);
|
||||||
@@ -351,25 +473,14 @@ function Search() //{{{
|
|||||||
// Called when the current search needs to be repeated
|
// Called when the current search needs to be repeated
|
||||||
findAgain: function (reverse)
|
findAgain: function (reverse)
|
||||||
{
|
{
|
||||||
// this hack is needed to make n/N work with the correct string, if
|
if (!rangeFind || !rangeFind.search(null, reverse))
|
||||||
// we typed /foo<esc> after the original search. Since searchString is
|
|
||||||
// readonly we have to call find() again to update it.
|
|
||||||
if (getBrowser().fastFind.searchString != lastSearchString)
|
|
||||||
this.find(lastSearchString, false);
|
|
||||||
|
|
||||||
var up = reverse ? !lastSearchBackwards : lastSearchBackwards;
|
|
||||||
var result = getBrowser().fastFind.findAgain(up, linksOnly);
|
|
||||||
|
|
||||||
if (result == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND)
|
|
||||||
{
|
|
||||||
liberator.echoerr("E486: Pattern not found: " + lastSearchPattern);
|
liberator.echoerr("E486: Pattern not found: " + lastSearchPattern);
|
||||||
}
|
else if (rangeFind.wrapped)
|
||||||
else if (result == Components.interfaces.nsITypeAheadFind.FIND_WRAPPED)
|
|
||||||
{
|
{
|
||||||
// hack needed, because wrapping causes a "scroll" event which clears
|
// hack needed, because wrapping causes a "scroll" event which clears
|
||||||
// our command line
|
// our command line
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
if (up)
|
if (rangeFind.backward)
|
||||||
commandline.echo("search hit TOP, continuing at BOTTOM",
|
commandline.echo("search hit TOP, continuing at BOTTOM",
|
||||||
commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
||||||
else
|
else
|
||||||
@@ -379,7 +490,7 @@ function Search() //{{{
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
liberator.echo((up ? "?" : "/") + lastSearchPattern, null, commandline.FORCE_SINGLELINE);
|
liberator.echo((rangeFind.backward ? "?" : "/") + lastSearchPattern, null, commandline.FORCE_SINGLELINE);
|
||||||
|
|
||||||
if (options["hlsearch"])
|
if (options["hlsearch"])
|
||||||
this.highlight(lastSearchString);
|
this.highlight(lastSearchString);
|
||||||
@@ -390,7 +501,7 @@ function Search() //{{{
|
|||||||
searchKeyPressed: function (command)
|
searchKeyPressed: function (command)
|
||||||
{
|
{
|
||||||
if (options["incsearch"])
|
if (options["incsearch"])
|
||||||
this.find(command, backwards);
|
rangeFind.search(command);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called when the enter key is pressed to trigger a search
|
// Called when the enter key is pressed to trigger a search
|
||||||
@@ -404,22 +515,19 @@ function Search() //{{{
|
|||||||
if (!command)
|
if (!command)
|
||||||
command = lastSearchPattern;
|
command = lastSearchPattern;
|
||||||
|
|
||||||
this.clear();
|
|
||||||
if (!options["incsearch"] || !found)
|
if (!options["incsearch"] || !found)
|
||||||
|
{
|
||||||
|
this.clear();
|
||||||
this.find(command, backwards);
|
this.find(command, backwards);
|
||||||
|
}
|
||||||
|
|
||||||
lastSearchBackwards = backwards;
|
lastSearchBackwards = backwards;
|
||||||
//lastSearchPattern = command.replace(backwards ? /\?.*/ : /\/.*/, ""); // XXX
|
//lastSearchPattern = command.replace(backwards ? /\?.*/ : /\/.*/, ""); // XXX
|
||||||
lastSearchPattern = command;
|
lastSearchPattern = command;
|
||||||
lastSearchString = searchString;
|
lastSearchString = searchString;
|
||||||
|
|
||||||
// TODO: move to find() when reverse incremental searching is kludged in
|
|
||||||
// need to find again for reverse searching
|
|
||||||
if (backwards)
|
|
||||||
setTimeout(function () { search.findAgain(false); }, 0);
|
|
||||||
|
|
||||||
if (options["hlsearch"])
|
if (options["hlsearch"])
|
||||||
this.highlight(searchString);
|
this.highlight(rangeFind.searchString);
|
||||||
|
|
||||||
modes.reset();
|
modes.reset();
|
||||||
},
|
},
|
||||||
@@ -430,15 +538,15 @@ function Search() //{{{
|
|||||||
{
|
{
|
||||||
this.clear();
|
this.clear();
|
||||||
// TODO: code to reposition the document to the place before search started
|
// TODO: code to reposition the document to the place before search started
|
||||||
|
if (rangeFind)
|
||||||
|
rangeFind.cancel();
|
||||||
|
rangeFind = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
// FIXME: thunderbird incompatible
|
// FIXME: thunderbird incompatible
|
||||||
// this is not dependent on the value of 'hlsearch'
|
// this is not dependent on the value of 'hlsearch'
|
||||||
highlight: function (text)
|
highlight: function (text)
|
||||||
{
|
{
|
||||||
if (config.name == "Muttator")
|
|
||||||
return;
|
|
||||||
|
|
||||||
// already highlighted?
|
// already highlighted?
|
||||||
if (window.content.document.getElementsByClassName("__liberator-search").length > 0)
|
if (window.content.document.getElementsByClassName("__liberator-search").length > 0)
|
||||||
return;
|
return;
|
||||||
@@ -449,7 +557,8 @@ function Search() //{{{
|
|||||||
highlightObj.highlightDoc(window.content, text);
|
highlightObj.highlightDoc(window.content, text);
|
||||||
|
|
||||||
// recreate selection since _highlightDoc collapses the selection backwards
|
// recreate selection since _highlightDoc collapses the selection backwards
|
||||||
getBrowser().fastFind.findAgain(false, linksOnly);
|
if (rangeFind)
|
||||||
|
rangeFind.search(null);
|
||||||
|
|
||||||
// TODO: remove highlighting from non-link matches (HTML - A/AREA with href attribute; XML - Xlink [type="simple"])
|
// TODO: remove highlighting from non-link matches (HTML - A/AREA with href attribute; XML - Xlink [type="simple"])
|
||||||
},
|
},
|
||||||
@@ -457,10 +566,9 @@ function Search() //{{{
|
|||||||
clear: function ()
|
clear: function ()
|
||||||
{
|
{
|
||||||
highlightObj.highlightDoc(window.content);
|
highlightObj.highlightDoc(window.content);
|
||||||
// need to manually collapse the selection if the document is not
|
},
|
||||||
// highlighted
|
|
||||||
getBrowser().fastFind.collapseSelection();
|
rangeFind: RangeFind
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
//}}}
|
//}}}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
Components.utils.import("resource://liberator/storage.jsm", modules);
|
Components.utils.import("resource://liberator/storage.jsm", modules);
|
||||||
|
|
||||||
["liberator.js",
|
["liberator.js",
|
||||||
|
"util.js",
|
||||||
"config.js",
|
"config.js",
|
||||||
"buffer.js",
|
"buffer.js",
|
||||||
"commands.js",
|
"commands.js",
|
||||||
@@ -37,8 +38,7 @@
|
|||||||
"modes.js",
|
"modes.js",
|
||||||
"options.js",
|
"options.js",
|
||||||
"template.js",
|
"template.js",
|
||||||
"ui.js",
|
"ui.js"].forEach(load);
|
||||||
"util.js"].forEach(load);
|
|
||||||
modules.config.scripts.forEach(load);
|
modules.config.scripts.forEach(load);
|
||||||
|
|
||||||
})()
|
})()
|
||||||
|
|||||||
Reference in New Issue
Block a user