Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: allow changing context module in REPL #33102

Closed

Conversation

aviatesk
Copy link
Sponsor Member

@aviatesk aviatesk commented Aug 28, 2019

UPDATED

After a discussion, I decided to add a functionality to change the REPL context module for execution, completion, and docs, in this PR

How works

The context module can be set via calling REPL.set_context_module(mod), and then execution/completion/show docs/display are all done in the context of mod

TODO

  • add tests
    • change module in the context of execution
    • change module in the context of completion
    • change module in the context of help
  • update docs ?: the method signature of eval_user_input has been changed and it appears in some docs like stack trace, and profiling.

[The original description]

Purpose

Allow REPL [tab] completions in a module context other than Main.

Description

Currently REPL's [tab] completions work only in the context of the Main module, but there are cases when we want to get completions in a context of the other specific module:
E.g.: Juno can change REPL's module so that an entered code gets evaluated in a context of that module, and in this case we may want to get completions in the context of the module as well.

With this PR, each REPL provider could call REPL.set_context_module(mod) hooking on its module change, and then can get the corresponding completions.

If this idea is acceptable, I would really like to add tests for that, of course.

/cc @pfitzseb

@aviatesk aviatesk force-pushed the context-module-for-tab-completion branch from 1f1e0b2 to bb6c268 Compare August 28, 2019 12:19
@KristofferC
Copy link
Sponsor Member

KristofferC commented Aug 28, 2019

This is already implemented by calling

REPLCompletions.completions(string, pos, context_module=Main)

function completions(string, pos, context_module=Main)::Completions

See it in use in IJulia:

https://github.com/JuliaLang/IJulia.jl/blob/92b01a291519ecf68fbb58019c29b7747044584d/src/handlers.jl#L125

But you mean that you want to be able to set that module globally for a REPL?

@aviatesk
Copy link
Sponsor Member Author

aviatesk commented Aug 28, 2019

Thanks for the quick reply.
Yeah, I know, but that's not enough since tab completions are hooked at https://github.com/JuliaLang/julia/blob/master/stdlib/REPL/src/LineEdit.jl#L1826 and can't be changed outside of base as far as I understand.

@pfitzseb
Copy link
Member

It would generally be useful to be able to set the REPL module (which Juno already allows you to do), both for execution and completions.
Only changing the context for completions on its own doesn't make too much sense, I think.

@aviatesk
Copy link
Sponsor Member Author

aviatesk commented Aug 28, 2019

It would generally be useful to be able to set the REPL module (which Juno already allows you to do), both for execution and completions.

It would make more sense, and I'm willing to implement that if it sounds good to the other maintainers as well.

AFAIU the current Juno's approach to change module context for REPL execution couldn't be applied to REPL completions (or could be done in a dirty way with lots of copy and paste in a same way as https://github.com/JunoLab/Atom.jl/blob/master/src/Atom.jl#L22), thus I think it would be better to be implemented here.

@pfitzseb
Copy link
Member

AFAICT you only need to change this line plus some display logic below to use context_module.
That would still be a somewhat hacky solution though, and I think it would be better to pipe the current module through the backend channel instead, so that each request is associated with the correct module.

@aviatesk
Copy link
Sponsor Member Author

aviatesk commented Aug 28, 2019

@pfitzseb

That would still be a somewhat hacky solution though, and I think it would be better to pipe the current module through the backend channel instead, so that each request is associated with the correct module.

Thanks for the pointers. Piping modules through REPLBackend sounds better.

@KristofferC
So adding the functionality to change the module context for execution and completions (and maybe for helps) would be the preferred, after all ?
If so I'm going to change the PR title and get to the work.

@aviatesk aviatesk force-pushed the context-module-for-tab-completion branch from bb6c268 to 705a4f2 Compare August 29, 2019 18:55
@aviatesk aviatesk changed the title allow context module in REPL tab completions allow changing context module in REPL Aug 29, 2019
@aviatesk aviatesk changed the title allow changing context module in REPL WIP: allow changing context module in REPL Aug 29, 2019
@aviatesk
Copy link
Sponsor Member Author

Before proceeding to write tests, I would really like to hear your comments on those updates: #33102 (comment)
(for the general purpose of this PR and the current implementation approach or something :))

@KristofferC @pfitzseb

@fredrikekre fredrikekre added the stdlib:REPL Julia's REPL (Read Eval Print Loop) label Aug 29, 2019
@aviatesk aviatesk force-pushed the context-module-for-tab-completion branch 4 times, most recently from 203abc1 to 01419e1 Compare August 31, 2019 14:27
@aviatesk aviatesk changed the title WIP: allow changing context module in REPL RFC: allow changing context module in REPL Aug 31, 2019
@aviatesk
Copy link
Sponsor Member Author

@pfitzseb @KristofferC @StefanKarpinski
I finished up the work for now, and would like to hear your comments. The build failures seems to be irrelevant to the changes I made.

@@ -229,7 +229,8 @@ repl_search(s) = repl_search(stdout, s)

function repl_corrections(io::IO, s)
print(io, "Couldn't find ")
printstyled(io, s, '\n', color=:cyan)
pre = context_module == Main ? [] : [context_module, '.']
Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I tried to mimic the behaviour introduced in #23806. It's not exactly same, but kind of consistent IMO.

@@ -1032,3 +1032,50 @@ fake_repl() do stdin_write, stdout_read, repl
wait(repltask)
@test istaskdone(repltask)
end

# context_module changing #33102
Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm feeling like I want to add more tests here, and would really appreciate if you give me your ideas to make this test more effective.

@aviatesk aviatesk force-pushed the context-module-for-tab-completion branch from 4d1d9aa to 57fe646 Compare September 3, 2019 19:23
Copy link
Member

@pfitzseb pfitzseb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO this looks good in general, but a couple of things to think about:

  • The whole context_module global is a bit messy as is -- I think it would be a bit cleaner (and much more complex) to only use the global in the frontend and put the current module into REPLDisplay and REPLCompletionProvider, so that those don't rely on any globals. Haven't thought this through in detail, so it might a) not be much better and b) infeasible ;)
  • It would be cool to have a minimal REPL mode that allows changing the current module without weird Main.Base.set_context_module invocations (which would be necessary in a baremodule). That mode could also display the current module.
  • Should we change the julia> prompt to show the current module? Or change its color when you're not in Main? I feel like people are going to be confused without some indication that you're not in the default module.

@@ -59,6 +59,11 @@ answer_color(::AbstractREPL) = ""

const JULIA_PROMPT = "julia> "

context_module = Main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const context_module = Ref{Module}(Main)

seems better here.

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, I just pushed the commit to use Ref for context_module :)

stdlib/REPL/src/REPL.jl Outdated Show resolved Hide resolved
@KristofferC
Copy link
Sponsor Member

Note that we have this global object to hold options:

mutable struct Options
hascolor::Bool
extra_keymap::Union{Dict,Vector{<:Dict}}
# controls the presumed tab width of code pasted into the REPL.
# Must satisfy `0 < tabwidth <= 16`.
tabwidth::Int
# Maximum number of entries in the kill ring queue.
# Beyond this number, oldest entries are discarded first.
kill_ring_max::Int
region_animation_duration::Float64
beep_duration::Float64
beep_blink::Float64
beep_maxduration::Float64
beep_colors::Vector{String}
beep_use_current::Bool
backspace_align::Bool
backspace_adjust::Bool
confirm_exit::Bool # ^D must be repeated to confirm exit
auto_indent::Bool # indent a newline like line above
auto_indent_tmp_off::Bool # switch auto_indent temporarily off if copy&paste
auto_indent_bracketed_paste::Bool # set to true if terminal knows paste mode
# cancel auto-indent when next character is entered within this time frame :
auto_indent_time_threshold::Float64
end
Options(;
hascolor = true,
extra_keymap = AnyDict[],
tabwidth = 8,
kill_ring_max = 100,
region_animation_duration = 0.2,
beep_duration = 0.2, beep_blink = 0.2, beep_maxduration = 1.0,
beep_colors = ["\e[90m"], # gray (text_colors not yet available)
beep_use_current = true,
backspace_align = true, backspace_adjust = backspace_align,
confirm_exit = false,
auto_indent = true,
auto_indent_tmp_off = false,
auto_indent_bracketed_paste = false,
auto_indent_time_threshold = 0.005) =
Options(hascolor, extra_keymap, tabwidth,
kill_ring_max, region_animation_duration,
beep_duration, beep_blink, beep_maxduration,
beep_colors, beep_use_current,
backspace_align, backspace_adjust, confirm_exit,
auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste, auto_indent_time_threshold)
# for use by REPLs not having an options field
const GlobalOptions = Options()

Perhaps the context_module would fit in there? Not sure.

@pfitzseb
Copy link
Member

pfitzseb commented Sep 4, 2019

So the reason why it's dangerous to have context_module as a global is that it can change at any time (e.g. when you switch the module in Juno), even between the backend evaluating a request and the result of that being displayed. To get this fully correct we can only access the global when initiating the request in the frontend, and then everything else (eval, completions, display) needs to use that, regardless of what value the global has now.

@aviatesk aviatesk changed the title RFC: allow changing context module in REPL WIP: allow changing context module in REPL Sep 5, 2019
@aviatesk aviatesk force-pushed the context-module-for-tab-completion branch from 57fe646 to 0cccfcf Compare September 11, 2019 11:16
@aviatesk
Copy link
Sponsor Member Author

@pfitzseb, thanks for your thoughtful review !

So the reason why it's dangerous to have context_module as a global is that it can change at any time (e.g. when you switch the module in Juno), even between the backend evaluating a request and the result of that being displayed. To get this fully correct we can only access the global when initiating the request in the frontend, and then everything else (eval, completions, display) needs to use that, regardless of what value the global has now.

Really agree with this idea, but

  • The whole context_module global is a bit messy as is -- I think it would be a bit cleaner (and much more complex) to only use the global in the frontend and put the current module into REPLDisplay and REPLCompletionProvider, so that those don't rely on any globals. Haven't thought this through in detail, so it might a) not be much better and b) infeasible ;)

So "only using the global in the frontend" would mean to handle context_module in respond and pass it to eval_with_backend, and print_response, right ?
Actually I don't have a solid understanding of what does the "frontend" here mean.

  • It would be cool to have a minimal REPL mode that allows changing the current module without weird Main.Base.set_context_module invocations (which would be necessary in a baremodule). That mode could also display the current module.

Yeah, sounds nice to me.
And more, the mode (I gonna call it as "Module mode") would allow an user to change context_module even for modules that are not imported to the current module, with the same logic as we change the module for workspace in Juno.

  • Should we change the julia> prompt to show the current module? Or change its color when you're not in Main? I feel like people are going to be confused without some indication that you're not in the default module.

Would be cool as well.

@aviatesk
Copy link
Sponsor Member Author

Note that we have this global object to hold options:

mutable struct Options
hascolor::Bool
extra_keymap::Union{Dict,Vector{<:Dict}}
# controls the presumed tab width of code pasted into the REPL.
# Must satisfy `0 < tabwidth <= 16`.
tabwidth::Int
# Maximum number of entries in the kill ring queue.
# Beyond this number, oldest entries are discarded first.
kill_ring_max::Int
region_animation_duration::Float64
beep_duration::Float64
beep_blink::Float64
beep_maxduration::Float64
beep_colors::Vector{String}
beep_use_current::Bool
backspace_align::Bool
backspace_adjust::Bool
confirm_exit::Bool # ^D must be repeated to confirm exit
auto_indent::Bool # indent a newline like line above
auto_indent_tmp_off::Bool # switch auto_indent temporarily off if copy&paste
auto_indent_bracketed_paste::Bool # set to true if terminal knows paste mode
# cancel auto-indent when next character is entered within this time frame :
auto_indent_time_threshold::Float64
end
Options(;
hascolor = true,
extra_keymap = AnyDict[],
tabwidth = 8,
kill_ring_max = 100,
region_animation_duration = 0.2,
beep_duration = 0.2, beep_blink = 0.2, beep_maxduration = 1.0,
beep_colors = ["\e[90m"], # gray (text_colors not yet available)
beep_use_current = true,
backspace_align = true, backspace_adjust = backspace_align,
confirm_exit = false,
auto_indent = true,
auto_indent_tmp_off = false,
auto_indent_bracketed_paste = false,
auto_indent_time_threshold = 0.005) =
Options(hascolor, extra_keymap, tabwidth,
kill_ring_max, region_animation_duration,
beep_duration, beep_blink, beep_maxduration,
beep_colors, beep_use_current,
backspace_align, backspace_adjust, confirm_exit,
auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste, auto_indent_time_threshold)
# for use by REPLs not having an options field
const GlobalOptions = Options()

Perhaps the context_module would fit in there? Not sure.

If we are okay with disabling the context module changing within BasicREPL and StreamREPL, I think we can make Options hold context_module.

@aviatesk
Copy link
Sponsor Member Author

aviatesk commented Jun 6, 2022

I think this is superseded by #33872.

@aviatesk aviatesk closed this Jun 6, 2022
@aviatesk aviatesk deleted the context-module-for-tab-completion branch June 6, 2022 10:31
@rfourquet
Copy link
Member

Sorry for having started #33872 without checking (or finding) that there was already another attempt...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib:REPL Julia's REPL (Read Eval Print Loop)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants