Skip to content

Commit

Permalink
fix: correctly handle odd formatting while indexing
Browse files Browse the repository at this point in the history
  • Loading branch information
kasiaMarek committed Aug 5, 2024
1 parent 6136010 commit 66cfbde
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 54 deletions.
171 changes: 119 additions & 52 deletions mtags/src/main/scala/scala/meta/internal/mtags/ScalaToplevelMtags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import scala.meta.internal.semanticdb.Scala._
import scala.meta.internal.semanticdb.SymbolInformation
import scala.meta.internal.semanticdb.SymbolInformation.Kind
import scala.meta.internal.tokenizers.LegacyScanner
import scala.meta.internal.tokenizers.LegacyToken
import scala.meta.internal.tokenizers.LegacyToken._
import scala.meta.internal.tokenizers.LegacyTokenData
import scala.meta.tokenizers.TokenizeException
Expand Down Expand Up @@ -275,7 +276,13 @@ class ScalaToplevelMtags(
)
}
}
loop(indent, isAfterNewline = false, currRegion, newExpectIgnoreBody)
val newIndent = parseMemberDefinitionLhs(DEF)
loop(
newIndent.getOrElse(indent),
isAfterNewline = newIndent.isDefined,
currRegion,
newExpectIgnoreBody
)
// inline extension method `extension (...) def foo = ...`
case DEF
if includeMembers && expectTemplate
Expand Down Expand Up @@ -314,22 +321,21 @@ class ScalaToplevelMtags(
currRegion.withTermOwner(owner),
expectTemplate
)
case DEF | VAL | VAR | GIVEN
case t @ (DEF | VAL | VAR | GIVEN)
if expectTemplate.map(!_.isExtension).getOrElse(true) =>
val isImplicit =
if (isInParenthesis(region))
expectTemplate.exists(
_.isImplicit
)
else region.isImplicit
if (needEmitTermMember()) {
withOwner(currRegion.termOwner) {
emitTerm(currRegion, isImplicit)
}
} else scanner.mtagsNextToken()
withOwner(currRegion.termOwner) {
emitTerm(currRegion, isImplicit, needEmitTermMember())
}
val newIndent = parseMemberDefinitionLhs(t)
loop(
indent,
isAfterNewline = false,
newIndent.getOrElse(indent),
isAfterNewline = newIndent.isDefined,
currRegion,
if (isInParenthesis(region)) expectTemplate else newExpectIgnoreBody
)
Expand All @@ -348,6 +354,18 @@ class ScalaToplevelMtags(
// skip comment because they might break indentation
scanner.mtagsNextToken()
loop(indent, isAfterNewline = false, currRegion, expectTemplate)
case EQUALS
if expectTemplate.exists(
_.ignoreBody
) && dialect.allowSignificantIndentation =>
val nextIndent =
acceptWhileIndented(expectTemplate.get.indent, isInsideBody = false)
loop(
nextIndent,
isAfterNewline = false,
currRegion,
None
)
case token
if isWhitespace(token) && dialect.allowSignificantIndentation =>
if (isNewline) {
Expand All @@ -360,15 +378,6 @@ class ScalaToplevelMtags(
resetRegion(next)
scanner.mtagsNextToken()
loop(0, isAfterNewline = true, next, None)
// basically for braceless def
case Some(expect) if expect.ignoreBody =>
val nextIndent = acceptWhileIndented(expect.indent)
loop(
nextIndent,
isAfterNewline = false,
currRegion,
None
)
case _ =>
scanner.mtagsNextToken()
loop(
Expand Down Expand Up @@ -647,6 +656,42 @@ class ScalaToplevelMtags(

}

private def parseMemberDefinitionLhs(token: LegacyToken): Option[Int] =
token match {
case DEF =>
val ident = parseMethodArgs()
val ident0 = parseTypeAnnotation(ident)
ident0
case _ => parseTypeAnnotation(None)
}

private def parseTypeAnnotation(ident: Option[Int]) = {
curr.token match {
case COLON =>
val newIdent = acceptTrivia()
curr.token match {
case IDENTIFIER =>
acceptAllAfterOverriddenIdentifier()
case _ => newIdent
}
case _ => ident
}
}

@tailrec
private def parseMethodArgs(): Option[Int] = {
val additionalIdent = acceptTrivia()
curr.token match {
case LBRACKET =>
acceptBalancedDelimeters(LBRACKET, RBRACKET)
parseMethodArgs()
case LPAREN =>
acceptBalancedDelimeters(LPAREN, RPAREN)
parseMethodArgs()
case _ => additionalIdent
}
}

@tailrec
private def findOverridden(
acc0: List[Identifier]
Expand Down Expand Up @@ -764,48 +809,56 @@ class ScalaToplevelMtags(
/**
* Enters a global element (def/val/var/given)
*/
def emitTerm(region: Region, isParentImplicit: Boolean): Unit = {
def emitTerm(
region: Region,
isParentImplicit: Boolean,
shouldEmit: Boolean
): Unit = {
val extensionProperty = if (isParentImplicit) EXTENSION else 0
val kind = curr.token
acceptTrivia()
kind match {
case VAL =>
valIdentifiers.foreach(name => {
term(
name.name,
name.pos,
Kind.METHOD,
SymbolInformation.Property.VAL.value | extensionProperty
)
if (shouldEmit)
term(
name.name,
name.pos,
Kind.METHOD,
SymbolInformation.Property.VAL.value | extensionProperty
)
resetRegion(region)
})
case VAR =>
valIdentifiers.foreach(name => {
method(
name.name,
"()",
name.pos,
SymbolInformation.Property.VAR.value | extensionProperty
)
if (shouldEmit)
method(
name.name,
"()",
name.pos,
SymbolInformation.Property.VAR.value | extensionProperty
)
resetRegion(region)
})
case DEF =>
methodIdentifier.foreach(name =>
method(
name.name,
region.overloads.disambiguator(name.name),
name.pos,
extensionProperty
)
if (shouldEmit)
method(
name.name,
region.overloads.disambiguator(name.name),
name.pos,
extensionProperty
)
)
case GIVEN =>
newGivenIdentifier.foreach { name =>
method(
name.name,
region.overloads.disambiguator(name.name),
name.pos,
SymbolInformation.Property.GIVEN.value
)
if (shouldEmit)
method(
name.name,
region.overloads.disambiguator(name.name),
name.pos,
SymbolInformation.Property.GIVEN.value
)
}
}

Expand Down Expand Up @@ -885,29 +938,43 @@ class ScalaToplevelMtags(
/**
* Consumes the token stream until outdent to the same indentation level
*/
def acceptWhileIndented(exitIndent: Int): Int = {
def acceptWhileIndented(
exitIndent: Int,
isInsideBody: Boolean = true
): Int = {
@tailrec
def loop(indent: Int, isAfterNL: Boolean): Int = {
def loop(indent: Int, isAfterNL: Boolean, isInsideBody: Boolean): Int = {
if (!isDone) {
curr.token match {
case _ if isNewline => scanner.mtagsNextToken(); loop(0, true)
case _ if isNewline =>
scanner.mtagsNextToken(); loop(0, true, isInsideBody)
case token if isWhitespace(token) && isAfterNL =>
scanner.mtagsNextToken()
loop(indent + 1, true)
loop(indent + 1, true, isInsideBody)
case token if isWhitespace(token) =>
scanner.mtagsNextToken()
loop(indent, false)
loop(indent, false, isInsideBody)
case COMMENT =>
scanner.mtagsNextToken()
loop(indent, false)
case _ if indent <= exitIndent => indent
loop(indent, false, isInsideBody)
case _ if isInsideBody && indent <= exitIndent => indent
case LBRACE if !isInsideBody =>
acceptBalancedDelimeters(LBRACE, RBRACE)
scanner.mtagsNextToken()
exitIndent
case EQUALS =>
scanner.mtagsNextToken()
loop(indent, false, isInsideBody)
case _ if !isInsideBody =>
acceptToStatSep()
loop(indent, false, isInsideBody = true)
case _ =>
scanner.mtagsNextToken()
loop(indent, false)
loop(indent, false, isInsideBody)
}
} else indent
}
loop(0, true)
loop(0, true, isInsideBody)
}

def acceptToStatSep(): Unit = {
Expand Down
72 changes: 70 additions & 2 deletions tests/unit/src/test/scala/tests/ScalaToplevelSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,38 @@ class ScalaToplevelSuite extends BaseToplevelSuite {
dialect = dialects.Scala3,
)

check(
"i6643",
"""|package a
|class A {
| def foo(): Int
| = {
| }
| class B
|}
|""".stripMargin,
List("a/", "a/A#", "a/A#foo().", "a/A#B#"),
dialect = dialects.Scala3,
mode = All,
)

check(
"i6643-2",
"""|package a
|class A {
| def foo
| (a: Int)
| (l: Int) = {
| ???
| }
| class B
|}
|""".stripMargin,
List("a/", "a/A#", "a/A#foo().", "a/A#B#"),
dialect = dialects.Scala3,
mode = All,
)

check(
"companion-to-type",
"""|package s
Expand Down Expand Up @@ -638,13 +670,35 @@ class ScalaToplevelSuite extends BaseToplevelSuite {
| case String => Char
| case Array[t] => t
| case Iterable[t] => t
| class Bar
|}
|class Foo
|""".stripMargin,
List("a/", "a/O.", "a/O.A# -> Set", "a/O.H# -> List", "a/O.W# -> Set",
"a/O.R# -> Set", "a/O.L# -> List", "a/O.Elem#"),
List("a/", "a/Foo#", "a/O.", "a/O.A# -> Set", "a/O.H# -> List",
"a/O.W# -> Set", "a/O.R# -> Set", "a/O.L# -> List", "a/O.Bar#",
"a/O.Elem#"),
dialect = dialects.Scala3,
mode = All,
)

check(
"overridden-type-alias-2",
"""|package a
|type A[X] = Set[X]
|type W[X] = mutable.Set[X]
|type Elem[X] = X match
| case String => Char
| case Array[t] => t
| case Iterable[t] =>
| class A()
| a
|class Bar
|""".stripMargin,
List("a/", "a/Bar#", "a/Test$package."),
dialect = dialects.Scala3,
mode = ToplevelWithInner,
)

check(
"refined-type",
"""|package a
Expand Down Expand Up @@ -697,4 +751,18 @@ class ScalaToplevelSuite extends BaseToplevelSuite {
),
mode = All,
)

check(
"value-definition",
"""|package a
|
|object A:
| val a = null.asInstanceOf[User]
| val b = 1
|end A
|""".stripMargin,
List("a/", "a/A.", "a/A.a.", "a/A.b."),
dialect = dialects.Scala3,
mode = All,
)
}

0 comments on commit 66cfbde

Please sign in to comment.