Skip to content

Commit 3e78238

Browse files
feat: Implement completions for named tuple fields (#21202)
closes scala#20478 --------- Co-authored-by: Jędrzej Rochala <[email protected]>
1 parent bbb45ca commit 3e78238

File tree

3 files changed

+96
-4
lines changed

3 files changed

+96
-4
lines changed

Diff for: compiler/src/dotty/tools/dotc/interactive/Completion.scala

+39-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import dotty.tools.dotc.core.Names
3232
import dotty.tools.dotc.core.Types
3333
import dotty.tools.dotc.core.Symbols
3434
import dotty.tools.dotc.core.Constants
35+
import dotty.tools.dotc.core.TypeOps
36+
import dotty.tools.dotc.core.StdNames
3537

3638
/**
3739
* One of the results of a completion query.
@@ -200,7 +202,8 @@ object Completion:
200202

201203
private def computeCompletions(
202204
pos: SourcePosition,
203-
mode: Mode, rawPrefix: String,
205+
mode: Mode,
206+
rawPrefix: String,
204207
adjustedPath: List[tpd.Tree],
205208
untpdPath: List[untpd.Tree],
206209
matches: Option[Name => Boolean]
@@ -442,9 +445,17 @@ object Completion:
442445
def selectionCompletions(qual: tpd.Tree)(using Context): CompletionMap =
443446
val adjustedQual = widenQualifier(qual)
444447

445-
implicitConversionMemberCompletions(adjustedQual) ++
446-
extensionCompletions(adjustedQual) ++
447-
directMemberCompletions(adjustedQual)
448+
val implicitConversionMembers = implicitConversionMemberCompletions(adjustedQual)
449+
val extensionMembers = extensionCompletions(adjustedQual)
450+
val directMembers = directMemberCompletions(adjustedQual)
451+
val namedTupleMembers = namedTupleCompletions(adjustedQual)
452+
453+
List(
454+
implicitConversionMembers,
455+
extensionMembers,
456+
directMembers,
457+
namedTupleMembers
458+
).reduce(_ ++ _)
448459

449460
/** Completions for members of `qual`'s type.
450461
* These include inherited definitions but not members added by extensions or implicit conversions
@@ -516,6 +527,30 @@ object Completion:
516527
.toSeq
517528
.groupByName
518529

530+
/** Completions for named tuples */
531+
private def namedTupleCompletions(qual: tpd.Tree)(using Context): CompletionMap =
532+
def namedTupleCompletionsFromType(tpe: Type): CompletionMap =
533+
val freshCtx = ctx.fresh.setExploreTyperState()
534+
inContext(freshCtx):
535+
tpe.namedTupleElementTypes
536+
.map { (name, tpe) =>
537+
val symbol = newSymbol(owner = NoSymbol, name, EmptyFlags, tpe)
538+
val denot = SymDenotation(symbol, NoSymbol, name, EmptyFlags, tpe)
539+
name -> denot
540+
}
541+
.toSeq
542+
.filter((name, denot) => include(denot, name))
543+
.groupByName
544+
545+
val qualTpe = qual.typeOpt
546+
if qualTpe.isNamedTupleType then
547+
namedTupleCompletionsFromType(qualTpe)
548+
else if qualTpe.derivesFrom(defn.SelectableClass) then
549+
val pre = if !TypeOps.isLegalPrefix(qualTpe) then Types.SkolemType(qualTpe) else qualTpe
550+
val fieldsType = pre.select(StdNames.tpnme.Fields).dealias.simplified
551+
namedTupleCompletionsFromType(fieldsType)
552+
else Map.empty
553+
519554
/** Completions from extension methods */
520555
private def extensionCompletions(qual: tpd.Tree)(using Context): CompletionMap =
521556
def asDefLikeType(tpe: Type): Type = tpe match

Diff for: language-server/test/dotty/tools/languageserver/CompletionTest.scala

+10
Original file line numberDiff line numberDiff line change
@@ -1723,4 +1723,14 @@ class CompletionTest {
17231723
.completion(m5, Set())
17241724
.completion(m6, Set())
17251725

1726+
@Test def namedTupleCompletion: Unit =
1727+
code"""|import scala.language.experimental.namedTuples
1728+
|
1729+
|val person: (name: String, city: String) =
1730+
| (name = "Jamie", city = "Lausanne")
1731+
|
1732+
|val n = person.na$m1
1733+
|"""
1734+
.completion(m1, Set(("name", Field, "String")))
1735+
17261736
}

Diff for: presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala

+47
Original file line numberDiff line numberDiff line change
@@ -1983,3 +1983,50 @@ class CompletionSuite extends BaseCompletionSuite:
19831983
|val foo: SomeClass
19841984
|""".stripMargin,
19851985
)
1986+
1987+
@Test def `namedTuple completions` =
1988+
check(
1989+
"""|import scala.language.experimental.namedTuples
1990+
|import scala.NamedTuple.*
1991+
|
1992+
|val person = (name = "Jamie", city = "Lausanne")
1993+
|
1994+
|val n = person.na@@""".stripMargin,
1995+
"name: String",
1996+
filter = _.contains("name")
1997+
)
1998+
1999+
@Test def `Selectable with namedTuple Fields member` =
2000+
check(
2001+
"""|import scala.language.experimental.namedTuples
2002+
|import scala.NamedTuple.*
2003+
|
2004+
|class NamedTupleSelectable extends Selectable {
2005+
| type Fields <: AnyNamedTuple
2006+
| def selectDynamic(name: String): Any = ???
2007+
|}
2008+
|
2009+
|val person2 = new NamedTupleSelectable {
2010+
| type Fields = (name: String, city: String)
2011+
|}
2012+
|
2013+
|val n = person2.na@@""".stripMargin,
2014+
"""|name: String
2015+
|selectDynamic(name: String): Any
2016+
""".stripMargin,
2017+
filter = _.contains("name")
2018+
)
2019+
2020+
@Test def `Selectable without namedTuple Fields mamber` =
2021+
check(
2022+
"""|class NonNamedTupleSelectable extends Selectable {
2023+
| def selectDynamic(name: String): Any = ???
2024+
|}
2025+
|
2026+
|val person2 = new NonNamedTupleSelectable {}
2027+
|
2028+
|val n = person2.na@@""".stripMargin,
2029+
"""|selectDynamic(name: String): Any
2030+
""".stripMargin,
2031+
filter = _.contains("name")
2032+
)

0 commit comments

Comments
 (0)