[ANN] TerminalPager.jl: REPL inline help added

I think that both displayed methods are valid. Similar to tuples, there can be a trailing comma after a non-empty argument list:

julia> abs(-1,)
1

Thanks, I didn’t know this. Hmm, I wonder whether this is a feature or an anti-feature. I understand why we have it for tuples and arrays as you might have your elements in separate lines and just want to add another line without caring whether it is the last line.
However, for callables, this only seem to make sense for varargs, which are the exception and not the norm, as otherwise you hardly add or remove some arguments.

Does anyone use this syntax in the REPL and would expect help for the single-argument method after writing the comma?

No I think if you’ve typed f('a', and then press TAB it’s reasonable for help to assume you want at least 2 arguments.

Definitely a feature. You want to be able to add and remove lines from calls like this:

foo(
    bar,
    baz,
)

Lua has the rule that you can’t have a trailing comma in function calls and it’s awful.

Besides, as the manual says

Tuples are an abstraction of the arguments of a function – without the function itself.

so it would be weird if their syntax were incompatible.

That’s not to say anything about what the REPL help mode should do, I don’t have a strong opinion on that.

2 Likes

The reason that the arguments are effectively a Tuple and if it is supported there, it should also be supported for callables, is a strong one.

Could you provide a real-life example for foo where you actually use it? As adding an element changes the method, I would really only expect it to be used for Varargs methods.

Most commonly when there are optional keyword args, which is perhaps less relevant for the help mode discussion (though keyword only-methods do exist). Also when iterating on my own code and signatures change (also less relevant for help mode).

1 Like

This is implemented in the newly released version 0.6.7 of TerminalPager.jl. So you will always get the extended help with <F1> and <alt> + <h>.

1 Like

Does this have to be enabled somehow, or have some known limitations? When I do using TerminalPager, then type IOContext (for example), and press Alt-h, it does nothing (this is on Windows, on both the Terminal app and on Wezterm). Pressing F1 for some reason types an A at that location.

I can open an issue if needed, just wanted to make sure I’m not misunderstanding something or running into known limitations.

It should be enabled after you loaded the package. We already had reports of working correctly on Windows, so it should work.

Is your text cursor above or at the very end of IOContext when you hit <Alt>+<h> (i.e. not after a trailing space)?

Which version of TerminalPager.jl do you currently use (TerminalPager |> pkgversion)?

1 Like

Alt+h not working was indeed a vesion issue. I had an older locally checked out version of TerminalPager added, so it hadn’t been updated with the other packages. Now after adding the registry version, Alt+h works great.

F1 still produces an A at the location (whether at the end of the text or in the middle of it), both on Wezterm and Terminal.

The Alt-h is a great usability improvement btw, a major step forward for the Julia REPL and makes TerminalPager.jl an even more essential part of the REPL for me. Thank you for adding it.

1 Like

Great that you have found and fixed the main issue. I am glad that you like the functionality.

Regarding <F1>: Could it be that your keyboard is sending the multimedia keycode instead of the <F1> keycode? Could you try using <Fn>+<F1> and report what this is doing?

I think for everyone with advanced typing skills, <alt>+<h> is the superior solution (at least on a Linux and Windows keyboard), but <F1> is the more common shortcut and therefore more discoverable. Therefore it would still nice to understand why that one is not working, although not strictly necessary.

It’s F1 on an external keyboard that does this (and Fn+F1 on the main laptop keyboard also leads to A). It seems to be a general Julia REPL thing btw, happens without TerminalPager loaded too. Searching it up, I found an old obscure issue Function keys i.e. F5 and F6 input random characters on windows console Ā· Issue #28799 Ā· JuliaLang/julia Ā· GitHub so it looks like this has been this way since Julia pre-1.0 days!

There’s a lot going on in julia/stdlib/REPL/src/LineEdit.jl at master Ā· JuliaLang/julia Ā· GitHub and I’m guessing somewhere in there, some part of the input codes generated for F1-F5 keys on Windows is getting gobbled up, leaving only an A (or B C D or E) behind - probably something unintentionally written with Linux assumptions early on.

1 Like

I’ve updated the issue #28799 with a bit more detail.

Meanwhile, I’ll see if F10 or something can be made to work on Windows as a temporary alternative until this is fixed - F6 and above don’t have this particular issue, but I don’t know if the REPL code is still processing them incorrectly in some other way, but I’ll give it a try sometime in the next week.

1 Like

Thanks for nailing it down to a general Julia Windows problem.
So this is beyond what we can fix in TerminalPager.jl.

Let’s see whether someone has fun (or is being payed for) fixing the problem for a proprietary system. I assume that escape_defaults and default_keymap might be good starting points.

I updated the issue thread on this, but basically it looks like this was fixed a few months ago (possibly inadvertently, while fixing pasting) and will work fine in the 1.13 Julia release.

Meanwhile, for 1.12, LTS, etc., I have this in my startup.jl to make F10 work like F1 was supposed to (since the original issue only affects F1-F5 keys, not the rest of them):

startup.jl
module Startup

# Configure F10 as pager-help key since F1 doesn't work on Windows on Julia 1.12 or lower
if isinteractive()
    F10_MAPPED::Bool = false
    # This section of the code is largely from BasicAutoloads.jl
    is_repl_ready() = isdefined(Base, :active_repl_backend) && isdefined(Base.active_repl_backend, :ast_transforms)
    function register_ast_transform_when_ready()
        iter = 0
        while !is_repl_ready() && iter < 120
            iter += 1
            sleep(0.02 * iter)
        end
        if is_repl_ready()
            push!(Base.active_repl_backend.ast_transforms, map_f10_pager)
        else
            @warn "Timed out waiting for `Base.active_repl_backend.ast_transforms` to become available. F10 for paged help will not work."
        end
    end

    function map_f10_pager(ast)
        if !F10_MAPPED && isdefined(Main, :TerminalPager) && Main.TerminalPager isa Module &&
           isdefined(Main.TerminalPager, :_show_pager_extended_help)
            LE = Base.REPL_MODULE_REF[].LineEdit
            main_modes = filter(Base.active_repl.interface.modes) do m
                m isa LE.Prompt && LE.prompt_string(m) in ["julia> ", "pager> "]
            end
            for m in main_modes
                escapes = m.keymap_dict['\e']
                escapes['[']['2']['1'] = Dict{Char,Any}()
                escapes['[']['2']['1']['~'] = Main.TerminalPager._show_pager_extended_help  # <F10>
            end
            global F10_MAPPED = true
        end
        return ast
    end

    @async register_ast_transform_when_ready()
end

end

Kinda hacky to use ast_transforms for this (among other internals), but that’s the best way I could think of and it seems to work reliably.