Asking for reindent all file messes with indentation #83

Closed
carlosedp opened this Issue Feb 27, 2012 · 12 comments

Projects

None yet

9 participants

@carlosedp

When I ask VIM to reindent the entire file using the command "gg=G", it messes with all indentation.

Example:

# Objects:
math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

# Splats:
race = (winner, runners...) ->
  print winner, runners

# Existence:
alert "I knew it!" if elvis?

# Splats:
race = (winner, runners...) ->
  print winner, runners

# Splats:
race = (winner, runners...) ->
  print winner, runners

Reindents to:

# Objects:
math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

  # Splats:
  race = (winner, runners...) ->
    print winner, runners

    # Existence:
    alert "I knew it!" if elvis?

    # Splats:
    race = (winner, runners...) ->
      print winner, runners

      # Splats:
      race = (winner, runners...) ->
        print winner, runners
@ssboisen
ssboisen commented Mar 1, 2012

+1

@AndrewRadev

It's impossible for Vim to behave otherwise. Take a look at these two blocks:

race = (winner, runners...) ->
  print winner, runners

alert "I knew it!" if elvis?
race = (winner, runners...) ->
  print winner, runners

  alert "I knew it!" if elvis?

Both are completely valid indentations -- there's no way for Vim to determine the "correct" one. "Correct" in this case is whatever the user decides while writing. It does handle some hints like this:

race = (winner, runners...) ->
  print winner, runners

Since the top line ends with a ->, then it's certain that the next line will be indented, but other than that, you can't really get automatic transformation for an indent-based language. From what I see, the idea here is to indent each line the same as the last one, so that pressing Enter at the end of a line puts you at the same level of indentation, and you can continue typing in the same scope. Which is as good as it can get, I think.

More importantly, why do you want to reindent the whole file, anyway? If there is a specific use case (I can think of one or two), maybe you should ask for help with that.

@ssboisen
ssboisen commented Mar 3, 2012

would it be possible to have two or three empty lines after the last
expression/statement be a rule to the indentation engine that indentation
should be reset? Maybe make it an option that is turned of by default.

When pasting code, using existing.code from another engine or changing
indentation width (rare usecase ofcause) it's nice to be able to do gg=G.
Den 03/03/2012 11.43 skrev "Andrew Radev" <
reply@reply.github.com

:

It's impossible for Vim to behave otherwise. Take a look at these two
blocks:

race = (winner, runners...) ->
 print winner, runners

alert "I knew it!" if elvis?
race = (winner, runners...) ->
 print winner, runners

 alert "I knew it!" if elvis?

Both are completely valid indentations -- there's no way for Vim to
determine the "correct" one. "Correct" in this case is whatever the user
decides while writing. It does handle some hints like this:

race = (winner, runners...) ->
 print winner, runners

Since the top line ends with a ->, then it's certain that the next line
will be indented, but other than that, you can't really get automatic
transformation for an indent-based language. From what I see, the idea here
is to indent each line the same as the last one, so that pressing Enter
at the end of a line puts you at the same level of indentation, and you can
continue typing in the same scope. Which is as good as it can get, I think.

More importantly, why do you want to reindent the whole file, anyway? If
there is a specific use case (I can think of one or two), maybe you should
ask for help with that.


Reply to this email directly or view it on GitHub:
#83 (comment)

@AndrewRadev

I don't think resetting indentation after multiple blank lines is a good idea. As far as I know, leaving more than one blank line in any context is generally frowned upon by most people, including me :).

Changing indentation width really does seem like a very rare use case, since coffeescript has the fairly strict convention of two spaces, so I don't think anything can be done there.

Regarding pasting, however, I feel your pain. To me, this is one of the more annoying issues with indent-based languages -- that it takes some effort to move code around, as opposed to pasting and reindenting. I've been trying to build a plugin to make this easier, but it's still very rough.

Here's something simple you could probably use, though: https://gist.github.com/1966581. Basically, pressing ,p would paste below the current line, maintaining its indent, while ,P would paste above. I can't really say if it'll work correctly in all cases, so if you find any issues with it, feel free to drop me a comment there.

By the way, I'm not a maintainer of the project or anything, I'm just giving my view on things. The owners of the project should have a final say regarding these issues.

@carlosedp

I think probably this indentation could use something like Python indentation does since both are indent based.

Gonna take a look at that.

@piotryordanov

Is there no way to get vim to indent the whole file?

@pacemkr
pacemkr commented Dec 20, 2012

+1 to this request

A possible sane rule would be to leave indentation alone if there is ambiguity and continue to the next line. This would still be a huge improvement over the current result of indenting every single function one more level relative to the previous function. What you end up with is not usable at all.

As it is now, its not even possible to select a function and auto-indent it. You end up shifting it relative to the previous line, very annoying.

@Chiel92
Chiel92 commented Apr 26, 2013

+1

@ireul-guo

+1

@kchmck kchmck added a commit that referenced this issue Aug 25, 2013
@kchmck Major rewrite of ftplugin
 - Clean up the code (fixes #110)
 - Make CoffeeRun output to its own scratch buffer (#83)
 - Split watch mode into its own CoffeeWatch command
   Now it correctly doesn't accept ranges and it can live in a separate scratch
   buffer from CoffeeCompile
bad0e15
@kchmck
Owner
kchmck commented Sep 3, 2013

This should at long last be fixed with the big rewrite! Running gg=G on the entire coffeescript source now produces this diff:

diff --git a/src/grammar.coffee b/src/grammar.coffee
index 0cd5b4d..69d4f8b 100644
--- a/src/grammar.coffee
+++ b/src/grammar.coffee
@@ -219,7 +219,7 @@ grammar =
     o 'ParamVar = Expression',                  -> new Param $1, $3
   ]

- # Function Parameters
+  # Function Parameters
   ParamVar: [
     o 'Identifier'
     o 'ThisProperty'
diff --git a/src/helpers.coffee b/src/helpers.coffee
index ec5b7a9..8df76b0 100644
--- a/src/helpers.coffee
+++ b/src/helpers.coffee
@@ -99,23 +99,23 @@ buildLocationData = (first, last) ->
 # object is an AST node, updates that object's locationData.
 # The object is returned either way.
 exports.addLocationDataFn = (first, last) ->
-    (obj) ->
-      if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
-        obj.updateLocationDataIfMissing buildLocationData(first, last)
+  (obj) ->
+    if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
+      obj.updateLocationDataIfMissing buildLocationData(first, last)

       return obj

 # Convert jison location data to a string.
 # `obj` can be a token, or a locationData.
 exports.locationDataToString = (obj) ->
-    if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
-    else if "first_line" of obj then locationData = obj
+  if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
+  else if "first_line" of obj then locationData = obj

-    if locationData
-      "#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
-      "#{locationData.last_line + 1}:#{locationData.last_column + 1}"
-    else
-      "No location data"
+  if locationData
+    "#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
+    "#{locationData.last_line + 1}:#{locationData.last_column + 1}"
+  else
+    "No location data"

# A `.coffee.md` compatible version of `basename`, that returns the file sans-extension.
exports.baseFileName = (file, stripExt = no, useWinPathSep = no) ->
diff --git a/src/lexer.coffee b/src/lexer.coffee
index 6a992b5..8e69720 100644
--- a/src/lexer.coffee
+++ b/src/lexer.coffee
@@ -45,9 +45,9 @@ exports.Lexer = class Lexer
     @tokens     = []             # Stream of parsed tokens in the form `['TYPE', value, location data]`.

     @chunkLine =
-        opts.line or 0         # The start line for the current @chunk.
+      opts.line or 0         # The start line for the current @chunk.
     @chunkColumn =
-        opts.column or 0       # The start column of the current @chunk.
+      opts.column or 0       # The start column of the current @chunk.
     code = @clean code         # The stripped, cleaned original source code.

     # At every position, run through this list of attempted matches,
@@ -84,8 +84,8 @@ exports.Lexer = class Lexer
     code = code.slice(1) if code.charCodeAt(0) is BOM
     code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
     if WHITESPACE.test code
-        code = "\n#{code}"
-        @chunkLine--
+      code = "\n#{code}"
+      @chunkLine--
     code = invertLiterate code if @literate
     code

diff --git a/src/rewriter.coffee b/src/rewriter.coffee
index dc598f1..a702ae1 100644
--- a/src/rewriter.coffee
+++ b/src/rewriter.coffee
@@ -7,9 +7,9 @@

 # Create a generated token: one that exists due to a use of implicit syntax.
 generate = (tag, value) ->
-    tok = [tag, value]
-    tok.generated = yes
-    tok
+  tok = [tag, value]
+  tok.generated = yes
+  tok

 # The **Rewriter** class is used by the [Lexer](lexer.html), directly against
 # its internal array of tokens.
@@ -341,11 +341,11 @@ class exports.Rewriter
       return 1 if     token[2]
       return 1 unless token.generated or token.explicit
       if token[0] is '{' and nextLocation=tokens[i + 1]?[2]
-          {first_line: line, first_column: column} = nextLocation
+        {first_line: line, first_column: column} = nextLocation
       else if prevLocation = tokens[i - 1]?[2]
-          {last_line: line, last_column: column} = prevLocation
+        {last_line: line, last_column: column} = prevLocation
       else
-          line = column = 0
+        line = column = 0
       token[2] =
         first_line:   line
         first_column: column
@kchmck kchmck closed this Sep 3, 2013
@michaelficarra

@kchmck: Please send that in as a pull request!

@kchmck
Owner
kchmck commented Sep 3, 2013

Done!

@lcharlick lcharlick added a commit to lcharlick/vim-coffee-script that referenced this issue Feb 21, 2014
@kchmck @lcharlick + lcharlick Major rewrite of ftplugin
 - Clean up the code (fixes #110)
 - Make CoffeeRun output to its own scratch buffer (#83)
 - Split watch mode into its own CoffeeWatch command
   Now it correctly doesn't accept ranges and it can live in a separate scratch
   buffer from CoffeeCompile
670a0c2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment