Julia: O que é necessário para incluir ClearStacktrace.jl no Base?

Criado em 25 mai. 2020  ·  99Comentários  ·  Fonte: JuliaLang/julia

O título diz tudo. Acho que descobri um bom formato para apresentar rastreamentos de pilha e acho que todos os usuários podem se beneficiar com isso. Atualmente está em ClearStacktrace.jl e depende de Crayons.jl para as cores.

Eu quero saber o que posso fazer para tornar este PR-pronto. Não sei como Julia Base lida com cores REPL, quais tenho permissão para usar, como as acesso sem Crayons etc. Também não sei se pode haver complexidades de pilha específicas do sistema rastros que esqueci, testando isso apenas em uma máquina Windows e um Mac.

A ideia básica é ter três colunas, função, módulo e assinatura. A função e o módulo juntos devem basicamente sempre caber em um REPL horizontalmente, enquanto as assinaturas podem ser muito longas e, portanto, podem transbordar para novas linhas, enquanto permanecem intactos ao redimensionar o REPL (em comparação com um layout de tabela mais complexo, porém frágil). Os caminhos de código recebem uma nova linha cada e também podem estourar se forem muito longos. Isso mantém os links clicáveis ​​em Atom / VSCode e similares intactos.

Apenas para referência, aqui está a comparação antes e depois do README:
Antes:
grafik
Depois de:
grafik

Comentários muito úteis

outra iteração: eu tentei colocar nomes de variáveis ​​lá, embora tudo pareça super hacky para mim, já que não estou muito familiarizado com todos os detalhes internos. Eu recolori os módulos porque parecia obter uma resposta positiva em geral, embora seja discutível como as cores devem ser atribuídas. Mas com as cores, sinto que o módulo de uma entrada se choca um pouco com o nome da função da próxima. Então, introduzi quebras de linha. Torna tudo mais longo, é claro, mas eu meio que gosto do espaço adicional para respirar e clareza

grafik

Todos 99 comentários

Seria muito bom ter isso. Ou pelo menos uma versão reduzida que melhora a impressão de rastreamentos de pilha por padrão no Base.

Sou totalmente a favor. A principal coisa que não entendo são as cores nas assinaturas. Eles parecem ... aleatórios? Eu acho que as diferentes cores existem para tornar mais fácil escolher os diferentes elementos, mas é um pouco estranho. Que tal cor == profundidade de aninhamento? Acho que a principal estranheza está, por exemplo, em Tuple{Symbol,Symbol} onde os dois Symbol s são de cores diferentes.

Há outra coisa que eu realmente gostaria de ver:

julia> foo(x::T, y::T) where T = error("whoops")
foo (generic function with 1 method)

julia> function bar()
           a = rand(3, 5)
           b = view(a, :, 2:3)
           c = reinterpret(Float32, b)
           foo(c, c)
       end
bar (generic function with 1 method)

julia> bar()
ERROR: whoops
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] foo(::Base.ReinterpretArray{Float32,2,Float64,SubArray{Float64,2,Array{Float64,2},Tuple{Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}}, ::Base.ReinterpretArray{Float32,2,Float64,SubArray{Float64,2,Array{Float64,2},Tuple{Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}}) at ./REPL[1]:1
 [3] bar() at ./REPL[2]:5
 [4] top-level scope at REPL[3]:1

mas desde

julia> m = first(methods(foo))
foo(x::T, y::T) where T in Main at REPL[1]:1

julia> m.sig
Tuple{typeof(foo),T,T} where T

parece que devemos ser capazes de imprimir a entrada 2 como algo como

[2] foo(::T, ::T) where T = Base.ReinterpretArray{Float32,2,Float64,SubArray{Float64,2,Array{Float64,2},Tuple{Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}}

Um pequeno comentário, mas para mim talvez seja um pouco demais de cor "salada". O contador do stackframe sendo azul, todas as cores diferentes na assinatura etc. Para as cores básicas até agora, usamos apenas as 16 cores do sistema (8 + 8 cores claras) e isso torna a coloração extravagante um pouco mais difícil.

Boas sugestões em geral. Talvez alguns comentários sobre por que fiz as escolhas de cores até agora:

Os números são azuis porque são importantes, portanto, não devem estar esmaecidos, mas torná-los brancos me pareceu que eles estavam lutando por atenção com os nomes das funções.

Os módulos usam as cores primárias em um ciclo, acho isso muito útil porque permite filtrar rapidamente o traço para os módulos relevantes. Isso tem muito a ver com o formato de rastreamento de pilha atual. A ideia é que as pessoas sejam forçadas a ler o mínimo possível para encontrar o que procuram. (Eu fiz principalmente pesquisas sobre movimentos oculares e atenção na minha vida acadêmica até agora, então esses aspectos são importantes para mim;))

As cores das assinaturas são muito discutíveis. Os da imagem acima foram escolhidos para serem apenas ligeiramente diferentes do cinza escuro. Ao mesmo tempo, eu queria que os diferentes componentes semânticos fossem discrimináveis. É por isso que todos os tipos e parâmetros de tipo (chaves e vírgulas) causam uma mudança de cor. Existem três cores, o que significa que não haverá dois vizinhos com a mesma cor. Descobri que é muito difícil distinguir palavras isoladas em assinaturas de tipos enormes se todas forem apenas brancas. Os tons ligeiramente diferentes ajudam muito, mas também tornam tudo um pouco mais barulhento, visualmente.

Como o conjunto de 16 cores não possui cores que são apenas ligeiramente diferentes do cinza escuro, provavelmente não é algo que pode ser incluído no Base. Em uma atualização recente, substituí as cores por três tons de cinza ligeiramente diferentes do conjunto ANSI, mas acho que mesmo isso não será compatível em todos os lugares.

Ah, também a cor da estrutura da pilha e as cores do módulo, bem como o cinza escuro e o branco, são todas cores do sistema. É apenas o meu tema VSCode particular que os renderiza como você vê aqui.

Seria ótimo ter isso.

Ao escanear tipos de argumento, geralmente é muito difícil descobrir onde um para e o próximo começa. Colorir o nível superior de forma diferente pode ser muito útil aqui, e talvez até numerar os argumentos:

 [3] (_1 :: DataFrames.var "# fun ## 309 #" { ... mais leve ... }, _2 :: NamedTuple { ... mais leve ... }, _3 :: Int64})

Devemos ser capazes de mostrar os nomes dos argumentos, como fazemos na impressão de methods(f) .

como é isso? Substituí as cores da assinatura por cinza escuro, mas :: é branco, portanto, fica saliente onde os tipos de argumento começam. especialmente útil com o tipo de monstro na posição 11
grafik

Eu gosto disso. Será ainda melhor com nomes de argumentos.

É uma pena que a ordem "função - módulo - argumentos" funcione tão bem para o layout, mas não faça sentido semanticamente. Eu não tenho uma solução; claramente é melhor que a função venha primeiro. Talvez o módulo possa ir para a próxima linha antes do local do arquivo? Afinal, faz parte da localização. Então, talvez também não precisemos do cabeçalho.

Acho que também podemos colocar os números em texto normal e os nomes das funções em negrito e / ou branco.

O nome do arquivo no final do codepath (e possivelmente o número da linha) poderia obter sua própria cor mais clara também?

Talvez o módulo possa ir para a próxima linha antes do local do arquivo? Afinal, faz parte da localização. Então, talvez também não precisemos do cabeçalho.

Vou experimentar algumas coisas com essas sugestões.

O nome do arquivo no final do codepath (e possivelmente o número da linha) poderia obter sua própria cor mais clara também?

Tecnicamente pode, é claro;) É sempre uma troca entre melhor visibilidade e ruído que chama a atenção.

Por que não escrever os módulos primeiro, já que eles fazem parte do nome completo da função? Talvez com cores possa ser claro o suficiente apenas para escrever Core.eval etc, ou eles ainda podem ser alinhados como colunas. (Ou talvez você tenha tentado isso e ficou horrível.)

Pensamento muito louco: se imprimir os tipos de argumento ocuparia mais de uma linha, imprima a assinatura dobrada e use REPL.TerminalMenus para permitir que os visualizadores a expandam. Algo como:

julia> run_command(args...)
    Function    Arguments              Location
[1] f           (x::Int, y::Float64)   MyModule1, /path/to/file1.jl: 15
[2] g          +(...)                  MyModule2, /path/to/file2.jl: 64
...

julia> lasttrace()
# presents the above in menu form, allowing user to expand line 2

Relacionado: # 35915 (que pretendo usar para criar um menu de árvore dobrada, https://github.com/JuliaCollections/FoldingTrees.jl).

Como fica com o codepath reorganizado nesta ordem: LineNumber Filename Path
Então seus olhos têm uma localização estável para procurar o número da linha e o nome do arquivo sem a necessidade de cor extra para eles (pareço estar sempre procurando o nome do arquivo e o número da linha enterrados nos detalhes, você pode dizer?)
E para o trabalho REPL interativo, adoro a ideia dobrada acima :)

Por que não escrever os módulos primeiro, já que eles fazem parte do nome completo da função

Sim, eu meio que gosto da ideia, acho que comecei sem cor e não funcionou muito bem, mas assim é na verdade bastante legível. A seguir tentarei sem colunas, embora as colunas sejam sempre legais porque guiam seus olhos.

grafik

Não tenho certeza sobre isso; o nome da função é uma informação muito mais importante do que o módulo. Para mim, pelo menos, a informação mais importante é definitivamente o nome da função, o nome do arquivo e o número da linha. Além disso, para scripts, a coluna da esquerda seria muito Main ou em branco, o que não é ideal para esse caso de uso.

Acho que quando estou lendo rastreamentos de pilha, procuro principalmente a última chamada em meu código antes de ir para o código Base / pacote, já que geralmente o bug está no meu código. Nesse caso, o módulo é muito importante e a chamada de função não é o que estou verificando primeiro.

Que tal agora:

[1]  get_ticklabels   ::AbstractPlotting.Automatic, ::Int64
     in MakieLayout at ~/.julia/packages/MakieLayout/COfBu/src/lineaxis.jl:372

Os nomes dos módulos ainda se alinham para que sejam fáceis de digitalizar.

Na verdade, eu tinha tentado algo parecido com isso. Eu pintei os próprios nomes de função desta vez, então eles ainda são os mais salientes, mas carregam as informações do Módulo em suas cores. Portanto, ainda há um agrupamento óbvio, mas com menos foco em todos os nomes dos módulos.

grafik

A primeira vez que vi isso provavelmente ficaria louco tentando descobrir o que as cores significavam: alegria: se houver cores por módulo, parece melhor apenas aplicar a cor ao nome do módulo.

Eu gosto dos nomes dos módulos, eles ficam um pouco perdidos nesta última imagem.

Se eles estiverem na parte inferior, talvez torná-los da mesma cor brilhante conectaria as coisas? (E deixe claro onde você cruzou o limite de um módulo.) E, talvez, se o nome do módulo aparecer no caminho, ele poderia simplesmente ser destacado lá - isso também moveria o nome colorido para fora da borda esquerda onde estão as funções.

Para se inspirar, eu posto algumas imagens da última vez que o ajuste da formatação do stacktrace surgiu.

bild

bild

Freqüentemente, o pacote exato não é tanto de interesse quanto a natureza do código: é o núcleo julia, uma biblioteca que estou usando, a biblioteca que estou desenvolvendo ou um script? Poderíamos ter quatro cores: core / base / dev / stdlib, pacotes, pacotes atualmente ]dev ed e todo o resto (incluindo script / REPL). Algumas dessas distinções são bastante arbitrárias e a classificação é um pouco frágil, mas colorir o método com base nisso (pelo menos para mim) maximizaria as informações sem exibir nenhum texto extra e mantendo um conjunto de cores pequeno e fácil de memorizar.

Para mim, as cores são mais sobre mudanças de limites, onde o código de um módulo começa e o outro termina. É por isso que provavelmente não atribuiria um significado inerente a eles. Por outro lado, isso pode ser confuso.

Tentei simplificar mais e removi as colunas novamente porque sem a coluna do módulo elas não são mais tão úteis. Então, primeiro eu pintei apenas o número da linha por módulos, mas isso ainda deixou o nome do arquivo não tão visível, o que muitos comentários disseram ser importante. Então eu pintei isso na cor do módulo também. O nome do módulo em si não é colorido porque está muito próximo do nome da função e é muito barulhento.

Aqui está a versão com números e nomes de arquivos coloridos:

grafik

aqui sem nomes de arquivos coloridos

grafik

Você também pode tentar colorir o caminho completo do arquivo?

Algumas ideias...
Nome do arquivo e número da linha em um local consistente com espaços duplos para destacá-los.
A cor do nível da pilha e a cor do módulo combinam entre si para emparelhar visualmente as linhas relacionadas.
Nome do arquivo, número da linha e caminho em tons ligeiramente diferentes dos tipos de parâmetro para distinguir sem confusão.
stacktrace
[As cores neste exemplo são arbitrárias, pensando mais em como suas posições se relacionam.
A falta de níveis de pilha no exemplo e a lista de tipo de parâmetro encurtada no último item são porque eu digitei o exemplo à mão.]

Código usado para produzir o exemplo acima, caso alguém queira brincar com ele:

function main()

    errors = [
              ("1", "get_ticklabels", ("AbstractPlotting.Automatic", "Int64"), "372", "lineaxis.jl", "MakieLayout", "~/.julia/packages/MakieLayout/COfBu/src")
              ("2", "get_ticklabels", ("AbstractPlotting.Automatic", "Int64"), "351", "lineaxis.jl", "MakieLayout", "~/.julia/packages/MakieLayout/COfBu/src")
              ("3", "#105", ("AbstractPlotting.Automatic",), "152", "lineaxis.jl", "MakieLayout", "~/.julia/packages/MakieLayout/COfBu/src")
              ("8", "OnUpdate", ("Tuple{Float32,Float32}",), "218", "Observables.jl", "Observables", "~/.julia/packages/Observables/0wrF6/src")
              ("9", "#setindex!#5", ("Observables.var\"#6#8\"",), "138", "Observables.jl", "Observables", "~/.julia/packages/Observables/0wrF6/src")
              ("10", "setindex!", ("Observables.Observable{Any}",), "126", "Observables.jl", "Observables", "~/.julia/packages/Observables/0wrF6/src")
              ("11", "#LineAxis#95", ("Base.Iterators.Pairs{Symbol,Observables.Observable,NTuple{28,Symbol},NamedTuple{(:endpoints, :limits, :flipped, :ticklabelrotation, :ticklabelalign, :lables
ize, :labelpadding, :ticklabelpad, :labelvisible, :label, :labelfont, :ticklabelfont, :ticklabelcolor}",), "270", "lineaxis.jl", "MakieLayout", "~/.julia/packages/MakieLayout/COfBu/src/lobjects")
    ]

    println()
    for (idx, err) in enumerate(errors)
        # Module color
        mc = idx <= 3 ? :light_red : (idx >= 7 ? :light_red : :light_yellow)
        # Path color
        pc = :blue
        printstyled("[", color=mc)
        printstyled("$(err[1])", color=mc)     # errorno
        printstyled("]  ", color=mc)
        printstyled("$(err[5])", color=pc)     # filename
        printstyled(":", color=pc)             # colon
        printstyled("$(err[4])", color=pc)     # lineno
        printstyled("  $(err[7])", color=pc)   # path
        println()
        printstyled("$(err[6]) ", color=mc)    # module
        printstyled("$(err[2]) ", color=:bold) # function
        printstyled("(", color=:light_blue)    # param types
        for t in err[3]
            printstyled("::", color=:white)
            printstyled("$t", color=:light_blue)
        end
        printstyled(")", color=:light_blue)
        println()
    end
end

O problema com isso é que isso não deixa os links clicáveis ​​no Atom / VSCode, etc. Isso é algo que eu uso com frequência e acredito que outros também fazem. Na verdade, eu até expandir explicitamente os caminhos básicos para torná-los clicáveis. É claro que isso imprime mais. Mas acho que aumenta a utilidade. Eu sei que há alguma maneira de pular para empilhar entradas de rastreamento por número no REPL com algum atalho, mas isso é muito menos intuitivo do que apenas clicar em um link, na minha opinião.

Pode fazer sentido aproveitar a oportunidade para unificar a forma como as informações da linha são impressas com o sistema de registro, por exemplo:

bild

Além disso, observe que o sistema de registro contrai o homedir

julia> include("foo.jl")
┌ Warning: foo
└ @ Main ~/julia/foo.jl:1

Acho que há duas ideias centrais na proposta aqui:

  • Coloque as informações da linha em uma linha separada da assinatura (e talvez a delimite com alguma cor para facilitar a localização).
  • Mostre o módulo.

Portanto, aqui está uma tentativa um pouco mais conservadora com essas duas ideias:

bild

Uma versão com nomes de variáveis ​​adicionados e coloridos apenas nos nomes dos módulos (mas de outra forma correspondendo a como @info imprime caminhos):

Screenshot 2020-05-28 at 15 55 56

Este não tem assinaturas de tipo muito longas, mas talvez ter nomes de variáveis ​​como este (se isso for possível?) Tornará mais fácil localizar o tipo mais externo de cada argumento.

Se houver três níveis neutros disponíveis (como negrito / normal / cinza), imprimir o caminho do arquivo de maneira mais leve do que a assinatura parece (IMO) uma boa maneira de impedir que as coisas funcionem juntas. (Isso também é o que @info faz.)

Adaptando o código de @timotaularson

 deixar
 printstyled ("\ njulia>", cor =: verde)
 println ("f ()")
 printstyled ("" "ERROR: DimensionMismatch (" A tem dimensões (1,2), mas B tem dimensões (3,4) ")" "", color =: light_red)
 println ("\ nStacktrace:")
 para (idx, err) em enumerar (erros)
 mc = idx <= 3? : blue: (idx> = 7?: blue:: yellow) # Cor do módulo
 printstyled ("[$ (err [1])]", cor =: normal, negrito = verdadeiro) # errorno
 printstyled (err [2], color =: normal, bold = true) # função
 printstyled ("(", color =: normal) # tipos de parâmetros
 para (i, t) em enumerar (err [3])
 i! = 1 && printstyled (",", cor =: normal)
 printstyled (rand ('a': 'z') ^ 2, color =: light_black)
 printstyled ("::", cor =: normal)
 printstyled ("$ t", color =: normal)
 fim
 estilo de impressão (")", cor =: normal)
 println ()
 printstyled ("@", color =: light_black)
 printstyled (err [6], color = mc) # módulo
 printstyled ("$ (err [7]) / $ (err [5]): $ (err [4])", color =: light_black) # path
 println ()
 fim
 fim

Acho que há duas ideias centrais na proposta aqui:

* Have the line info on a separate line from the signature (and perhaps delimited it with some color to make it easier to find).

* Show the module.

Portanto, aqui está uma tentativa um pouco mais conservadora com essas duas ideias:

Eu gosto disso. Acho que o termo é "mate seus queridos", e meu querido pode ser a cor. Mas posso ver como algo atenuado pode ser mais adequado para algo tão básico. Mudei um pouco para que o nome do módulo e o nome da função fiquem alinhados, o que cria uma linha de contraste vertical quase semelhante a uma coluna. Isso deve ajudar a encontrar as informações relevantes. Embora eu ainda ache que não colocaria os tipos em branco, porque eles ficam muito bagunçados. Se tivéssemos nomes de variáveis, talvez aqueles em branco ficassem bem.

grafik

Eu gosto disso. Acho que o termo é "mate seus queridos", e meu querido pode ser a cor.

Para que você saiba que conheço sua situação, https://github.com/JuliaLang/julia/pull/18228. Esse é um assunto muito divertido de andar de bicicleta e todo mundo tem um balde de tinta, hein. Como você pode ver, esse RP também começou de forma bastante ambiciosa, mas aos poucos foi ficando cada vez mais conservador, heh.

Para mim, a última proposta é definitivamente uma melhoria em relação ao status quo. Duas coisas que estou pensando:

  • Se possível, seria bom ter algum tipo de "gancho" para as informações da linha, porque elas tendem a se misturar com a assinatura quando a assinatura é longa.
  • Devemos provavelmente alinhar os números do stackframe quando houver mais de 10 deles:
    [ 8] [ 9] [10] [11]
    etc. Isso faria com que @ também se alinhasse.

Eu gosto, acho que estamos chegando a algum lugar. Ser criterioso com as cores é sempre bom, pois isso expõe menos área de superfície para bicicletas, além de diminuir o risco de uma cor ficar ilegível no terminal de alguém.

Espaçar as coisas é bom, mas infelizmente a sintaxe f (...) é explicitamente proibida, então acho que precisamos remover o espaço ou remover os parênteses.

A amostra mais recente geralmente parece boa, mas apoio a ideia:

Se possível, seria bom ter algum tipo de "gancho" para as informações da linha, porque elas tendem a se misturar com a assinatura quando a assinatura é longa.

E espero que o gancho tenha uma cor ou (de preferência) brilho diferente.

Talvez você pudesse fazer o ciclo de cores para os módulos, mas apenas aplicá-lo ao [] em ambos os lados do número do nível da pilha, então se você não puder ver as cores por algum motivo, não se perderá muito.

Talvez você pudesse fazer o ciclo de cores para os módulos, mas apenas aplicá-lo ao []

É difícil até mesmo ver a cor corretamente se forem apenas colchetes e eles estiverem em uma área contrastante, então eu não acho que funciona muito bem ..

a sintaxe f (...) é explicitamente proibida

Acho que funciona bem mesmo sem espaço, por causa da diferença de contraste.

Se possível, seria bom ter algum tipo de "gancho" em seus olhos para as informações da linha

Eu concordo, acho que funciona bem deixar os números das linhas em cinza, mas em negrito, o que os torna ligeiramente salientes se você olhar para eles, mas o próximo passo de torná-los brancos é muito barulhento na minha opinião.

Devemos provavelmente alinhar os números do stackframe

Eu tentei com espaços dentro dos colchetes como no seu exemplo, mas achei um pouco estranho, então agora estou alinhando corretamente os números entre colchetes. Acho que funciona muito bem.

Esta é a versão mais recente com todas essas ideias incorporadas:

grafik

E como um bônus, veja como ele ficaria com nomes de variáveis ​​(aleatórios). Novamente, estou usando cinza em negrito porque todos esses nomes de variáveis ​​não devem lutar por sua atenção, mas devem ser um pouco perceptíveis se você procurar por eles
grafik

Eu também gosto de onde isso vai dar. Eu quero apenas colocar uma votação em algum tipo de destaque do nome do módulo. Na verdade, gosto bastante do estilo em que o mesmo módulo é colorido da mesma maneira e alterna entre as cores (há um problema de cor de gráfico aqui se você considerar duas linhas de rastreamento de pilha adjacentes como uma borda de gráfico entre seus módulos). Infelizmente, não é óbvio o que essa cor significa. Talvez colorir todos os nomes dos módulos em uma única cor?

Este alinhamento parece bom para mim.

Ainda acho que ter os caminhos mais claramente distintos das assinaturas ajudaria a separar as coisas - 3 níveis de importância, nome / assinatura / caminho. As macros de registro @info etc. as imprimem de maneira mais clara do que as outras linhas.

Seria extremamente bom ter nomes de variáveis. Votaria que eles seriam menos significativos do que as informações da assinatura, provavelmente?

Ter os nomes dos módulos alinhados à esquerda ajuda você a digitalizá-los, embora esteja triste ao ver as cores desaparecerem.

Meus olhos parecem saber onde encontrar coisas neste aqui :)
Uma linha em branco entre os módulos ocuparia muito espaço vertical?

Colorir as linhas com 2 cores diferentes para que as linhas alternadas tenham a mesma cor - como geralmente é feito em tabelas, pode ajudar na legibilidade.

O ciclo de cores @ próximo ao nome do módulo em vez do próprio nome do módulo se destaca (porque @ é um caractere muito grande) sem competir muito com o nome da função destacada.

Muitas vezes desejei os nomes dos parâmetros, pois eles ajudam a encontrar o tipo de parâmetro correto sem ter que contá-los.

O caminho (e talvez o nome do módulo próximo a ele) pode ter um brilho diferente dos tipos? Talvez apenas correspondendo os nomes dos parâmetros? (Ou todos com pouca luz juntos, se você seguir a sugestão de mcabbott de tornar os nomes mais obscuros do que os tipos)

Você pode espelhar explicitamente o que o registro usa, embora o ideal seja com cores diferentes:

for i in 1:3
    printstyled("┌[", color=:magenta, bold=true); print(i); printstyled("] ", color=:magenta, bold=true)
    printstyled("get_ticklabels", bold=true); print("("); printstyled("style", color=:light_black); print("::AbstractPlotting.Automatic, "); printstyled("n", color=:light_black); print("::Int64")
    i!=2 ? println(")") : begin print(", "); printstyled("xs", color=:light_black);  print("::Array{Float64,2}, ");  printstyled("ys", color=:light_black);  print("::Array{Float64,1}, ");  printstyled("yes", color=:light_black);  print("::Bool, ");  printstyled("no", color=:light_black);  println("::Bool)") end
    printstyled("└ @ ", color=:magenta, bold=true); printstyled("MakieLayout", color=:magenta); printstyled(" ~/.julia/packages/MakieLayout/COfBu/src/lineaxis.jl:372", color=:light_black); println();
end
<strong i="6">@error</strong> join([join(rand('a':'z', rand(1:9))) for _ in 1:25]," ") pi

Editar: agora com uma linha mais longa que envolve, e uma imagem:
Screenshot 2020-05-28 at 20 21 48

Você pode espelhar explicitamente o que o registro usa

O que eu não gosto muito sobre isso é que as quebras de linha que estão lá apenas por causa da largura REPL específica não se transferem bem se você redimensionar sua janela / REPL (se você quiser abrir espaço para linhas mais longas por exemplo ) ou copie / cole o texto em janelas de larguras diferentes

Sim, eu não proporia quebrar explicitamente as linhas para tornar os marcadores contínuos. Na verdade, pode ser uma boa coisa deixar as linhas quebradas aparecerem e, portanto, parecerem diferentes. Principalmente uma ideia de como vincular a cor ao nome da função e como torná-la talvez menos intrusiva, se for parte da borda em vez de parecer o título mais importante.

Devo dizer que gostei da cor, mas posso entender a preferência de ser conservador com ela.

Quero, no entanto, dar meu apoio a algo como

Freqüentemente, o pacote exato não é tanto de interesse quanto a natureza do código: é o núcleo julia, uma biblioteca que estou usando, a biblioteca que estou desenvolvendo ou um script? Poderíamos ter quatro cores: core / base / dev / stdlib, pacotes, pacotes atualmente ]dev ed e todo o resto (incluindo script / REPL). [...]

Como não estou hackeando na base e raramente em pacotes (como a maioria das pessoas faz, eu presumo), 90% dos erros precisam ser corrigidos em meu script, 9% em um pacote e 1% na base. Se eu cometi um erro ao chamar uma função, não preciso saber exatamente aqui na base o erro foi lançado, já que não vou lá para consertá-lo de qualquer maneira, preciso consertar meu código.

Portanto, gostaria de receber um ajudante visual para ver onde estou deixando meu código (e talvez também onde estou deixando o código do pacote, indo para a base) porque na maioria dos casos, qualquer coisa depois disso posso ignorar com segurança por enquanto. Se não for pela cor, talvez com -------------------------------------------- ou algo assim? Deve haver apenas 2-3 desses.

Se os módulos ficarem coloridos, talvez o Base / Core deva ser deixado de fora e sem cor.

Na verdade, gosto bastante do estilo em que o mesmo módulo é colorido da mesma maneira e circula pelas cores

Eu estou bem com isso também. Se vamos usar muitas cores, acho que devem ser usadas para isso. Também ajuda a definir os limites entre o código do usuário e do pacote sem ocupar muito espaço vertical.

Se os módulos ficarem coloridos, talvez o Base / Core deva ser deixado de fora e sem cor.

Sim, Base e Core sempre podem ser cinza escuro ou algo assim, deixando mais cores para todos os outros :)

Se isso deixar de funcionar perfeitamente em 16 cores, pode ser bom transformá-los em um hash dos nomes dos módulos, para que você memorize alguns deles.

Ou tem apenas duas cores, uma para Base / Core e outra para módulos externos?

Acho que não deve ser um grande problema alternar as cores porque minha versão atual tem 6 opções de escala de cinza:

crayon"blue",
crayon"yellow",
crayon"red",
crayon"green",
crayon"cyan",
crayon"magenta",

Existem variantes de luz também, eu acho, mas meu esquema de cores em VSCode (Material) as define da mesma forma que os irmãos mais escuros, então presumo que isso possa acontecer em alguns esquemas. Mas qual a probabilidade de um rastreamento de pilha passar por mais de 6 módulos de biblioteca diferentes não básicos ou não padrão? Claro pacotes usar o código de vários módulos, mas em uma pilha específica não deve haver muitos. Pelo menos isso não deve causar um grande problema ao decifrar o rastreamento de pilha, mesmo se houver colisões de cores.

então pode ser bom torná-los um hash dos nomes dos módulos, para que você memorize alguns deles

Isso é realmente brilhante.

É ótimo ver pessoas trabalhando nisso.
Eu estaria interessado em ver a aparência do alinhamento correto dos nomes das funções:

 [1] get_ticklabels(code_lowered::...
 [2] get_ticklabels(real::AbstractPlotting
 [3] #105(mtime::
 [4] OnUpdate(vecormat::
 [5] #setindex!#5(atexti::

vs

 [1] get_ticklabels(code_lowered::...
 [2] get_ticklabels(real::AbstractPlotting
 [3]           #105(mtime::
 [4]       OnUpdate(vecormat::
 [5]   #setindex!#5(atexti::
 [6]      setindex!(mapslices

Pode ser mais fácil para o olho captar onde os argumentos da função começam e oferecer assistência na análise da cadeia de chamadas de função sem perder muito espaço. Ele ainda precisa ser apoiado por cores, caso contrário, as informações e discussões dos locais afogariam tudo.

então pode ser bom torná-los um hash dos nomes dos módulos, para que você memorize alguns deles

Isso é realmente brilhante.

Oh, olá azul-petróleo escuro, meu velho amigo ...

então pode ser bom torná-los um hash dos nomes dos módulos, para que você memorize alguns deles

Eu costumava fazer com que o emacs fizesse exatamente isso para os apelidos do IRC. Não funcionou muito bem porque alguns dos meus amigos receberam a mesma cor e era mais confuso do que qualquer coisa, então acabei codificando cores para meus amigos. Eu não recomendaria seguir esse caminho, por mais legal que pareça. Ter um conjunto reduzido de cores para cada tipo de código (por exemplo, Julia, pacote, pacote desenvolvido, usuário) parece mais útil.

Eu pousei em um esquema de cores do editor que faz isso, e é moderadamente útil - todas as variáveis ​​que poderiam ser azuis são vários tons de verde e azul, com strings e palavras-chave, etc. diferentes. Talvez o ideal seja algo assim em classes amplas como as que você sugere (por exemplo, biblioteca padrão em vermelho-roxo, baixada em verde-azul, desenvolvida em laranja-amarelo, base em cinza). Ou talvez isso seja muito trabalhoso!

outra iteração: eu tentei colocar nomes de variáveis ​​lá, embora tudo pareça super hacky para mim, já que não estou muito familiarizado com todos os detalhes internos. Eu recolori os módulos porque parecia obter uma resposta positiva em geral, embora seja discutível como as cores devem ser atribuídas. Mas com as cores, sinto que o módulo de uma entrada se choca um pouco com o nome da função da próxima. Então, introduzi quebras de linha. Torna tudo mais longo, é claro, mas eu meio que gosto do espaço adicional para respirar e clareza

grafik

Gostei da sua última amostra, é muito clara e legível :)

Abaixo está apenas um experimento aplicando a coloração do módulo ao "@" em vez do nome do módulo para reduzir o conflito que você mencionou com o nome da função, e tentando destacar os tipos para que o caminho possa ser distinguido um pouco mais facilmente sem ter que introduzir espaços em branco linhas.

stacktrace2

@jkrumbiegel Isso é lindo. Eu preciso disso na minha vida.

Eu gosto daqueles azuis suaves

@jkrumbiegel Ótimo, não tenho certeza se o símbolo @ é necessário, pode parecer mais limpo sem ele. Se você quiser explicar quais são essas informações, você também pode simplesmente escrevê-las como Jeff sugeriu:
in MakieLayout at ~/.julia/packages/MakieLayout/COfBu/src/lineaxis.jl:372

@jkrumbiegel Isso parece ótimo! Existe uma maneira de destacar apenas o nome do arquivo (não o caminho completo) e o número da linha? Talvez um cinza ligeiramente mais brilhante?

Adorável! Na verdade, gosto muito das quebras de linha - elas realmente ajudam visualmente e nossos rastreamentos de pilha tendem a ser tão longos que tenho que rolar de qualquer maneira, prefiro que fique claro do que reduzir um pouco a rolagem.

Os :: destacados são brilhantes. Se eu tivesse que plotar uma curva mostrando o conteúdo de informação de cada caractere da lista de parâmetros em relação à posição do caractere, :: marcaria exatamente os máximos locais dessa curva. Eu sei olhar para a esquerda deles para o nome e para a direita para a parte mais importante do tipo, depois disso o conteúdo da informação apenas cai.

Não concordo com esse comentário: o destaque deve enfatizar coisas importantes para que o olho saiba o que ler, e o :: definitivamente não é algo importante que eu deva ler. Tal como está, o branco é muito mais perceptível do que o arrojado azul escuro. Distinguir os parâmetros seria mais claro apenas tendo em branco os nomes dos argumentos (talvez com a exceção de quando não há nenhum argumento nomeado, neste caso :: pode estar em branco para delimitar argumentos). O mesmo vale para o número da linha, prefiro enfatizar o nome do arquivo do que o número da linha.

Os nomes dos pacotes coloridos correm o risco de ofuscar o mais importante, o nome da função, mas tê-los em negrito e com quebras de linha realmente limita esse risco, muito bem!

Pensei muito no :: antes de torná-los brancos. É claro que os nomes dos argumentos e os tipos são as informações reais, então você pode pensar que eles devem ser destacados. Mas minha forte impressão ao destacá-los é que eles chamam a sua atenção. Mas não é importante ler todos os nomes e tipos. Você só deve conseguir encontrar aqueles que está procurando em uma função específica. Como vejo as etapas de leitura de um rastreamento de pilha:

  1. Obtenha uma visão geral
  2. Qual função em qual módulo errou?
  3. Quais outras funções chamaram esta função de quais outros módulos?
  4. Existe um limite crítico entre meu próprio código e base ou outro código de pacote? Cadê?
  5. Que função está nessa fronteira?
  6. Que tipos / argumentos foram usados? Onde termina um argumento e começa o seguinte? Isso é especialmente difícil com tipos paramétricos longos agora.
  7. Isso explica o erro?
  8. Se isso não ajudar, passe por tudo com um pente fino

Portanto, as informações de tipo / argumento são, em minha opinião, usadas somente após orientar e compreender a estrutura geral. Uma coisa a entender sobre o realce é que ele não torna necessariamente mais fácil encontrar as coisas, apenas se os realces forem relativamente esparsos. Portanto, acho que o :: destaque não atrapalha e chama sua atenção até que você comece a procurar por informações de tipo / variável. E nessa etapa dá a seus olhos ganchos para pular, nada mais, nada menos. Acho isso bastante eficiente.

Como observação lateral, nem todo argumento tem um nome, portanto, destacar os nomes das variáveis ​​nem sempre proporcionaria uma boa separação. Acho que o :: está sempre aí.

Parece uma boa lista, minha principal reclamação é o ponto 6, encontrar as lacunas nas assinaturas de tipos longos. Imprimir :: superbrilhante ajuda, embora eu concorde que é um pouco estranho. Imprimir os nomes dos argumentos (ou talvez algum marcador _1 se nenhum) ajudaria um pouco. Talvez imprimir o tipo mais externo normalmente, e depois todos os seus argumentos em cinza, ajudasse muito?

Quantos níveis de destaque estão realmente disponíveis? :white não se destacará em fundos claros; na verdade, acho que podemos estar limitados ao normal, em negrito e :light_black (mais cores). No momento, o nome da função e o caminho estão em negrito. Em @error etc, o caminho é light_black, o que parece ótimo.

Gosto do uso de cores para destacar os módulos. Em alguns dos exemplos acima, ele ofusca o outro texto, mas isso é verdade para cores simples? Em alguns temas diferentes, "Módulo" aqui é relativamente silenciado (e ::Array, ::Adjoint, ::Int muito claramente a assinatura de nível superior):

    printstyled("\nfunction", bold=true); print("(x::Array"); printstyled("{Float64,1}", color=:light_black); print(", y::Adjoint"); printstyled("{Float64,2,Array{Float64,2}}", color=:light_black); println(", z::Int64)")
    printstyled(" @ ", color=:light_black); printstyled("Module", color=:blue); printstyled(" ~/.julia/dev/Package/src/Package.jl:33 \n", color=:light_black)
end

Screenshot 2020-05-31 at 16 14 35

outra iteração: eu tentei colocar nomes de variáveis ​​lá, embora tudo pareça super hacky para mim, já que não estou muito familiarizado com todos os detalhes internos. Eu recolori os módulos porque parecia obter uma resposta positiva em geral, embora seja discutível como as cores devem ser atribuídas. Mas com as cores, sinto que o módulo de uma entrada se choca um pouco com o nome da função da próxima. Então, introduzi quebras de linha. Torna tudo mais longo, é claro, mas eu meio que gosto do espaço adicional para respirar e clareza

grafik

Pode ser um pouco fora do assunto, mas já que temos isso, podemos ter um show mais bonito para a saída de methods e methodswith ? Também é uma dor.

Além disso, a cor dos módulos pode ser esmaecida, como amarelo não exato, mas amarelo acinzentado. Ou podemos ter essas cores configuráveis, como startup.jl ?

@jkrumbiegel Isso parece ótimo! Existe uma maneira de destacar apenas o nome do arquivo (não o caminho completo) e o número da linha? Talvez um cinza ligeiramente mais brilhante?

Ele poderia: https://github.com/JuliaLang/julia/issues/36026#issuecomment -634481656. Mas é um pouco estranho para mim, já que o nome do arquivo faz parte do caminho, então por que ele usa uma cor diferente? Normalmente, eu apenas clico no caminho e o VSCode o abre para mim, portanto, não importa o nome do arquivo.

Eu deixei de usar Crayon.jl, agora que na verdade não uso mais cores complexas. Também ajuda com o tempo de carregamento do pacote. Descobri uma maneira de aumentar a visibilidade do nome do arquivo e do número da linha sem tornar-se visualmente opressor, sublinhando em cinza escuro. Isso também parece sensato, já que estamos acostumados com caminhos / links sublinhados. Branco ou outro realce era muito forte lá, negrito muito fraco.

Além disso, mudei para cores claras como as primeiras cores no ciclador, o que eu deveria ter feito em primeiro lugar, mas elas parecem iguais no meu tema, então nunca percebi. Isso deve ser melhor para temas onde o azul escuro é dificilmente visível (embora a culpa seja do tema).

Registrei este estilo como versão 0.2 em ClearStacktrace.jl para que possamos experimentá-lo um pouco mais.

Dois exemplos:

example 1

example 2

É um trabalho muito bom.
Você tem os nomes dos parâmetros mais claros e os tipos mais escuros. Ficou melhor assim / melhor lido do que com os tipos mais claros e os nomes mais escuros?

Sim, como os tipos geralmente são muito mais longos, não ajuda muito torná-los mais leves.

Pensamentos aleatórios:

  • talvez regras horizontais entre grupos de entradas de rastreamento de pilha no mesmo módulo? talvez muito ocupado procurando
  • pode ser bom no topo do rastreamento de pilha também, para separá-lo claramente da mensagem de erro

Todos estes são muito melhores do que o que temos agora ...

Eu deixei de usar Crayon.jl, agora que na verdade não uso mais cores complexas. Também ajuda com o tempo de carregamento do pacote.

Isso causou um tempo de carregamento significativo? Carrega muito rápido sozinho para mim

julia> <strong i="8">@time</strong> using Crayons
  0.014410 seconds (22.60 k allocations: 2.274 MiB)

Essa versão é ótima, vamos colocar isso no Base.

Concordo: vamos fazer essa última versão.

Obrigado por fazer todo o trabalho aqui, @jkrumbiegel . Ótimo ter uma versão que podemos experimentar ...

E bifurcação: eu acho que é um pouco a respeito do erro rand(5) .* rand(7) plus ocupar 35 linhas? Então criei https://github.com/mcabbott/ClearStacktrace.jl/tree/milder para experimentar. (Além de problemas de cor etc. discutidos acima.) Isso é quase https://github.com/JuliaLang/julia/issues/36026#issuecomment -635294818 com mais cores.

Observe que na impressão de rastreamento de pilha atual os quadros 8-11 naquele exemplo não são mostrados (eles são parte do REPL e estariam em todos os rastreamentos de pilha REPL).

Na verdade, isso melhorou, o que é ótimo. Mas ainda vai de 8 linhas (apenas para o rastreamento de pilha) a 20 (ClearStacktrace 0,2). Há uma certa compensação entre quão bonito é e não perder o seu lugar.

Os caminhos também são impressos de forma muito mais compacta no Base, ./broadcast.jl:495 vez de //Applications/Julia-1.5.app/Contents/Resources/julia/bin/../share/julia/base/broadcast.jl:495 , isso também reduziria a necessidade de linhas vazias.

Os caminhos básicos são expandidos propositalmente para torná-los clicáveis. Você pode desativar isso no ClearStacktrace. Eu também poderia tornar as novas linhas opcionais para pessoas que não gostam delas. Pode ser apenas uma variável de ambiente.

E eu acho que devo ter esquecido de copiar a parte da função que recorta os últimos quadros que nunca mudam

Eu deixei de usar Crayon.jl, agora que na verdade não uso mais cores complexas. Também ajuda com o tempo de carregamento do pacote. Descobri uma maneira de aumentar a visibilidade do nome do arquivo e do número da linha sem tornar-se visualmente opressor, sublinhando em cinza escuro. Isso também parece sensato, já que estamos acostumados com caminhos / links sublinhados. Branco ou outro realce era muito forte lá, negrito muito fraco.

Além disso, mudei para cores claras como as primeiras cores no ciclador, o que eu deveria ter feito em primeiro lugar, mas elas parecem iguais no meu tema, então nunca percebi. Isso deve ser melhor para temas onde o azul escuro é dificilmente visível (embora a culpa seja do tema).

Registrei este estilo como versão 0.2 em ClearStacktrace.jl para que possamos experimentá-lo um pouco mais.

Dois exemplos:

example 1

example 2

Curioso para saber por que há um / extra em //Applications/Julia-1.4.app/ ?

Provavelmente um bug de divisão e reingresso no caminho

Eu deixei de usar Crayon.jl, agora que na verdade não uso mais cores complexas. Também ajuda com o tempo de carregamento do pacote.

Isso causou um tempo de carregamento significativo? Carrega muito rápido sozinho para mim

julia> <strong i="9">@time</strong> using Crayons

  0.014410 seconds (22.60 k allocations: 2.274 MiB)

Não, eu removi principalmente porque não o teria na base :) O tempo de carregamento foi apenas um palpite

Acho que pode haver muitas linhas em branco se tivermos muitos frames de stacktrace, conforme mostrado em https://github.com/JuliaLang/julia/issues/36026#issuecomment -636912686?

Para não atrapalhar a discussão (a última versão é ótima e uma grande melhoria), mas no tópico de muitas novas linhas, o problema é que ao trabalhar no terminal me parece muito melhor imprimir o stacktrace "invertido" (como Eu propus originalmente em https://github.com/JuliaLang/julia/pull/18228) como:

...
[3] frame
[2] frame
[1] frame
Error: Some error happened
blah blah

A informação mais importante é a própria mensagem de erro e os frames no topo da pilha (mais perto do erro) e imprimi-la nesta ordem que estará sempre visível sem rolar. No momento, frequentemente preciso rolar para cima para ver a mensagem de erro e não apenas o final de um rastreamento de pilha.

No entanto, ao ler um rastreamento de pilha em um webstite que foi copiado e colado do terminal, você deseja a ordem oposta porque rola de cima para baixo e não de baixo para cima como faz em um terminal ... É complicado obter um bom design para ambos os casos.

No entanto, ao ler um rastreamento de pilha em um webstite que foi copiado e colado do terminal, você deseja a ordem oposta porque rola de cima para baixo e não de baixo para cima como faz em um terminal ... É complicado obter um bom design para ambos os casos.

Na verdade, eu tinha um código em ClearStacktrace.jl por um momento que me permitiu reimprimir o último stacktrace. Eu tinha imaginado isso para cortar tipos superlongos no máximo de alguns caracteres e ser capaz de reimprimir por completo se toda a informação fosse necessária. Mas seu caso de uso também seria interessante. Posso imaginar um reprint(inverted = true) ou mesmo reprint(html = true) onde imprimiria uma versão html que reteria a cor para colar em um site.

Além disso, concordo que, dada a direção da rolagem, pode fazer sentido inverter tudo por padrão.

ipython imprime quadros na ordem oposta e, apesar de não ter que rolar, sempre achei inexplicavelmente confuso. Talvez seja apenas devido à minha experiência anterior com gdb e outros sistemas em que o quadro mais interno está no topo, ou talvez outras pessoas também se sintam assim?

Falando em gdb, eles têm uma solução razoável para rastros muito longos com seu pager: "pressione enter para mais frames".

A propósito, adoro o design visual mais recente em https://github.com/JuliaLang/julia/issues/36026#issuecomment -636912686 e ficaria extremamente feliz se o fundíssemos. Alterar a ordem dos quadros ou adicionar recursos interativos parecem questões distintas.

Com relação aos nomes de arquivo, espero que possamos eventualmente usar sequências OSC de hiperlink de terminal ( sim, hiperlinks em terminais são amplamente suportados! ) E ter uma maneira de o editor do usuário pegá-los.

Falando em encontrar o "quadro mais interno", eu uso linguagens suficientes no curso do meu trabalho que nunca consigo me lembrar se deveria estar olhando para o topo ou a base de um stacktrace para meu código em qualquer linguagem em particular. Então, acabo examinando o nome do arquivo até encontrar um que reconheço. O sublinhado mostrado aqui ajudaria, então esta é uma melhoria clara. Mas ainda me pergunto se há uma boa maneira de dizer se eu deveria estar olhando para cima ou para baixo. Em princípio, imprimir YOUR CODE HERE em uma extremidade e OTHER CODE HERE na outra me ajudaria, mas não parece muito elegante.

Fiz um PR para a Base aqui https://github.com/JuliaLang/julia/pull/36134
Até onde sei, funciona, mas vou precisar de ajuda para deixá-lo pronto para a fusão

Existe uma maneira de omitir parâmetros de tipo? O problema que vemos muitas vezes é que a quantidade de parâmetros de tipo em DiffEq, ForwardDiff, etc. pode tornar as coisas ... assustadoras. Se por padrão ele dissesse apenas Dual , exceto no caso em que há despacho para outros métodos devido aos parâmetros de tipo, então eu acho que você reduziria 90% do ruído na maioria dos rastreamentos de pilha que li (e em nos outros casos, a sugestão de @timholy é realmente necessária, já que geralmente se trata de criar tipos ou combinar parâmetros de tipo)

Se eles corresponderem a algum alias de tipo existente, eles simplesmente desaparecerão automaticamente agora (# 36107). Caso contrário, eles indicam possíveis gargalos de desempenho de compilação - então, possivelmente, vale a pena investigar?

Isso é feito agora.

Para algumas pessoas, mas a maioria fica confusa quando ele imprime um tipo que ocuparia 3 páginas de papel impresso, então provavelmente deve ser algo opcional. Vou abrir outra questão para discutir isso.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

dpsanders picture dpsanders  ·  3Comentários

StefanKarpinski picture StefanKarpinski  ·  3Comentários

TotalVerb picture TotalVerb  ·  3Comentários

StefanKarpinski picture StefanKarpinski  ·  3Comentários

Keno picture Keno  ·  3Comentários