fsharp / emacs-fsharp-mode

F# Emacs mode
Apache License 2.0
208 stars 62 forks source link

Problems in Block Detection and Expression Traversal #224

Open Gastove opened 5 years ago

Gastove commented 5 years ago

Description

As part of #218, I've been digging in to the code fsharp-mode uses to compute indentation. This is a fairly complicated topic, including regexen for keyword detection, predicate functions, and motion functions. In particular, I've found a set of problems with fsharp-statement-opens-block-p and fsharp-goto-beyond-final-line. This goes like so:

  1. fsharp-statement-opens-block-p needs to compute whether or not the current statement creates a new block or not. To do this, it searches between the current value of point, and an endpoint called finish. finish is set to the value of fsharp-goto-beyond-final-line.
  2. fsharp-goto-beyond-final-line claims to go to the line after the end of "the current statement."[^1]
  3. fsharp-goto-beyond-final-line uses a while loop and-ing on fsharp-continuation-line-p.
  4. fsharp-continuation-line-p only considers a line a "continuation line" if it either 1) ends in a dangling arithmetic operator or 2) point is inside a block delimited by a pair (i.e. point is inside [] or {}). This means the function almost always goes one line forward, then returns point at that position.
  5. fsharp-statement-opens-block-p immediately adjusts the value returned from fsharp-continuation-line-p by subtracting 1.

To summarize:

  1. fsharp-statement-opens-block-p is taking an incredibly convoluted approach to searching until end of same line. This might be correct, but there are no previously existing tests.
  2. fsharp-goto-beyond-final-line definitely does no such thing. (Again, no tests.)
  3. fsharp-continuation-line-p is being used when we likely want a slightly different function.

For example, let x = 5 + is a "continuation line", but type Shape = is not.

(As a final, frustrating note, F# does not, in fact, have statements, at all.)

Proposed Solutions

fsharp-statement-opens-block-p needs to reliably return whether or not the statement (which is to say, expression) opens a block. It is used all over the place, so should be well documented, and under test. (Also, the name should be changed to use "expression" over "statement".)

fsharp-goto-beyond-final-line should do so, and be under test. I assert we need a function similar to, but distinct from, fsharp-continuation-line-p. Something like fsharp-in-block-p -- "tell me if this line could be considered part of an ongoing block." Right now, there is no code to detect this -- it's handled in fsharp-compute-indentation as the final default "fall-through" case, so lacks a proper predicate. We should probably have one!

Footnotes

[^1] Note that if #223 has been merged, this will say "the current expression."