Skip to content
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: sorting workspace members with same name by frequency #6393

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Temurlock
Copy link
Contributor

inspired by: scalameta/metals-feature-requests#254 (comment)

Added ranking by frequency for workspace symbols with the same name.
It useful when project has multiple objects with same name (like Future (scala, java, twitter) or Clock (java, cats, zio))
It uses ReferenceProvider to count files where workspace member could have been referenced
False positive result from bloom filter is counted as reference in file to keep sorting fast (if we wanted to make it precise we should read semanticdb of matching paths which is expensive)

@Temurlock Temurlock force-pushed the sorting_workspace_members branch 2 times, most recently from c19e31b to b764a3a Compare May 9, 2024 21:05
Copy link
Contributor

@filipwiech filipwiech left a comment

Choose a reason for hiding this comment

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

This seems like a really nice idea, and limiting it to the symbols with the same name only looks like a safe bet that shouldn't disrupt anything. 🙂

Comment on lines 324 to 327
if (!visited(sourcePath)) {
visited.add(sourcePath)
sourcePath.exists
} else false
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!visited(sourcePath)) {
visited.add(sourcePath)
sourcePath.exists
} else false
if (visited.add(sourcePath)) {
sourcePath.exists
} else false

This could be simplified to check whether the given element already belongs to the set only once. 🙂

Copy link
Contributor Author

@Temurlock Temurlock May 10, 2024

Choose a reason for hiding this comment

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

Thanks, made it shorter: visited.add(sourcePath) && sourcePath.exists

@@ -266,6 +266,8 @@ public abstract PresentationCompiler withScheduledExecutorService(
*/
public abstract PresentationCompiler withWorkspace(Path workspace);

public abstract PresentationCompiler withReferenceCounter(ReferenceCountProvider provider);
Copy link
Contributor

Choose a reason for hiding this comment

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

We also use old mtags for scala versions that aren't supported anymore, so each new added method needs a default implementation. This is the reason for most of the CI fail.

Comment on lines 16 to 31
Seq(
if (scalaVersion.startsWith("2.13"))
Dependency.of(Module.of("org.typelevel", s"cats-effect_2.13"), "3.5.3")
else if (isScala3Version(scalaVersion))
Dependency.of(Module.of("org.typelevel", s"cats-effect_3"), "3.5.3")
else
Dependency.of(Module.of("org.typelevel", s"cats-effect_2.12"), "3.5.3")
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't it be enough to use binaryVersion instead?

else
completion.getItems.asScala
.sortBy(_.getLabel())
.map(item => fullServer.completionItemResolve(item).get())
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: maybe move the common line out of if statement.

Comment on lines 231 to 236
referenceCache
.getOrElseUpdate(
w2.sym,
referenceCounter
.references(buildTargetIdentifier, semanticdbSymbol(w2.sym))
)
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: maybe extract this part


public interface ReferenceCountProvider {

Integer references(String buildTargetIdentifier, String symbol);
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder whether we should make it more generic. Like workspacePriority which we could modify on Metals side.

Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

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

Some comments plus you need to rebase your PR and run scalafmt


public interface CompletionItemPriority {

Integer workspaceMemberPriority(String buildTargetIdentifier, String symbol);
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add docs what is expected by returning an integer here. What happens when the number is lower and higher.

config: PresentationCompilerConfig = PresentationCompilerConfigImpl(),
folderPath: Option[Path] = None,
reportsLevel: ReportLevel = ReportLevel.Info,
buildTargetIdentifier: String = "",
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably need to run scalafmt via ./bin/scalafmt

@@ -192,6 +192,19 @@ trait Completions { this: MetalsGlobal =>
new Ordering[Member] {
val queryLower = query.toLowerCase()
val fuzzyCache = mutable.Map.empty[Symbol, Int]
val workspaceMemberPriorityCache = mutable.Map.empty[Symbol, Int]
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this cache be properly refreshed when the frequency changes? (the tests seem to indicate so) Maybe we should control and cache it in ReferenceProvider? For example every time semanticdb changes we could refresh the cache for all symbols contained in the TextDocument? So it would not cause any additional time for completions?

val futureCompletionResult: List[String] =
List("Future - scala.concurrent", "Future - java.util.concurrent")

checkItems(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use check it would still be visible which one is first.

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.

None yet

4 participants