-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Reimplement SyntaxHighlighter using Scanner and Parser #4537
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
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.
Hello, and thank you for opening this PR! 🎉
All contributors have signed the CLA, thank you! ❤️
Have an awesome day! ☀️
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.
Great job! Once you're done, you can remove all the code from the old implementation
|
||
val sf = new SourceFile("<highlighting>", in.toCharArray) | ||
val p = new Parser(sf) | ||
val s = new Scanner(sf) |
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 we could use better names: s/sf/source, s/p/parser, s/s/scanner, s/p/pos...
|
||
val outputH = Array.fill(in.length)(NoColor) | ||
|
||
def highlightRange(p: Position, color: String): 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.
We usually don't type annotate inner methods
def highlightRange(p: Position, color: String): Unit = { | ||
if(p.exists) { | ||
for { | ||
i <- p.start until math.min(p.end, outputH.length) |
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 will hide bugs with positions. Let's catch IndexOutOfBoundException
and print something like:
Encountered tree with invalid position, please open an issue with the code snippet that caused the error
val outputH = Array.fill(in.length)(NoColor) | ||
|
||
def highlightRange(p: Position, color: String): Unit = { | ||
if(p.exists) { |
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.
missing space after if
if(p.exists) { | ||
for { | ||
i <- p.start until math.min(p.end, outputH.length) | ||
} outputH(i) = color |
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.
Prefer syntax with parentheses:
for (i <- p.start until p.end)
outputH(i) = color
} | ||
sb.append(in(idx)) | ||
} | ||
if(outputH.last != NoColor) { |
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.
What if the input is empty? I would test against this at the beginning
for { | ||
idx <- outputH.indices | ||
} { | ||
if(idx == 0 || outputH(idx-1) != outputH(idx)){ |
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.
If color at index 0 is NoColor
, we should not add it
val offsetStart = s.offset | ||
|
||
if(s.token == IDENTIFIER && s.name == nme.???) { | ||
highlightRange(Position(s.offset, s.offset + s.name.length), Console.RED_B) |
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.
define highlightRange
and highlightPosition
as:
def highlightRange(from: Int, to: Int, color: String) = ...
def highlightPosition(pos: Postition, color: String) =
if (pos.exists) highlightRange(pos.start, pos.end, color)
sb.append(NoColor) | ||
} | ||
|
||
sb.mkString |
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.
Even if it does the same thing, we prefer calling toString
on StringBuilder
|
||
val sf = new SourceFile("<highlighting>", in.toCharArray) | ||
val p = new Parser(sf) | ||
val s = new Scanner(sf) |
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.
Move the scanner declaration closer to use site.
@allanrenucci PR was updated addressing your comments. |
You should be able to match on
In |
colorAt(i) = color | ||
} catch { | ||
case _: IndexOutOfBoundsException => | ||
ctx.error("Encountered tree with invalid position, please open an issue with the code snippet that caused the error") |
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.
ctx.error
will not print anything since your are using a dummy reporter. Simply use println
@slothspot Let's not bother about annotations in this PR. This can be fixed in a separate PR. I'd like to get this in and close all bugs related to syntax highlighting. Can you update the tests cases and add |
Preliminary support for annotations was added. Questions: |
And due to your last request: |
You are matching the contstructor of the class. The position looks weird to me too. I will have to investigate. What you can try to do is not print synthetic positions:
Yes, let's handle annoations in a separate PR. Please remove the code for annotations. You can keep the tests and mark them as
Yes, we can always change it later
You could special case the tree |
|
||
if (isKwd) { | ||
val offsetEnd = scanner.lastOffset | ||
highlightPosition(Position(offsetStart, offsetEnd), KeywordColor) |
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.
highlightRange
?
|} | ||
""".stripMargin | ||
|
||
test(source, expected) |
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.
Prefer simple single line test case:
test("@inline val bar = 42", ...)
@@ -89,12 +111,12 @@ class SyntaxHighlightingTests { | |||
|
|||
@Test | |||
def unionTypes = { |
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.
Let remove this test and move it to def types
. Also I don't think we need all these combinations. Let's keep:
test("type A = String|Int", "<K|type> <T|A> = <T|String|Int>")
test("type B = String & Int", "<K|type> <T|A> = <T|String & Int>")
test("fn[String](input)", "fn[<T|String>](input)")
import util.Chars | ||
import util.{Chars, SourceFile} | ||
|
||
import scala.collection.mutable | ||
|
||
/** This object provides functions for syntax highlighting in the REPL */ | ||
object SyntaxHighlighting { |
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.
Remove all the legacy code
d4b4a92
to
53cf6ec
Compare
53cf6ec
to
183ce6e
Compare
0921b19
to
fa0ea25
Compare
@smarter Added more tests that catch current bugs. Please cherry-pick. |
@danielyli Thanks! Pushed to the PR. |
- Highlight is based on MemberDef with annotations present - move scanner highlighter before parser so that annotation @inline wholdn't be highlighted as keyword - annotations are highlighted when full member definition is available
If we ignore trees named <error> and <init>, it is not so fragile after all...
Prior to this commit, an annotation with one or more parameter lists would have incorrect positions attached to each parameter list. For example, the annotation @foo("bar") would have the erroneous position `<4..10>` attached to its `Apply` node instead of the correct position `<4..11>`. This was caused by forget- ting to take into account the closing parenthesis.
These tests currently fail because of a bug in the syntax highlighter.
Test for `1Lx` currently fails; tests for float literals pass.
d6444e3
to
4b08b9c
Compare
4b08b9c
to
9ea233b
Compare
I rebased the PR. The fix in d31f32e is not correct. It breaks a lot of stuff. The compiler does not bootstrap anymore |
The end position was correctly set but the start position was wrong: the start position of `fn(args)` should be the same as the start position of `fn` itself.
Closed in favor of #5137. Thanks @slothspot for the original PR |
WORK IN PROGRESS
Problems
Nice parts:
Next steps:
fix tests and reach features parity with current highlighter implementation
@allanrenucci I would appreciate your feedback on the current state of this experiment and clarification on comments highlighting and list of keywords which shouldn't be highlighted