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
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 2 | .editorconfig:8 [*] |
| indent_style | space | .editorconfig:7 [*] |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 2derivada — não está escrita em nenhum arquivo | .editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado |
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 4 | .editorconfig:20 [lib/**.js]substitui .editorconfig:8 |
| indent_style | space | .editorconfig:7 [*] |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 4derivada — não está escrita em nenhum arquivo | .editorconfig:20 [lib/**.js]copiada de indent_size, que não tinha tab_width ao lado |
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 4 | .editorconfig:20 [lib/**.js]substitui .editorconfig:8 |
| indent_style | space | .editorconfig:7 [*] |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 4derivada — não está escrita em nenhum arquivo | .editorconfig:20 [lib/**.js]copiada de indent_size, que não tinha tab_width ao lado |
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 4 | .editorconfig:11 [*.{py,rs}]substitui .editorconfig:8 |
| indent_style | space | .editorconfig:7 [*] |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 4derivada — não está escrita em nenhum arquivo | .editorconfig:11 [*.{py,rs}]copiada de indent_size, que não tinha tab_width ao lado |
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 2 | .editorconfig:8 [*] |
| indent_style | tab | .editorconfig:17 [Makefile]substitui .editorconfig:7 |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 2derivada — não está escrita em nenhum arquivo | .editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado |
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 2 | .editorconfig:8 [*] |
| indent_style | space | .editorconfig:7 [*] |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 2derivada — não está escrita em nenhum arquivo | .editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado |
| trim_trailing_whitespace | false | .editorconfig:14 [*.{md, txt}] |
Cadeia de busca: .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 2 | .editorconfig:8 [*] |
| indent_style | space | .editorconfig:7 [*] |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 2derivada — não está escrita em nenhum arquivo | .editorconfig:8 [*]copiada de indent_size, que não tinha tab_width ao lado |
Cadeia de busca: vendor/.editorconfig → .editorconfig · interrompida por root = true
| Propriedade | Valor | Definida em |
|---|---|---|
| charset | utf-8 | .editorconfig:4 [*] |
| end_of_line | lf | .editorconfig:5 [*] |
| indent_size | 2 | .editorconfig:8 [*] |
| indent_style | tab | vendor/.editorconfig:2 [*]substitui .editorconfig:7 |
| insert_final_newline | true | .editorconfig:6 [*] |
| tab_width | 4 | vendor/.editorconfig:3 [*] |
| trim_trailing_whitespace | false | vendor/.editorconfig:4 [*] |
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
- 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.
- 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.
- 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.
- 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.
- 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
Testador de .gitignore
Cole um .gitignore e uma lista de caminhos: veja quais são ignorados e qual linha decidiu, como responde o git check-ignore -v.
Gerador de EditorConfig
Crie um arquivo .editorconfig com estilo de indentação, fim de linha, charset e overrides comuns, no seu navegador.
Testador de padrões glob
Teste caminhos de arquivo contra um padrão glob (*, **, ?, [...], {a,b}) no seu navegador.
Inspetor de módulos WebAssembly
Analise um .wasm no navegador: tamanho de cada seção, importações e exportações com tipos, páginas de memória, função start e seções name e producers.
Inspetor de estrutura GIF
Abra um .gif e leia seus blocos: atraso e descarte de cada quadro, repetição, paletas, transparência e quantos bytes cada quadro realmente custa.
Validador de JSON Schema
Verifique um documento JSON contra um JSON Schema no navegador: cada erro traz o JSON Pointer, a linha onde cai e a palavra-chave que falhou.