Js-beautify: o recuo em cadeia leva a um recuo excessivo

Criado em 20 jun. 2014  ·  18Comentários  ·  Fonte: beautify-web/js-beautify

Usando isso via https://github.com/enginespot/js-beautify-sublime

Esperado:

.foo()
.bar();

Real:

  .foo()
  .bar();

Para ilustrar o problema: o recuo atual pode levar a EOFs como este:

      });
    });
})();

Isso me parece um erro e me levará a procurar a causa – ou pior, me deixará cego para os reais =(

enhancement

Comentários muito úteis

Eu tenho que marcar esta solicitação com +1, isso é especialmente útil ao trabalhar com Promises.

Promise.resolve()
.then(function() {
  return foo.bar()
})
.then(function() {
  return foo.baz();
})
.then(function() {
 //...
}) //...
//...

Esse encadeamento pode continuar por um tempo, especialmente ao escrever um ponto final de API mais envolvido, e quando você está olhando para o fundo, é constantemente pego de surpresa com a forma como o recuo termina em relação a algo próximo.

Eu acredito que é importante que todo recuo de fechamento seja um nível de diferença de profundidade do próximo mais próximo.

Todos 18 comentários

Você poderia nos fornecer a entrada completa e a saída esperada, juntamente com sua configuração? Estou tendo dificuldade em correlacionar seu fragmento esperado (que parece incorreto, honestamente) com os blocos de fechamento. Obrigado!

Como muitas coisas nesta área, é claro que isso é uma questão de preferência e hábito pessoal, e não uma verdade objetiva.

Após o processamento, você pode ver algo assim:

(function () {
    'use strict';

    angular
        .module('module', [])
        .directive('appVersion', ['version',
            function (version) {
                return function (scope, elm, attrs) {
                    elm.text(version);
                };
            }
        ])
        .directive('foo', [

            function () {
                return {};
            }
        ])
        .directive('bar', [

            function () {
                return {};
            }
        ]);
})();

Eu gostaria deste:

(function () {
    'use strict';

    angular
    .module('module', [])
    .directive('appVersion', ['version',
        function (version) {
            return function (scope, elm, attrs) {
                elm.text(version);
            };
        }
    ])
    .directive('foo', [
        function () {
            return {};
        }
    ])
    .directive('bar', [
        function () {
            return {};
        }
    ]);
})();

Esta é a minha configuração:

{
    "indent_level": 0,
    "indent_with_tabs": true,
    "preserve_newlines": true,
    "max_preserve_newlines": 5,
    "jslint_happy": true,
    "brace_style": "collapse",
    "keep_array_indentation": false,
    "keep_function_indentation": false,
    "space_before_conditional": true,
    "break_chained_methods": true,
    "eval_code": false,
    "unescape_strings": false,
    "wrap_line_length": 0,

    // jsbeautify options
    "format_on_save": true,
    "use_original_indentation": true
}

+1

O problema que você está falando é este travessão:

    angular
        .module('module', [])

Vou tirar um minuto para sentir um toque incrível por estarmos tão perto do que você quer ver. Em tempos passados, não teríamos chegado nem perto. :sorrir:

O recuo destina-se a manter a clareza de quais elementos fazem parte de uma declaração específica. No caso geral, o recuo é basicamente correto. Neste caso específico, você tem uma declaração muito longa, mas por #200 o embelezador não sabe que não há declarações significativas depois dessa. O embelezador não se destina a ser um formatador totalmente configurável - destina-se a abordar o caso geral

Para adicionar um pouco de profundidade a esta discussão, por favor, dê uma olhada nestes exemplos e diga-me como deve ser a formatação.

alpha
    .cooker(function() {
        some
            .thing()
            .should()
            .happen();
        elsewhere
            .some_other_thing()
            .should()
            .happen();
    })
    .thenclose()
beta(zeta);
omega
    .cage(function() {
        random
            .things()
            .should()
            .happen();
        elsewhere
            .some_other_thing()
            .should()
            .happen();
    })
    .thendie()

Vou tirar um minuto para sentir um toque incrível por estarmos tão perto do que você quer ver.

Absolutamente! =)

Em relação ao recuo, acho que seu exemplo deve ficar assim:

alpha
.cooker(function() {
    some
        .thing()
        .should()
        .happen();
    elsewhere
        .some_other_thing()
        .should()
        .happen();
})
.thenclose()
beta(zeta);
omega
.cage(function() {
    random
        .things()
        .should()
        .happen();
    elsewhere
        .some_other_thing()
        .should()
        .happen();
})
.thendie()

A máxima aqui é a mesma das chaves: início e fim devem estar no mesmo recuo. Além disso, as convenções de código de Douglas Crockford prescrevem o switch da maneira que o fazem precisamente para evitar o recuo excessivo.

Exceto que js-beautify não segue o crockford por padrão, e se você executar o acima através do jslint, ele reclamará que .cooker( está no recuo errado.

No seu exemplo, parece-me que é muito fácil para beta(zeta); passar despercebido.
Além disso, você mostra alguns recuos em cadeia e outros não. Que lógica o embelezador deve usar para decidir quais recuam e quais não?

Vou deixar isso em aberto - o exemplo que você fornece parece ser baseado em AngularJS, então esse idioma pode ganhar maior aceitação ao longo do tempo. Mas não é algo que poderemos incorporar tão cedo.

Sinto muito: errei a indentação. Nenhum deles deveria ser recuado. E para beta(zeta); não passar despercebido, eu usaria linhas vazias, assim:

alpha
.cooker(function() {
    some
    .thing()
    .should()
    .happen();

    elsewhere
    .some_other_thing()
    .should()
    .happen();
})
.thenclose();

beta(zeta);

omega
.cage(function() {
    random
    .things()
    .should()
    .happen();

    elsewhere
    .some_other_thing()
    .should()
    .happen();
})
.thendie();

Como eu disse no início, acho que é uma questão de gosto pessoal. E especificamente com correntes de linha única, estou menos inclinado a reduzir o recuo. Mas acho o case multi-linha muito ruim e ter estilos mistos seria horrível, então eu definitivamente seguiria a estratégia de menos recuo, sempre.

Você pode olhar para # 485. Com esta próxima correção, o seguinte agora permanecerá inalterado quando passar pelo embelezador:

(function () {
    'use strict';

    angular
        .module('module', [])
        .directive('appVersion', ['version', function (version) {
            return function (scope, elm, attrs) {
                elm.text(version);
            };
        }])
        .directive('foo', [function () {
            return {};
        }])
        .directive('bar', [function () {
            return {};
        }]);
})();

Ainda não é o que você gostaria, mas o embelezador não forçará mais a declaração da função para uma nova linha (enquanto ainda respeita uma nova linha se você a incluir).

Eu tenho que marcar esta solicitação com +1, isso é especialmente útil ao trabalhar com Promises.

Promise.resolve()
.then(function() {
  return foo.bar()
})
.then(function() {
  return foo.baz();
})
.then(function() {
 //...
}) //...
//...

Esse encadeamento pode continuar por um tempo, especialmente ao escrever um ponto final de API mais envolvido, e quando você está olhando para o fundo, é constantemente pego de surpresa com a forma como o recuo termina em relação a algo próximo.

Eu acredito que é importante que todo recuo de fechamento seja um nível de diferença de profundidade do próximo mais próximo.

:+1:

Eu acredito que é importante que todo recuo de fechamento seja um nível de diferença de profundidade do próximo mais próximo.

Para mim, este é o argumento decisivo que prova que o recuo extra é enganoso e, em última análise, um erro. Eu percebo que alguns podem sentir que há algum sentido em que

Promise.resolve()
  .then(function() {
    return foo.bar()
  })

parece refletir que o then() em algum sentido tem um relacionamento pai-filho com Promise.resolve() , mas se isso for verdade, então cada then() subsequente tem esse relacionamento com o anterior then() . Claro que não existe realmente _no_ tal relacionamento pai-filho, e recuar como se houvesse todo o caminho faria uma grande bagunça, então ninguém faz isso. Mas recuar o primeiro then() apenas faz uma _pequena_ bagunça em vez de uma enorme – é só que algumas pessoas parecem dispostas a tolerar essa pequena bagunça, enquanto alguns de nós preferem não ter _qualquer_ bagunça em nosso código se nós podemos ajudá-lo.

Pode ser bom ter a indicação visual que a indentação fornece, mas neste caso está sobrecarregando o significado da indentação – não apenas indicando um novo escopo, mas também indicando um método encadeado. No entanto, já temos o . para indicar um método encadeado, e como o . está no início do texto na linha, ele realmente fornece todo o (pseudo-)recuo necessário contanto que você esteja prestando atenção nele.

Portanto, não é _realmente_ _apenas _ uma questão de preferência pessoal – é uma questão de vantagens e desvantagens de ambas as abordagens. (É claro que a preferência pessoal _está_ envolvida, porque alguns podem não se importar com certas desvantagens ou benefícios, mas a discussão pode ser mais proveitosa se discutirmos quais são essas vantagens e desvantagens, em vez de apenas dizer "eu prefiro _x_" ou "eu prefira _y_".)

Eu acho que é forte o argumento de que as desvantagens da indentação extra são significativas, enquanto os benefícios podem ser obtidos de outra maneira.

Desvantagens do recuo extra para métodos encadeados:

  • seu recuo não é mais um indicador confiável de escopo
  • sua pontuação final provavelmente fará você pensar que cometeu um erro

Benefícios:

  • você obtém uma indicação visual maior do encadeamento de métodos do que apenas um . por si só fornece (MAS você _do_ obtém essa indicação com apenas um . )

+1

+1 Isso está levando a erros Expected exactly one space between '{a}' and '{b}' no jslint.

Exemplo:

gulp.task('changelog', function () {
    return gulp.src('CHANGELOG.md', {
            buffer: false
        })
        .pipe(conventionalChangelog({
            preset: 'angular' // Or to any other commit message convention you use.
        }))
        .pipe(gulp.dest('./'));
});

Erros:

4   Expected 'buffer' at column 9, not column 13.    buffer: false
5   Expected '}' at column 5, not column 9.  })

Maneira correta (para jslint):

gulp.task('changelog', function () {
    return gulp.src('CHANGELOG.md', {
        buffer: false
    })
        .pipe(conventionalChangelog({
            preset: 'angular' // Or to any other commit message convention you use.
        }))
        .pipe(gulp.dest('./'));
});

Eu preferiria muito ter isso como uma opção, principalmente para evitar recuo extra desnecessário no encadeamento de promessas:

  // fetch `config.json` and then set as constant
  $http.get('config.json').then(function(response) {
    angular.module('myApp').constant('CONFIG', response.data);
  })
  .then(function() {
    angular.bootstrap(document, ['myApp']);
  })
  .catch(function(err) {
    var message = (err && err.data || err && err.message);
    console.error('Unable to bootstrap application.', err);
    window.alert('Unable to bootstrap application.' + (message ? '\n\n' + message : ''));
  });

Eu acho que o recuo extra é o equivalente a:

try {
    // 4 spaces
  } // 2
  catch () {
    // 4
  }

Faz sentido se o token filho que envolve um bloco de recuo estiver em uma nova linha da variável inicial:

    .module('module', function() {
      // .module starts on new line, so this block has 2 indents
    })

vs.

  angular.module('module', function() {
    // .module is on the same line as the initial variable angular, so this block has 1 indent
  })

Mas se tudo pode caber em uma linha, o recuo duplo não deve acontecer.

(Como está, o acima seria linted/esperado como:)

angular.module('module', function() {
    // double indent
  });

Eu marquei isso como um aprimoramento.
Não estou argumentando contra ser uma boa ideia, só não tenho tempo.

Todos vocês "+1" e aqueles que comentaram, sintam-se à vontade para contribuir com um pull request.

+1 desejo

Eu abri um PR para isso

https://github.com/beautify-web/js-beautify/pull/927

se a integração contínua finalmente atualizar o status de PR, ela deverá estar pronta para mesclar.

+1 Isso é chato pra caramba

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