Ir para o conteúdo
AZ Tools

Testador de EditorConfig

Um .editorconfig não é lido de cima para baixo, e sim do arquivo para cima. Cada diretório entre o arquivo-fonte e a raiz do disco pode conter um; a busca só para naquele que declara root = true no preâmbulo; o arquivo mais próximo vence propriedade a propriedade; e dentro de um mesmo arquivo vence a última seção que casa, sem nenhuma ideia de regra mais específica. São quatro regras puxando em direções diferentes, e é por isso que a pergunta "por que este arquivo está com quatro espaços?" quase nunca se responde na linha que você está olhando. Esta ferramenta recebe a configuração — ou várias, cada uma introduzida por uma linha marcadora "# path: src/.editorconfig" — e uma lista de caminhos. Para cada caminho ela mostra o conjunto de propriedades resolvido com o arquivo, a linha e o glob da seção que produziu cada valor, a cadeia de arquivos consultados na subida e o ponto em que root = true a interrompeu. A correspondência usa o glob do EditorConfig, que não é fnmatch nem o do gitignore. Um * sozinho para na barra e ** a atravessa; ? é um caractere que não seja barra; [abc] e [!abc] são classes de caracteres que deixam de ser classes assim que uma barra aparece dentro delas; {js,ts} é alternância, enquanto {js} sozinho casa com esses três caracteres literais; {1..10} aceita qualquer inteiro do intervalo, negativos inclusive, e recusa um zero à esquerda. E a regra que quase todo mundo erra: um glob com uma barra em qualquer posição que não o fim fica ancorado ao diretório do próprio .editorconfig, ao passo que um glob sem barra compara apenas o nome do arquivo, em qualquer profundidade. Três valores do resultado costumam não estar escritos em lugar nenhum. indent_size vira tab quando indent_style é tab e nenhum tamanho foi dado; tab_width copia indent_size quando este é um número; e indent_size assume tab_width quando vale a palavra tab. Os três são aplicados depois da mesclagem, então um tab_width do arquivo raiz pode acabar alimentando um indent_size escrito três diretórios abaixo; essas linhas aparecem marcadas como derivadas, com a linha de origem. O painel de achados aponta o que o arquivo não consegue contar sobre si: uma seção que não casa com nenhum dos seus caminhos, um valor definido que sempre perde, indent_size sem indent_style, um {js, ts} cujo espaço entra na comparação, um nome de seção repetido, uma chave fora das nove padrão, um valor que a propriedade não aceita, uma propriedade escrita antes da primeira seção (que é descartada em silêncio) e a ausência de root = true, que mantém em jogo qualquer .editorconfig do seu diretório pessoal. O que ela não diz: se o seu editor respeita tudo isso. O suporte depende do plugin — max_line_length e vários valores de charset são ignorados com frequência — e unset é entregue ao plugin como o valor literal unset, para que ele aja. "Não casa com nada" também vale apenas dentro dos caminhos que você colou, então use uma lista representativa. Tudo roda no seu navegador; nenhum arquivo é enviado.

Arquivos de configuração

2

Seções

6

Caminhos

8

Achados

1

src/app.js

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size2.editorconfig:8 [*]
indent_stylespace.editorconfig:7 [*]
insert_final_newlinetrue.editorconfig:6 [*]
tab_width2derivada — não está escrita em nenhum arquivo.editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado
lib/util.js

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size4.editorconfig:20 [lib/**.js]substitui .editorconfig:8
indent_stylespace.editorconfig:7 [*]
insert_final_newlinetrue.editorconfig:6 [*]
tab_width4derivada — não está escrita em nenhum arquivo.editorconfig:20 [lib/**.js]copiada de indent_size, que não tinha tab_width ao lado
lib/deep/util.min.js

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size4.editorconfig:20 [lib/**.js]substitui .editorconfig:8
indent_stylespace.editorconfig:7 [*]
insert_final_newlinetrue.editorconfig:6 [*]
tab_width4derivada — não está escrita em nenhum arquivo.editorconfig:20 [lib/**.js]copiada de indent_size, que não tinha tab_width ao lado
main.py

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size4.editorconfig:11 [*.{py,rs}]substitui .editorconfig:8
indent_stylespace.editorconfig:7 [*]
insert_final_newlinetrue.editorconfig:6 [*]
tab_width4derivada — não está escrita em nenhum arquivo.editorconfig:11 [*.{py,rs}]copiada de indent_size, que não tinha tab_width ao lado
Makefile

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size2.editorconfig:8 [*]
indent_styletab.editorconfig:17 [Makefile]substitui .editorconfig:7
insert_final_newlinetrue.editorconfig:6 [*]
tab_width2derivada — não está escrita em nenhum arquivo.editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado
docs/guide.md

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size2.editorconfig:8 [*]
indent_stylespace.editorconfig:7 [*]
insert_final_newlinetrue.editorconfig:6 [*]
tab_width2derivada — não está escrita em nenhum arquivo.editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado
trim_trailing_whitespacefalse.editorconfig:14 [*.{md, txt}]
notes.txt

Cadeia de busca: .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size2.editorconfig:8 [*]
indent_stylespace.editorconfig:7 [*]
insert_final_newlinetrue.editorconfig:6 [*]
tab_width2derivada — não está escrita em nenhum arquivo.editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado
vendor/jquery.js

Cadeia de busca: vendor/.editorconfig → .editorconfig · interrompida por root = true

PropriedadeValorDefinida em
charsetutf-8.editorconfig:4 [*]
end_of_linelf.editorconfig:5 [*]
indent_size2.editorconfig:8 [*]
indent_styletabvendor/.editorconfig:2 [*]substitui .editorconfig:7
insert_final_newlinetrue.editorconfig:6 [*]
tab_width4vendor/.editorconfig:3 [*]
trim_trailing_whitespacefalsevendor/.editorconfig:4 [*]
Achados

Cada linha é um ponto em que o arquivo faz algo diferente do que aparenta.

  • .editorconfig:13 — um espaço dentro das chaves faz parte do texto comparado: [*.{md, txt}]

A resolução segue o editorconfig-core; nomes de seção diferenciam maiúsculas. Nada é enviado.

Como usar

  1. Cole o .editorconfig na primeira caixa. Se houver mais de um arquivo, introduza cada um com uma linha marcadora como "# path: vendor/.editorconfig" e coloque o da raiz do projeto primeiro.
  2. Liste os caminhos a testar na segunda caixa, um por linha, relativos à raiz do projeto: o diretório que contém o .editorconfig de nível mais alto.
  3. Leia a tabela de cada caminho: a propriedade, o valor resolvido e o arquivo, a linha e o glob da seção que o definiu. Uma linha marcada como derivada não está escrita em arquivo nenhum; a nota diz qual regra a produziu.
  4. Confira a cadeia de busca acima de cada tabela: os .editorconfig consultados, do mais próximo ao mais distante, e se root = true interrompeu o percurso ou se ele seguiria acima do seu projeto.
  5. Percorra o painel de achados. Cada entrada aponta um arquivo e uma linha em que a configuração faz algo diferente do que aparenta: uma seção morta, um ajuste que nunca vence, um espaço dentro das chaves.

Perguntas frequentes

Por que meu arquivo usa quatro espaços se [*] diz dois?
Porque algo posterior ou mais próximo venceu, e o EditorConfig não tem hierarquia de especificidade a que recorrer. Dentro de um arquivo vence a última seção que casa, então um bloco [*.py] vinte linhas abaixo substitui o seu bloco [*] sem avisar — e um segundo bloco [*] mais abaixo faz o mesmo, razão pela qual um nome de seção repetido importa. Entre arquivos vence, propriedade a propriedade, o .editorconfig mais próximo, de modo que um arquivo de duas linhas no diretório que você está editando derrota o arquivo caprichado da raiz. Ordem e distância são a história inteira, e é exatamente isso que a coluna "Definida em" relata: o arquivo, a linha e o glob da seção que produziu o valor, com os ajustes derrotados listados abaixo.
Qual a diferença entre [*.js], [lib/*.js] e [**/*.js]?
A presença de uma barra, e nada mais. Um glob sem barra é comparado apenas com o nome do arquivo, em qualquer profundidade, então [*.js] cobre src/a.js e vendor/dist/b.js igualmente. Assim que o glob contém uma barra em qualquer posição que não o fim, ele fica ancorado ao diretório do .editorconfig que o contém: [lib/*.js] casa com lib/a.js e não com lib/sub/a.js, nem com src/lib/a.js. [**/*.js] é a forma ancorada que atravessa diretórios, porque ** cruza as barras onde um * sozinho para, e o /**/ do meio também representa a ausência de diretório, de modo que casa tanto com a.js quanto com lib/sub/a.js. Uma armadilha final: uma barra no fim, como em [lib/], não casa com nada, porque o caminho de um arquivo nunca termina em barra. A subárvore se escreve [lib/**].
De onde vêm indent_size = tab e tab_width se eu nunca os escrevi?
Das regras de reserva que o núcleo aplica depois de mesclar tudo. Se indent_style é tab e nenhum indent_size foi definido, indent_size passa a ser a palavra tab. Se indent_size é um número e não há tab_width, tab_width o copia — por isso um arquivo que só menciona indent_size relata também um tab_width. No sentido inverso, se indent_size vale tab e existe um tab_width, indent_size assume esse número. Como isso acontece sobre o resultado já mesclado, as duas metades podem vir de arquivos diferentes: um tab_width da raiz pode alimentar um indent_size escrito três diretórios abaixo. unset percorre a mesma máquina, então indent_size = unset deixa o tab_width também em unset.
O que root = true realmente interrompe?
A busca para cima por outros arquivos .editorconfig. Sem ele o percurso não para no seu projeto: continua por todos os diretórios acima, incluindo sua pasta pessoal e a raiz do sistema de arquivos, e o que for encontrado lá preenche as propriedades que o seu arquivo não definiu. Dois detalhes decidem se funciona. Ele precisa estar no preâmbulo, acima do primeiro cabeçalho de seção: escrito dentro de um bloco [*] é apenas uma propriedade comum chamada root e não interrompe nada. E ele é lido mesmo que nenhuma seção daquele arquivo case, então uma configuração com todas as seções erradas encerra a busca do mesmo jeito.
Por que [*.{js, ts}] não se aplica aos meus arquivos .ts?
Porque o espaço faz parte do padrão. A alternância entre chaves compara literalmente as cadeias separadas por vírgula, sem aparar espaço algum em um cabeçalho de seção, então {js, ts} oferece "js" e " ts", e só casa com a segunda um arquivo cujo nome realmente tenha um espaço antes de ts. Vale conhecer duas armadilhas vizinhas: {js} sem vírgula não é alternância e casa com os caracteres literais {js}, de forma que [*.{js}] se aplica a um arquivo chamado app.{js} e a mais nenhum; e um intervalo numérico como {1..10} recusa o zero à esquerda, então 1.txt e 10.txt casam, mas 01.txt não.
Uma barra invertida escapa um asterisco?
Não na implementação de referência, apesar do que o texto do padrão sugere. O núcleo traduz o glob para uma expressão regular em uma única passagem, e o escape só é respeitado para a vírgula, a chave de fechamento, # e ; — os caracteres que, sem ele, encerrariam o padrão ou dividiriam uma alternância. Uma barra invertida antes de um asterisco ou de uma interrogação é descartada e o curinga continua casando com qualquer coisa, então [a\*b.txt] casa também com aXb.txt. Pior: \[ e \{ deixam a expressão gerada desbalanceada, a compilação do glob falha e o plugin do editor costuma reagir ignorando o .editorconfig inteiro sem dizer nada. Esta ferramenta reproduz isso em vez de adivinhar e marca a seção como um glob inutilizável. O conselho prático é manter chaves, colchetes e asteriscos fora dos cabeçalhos de seção.

Ferramentas relacionadas