Skip to content

Fix typos and alignment in indentation.md #9417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 83 additions & 68 deletions docs/docs/reference/other-new-features/indentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ As an experimental feature, Scala 3 enforces some rules on indentation and allow
some occurrences of braces `{...}` to be optional.
It can be turned off with the compiler flag `-noindent`.

- First, some badly indented programs are flagged with warnings.
- Second, some occurrences of braces `{...}` are made optional. Generally, the rule
is that adding a pair of optional braces will not change the meaning of a well-indented program.
- First, some badly indented programs are flagged with warnings.
- Second, some occurrences of braces `{...}` are made optional. Generally, the rule
is that adding a pair of optional braces will not change the meaning of a well-indented program.

### Indentation Rules

Expand Down Expand Up @@ -62,9 +62,11 @@ There are two rules:
- after the leading parameters of an `extension`, or
- after a ": at end of line" token (see below)
- after one of the following tokens:

```
= => <- if then else while do try catch finally for yield match return
```

If an `<indent>` is inserted, the indentation width of the token on the next line
is pushed onto `IW`, which makes it the new current indentation width.

Expand All @@ -75,7 +77,7 @@ There are two rules:
- the first token on the next line is not a
[leading infix operator](../changed-features/operators.html).

If an `<outdent>` is inserted, the top element if popped from `IW`.
If an `<outdent>` is inserted, the top element is popped from `IW`.
If the indentation width of the token on the next line is still less than the new current indentation width, step (2) repeats. Therefore, several `<outdent>` tokens
may be inserted in a row.

Expand All @@ -84,12 +86,14 @@ There are two rules:
An `<outdent>` is finally inserted in front of a comma that follows a statement sequence starting with an `<indent>` if the indented region is itself enclosed in parentheses

It is an error if the indentation width of the token following an `<outdent>` does not match the indentation of some previous line in the enclosing indentation region. For instance, the following would be rejected.

```scala
if x < 0
-x
else // error: `else` does not align correctly
x
```

Indentation tokens are only inserted in regions where newline statement separators are also inferred:
at the toplevel, inside braces `{...}`, but not inside parentheses `(...)`, patterns or types.

Expand All @@ -105,6 +109,7 @@ that can start an indentation region. The Scala grammar is changed so an optiona
Analogous rules apply for enum bodies, type refinements, definitions in an instance creation expressions, and local packages containing nested definitions.

With these new rules, the following constructs are all valid:

```scala
trait A:
def f: Int
Expand Down Expand Up @@ -137,12 +142,14 @@ package q:
```

The syntax changes allowing this are as follows:

```
TemplateBody ::= [colonEol] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’
EnumBody ::= [colonEol] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
Packaging ::= ‘package’ QualId [colonEol] ‘{’ TopStatSeq ‘}’
RefinedType ::= AnnotType {[colonEol] Refinement}
```

Here, `colonEol` stands for ": at end of line", as described above.
The lexical analyzer is modified so that a `:` at the end of a line
is reported as `colonEol` if the parser is at a point where a `colonEol` is
Expand All @@ -166,13 +173,14 @@ Indentation can be mixed freely with braces. For interpreting indentation inside

The indentation rules for `match` expressions and `catch` clauses are refined as follows:

- An indentation region is opened after a `match` or `catch` also if the following `case`
appears at the indentation width that's current for the `match` itself.
- In that case, the indentation region closes at the first token at that
same indentation width that is not a `case`, or at any token with a smaller
indentation width, whichever comes first.
- An indentation region is opened after a `match` or `catch` also if the following `case`
appears at the indentation width that's current for the `match` itself.
- In that case, the indentation region closes at the first token at that
same indentation width that is not a `case`, or at any token with a smaller
indentation width, whichever comes first.

The rules allow to write `match` expressions where cases are not indented themselves, as in the example below:

```scala
x match
case 1 => print("I")
Expand All @@ -189,6 +197,7 @@ println(".")
Indentation-based syntax has many advantages over other conventions. But one possible problem is that it makes it hard to discern when a large indentation region ends, since there is no specific token that delineates the end. Braces are not much better since a brace by itself also contains no information about what region is closed.

To solve this problem, Scala 3 offers an optional `end` marker. Example:

```scala
def largeMethod(...) =
...
Expand All @@ -199,80 +208,84 @@ def largeMethod(...) =
... // more code
end largeMethod
```

An `end` marker consists of the identifier `end` and a follow-on specifier token that together constitute all the tokes of a line. Possible specifier tokens are
identifiers or one of the following keywords

```scala
if while for match try new this val given
```

End markers are allowed in statement sequences. The specifier token `s` of an end marker must correspond to the statement that precedes it. This means:

- If the statement defines a member `x` then `s` must be the same identifier `x`.
- If the statement defines a constructor then `s` must be `this`.
- If the statement defines an anonymous given, then `s` must be `given`.
- If the statement defines an anonymous extension, then `s` must be `extension`.
- If the statement defines an anonymous class, then `s` must be `new`.
- If the statement is a `val` definition binding a pattern, then `s` must be `val`.
- If the statement is a package clause that refers to package `p`, then `s` must be the same identifier `p`.
- If the statement is an `if`, `while`, `for`, `try`, or `match` statement, then `s` must be that same token.
- If the statement defines a member `x` then `s` must be the same identifier `x`.
- If the statement defines a constructor then `s` must be `this`.
- If the statement defines an anonymous given, then `s` must be `given`.
- If the statement defines an anonymous extension, then `s` must be `extension`.
- If the statement defines an anonymous class, then `s` must be `new`.
- If the statement is a `val` definition binding a pattern, then `s` must be `val`.
- If the statement is a package clause that refers to package `p`, then `s` must be the same identifier `p`.
- If the statement is an `if`, `while`, `for`, `try`, or `match` statement, then `s` must be that same token.

For instance, the following end markers are all legal:
```scala
package p1.p2:

abstract class C():

def this(x: Int) =
this()
if x > 0 then
val a :: b =
x :: Nil
end val
var y =
x
end y
while y > 0 do
println(y)
y -= 1
end while
try
x match
case 0 => println("0")
case _ =>
end match
finally
println("done")
end try
end if
end this

def f: String
end C

object C:
given C =
new C:
def f = "!"
end f
end new
end given
end C

extension (x: C)
def ff: String = x.f ++ x.f
end extension

end p2

```scala
package p1.p2:

abstract class C():

def this(x: Int) =
this()
if x > 0 then
val a :: b =
x :: Nil
end val
var y =
x
end y
while y > 0 do
println(y)
y -= 1
end while
try
x match
case 0 => println("0")
case _ =>
end match
finally
println("done")
end try
end if
end this

def f: String
end C

object C:
given C =
new C:
def f = "!"
end f
end new
end given
end C

extension (x: C)
def ff: String = x.f ++ x.f
end extension

end p2
```

#### When to Use End Markers

It is recommended that `end` markers are used for code where the extent of an indentation region is not immediately apparent "at a glance". People will have different preferences what this means, but one can nevertheless give some guidelines that stem from experience. An end marker makes sense if

- the construct contains blank lines, or
- the construct is long, say 15-20 lines or more,
- the construct ends heavily indented, say 4 indentation levels or more.
- the construct contains blank lines, or
- the construct is long, say 15-20 lines or more,
- the construct ends heavily indented, say 4 indentation levels or more.

If none of these criteria apply, it's often better to not use an end marker since the code will be just as clear and more concise. If there are several ending regions that satisfy one of the criteria above, we usually need an end marker only for the outermost closed reason. So cascades of end markers as in the example above are usually better avoided.
If none of these criteria apply, it's often better to not use an end marker since the code will be just as clear and more concise. If there are several ending regions that satisfy one of the criteria above, we usually need an end marker only for the outermost closed region. So cascades of end markers as in the example above are usually better avoided.

#### Syntax

Expand Down Expand Up @@ -359,14 +372,16 @@ times(10):
println("ah")
println("ha")
```

or

```scala
xs.map:
x =>
val y = x - 1
y * y
```

Colons at the end of lines are their own token, distinct from normal `:`.
The Scala grammar is changed in this variant so that colons at end of lines are accepted at all points
where an opening brace enclosing a function argument is legal. Special provisions are taken so that method result types can still use a colon on the end of a line, followed by the actual type on the next.