505505
506506# Take a file-system path and try to form a concise representation of it
507507# based on the package ecosystem
508- function short_path (spath:: Symbol , filenamecache:: Dict{Symbol, Tuple{String,String}} )
508+ function short_path (spath:: Symbol , filenamecache:: Dict{Symbol, Tuple{String,String,String }} )
509509 return get! (filenamecache, spath) do
510510 path = Base. fixup_stdlib_path (string (spath))
511+ possible_base_path = normpath (joinpath (Sys. BINDIR, Base. DATAROOTDIR, " julia" , " base" , path))
511512 if isabspath (path)
512513 if ispath (path)
513514 # try to replace the file-system prefix with a short "@Module" one,
@@ -524,20 +525,21 @@ function short_path(spath::Symbol, filenamecache::Dict{Symbol, Tuple{String,Stri
524525 pkgid = Base. project_file_name_uuid (project_file, " " )
525526 isempty (pkgid. name) && return path # bad Project file
526527 # return the joined the module name prefix and path suffix
527- path = path[nextind (path, sizeof (root)): end ]
528- return string (" @" , pkgid. name), path
528+ _short_path = path[nextind (path, sizeof (root)): end ]
529+ return path, string (" @" , pkgid. name), _short_path
529530 end
530531 end
531532 end
532533 end
533- return " " , path
534- elseif isfile (joinpath (Sys . BINDIR, Base . DATAROOTDIR, " julia " , " base " , path) )
534+ return path, " " , path
535+ elseif isfile (possible_base_path )
535536 # do the same mechanic for Base (or Core/Compiler) files as above,
536537 # but they start from a relative path
537- return " @Base" , normpath (path)
538+ return possible_base_path, " @Base" , normpath (path)
538539 else
539540 # for non-existent relative paths (such as "REPL[1]"), just consider simplifying them
540- return " " , normpath (path) # drop leading "./"
541+ path = normpath (path)
542+ return " " , " " , path # drop leading "./"
541543 end
542544 end
543545end
@@ -758,6 +760,8 @@ function parse_flat(::Type{T}, data::Vector{UInt64}, lidict::Union{LineInfoDict,
758760 return (lilist, n, m, totalshots, nsleeping)
759761end
760762
763+ const FileNameMap = Dict{Symbol,Tuple{String,String,String}}
764+
761765function flat (io:: IO , data:: Vector{UInt64} , lidict:: Union{LineInfoDict, LineInfoFlatDict} , cols:: Int , fmt:: ProfileFormat ,
762766 threads:: Union{Int,AbstractVector{Int}} , tasks:: Union{UInt,AbstractVector{UInt}} , is_subsection:: Bool )
763767 lilist, n, m, totalshots, nsleeping = parse_flat (fmt. combine ? StackFrame : UInt64, data, lidict, fmt. C, threads, tasks)
@@ -768,7 +772,7 @@ function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfo
768772 m = m[keep]
769773 end
770774 util_perc = (1 - (nsleeping / totalshots)) * 100
771- filenamemap = Dict {Symbol,Tuple{String,String}} ()
775+ filenamemap = FileNameMap ()
772776 if isempty (lilist)
773777 if is_subsection
774778 Base. print (io, " Total snapshots: " )
@@ -790,9 +794,34 @@ function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfo
790794 return false
791795end
792796
797+ # make a terminal-clickable link to the file and linenum.
798+ # Similar to `define_default_editors` in `Base.Filesystem` but for creating URIs not commands
799+ function editor_link (path:: String , linenum:: Int )
800+ editor = get (ENV , " JULIA_EDITOR" , " " )
801+
802+ if editor == " code"
803+ return " vscode://file/$path :$linenum "
804+ elseif editor == " subl" || editor == " sublime_text"
805+ return " subl://$path :$linenum "
806+ elseif editor == " idea" || occursin (" idea" , editor)
807+ return " idea://open?file=$path &line=$linenum "
808+ elseif editor == " pycharm"
809+ return " pycharm://open?file=$path &line=$linenum "
810+ elseif editor == " atom"
811+ return " atom://core/open/file?filename=$path &line=$linenum "
812+ elseif editor == " emacsclient"
813+ return " emacs://open?file=$path &line=$linenum "
814+ elseif editor == " vim" || editor == " nvim"
815+ return " vim://open?file=$path &line=$linenum "
816+ else
817+ # TODO : convert the path to a generic URI (line numbers are not supported by generic URI)
818+ return path
819+ end
820+ end
821+
793822function print_flat (io:: IO , lilist:: Vector{StackFrame} ,
794823 n:: Vector{Int} , m:: Vector{Int} ,
795- cols:: Int , filenamemap:: Dict{Symbol,Tuple{String,String}} ,
824+ cols:: Int , filenamemap:: FileNameMap ,
796825 fmt:: ProfileFormat )
797826 if fmt. sortedby === :count
798827 p = sortperm (n)
@@ -804,7 +833,7 @@ function print_flat(io::IO, lilist::Vector{StackFrame},
804833 lilist = lilist[p]
805834 n = n[p]
806835 m = m[p]
807- pkgnames_filenames = Tuple{String,String}[short_path (li. file, filenamemap) for li in lilist]
836+ pkgnames_filenames = Tuple{String,String,String }[short_path (li. file, filenamemap) for li in lilist]
808837 funcnames = String[string (li. func) for li in lilist]
809838 wcounts = max (6 , ndigits (maximum (n)))
810839 wself = max (9 , ndigits (maximum (m)))
@@ -815,7 +844,7 @@ function print_flat(io::IO, lilist::Vector{StackFrame},
815844 li = lilist[i]
816845 maxline = max (maxline, li. line)
817846 maxfunc = max (maxfunc, textwidth (funcnames[i]))
818- maxfile = max (maxfile, sum (textwidth, pkgnames_filenames[i]) + 1 )
847+ maxfile = max (maxfile, sum (textwidth, pkgnames_filenames[i][ 2 : 3 ] ) + 1 )
819848 end
820849 wline = max (5 , ndigits (maxline))
821850 ntext = max (20 , cols - wcounts - wself - wline - 3 )
@@ -843,7 +872,7 @@ function print_flat(io::IO, lilist::Vector{StackFrame},
843872 Base. print (io, " [any unknown stackframes]" )
844873 end
845874 else
846- pkgname, file = pkgnames_filenames[i]
875+ path, pkgname, file = pkgnames_filenames[i]
847876 isempty (file) && (file = " [unknown file]" )
848877 pkgcolor = get! (() -> popfirst! (Base. STACKTRACE_MODULECOLORS), PACKAGE_FIXEDCOLORS, pkgname)
849878 Base. printstyled (io, pkgname, color= pkgcolor)
@@ -853,7 +882,12 @@ function print_flat(io::IO, lilist::Vector{StackFrame},
853882 Base. print (io, " /" )
854883 wpad -= 1
855884 end
856- Base. print (io, rpad (file_trunc, wpad, " " ), " " )
885+ if isempty (path)
886+ Base. print (io, rpad (file_trunc, wpad, " " ))
887+ else
888+ link = editor_link (path, li. line)
889+ Base. print (io, rpad (styled " {link=$link:$file_trunc}" , wpad, " " ))
890+ end
857891 Base. print (io, lpad (li. line > 0 ? string (li. line) : " ?" , wline, " " ), " " )
858892 fname = funcnames[i]
859893 if ! li. from_c && li. linfo != = nothing
902936# mimics Stacktraces
903937const PACKAGE_FIXEDCOLORS = Dict {String, Any} (" @Base" => :gray , " @Core" => :gray )
904938
905- function tree_format (frames:: Vector{<:StackFrameTree} , level:: Int , cols:: Int , maxes, filenamemap:: Dict{Symbol,Tuple{String,String}} , showpointer:: Bool )
939+ function tree_format (frames:: Vector{<:StackFrameTree} , level:: Int , cols:: Int , maxes, filenamemap:: FileNameMap , showpointer:: Bool )
906940 nindent = min (cols>> 1 , level)
907941 ndigoverhead = ndigits (maxes. overhead)
908942 ndigcounts = ndigits (maxes. count)
@@ -937,7 +971,7 @@ function tree_format(frames::Vector{<:StackFrameTree}, level::Int, cols::Int, ma
937971 else
938972 fname = string (li. func)
939973 end
940- pkgname, filename = short_path (li. file, filenamemap)
974+ path, pkgname, filename = short_path (li. file, filenamemap)
941975 if showpointer
942976 fname = string (
943977 " 0x" ,
@@ -947,14 +981,16 @@ function tree_format(frames::Vector{<:StackFrameTree}, level::Int, cols::Int, ma
947981 end
948982 pkgcolor = get! (() -> popfirst! (Base. STACKTRACE_MODULECOLORS), PACKAGE_FIXEDCOLORS, pkgname)
949983 remaining_path = ltruncate (filename, widthfile - textwidth (pkgname) - 1 )
950- strs[i] = Base. annotatedstring (stroverhead, " ╎" , base, strcount, " " ,
951- styled " {$pkgcolor:$pkgname}" ,
952- ! isempty (pkgname) && ! startswith (remaining_path, " /" ) ? " /" : " " ,
953- remaining_path,
954- " :" ,
955- li. line == - 1 ? " ?" : string (li. line),
956- " ; " ,
957- fname)
984+ linenum = li. line == - 1 ? " ?" : string (li. line)
985+ slash = (! isempty (pkgname) && ! startswith (remaining_path, " /" )) ? " /" : " "
986+ styled_path = styled " {$pkgcolor:$pkgname}$slash$remaining_path:$linenum"
987+ rich_file = if isempty (path)
988+ styled_path
989+ else
990+ link = editor_link (path, li. line)
991+ styled " {link=$link:$styled_path}"
992+ end
993+ strs[i] = Base. annotatedstring (stroverhead, " ╎" , base, strcount, " " , rich_file, " ; $fname " )
958994 end
959995 else
960996 strs[i] = string (stroverhead, " ╎" , base, strcount, " [unknown stackframe]" )
@@ -1118,7 +1154,7 @@ end
11181154# avoid stack overflows.
11191155function print_tree (io:: IO , bt:: StackFrameTree{T} , cols:: Int , fmt:: ProfileFormat , is_subsection:: Bool ) where T
11201156 maxes = maxstats (bt)
1121- filenamemap = Dict {Symbol,Tuple{String,String}} ()
1157+ filenamemap = FileNameMap ()
11221158 worklist = [(bt, 0 , 0 , AnnotatedString (" " ))]
11231159 if ! is_subsection
11241160 Base. print (io, " Overhead ╎ [+additional indent] Count File:Line; Function\n " )
0 commit comments