diff --git a/AUTHORS b/AUTHORS index 7c1d424e..7e833f20 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,6 +13,7 @@ Inactive/former developers: * Marco Candrian (mac@calmar.ws) Patches (in no special order): + * Xie&Tian (multibyte support for hints) * Juergen Descher * Kazuo (count support for ctrl-^) * Daniel Schaffrath (;b support) diff --git a/NEWS b/NEWS index 6470c8df..7f8c634e 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ 2008-XX-XX: * version 2.0 (probably) + * IMPORTANT: AlwaysHint modes were removed as they didn't make too + much sense with the new hint system * IMPORTANT: command actions now take an args object, returned from commands.parseArgs, as their first argument. This will break any commands not using the args parser explicitly. The old string value is now @@ -10,6 +12,7 @@ special versions for the old behavior * IMPORTANT: renamed Startup and Quit autocmd events to VimperatorEnter and VimperatorLeave respectively + * multibyte support for hints (thanks Xie&Tian) * add 'exrc' * add 'errorbells' * add shell command completion for :! diff --git a/content/events.js b/content/events.js index 0769f283..cef30741 100644 --- a/content/events.js +++ b/content/events.js @@ -1215,8 +1215,7 @@ function Events() //{{{ event.stopPropagation(); return true; } - else if (!(modes.extended & modes.INACTIVE_HINT) && - !mappings.hasMap(liberator.mode, input.buffer + key)) + else if (!mappings.hasMap(liberator.mode, input.buffer + key)) { macros.set(currentMacro, macros.get(currentMacro) + key); } @@ -1320,13 +1319,23 @@ function Events() //{{{ event.stopPropagation(); return false; } - // if Hint mode is on, special handling of keys is required - if (liberator.mode == modes.HINTS) + + if (modes.extended & modes.HINTS) { - hints.onEvent(event); - event.preventDefault(); - event.stopPropagation(); - return false; + // under HINT mode, certain keys are redirected to hints.onEvent + if (key == "" || key == "" || key == "" + || key == mappings.getMapLeader() + || (key == "" && hints.previnput == "number") + || (/^[0-9]$/.test(key) && !hints.escNumbers)) + { + hints.onEvent(event); + event.preventDefault(); + event.stopPropagation(); + return false; + } + + // others are left to generate the 'input' event or handled by firefox + return ; } } diff --git a/content/hints.js b/content/hints.js index 3b5ed10f..e5387587 100644 --- a/content/hints.js +++ b/content/hints.js @@ -38,6 +38,7 @@ function Hints() //{{{ var hintString = ""; // the typed string part of the hint is in this string var hintNumber = 0; // only the numerical part of the hint var usedTabKey = false; // when we used to select an element + var prevInput = ""; // record previous user input type, "text" || "number" // hints[] = [elem, text, span, imgspan, elem.style.backgroundColor, elem.style.color] var pageHints = []; @@ -50,6 +51,23 @@ function Hints() //{{{ // keep track of the documents which we generated the hints for // docs = { doc: document, start: start_index in hints[], end: end_index in hints[] } var docs = []; + + const hintDescriptions = { + a: "Save hint with prompt:", + s: "Save hint:", + o: "Follow hint:", + t: "Follow hint in a new tab:", + b: "Follow hint in a background tab:", + O: "Open location based on hint:", + T: "Open new tab based on hint:", + v: "View hint source:", + w: "Follow hint in a new window:", + W: "Open new window based on hint:", + y: "Yank hint location:", + Y: "Yank hint description:" + } + hintDescriptions[";"] = "Focus hint:"; + hintDescriptions["?"] = "Show information for hint:"; // reset all important variables function reset() @@ -58,6 +76,7 @@ function Hints() //{{{ hintString = ""; hintNumber = 0; usedTabKey = false; + prevInput = ""; pageHints = []; validHints = []; canUpdate = false; @@ -71,9 +90,7 @@ function Hints() //{{{ function updateStatusline() { - statusline.updateInputBuffer((escapeNumbers ? mappings.getMapLeader() + " " : "") + // sign for escapeNumbers - (hintString ? "\"" + hintString + "\"" : "") + - (hintNumber > 0 ? " <" + hintNumber + ">" : "")); + statusline.updateInputBuffer((escapeNumbers ? mappings.getMapLeader() : "") + (hintNumber || "")); } function generate(win) @@ -162,7 +179,7 @@ function Hints() //{{{ var elem, tagname, text, rect, span, imgspan; var hintnum = 1; - var validHint = hintMatcher(hintString); + var validHint = hintMatcher(hintString.toLowerCase()); var activeHint = hintNumber || 1; validHints = []; @@ -325,55 +342,64 @@ function Hints() //{{{ switch (submode) { case ";": buffer.focusElement(elem); break; - case "?": buffer.showElementInfo(elem); break; case "a": buffer.saveLink(elem, false); break; case "s": buffer.saveLink(elem, true); break; case "o": buffer.followLink(elem, liberator.CURRENT_TAB); break; - case "O": commandline.open(":", "open " + loc, modes.EX); break; case "t": buffer.followLink(elem, liberator.NEW_TAB); break; case "b": buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB); break; - case "T": commandline.open(":", "tabopen " + loc, modes.EX); break; case "v": buffer.viewSource(loc, false); break; case "V": buffer.viewSource(loc, true); break; - case "w": buffer.followLink(elem, liberator.NEW_WINDOW); break; - case "W": commandline.open(":", "winopen " + loc, modes.EX); break; + case "w": buffer.followLink(elem, liberator.NEW_WINDOW); break; + + // some modes need a timeout as they depend on the command line which is still blocked + // 500ms because otherwise `fad' would delete a tab, if "a" would make an "address" link unique + case "?": setTimeout(function () { buffer.showElementInfo(elem); }, timeout + 50); break; case "y": setTimeout(function () { util.copyToClipboard(loc, true); }, timeout + 50); break; case "Y": setTimeout(function () { util.copyToClipboard(elem.textContent || "", true); }, timeout + 50); break; + case "O": setTimeout(function () { commandline.open(":", "open " + loc, modes.EX); }, timeout); break; + case "T": setTimeout(function () { commandline.open(":", "tabopen " + loc, modes.EX); }, timeout); break; + case "W": setTimeout(function () { commandline.open(":", "winopen " + loc, modes.EX); }, timeout); break; default: liberator.echoerr("INTERNAL ERROR: unknown submode: " + submode); } removeHints(timeout); - if (modes.extended & modes.ALWAYS_HINT) + if (timeout == 0 || modes.isReplaying) { - setTimeout(function () { - canUpdate = true; - hintString = ""; - hintNumber = 0; - statusline.updateInputBuffer(""); - }, timeout); + // force a possible mode change, based on wheter an input field has focus + events.onFocusChange(); + if (modes.extended & modes.HINTS) + modes.reset(false); } else { - if (timeout == 0 || modes.isReplaying) - { - // force a possible mode change, based on wheter an input field has focus - events.onFocusChange(); - if (liberator.mode == modes.HINTS) - modes.reset(false); - } - else - { - modes.add(modes.INACTIVE_HINT); - setTimeout(function () { - if (liberator.mode == modes.HINTS) - modes.pop(); - }, timeout); - } + setTimeout(function () { + if (modes.extended & modes.HINTS) + modes.reset(); + }, timeout); } return true; } + + function onInput (event) + { + prevInput = "text"; + + // clear any timeout which might be active after pressing a number + if (activeTimeout) + { + clearTimeout(activeTimeout); + activeTimeout = null; + } + + hintNumber = 0; + hintString = commandline.getCommand(); + updateStatusline(); + showHints(); + if (validHints.length == 1) + processHints(false); + } function hintMatcher(hintString) //{{{ { @@ -599,22 +625,33 @@ function Hints() //{{{ mappings.add(myModes, ["f"], "Start QuickHint mode", - function () { hints.show(modes.QUICK_HINT); }); + function () + { + commandline.input("Follow hint:", null, { onChange: onInput }); + modes.extended = modes.HINTS | modes.QUICK_HINT; + hints.show(modes.QUICK_HINT); + }); mappings.add(myModes, ["F"], "Start QuickHint mode, but open link in a new tab", - function () { hints.show(modes.QUICK_HINT, "t"); }); + function () + { + commandline.input("Follow hint in a new tab:", null, { onChange: onInput }); + modes.extended = modes.HINTS | modes.QUICK_HINT; + hints.show(modes.QUICK_HINT, "t"); + }); mappings.add(myModes, [";"], "Start an extended hint mode", function (arg) { - if (arg == "f") - hints.show(modes.ALWAYS_HINT, "o"); - else if (arg == "F") - hints.show(modes.ALWAYS_HINT, "t"); - else - hints.show(modes.EXTENDED_HINT, arg); + let prompt = hintDescriptions[arg]; + if (!prompt) + return liberator.beep(); + + commandline.input(prompt, null, { onChange: onInput }); + modes.extended = modes.HINTS | modes.EXTENDED_HINT; + hints.show(modes.EXTENDED_HINT, arg); }, { flags: Mappings.flags.ARGUMENT }); @@ -632,10 +669,11 @@ function Hints() //{{{ return; } - modes.push(modes.HINTS, mode, win != undefined); submode = minor || "o"; // open is the default mode hintString = filter || ""; hintNumber = 0; + usedTab = false; + prevInput = ""; canUpdate = false; generate(win); @@ -684,11 +722,6 @@ function Hints() //{{{ followFirst = true; break; - case "": - hintString += " "; - escapeNumbers = false; - break; - case "": case "": usedTabKey = true; @@ -707,20 +740,17 @@ function Hints() //{{{ hintNumber = validHints.length; } showActiveHint(hintNumber, oldID); + updateStatusline(); return; case "": if (hintNumber > 0 && !usedTabKey) { hintNumber = Math.floor(hintNumber / 10); + if (hintNumber == 0) + prevInput = "text"; } - else if (hintString != "") - { - usedTabKey = false; - hintNumber = 0; - hintString = hintString.substr(0, hintString.length - 1); - } - else + else { usedTabKey = false; hintNumber = 0; @@ -729,12 +759,6 @@ function Hints() //{{{ } break; - case "": - case "": - hintString = ""; - hintNumber = 0; - break; - case mappings.getMapLeader(): escapeNumbers = !escapeNumbers; if (escapeNumbers && usedTabKey) // hintNumber not used normally, but someone may wants to toggle @@ -744,23 +768,10 @@ function Hints() //{{{ return; default: - // pass any special or ctrl- etc. prefixed key back to the main liberator loop - if (/^<./.test(key) || key == ":") - { - var map = null; - if ((map = mappings.get(modes.NORMAL, key)) || - (map = mappings.get(modes.HINTS, key))) - { - map.execute(null, -1); - return; - } - - liberator.beep(); - return; - } - - if (/^[0-9]$/.test(key) && !escapeNumbers) + if (/^[0-9]$/.test(key)) { + prevInput = "number"; + var oldHintNumber = hintNumber; if (hintNumber == 0 || usedTabKey) { @@ -802,14 +813,6 @@ function Hints() //{{{ processHints(true); return; } - - hintString += key; - hintNumber = 0; // after some text input - if (usedTabKey) - { - usedTabKey = false; - showActiveHint(1, hintNumber); - } } updateStatusline(); diff --git a/content/liberator.js b/content/liberator.js index d337eb24..27e53065 100644 --- a/content/liberator.js +++ b/content/liberator.js @@ -593,7 +593,7 @@ const liberator = (function () //{{{ triggerCallback: function (type, mode, data) { - //liberator.dump("type: " + type + " mode: " + mode + "data: " + data + "\n"); + // liberator.dump("type: " + type + " mode: " + mode + "data: " + data + "\n"); for (let i = 0; i < callbacks.length; i++) { var [thistype, thismode, thisfunc] = callbacks[i]; diff --git a/content/modes.js b/content/modes.js index 50fddaa5..348b0027 100644 --- a/content/modes.js +++ b/content/modes.js @@ -54,10 +54,6 @@ const modes = (function () //{{{ ext += " (quick)"; if (extended & modes.EXTENDED_HINT) ext += " (extended)"; - if (extended & modes.ALWAYS_HINT) - ext += " (always)"; - if (extended & modes.INACTIVE_HINT) - ext += " (inactive)"; if (extended & modes.MENU) // TODO: desirable? ext += " (menu)"; @@ -76,8 +72,12 @@ const modes = (function () //{{{ return "-- INSERT" + ext; case modes.VISUAL: return (extended & modes.LINE) ? "-- VISUAL LINE" + ext : "-- VISUAL" + ext; - case modes.HINTS: - return "-- HINTS" + ext; + // under modes.COMMAND_LINE, this block will never be reached + case modes.COMMAND_LINE: // since modes.HINTS is actually not a main mode + if (extended & modes.HINTS) + return "-- HINTS" + ext; + else + return macromode; case modes.CARET: return "-- CARET" + ext; case modes.TEXTAREA: @@ -129,11 +129,10 @@ const modes = (function () //{{{ plugins.stop(); break; - case modes.HINTS: - hints.hide(); - break; - case modes.COMMAND_LINE: + // clean up for HINT mode + if (modes.extended & modes.HINTS) + hints.hide(); commandline.close(); break; } @@ -176,12 +175,10 @@ const modes = (function () //{{{ SEARCH_BACKWARD: 1 << 14, QUICK_HINT: 1 << 15, EXTENDED_HINT: 1 << 16, - ALWAYS_HINT: 1 << 17, - INACTIVE_HINT: 1 << 18, // a short time after following a hint, we do not accept any input - MENU: 1 << 19, // a popupmenu is active - LINE: 1 << 20, // linewise visual mode - RECORDING: 1 << 21, - PROMPT: 1 << 22, + MENU: 1 << 17, // a popupmenu is active + LINE: 1 << 18, // linewise visual mode + RECORDING: 1 << 19, + PROMPT: 1 << 20, __iterator__: function () util.Array.iterator(this.all), diff --git a/content/ui.js b/content/ui.js index e49447a6..315054e1 100644 --- a/content/ui.js +++ b/content/ui.js @@ -148,7 +148,8 @@ function CommandLine() //{{{ var multilineCallback = null; // callback for prompt mode - var promptCallback = null; + var promptSubmitCallback = null; + var promptChangeCallback = null; var promptCompleter = null; liberator.registerCallback("change", modes.EX, function (command) { @@ -160,8 +161,8 @@ function CommandLine() //{{{ function closePrompt(value) { - let callback = promptCallback; - promptCallback = null; + let callback = promptSubmitCallback; + promptSubmitCallback = null; currentExtendedMode = null; commandline.clear(); if (callback) @@ -169,6 +170,8 @@ function CommandLine() //{{{ } liberator.registerCallback("cancel", modes.PROMPT, closePrompt); liberator.registerCallback("submit", modes.PROMPT, closePrompt); + liberator.registerCallback("change", modes.PROMPT, + function (str) { if (promptChangeCallback) return promptChangeCallback(str); }); liberator.registerCallback("complete", modes.PROMPT, function (str) { if (promptCompleter) return promptCompleter(str); }); @@ -665,7 +668,8 @@ function CommandLine() //{{{ { extra = extra || {}; - promptCallback = callback; + promptSubmitCallback = callback; + promptChangeCallback = extra.onChange; promptCompleter = extra.completer; modes.push(modes.COMMAND_LINE, modes.PROMPT); currentExtendedMode = modes.PROMPT; diff --git a/locale/en-US/hints.txt b/locale/en-US/hints.txt index 642e468c..9f980920 100644 --- a/locale/en-US/hints.txt +++ b/locale/en-US/hints.txt @@ -52,15 +52,8 @@ this hint mode. Then press [a]24[a] to copy the hint location. * [m]y[m] to yank its destination location * [m]Y[m] to yank its text description -Additionally there are two {mode}s, which will start an AlwaysHint mode: - -* [m]f[m] to open its location in the current tab -* [m]F[m] to open its location in a new tab - -These work like the [m]f[m] or [m]F[m] mappings but will keep you in -AlwaysHint mode. This is useful if you want to open many links of one page -without pressing [m]f[m] or [m]F[m] each time. Hintable elements for all -extended hint modes can be set in the 'extendedhinttags' XPath string. +Hintable elements for all extended hint modes can be set in the +'extendedhinttags' XPath string. ________________________________________________________________________________ // vim: set syntax=asciidoc: