-
-
Notifications
You must be signed in to change notification settings - Fork 27
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
Incompatible with the Python shell #79
Comments
Does it work with basic? It seems the dynamic completion table is fundamentally incompatible with style like orderless, partial-completion, initials, substring and flex since these styles pass the empty string as prefix argument to the completion table. So this works with none of the advanced completion styles, as you already mentioned. If the dynamic python I should add, there are many Orderless issues where the issue with dynamic completion tables has already been discussed, see #20, #24, #45, #67. One possible workaround for orderless would be to treat the first token specially, see #49. However this would be a deviation from "orderless" style. If I recall correctly, @oantolin is not fond of this idea, and I am neither. In particular since orderless is not doing worse than styles like (setq completion-styles '(basic orderless)) Ideally you only set this style for the filtering used by Corfu, Company or |
If |
It works with basic. And the Python shell already gets (or at least
could in principle get) a filtered list from the inferior process.
Assuming this is the case, what would be the canonical approach here?
Change `completion-fn` to ignore its string argument, or replace
`completion-table-dynamic` by something else? (I am not into the ins
and outs of completion.)
|
There is no good canonical solution as I mentioned before. The function should not ignore the string argument. It should return all possible completion strings if the string argument is empty, which is not efficient. If the string is non-empty, all possible completions with this prefix should be returned, such that the completion result is compatible with the I am afraid, the canonical solution is to not use |
I don't follow your reasoning. The way completion in the REPL works is, the REPL sends the contents of the current line to the Python inferior and receives a pre-filtered list of completions. This might or might not be efficient, but it's inescapable. So I already have the precomputed candidates. I just need to pass it along in a way that pleases Emacs's completion system. |
I don't know how the Python package works internally. All I am saying is that the Try this with Vertico: (defun test-dynamic-table ()
(completing-read "> "
(completion-table-dynamic
(lambda (input)
(message "Input: %S" input)
'("first" "second" "third")))))
(let ((completion-styles '(basic)))
(test-dynamic-table))
(let ((completion-styles '(substring)))
(test-dynamic-table))
(let ((completion-styles '(orderless)))
(test-dynamic-table)) EDIT: If you look at |
I agree with everything @minad said, but want to add that while I don't really like the idea of treating the first component differently I'm starting to think it might be worth doing. I'd do something like: if the first component happens to compile to a regexp |
Why not add a configuration variable |
Instead of relying on the criterion |
I guess. How about this idea: if any of the components compiles to an anchored regexp of the form |
You'd need an explicit |
Ah, but that probably wouldn't work for completion-at-point, would it? People don't want to type the |
I think this is not such a good idea, or at least it is an orthogonal idea. It would work as an additional optimization (if you check for ^ and check that the rest of the string is a literal), but it would not resolve the scenario we are talking about here. The issue here relies on the prefix of the input to be used as input to the completion table, since you trigger completion at point with TAB after having entered a certain prefix. |
Yes, we wrote at the same time. It is still a good idea as optimization. However having such |
The configuration variable is viable, I'm just trying to explore other options a bit to see if we can't come up with something even better. How about doing the |
I could even add such a style dispatcher to orderless so people don't have to write the 2 line function. |
I always forget how powerful the style dispatchers are. They can also dispatch by index, right? Then this is clearly the better solution. I don't feel strongly about to the optimization - I think there is no need to add this. |
@minad I think you are overestimating my understanding of the completion system :-) If you look at my (pseudo)code at the top of this issue, isn't it true that |
No, this is not the case. The function
This would work with the downside that the list of completions will not be dynamically updated when you delete characters from the initial input string between start and end. Besides that it will probably work well. However the solution @oantolin proposed with the style dispatcher is clearly better. Then this special style dispatcher should be used to define a |
Okay, I think I get it now. Then I'm unsure whether or not to submit a patch to Emacs.
Out of curiosity, why is the completion table called with a full candidate sometimes when I turn Vertico off? Just typing
|
There is nothing wrong here.
For some of the actions of the programmable completion function (see the documentation of programmable completion or completing-read), the completion style is bypassed and the actual input string is passed to the completion function. This happens for example for the |
Sorry for the incoherent questions! |
Without the optimization having a style dispatcher prepend a |
The optimization could also be simplified by only checking the first component for a |
It's not your fault at all @astoff, the API is pretty hard to understand. To write orderless I actually had to read a good chunk of minibuffer.el, docs weren't enough for me. |
Actually,
But in this case there seems to be no point in returning a dynamic completion table (which is fast to create) when querying it is potentially slow — or am I missing something? (The eager thing would have the same mild drawbacks of ignoring the string argument to the completion table, but nevermind.) |
It is not a good idea - as described in the documentation. The returned completion table would not work for |
Coincidentally, I had a longer discussion about this with the company maintainer @dgutov. Dmitry argues that recalling the capf every time is the safer and more robust approach in order to also support completion tables which do not respect the NOTE. He is right about this. However there are other technical reasons why Corfu is not recalling the capf every time (Corfu relies on the completion-in-region infrastructure, which does not allow this), so Corfu only fully supports capfs which respect the NOTE. My opinion is that the NOTE should be formulated in a more binding way - this should actually be the contract which capfs should follow and it is not some optional constraint the capf author can follow or not. The situation is a bit of a dilemma - the completion-in-region infrastructure indicates that capfs are required to respect the NOTE, but the NOTE says that the contract should only be followed "ideally". |
Now I realize that if you supply python.el's dynamic completion table to Vertico or Corfu, you could be doing a network transaction on every key press (I'm running the Python interpreter over Tramp a lot lately). Even without an SSH connection in between, each keypress would trigger some rather flimsy comint communication. In fact, I've occasionaly seen some garbage dumped in the shell buffer while Corfuing. Clearly it was caused by the comint input hooks getting out of sync with the inferior process. Now, if I patched python.el to compute the completions eagerly, you would have to contact the inferior process only once per completion-in-region session. You might freeze for a little bit after pressing TAB for the first time, but after that it would be fast to do further filtering. And one could use flex or orderless, at the expense of So, in fact, following the NOTE seems pretty detrimental to Corfu and Vertico, at least in this rather particular case of comint-based completion. |
Right. |
So, we have 3 possible implied contracts, from UI to CAPF:
Oh boy. |
FWIW, the scheme I described implies 3, not 2. Though it would usually work with any of 1, 2, 3. |
If orderless finds that some component of the pattern compiles to a regexp of the form ^literal it will now take the first such regexp and add the literal to the prefix which which it calls all-completions. That's a slight lie: it actually looks for regexps of the form \(?:^literal\) since that is what the pattern compiler actually produces. With this change people can now add a style dispatcher that adds a ^ to the first component to use with certain completion-at-point functions that can really take advantage of knowing a prefix to return fewer completions. Some even (incorrectly) refuse to return all possible completions for an empty string! See the discussion in #79 and the issues linked therein.
I implemented the |
@dgutov, @jakanakaevangeli What do you think of the following cache invalidation criterium: in a pre-command hook, check if this command is I've tested the Python shell completion with company, and it is pretty laggy if the inferior process is running remotely, since each key press triggers a network request. So this might actually be enough justification to change it. And then orderless will also work OOTB, as a side effect. Please just don't tell anyone about my hidden agenda. |
Augusto Stoffel ***@***.***> writes:
@dgutov, @jakanakaevangeli What do you think of the following cache
invalidation criterium: in a pre-command hook, check if this command
is `self-insert-command` and the syntax class is word or symbol. If
not, then kill the cache.
It could work fine, at least with the default
completion-in-region-function, but has some flaws. Example: what if the
user types C-y to paste a ";". This is not a self-insert-command and
cache will not get invalidated.
Another idea for a cache invalidation scheme:
python-shell-CAP calculates completion START (this is fast, only a regex
search). Save it as a marker. From the completion table function,
calculate START again and if it differs from the saved START marker,
invalidate the cache.
|
This is not a self-insert-command, so the cache will be invalidated. It will also be invalidated if the user pastes an "x". This is was a false positive, but nevermind. |
Augusto Stoffel ***@***.***> writes:
This is not a self-insert-command, so the cache *will* be
invalidated. Nevermind it was a false positive.
A, sorry, my misunderstanding then. You can disregard my post.
|
Is there also a possibility to handle quoted regexps? A style dispatcher could |
It can work, but since the caching would be implemented inside the completion table, I think for it to concern itself with user input and particular invoked commands, is pretty dirty. But you can try and see, IDK. |
@astoff Why not save and examine the probe text passed into the table (when it is non-empty), updating the cache when the saved probe is not a prefix of the current probe (as I suggested for eglot here)? I'm experimenting with it for my ipython mode, and it requires moving cache out of a lexical to a global, due to all the other lambda's that the CAPF creates (like docsig), as well as to handle contract flavor 3 (ala company). To know when to "start anew" with flavor 3, we can use your idea of |
Daniel Mendler ***@***.***> writes:
Is there also a possibility to handle quoted regexps? A style
dispatcher could `regexp-quote` the first word and prepend it with ^.
FWIW, here's a correct inverse of regexp-quote (returns nil for values
outside of regexp-quote's codomain.)
(defun regexp-unquote (regex)
"Return a string passed to `regexp-quote' to produce REGEX.
Return nil, if REGEX couldn't have been produced by `regexp-quote'."
(unless (string-match-p "[$*+.?[\\^]"
(replace-regexp-in-string "\\\\[$*+.?[\\^]" "" regex
'fixedcase 'literal))
(replace-regexp-in-string "\\\\\\([$*+.?[\\^]\\)" "\\1" regex
'fixedcase)))
|
@jakanakaevangeli Great, I was looking before if |
Sure, why not, @minad? But I'm not sure it's necessary. Thanks for the |
@oantolin this allows a simpler dispatcher which just takes the first word, quotes it and prepends it with ^. In the current implementation, this will only work for non-regexp characters. But sure, it is an edge case. |
The dispatcher I had in mind was simpler still: it just prepended a |
Okay, there are two topics in parallel now. We should start using mailing lists, like the pros. I'll reply to @jdtsmith's last comment here, but maybe we should continue discussing the performance/caching issue at https://debbugs.gnu.org/cgi/bugreport.cgi?bug=50459 My criterium is pretty simple and has no false negatives (i.e., it never misses invalidating the cache when needed). But now I realize that there are a few false positives that can be mildly problematic. E.g., when the user presses TAB. I think (a slight refinement of) your suggestion makes sense, specially for reasonably dumb comint-based capf. In Eglot, where the completion is more context-sensitive, there can be weird edge cases too. Imagine, in Lisp, that I try to complete "(x" (a function starting with x), then delete the parenthesis and try to complete "x" (a variable starting with x). Adding a marker as additional safeguard doesn't help either. But this doesn't seem a very common thing to happen anyway. |
Yeah, that's an issue; you could also be crazy and put your region inside a string in mid-completion (change In terms of invalidation to handle all the cases 1,2,3, it gets complicated. I'm thinking it may be necessary to accept all the "frequent table dumping" of contract flavor 3 as false positives. Just kill the cache and start over (unnecessarily, sometimes, but oh well). For flavors 1 & 2, table dumps are true positive invalidations. Flavor 1 requires additional in media res invalidations too. |
Okay, I tested @jdtsmith's idea for the cache invalidation and it seems to work well. I submitted a patch to the Emacs bug linked above, in case anyone wants to take a look. |
It now occurred to me that in the ^literal case, it would be possible to support completion of a common prefix substring. The effect wouldn't be too different from In any case, I'll close the issue, because now the Python completion table does caching (which is kind of important when the shell is remote) and therefore works with orderless as a side effect. |
@astoff there are two reasons to still do the prefix special casing:
You are right that Note that @oantolin already implemented the optimization - I think it is good to have it. My only nitpick is that I would like to see a more robust implementation (#81). |
@minad Sure, I understand that part — I've learned some things in this long discussion :-) But I was referring to something else, namely the common prefix insertion feature ( My point was that while this feature doesn't make sense in the regular orderless style, it could be implemented for the “mostly orderless” style, since changing the query from |
@astoff Okay, I missed that you singled out the TAB completion. Indeed it would be possible to support this. I think it is worth doing this if the |
Oh, good point @astoff! We could have TAB do something useful now. (Well, I'm a little skeptical that it actually is useful, but the point is it could do something. 😛) |
* This code was introduced to ease the usage of Orderless, where the input must not necessarily occur at the beginning. My recommendation is to use an Orderless style dispatcher instead, where the first word is prefixed with ^ for Corfu. Doing this even results in a performance win, since Orderless compiles ^words in a smart way such that prefix filtering takes place inside the completion table. See oantolin/orderless#79 and oantolin/orderless#81. See the Orderless style dispatcher from the Consult wiki https://github.com/minad/consult/wiki: (defun +orderless-dispatch (pattern index _total) (cond ;; Treat first component as prefix. This is useful for Corfu completion-in-region. ((and completion-in-region-mode (= index 0)) `(orderless-regexp . ,(concat "^" (regexp-quote pattern)))) ...)) * If you use the basic completion style, the input is already used for prefix filtering and this change has no effect. * If you use another completion style which even sorts the candidates itself, e.g., flex, this change is advantageous since it gives the completion style more control.
Orderless (but also the
flex
andsubstring
styles) doesn't work with the Python shell completion. It's probably an issue there, but I wanted to ask if there is anything obviously wrong with this pattern:The text was updated successfully, but these errors were encountered: