-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Update -Yindent-colons functionality #11752
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
Conversation
Systamtically accept a `:` as COLON when it cannot be confused with a `:` at EOL.
Treat instead indentation as significant. I.e. return 2 // one statement return println(2) // two statements
Replace -Yindent-colons with language import import language.experimental.fewerBraces
08853d4
to
e80a192
Compare
This allows a useful pattern for expressing curried function arguments with `:`. E.g. files.get(fileName).fold: "not found" : f => f.readLine() Without the change here, the second `:` would have to be on the same column as `files`.
Two reasons: 1. It would complicate the syntax 2. It looks weird anyway
Some users might write things like `then:` by accident. In this case we should report an error, skip the `:`, and continue. No skip ahead to an anchor token is wanted.
@tgodzik Do you want to review this? That would be a good start to adapt Metals accordingly. We need this merged by Monday. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for letting me know! We do not support the experimental flag in scalameta yet. We want to make most tooling work for the basic optional braces first. Which means, we are focusing on scalafmt currently.
I do have my doubts about this syntax, but it might be better to leave up to the community to give some feedback on this.
@@ -273,7 +273,7 @@ object Tokens extends TokensCommon { | |||
final val closingRegionTokens = BitSet(RBRACE, RPAREN, RBRACKET, CASE) | statCtdTokens | |||
|
|||
final val canStartIndentTokens: BitSet = | |||
statCtdTokens | BitSet(COLONEOL, WITH, EQUALS, ARROW, CTXARROW, LARROW, WHILE, TRY, FOR, IF, THROW) | |||
statCtdTokens | BitSet(COLONEOL, WITH, EQUALS, ARROW, CTXARROW, LARROW, WHILE, TRY, FOR, IF, THROW, RETURN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a global change, not only for the experimental flag? We should update it in Scalameta then, but should not be problematic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's a global change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val fileNames = files.values | ||
s"""no file named $fileName found among | ||
|${values.mkString(\n)}""".stripMargin | ||
: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we able to handle this another way? It seems a bit wrong? Kind of like someone left the colon by mistake.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe something like a soft keyword and
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think curried functions with complex arguments are pretty much an anti-pattern anyway, but others like their Option.fold
s. The current possibility falls out from the rules we have. Before this PR there was a special added rule that disallowed the :
at start of line. But that would leave no way to handle curried functions except with braces. So I think it's probably better to have simpler, more orthogonal rules and let users decide whether they like this pattern or whether they want to keep using braces after all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like it might look better if there will be an additional per-parameter indentation.
Smth like:
val firstLine = files.get(filename).fold:
{smth here} =>
val filenames = files.values
s"""no file named $fileName found among
|${values.mkString(\n)}""".stripMargin
f =>
val lines = f.iterator.map(_.readline)
lines.mkString("\n")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does not work. This would group both lambdas in a sequence as a single parameter block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I missed that :
is about different parameter sections.
Should this work if :
have the same indentation level as the first parameter?
val firstLine = files.get(filename).fold:
val filenames = files.values
s"""no file named $fileName found among
|${values.mkString(\n)}""".stripMargin
:
f =>
val lines = f.iterator.map(_.readline)
lines.mkString("\n")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, then it does not work. Everything at the same indentation level is one argument
|
||
Similar to what is done for classes and objects, a `:` that follows a function reference at the end of a line means braces can be omitted for function arguments. Example: | ||
```scala | ||
times(10): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks rather ok, but honestly I am not sure if it helps with the readability. But I guess if we are going fully with optional braces, we should allow this.
else | ||
2 | ||
|
||
def bar(): Unit = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this equivalent to:
def bar(): Unit =
return assert(false)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No; assert(false) is dead code in that case.
Previously, return could not start an indentation, but starting with scala/scala3#11752 it can. This fixes scalameta to parse accordingly.
This reverts commit 0bd9739. Change syntax and tests to allow `:` after operator. It turns out that the intent project of the CB already uses them in a way that looks quite reasonable.
@tgodzik Is this ready to merge? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good on my side!
Previously, return could not start an indentation, but starting with scala/scala3#11752 it can. This fixes scalameta to parse accordingly. Additionally, this change also created some issues with empty `return` statements in pattern matches. We would incorrectly think that the next statement is indented, because we never saved case block indentation. I added a new CaseRegion in this PR, so that we can remember the indentation. This is only added in `match` with braces as indented match clauses work correctly here.
Previously, return could not start an indentation, but starting with scala/scala3#11752 it can. This fixes scalameta to parse accordingly. Additionally, this change also created some issues with empty `return` statements in pattern matches. We would incorrectly think that the next statement is indented, because we never saved case block indentation. I added a new CaseRegion in this PR, so that we can remember the indentation. This is only added in `match` with braces as indented match clauses work correctly here.
Previously, return could not start an indentation, but starting with scala/scala3#11752 it can. This fixes scalameta to parse accordingly. Additionally, this change also created some issues with empty `return` statements in pattern matches. We would incorrectly think that the next statement is indented, because we never saved case block indentation. I added a new CaseRegion in this PR, so that we can remember the indentation. This is only added in `match` with braces as indented match clauses work correctly here.
Previously, return could not start an indentation, but starting with scala/scala3#11752 it can. This fixes scalameta to parse accordingly. Additionally, this change also created some issues with empty `return` statements in pattern matches. We would incorrectly think that the next statement is indented, because we never saved case block indentation. I added a new CaseRegion in this PR, so that we can remember the indentation. This is only added in `match` with braces as indented match clauses work correctly here.
I just want to mention that it's pretty jarring not to have this feature currently. See e.g. munit test syntax -- you are forced to use braces for every test: test("mytest") {
assert(true)
} |
I agree. It's the maximum we could achieve against community pushback and
general skepticism. If you want this, you can help by demanding the
functionality and defending it vigorously in contributors.
…On Wed, Apr 7, 2021 at 8:26 AM Felix Palludan Hargreaves < ***@***.***> wrote:
I just want to mention that it's pretty jarring not to have this feature
currently. See e.g. munit test syntax -- you are *forced* to use braces
for every test:
test("mytest") {
assert(true)
}
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub, or unsubscribe.
--
Prof. Martin Odersky
LAMP/IC, EPFL
|
I have another use case in which makes this useful - example code were we want to shadow variables with the same name (reinventing names is tedious). Code for students using the DJL Java library, braces here just seems excessive. val manager = NDManager.newBaseManager()
val x = manager.create(Array(1, 2, 3))
println(s"x.getDevice() = ${x.getDevice()}")
// 5.5.2.1. Storage on the GPU: https://d2l.djl.ai/chapter_deep-learning-computation/use-gpu.html#storage-on-the-gpu
locally:
val x = manager.ones(new Shape(2, 3), DataType.FLOAT32, tryGpu(0))
println(s"x = $x")
// Second GPU?
val y = manager.ones(new Shape(2, 3), DataType.FLOAT32, tryGpu(0))
println(s"y = $y")
// 5.5.2.2. Copying: https://d2l.djl.ai/chapter_deep-learning-computation/use-gpu.html#copying
val z = x.toDevice(tryGpu(1), true)
println(s"Gpu(0) x = $x")
println(s"Gpu(1) z = $z")
// Now that the data is on the same GPU (both z and y are), we can add them up.
y.add(z)
I take it that would be in this link? Seems to be closed though. https://contributors.scala-lang.org/t/feedback-sought-optional-braces/4702/587 But now I cannot use this in RC2: import language.experimental.fewerBraces because of: or am I wrong? TIA |
Move functionality under -Yindent-colons closer to what we discussed in the contributors thread
experimental.fewerBraces
language feature:
at EOL as a normal:
if it cannot start an argumentreturn
; no:
required or allowedNOTE: The changes to indentation after
return
are in standard Scala 3. The other implemented functionality is under an language importimport experimental.fewerBraces
. It's not part of Scala 3.0 and might still change in the future.I also attempted a change to make indentation significant after symbolic operators. But I had to abandon that part, since it changes semantics. Consider
If we made indentation significant after
-
, the expression would be parsed asa - (b + c)
instead of(a - b) + c
. So we can't do that.