* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. @ 2021-09-07 17:52 Augusto Stoffel 2021-09-08 17:44 ` Augusto Stoffel ` (2 more replies) 0 siblings, 3 replies; 26+ messages in thread From: Augusto Stoffel @ 2021-09-07 17:52 UTC (permalink / raw) To: 50459 If I start a Python shell on Emacs -Q and type, say ``` x = [] x.c<tab> ``` then I see, as expected, ``` Possible completions are: x.clear x.copy x.count ``` Now, if I (setq completion-styles '(flex)), then no completions as reported in the same situation. Same thing with the `orderless' or `substring' completion styles. Moreover, the same observation holds for native completion on or off. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-07 17:52 bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc Augusto Stoffel @ 2021-09-08 17:44 ` Augusto Stoffel 2021-09-09 7:11 ` Michael Albinus 2021-09-09 7:30 ` Gregory Heytings 2021-09-10 19:08 ` bug#50459: 28.0.50; [PATCH] " Augusto Stoffel 2 siblings, 1 reply; 26+ messages in thread From: Augusto Stoffel @ 2021-09-08 17:44 UTC (permalink / raw) To: 50459 On Tue, 7 Sep 2021 at 19:52, Augusto Stoffel <arstoffel@gmail.com> wrote: > If I start a Python shell on Emacs -Q and type, say > > ``` > x = [] > x.c<tab> > ``` > > then I see, as expected, > > ``` > Possible completions are: > x.clear > x.copy > x.count > ``` > > Now, if I (setq completion-styles '(flex)), then no completions as > reported in the same situation. Same thing with the `orderless' or > `substring' completion styles. > > Moreover, the same observation holds for native completion on or off. On closer inspection (and discussion with the orderless people), the completion-at-point function in pyhton.el conforms to the completion API. The issue lies in the fancier completion styles, which always send the empty string as the STRING argument to any completion table. There's a workaround for this, which I could provide, but this doesn't seem advisable. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-08 17:44 ` Augusto Stoffel @ 2021-09-09 7:11 ` Michael Albinus 2021-09-09 7:32 ` Gregory Heytings 0 siblings, 1 reply; 26+ messages in thread From: Michael Albinus @ 2021-09-09 7:11 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459 Augusto Stoffel <arstoffel@gmail.com> writes: Hi Augusto, > On closer inspection (and discussion with the orderless people), the > completion-at-point function in pyhton.el conforms to the completion API. > > The issue lies in the fancier completion styles, which always send the > empty string as the STRING argument to any completion table. FTR, a similar issue has been discussed recently for remote file names in bug#50387. Best regards, Michael. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 7:11 ` Michael Albinus @ 2021-09-09 7:32 ` Gregory Heytings 0 siblings, 0 replies; 26+ messages in thread From: Gregory Heytings @ 2021-09-09 7:32 UTC (permalink / raw) To: Michael Albinus; +Cc: Augusto Stoffel, 50459 >> The issue lies in the fancier completion styles, which always send the >> empty string as the STRING argument to any completion table. > > FTR, a similar issue has been discussed recently for remote file names > in bug#50387. > Absolutely not. With (add-to-list 'completion-styles 'substring) or (add-to-list 'completion-styles 'flex), completion works as expected in Python shells, and not in Tramp. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-07 17:52 bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc Augusto Stoffel 2021-09-08 17:44 ` Augusto Stoffel @ 2021-09-09 7:30 ` Gregory Heytings 2021-09-09 7:40 ` Augusto Stoffel 2021-09-10 19:08 ` bug#50459: 28.0.50; [PATCH] " Augusto Stoffel 2 siblings, 1 reply; 26+ messages in thread From: Gregory Heytings @ 2021-09-09 7:30 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459 > > Now, if I (setq completion-styles '(flex)), then no completions as > reported in the same situation. Same thing with the `orderless' or > `substring' completion styles. > You shouldn't use (setq completion-styles '(flex)), you should use (add-to-list 'completion-styles 'flex). Otherwise the default completion styles are not used anymore. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 7:30 ` Gregory Heytings @ 2021-09-09 7:40 ` Augusto Stoffel 2021-09-09 7:49 ` Gregory Heytings 0 siblings, 1 reply; 26+ messages in thread From: Augusto Stoffel @ 2021-09-09 7:40 UTC (permalink / raw) To: Gregory Heytings; +Cc: 50459 On Thu, 9 Sep 2021 at 07:30, Gregory Heytings <gregory@heytings.org> wrote: > You shouldn't use (setq completion-styles '(flex)), you should use > (add-to-list 'completion-styles 'flex). Otherwise the default > completion styles are not used anymore. This doesn't change the issue described in the subject line. I would still not see any flex completions if I did what you suggest. Granted, completion wouldn't be totally broken. But I don't mind letting the brokenness manifest itself. Therefore I use (setq completion-styles '(orderless)). ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 7:40 ` Augusto Stoffel @ 2021-09-09 7:49 ` Gregory Heytings 2021-09-09 8:45 ` Augusto Stoffel 0 siblings, 1 reply; 26+ messages in thread From: Gregory Heytings @ 2021-09-09 7:49 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459 >> You shouldn't use (setq completion-styles '(flex)), you should use >> (add-to-list 'completion-styles 'flex). Otherwise the default >> completion styles are not used anymore. > > This doesn't change the issue described in the subject line. I would > still not see any flex completions if I did what you suggest. > Because the flex completion mechanism returns no completions, and the next completion mechanism is called. What kind of flex completions would you expect to see after x.t TAB in your example? > > Granted, completion wouldn't be totally broken. But I don't mind > letting the brokenness manifest itself. Therefore I use (setq > completion-styles '(orderless)). > It's not broken, it works as designed. Instead of asking each user of the completion mechanism to implement a specific function for substring / flex completion, these mechanisms tell them "please return all possible completions, I'll do the filtering job for you". ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 7:49 ` Gregory Heytings @ 2021-09-09 8:45 ` Augusto Stoffel 2021-09-09 8:50 ` Gregory Heytings 0 siblings, 1 reply; 26+ messages in thread From: Augusto Stoffel @ 2021-09-09 8:45 UTC (permalink / raw) To: Gregory Heytings; +Cc: 50459 On Thu, 9 Sep 2021 at 07:49, Gregory Heytings <gregory@heytings.org> wrote: >>> You shouldn't use (setq completion-styles '(flex)), you should use >>> (add-to-list 'completion-styles 'flex). Otherwise the default >>> completion styles are not used anymore. >> >> This doesn't change the issue described in the subject line. I >> would still not see any flex completions if I did what you suggest. >> > > Because the flex completion mechanism returns no completions, and the > next completion mechanism is called. What kind of flex completions > would you expect to see after x.t TAB in your example? For sure 'x.count' should be a candidate, just like in Elisp 'set' shows up as a possible completion after typing '(t TAB'. Whether or not your example should allow 'fix.it' as a completion is up to debate. > >> >> Granted, completion wouldn't be totally broken. But I don't mind >> letting the brokenness manifest itself. Therefore I use (setq >> completion-styles '(orderless)). >> > > It's not broken, it works as designed. Instead of asking each user of > the completion mechanism to implement a specific function for > substring / flex completion, these mechanisms tell them "please return > all possible completions, I'll do the filtering job for you". Right, this is not a problem with the flex style, it's a problem with the Python completion table. More specifically, with its notion of "all completions". ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 8:45 ` Augusto Stoffel @ 2021-09-09 8:50 ` Gregory Heytings 2021-09-09 16:46 ` Augusto Stoffel 0 siblings, 1 reply; 26+ messages in thread From: Gregory Heytings @ 2021-09-09 8:50 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459 >> It's not broken, it works as designed. Instead of asking each user of >> the completion mechanism to implement a specific function for substring >> / flex completion, these mechanisms tell them "please return all >> possible completions, I'll do the filtering job for you". > > Right, this is not a problem with the flex style, it's a problem with > the Python completion table. More specifically, with its notion of "all > completions". > Exactly. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 8:50 ` Gregory Heytings @ 2021-09-09 16:46 ` Augusto Stoffel 2021-09-10 11:37 ` Lars Ingebrigtsen 2021-09-10 13:32 ` Dmitry Gutov 0 siblings, 2 replies; 26+ messages in thread From: Augusto Stoffel @ 2021-09-09 16:46 UTC (permalink / raw) To: 50459; +Cc: Gregory Heytings, Michael Albinus [-- Attachment #1: Type: text/plain, Size: 1006 bytes --] After more discussions elsewhere [1], I noticed another problem with the dynamic completion table from python.el. Each call to the completion table requires communication with the inferior process. If called frequently enough, the comint input filters can get confused, and some garbage gets printed to the shell. If the inferior is running over Tramp, the problem gets amplified. Now, this is not likely to cause much trouble when calling `completion-at-point' manually, but it is a problem with any of continuously-updating completion UIs like Company or Corfu. To alleviate this, the completion-at-point function could implement some sort of caching. The difficult question is when to invalidate the cache. I've attached one possiblility as a draft patch. If the approach seems reasonable, then I'll format it properly. As a side effect, the patch also solves the original issue described in this ticket. Please let me know what you think. [1] https://github.com/oantolin/orderless/issues/79 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: python-capf.diff --] [-- Type: text/x-patch, Size: 1471 bytes --] diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4f222b4cf5..3239722981 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -3841,6 +3841,8 @@ python-shell-completion-get-completions (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) +(defvar-local python-shell--capf-cache nil) + (defun python-shell-completion-at-point (&optional process) "Function for `completion-at-point-functions' in `inferior-python-mode'. Optional argument PROCESS forces completions to be retrieved @@ -3895,11 +3897,13 @@ python-shell-completion-at-point #'ignore #'python-shell-completion-get-completions)) (t #'python-shell-completion-native-get-completions))))) - (list start end - (completion-table-dynamic - (apply-partially - completion-fn - process import-statement))))) + (let ((re (or (car python-shell--capf-cache) regexp-unmatchable)) + (prefix (buffer-substring-no-properties start end))) + (unless (string-match re prefix) + (setq python-shell--capf-cache + (cons (concat "\\`" (regexp-quote prefix) "\\(?:\\sw\\|\\s_\\)*\\'") + (funcall completion-fn process import-statement prefix))))) + (list start end (cdr python-shell--capf-cache)))) (define-obsolete-function-alias 'python-shell-completion-complete-at-point ^ permalink raw reply related [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 16:46 ` Augusto Stoffel @ 2021-09-10 11:37 ` Lars Ingebrigtsen 2021-09-10 11:50 ` Augusto Stoffel 2021-09-10 13:32 ` Dmitry Gutov 1 sibling, 1 reply; 26+ messages in thread From: Lars Ingebrigtsen @ 2021-09-10 11:37 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459, Gregory Heytings, Michael Albinus Augusto Stoffel <arstoffel@gmail.com> writes: > To alleviate this, the completion-at-point function could implement some > sort of caching. The difficult question is when to invalidate the > cache. I've attached one possiblility as a draft patch. If the > approach seems reasonable, then I'll format it properly. Would it be possible to do caching at a lower level instead of in python-mode? > As a side effect, the patch also solves the original issue described in > this ticket. [...] > - (list start end > - (completion-table-dynamic > - (apply-partially > - completion-fn > - process import-statement))))) > + (let ((re (or (car python-shell--capf-cache) regexp-unmatchable)) > + (prefix (buffer-substring-no-properties start end))) > + (unless (string-match re prefix) > + (setq python-shell--capf-cache > + (cons (concat "\\`" (regexp-quote prefix) "\\(?:\\sw\\|\\s_\\)*\\'") > + (funcall completion-fn process import-statement prefix))))) > + (list start end (cdr python-shell--capf-cache)))) I'm not sure I understand this patch -- it's not using `completion-table-dynamic' at all now? (But my understanding of the completion functions in Emacs is pretty lacking.) -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 11:37 ` Lars Ingebrigtsen @ 2021-09-10 11:50 ` Augusto Stoffel 2021-09-10 13:14 ` João Távora 2021-09-11 12:09 ` Lars Ingebrigtsen 0 siblings, 2 replies; 26+ messages in thread From: Augusto Stoffel @ 2021-09-10 11:50 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 50459, Gregory Heytings, Michael Albinus, joaotavora On Fri, 10 Sep 2021 at 13:37, Lars Ingebrigtsen <larsi@gnus.org> wrote: > Augusto Stoffel <arstoffel@gmail.com> writes: > >> To alleviate this, the completion-at-point function could implement some >> sort of caching. The difficult question is when to invalidate the >> cache. I've attached one possiblility as a draft patch. If the >> approach seems reasonable, then I'll format it properly. > > Would it be possible to do caching at a lower level instead of in > python-mode? I'm not an expert in this either, but I think the caching mechanism would be pretty particular to the circumstances of each completion table, so it indeed belongs here. Maybe João can say more? Since this comint-based completion is not super smart (it's not context-dependent at all), the rather naive caching invalidation used here seems sufficient to me. > >> As a side effect, the patch also solves the original issue described in >> this ticket. > > [...] > >> - (list start end >> - (completion-table-dynamic >> - (apply-partially >> - completion-fn >> - process import-statement))))) >> + (let ((re (or (car python-shell--capf-cache) regexp-unmatchable)) >> + (prefix (buffer-substring-no-properties start end))) >> + (unless (string-match re prefix) >> + (setq python-shell--capf-cache >> + (cons (concat "\\`" (regexp-quote prefix) >> "\\(?:\\sw\\|\\s_\\)*\\'") >> + (funcall completion-fn process import-statement prefix))))) >> + (list start end (cdr python-shell--capf-cache)))) > > I'm not sure I understand this patch -- it's not using > `completion-table-dynamic' at all now? (But my understanding of the > completion functions in Emacs is pretty lacking.) That's true: the logic here is that the completions are computed eagerly, and then cached until still valid. So if you type fo<tab>o.bar.baz the inferior process is contacted 3 times: after the <tab>, and after each dot. Before, a lazy table was returned, but the inferior would be contacted after each character anyway (if using Company or similar). ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 11:50 ` Augusto Stoffel @ 2021-09-10 13:14 ` João Távora 2021-09-10 13:28 ` Dmitry Gutov 2021-09-11 12:09 ` Lars Ingebrigtsen 1 sibling, 1 reply; 26+ messages in thread From: João Távora @ 2021-09-10 13:14 UTC (permalink / raw) To: Augusto Stoffel Cc: 50459, Lars Ingebrigtsen, Michael Albinus, Gregory Heytings Augusto Stoffel <arstoffel@gmail.com> writes: > On Fri, 10 Sep 2021 at 13:37, Lars Ingebrigtsen <larsi@gnus.org> wrote: > >> Augusto Stoffel <arstoffel@gmail.com> writes: >> >>> To alleviate this, the completion-at-point function could implement some >>> sort of caching. The difficult question is when to invalidate the >>> cache. I've attached one possiblility as a draft patch. If the >>> approach seems reasonable, then I'll format it properly. >> >> Would it be possible to do caching at a lower level instead of in >> python-mode? > > I'm not an expert in this either, but I think the caching mechanism > would be pretty particular to the circumstances of each completion > table, so it indeed belongs here. Maybe João can say more? I'm sorry, I'm quite overloaded lately and can't read this long thread. I've been participating in a few discussions about this and all I can add here are generic comments, like the intepretation that I make from the docstring of completion-at-point-functions Special hook to find the completion table for the entity at point. Each function on this hook is called in turn without any argument and should return either nil, meaning it is not applicable at point, or a function of no arguments to perform completion (discouraged), or a list of the form (START END COLLECTION . PROPS), where: START and END delimit the entity to complete and should include point, ^^^^^^^^^^^^^^^^^^^^^^ COLLECTION is the completion table to use to complete the entity, and ^^^^^^^^^^^^^^^^^^^ PROPS is a property list for additional information. As I've underlined, it's _that_ entity, not some other entity that the "backend" aka "capf" should complete. So: a) while the entity is "the same" (START and END are the same and POINT is somewhere in between) then the backend can do caching inside COLLECTION (Eglot does caching between calls to try-completion, all-completions, and others, for example). b) if the entity changes because the buffer has been changed, by any means -- including the means of an completion UI -- then the completion UI should re-invoke the capf to get a the new entity to complete and the new COLLECTION to complete it. Emacs's default completion UIs do this, and so does Company, if I'm not mistaken. If the backend is super smart and can be faster about responding to this new capf invocation validly given the previous respose that's fine. But my point is this other caching is much more difficult to do accurately because of buffer changes and assumptions about the filtering strategy. For example, a cache that assumes that adding a character to the end of entity will always produce a subset of previously obtained completions will fail if the completion style is some kind of "regexp-based" thing, or if that character is '.', for example But it might work decently in some situations. Anyway, another generic point that I've been trying to make is that filtering/completion-styling should be done as close to the source of completions as possible. In Elisp this is easy to do (completions are basically lists of strings or the big cheap-to-access obarray). If the source of the completions is removed from Emacs by some latency, the experience is never going to be as good as Elisp. - If you want to fully honour `completion-styles` which is an Elisp facility, you need to know most complete set of completions possible at all necessary times. That's going to be slow (but read my final paragraph). - If you're OK with letting the server do the filtering and the highlighting, you can make a "backend" style like I did for SLY, for example. It's going to be faster, but `completion-styles` won't be honoured. That's doesn't mean you give up 100% on "flex". In SLY, there is flex implemented on the Common Lisp side, and for Eglot, many LSP server do their own flex matching. In any case, it is generally possible to design responsive backends systems that let the user interact with Emacs while the "slow requests" are ongoing, by having these backends cancel themselves when input is available. This uses while-no-input and sit-for. See jsonrpc.el's jsonrpc-request for an example. I've been using these kinds of systems very successfully over the past 4-5 years with SLY, Eglot as backends and Company UI as a frontend. Hope this helped, João ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 13:14 ` João Távora @ 2021-09-10 13:28 ` Dmitry Gutov 2021-09-10 14:06 ` João Távora 0 siblings, 1 reply; 26+ messages in thread From: Dmitry Gutov @ 2021-09-10 13:28 UTC (permalink / raw) To: João Távora, Augusto Stoffel Cc: 50459, Lars Ingebrigtsen, Michael Albinus, Gregory Heytings On 10.09.2021 16:14, João Távora wrote: > - If you're OK with letting the server do the filtering and the > highlighting, you can make a "backend" style like I did for SLY, for > example. It's going to be faster, but `completion-styles` won't be > honoured. That's doesn't mean you give up 100% on "flex". In SLY, > there is flex implemented on the Common Lisp side, and for Eglot, many > LSP server do their own flex matching. You can't really do that with python-shell completion. Nor do you need do: the basic pcmpl mechanism should work just fine with it, and for performance the completion table just needs some smarter caching. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 13:28 ` Dmitry Gutov @ 2021-09-10 14:06 ` João Távora 2021-09-10 14:22 ` Dmitry Gutov 0 siblings, 1 reply; 26+ messages in thread From: João Távora @ 2021-09-10 14:06 UTC (permalink / raw) To: Dmitry Gutov Cc: Augusto Stoffel, Lars Ingebrigtsen, Michael Albinus, 50459, Gregory Heytings On Fri, Sep 10, 2021 at 2:28 PM Dmitry Gutov <dgutov@yandex.ru> wrote: > > On 10.09.2021 16:14, João Távora wrote: > > - If you're OK with letting the server do the filtering and the > > highlighting, you can make a "backend" style like I did for SLY, for > > example. It's going to be faster, but `completion-styles` won't be > > honoured. That's doesn't mean you give up 100% on "flex". In SLY, > > there is flex implemented on the Common Lisp side, and for Eglot, many > > LSP server do their own flex matching. > > You can't really do that with python-shell completion. Probably not unless you write some python, no. I don't see that as being that dirty. > Nor do you need > do: the basic pcmpl mechanism should work just fine with it, and for > performance the completion table just needs some smarter caching. Yes, as I said in b), with "sufficiently smart caching" (and infinite memory space) you can do everything, indeed. It's one of the famous "two hard problems" though, so good luck. João Távora ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 14:06 ` João Távora @ 2021-09-10 14:22 ` Dmitry Gutov 2021-09-10 14:39 ` João Távora 0 siblings, 1 reply; 26+ messages in thread From: Dmitry Gutov @ 2021-09-10 14:22 UTC (permalink / raw) To: João Távora Cc: Augusto Stoffel, Lars Ingebrigtsen, Michael Albinus, 50459, Gregory Heytings On 10.09.2021 17:06, João Távora wrote: > On Fri, Sep 10, 2021 at 2:28 PM Dmitry Gutov <dgutov@yandex.ru> wrote: >> >> On 10.09.2021 16:14, João Távora wrote: >>> - If you're OK with letting the server do the filtering and the >>> highlighting, you can make a "backend" style like I did for SLY, for >>> example. It's going to be faster, but `completion-styles` won't be >>> honoured. That's doesn't mean you give up 100% on "flex". In SLY, >>> there is flex implemented on the Common Lisp side, and for Eglot, many >>> LSP server do their own flex matching. >> >> You can't really do that with python-shell completion. > > Probably not unless you write some python, no. I don't see that > as being that dirty. I didn't say it was dirty, just not very fitting for the current approach: when you do completion by piping code for evaluation through inferior shell, you generally like that code to be simple. And reimplementing every completion style in Python seems like anything but. >> Nor do you need >> do: the basic pcmpl mechanism should work just fine with it, and for >> performance the completion table just needs some smarter caching. > > Yes, as I said in b), with "sufficiently smart caching" (and infinite > memory space) you can do everything, indeed. It's one of the > famous "two hard problems" though, so good luck. Completion backends do caching anyways, whether it's on the Emacs side, or somewhere inside a language server. Since completion logic is defined on "our" side here (despite being written in Python), caching logic can reside here too. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 14:22 ` Dmitry Gutov @ 2021-09-10 14:39 ` João Távora 2021-09-10 14:43 ` Dmitry Gutov 0 siblings, 1 reply; 26+ messages in thread From: João Távora @ 2021-09-10 14:39 UTC (permalink / raw) To: Dmitry Gutov Cc: Augusto Stoffel, Lars Ingebrigtsen, Michael Albinus, 50459, Gregory Heytings On Fri, Sep 10, 2021 at 3:22 PM Dmitry Gutov <dgutov@yandex.ru> wrote: > > On 10.09.2021 17:06, João Távora wrote: > > On Fri, Sep 10, 2021 at 2:28 PM Dmitry Gutov <dgutov@yandex.ru> wrote: > >> > >> On 10.09.2021 16:14, João Távora wrote: > >>> - If you're OK with letting the server do the filtering and the > >>> highlighting, you can make a "backend" style like I did for SLY, for > >>> example. It's going to be faster, but `completion-styles` won't be > >>> honoured. That's doesn't mean you give up 100% on "flex". In SLY, > >>> there is flex implemented on the Common Lisp side, and for Eglot, many > >>> LSP server do their own flex matching. > >> > >> You can't really do that with python-shell completion. > > > > Probably not unless you write some python, no. I don't see that > > as being that dirty. > > I didn't say it was dirty, just not very fitting for the current > approach: when you do completion by piping code for evaluation through > inferior shell, you generally like that code to be simple. And > reimplementing every completion style in Python seems like anything but. I was just volunteering an opinion on how to solve _most_ of the problem (hence I wrote "don't give up 100%") Never did I suggest to implement "every completion style". SLY implements only two in Common Lisp. They're pretty sufficient for most users it seems. Most language servers seem to implement flex, some only prefix, some may be configurable I suppose. > Completion backends do caching anyways, whether it's on the Emacs side, > or somewhere inside a language server. There are many types of caching, as I tried to explain. Inter-capf-invocation caching (if you can understand what I mean) is possible, but is probably harder to get right than the "intra" version. I'm really just stating the obvious: the more removed you are from the source of truth, the harder it becomes to cache. But also, it's true that are definitely valuable, if implemented correctly or if a degree of inaccuracy is tolerated. Fast and accurate is hard. João Távora ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 14:39 ` João Távora @ 2021-09-10 14:43 ` Dmitry Gutov 2021-09-10 19:27 ` Augusto Stoffel 0 siblings, 1 reply; 26+ messages in thread From: Dmitry Gutov @ 2021-09-10 14:43 UTC (permalink / raw) To: João Távora Cc: Augusto Stoffel, Lars Ingebrigtsen, Michael Albinus, 50459, Gregory Heytings On 10.09.2021 17:39, João Távora wrote: >> Completion backends do caching anyways, whether it's on the Emacs side, >> or somewhere inside a language server. > There are many types of caching, as I tried to explain. > Inter-capf-invocation caching (if you can understand what I mean) > is possible, but is probably harder to get right than the "intra" version. The proposed patch provides the "intra" version. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 14:43 ` Dmitry Gutov @ 2021-09-10 19:27 ` Augusto Stoffel 2021-09-10 20:08 ` João Távora 0 siblings, 1 reply; 26+ messages in thread From: Augusto Stoffel @ 2021-09-10 19:27 UTC (permalink / raw) To: Dmitry Gutov Cc: Michael Albinus, Lars Ingebrigtsen, 50459, Gregory Heytings, João Távora On Fri, 10 Sep 2021 at 17:43, Dmitry Gutov <dgutov@yandex.ru> wrote: > On 10.09.2021 17:39, João Távora wrote: >>> Completion backends do caching anyways, whether it's on the Emacs side, >>> or somewhere inside a language server. >> There are many types of caching, as I tried to explain. >> Inter-capf-invocation caching (if you can understand what I mean) >> is possible, but is probably harder to get right than the "intra" version. > > The proposed patch provides the "intra" version. I just wanted to emphasize that the approach of evaluating code and asking an interpreter for completions is obviously a dead end. I guess it is still the best available at the moment for the REPL, so it makes sense to ensure it's reasonably correct and doesn't hang your comint. But further optimizations are not really worth the effort. The LSP stuff is much more interesting, and there the caching question is pretty hairy... ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 19:27 ` Augusto Stoffel @ 2021-09-10 20:08 ` João Távora 0 siblings, 0 replies; 26+ messages in thread From: João Távora @ 2021-09-10 20:08 UTC (permalink / raw) To: Augusto Stoffel Cc: Michael Albinus, Lars Ingebrigtsen, 50459, Gregory Heytings, Dmitry Gutov On Fri, Sep 10, 2021 at 8:27 PM Augusto Stoffel <arstoffel@gmail.com> wrote: > > On Fri, 10 Sep 2021 at 17:43, Dmitry Gutov <dgutov@yandex.ru> wrote: > > > On 10.09.2021 17:39, João Távora wrote: > >>> Completion backends do caching anyways, whether it's on the Emacs side, > >>> or somewhere inside a language server. > >> There are many types of caching, as I tried to explain. > >> Inter-capf-invocation caching (if you can understand what I mean) > >> is possible, but is probably harder to get right than the "intra" version. > > > > The proposed patch provides the "intra" version. > > I just wanted to emphasize that the approach of evaluating code and > asking an interpreter for completions is obviously a dead end. I wouldn't put it that strongly, but for sure not as easy to leverage as a ready to use LSP server. That's the promise of LSP, at least. Also, in my experience, I've found that completions are just one aspect of IDEs, and perhaps not even the most important. "Find definition" and diagnostics are perhaps more important for me. So yak-shaving and obsessing over optimizations to this reasonably hard problem (implementing responsive and fast UIs with arbitrarily sophisticated filtering styles over data that is some tenths of seconds away) might not be worth it. OTOH, yak-shaving is what Emacs is about, in some measure ,and if someone shaves the yak for me, I'll gladly take a shaved yak. João ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 11:50 ` Augusto Stoffel 2021-09-10 13:14 ` João Távora @ 2021-09-11 12:09 ` Lars Ingebrigtsen 2021-09-11 12:34 ` Augusto Stoffel 1 sibling, 1 reply; 26+ messages in thread From: Lars Ingebrigtsen @ 2021-09-11 12:09 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459, Gregory Heytings, Michael Albinus, joaotavora Augusto Stoffel <arstoffel@gmail.com> writes: > That's true: the logic here is that the completions are computed > eagerly, and then cached until still valid. So if you type > > fo<tab>o.bar.baz > > the inferior process is contacted 3 times: after the <tab>, and after > each dot. Before, a lazy table was returned, but the inferior would be > contacted after each character anyway (if using Company or similar). Ah, I see, then that sounds like a definite improvement. My other question is then how the cache is flushed... -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-11 12:09 ` Lars Ingebrigtsen @ 2021-09-11 12:34 ` Augusto Stoffel 2021-09-11 12:36 ` Lars Ingebrigtsen 0 siblings, 1 reply; 26+ messages in thread From: Augusto Stoffel @ 2021-09-11 12:34 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 50459, Gregory Heytings, Michael Albinus, joaotavora On Sat, 11 Sep 2021 at 14:09, Lars Ingebrigtsen <larsi@gnus.org> wrote: > Augusto Stoffel <arstoffel@gmail.com> writes: > >> That's true: the logic here is that the completions are computed >> eagerly, and then cached until still valid. So if you type >> >> fo<tab>o.bar.baz >> >> the inferior process is contacted 3 times: after the <tab>, and after >> each dot. Before, a lazy table was returned, but the inferior would be >> contacted after each character anyway (if using Company or similar). > > Ah, I see, then that sounds like a definite improvement. My other > question is then how the cache is flushed... The cache will be flushed, at the earliest, when a new call to 'python-shell-completion-at-point' is made. And the criterium is the following: - If the prompt moved, then the cache is flushed. - If the current completion prefix (the text between 'start' and 'end') is not identical to the original completion prefix, except for the addition of some word or symbol characters at the end, then the cache is flushed. That's it in a nutshell. I can give more details if you want. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-11 12:34 ` Augusto Stoffel @ 2021-09-11 12:36 ` Lars Ingebrigtsen 0 siblings, 0 replies; 26+ messages in thread From: Lars Ingebrigtsen @ 2021-09-11 12:36 UTC (permalink / raw) To: Augusto Stoffel; +Cc: 50459, Gregory Heytings, Michael Albinus, joaotavora Augusto Stoffel <arstoffel@gmail.com> writes: > The cache will be flushed, at the earliest, when a new call to > 'python-shell-completion-at-point' is made. And the criterium is the > following: > > - If the prompt moved, then the cache is flushed. > - If the current completion prefix (the text between 'start' and 'end') > is not identical to the original completion prefix, except for the > addition of some word or symbol characters at the end, then the cache > is flushed. Ah, I got the last bit, but I missed the first part. Well, then I think this sounds like a good approach? -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc. 2021-09-09 16:46 ` Augusto Stoffel 2021-09-10 11:37 ` Lars Ingebrigtsen @ 2021-09-10 13:32 ` Dmitry Gutov 1 sibling, 0 replies; 26+ messages in thread From: Dmitry Gutov @ 2021-09-10 13:32 UTC (permalink / raw) To: Augusto Stoffel, 50459; +Cc: Gregory Heytings, Michael Albinus On 09.09.2021 19:46, Augusto Stoffel wrote: > To alleviate this, the completion-at-point function could implement some > sort of caching. The difficult question is when to invalidate the > cache. I've attached one possiblility as a draft patch. If the > approach seems reasonable, then I'll format it properly. It's much simpler than what we talked about, but given that python-shell-completion-at-point doesn't really look past the current line, your approach should work fine. I'm not very familiar with the code, so I cannot approve the exact patch, though, sorry. As a bonus, though, maybe add the position of prompt on the shell-buffer to the invalidation key? Like, if a user imports some new lib in there, that can bring in new completions. ^ permalink raw reply [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; [PATCH] Python shell completion is incompatible with flex, orderless, etc. 2021-09-07 17:52 bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc Augusto Stoffel 2021-09-08 17:44 ` Augusto Stoffel 2021-09-09 7:30 ` Gregory Heytings @ 2021-09-10 19:08 ` Augusto Stoffel 2021-09-11 12:50 ` Lars Ingebrigtsen 2 siblings, 1 reply; 26+ messages in thread From: Augusto Stoffel @ 2021-09-10 19:08 UTC (permalink / raw) To: Dmitry Gutov; +Cc: 50459, Gregory Heytings, Michael Albinus [-- Attachment #1: Type: text/plain, Size: 1414 bytes --] Okay, I've attached a patch which seems to work fine. On Fri, 10 Sep 2021 at 16:32, Dmitry Gutov <dgutov@yandex.ru> wrote: > On 09.09.2021 19:46, Augusto Stoffel wrote: >> To alleviate this, the completion-at-point function could implement some >> sort of caching. The difficult question is when to invalidate the >> cache. I've attached one possiblility as a draft patch. If the >> approach seems reasonable, then I'll format it properly. > > It's much simpler than what we talked about, but given that > python-shell-completion-at-point doesn't really look past the current > line, your approach should work fine. Yes, and you're right --- I've did it that way because this comint-based completion basically only works for globals anyway. > > I'm not very familiar with the code, so I cannot approve the exact > patch, though, sorry. > > As a bonus, though, maybe add the position of prompt on the > shell-buffer to the invalidation key? Like, if a user imports some new > lib in there, that can bring in new completions. Good idea. I've added this to the patch. It still won't show the new completions until the user evaluates the import, but this is the best you can get by querying an interpreter for the completions. I've also changed a bit the way the "native completion" setup code is sent, so that it doesn't print a message directly in the shell buffer (there is still an echo area message). [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Implement-caching-for-python-shell-completion-at-poi.patch --] [-- Type: text/x-patch, Size: 5953 bytes --] From 3f27a63fe5be884464a6a68407e0b6990186c4a6 Mon Sep 17 00:00:00 2001 From: Augusto Stoffel <arstoffel@gmail.com> Date: Fri, 10 Sep 2021 20:44:10 +0200 Subject: [PATCH] Implement caching for 'python-shell-completion-at-point' * lisp/progmodes/python.el (python-shell-completion-at-point): cache results, since computing them involves talking with the inferior process and, potentially, network communications (python-shell--capf-cache): new variable, for cache (python-shell-completion-get-completions, python-shell-completion-native-get-completions): 'import' argument is not needed anymore. (python-shell-completion-native-setup, python-shell-completion-native-try): pass the setup code synchronously, to avoid printing a message in the shell. --- lisp/progmodes/python.el | 59 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 2eef52de0c..e71a8102df 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -3577,13 +3577,12 @@ python-shell-completion-native-try python-shell-completion-native-try-output-timeout)) (python-shell-completion-native-get-completions (get-buffer-process (current-buffer)) - nil "_"))) + "_"))) (defun python-shell-completion-native-setup () "Try to setup native completion, return non-nil on success." - (let ((process (python-shell-get-process))) - (with-current-buffer (process-buffer process) - (python-shell-send-string " + (let* ((process (python-shell-get-process)) + (output (python-shell-send-string-no-output " def __PYTHON_EL_native_completion_setup(): try: import readline @@ -3693,14 +3692,10 @@ python-shell-completion-native-setup print ('python.el: native completion setup failed, %s: %s' % sys.exc_info()[:2]) -__PYTHON_EL_native_completion_setup()" process) - (when (and - (python-shell-accept-process-output - process python-shell-completion-native-try-output-timeout) - (save-excursion - (re-search-backward - (regexp-quote "python.el: native completion setup loaded") nil t 1))) - (python-shell-completion-native-try))))) +__PYTHON_EL_native_completion_setup()" process))) + (when (string-match-p "python\\.el: native completion setup loaded" + output) + (python-shell-completion-native-try)))) (defun python-shell-completion-native-turn-off (&optional msg) "Turn off shell native completions. @@ -3760,13 +3755,10 @@ python-shell-completion-native-toggle (python-shell-completion-native-turn-on msg)) python-shell-completion-native-enable)) -(defun python-shell-completion-native-get-completions (process import input) - "Get completions using native readline for PROCESS. -When IMPORT is non-nil takes precedence over INPUT for -completion." +(defun python-shell-completion-native-get-completions (process input) + "Get completions of INPUT using native readline for PROCESS." (with-current-buffer (process-buffer process) - (let* ((input (or import input)) - (original-filter-fn (process-filter process)) + (let* ((original-filter-fn (process-filter process)) (redirect-buffer (get-buffer-create python-shell-completion-native-redirect-buffer)) (trigger "\t") @@ -3818,11 +3810,8 @@ python-shell-completion-native-get-completions :test #'string=)))) (set-process-filter process original-filter-fn))))) -(defun python-shell-completion-get-completions (process import input) - "Do completion at point using PROCESS for IMPORT or INPUT. -When IMPORT is non-nil takes precedence over INPUT for -completion." - (setq input (or import input)) +(defun python-shell-completion-get-completions (process input) + "Get completions of INPUT using PROCESS." (with-current-buffer (process-buffer process) (let ((completions (python-util-strip-string @@ -3836,6 +3825,9 @@ python-shell-completion-get-completions (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) +(defvar-local python-shell--capf-cache nil + "Variable to store cached completions and invalidation keys.") + (defun python-shell-completion-at-point (&optional process) "Function for `completion-at-point-functions' in `inferior-python-mode'. Optional argument PROCESS forces completions to be retrieved @@ -3889,12 +3881,21 @@ python-shell-completion-at-point ;; it during a multiline statement (Bug#28051). #'ignore #'python-shell-completion-get-completions)) - (t #'python-shell-completion-native-get-completions))))) - (list start end - (completion-table-dynamic - (apply-partially - completion-fn - process import-statement))))) + (t #'python-shell-completion-native-get-completions)))) + (prev-prompt (car python-shell--capf-cache)) + (re (or (cadr python-shell--capf-cache) regexp-unmatchable)) + (prefix (buffer-substring-no-properties start end))) + ;; To invalidate the cache, we check if the prompt position or the + ;; completion prefix changed. + (unless (and (equal prev-prompt (car prompt-boundaries)) + (string-match re prefix)) + (setq python-shell--capf-cache + `(,(car prompt-boundaries) + ,(if (string-empty-p prefix) + regexp-unmatchable + (concat "\\`" (regexp-quote prefix) "\\(?:\\sw\\|\\s_\\)*\\'")) + ,@(funcall completion-fn process (or import-statement prefix))))) + (list start end (cddr python-shell--capf-cache)))) (define-obsolete-function-alias 'python-shell-completion-complete-at-point -- 2.31.1 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* bug#50459: 28.0.50; [PATCH] Python shell completion is incompatible with flex, orderless, etc. 2021-09-10 19:08 ` bug#50459: 28.0.50; [PATCH] " Augusto Stoffel @ 2021-09-11 12:50 ` Lars Ingebrigtsen 0 siblings, 0 replies; 26+ messages in thread From: Lars Ingebrigtsen @ 2021-09-11 12:50 UTC (permalink / raw) To: Augusto Stoffel; +Cc: Michael Albinus, Gregory Heytings, 50459, Dmitry Gutov Augusto Stoffel <arstoffel@gmail.com> writes: > Good idea. I've added this to the patch. It still won't show the new > completions until the user evaluates the import, but this is the best > you can get by querying an interpreter for the completions. > > I've also changed a bit the way the "native completion" setup code is > sent, so that it doesn't print a message directly in the shell buffer > (there is still an echo area message). Looks good to me; pushed to Emacs 28 now. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2021-09-11 12:50 UTC | newest] Thread overview: 26+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2021-09-07 17:52 bug#50459: 28.0.50; Python shell completion is incompatible with flex, orderless, etc Augusto Stoffel 2021-09-08 17:44 ` Augusto Stoffel 2021-09-09 7:11 ` Michael Albinus 2021-09-09 7:32 ` Gregory Heytings 2021-09-09 7:30 ` Gregory Heytings 2021-09-09 7:40 ` Augusto Stoffel 2021-09-09 7:49 ` Gregory Heytings 2021-09-09 8:45 ` Augusto Stoffel 2021-09-09 8:50 ` Gregory Heytings 2021-09-09 16:46 ` Augusto Stoffel 2021-09-10 11:37 ` Lars Ingebrigtsen 2021-09-10 11:50 ` Augusto Stoffel 2021-09-10 13:14 ` João Távora 2021-09-10 13:28 ` Dmitry Gutov 2021-09-10 14:06 ` João Távora 2021-09-10 14:22 ` Dmitry Gutov 2021-09-10 14:39 ` João Távora 2021-09-10 14:43 ` Dmitry Gutov 2021-09-10 19:27 ` Augusto Stoffel 2021-09-10 20:08 ` João Távora 2021-09-11 12:09 ` Lars Ingebrigtsen 2021-09-11 12:34 ` Augusto Stoffel 2021-09-11 12:36 ` Lars Ingebrigtsen 2021-09-10 13:32 ` Dmitry Gutov 2021-09-10 19:08 ` bug#50459: 28.0.50; [PATCH] " Augusto Stoffel 2021-09-11 12:50 ` Lars Ingebrigtsen
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).