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

Make the completion logic easier to extend #11794

Merged
merged 1 commit into from
Mar 19, 2021

Conversation

alexarchambault
Copy link
Contributor

Ref com-lihaoyi/Ammonite#1150

This PR makes the code handling completion easier to extend, so that Ammonite can rely on it and can add support for its import $ivy and "deep" completions.

In more detail, this PR:

  • makes public a few methods and classes in Completion,
  • tries to add back-ticks to labels when necessary.

It also adds non-regression tests, testing that the API can be extended the way Ammonite expects it, and that back-ticks are added when necessary.

@alexarchambault
Copy link
Contributor Author

About the stability of the methods that are made public: as far as Ammonite is concerned, it's ok if binary and / or compatibility are broken from time to time, as long as Ammonite can still extend completion the way it's done in the additional tests.

// initially adapted from
// https://github.com/scala/scala/blob/decbd53f1bde4600c8ff860f30a79f028a8e431d/
// src/reflect/scala/reflect/internal/Printers.scala#L573-L584
else if (name == nme.CONSTRUCTOR) "this"
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not really sure if it's possible to get a completion that would require such renaming. Primary constructors are explicitly excluded while collecting completions. I have no idea why this is not done explicitly for secondary constructors but I they don't seem to be listed among other completions. BTW completing names after new doesn't seem to work currently

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That comes from the linked code. I removed it.

else {
val decName = name.decode.toString
val hasSpecialChar = decName.exists { ch =>
brackets.contains(ch) || ch.isWhitespace || isDot(ch)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should also include comma, semicolon and not sure what else. I checked these in ammonite and they seem not to get backticks there as expected. Non-letter unicode characters like § don't get handled properly either. I'm wondering if we could reuse some existing logic (e.g. taken from the Scanner) to find out if something is a valid identifier name by itself or not instead of reinventing the wheel here. In case that would be too heavy in terms of performance we could first filter out the names that for sure don't require backticks and then validate the rest with the proper logic.
We should also add some dedicated tests for such corner cases

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, but maybe this can be fixed later? As soon as this PR is merged, this should allow to merge com-lihaoyi/Ammonite#1150 (using dotty nighlies).

val hasSpecialChar = decName.exists { ch =>
brackets.contains(ch) || ch.isWhitespace || isDot(ch)
}
def isOperatorLike = (name.isOperatorName || decName.exists(isOperatorPart)) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Am I right that this would put all simple operators like + or :: into backticks? That seems unnecessary. Also mixed Identifiers like foo_++ don't need backticks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems it doesn't for operators like ++ or ::, thanks to the condition decName.exists(isScalaLetter) right after I think. But it does for things like foo_++, yes, which is unfortunate.

I have a slightly different version of that PR, that allows not to change the current behavior in dotty, while still allowing Ammonite to do things slightly differently in that area. Would you be fine with that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I just pushed that (no back-tick-related changes for the repl itself).

import org.junit.Assert._
import org.junit.Test

class CompletionTests extends DottyTest:
Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest renaming this to something like CustomCompletionTest not to cause confusion with the already existing CompletionTest

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

isOperatorLike ||
nme.keywords(term) && term != nme.USCOREkw

if (needsBackTicks) s"`$decName`"
Copy link
Contributor

Choose a reason for hiding this comment

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

What would happen if a user has already typed a backtick before pressing TAB in REPL?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't work too well... We pass both back-ticked and non-back-ticked labels to JLine, but it sometimes only keeps one or the other. In manual tests, it completes Foo. with both, but Foo.a or Foo.`a` only with the non-back-ticked (former) or back-ticked (latter).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the code I just pushed, the dotty repl still behaves as before (no back ticks whatsoever).

@prolativ prolativ assigned alexarchambault and unassigned prolativ Mar 18, 2021
@smarter smarter added this to the 3.0.0-RC2 milestone Mar 18, 2021
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.

4 participants