Skip to content

Commit d7af2d5

Browse files
authored
Merge pull request #208 from JunoLab/avi/partialdots
strip trailing dots in goto/datatip (and rename)
2 parents 66270cc + 9d80e2f commit d7af2d5

File tree

6 files changed

+236
-233
lines changed

6 files changed

+236
-233
lines changed

src/datatip.jl

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#=
2-
@TODO:
3-
Use our own UI components for this: atom-ide-ui is already deprecated, ugly, not fully functional, and and...
4-
Once we can come to handle links within datatips, we may want to append method tables as well
2+
@TODO use our own UI components for this:
3+
atom-ide-ui is already deprecated, ugly, not fully functional, and and...
54
=#
65

76
handle("datatip") do data
@@ -25,7 +24,7 @@ function datatip(word, mod, path, column = 1, row = 1, startrow = 0, context = "
2524
ldt = localdatatip(word, column, row, startrow, context)
2625
isempty(ldt) || return datatip(ldt)
2726

28-
tdt = topleveldatatip(mod, word)
27+
tdt = globaldatatip(mod, word)
2928
tdt !== nothing && return Dict(:error => false, :strings => tdt)
3029

3130
return Dict(:error => true) # nothing hits
@@ -36,7 +35,7 @@ datatip(dt::Int) = Dict(:error => false, :line => dt)
3635
datatip(dt::Vector{Int}) = datatip(dt[1])
3736

3837
function localdatatip(word, column, row, startrow, context)
39-
word = first(split(word, '.')) # ignore dot accessors
38+
word = first(split(word, '.')) # always ignore dot accessors
4039
position = row - startrow
4140
ls = locals(context, position, column)
4241
filter!(ls) do l
@@ -56,7 +55,7 @@ function localdatatip(l, word, startrow)
5655
end
5756
end
5857

59-
function topleveldatatip(mod, word)
58+
function globaldatatip(mod, word)
6059
docs = @errs getdocs(mod, word)
6160
docs isa EvalError && return nothing
6261

@@ -71,6 +70,9 @@ function topleveldatatip(mod, word)
7170

7271
processdoc!(docs, docstr, datatip)
7372

73+
ml = methods(val)
74+
processmethods!(ml, datatip)
75+
7476
return datatip
7577
end
7678

@@ -122,6 +124,20 @@ processval!(val::Function, docstr, datatip) = begin
122124
end
123125
processval!(::Undefined, docstr, datatip) = nothing
124126

127+
function processmethods!(ml, datatip)
128+
ms = collect(ml)
129+
isempty(ms) && return
130+
131+
substr = s"<code>\g<sig></code> in <code>\g<mod></code> at \g<loc>"
132+
msstr = map(ms) do m
133+
s = replace(string(m), methodloc_regex => substr)
134+
"<li>$s</li>"
135+
end |> join
136+
137+
name = ms[1].name
138+
pushmarkdown!(datatip, "<details><summary><code>$name</code> has **$(length(ms))** methods:</summary><ul>$(msstr)</ul></details>")
139+
end
140+
125141
function pushmarkdown!(datatip, markdown)
126142
(markdown == "" || markdown == "\n") && return
127143
push!(datatip, Dict(:type => :markdown, :value => markdown))

src/display/methods.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ stripparams(t) = replace(t, r"\{([A-Za-z, ]*?)\}" => "")
44

55
interpose(xs, y) = map(i -> iseven(i) ? xs[i÷2] : y, 2:2length(xs))
66

7+
const methodloc_regex = r"(?<sig>.+) in (?<mod>.+) at (?<loc>.+)$"
8+
79
function view(m::Method)
810
str = sprint(show, "text/html", m)
9-
str = replace(str, r" in .* at .*$" => "")
11+
str = replace(str, methodloc_regex => s"\g<sig>")
1012
str = string("<span>", str, "</span>")
1113
tv, decls, file, line = Base.arg_decl_parts(m)
1214
HTML(str), file == :null ? "not found" : Atom.baselink(string(file), line)

src/goto.jl

Lines changed: 30 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function gotosymbol(
3232
localitems = localgotoitem(word, path, column, row, startrow, context)
3333
isempty(localitems) || return Dict(
3434
:error => false,
35-
:items => map(Dict, localitems),
35+
:items => map(Dict, localitems)
3636
)
3737
end
3838

@@ -66,7 +66,7 @@ Dict(gotoitem::GotoItem) = Dict(
6666
### local goto
6767

6868
function localgotoitem(word, path, column, row, startrow, context)
69-
word = first(split(word, '.')) # ignore dot accessors
69+
word = first(split(word, '.')) # always ignore dot accessors
7070
position = row - startrow
7171
ls = locals(context, position, column)
7272
filter!(ls) do l
@@ -86,31 +86,31 @@ localgotoitem(word, ::Nothing, column, row, startrow, context) = [] # when `path
8686
function globalgotoitems(word, mod, text, path)
8787
mod = getmodule(mod)
8888

89-
moduleitems = modulegotoitems(word, mod)
90-
isempty(moduleitems) || return moduleitems
89+
# strip a dot-accessed module if exists
90+
identifiers = split(word, '.')
91+
head = string(identifiers[1])
92+
if head word && getfield′(mod, head) isa Module
93+
# if `head` is a module, update `word` and `mod`
94+
nextword = join(identifiers[2:end], '.')
95+
return globalgotoitems(nextword, head, text, path)
96+
end
97+
98+
val = getfield′(mod, word)
99+
val isa Module && return [GotoItem(val)] # module goto
91100

92101
toplevelitems = toplevelgotoitems(word, mod, text, path)
93102

94-
# only append methods that are not caught by `toplevelgotoitems`
103+
# append method gotos that are not caught by `toplevelgotoitems`
104+
ml = methods(val)
95105
files = map(item -> item.file, toplevelitems)
96-
methoditems = filter!(item -> item.file files, methodgotoitems(mod, word))
97-
106+
methoditems = filter!(item -> item.file files, methodgotoitems(ml))
98107
append!(toplevelitems, methoditems)
99108
end
100109

101110
## module goto
102111

103-
function modulegotoitems(word, mod)::Vector{GotoItem}
104-
mod = getfield′(mod, Symbol(word))
105-
return mod isa Module ? [GotoItem(mod)] : []
106-
end
107-
108112
function GotoItem(mod::Module)
109-
file, line = if mod == Main
110-
MAIN_MODULE_LOCATION[]
111-
else
112-
moduledefinition(mod)
113-
end
113+
file, line = mod == Main ? MAIN_MODULE_LOCATION[] : moduledefinition(mod)
114114
GotoItem(string(mod), file, line - 1)
115115
end
116116

@@ -120,16 +120,6 @@ const PathItemsMaps = Dict{String, Vector{ToplevelItem}}
120120
const SYMBOLSCACHE = Dict{String, PathItemsMaps}()
121121

122122
function toplevelgotoitems(word, mod, text, path)
123-
# strip a dot-accessed module if exists
124-
identifiers = split(word, '.')
125-
head = identifiers[1]
126-
if head word && (val = getfield′(mod, string(head))) isa Module
127-
# if `head` is a module, update `word` and `mod`
128-
nextword = join(identifiers[2:end], '.')
129-
nextmod = val
130-
return toplevelgotoitems(nextword, nextmod, text, path)
131-
end
132-
133123
key = string(mod)
134124
pathitemsmaps = if haskey(SYMBOLSCACHE, key)
135125
SYMBOLSCACHE[key]
@@ -139,8 +129,8 @@ function toplevelgotoitems(word, mod, text, path)
139129

140130
ismacro(word) && (word = lstrip(word, '@'))
141131
ret = Vector{GotoItem}()
142-
for (path, items) pathitemsmaps
143-
for item filter(item -> filtertoplevelitem(word, item), items)
132+
for (path, items) in pathitemsmaps
133+
for item in filter(item -> filtertoplevelitem(word, item), items)
144134
push!(ret, GotoItem(path, item))
145135
end
146136
end
@@ -169,7 +159,7 @@ end
169159
function _searchtoplevelitems(mod::Module, pathitemsmaps::PathItemsMaps)
170160
entrypath, paths = modulefiles(mod) # Revise-like approach
171161
if entrypath !== nothing
172-
for p [entrypath; paths]
162+
for p in [entrypath; paths]
173163
_searchtoplevelitems(p, pathitemsmaps)
174164
end
175165
else # if Revise-like approach fails, fallback to CSTParser-based approach
@@ -188,14 +178,13 @@ function _searchtoplevelitems(path::String, pathitemsmaps::PathItemsMaps)
188178
push!(pathitemsmaps, pathitemsmap)
189179
end
190180

191-
# module-walk by CSTParser-based, looking for toplevel `installed` calls
181+
# module-walk based on CSTParser, looking for toplevel `installed` calls
192182
function _searchtoplevelitems(text::String, path::String, pathitemsmaps::PathItemsMaps)
193183
parsed = CSTParser.parse(text, true)
194184
items = toplevelitems(parsed, text)
195-
pathitemsmap = path => items
196-
push!(pathitemsmaps, pathitemsmap)
185+
push!(pathitemsmaps, path => items)
197186

198-
# looking for toplevel `installed` calls
187+
# looking for toplevel `include` calls
199188
for item in items
200189
if item isa ToplevelCall
201190
expr = item.expr
@@ -278,7 +267,7 @@ function regeneratesymbols()
278267
unloadedlen = length(unloaded)
279268
total = loadedlen + unloadedlen
280269

281-
for (i, mod) enumerate(Base.loaded_modules_array())
270+
for (i, mod) in enumerate(Base.loaded_modules_array())
282271
try
283272
modstr = string(mod)
284273
modstr == "__PackagePrecompilationStatementModule" && continue # will cause error
@@ -292,7 +281,7 @@ function regeneratesymbols()
292281
end
293282
end
294283

295-
for (i, pkg) enumerate(unloaded)
284+
for (i, pkg) in enumerate(unloaded)
296285
try
297286
path = Base.find_package(pkg)
298287
text = read(path, String)
@@ -311,27 +300,19 @@ end
311300

312301
## method goto
313302

314-
function methodgotoitems(mod, word)::Vector{GotoItem}
315-
ms = @errs getmethods(mod, word)
316-
if ms isa EvalError
317-
[]
318-
else
319-
map(GotoItem, aggregatemethods(ms))
320-
end
321-
end
303+
methodgotoitems(ml) = map(GotoItem, aggregatemethods(ml))
322304

323305
# aggregate methods with default arguments to the ones with full arguments
324-
aggregatemethods(f) = aggregatemethods(methods(f))
325-
aggregatemethods(ms::MethodList) = aggregatemethods(collect(ms))
326-
function aggregatemethods(ms::Vector{Method})
327-
ms = sort(ms, by = m -> m.nargs, rev = true)
306+
function aggregatemethods(ml)
307+
ms = collect(ml)
308+
sort!(ms, by = m -> m.nargs, rev = true)
328309
unique(m -> (m.file, m.line), ms)
329310
end
330311

331312
function GotoItem(m::Method)
332313
_, link = view(m)
333314
sig = sprint(show, m)
334-
text = replace(sig, r" in .* at .*$" => "")
315+
text = replace(sig, methodloc_regex => s"\g<sig>")
335316
file = link.file
336317
line = link.line - 1
337318
secondary = join(link.contents)

src/utils.jl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ end
111111
# string utilties
112112
# ---------------
113113

114+
"""
115+
strlimit(str::AbstractString, limit::Int = 30, ellipsis::AbstractString = "…")
116+
117+
Chops off `str` so that its _length_ doesn't exceed `limit`. The excessive part
118+
will be replaced by `ellipsis`.
119+
120+
!!! note
121+
The length of returned string will _never_ exceed `limit`.
122+
"""
114123
function strlimit(str::AbstractString, limit::Int = 30, ellipsis::AbstractString = "")
115124
will_append = length(str) > limit
116125

@@ -130,7 +139,11 @@ end
130139

131140
shortstr(val) = strlimit(string(val), 20)
132141

133-
# singleton type for undefined values
142+
"""
143+
Undefined
144+
145+
singleton type representing undefined values
146+
"""
134147
struct Undefined end
135148

136149
# get utilities

test/datatip.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@test localdatatip("l", 4) == 3 # line
2020
end
2121

22-
# remove dot accessors
22+
# ignore dot accessors
2323
let str = """
2424
function withdots(expr::CSTParser.EXPR)
2525
bind = CSTParser.bindingof(expr.args[1])
@@ -68,8 +68,8 @@
6868
end
6969
end
7070

71-
@testset "toplevel datatips" begin
72-
using Atom: topleveldatatip
71+
@testset "global datatips" begin
72+
using Atom: globaldatatip
7373

7474
## method datatip
7575
@eval Main begin
@@ -81,7 +81,7 @@
8181
datatipmethodtest() = nothing
8282
end
8383

84-
let datatip = topleveldatatip("Main", "datatipmethodtest")
84+
let datatip = globaldatatip("Main", "datatipmethodtest")
8585
@test datatip isa Vector
8686
firsttip = datatip[1]
8787
secondtip = datatip[2]
@@ -94,7 +94,7 @@
9494
## variable datatip
9595
@eval Main datatipvariabletest = "this string should be shown in datatip"
9696

97-
let datatip = topleveldatatip("Main", "datatipvariabletest")
97+
let datatip = globaldatatip("Main", "datatipvariabletest")
9898
@test datatip isa Vector
9999
firsttip = datatip[1]
100100
@test firsttip[:type] == :snippet

0 commit comments

Comments
 (0)