-
Notifications
You must be signed in to change notification settings - Fork 333
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
improvement: use pc for finding references of local symbols and when semanticdb is missing #5940
Conversation
a4d5d77
to
af2ba5a
Compare
ec87650
to
64412d6
Compare
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 work!
I have some questions, but nothing major
mtags/src/main/scala/scala/meta/internal/mtags/ScalaToplevelMtags.scala
Outdated
Show resolved
Hide resolved
set.foreach(bloom.put) | ||
} | ||
|
||
private def collectIdentifiers( |
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.
Why do we need this? Isn't the referencesProvider().addIdentifiers(source, identifiers)
in Indexer.scala
enough?
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.
It updates identifiers on didSave
.
metals/src/main/scala/scala/meta/internal/rename/RenameProvider.scala
Outdated
Show resolved
Hide resolved
mtags/src/main/scala-3/scala/meta/internal/pc/PcSymbolSearch.scala
Outdated
Show resolved
Hide resolved
mtags/src/main/scala-3/scala/meta/internal/pc/PcRenameProvider.scala
Outdated
Show resolved
Hide resolved
mtags/src/main/scala-3/scala/meta/internal/pc/PcReferencesProvider.scala
Outdated
Show resolved
Hide resolved
405ac4a
to
ca68174
Compare
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.
That is some great work, would be awesome if we really just allow user only a bit slower experience when semanticdb is missing.
/** | ||
* Returns the references of the symbol under the current position in the target files. | ||
*/ | ||
public CompletableFuture<java.util.List<DefinitionResult>> references(OffsetParams params, java.util.List<VirtualFileParams> targetFiles, boolean includeDefinition) { |
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 should probably add an additional ReferenceResults
class just for the sake of clarity. And also we could have MultipleFileOffsetParams
or ReferenceOffsetParams
, so we would not need more params
|
||
case class IndexEntry( | ||
id: BuildTargetIdentifier, | ||
bloom: BloomFilter[CharSequence], | ||
) | ||
val index: TrieMap[Path, IndexEntry] = TrieMap.empty | ||
val tokenIndex: TrieMap[Path, IndexEntry] = TrieMap.empty |
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.
Do you have an idea how much more memory this takes up? And maybe how much longer indexing takes? Or are the indexing times negligible since we already tokenize the files?
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'll check both. Indexing should be similar but there may be additional cost to building the bloom filter.
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.
In metals repo:
memory: identifier index using 1.37M (77,536 elements)
Benchmarks:
Benchmark Mode Cnt Score Error Units
MetalsBench.alltoplevelsScalaIndex ss 10 0.419 ± 0.003 s/op
MetalsBench.alltoplevelsScalaIndexWithCollectIdents ss 10 0.499 ± 0.011 s/op
MetalsBench.alltoplevelsScalaIndexWithBuildIdentifierIndex ss 10 0.627 ± 0.016 s/op
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 should be fine then, that's not a lot 👍
The only issue I can think of is anything that is not named like apply methods, but we can try and handle that later.
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.
Sure. I guess the best way to go about it would be not to send targetUri
s right away but have pc resolve the name. We could also use untyped trees. Anyway it will quickly get hairy for things like:
case class Foo() {
def apply()
}
val f = Foo()
val h = f()
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 could also look at synthetic symbols as we already have some logic for it (or at least the apply one) and we search for apply, it should be found properly
} | ||
if targetFiles.nonEmpty | ||
} yield compilers | ||
.references(params, targetFiles, EmptyCancelToken) |
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.
Would be great to forward an actual cancel token so if it takes longer to find references this can actually be cancelled.
metals/src/main/scala/scala/meta/internal/metals/ReferenceProvider.scala
Show resolved
Hide resolved
class PcReferencesProvider( | ||
compiler: MetalsGlobal, | ||
params: OffsetParams, | ||
targetFiles: List[VirtualFileParams], |
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 wonder if we shouldn't read the files on demand instead of sending them in the params here. There are cases when we will not need targetFiles to be read at all. So maybe we should just read it here. Also sending too many large files might cause OOM maybe?
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.
Yeah, I think it's reasonable to ask for batches instead of giving all at once.
mtags/src/main/scala-2/scala/meta/internal/pc/PcReferencesProvider.scala
Outdated
Show resolved
Hide resolved
b0f6da9
to
27707f9
Compare
3993147
to
5e021f6
Compare
Maybe we should also use pc for files with stale semanticDB? |
That makes sense, would reduce the need to calculate the diff, which isn't 100% sure anyway. We can do that later if needed. |
5e021f6
to
3e2b2eb
Compare
3e2b2eb
to
8db6722
Compare
params.params().uri().toString(), | ||
highlight.getRange(), | ||
) | ||
val adjust = |
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.
Could we adjsut things in Compilers
as with all other requests?
@@ -1362,3 +1401,11 @@ object Compilers { | |||
case object Default extends PresentationCompilerKey | |||
} | |||
} | |||
|
|||
case class PcAdjustFileParams( |
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 would not add these new params if possible to just adjsut results afterward.
mtags-interfaces/src/main/java/scala/meta/pc/ReferencesRequest.java
Outdated
Show resolved
Hide resolved
|
||
check( | ||
"basic3", | ||
"""|/a/src/main/scala/O.scala |
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.
Might be worth adding a few mroe tests here
@@ -703,6 +719,28 @@ class Compilers( | |||
} | |||
}.getOrElse(Future.successful(Nil.asJava)) | |||
|
|||
def references( | |||
params: ReferenceParams, | |||
targetFiles: List[AbsolutePath], |
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.
targetFiles: List[AbsolutePath], | |
searchInFiles: List[AbsolutePath], |
.map( | ||
_.asScala.toList.map { defRes => | ||
val locations = defRes.locations().asScala.toList | ||
ReferencesResult(defRes.symbol(), locations) |
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.
Can we run adjust on locations here?
val request = PcReferencesRequest( | ||
CompilerOffsetParamsUtils.fromPos(pos, token), | ||
params.getContext().isIncludeDeclaration(), | ||
targetFiles.map(_.toURI).asJava, |
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 instead, we first run references on the main file, which then returns the symbol and then we use that symbol on each of the other files? I think we were talking about it at some point, but it seems much less added mental overhead.
We would need to maybe create ReferenceParams that contain:
Either[Position, Symbol]
If it's a position then we search at the specific position and all references, otherwise we search for symbol. We don't need to have the additional Buffers etc.
|
||
private val compilers: Compilers = register( | ||
val compilers: Compilers = register( |
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.
Why remove private?
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 can't find it being used currently, could be private
363234b
to
0985176
Compare
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.
Some minor comments, otherwise a see a few things for a follow up:
- we should close presentation compiler for non active targets, otherwise we might easily go OOM in for example Bazel
- we don't search for references if we do go to def on definition
- it seems like sometimes we get duplicated location, where one location is empty, not sure if I can reproduce, but will try
|
||
private val compilers: Compilers = register( | ||
val compilers: Compilers = register( |
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 can't find it being used currently, could be private
metals/src/main/scala/scala/meta/internal/metals/ReferenceProvider.scala
Outdated
Show resolved
Hide resolved
import org.eclipse.{lsp4j => l} | ||
|
||
trait PcReferencesProvider { | ||
_: WithCompilationUnit with PcCollector[(String, Option[l.Range])] => |
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.
Ideally WithCompilationUnit would also be a trait so that we can use normal inheritance, but if possible it can be fixed later.
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'm not sure yet how to do that nicely.
mtags/src/main/scala/scala/meta/internal/mtags/MtagsIndexer.scala
Outdated
Show resolved
Hide resolved
👍
included in the PR description
I purposefully return
|
04c2c9f
to
3321128
Compare
semanticdb only:
time: found 459 references to symbol 'scala/Boolean#`&&`().' in 0.25s
bloom filters + pc (that's a bit nonsensical, since bloom filters are build based on semanticdb):
time: found 429 references to symbol 'scala/Boolean#`&&`().' in 6.47s
Note: This won't work for
definitionOrReferences
if semanticdb is missing. But it requires a bit more effort to make it work reasonably, so I'd rather leave it as a followup.connected to: #5759