Skip to content

Commit

Permalink
Run OhMyREPL keybindings in a fixed world age
Browse files Browse the repository at this point in the history
This should avoid most invalidations caused by loading other packages.

Also remove the NEW_KEYBINDINGS global as it's hard to follow where this
is added to.

Rearrange all overrides so the implementation code can be precompiled,
with the only thing which is `eval`d at runtime being the method
pirating itself.

Include global mutable "hook" binding for `LineEdit.refresh_line` to
allow us to fix the world age of the implementation.
  • Loading branch information
c42f committed Aug 9, 2024
1 parent 6cd408c commit b76197c
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 81 deletions.
1 change: 0 additions & 1 deletion src/BracketInserter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,4 @@ function insert_into_keymap!(D::Dict)
end
end

insert_into_keymap!(OhMyREPL.Prompt.NEW_KEYBINDINGS)
end # module
57 changes: 34 additions & 23 deletions src/OhMyREPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,26 @@ export colorscheme!, colorschemes, enable_autocomplete_brackets, enable_highligh

const SUPPORTS_256_COLORS = !(Sys.iswindows() && VERSION < v"1.5.3")

# Wrap the function `f` so that it's always invoked in the given `world_age`
function fix_world_age(f, world_age)
if world_age == typemax(UInt)
function (args...; kws...)
Base.invokelatest(f, args...; kws...)
end
else
function (args...; kws...)
Base.invoke_in_world(world_age, f, args...; kws...)
end
end
end

include("repl_pass.jl")
include("repl.jl")
include("passes/Passes.jl")

include("BracketInserter.jl")
include("prompt.jl")
include("hooks.jl")

import .BracketInserter.enable_autocomplete_brackets

Expand Down Expand Up @@ -91,50 +105,47 @@ const ENABLE_FZF = Ref(true)
enable_fzf(v::Bool) = ENABLE_FZF[] = v

using Pkg
function reinsert_after_pkg()
repl = Base.active_repl
function reinsert_after_pkg(repl, world_age)
mirepl = isdefined(repl,:mi) ? repl.mi : repl
main_mode = mirepl.interface.modes[1]
m = first(methods(main_mode.keymap_dict[']']))
if m.module == Pkg.REPLMode
Prompt.insert_keybindings()
Prompt.insert_keybindings(repl, world_age)
end
end

function setup_repl(repl, world_age)
if !isdefined(repl, :interface)
repl.interface = REPL.setup_interface(repl)
end
Prompt.insert_keybindings(repl, world_age)
@async begin
sleep(0.25)
reinsert_after_pkg(repl, world_age)
end
update_interface(repl.interface)

global _refresh_line_hook = fix_world_age(_refresh_line, world_age)
end

function __init__()
options = Base.JLOptions()
world_age = Base.get_world_counter()
# command-line
if (options.isinteractive != 1) && options.commands != C_NULL
return
end

if isdefined(Base, :active_repl)
if !isdefined(Base.active_repl, :interface)
Base.active_repl.interface = REPL.setup_interface(Base.active_repl)
end
Prompt.insert_keybindings()
@async begin
sleep(0.25)
reinsert_after_pkg()
end
setup_repl(Base.active_repl, world_age)
else
atreplinit() do repl
if !isdefined(repl, :interface)
repl.interface = REPL.setup_interface(repl)
end
Prompt.insert_keybindings()
@async begin
sleep(0.25)
reinsert_after_pkg()
end
update_interface(repl.interface)
setup_repl(Base.active_repl, world_age)
end
end

if ccall(:jl_generating_output, Cint, ()) == 0
include(joinpath(@__DIR__, "refresh_lines.jl"))
include(joinpath(@__DIR__, "output_prompt_overwrite.jl"))
include(joinpath(@__DIR__, "MarkdownHighlighter.jl"))
activate_hooks()
end
end

Expand Down
55 changes: 49 additions & 6 deletions src/MarkdownHighlighter.jl → src/hooks.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@

import REPL
import REPL.LineEdit
using Crayons
import Markdown

import .OhMyREPL.Passes.SyntaxHighlighter.SYNTAX_HIGHLIGHTER_SETTINGS
import .OhMyREPL.HIGHLIGHT_MARKDOWN
function _refresh_line(s::REPL.LineEdit.BufferLike)
LineEdit.refresh_multi_line(s)
OhMyREPL.Prompt.rewrite_with_ANSI(s)
end

function _REPL_display(d::REPL.REPLDisplay, mime::MIME"text/plain", @nospecialize(x))
x = Ref{Any}(x)
REPL.with_repl_linfo(d.repl) do io
if isdefined(REPL, :active_module)
mod = REPL.active_module(d)::Module
else
mod = Main
end
io = IOContext(io, :limit => true, :module => mod)
if OUTPUT_PROMPT !== nothing
output_prompt = OUTPUT_PROMPT isa String ? OUTPUT_PROMPT : OUTPUT_PROMPT()
write(io, OUTPUT_PROMPT_PREFIX)
write(io, output_prompt, "\e[0m")
end
get(io, :color, false) && write(io, REPL.answer_color(d.repl))
if isdefined(d.repl, :options) && isdefined(d.repl.options, :iocontext)
# this can override the :limit property set initially
io = foldl(IOContext, d.repl.options.iocontext, init=io)
end
show(io, mime, x[])
println(io)
end
return nothing
end

split_lines(s::AbstractString) = isdefined(Markdown, :lines) ? Markdown.lines(s) : split(s, '\n')

function Markdown.term(io::IO, md::Markdown.Code, columns)
function _Markdown_term(io::IO, md::Markdown.Code, columns)
code = md.code
# Want to remove potential.
lang = md.language == "" ? "" : first(split(md.language))
Expand Down Expand Up @@ -37,11 +65,11 @@ function Markdown.term(io::IO, md::Markdown.Code, columns)
push!(outputs, "")
end

if do_syntax && HIGHLIGHT_MARKDOWN[]
if do_syntax && OhMyREPL.HIGHLIGHT_MARKDOWN[]
for (i, (sourcecode, output)) in enumerate(zip(sourcecodes, outputs))
tokens = collect(tokenize(sourcecode))
crayons = fill(Crayon(), length(tokens))
SYNTAX_HIGHLIGHTER_SETTINGS(crayons, tokens, 0, sourcecode)
OhMyREPL.Passes.SyntaxHighlighter.SYNTAX_HIGHLIGHTER_SETTINGS(crayons, tokens, 0, sourcecode)
buff = IOBuffer()
if lang == "jldoctest" || lang == "julia-repl"
print(buff, Crayon(foreground = :red, bold = true), "julia> ", Crayon(reset = true))
Expand Down Expand Up @@ -72,3 +100,18 @@ function Markdown.term(io::IO, md::Markdown.Code, columns)
end
end
end

_refresh_line_hook = _refresh_line

function activate_hooks()
@eval begin
LineEdit.refresh_line(s::REPL.LineEdit.BufferLike) =
_refresh_line_hook(s)
Markdown.term(io::IO, md::Markdown.Code, columns) =
_Markdown_term(io, md, columns)
end
if !isdefined(REPL, :IPython)
@eval REPL.display(d::REPL.REPLDisplay, mime::MIME"text/plain", x) =
_REPL_display(d, mime, x)
end
end
28 changes: 0 additions & 28 deletions src/output_prompt_overwrite.jl

This file was deleted.

5 changes: 5 additions & 0 deletions src/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ precompile(Tuple{OhMyREPL.Passes.SyntaxHighlighter.SyntaxHighlighterSettings, Ar
precompile(Tuple{OhMyREPL.Passes.BracketHighlighter.BracketHighlighterSettings, Array{Crayons.Crayon, 1}, Array{JuliaSyntax.Token, 1}, Int64, String})
precompile(Tuple{OhMyREPL.Passes.RainbowBrackets.RainbowBracketsSettings, Array{Crayons.Crayon, 1}, Array{JuliaSyntax.Token, 1}, Int64, String})
precompile(Tuple{typeof(OhMyREPL.untokenize_with_ANSI), Base.IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, OhMyREPL.PassHandler, Array{JuliaSyntax.Token, 1}, String, Int64})
precompile(_refresh_line, (REPL.LineEdit.ModeState,))
precompile(_refresh_line, (REPL.LineEdit.MIState,))
precompile(_refresh_line, (REPL.LineEdit.IOBuffer,))
precompile(_REPL_display, (REPL.REPLDisplay, MIME"text/plain", String))
precompile(_Markdown_term, (IO, Markdown.Code, Int))
6 changes: 0 additions & 6 deletions src/refresh_lines.jl

This file was deleted.

37 changes: 20 additions & 17 deletions src/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import REPL.Terminals: raw!, width, height, cmove, getX, TerminalBuffer,
getY, clear_line, beep, disable_bracketed_paste, enable_bracketed_paste

using OhMyREPL
import OhMyREPL: untokenize_with_ANSI, apply_passes!, PASS_HANDLER
import OhMyREPL: untokenize_with_ANSI, apply_passes!, PASS_HANDLER, fix_world_age

if VERSION > v"1.3"
import JLFzf
Expand Down Expand Up @@ -89,8 +89,7 @@ function rewrite_with_ANSI(s, cursormove::Bool = false)
end
end


function create_keybindings()
function create_keybindings(prefix_hist_prompt, world_age)
D = Dict{Any, Any}()
D['\b'] = (s, data, c) -> if LineEdit.edit_backspace(s, true)
rewrite_with_ANSI(s)
Expand Down Expand Up @@ -289,29 +288,33 @@ function create_keybindings()
LineEdit.enter_search(s, p, true)
end
end
return D

# Up Arrow
D["\e[A"] = (s,o...)-> begin
LineEdit.edit_move_up(buffer(s)) || LineEdit.enter_prefix_search(s, prefix_hist_prompt, true)
Prompt.rewrite_with_ANSI(s)
end
# Down Arrow
D["\e[B"] = (s,o...)-> begin
LineEdit.edit_move_down(buffer(s)) || LineEdit.enter_prefix_search(s, prefix_hist_prompt, false)
Prompt.rewrite_with_ANSI(s)
end

OhMyREPL.BracketInserter.insert_into_keymap!(D)

return Dict(k=>fix_world_age(f, world_age) for (k,f) in D)
end
NEW_KEYBINDINGS = create_keybindings()

function insert_keybindings(repl = Base.active_repl)
function insert_keybindings(repl, world_age)
mirepl = (isdefined(repl,:mistate) && !isnothing(repl.mistate)) ? repl.mistate : repl
interface_modes = mirepl.interface.modes
main_mode = interface_modes[1]
php_idx = findfirst(Base.Fix2(isa, LineEdit.PrefixHistoryPrompt), interface_modes)
p = interface_modes[php_idx]

# Up Arrow
NEW_KEYBINDINGS["\e[A"] = (s,o...)-> begin
LineEdit.edit_move_up(buffer(s)) || LineEdit.enter_prefix_search(s, p, true)
Prompt.rewrite_with_ANSI(s)
end
# Down Arrow
NEW_KEYBINDINGS["\e[B"] = (s,o...)-> begin
LineEdit.edit_move_down(buffer(s)) || LineEdit.enter_prefix_search(s, p, false)
Prompt.rewrite_with_ANSI(s)
end
keybinds = create_keybindings(p, world_age)

main_mode.keymap_dict = LineEdit.keymap(Dict{Any, Any}[NEW_KEYBINDINGS, main_mode.keymap_dict])
main_mode.keymap_dict = LineEdit.keymap(Dict{Any, Any}[keybinds, main_mode.keymap_dict])
end

function _commit_line(s, data, c)
Expand Down

0 comments on commit b76197c

Please sign in to comment.