diff --git a/chrome/content/vimperator/bookmarks.js b/chrome/content/vimperator/bookmarks.js index 467a609b..43932b51 100644 --- a/chrome/content/vimperator/bookmarks.js +++ b/chrome/content/vimperator/bookmarks.js @@ -352,8 +352,10 @@ function Marks() //{{{ //////////////////////////////////////////////////////////////////////////////// ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - var marks = {}; - var pendingMarks = []; + + var local_marks = {}; + var url_marks = {}; + var pending_jumps = []; var appcontent = document.getElementById("appcontent"); if (appcontent) appcontent.addEventListener("load", onPageLoad, true); @@ -361,152 +363,156 @@ function Marks() //{{{ function onPageLoad(event) { var win = event.originalTarget.defaultView; - for (var i = 0, length = pendingMarks.length; i < length; i++) + for (var i = 0, length = pending_jumps.length; i < length; i++) { - var mark = pendingMarks[i]; + var mark = pending_jumps[i]; if (win.location.href == mark.location) { win.scrollTo(mark.position.x * win.scrollMaxX, mark.position.y * win.scrollMaxY); - pendingMarks.splice(i, 1); + pending_jumps.splice(i, 1); return; } } } - function remove(mark) + function removeLocalMark(mark) { - var ok = false; - if (mark.match(/^[A-Z0-9]$/)) + if (mark in local_marks) { - if (mark in marks) + var win = window.content; + for (var i = 0; i < local_marks[mark].length; i++) { - delete marks[mark]; - ok = true; - } - } - else if (mark.match(/^[a-z]$/)) - { - if (mark in marks) - { - var win = vimperator.getCurrentBuffer(), length = marks[mark].length; - for (var i = 0; i < length; i++) + if (local_marks[mark][i].location == win.location.href) { - if (marks[mark][i].location == win.location.href) - { - marks[mark].splice(i, 1); - ok = true; - break; - } + vimperator.log("Deleting local mark: " + mark + " | " + local_marks[mark][i].location + " | (" + local_marks[mark][i].position.x + ", " + local_marks[mark][i].position.y + ") | tab: " + vimperator.tabs.index(local_marks[mark][i].tab)); + local_marks[mark].splice(i, 1); + if (local_marks[mark].length == 0) + delete local_marks[mark]; + break; } } } + } - if (!ok) + function removeURLMark(mark) + { + if (mark in url_marks) { - vimperator.echoerr("E20: Mark not set"); - return false; + vimperator.log("Deleting URL mark: " + mark + " | " + url_marks[mark].location + " | (" + url_marks[mark].position.x + ", " + url_marks[mark].position.y + ") | tab: " + vimperator.tabs.index(url_marks[mark].tab)); + delete url_marks[mark]; } - return ok; + } + + function isLocalMark(mark) + { + return /^[a-z]$/.test(mark); + } + + function isURLMark(mark) + { + return /^[A-Z0-9]$/.test(mark); } /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ + + // TODO: add support for frameset pages this.add = function(mark) { - var win = vimperator.getCurrentBuffer(); + var win = window.content; + + if (win.document.body.localName.toLowerCase() == "frameset") + { + vimperator.echo("marks support for frameset pages not implemented yet"); + return; + } + var x = win.scrollMaxX ? win.pageXOffset / win.scrollMaxX : 0; var y = win.scrollMaxY ? win.pageYOffset / win.scrollMaxY : 0; var position = { x: x, y: y }; - if (mark.match(/^[A-Z0-9]$/)) - marks[mark] = { location: win.location.href, position: position, tab: vimperator.tabs.getTab() }; - else if (mark.match(/^[a-z]$/)) + if (isURLMark(mark)) { - if (!marks[mark]) - marks[mark] = []; - marks[mark].push({ location: win.location.href, position: position }); + vimperator.log("Adding URL mark: " + mark + " | " + win.location.href + " | (" + position.x + ", " + position.y + ") | tab: " + vimperator.tabs.index(vimperator.tabs.getTab())); + url_marks[mark] = { location: win.location.href, position: position, tab: vimperator.tabs.getTab() }; + } + else if (isLocalMark(mark)) + { + // remove any previous mark of the same name for this location + removeLocalMark(mark); + if (!local_marks[mark]) + local_marks[mark] = []; + vimperator.log("Adding local mark: " + mark + " | " + win.location.href + " | (" + position.x + ", " + position.y + ")"); + local_marks[mark].push({ location: win.location.href, position: position }); } - else - return false; - - return this; } - // TODO: add support for mark ranges (p-z) this.remove = function(marks_str, special) { if (special) { - var win = vimperator.getCurrentBuffer(); - for (var i in marks) - { - if (i.match(/^[A-Z0-9]$/)) - continue; - - var length = marks[i].length; - for (var j = 0; j < length; j++) - { - if (marks[i][j].location == win.location.href) - { - marks[i].splice(j, 1); - ok = true; - break; - } - } - } - return this; + // :delmarks! only deletes a-z marks + for (var mark in local_marks) + removeLocalMark(mark); } - marks_str.split(/ /).forEach(function(mark) + else { - if (mark.length == 1) - remove(mark); - else - for (var i = 0; i < mark.length; i++) - { - remove(mark.substr(i, 1)); - } - }); - return this; + var pattern = new RegExp("[" + marks_str.replace(/\s+/g, '') + "]"); + for (mark in url_marks) + { + if (pattern.test(mark)) + removeURLMark(mark); + } + for (mark in local_marks) + { + if (pattern.test(mark)) + removeLocalMark(mark); + } + } } this.jumpTo = function(mark) { var ok = false; - if (mark.match(/^[A-Z0-9]$/)) + if (isURLMark(mark)) { - var slice = marks[mark]; + var slice = url_marks[mark]; if (slice && slice.tab && slice.tab.linkedBrowser) { if (!slice.tab.parentNode) { - pendingMarks.push(slice); + pending_jumps.push(slice); + // NOTE: this obviously won't work on generated pages using + // non-unique URLs, like Vimperator's help :( openURLsInNewTab(slice.location, true); return; } var index = vimperator.tabs.index(slice.tab); - if (index) + if (index != -1) { vimperator.tabs.select(index); var win = slice.tab.linkedBrowser.contentWindow; if (win.location.href != slice.location) { - pendingMarks.push(slice); + pending_jumps.push(slice); win.location.href = slice.location; return; } + vimperator.log("Jumping to URL mark: " + mark + " | " + slice.location + " | (" + slice.position.x + ", " + slice.position.y + ") | tab: " + vimperator.tabs.index(slice.tab)); win.scrollTo(slice.position.x * win.scrollMaxX, slice.position.y * win.scrollMaxY); ok = true; } } } - else if (mark.match(/^[a-z]$/)) + else if (isLocalMark(mark)) { - var win = vimperator.getCurrentBuffer(); - var slice = marks[mark] || []; + var win = window.content; + var slice = local_marks[mark] || []; for (var i = 0; i < slice.length; i++) { if (win.location.href == slice[i].location) { + vimperator.log("Jumping to local mark: " + mark + " | " + slice[i].location + " | (" + slice[i].position.x + ", " + slice[i].position.y + ")"); win.scrollTo(slice[i].position.x * win.scrollMaxX, slice[i].position.y * win.scrollMaxY); ok = true; } @@ -514,17 +520,14 @@ function Marks() //{{{ } if (!ok) - { - vimperator.echoerr("E20: Mark not set"); - return false; - } - return this; + vimperator.echoerr("E20: Mark not set"); // FIXME: move up? + + return ok; } - // TODO: show marks like vim does (when the multiline echo impl is done) or in the preview windwo right now + // TODO: show marks like vim does (when the multiline echo impl is done) or in the preview window right now this.list = function() { - return this; } //}}} } //}}} diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js index 3a074f64..04863b5d 100644 --- a/chrome/content/vimperator/commands.js +++ b/chrome/content/vimperator/commands.js @@ -329,12 +329,53 @@ function Commands() //{{{ } )); addDefaultCommand(new Command(["delm[arks]"], - function(args, special) { vimperator.marks.remove(args, special); }, + function(args, special) { + if (!special && !args) + { + vimperator.echoerr("E471: Argument required"); + return; + } + if (special && args) + { + vimperator.echoerr("E474: Invalid argument"); + return + } + var matches; + if (matches = args.match(/(?:(?:^|[^a-zA-Z0-9])-|-(?:$|[^a-zA-Z0-9])|[^a-zA-Z0-9 -]).*/)) + { + // TODO: this currently differs from Vim's behaviour which + // deletes any valid marks in the arg list, up to the first + // invalid arg, as well as giving the error message. Do we want + // to match this behaviour? + vimperator.echoerr("E475: Invalid argument: " + matches[0]); + return; + } + // check for illegal ranges - only allow a-z A-Z 0-9 + if (matches = args.match(/[a-zA-Z0-9]-[a-zA-Z0-9]/)) + { + for (var i = 0; i < matches.length; i++) + { + var start = matches[i][0]; + var end = matches[i][2]; + if (/[a-z]/.test(start) != /[a-z]/.test(end) || + /[A-Z]/.test(start) != /[A-Z]/.test(end) || + /[0-9]/.test(start) != /[0-9]/.test(end) || + start > end) + { + vimperator.echoerr("E475: Invalid argument: " + args.match(new RegExp(matches[i] + ".*"))[0]); + return; + } + } + } + + vimperator.marks.remove(args, special); + }, { usage: ["delm[arks]! {marks}"], short_help: "Delete the specified marks {a-zA-Z}", help: "Marks are presented as a list. Example:
" + ":delmarks Aa b p will delete marks A, a, b and p
" + + ":delmarks b-p will delete all marks in the range b to p
" + ":delmarks! will delete all marks for the current buffer", } @@ -450,7 +491,23 @@ function Commands() //{{{ } )); addDefaultCommand(new Command(["ma[rk]"], - function(args) { vimperator.marks.add(args) }, + function(args) { + if (!args) { + vimperator.echoerr("E471: Argument required"); + return; + } + if (args.length > 1) { + vimperator.echoerr("E488: Trailing characters"); + return; + } + if (!/[a-zA-Z]/.test(args)) + { + vimperator.echoerr("E191: Argument must be a letter or forward/backward quote"); + return; + } + + vimperator.marks.add(args); + }, { usage: ["ma[rk] {arg}"], short_help: "Mark current location within the webpage", @@ -458,8 +515,8 @@ function Commands() //{{{ )); addDefaultCommand(new Command(["marks"], function(args) { - vimperator.echo("marks not implemented yet"); - //vimperator.marks.list(args) + //vimperator.echo("marks not implemented yet"); + vimperator.marks.list(args) }, { usage: ["marks {arg}"], diff --git a/chrome/content/vimperator/vimperator.js b/chrome/content/vimperator/vimperator.js index fbc82f9e..52087736 100644 --- a/chrome/content/vimperator/vimperator.js +++ b/chrome/content/vimperator/vimperator.js @@ -309,12 +309,6 @@ function Vimperator() //{{{ content.focus(); } - - this.getCurrentBuffer = function() - { - return document.commandDispatcher.focusedWindow; - } - /** * logs any object to the javascript error console * also prints all properties of thie object @@ -918,7 +912,7 @@ function Tabs() //{{{ // @returns the index of the currently selected tab starting with 0 this.index = function(tab) { - if(tab) + if (tab) { var length = getBrowser().mTabs.length; for (var i = 0; i < length; i++) @@ -926,7 +920,7 @@ function Tabs() //{{{ if (getBrowser().mTabs[i] == tab) return i; } - return false; + return -1; } return getBrowser().tabContainer.selectedIndex;