Skip to content

Conversation

@warcholjakub
Copy link
Collaborator

Summary

This PR introduces precise error highlighting. Errors are now displayed using their actual range instead of highlighting the entire line.

Key changes

  • Errors are now highlighted using their actual range (startColumn/endColumn), not the entire line.
  • Renamed LineMapper to PositionMapper, as instrumentation can shift both lines and columns. PositionMapper now maps both.
  • Replaced all usages of Int => Int (from the old LineMapper) with Option[PositionMapper].
  • Updated the Problem type: now includes severity, line, message, startColumn, and endColumn.
  • Added and updated tests to verify column mapping in PositionMapperSpecs (formerly LineMapperSpecs).
  • Refactored SBT and Scala-CLI runners and their tests to use the new PositionMapper and to assert precise error positions.
  • Improved error handling for cases where error line is outside the document: such errors are now shown at the end of the last line.

Screenshots / GIFs

Before

pr_1

After

pr_2

warcholjakub and others added 2 commits October 1, 2025 13:00
- Errors are now highlighted using the actual error range (startColumn/endColumn), not the entire line.
- Renamed LineMapper to PositionMapper, as it now maps both lines and columns (instrumentation can shift columns too).
- Replaced all Int => Int usages with Option[PositionMapper].
- Added tests for column mapping in PositionMapperSpecs (formerly LineMapperSpecs).
- Updated the Problem type: now includes severity, line, message, startColumn, and endColumn.
val clampedStart = (start min (lineLength + 1)) max 1
val clampedEnd = (end min (lineLength + 1)) max clampedStart
(lineInfo.from + clampedStart - 1, lineInfo.from + clampedEnd - 1)
case (Some(start), _) if start > 0 =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this ever happen? Maybe lets throw here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question 😅. I honestly can't remember what the use case for this was. I’m sure I had a reason, but from what I’ve tested now, I couldn’t reproduce this case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe lets make it a tuple Option[(Int, Int)] ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Diagnostic requires a Double. Is this the solution you had in mind?

val preciseRangeOpt: Option[(Double, Double)] =
  if (problem.line.get > maxLine) {
    val endPos = lineInfo.to
    Some((endPos, endPos))
  } else {
    (problem.startColumn, problem.endColumn) match {
      case (Some(start), Some(end)) if start > 0 && end >= start =>
        val clampedStart = (start min (lineLength + 1)) max 1
        val clampedEnd = (end min (lineLength + 1)) max clampedStart
        Some((lineInfo.from + clampedStart - 1, lineInfo.from + clampedEnd - 1))
      case _ =>
        None
    }
  }

val (startColumn, endColumn) = preciseRangeOpt match {
  case Some((start, end)) =>
    (start, end)
  case None =>
    (lineInfo.from, lineInfo.to)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants