From 4896949964c397f375669e7ae42dc319a744705b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 1 Dec 2020 17:02:23 -0500 Subject: [PATCH 1/2] better sub/superscript tab completion --- NEWS.md | 3 +++ doc/src/manual/variables.md | 4 ++-- stdlib/REPL/src/REPLCompletions.jl | 11 +++++++++++ stdlib/REPL/src/docview.jl | 23 ++++++++++++++++++++++- stdlib/REPL/test/docview.jl | 5 +++++ stdlib/REPL/test/replcompletions.jl | 11 +++++++++++ 6 files changed, 54 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index d74a3aa304cc0..2dbc12bcc1471 100644 --- a/NEWS.md +++ b/NEWS.md @@ -208,6 +208,9 @@ Standard library changes + `suppress_output` (primarily a testing option) has been added as a keyword argument to `request`, rather than a configuration option. +* Tab completion now supports runs of consecutive sub/superscript characters, + e.g. `\^(3)` tab-completes to `⁽³⁾` ([#38649]). + * Windows REPL now supports 24-bit colors, by correctly interpreting virtual terminal escapes. #### SparseArrays diff --git a/doc/src/manual/variables.md b/doc/src/manual/variables.md index e7b2899dd931b..c68fbd304d33a 100644 --- a/doc/src/manual/variables.md +++ b/doc/src/manual/variables.md @@ -54,8 +54,8 @@ julia> 안녕하세요 = "Hello" In the Julia REPL and several other Julia editing environments, you can type many Unicode math symbols by typing the backslashed LaTeX symbol name followed by tab. For example, the variable -name `δ` can be entered by typing `\delta`-*tab*, or even `α̂₂` by `\alpha`-*tab*-`\hat`- -*tab*-`\_2`-*tab*. (If you find a symbol somewhere, e.g. in someone else's code, +name `δ` can be entered by typing `\delta`-*tab*, or even `α̂⁽²⁾` by `\alpha`-*tab*-`\hat`- +*tab*-`\^(2)`-*tab*. (If you find a symbol somewhere, e.g. in someone else's code, that you don't know how to type, the REPL help will tell you: just type `?` and then paste the symbol.) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 8e67fd67cd292..024fe1425a478 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -533,6 +533,11 @@ const whitespace_chars = [" \t\n\r"...] # bslash_completions function to try and complete on escaped characters in strings const bslash_separators = [whitespace_chars..., "\"'`"...] +const subscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\_") && length(k)==3) +const subscript_regex = Regex("^\\\\_[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z") +const superscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\^") && length(k)==3) +const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z") + # Aux function to detect whether we're right after a # using or import keyword function afterusing(string::String, startpos::Int) @@ -555,6 +560,12 @@ function bslash_completions(string::String, pos::Int) latex = get(latex_symbols, s, "") if !isempty(latex) # complete an exact match return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true)) + elseif occursin(subscript_regex, s) + sub = map(c -> subscripts[c], s[3:end]) + return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true)) + elseif occursin(superscript_regex, s) + sup = map(c -> superscripts[c], s[3:end]) + return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true)) end emoji = get(emoji_symbols, s, "") if !isempty(emoji) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index e58910b986306..e0723831522b8 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -349,15 +349,36 @@ function repl_latex(io::IO, s::String) print(io, "\"") printstyled(io, s, color=:cyan) print(io, "\" can be typed by ") + state = '\0' with_output_color(:cyan, io) do io for c in s cstr = string(c) if haskey(symbols_latex, cstr) - print(io, symbols_latex[cstr], "") + latex = symbols_latex[cstr] + if length(latex) == 3 && latex[2] in ('^','_') + # coalesce runs of sub/superscripts + if state != latex[2] + '\0' != state && print(io, "") + print(io, latex[1:2]) + state = latex[2] + end + print(io, latex[3]) + else + if '\0' != state + print(io, "") + state = '\0' + end + print(io, latex, "") + end else + if '\0' != state + print(io, "") + state = '\0' + end print(io, c) end end + '\0' != state && print(io, "") end println(io, '\n') end diff --git a/stdlib/REPL/test/docview.jl b/stdlib/REPL/test/docview.jl index e1763fc99672d..a5935a0426434 100644 --- a/stdlib/REPL/test/docview.jl +++ b/stdlib/REPL/test/docview.jl @@ -14,6 +14,11 @@ import Markdown Core.eval(Main, REPL.helpmode(buf, "🐨")) String(take!(buf)) end, "\"🐨\" can be typed by \\:koala:\n") + + @test startswith(let buf = IOBuffer() + Core.eval(Main, REPL.helpmode(buf, "ᵞ₁₂₃¹²³α")) + String(take!(buf)) + end, "\"ᵞ₁₂₃¹²³α\" can be typed by \\^gamma\\_123\\^123\\alpha\n") end @testset "Non-Markdown" begin diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 918803236d650..2478ca3c77720 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -881,6 +881,17 @@ let s = "CompletionFoo.tuple." @test isempty(c) end +@testset "sub/superscripts" begin + @test "⁽¹²³⁾ⁿ" in test_complete("\\^(123)n")[1] + @test "ⁿ" in test_complete("\\^n")[1] + @test "ᵞ" in test_complete("\\^gamma")[1] + @test isempty(test_complete("\\^(123)nq")[1]) + @test "₍₁₂₃₎ₙ" in test_complete("\\_(123)n")[1] + @test "ₙ" in test_complete("\\_n")[1] + @test "ᵧ" in test_complete("\\_gamma")[1] + @test isempty(test_complete("\\_(123)nq")[1]) +end + # test Dicts function test_dict_completion(dict_name) s = "$dict_name[\"ab" From dd732df5a5251931df81fc8c91ca137efa0f52d2 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 1 Dec 2020 23:12:31 -0500 Subject: [PATCH 2/2] Update stdlib/REPL/src/REPLCompletions.jl Co-authored-by: Jeff Bezanson --- stdlib/REPL/src/REPLCompletions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 024fe1425a478..3002adfad7bed 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -536,7 +536,7 @@ const bslash_separators = [whitespace_chars..., "\"'`"...] const subscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\_") && length(k)==3) const subscript_regex = Regex("^\\\\_[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z") const superscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\^") && length(k)==3) -const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z") +const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(superscripts)) * "]+\\z") # Aux function to detect whether we're right after a # using or import keyword