1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-22 08:17:58 +01:00

merge native / and ? search

This commit is contained in:
Doug Kearns
2007-09-03 05:12:50 +00:00
parent abd42d741c
commit b009a6160b
7 changed files with 129 additions and 419 deletions

3
NEWS
View File

@@ -1,7 +1,10 @@
<pre>
2007-xx-xx:
* version 0.5.1
* native / and ? search and n and N working again
* the URL in the status line can be selected with the mouse again
* the Windows default RC file is now ~/_vimperatorrc and the plugin
directory is ~/vimperator/plugin
* commandline history now works properly on Windows
* filename completion now works on Windows
* the Bookmarks Toolbar Folder is now read when bookmarks are first

View File

@@ -682,6 +682,16 @@ function Commands() //{{{
help: "If <code class=\"argument\">[arg]</code> is specified then limit the list to those marks mentioned."
}
));
addDefaultCommand(new Command(["noh[lsearch]"],
function(args)
{
vimperator.search.clear();
},
{
short_help: "Clear the current selection",
help: "TODO"
}
));
addDefaultCommand(new Command(["norm[al]"],
function(args)
{

View File

@@ -11,9 +11,7 @@ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Initial Developer of the Original Code is Shawn Betts.
Portions created by the Initial Developer are Copyright (C) 2004,2005
by the Initial Developer. All Rights Reserved.
(c) 2006-2007: Martin Stubenschrott <stubenschrott@gmx.net>
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -28,443 +26,135 @@ the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/
// Finder for vimperator
// Author: Nigel McNie <http://nigel.mcnie.name/>
// Original Author: Shawn Betts
//
// The algorithm was taken from conkeror <http://conkeror.mozdev.net/>, but
// extensively refactored and changed to behave like vim (naturally!)
// The window to search in (which frame)
//var gWin = null;
//var gSelCtrl = null;
//function highlight(range, node) {
// var startContainer = range.startContainer;
// var startOffset = range.startOffset;
// var endOffset = range.endOffset;
// var docfrag = range.extractContents();
// var before = startContainer.splitText(startOffset);
// var parent = before.parentNode;
// node.appendChild(docfrag);
// parent.insertBefore(node, before);
// return node;
//}
// Clears the current selection
// @todo this should be in vimperator.js, and not depend on searcher.gSelCtrl
function clearSelection() {
//var selctrl = gSelCtrl;
var selctrl = vimperator.search.gSelCtrl;
var sel = selctrl.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
sel.removeAllRanges();
gFindBar.highlightDoc();
}
// Sets what is currently selected
// @todo as for clearSelection
function setSelection(range) {
try {
var selctrlcomp = Components.interfaces.nsISelectionController;
//var selctrl = gSelCtrl;
var selctrl = vimperator.search.gSelCtrl;
var sel = selctrl.getSelection(selctrlcomp.SELECTION_NORMAL);
sel.removeAllRanges();
sel.addRange(range.cloneRange());
selctrl.scrollSelectionIntoView(selctrlcomp.SELECTION_NORMAL,
selctrlcomp.SELECTION_FOCUS_REGION,
true);
}
catch (e) {
alert("setSelection: " + e);
}
}
// Highlight find matches and move selection to the first occurrence
// starting from pt.
// @todo move into searcher and clean up
function highlightFind(str, color, wrapped, dir, pt)
{
try {
var gWin = vimperator.search.gWin;//document.commandDispatcher.focusedWindow;
if (!gWin) {
alert('gWin does not exist here...');
alert(vimperator.search.gWin);
}
var doc = gWin.document;
var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"].createInstance()
.QueryInterface(Components.interfaces.nsIFind);
var searchRange;
var startPt;
var endPt;
var body = doc.body;
finder.findBackwards = !dir;
searchRange = doc.createRange();
startPt = doc.createRange();
endPt = doc.createRange();
var count = body.childNodes.length;
// Search range in the doc
searchRange.setStart(body,0);
searchRange.setEnd(body, count);
if (!dir) {
if (pt == null) {
startPt.setStart(body, count);
startPt.setEnd(body, count);
} else {
startPt.setStart(pt.startContainer, pt.startOffset);
startPt.setEnd(pt.startContainer, pt.startOffset);
}
endPt.setStart(body, 0);
endPt.setEnd(body, 0);
} else {
if (pt == null) {
startPt.setStart(body, 0);
startPt.setEnd(body, 0);
} else {
startPt.setStart(pt.endContainer, pt.endOffset);
startPt.setEnd(pt.endContainer, pt.endOffset);
}
endPt.setStart(body, count);
endPt.setEnd(body, count);
}
// search the doc
var retRange = null;
var selectionRange = null;
if (!wrapped) {
do {
retRange = finder.Find(str, searchRange, startPt, endPt);
var keepSearching = false;
if (retRange) {
var sc = retRange.startContainer;
var ec = retRange.endContainer;
var scp = sc.parentNode;
var ecp = ec.parentNode;
var sy1 = abs_point(scp).y;
var ey2 = abs_point(ecp).y + ecp.offsetHeight;
startPt = retRange.startContainer.ownerDocument.createRange();
if (!dir) {
startPt.setStart(retRange.startContainer, retRange.startOffset);
startPt.setEnd(retRange.startContainer, retRange.startOffset);
} else {
startPt.setStart(retRange.endContainer, retRange.endOffset);
startPt.setEnd(retRange.endContainer, retRange.endOffset);
}
// We want to find a match that is completely
// visible, otherwise the view will scroll just a
// bit to fit the selection in completely.
// alert ("sy1: " + sy1 + " scry: " + gWin.scrollY);
// alert ("ey2: " + ey2 + " bot: " + (gWin.scrollY + gWin.innerHeight));
keepSearching = (dir && sy1 < gWin.scrollY)
|| (!dir && ey2 >= gWin.scrollY + gWin.innerHeight);
}
} while (retRange && keepSearching);
} else {
retRange = finder.Find(str, searchRange, startPt, endPt);
}
if (retRange) {
setSelection(retRange);
selectionRange = retRange.cloneRange();
// highlightAllBut(str, retRange, color);
} else {
}
return selectionRange;
} catch(e) { alert('highlightFind:'+e); }
}
function clearHighlight()
{
gFindBar.highlightDoc();
var win = window.content;
var doc = win.document;
if (!document)
return;
var elem = null;
while ((elem = doc.getElementById("__vimperator-findbar-search-id"))) {
var child = null;
var docfrag = doc.createDocumentFragment();
var next = elem.nextSibling;
var parent = elem.parentNode;
while ((child = elem.firstChild)) {
docfrag.appendChild(child);
}
parent.removeChild(elem);
parent.insertBefore(docfrag, next);
}
}
/*
* Finds the absolute X and Y co-ordinates of a given node from the top left of
* the document
*
* Taken from conkeror utils.js
*/
function abs_point (node) {
var orig = node;
var pt = {};
try {
pt.x = node.offsetLeft;
pt.y = node.offsetTop;
// Find imagemap's coordinates
if (node.tagName == "AREA") {
var coords = node.getAttribute("coords").split(",");
pt.x += Number(coords[0]);
pt.y += Number(coords[1]);
}
node = node.offsetParent;
while (node.tagName != "BODY") {
pt.x += node.offsetLeft;
pt.y += node.offsetTop;
node = node.offsetParent;
}
}
catch (e) {
// Ignore
}
return pt;
}
// Vimperator searcher
// make sure you only create this object when the "vimperator" object is ready
//vimperator.search = new function()
// vimperator.search = new function()
function Search() //{{{
{
var self = this; // needed for callbacks since "this" is the "vimperator" object in a callback
this.gWin = null;
this.gSelCtrl = null;
this.gFindState = [];
var found = false; // true if the last search was successful
var backwards = false;
var lastsearch = ""; // keep track of the last searched string
var lastsearch_backwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
// Event handlers for search - closure is needed
vimperator.registerCallback("change", vimperator.modes.SEARCH_FORWARD, function(command){ self.searchKeyPressed(command); });
vimperator.registerCallback("submit", vimperator.modes.SEARCH_FORWARD, function(command){ self.searchSubmitted(command); });
vimperator.registerCallback("cancel", vimperator.modes.SEARCH_FORWARD, function(){ self.searchCancelled(); });
vimperator.registerCallback("cancel", vimperator.modes.SEARCH_FORWARD, function(){ self.searchCanceled(); });
// TODO: allow advanced modes in register/triggerCallback
vimperator.registerCallback("change", vimperator.modes.SEARCH_BACKWARD, function(command){ self.searchKeyPressed(command); });
vimperator.registerCallback("submit", vimperator.modes.SEARCH_BACKWARD, function(command){ self.searchSubmitted(command); });
vimperator.registerCallback("cancel", vimperator.modes.SEARCH_BACKWARD, function(){ self.searchCanceled(); });
// Called when the search dialog is asked for. Sets up everything necessary
// for this round of searching
this.openSearchDialog = function()
// Called when the search dialog is asked for
// If you omit "mode", it will default to forward searching
this.openSearchDialog = function(mode)
{
// Get a reference to the focused window if necessary
if (this.gWin == null) this.gWin = document.commandDispatcher.focusedWindow;
// Change the currently selected text to not be the attention colour
// @todo: check what this REALLY does
try {
this.gSelCtrl = this.getFocusedSelCtrl();
this.gSelCtrl.setDisplaySelection(Components.interfaces.nsISelectionController.SELECTION_ATTENTION);
this.gSelCtrl.repaintSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
if (mode == vimperator.modes.SEARCH_BACKWARD)
{
vimperator.commandline.open('?', '', vimperator.modes.SEARCH_BACKWARD);
backwards = true;
}
catch (e) {
alert('Could not change the colour of the current selection:' + e);
else
{
vimperator.commandline.open('/', '', vimperator.modes.SEARCH_FORWARD);
backwards = false;
}
// Initialize the state list for this attempt at searching
var state = this.createInitialFindState();
this.gFindState = [];
this.gFindState.push(state);
this.resumeFindState(state);
vimperator.commandline.open('/', '', vimperator.modes.SEARCH_FORWARD);
// TODO: focus the top of the currently visible screen
}
// Called when the current search needs to be repeated in the forward
// direction
// @todo will need re-jigging when reverse search comes in
this.findNext = function() {
this.find(this.lastFindState()["search-str"], true, this.lastFindState()["range"]);
this.resumeFindState(this.lastFindState());
// if there is still a search result
if (this.lastFindState()["range"]) {
if (this.lastFindState()["wrapped"]) {
vimperator.echoerr("search hit BOTTOM, continuing at TOP");
this.lastFindState()["wrapped"] = false;
}
else {
// TODO: this could probably be done in a nicer way - perhaps
// echoErr could not clobber all of this information somehow?
vimperator.echo('/' + this.lastFindState()["search-str"]);
}
}
// Finds text in a page
// TODO: backwards seems impossible i fear :(
this.find = function(str, backwards)
{
const FIND_NORMAL = 0;
const FIND_TYPEAHEAD = 1;
const FIND_LINKS = 2;
found = getBrowser().fastFind.find(str, false) != Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND;
return found;
}
// Called when the current search needs to be repeated in the backward
// direction
this.findPrevious = function() {
this.find(this.lastFindState()["search-str"], false, this.lastFindState()["range"]);
this.resumeFindState(this.lastFindState());
// if there is still a search result
if (this.lastFindState()["range"]) {
if (this.lastFindState()["wrapped"]) {
vimperator.echoerr("search hit TOP, continuing at BOTTOM");
this.lastFindState()["wrapped"] = false;
}
else {
vimperator.echo('/' + this.lastFindState()["search-str"]);
}
// Called when the current search needs to be repeated
this.findAgain = function(reverse)
{
// this hack is needed to make n/N work with the correct string, if
// we typed /foo<esc> after the original search
if (getBrowser().fastFind.searchString != lastsearch)
{
this.clear();
this.find(lastsearch, false);
gFindBar.highlightDoc("yellow", "black", lastsearch);
}
var up = reverse ? !lastsearch_backwards : lastsearch_backwards;
var result;
if (up)
result = getBrowser().fastFind.findPrevious();
else
result = getBrowser().fastFind.findNext();
if (result == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND)
vimperator.echoerr("E486: Pattern not found: " + lastsearch);
else if (result == Components.interfaces.nsITypeAheadFind.FIND_WRAPPED)
{
// hack needed, because wrapping causes a "scroll" event which clears
// our command line
setTimeout( function() {
if (up)
vimperator.echoerr("search hit TOP, continuing at BOTTOM");
else
vimperator.echoerr("search hit BOTTOM, continuing at TOP");
}, 10);
}
else // just clear the command line if something has been found
vimperator.echo("");
}
// Called when the user types a key in the search dialog. Triggers a find attempt
this.searchKeyPressed = function(command) {
if (command != "") {
var str = vimperator.commandline.getCommand();
this.find(str, true, this.lastFindState()["point"]);
this.resumeFindState(this.lastFindState());
}
else {
clearSelection();
}
this.searchKeyPressed = function(command)
{
// TODO: check for 'incsearch'
var backward = vimperator.hasMode(vimperator.modes.SEARCH_BACKWARD);
this.find(command, backward);
}
// Called when the enter key is pressed to trigger a search
this.searchSubmitted = function(command) {
//removeMode(MODE_SEARCH);
this.searchSubmitted = function(command)
{
this.clear();
gFindBar.highlightDoc("yellow", "black", command);
// need to find again to draw the highlight of the current search
// result over the "highlight all" search results
// very hacky, but seem to work
setTimeout(function() { self.findAgain(false); }, 10);
lastsearch_backwards = backwards;
lastsearch = command;
vimperator.setMode(vimperator.modes.NORMAL);
if (this.lastFindState()["range"] == null) {
vimperator.echoerr("E492: Pattern not found: " + this.lastFindState()["search-str"]);
}
vimperator.focusContent();
}
// Called when the search is cancelled - for example if someone presses
// escape while typing a search
this.searchCancelled = function() {
this.searchCanceled = function()
{
//removeMode(MODE_SEARCH);
vimperator.setMode(vimperator.modes.NORMAL);
clearSelection();
this.clear();
vimperator.focusContent();
}
//
// Helper methods
//
// Turn on the selection in all frames
// @todo to tell the truth, I have no idea what this does
this.getFocusedSelCtrl = function() {
var ds = getBrowser().docShell;
var dsEnum = ds.getDocShellEnumerator(Components.interfaces.nsIDocShellTreeItem.typeContent,
Components.interfaces.nsIDocShell.ENUMERATE_FORWARDS);
while (dsEnum.hasMoreElements()) {
ds = dsEnum.getNext().QueryInterface(Components.interfaces.nsIDocShell);
if (ds.hasFocus) {
var display = ds.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsISelectionDisplay);
if (!display) return null;
return display.QueryInterface(Components.interfaces.nsISelectionController);
}
}
// One last try
return getBrowser().docShell
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsISelectionDisplay)
.QueryInterface(Components.interfaces.nsISelectionController);
}
// Creates a default find state
this.createInitialFindState = function() {
var state = [];
state["screenx"] = this.gWin.scrollX;
state["screeny"] = this.gWin.scrollY;
state["search-str"] = "";
state["wrapped"] = false;
state["point"] = null;
state["range"] = document.createRange();
state["selection"] = null;
state["direction"] = true;
return state;
}
// Given a find state, moves the browser to the way it should be in the
// state - highlighting the correct thing and the screen scrolled to the
// correct location
this.resumeFindState = function(state) {
if (state["selection"]) {
setSelection(state["selection"]);
}
else {
clearSelection();
}
this.gWin.scrollTo(state["screenx"], state["screeny"]);
}
// Retrieves the current find state that we're in
// @todo rename to currentFindState?
this.lastFindState = function() {
return this.gFindState[this.gFindState.length - 1];
}
// Adds a find state to the stack of such states. This is done every time a find is successful
this.addFindState = function(screenX, screenY, searchStr, wrapped, point, range, selection, direction) {
var state = [];
state["screenx"] = screenX;
state["screeny"] = screenY;
state["search-str"] = searchStr;
state["wrapped"] = wrapped;
state["point"] = point;
state["range"] = range;
state["selection"] = selection;
state["direction"] = direction;
this.gFindState.push(state);
}
// Finds text in a page
this.find = function(str, dir, pt)
this.clear = function()
{
var norecurse = arguments[3];
var matchRange;
clearHighlight();
// Should we wrap this time?
var wrapped = this.lastFindState()["wrapped"];
var point = pt;
if (this.lastFindState()["wrapped"] == false
&& this.lastFindState()["range"] == null
&& this.lastFindState()["search-str"] == str
&& this.lastFindState()["direction"] == dir) {
wrapped = true;
point = null;
}
gFindBar.highlightDoc('yellow', 'black', str);
matchRange = highlightFind(str, "lightblue", wrapped, dir, point);
if (matchRange == null) {
// No more matches in this direction. So add the state and then find
// again to wrap around. But only find again once to prevent infinite
// recursion if an error occurs
this.addFindState(this.gWin.scrollX, this.gWin.scrollY, str, wrapped, point,
matchRange, this.lastFindState()["selection"], dir);
if (!norecurse)
this.find(str, dir, pt, true);
}
else {
this.addFindState(this.gWin.scrollX, this.gWin.scrollY, str, wrapped,
point, matchRange, matchRange, dir);
}
gFindBar.highlightDoc();
}
} //}}}
//// @TODO should be moved into commands.js
//vimperator.commands.add(new Command(["noh[lsearch]"],
// clearSelection,
// {
// short_help: "Clear the current selection"
// }
//));
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -938,27 +938,32 @@ function Mappings() //{{{
));
// search management
addDefaultMap(new Map(vimperator.modes.NORMAL, ["g/"],
function() { vimperator.search.openSearchDialog(); },
addDefaultMap(new Map(vimperator.modes.NORMAL, ["/"],
function() { vimperator.search.openSearchDialog(vimperator.modes.SEARCH_FORWARD); },
{
short_help: "Search forward for a pattern",
help: "Buggy on many sites, use / if you want a reliable search!"
help: "TODO"
}
));
addDefaultMap(new Map(vimperator.modes.NORMAL, ["?"],
function() { vimperator.search.openSearchDialog(vimperator.modes.SEARCH_BACKWARD); },
{
short_help: "Search backwards for a pattern",
help: "TODO"
}
));
addDefaultMap(new Map(vimperator.modes.NORMAL, ["n"],
function() { vimperator.search.findNext(); },
function() { vimperator.search.findAgain(false); },
{
short_help: "Find next",
help: "Repeat the last \"g/\" 1 time (until count is supported). <br/>" +
"NOTE: As \"g/\" is a little broken right now, use &lt;F3&gt; to go to the next search item of the \"/\" search for now."
help: "Repeat the last search 1 time (until count is supported)."
}
));
addDefaultMap(new Map(vimperator.modes.NORMAL, ["N"],
function() { vimperator.search.findPrevious(); },
function() { vimperator.search.findAgain(true); },
{
short_help: "Find previous",
help: "Repeat the last \"g/\" 1 time (until count is supported) in the opposite direction.<br/>" +
"NOTE: As \"g/\" is a little broken right now, use &lt;S-F3&gt; to go to the previous search item of the \"/\" search for now."
help: "Repeat the last search 1 time (until count is supported) in the opposite direction."
}
));

View File

@@ -415,7 +415,7 @@ function Options() //{{{
addOption(new Option(["hintstyle", "hs"], "string",
{
short_help: "CSS specification of unfocused hints appearance",
default_value: "z-index:5000; font-family:monospace; font-size:12px; color:black; background-color:yellow; " +
default_value: "z-index:5000; font-family:monospace; font-size:12px; color:white; background-color:red; " +
"border-color:ButtonShadow; border-width:0px; border-style:solid; padding:0px 1px 0px 1px; position:absolute;"
}
));

View File

@@ -528,8 +528,10 @@ function CommandLine() //{{{
// and blur the command line if there is no text left
if (command.length == 0)
{
this.clear();
vimperator.triggerCallback("cancel", cur_extended_mode);
vimperator.setMode(old_mode, old_extended_mode);
vimperator.focusContent();
this.clear();
}
}
else // any other key

View File

@@ -18,11 +18,11 @@ syn match vimperatorComment +".*$+ contains=vimperatorTodo,@Spell
syn keyword vimperatorCommand addo[ns] ba[ck] bd[elete] bw[ipeout] bun[load] tabc[lose] beep bma[dd] bmd[el] bookm[arks] bm
\ b[uffer] buffers files ls delm[arks] downl[oads] dl ec[ho] echoe[rr] exe[cute] exu[sage] fo[rward] fw ha[rdcopy] h[elp]
\ hist[ory] hs javas[cript] js mapc[lear] ma[rk] map marks no[remap] o[pen] e[dit] pc[lose] pref[erences] prefs q[uit]
\ quita[ll] qa[ll] re[load] reloada[ll] res[tart] sav[eas] se[t] so[urce] st[op] tab tabl[ast] tabm[ove] tabn[ext] tn[ext]
\ tabo[nly] tabopen t[open] tabnew tabe[dit] tabp[revious] tp[revious] tabN[ext] tN[ext] tabr[ewind] tabfir[st] u[ndo]
\ qmarka[dd] qma[dd] qmarkd[el] qmd[el] qmarks qms unm[ap] ve[rsion] viu[sage] win[open] w[open] wine[dit] wqa[ll] wq
\ xa[ll] zo[om] noh[ilight]
\ hist[ory] hs javas[cript] js mapc[lear] ma[rk] map marks noh[lsearch] no[remap] o[pen] e[dit] pc[lose] pref[erences]
\ prefs q[uit] quita[ll] qa[ll] re[load] reloada[ll] res[tart] sav[eas] se[t] so[urce] st[op] tab tabl[ast] tabm[ove]
\ tabn[ext] tn[ext] tabo[nly] tabopen t[open] tabnew tabe[dit] tabp[revious] tp[revious] tabN[ext] tN[ext] tabr[ewind]
\ tabfir[st] u[ndo] qmarka[dd] qma[dd] qmarkd[el] qmd[el] qmarks qms unm[ap] ve[rsion] viu[sage] win[open] w[open]
\ wine[dit] wqa[ll] wq xa[ll] zo[om]
\ contained
" FIXME