Js-beautify: la sangría en cadena conduce a una sangría excesiva

Creado en 20 jun. 2014  ·  18Comentarios  ·  Fuente: beautify-web/js-beautify

Usando esto a través de https://github.com/enginespot/js-beautify-sublime

Previsto:

.foo()
.bar();

Real:

  .foo()
  .bar();

Para ilustrar el problema: la sangría actual puede conducir a EOF como este:

      });
    });
})();

Eso me parece un error, y me pedirá que busque la causa, o peor aún, me hará ciego a los reales =(

enhancement

Comentario más útil

Tengo que hacer +1 en esta solicitud, esto es especialmente útil cuando se trabaja con Promises.

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

Este encadenamiento puede continuar por un tiempo, especialmente cuando se escribe un punto final de API más complicado, y en el momento en que miras la parte inferior, te sorprende constantemente con la forma en que termina la sangría en relación con algo cercano.

Creo que es importante que todas las sangrías de cierre tengan un nivel de diferencia de profundidad con respecto al siguiente más cercano.

Todos 18 comentarios

¿Podría darnos la entrada completa y la salida esperada, junto con su configuración? Tengo dificultades para correlacionar su fragmento esperado (que parece incorrecto, sinceramente) con los bloques de cierre. ¡Gracias!

Como muchas cosas en esta área, esto es, por supuesto, una cuestión de preferencia y hábito personal más que una verdad objetiva.

Después del procesamiento, es posible que vea algo como esto:

(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 {};
            }
        ]);
})();

Me gustaría esto:

(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 es mi configuración:

{
    "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

El problema del que estás hablando es este guión:

    angular
        .module('module', [])

Voy a tomarme un minuto para sentir un poco de asombro porque estamos tan cerca de lo que quieres ver. En tiempos pasados, no habríamos llegado a ningún lado cerca. :sonreír:

La sangría allí está destinada a mantener la claridad de qué elementos son parte de una declaración en particular. En el caso general, la sangría es básicamente correcta. En este caso específico, tiene una declaración muy larga, pero según el n. ° 200, el embellecedor no sabe que no hay declaraciones significativas después de esa. El embellecedor no pretende ser un formateador totalmente configurable; está destinado a abordar el caso general

Para agregar algo de profundidad a esta discusión, mire estos ejemplos y dígame cómo debería verse el formato.

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()

Voy a tomarme un minuto para sentir un poco de asombro porque estamos tan cerca de lo que quieres ver.

¡Absolutamente! =)

Con respecto a la sangría, creo que su ejemplo debería verse así:

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()

La máxima aquí es la misma que para las llaves: el inicio y el final deben tener la misma sangría. Además, las convenciones de código de Douglas Crockford prescriben el switch la forma en que lo hacen precisamente para evitar la sangría excesiva.

Excepto que js-beautify no sigue a crockford de forma predeterminada, y si ejecuta lo anterior a través de jslint, se quejará de que .cooker( tiene una sangría incorrecta.

En su ejemplo, me parece que es demasiado fácil pasar por alto beta(zeta); .
Además, muestra algunas sangrías en cadena y otras no. ¿Qué lógica debe usar el embellecedor para decidir cuáles sangran y cuáles no?

Voy a dejar esto abierto: el ejemplo que proporciona parece estar basado en AngularJS, por lo que este idioma puede ganar una mayor aceptación con el tiempo. Pero no es algo que podamos incorporar en el corto plazo.

Lo siento mucho: perdí la sangría. Se suponía que ninguno de ellos tenía sangría. Y para que beta(zeta); no se pase por alto, usaría líneas vacías, así:

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 decía al principio, creo que es cuestión de gustos personales. Y específicamente con cadenas de una sola línea estoy menos inclinado a reducir la sangría. Pero encuentro que el caso de varias líneas es muy malo y tener estilos mixtos sería horrible, así que definitivamente me apegaría a la estrategia de menos sangría, siempre.

Podrías mirar el #485. Con esta próxima solución, lo siguiente ahora permanecerá sin cambios cuando pase por el embellecedor:

(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 {};
        }]);
})();

Todavía no es lo que le gustaría, pero el embellecedor ya no forzará la declaración de la función a una nueva línea (sin dejar de respetar una nueva línea si la incluye).

Tengo que hacer +1 en esta solicitud, esto es especialmente útil cuando se trabaja con Promises.

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

Este encadenamiento puede continuar por un tiempo, especialmente cuando se escribe un punto final de API más complicado, y en el momento en que miras la parte inferior, te sorprende constantemente con la forma en que termina la sangría en relación con algo cercano.

Creo que es importante que todas las sangrías de cierre tengan un nivel de diferencia de profundidad con respecto al siguiente más cercano.

:+1:

Creo que es importante que todas las sangrías de cierre tengan un nivel de diferencia de profundidad con respecto al siguiente más cercano.

Para mí, este es el factor decisivo que demuestra que la sangría adicional es engañosa y, en última instancia, un error. Me doy cuenta de que algunos pueden sentir que hay algún sentido en el que

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

parece reflejar que then() en algún sentido tiene una relación padre-hijo con Promise.resolve() , pero si eso es cierto, entonces cada then() subsiguiente también tiene esa relación con el anterior then() . Por supuesto, realmente no existe tal relación padre-hijo, y la sangría como si la hubiera hasta el fondo sería un gran lío, por lo que nadie lo hace. Pero sangrar los primeros then() solo crea un _pequeño_ lío en lugar de uno enorme; es solo que algunas personas parecen dispuestas a soportar ese pequeño lío, mientras que algunos de nosotros preferimos no tener _ningún_ lío en nuestro código si podemos ayudarlo.

Puede ser bueno tener la indicación visual que proporciona la sangría, pero en este caso está sobrecargando el significado de la sangría, no solo indica un nuevo ámbito, sino que también indica un método encadenado. Sin embargo, ya tenemos . para indicar un método encadenado, y debido a que . está al comienzo del texto en la línea, realmente proporciona toda la (pseudo) sangría que se necesita. siempre y cuando le estés prestando atención.

Así que no es _realmente_ _solo _ una cuestión de preferencia personal, es una cuestión de ventajas e inconvenientes de ambos enfoques. (La preferencia personal _está_ por supuesto involucrada, porque a algunos puede no importarles ciertos inconvenientes o ciertos beneficios, pero la discusión puede ser más fructífera si analizamos cuáles _son_ esos beneficios y esos inconvenientes, en lugar de simplemente decir "Prefiero _x_" o "Yo prefiero _y_".)

Creo que el caso es sólido de que los inconvenientes de la sangría adicional son significativos, mientras que los beneficios se pueden obtener de otra manera.

Inconvenientes de la sangría adicional para métodos encadenados:

  • su sangría ya no es un indicador confiable del alcance
  • es probable que su puntuación final le haga pensar que ha cometido un error

Beneficios:

  • obtiene una indicación visual más grande del encadenamiento de métodos que solo un . por sí mismo (PERO _obtiene_ esa indicación con solo un . )

+1

+1 Esto conduce a Expected exactly one space between '{a}' and '{b}' errores en jslint.

Ejemplo:

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('./'));
});

Errores:

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

Forma correcta (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('./'));
});

Preferiría tener esto como una opción, principalmente para evitar una sangría adicional innecesaria en el encadenamiento de promesas:

  // 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 : ''));
  });

Creo que la sangría adicional es el equivalente a:

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

Tiene sentido si el token secundario que envuelve un bloque de sangría está en una nueva línea desde la variable inicial:

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

contra

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

Pero si todo puede caber en una línea, entonces la doble sangría no debería ocurrir.

(Tal como está, lo anterior estaría alineado/esperado como :)

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

He marcado esto como una mejora.
No estoy argumentando en contra de que sea una buena idea, simplemente no tengo tiempo.

Todos los usuarios de "+1" y aquellos que han comentado, siéntanse libres de contribuir con una solicitud de extracción.

+1 quiero

He abierto un PR para esto

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

si la integración continua finalmente actualizara el estado de PR, debería estar listo para fusionarse.

+1 Esto es aburrido como el infierno

¿Fue útil esta página
0 / 5 - 0 calificaciones