More identation syntax end marker variations

The current indentation syntax leaves me frequently wanting for more end marker variations when I’m using imperative method calls (read: not defining vals/vars/defs/…) or when the last expression of a block is meant to be the result to a higher-order function so there’s no need to name the result.

For instance:

locally:
  // do things
end locally // can we support this?

elements.foreach: elem =>
  // do something with `elem`
end foreach // can we support this?

db.transaction:
  // do work
end transaction

elements.map: el =>
  Future:
    // async code
  end Future
.awaitAll

There was some discussion [1] about this over 2 years ago … but this was before indentation syntax became more widely supported and adopted. Also ‘direct style’ seems to promoted as an important language direction, and within that style there’s often many code blocks delimited by methods that provide capabilities. Maybe the outlook and attitudes have changed since? Is this worth revisiting?

thanks

[1] On braces and indentation-based syntax - #10 by Ichoran

3 Likes

Relevant discussion: End markers for fewer braces blocks

1 Like

I didn’t start a discussion or a ticket but a PR.

I was quickly scared off by the use of the S word. (Either “spec” or “SIP”.)

I only dipped my toe in the water, which was quite cold.

You already mention allowing Future for implicit apply. :+1:

I agree generalizing end markers like this would useful. I won’t have time to work on this personally (and yes, the majority of the work would be SIP and Spec), but if others want to push this I’d be very interested.

1 Like

I agree that it can be occasionally be tricky to discern where a fewer-braces block ends, but I think the answer to that is better tooling rather than a change to the language spec. We already have editors that can highlight matching braces or show inferred types. There’s no reason why they wouldn’t be able to show where a block ends. Ideally they’d only do this for blocks longer than a certain threshold, maybe 10 lines.

In metals we could add Inlay Hints to show artificial end markers and have a code action to add them. That should not be a very hard issue to do actually.

8 Likes

Ok, end markers seem to be tricky since they require an empty line to exist where we could put the actual inlay hints. We could make that work, but a lot of edge cases would be impossible to handle.

With a source like the below we don’t have any place to put the markers

object Main:
  def hello =
     println("Hello")
     println("Hello")

but here we can (inlay hints denoted by comments)

object Main:
  def hello =
     println("Hello")
     println("Hello")
  /*end hello*/
/*end Main*/

but if we had one line available it becomes problematic again.

We could just add them at the end of the line like:

object Main:
  def hello =
     println("Hello")
     println("Hello") /*end hello*/ /*end Main*/

Another thing we could do is add artificial braces, but that would also feel awkward and doesn’t feel too useful:

object Main: /*{*/
  def hello =  /*{*/
     println("Hello")
     println("Hello") /*}*/ /*}*/

opinions? I think the end of line markers are probably most helpful and least weird, though not ideal for sure.

I rely on scalafmt to add end markers above a certain number of lines. I think dedicated inlay hints are somewhat redundant with AI and scalafmt at our disposal.

1 Like

I prefer inlay hints than actual code !
Especially because I can remove it if it becomes too burdensome

Isn’t there a way to add space between lines ?
For example on top of a main method I get the run | debug buttons

This would look something like:

1 object Main:
2 def hello =
3   println("Hello")
4   println("Hello")
  end def // this would be greyed like inferred types
5 whateverIsOnThisLine
1 Like

The run | debug buttons are code lenses and by default there is no way to add them below a line. It’s only possible to add them above.

Also, code lenses are not used to show things like end markers, it’s inlay hints that is usually reserved for such usages and it needs to have an existing line.

Maybe, we could try to add end markers to an empty line if it exists, otherwise show them on the right.

Well we don’t really need end markers at the very end of the file, what about:

1 object Main:
2 def hello =
3   println("Hello")
4   println("Hello")
v  end def // code lens "for the following line"
5 whateverIsOnThisLine

scalafmt is not an ideal solution because most of the time you are not in control of the scalafmt configuration. Unless you can convince your teammates (or that random GitHub project you want to contribute a fix to) of such a scalafmt configuration, inlay hints provided by your editor are a better solution because they work with every codebase you open in your editor. Unfortunately it doesn’t seem to be possible at least in VS Code, according to @tgodzik. And convincing Microsoft of allowing “inlay hint lines” is probably not easy.

But maybe having in inlay hint at the end of the last line of the block is good enough.

Created a feature request to gather more feedback Show inlay hints for end markers · Issue #433 · scalameta/metals-feature-requests · GitHub

I should be able to implement this fairly quickly, though I will still ponder about how best to do it. Maybe I will figure out a way to do it better.

3 Likes