diff --git a/src/completions.jl b/src/completions.jl index 80dbb01d..6e1121f1 100644 --- a/src/completions.jl +++ b/src/completions.jl @@ -145,19 +145,14 @@ completionurl(c::REPLCompletions.ModuleCompletion) = begin mod, name = c.parent, c.mod val = getfield′(mod, name) if val isa Module # module info - parentmodule(val) == val || val ∈ (Main, Base, Core) ? - "atom://julia-client/?moduleinfo=true&mod=$(name)" : - "atom://julia-client/?moduleinfo=true&mod=$(mod).$(name)" + urimoduleinfo(parentmodule(val) == val || val ∈ (Base, Core) ? name : "$mod.$name") else - "atom://julia-client/?docs=true&mod=$(mod)&word=$(name)" + uridocs(mod, name) end end -completionurl(c::REPLCompletions.MethodCompletion) = - "atom://julia-client/?docs=true&mod=$(c.method.module)&word=$(c.method.name)" -completionurl(c::REPLCompletions.PackageCompletion) = - "atom://julia-client/?moduleinfo=true&mod=$(c.package)" -completionurl(c::REPLCompletions.KeywordCompletion) = - "atom://julia-client/?docs=true&mod=Main&word=$(c.keyword)" +completionurl(c::REPLCompletions.MethodCompletion) = uridocs(c.method.module, c.method.name) +completionurl(c::REPLCompletions.PackageCompletion) = urimoduleinfo(c.package) +completionurl(c::REPLCompletions.KeywordCompletion) = uridocs("Main", c.keyword) completionmodule(mod, c) = shortstr(mod) completionmodule(mod, c::REPLCompletions.ModuleCompletion) = shortstr(c.parent) @@ -183,8 +178,6 @@ completiontype(::REPLCompletions.DictCompletion) = "property" completiontype(::REPLCompletions.KeywordCompletion) = "keyword" completiontype(::REPLCompletions.PathCompletion) = "path" -ismacro(ct::AbstractString) = startswith(ct, '@') || endswith(ct, '"') - completionicon(c) = "" completionicon(c::REPLCompletions.ModuleCompletion) = begin ismacro(c.mod) && return "icon-mention" diff --git a/src/docs.jl b/src/docs.jl index d4909835..473a42f3 100644 --- a/src/docs.jl +++ b/src/docs.jl @@ -32,7 +32,7 @@ function renderitem(x) mod = getmodule(x.mod) name = Symbol(x.name) - r[:typ], r[:icon], r[:nativetype] = if (name !== :ans || mod === Base) && name ∈ keys(Docs.keywords) + r[:typ], r[:icon], r[:nativetype] = if (name !== :ans || mod === Base) && iskeyword(name) "keyword", "k", x.typ else val = getfield′(mod, name) diff --git a/src/utils.jl b/src/utils.jl index f4e6613e..53872c6e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,3 +1,20 @@ +# file utilities +# -------------- + +function isfile′(p) + try + isfile(p) + catch + false + end +end + +iswritablefile(file) = Base.uperm(file) == 0x06 +nonwritablefiles(files) = filter(!iswritablefile, files) + +# path utilities +# -------------- + include("path_matching.jl") isuntitled(p) = occursin(r"^(\.\\|\./)?untitled-[\d\w]+(:\d+)?$", p) @@ -12,14 +29,6 @@ function realpath′(p) end end -function isfile′(p) - try - isfile(p) - catch - false - end -end - using Pkg, OrderedCollections function finddevpackages() usage_file = joinpath(Pkg.logdir(), "manifest_usage.toml") @@ -99,6 +108,9 @@ function md_hlines(md) return MD(v) end +# string utilties +# --------------- + function strlimit(str::AbstractString, limit::Int = 30, ellipsis::AbstractString = "…") will_append = length(str) > limit @@ -122,34 +134,56 @@ shortstr(val) = strlimit(string(val), 20) struct Undefined end # get utilities +# ------------- + using CodeTools """ - getfield′(mod::Module, name::String, default = Undefined()) + getfield′(mod::Module, name::AbstractString, default = Undefined()) getfield′(mod::Module, name::Symbol, default = Undefined()) + getfield′(mod::AbstractString, name::Symbol, default = Undefined()) getfield′(object, name::Symbol, default = Undefined()) + getfield′(object, name::AbstractString, default = Undefined()) Returns the specified field of a given `Module` or some arbitrary `object`, or `default` if no such a field is found. """ -getfield′(mod::Module, name::String, default = Undefined()) = CodeTools.getthing(mod, name, default) +getfield′(mod::Module, name::AbstractString, default = Undefined()) = CodeTools.getthing(mod, name, default) getfield′(mod::Module, name::Symbol, default = Undefined()) = getfield′(mod, string(name), default) +getfield′(mod::AbstractString, name::Symbol, default = Undefined()) = getfield′(getmodule(mod), string(name), default) getfield′(@nospecialize(object), name::Symbol, default = Undefined()) = isdefined(object, name) ? getfield(object, name) : default +getfield′(@nospecialize(object), name::AbstractString, default = Undefined()) = isdefined(object, name) ? getfield(object, Symbol(name)) : default """ - getmodule(mod::String) - getmodule(parent::Union{Nothing, Module}, mod::String) + getmodule(mod::AbstractString) + getmodule(parent::Union{Nothing, Module}, mod::AbstractString) getmodule(code::AbstractString, pos; filemod) Calls `CodeTools.getmodule(args...)`, but returns `Main` instead of `nothing` in a fallback case. """ getmodule(args...) = (m = CodeTools.getmodule(args...)) === nothing ? Main : m -getmethods(mod::Module, word::String) = methods(CodeTools.getthing(mod, word)) -getmethods(mod::String, word::String) = getmethods(getmodule(mod), word) +""" + getmethods(mod::Module, word::AbstractString) + getmethods(mod::AbstractString, word::AbstractString) + +Returns the [`MethodList`](@ref) for `word`, which is bound within `mod` module. +""" +getmethods(mod::Module, word::AbstractString) = methods(CodeTools.getthing(mod, word)) +getmethods(mod::AbstractString, word::AbstractString) = getmethods(getmodule(mod), word) + +""" + getdocs(mod::Module, word::AbstractString, fallbackmod::Module = Main) + getdocs(mod::AbstractString, word::AbstractString, fallbackmod::Module = Main) + +Retrieves docs for `mod.word` with [`@doc`](@ref) macro. If `@doc` is not available + within `mod` module, `@doc` will be evaluated in `fallbackmod` module if possible. -getdocs(mod::Module, word::String, fallbackmod::Module = Main) = begin - md = if Symbol(word) in keys(Docs.keywords) +!!! note + You may want to run [`cangetdocs`](@ref) in advance. +""" +getdocs(mod::Module, word::AbstractString, fallbackmod::Module = Main) = begin + md = if iskeyword(word) Core.eval(Main, :(@doc($(Symbol(word))))) else docsym = Symbol("@doc") @@ -164,13 +198,39 @@ getdocs(mod::Module, word::String, fallbackmod::Module = Main) = begin end md_hlines(md) end -getdocs(mod::String, word::String, fallbackmod::Module = Main) = +getdocs(mod::AbstractString, word::AbstractString, fallbackmod::Module = Main) = getdocs(getmodule(mod), word, fallbackmod) +""" + cangetdocs(mod::Module, word::Symbol) + cangetdocs(mod::Module, word::AbstractString) + cangetdocs(mod::AbstractString, word::Union{Symbol, AbstractString}) + +Checks if the documentation bindings for `mod.word` is resolved and `mod.word` + is not deprecated. +""" cangetdocs(mod::Module, word::Symbol) = Base.isbindingresolved(mod, word) && !Base.isdeprecated(mod, word) -cangetdocs(mod::Module, word::String) = cangetdocs(mod, Symbol(word)) +cangetdocs(mod::Module, word::AbstractString) = cangetdocs(mod, Symbol(word)) +cangetdocs(mod::AbstractString, word::Union{Symbol, AbstractString}) = cangetdocs(getmodule(mod), word) + +# is utilities +# ------------ + +iskeyword(word::Symbol) = word in keys(Docs.keywords) +iskeyword(word::AbstractString) = iskeyword(Symbol(word)) + +ismacro(ct::AbstractString) = startswith(ct, '@') || endswith(ct, '"') +ismacro(f::Function) = startswith(string(methods(f).mt.name), "@") + +# uri utilties +# ------------ + +uriopen(file, line = 0) = "atom://julia-client/?open=true&file=$(file)&line=$(line)" +uridocs(mod, word) = "atom://julia-client/?docs=true&mod=$(mod)&word=$(word)" +urigoto(mod, word) = "atom://julia-client/?goto=true&mod=$(mod)&word=$(word)" +urimoduleinfo(mod) = "atom://julia-client/?moduleinfo=true&mod=$(mod)" #= module file detections diff --git a/src/workspace.jl b/src/workspace.jl index 09757643..64613941 100644 --- a/src/workspace.jl +++ b/src/workspace.jl @@ -56,5 +56,3 @@ wsicon(mod, name, ::Expr) = "icon-code" wsicon(mod, name, ::Symbol) = "icon-code" wsicon(mod, name, ::Exception) = "icon-bug" wsicon(mod, name, ::Undefined) = "icon-circle-slash" - -ismacro(f::Function) = startswith(string(methods(f).mt.name), "@") diff --git a/test/utils.jl b/test/utils.jl index 6db020d9..45480a5a 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -48,7 +48,7 @@ end @test Atom.finddevpackages() isa AbstractDict end -#TODO: baselink, edit +# TODO: baselink, edit cd(old_pwd) end @@ -79,18 +79,38 @@ end end end -@testset "limiting excessive strings" begin - using Atom: strlimit +@testset "is utilities" begin + @testset "iskeyword" begin + using Atom: iskeyword - # only including ASCII - @test strlimit("julia", 5) == "julia" - @test strlimit("julia", 4) == "jul…" - @test strlimit("Julia in the Nutshell", 21, " ...") == "Julia in the Nutshell" - @test strlimit("Julia in the Nutshell", 20, " ...") == "Julia in the Nut ..." + @test iskeyword(:begin) + @test iskeyword("begin") + @test !iskeyword(:iskeyword) + end + + @testset "ismacro" begin + using Atom: ismacro + + @test ismacro("@view") + @test ismacro("r\"") + @test ismacro(getfield(Main, Symbol("@view"))) + end +end - # including Unicode: should respect _length_ of strings, not code units - @test strlimit("jμλια", 5) == "jμλια" - @test strlimit("jμλια", 4) == "jμλ…" - @test strlimit("Jμλια in the Nutshell", 21, " ...") == "Jμλια in the Nutshell" - @test strlimit("Jμλια in the Nutshell", 20, " ...") == "Jμλια in the Nut ..." +@testset "string utilities" begin + @testset "limiting excessive strings" begin + using Atom: strlimit + + # only including ASCII + @test strlimit("julia", 5) == "julia" + @test strlimit("julia", 4) == "jul…" + @test strlimit("Julia in the Nutshell", 21, " ...") == "Julia in the Nutshell" + @test strlimit("Julia in the Nutshell", 20, " ...") == "Julia in the Nut ..." + + # including Unicode: should respect _length_ of strings, not code units + @test strlimit("jμλια", 5) == "jμλια" + @test strlimit("jμλια", 4) == "jμλ…" + @test strlimit("Jμλια in the Nutshell", 21, " ...") == "Jμλια in the Nutshell" + @test strlimit("Jμλια in the Nutshell", 20, " ...") == "Jμλια in the Nut ..." + end end