Skip to content

runtime(python): highlight ellipsis literals#18107

Closed
jparise wants to merge 5 commits intovim:masterfrom
jparise:python-ellipsis
Closed

runtime(python): highlight ellipsis literals#18107
jparise wants to merge 5 commits intovim:masterfrom
jparise:python-ellipsis

Conversation

@jparise
Copy link
Copy Markdown
Contributor

@jparise jparise commented Aug 25, 2025

The ellipsis literal (...) can be used in multiple contexts:

  • Placeholders: class Foo: ...
  • Containers: Tuple[int, ...]
  • Assignments: x = ...

This is a trickier pattern to match because we can't rely on keyword boundaries, so we instead look for exactly three dots (...).

This does mean that we will match the ... portion of x...x, which isn't valid Python syntax, but I think that's an acceptable trade-off that avoids making this pattern much more complex.

Ref: https://docs.python.org/3/library/constants.html#Ellipsis

@jparise
Copy link
Copy Markdown
Contributor Author

jparise commented Aug 25, 2025

@zvezdan with this, I believe we now cover all of the builtin constants.

@zvezdan
Copy link
Copy Markdown
Contributor

zvezdan commented Aug 26, 2025

This does mean that we will match the ... portion of x...x, which isn't valid Python syntax, but I think that's an acceptable trade-off that avoids making this pattern much more complex.

It can be used in doctest expected result line(s) to indicate anything in between.
See this example from the documentation, specifically the part

"""Some description.

>>> exec(s)  #doctest: +ELLIPSIS
-3.21716034272e-0...7
"""

This, unfortunately, shows why the matching needs to be more complex.
In an example like this:

"""A doctest

>>> class MyClass:
...     def __init__(self, x):
...         try:
...             self.a = c
...         except ValueError:
...             self.b = c

"""

now shows all the continuation lines as ellipsis and they should be the same highlight as a regular code and >>>.

@jparise
Copy link
Copy Markdown
Contributor Author

jparise commented Aug 27, 2025

This does mean that we will match the ... portion of x...x, which isn't valid Python syntax, but I think that's an acceptable trade-off that avoids making this pattern much more complex.

It can be used in doctest expected result line(s) to indicate anything in between. See this example from the documentation, specifically the part

"""Some description.

>>> exec(s)  #doctest: +ELLIPSIS
-3.21716034272e-0...7
"""

Ah, I wasn't aware of +ELLIPSIS! To confirm, the ... is not matched/highlighted with my proposed change, but you're suggesting it should be, correct?

This, unfortunately, shows why the matching needs to be more complex. In an example like this:

"""A doctest

>>> class MyClass:
...     def __init__(self, x):
...         try:
...             self.a = c
...         except ValueError:
...             self.b = c

"""

now shows all the continuation lines as ellipsis and they should be the same highlight as a regular code and >>>.

Yes, this is definitely flawed. I'll work on a solution to this case.

@jparise
Copy link
Copy Markdown
Contributor Author

jparise commented Aug 31, 2025

"""A doctest

>>> class MyClass:
...     def __init__(self, x):
...         try:
...             self.a = c
...         except ValueError:
...             self.b = c

"""

now shows all the continuation lines as ellipsis and they should be the same highlight as a regular code and >>>.

@zvezdan, I believe I've addressed this in the most recent commit. I took this approach:

  1. Moved the ellipsis literal matching into a distinct pythonEllipsis group
  2. Linked that group to pythonBuiltin
  3. Added a doctest-aware pythonEllipsis that is used in pythonDoctest contexts

Comment thread runtime/syntax/python.vim Outdated
\ contains=ALLBUT,pythonBuiltin,pythonClass,pythonFunction,pythonType,pythonAsync
\ transparent
" the ellipsis literal `...` can be used in multiple syntactic contexts
syn match pythonEllipsis "\%(^\|[^.]\)\zs\.\.\.\ze\%([^.]\|$\)" display
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This won't match if it follows another syntax match. E.g. type(...) if the parens are syntax matches. You might want to use a lookahead as well for symmetry but it's not required.

Suggested change
syn match pythonEllipsis "\%(^\|[^.]\)\zs\.\.\.\ze\%([^.]\|$\)" display
syn match pythonBuiltin "\.\@1<!.\.\.\ze\%([^.]\|$\)" display

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a very nice suggestion. Thanks! I applied it to the latest revision.

(It still needs to be pythonEllipsis due to my most recent change for doctest awareness.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry I didn't intend to suggest renaming the group. I think that may have been the name in an earlier draft and it was changed underneath my other open comment window.

@jparise jparise force-pushed the python-ellipsis branch 4 times, most recently from a705aed to d2a9df9 Compare September 5, 2025 16:07
The ellipsis literal (`...`) can be used in multiple contexts:

- Placeholders:     `class Foo: ...`
- Containers:       `Tuple[int, ...]`
- Assignments:      `x = ...`

This is a trickier pattern to match because we can't rely on keyword
boundaries, so we instead look for exactly three dots (`...`).

This does mean that we will match the `...` portion of `x...x`, which
isn't valid Python syntax, but I think that's an acceptable trade-off
that avoids making this pattern much more complex.

Ref: https://docs.python.org/3/library/constants.html#Ellipsis
Use a negative lookbehind and lookahead to ensure we only match our
three consecutive dots. This also eliminates the need to explicitly
check for the start and end of the line.
Also expand the set of test cases to include Numpy indexing.
@jparise
Copy link
Copy Markdown
Contributor Author

jparise commented Sep 5, 2025

This does mean that we will match the ... portion of x...x, which isn't valid Python syntax, but I think that's an acceptable trade-off that avoids making this pattern much more complex.

It can be used in doctest expected result line(s) to indicate anything in between. See this example from the documentation, specifically the part

"""Some description.

>>> exec(s)  #doctest: +ELLIPSIS
-3.21716034272e-0...7
"""

The latest revision now correctly highlights ... for doctest values like this. (It's always on and isn't conditional on +ELLIPSIS.) I also added a few test covers to cover these examples.

@chrisbra
Copy link
Copy Markdown
Member

chrisbra commented Sep 8, 2025

thanks

@chrisbra chrisbra closed this in 77cfc49 Sep 8, 2025
@jparise jparise deleted the python-ellipsis branch September 8, 2025 23:00
zeertzjq added a commit to zeertzjq/neovim that referenced this pull request Sep 9, 2025
The ellipsis literal (`...`) can be used in multiple contexts:

- Placeholders:     `class Foo: ...`
- Containers:       `Tuple[int, ...]`
- Assignments:      `x = ...`

This is a trickier pattern to match because we can't rely on keyword
boundaries, so we instead look for exactly three dots (`...`).

This does mean that we will match the `...` portion of `x...x`, which
isn't valid Python syntax, but I think that's an acceptable trade-off
that avoids making this pattern much more complex.

Reference:
- https://docs.python.org/3/library/constants.html#Ellipsis

closes: vim/vim#18107

vim/vim@77cfc49

Co-authored-by: Jon Parise <jon@indelible.org>
dundargoc pushed a commit to dundargoc/neovim that referenced this pull request Sep 27, 2025
The ellipsis literal (`...`) can be used in multiple contexts:

- Placeholders:     `class Foo: ...`
- Containers:       `Tuple[int, ...]`
- Assignments:      `x = ...`

This is a trickier pattern to match because we can't rely on keyword
boundaries, so we instead look for exactly three dots (`...`).

This does mean that we will match the `...` portion of `x...x`, which
isn't valid Python syntax, but I think that's an acceptable trade-off
that avoids making this pattern much more complex.

Reference:
- https://docs.python.org/3/library/constants.html#Ellipsis

closes: vim/vim#18107

vim/vim@77cfc49

Co-authored-by: Jon Parise <jon@indelible.org>
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