Repository: qutebrowser/qutebrowser

Issue:
"Focus first input" command / autoselect first hint
`gi` in dwb/Vimium. Requested by rafi in IRC.

See also #13 

dwb implementation (js):

``` js
    var p_focusInput = function()
    {
        var i;
        var res = document.body.querySelectorAll('input[type=text], input[type=password], textarea');
        if (res.length === 0)
        {
            return "_dwb_no_input_";
        }
        var styles = document.styleSheets[0];
        styles.insertRule('input:focus { outline: 2px solid #1793d1; }', 0);
        if (!globals.activeInput)
        {
            globals.activeInput = res[0];
        }
        else
        {
            for (i=0; i<res.length; i++)
            {
                if (res[i] == globals.activeInput)
                {
                    globals.activeInput = res[i+1] || res[0];
                    break;
                }
            }
        }
        globals.activeInput.focus();
    }
```

Though probably we should just implement a `--first` flag or so for hints, which automatically selects the first one.



Discussion:
`--first` sounds nice, but it should select the first **input**, not _anchor_.

You can already tell `:hint` which kinds of elements to focus: https://github.com/The-Compiler/qutebrowser/blob/master/doc/help/commands.asciidoc#hint

Though a `input` group is indeed missing, mainly because checking if a field is editable involves more than just a CSS selector. I'll open another bug for that, because I'm on a bug opening spree anyways.

Another +1!
Hope you guys find a solution!

If `gi` should start hint mode with the `input` type, does the editability really matter? Maybe for `--first`, but otherwise there will just be some dead-end hints for the user to ignore, but still less than using plain `:hint`.

My 2 cents on this topic: The best way to implement something like this IMO is to provide a hints setting to automatically follow hints if there's only a single match.

In that case, I can use `;gt` to automatically select the input field if there only is one visible on the page, without having to type the hint key associated with it first.

I disagree, that is a different feature.
For  example, to type in this answer, I just hit `gi` (pentadactyl) and immediately was in the answer field. I don't know exactly how it works, but somehow `gi` selects nearly always the "correct" Input, even if there are multiple possible hints like on github.

Yes, it's a different functionality but it would still select the right thing automatically often enough for me to consider it a reasonable alternative. (For example while I'm typing this comment, this is the only input box visible on-screen)

In fact, my suggestion ties into another feature on my wish list: automatically follow hints as soon as there is exactly 1 match, even dynamically (e.g. say I use hint mode = words and a hint is “bicycle”, as soon as I press “bi” it might already uniquely identify the option so it would get selected right away - saving me a bunch of extra keystrokes)

So implementing that would kill two birds with one stone as far as I'm concerned.

> In fact, my suggestion ties into another feature on my wish list: automatically follow hints as soon as there is exactly 1 match

That's already implemented: `:set hints auto-follow true` (and it's even enabled by default)

> That's already implemented: `:set hints auto-follow true` (and it's even enabled by default)

It however doesn't do partial matches - it's only controlling whether you need to press enter after a full match.

@lahwaacz I had a look at the source code and honestly it seems like the current state is bugged more than anything: Have a look at these lines: https://github.com/The-Compiler/qutebrowser/blob/master/qutebrowser/browser/hints.py#L849

The way this is written (with the explicit branch for the non-'number' case) makes it painfully obvious that the original, intended behavior of this function was to always auto-follow as soon as the match was unique (just how I imagine it working), but for some reason (I don't know why), `filter_hints` never ends up getting called as you type except in `number` mode. (This strikes me as especially odd because the extra hints _do_ go away as you type! What is filtering them, if not filter_hints?)

Well, the funny thing is that the `filter_hints` method is called only for the `number` mode. As this is too off-topic for my taste, I created #1809 for that.

Assuming that the "wok in progress" label was related to #1817, this is currently not being worked on.

We were discussing this in IRC, and I was shortly possessed by Frankenstein and created following monster: `:jseval --quiet var inputs = document.getElementsByTagName("input"); for(var i = 0; i < inputs.length; i++) { var hidden = false; for(var j = 0; j < inputs[i].attributes.length; j++) { hidden = hidden || inputs[i].attributes[j].value.includes("hidden"); }; if(!hidden) { inputs[i].focus(); break; } }`

Basically, this will (with a very sofisticated heuristic) select the first visible input element on the current page. You can bind it to `z` or anything you want using:

```
bind z enter-mode insert ;; jseval --quiet var inputs = document.getElementsByTagName("input"); for(var i = 0; i < inputs.length; i++) { var hidden = false; for(var j = 0; j < inputs[i].attributes.length; j++) { hidden = hidden || inputs[i].attributes[j].value.includes("hidden"); }; if(!hidden) { inputs[i].focus(); break; } }
```

Finally, a readable version for those who dislike pasting a bunch of random javascript in their browser:

``` javascript
// get all HTMLelements with an "input" tag
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) { // for each input tag, do stuff
    // assuming the field is not hidden
    var hidden = false;
    for(var j = 0; j < inputs[i].attributes.length; j++) { // for each attribute of this element
        // if the attributes value contains "hidden", it's probably hidden.
        hidden = hidden || inputs[i].attributes[j].value.includes("hidden");
    }
    if(!hidden) {
        inputs[i].focus(); // grab focus
        break;             // gtfo
    }
}
```

I've only tested this on a few sites, but it should never break anything, it just might not work all the time.

@NoctuaNivalis you say “first visible”, but for example running that command right now (scrolled to the bottom of this issue page) selects the search bar at the top of the page instead of the text box down here.

(The `gi` binding I use does not have this issue)
Ah yes, it's the first visible on the current (entire) page, not just the viewbox. I don't think you can check actual visibility on the screen with javascript?

Edit: apparently you can, see [here](http://stackoverflow.com/questions/5353934/check-if-element-is-visible-on-screen#5354536). I don't mind it jumping to the top, though, as I've started using this mostly for a quick-search.
FWIW here's what I do for hints: https://github.com/The-Compiler/qutebrowser/blob/v0.9.0/qutebrowser/javascript/webelem.js#L69-L106
`--first` sounds like the right idea. Is it still the plan to solve it like that?
Still sounds good to me!
I was missing this feature from cVim, but now I find that binding `gi` to `hint inputs` is even better.
Is there an option to automatically select a hint if there is only one available? For example If I open ddg.gg and press `hint inputs` there is only one input field and would be great to autoselect it.
@asmbits `:set hints.auto_follow always`
that's it, thanks!
@The-Compiler on some sites it doesn't work. For example on google.com, there is only one input field but it is not selected automatically.

Resolving patch:
diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -172,6 +172,7 @@ class HintContext:
     tab = attr.ib(None)
     group = attr.ib(None)
     hint_mode = attr.ib(None)
+    first = attr.ib(False)
 
     def get_args(self, urlstr):
         """Get the arguments, with {hint-url} replaced by the given URL."""
@@ -612,6 +613,9 @@ def _start_cb(self, elems):
         modeman.enter(self._win_id, usertypes.KeyMode.hint,
                       'HintManager.start')
 
+        if self._context.first:
+            self._fire(strings[0])
+            return
         # to make auto_follow == 'always' work
         self._handle_auto_follow()
 
@@ -620,7 +624,8 @@ def _start_cb(self, elems):
     @cmdutils.argument('win_id', win_id=True)
     def start(self,  # pylint: disable=keyword-arg-before-vararg
               group=webelem.Group.all, target=Target.normal,
-              *args, win_id, mode=None, add_history=False, rapid=False):
+              *args, win_id, mode=None, add_history=False, rapid=False,
+              first=False):
         """Start hinting.
 
         Args:
@@ -632,6 +637,7 @@ def start(self,  # pylint: disable=keyword-arg-before-vararg
             add_history: Whether to add the spawned or yanked link to the
                          browsing history.
             group: The element types to hint.
+            first: Click the first hinted element without prompting.
 
                 - `all`: All clickable elements.
                 - `links`: Only links.
@@ -713,6 +719,7 @@ def start(self,  # pylint: disable=keyword-arg-before-vararg
         self._context.rapid = rapid
         self._context.hint_mode = mode
         self._context.add_history = add_history
+        self._context.first = first
         try:
             self._context.baseurl = tabbed_browser.current_url()
         except qtutils.QtValueError:


Test patch:
diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature
--- a/tests/end2end/features/hints.feature
+++ b/tests/end2end/features/hints.feature
@@ -509,3 +509,17 @@ Feature: Using hints
         And I press the key "hello"
         And I press the key "<Enter>"
         Then data/hello.txt should be loaded
+
+    Scenario: Using --first with normal links
+        When I open data/hints/html/simple.html
+        And I hint with args "all --first"
+        Then data/hello.txt should be loaded
+
+    Scenario: Using --first with inputs
+        When I open data/hints/input.html
+        And I hint with args "inputs --first"
+        And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
+        # ensure we clicked the first element
+        And I run :jseval console.log(document.activeElement.id == "qute-input");
+        And I run :leave-mode
+        Then the javascript message "true" should be logged