From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: =?UTF-8?Q?Jo=C3=A3o_?= =?UTF-8?Q?T=C3=A1vora?= Newsgroups: gmane.emacs.bugs Subject: bug#47711: bug#48841: bug#47711: bug#48841: bug#47711: [PATCH VERSION 2] Add new `completion-filter-completions` API and deferred highlighting Date: Fri, 27 Oct 2023 18:16:42 +0100 Message-ID: <877cn8asud.fsf@gmail.com> References: <3d3f894f-a6fa-53ae-5d50-c3aa9bffc73e@daniel-mendler.de> <56ab18b1-4348-9b2c-85bb-af9b76cd429a@daniel-mendler.de> <328f87eb-6474-1442-e1ca-9ae8deb2a84a@yandex.ru> <83fsvcbio7.fsf@gnu.org> <9f432d18-e70f-54c1-0173-1899fb66d176@gutov.dev> <877cnafv39.fsf@gmail.com> <9447dde3-b8e7-2ec0-9a9c-72c4cf9d12a8@gutov.dev> <7d14bc13-4419-816c-5708-c42988c39c02@gutov.dev> <5d0a78cc-4fa0-ef04-3462-1826f17d7d56@gutov.dev> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="7320"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: Daniel Mendler , Eli Zaretskii , Stefan Monnier , 47711@debbugs.gnu.org To: Dmitry Gutov Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri Oct 27 19:14:43 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qwQPy-0001ho-M2 for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 27 Oct 2023 19:14:42 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qwQPp-0006za-Bv; Fri, 27 Oct 2023 13:14:33 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qwQPm-0006z5-TO for bug-gnu-emacs@gnu.org; Fri, 27 Oct 2023 13:14:31 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qwQPm-0005ge-Jf for bug-gnu-emacs@gnu.org; Fri, 27 Oct 2023 13:14:30 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1qwQQH-0006L5-TW for bug-gnu-emacs@gnu.org; Fri, 27 Oct 2023 13:15:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: =?UTF-8?Q?Jo=C3=A3o_?= =?UTF-8?Q?T=C3=A1vora?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 27 Oct 2023 17:15:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 47711 X-GNU-PR-Package: emacs Original-Received: via spool by 47711-submit@debbugs.gnu.org id=B47711.169842686824298 (code B ref 47711); Fri, 27 Oct 2023 17:15:01 +0000 Original-Received: (at 47711) by debbugs.gnu.org; 27 Oct 2023 17:14:28 +0000 Original-Received: from localhost ([127.0.0.1]:37024 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qwQPi-0006Jp-T3 for submit@debbugs.gnu.org; Fri, 27 Oct 2023 13:14:27 -0400 Original-Received: from mail-ot1-x32e.google.com ([2607:f8b0:4864:20::32e]:59426) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qwQPf-0006JU-Ma for 47711@debbugs.gnu.org; Fri, 27 Oct 2023 13:14:24 -0400 Original-Received: by mail-ot1-x32e.google.com with SMTP id 46e09a7af769-6c4a25f6390so1498969a34.2 for <47711@debbugs.gnu.org>; Fri, 27 Oct 2023 10:13:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698426825; x=1699031625; darn=debbugs.gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=xb8HZlLKLUOYer2snSqmXG4Smpla9VhAVUCg/sr6lIg=; b=AQXuZGaP+KDqvLB15k2SOtUEQc688AsJJO+HE5f/SUK27oSUiuXCDMo1lNGRP3c2Ck d7dbmoOgdOenl922AQRe7MuH8F8LQqQ9vGK7MdxuTuFvQxwyeWgC6fFPDF0UyRBxJkKh YM4vViovT9rcPN4BhHjIImub/MVs4oiEOt9TCJpT+OgmGNH8Tf1FedfZ2OcEkCz+ZMsW 9x/MEaHD1EoNsNKGzsx6rzsmreX3c+u6CjF2X+BoADJSXXzqoM/sWLIorsi7rx/WwpG5 dw83SA8EjwxT75K+6lC3oRxtGkF/JB/G6hrtJMz8fU+ZMbHXd29WAzovBHi0vumpbRe4 +NLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698426825; x=1699031625; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=xb8HZlLKLUOYer2snSqmXG4Smpla9VhAVUCg/sr6lIg=; b=sOenbwdDSiPR1TQ9mtR1cNDe1Vu6AFFHxy2b26SxswCtYX0lmDlKAamgSDDiw5fF8G cIjhBTMEv+XMHQ7JLvrLLOR8URhEAvZLitSCWpALRmQJaq0Sj41+ntIFJp1xy+1rmpF0 tSfhqAsaU2AXBjU20BM8TYU9KsXdIJu/2hkgJsQorfB8fA4/YqdwYHjL7pIf5HU6+wJs FUHUSXaM5tMDKR1UAcgAN4VsyAk+SW91N1xN9e/zT0Qm2G/pY6djdwhiNksB+gzgM4Ek nQJciQxrxEvt3MnBVNbEmd9Nf2G/bKhvG9Gr838JDiKx1yYepp7tHdAlQHm9gOAsf7a3 dNHQ== X-Gm-Message-State: AOJu0YzNrvJLwYIPvqXHUfTp3xsiaacH7N93XKDOsucYNnEk03QXPC5g n6l7FQTNqTTpq9MNal6b2Upy1+J03pdYqA== X-Google-Smtp-Source: AGHT+IHsECQld+u4m9wYOp+7GAjRQdcfvTy3pu2TOz0kfA6mlsG8dXbKxQgbpIafhdDWJiBg61kgKA== X-Received: by 2002:a05:6830:2b23:b0:6b4:5ed3:8246 with SMTP id l35-20020a0568302b2300b006b45ed38246mr4825036otv.2.1698426825106; Fri, 27 Oct 2023 10:13:45 -0700 (PDT) Original-Received: from krug (87-196-80-249.net.novis.pt. [87.196.80.249]) by smtp.gmail.com with ESMTPSA id v31-20020a4a8c62000000b0057b43a25deasm445820ooj.3.2023.10.27.10.13.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Oct 2023 10:13:44 -0700 (PDT) In-Reply-To: <5d0a78cc-4fa0-ef04-3462-1826f17d7d56@gutov.dev> (Dmitry Gutov's message of "Fri, 27 Oct 2023 16:29:03 +0300") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:273395 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Dmitry Gutov writes: > but that can't be that reason. Indeed it isn't. But you're not far off, I think. Anyway, in the (completing-read "" obarray) scenario, the shortcut is taken, and that and the fact that the optimization then completing skips scoring for it -- which is blatantly wrong -- is why it is much faster. Looking at this, it seems that string-match with a non-group-capturing regexp over all the matches (the thing that the change was optimizing away) isn't that expensive in the overall gist of things. Thus in the C-h v case, the shortcut isn't taken and the optimization does what it should, but nothing gargantuan, around 8-9%. Also keep in mind this yoyo case is pretty contrived and I suspect more realistic cases would have even more modest gains. My attempts to fix the optimization to work correctly in all cases complicated the code and then slowed down the bare completion-read case. Not worth it IMO, therefore I have reverted it in the branch and in the latest patch I attach (lazy-hilit-2023-v4.diff). Here are the latest measurements for the "yoyo" test, now considering the C-h v scenario: ;; Daniel+Dmitry completin-read: 0.834s avg ;; Daniel+Dmitry C-h v: 0.988s avg =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 ;; lazy-hilit completing-read: 0.825s avg ;; lazy-hilit C-h v: 0.946s avg Again, this shows my patch to be about 2-4% faster, though not really relevant. Again, the optimization I removed, if it were done correctly (which it wasn't) could maybe shave off another 8-9% off that. > But there are differences. The first is that the highlighter function > takes one string as an argument instead of a collection. I mentioned > this before, this will be much handier to use in company-capf. Don't fully follow this. Can you perhaps show two versions (or two snippets) of the company-capf code, the handy and the non-handy version? > Second, in Daniel's patch the "adjust metadata" function got a > different, arguably better, calling convention. That's not dependent > on the rest of the patch, so it can be considered separately. Maybe. Changes to calling convention can be argued for but, when possible, they shoudn't be in with performance improvements. > Third, it made a principled stance to avoid altering the original > strings, even the non-visual text properties. This approach could be > adopted piecewise from Daniel's patch, especially if the performance > ends up the same or insignificantly different in practical usage. If we really wanted to, we could also adopt the non-propertizing approach in my lazy-hilit patch, by calculating the score "just in time", much like Daniel's patch does it. But it should be clear that what we save in allocation in completion-pcm--hilit-commonality, we'll pay for in the same amount in consing later. So no performance benefit. And if we do that, don't forget there will be the ugly "unquoted" complication to deal with. Then again, in my understanding that complication is due to similar problem of mixing business and display logic. That's assuming I understand this comment in minibuffer.el correctly: ;; Here we choose to quote all elements returned, but a better option ;; would be to return unquoted elements together with a function to ;; requote them, so that *Completions* can show nicer unquoted values ;; which only get quoted when needed by choose-completion. So I would look into solving that first, instead of allowing the "unquoted" hacks to spread even further in minibuffer.el > As for whether we migrate to the completion-filter-completions API, I > don't have a strong opinion. If we still think that the next revision > of the completion API will be radically different, then there is not > much point in making medium-sized steps like that. OTOH, if we end up > with the same API for the next decade and more, > completion-filter-completions does look more meaningful, and more > easily extensible (e.g. next we could add a pair (completion-scorer > . fn) to its return value; and so on). And again, the implementation > could be a simple wrapper at first. I agree on some points, and looking at this from a API-convenience standpoint I note two things: * To get late highlighting in Daniel's patch, one has to use a whole new API entry function to gather completions, and another to fontify completions. One also deprecates an existing member of the API. * In my patch, one binds a new API varaible and uses a function to fontify completions. No deprecations. Even if it wasn't a much simpler change to minibuffer.el without any backward-compatibility gymnastics, I still think the latter is much cleaner and easier to implement for completion frontends. The fact that there's no deprecation of one of the most important members of the API to date makes it more confortable to understand IMO. Jo=C3=A3o --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=lazy-hilit-2023-v4.diff diff --git a/lisp/icomplete.el b/lisp/icomplete.el index e6fdd1f1836..3e888c8b06a 100644 --- a/lisp/icomplete.el +++ b/lisp/icomplete.el @@ -722,7 +722,8 @@ icomplete-exhibit ;; Check if still in the right buffer (bug#61308) (or (window-minibuffer-p) completion-in-region--data) (icomplete-simple-completing-p)) ;Shouldn't be necessary. - (let ((saved-point (point))) + (let ((saved-point (point)) + (completion-lazy-hilit t)) (save-excursion (goto-char (icomplete--field-end)) ;; Insert the match-status information: @@ -754,12 +755,13 @@ icomplete-exhibit (overlay-end rfn-eshadow-overlay))) (let* ((field-string (icomplete--field-string)) (text (while-no-input + (benchmark-progn (icomplete-completions field-string (icomplete--completion-table) (icomplete--completion-predicate) (if (window-minibuffer-p) - (eq minibuffer--require-match t))))) + (eq minibuffer--require-match t)))))) (buffer-undo-list t) deactivate-mark) ;; Do nothing if while-no-input was aborted. @@ -901,7 +903,7 @@ icomplete--render-vertical 'icomplete-selected-match 'append comp) collect (concat prefix (make-string (- max-prefix-len (length prefix)) ? ) - comp + (completion-lazy-hilit comp) (make-string (- max-comp-len (length comp)) ? ) suffix) into lines-aux @@ -1067,7 +1069,8 @@ icomplete-completions (if (< prospects-len prospects-max) (push comp prospects) (setq limit t))) - (setq prospects (nreverse prospects)) + (setq prospects + (nreverse (mapcar #'completion-lazy-hilit prospects))) ;; Decorate first of the prospects. (when prospects (let ((first (copy-sequence (pop prospects)))) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 2120e31775e..cd8eeee2c78 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1234,6 +1234,7 @@ completion-all-completions POINT is the position of point within STRING. The return value is a list of completions and may contain the base-size in the last `cdr'." + (setq completion-lazy-hilit-fn nil) ;; FIXME: We need to additionally return the info needed for the ;; second part of completion-base-position. (completion--nth-completion 2 string table pred point metadata)) @@ -3749,108 +3750,194 @@ flex-score-match-tightness than the latter (which has two \"holes\" and three one-letter-long matches).") +(defvar-local completion-lazy-hilit nil + "If non-nil, request completion lazy highlighting. + +Completion-presenting frontends may opt to bind this variable to +non-nil value in the context of completion-producing calls (such +as `completion-all-sorted-completions'). This hints the +intervening completion styles that they do not need to +fontify (i.e. propertize with the `face' property) completion +strings with highlights of the matching parts. + +When doing so, it is the frontend -- not the style -- who becomes +responsible this fontification. The frontend binds this variable +to non-nil, and calls the function with the same name +`completion-lazy-hilit' on each completion string that is to be +displayed to the user. + +Note that only some completion styles take advantage of this +variable for optimization purposes. Other styles will ignore the +hint and greedily fontify as usual. It is still safe for a +frontend to call `completion-lazy-hilit' in these situations. + +To author a completion style that takes advantage see +`completion-lazy-hilit-fn' and look in the source of +`completion-pcm--hilit-commonality'.") + +(defvar completion-lazy-hilit-fn nil + "Used by completions styles to honouring `completion-lazy-hilit'. +When a given style wants to enable support for +`completion-lazy-hilit' (which see), that style should set this +variable to a function of one argument, a fresh string to be +displayed to the user. The function is responsible for +destructively highlighting the string.") + +(defun completion-lazy-hilit (str) + "Return a copy of completion STR that is `face'-propertized. +See documentation for variable `completion-lazy-hilit' for more +details." + (if (and completion-lazy-hilit completion-lazy-hilit-fn) + (funcall completion-lazy-hilit-fn (copy-sequence str)) + str)) + +(defun completion--hilit-from-re (string regexp) + "Fontify STRING with `completions-common-part' using REGEXP." + (let* ((md (and regexp (string-match regexp string) (cddr (match-data t)))) + (me (and md (match-end 0))) + (from 0)) + (while md + (add-face-text-property from (pop md) 'completions-common-part nil string) + (setq from (pop md))) + (unless (or (not me) (= from me)) + (add-face-text-property from me 'completions-common-part nil string)) + string)) + +(defun completion--flex-score-1 (md-groups match-end len) + "Compute matching score of completion. +The score lies in the range between 0 and 1, where 1 corresponds to +the full match. +MD-GROUPS is the \"group\" part of the match data. +MATCH-END is the end of the match. +LEN is the length of the completion string." + (let* ((from 0) + ;; To understand how this works, consider these simple + ;; ascii diagrams showing how the pattern "foo" + ;; flex-matches "fabrobazo", "fbarbazoo" and + ;; "barfoobaz": + + ;; f abr o baz o + ;; + --- + --- + + + ;; f barbaz oo + ;; + ------ ++ + + ;; bar foo baz + ;; +++ + + ;; "+" indicates parts where the pattern matched. A + ;; "hole" in the middle of the string is indicated by + ;; "-". Note that there are no "holes" near the edges + ;; of the string. The completion score is a number + ;; bound by (0..1] (i.e., larger than (but not equal + ;; to) zero, and smaller or equal to one): the higher + ;; the better and only a perfect match (pattern equals + ;; string) will have score 1. The formula takes the + ;; form of a quotient. For the numerator, we use the + ;; number of +, i.e. the length of the pattern. For + ;; the denominator, it first computes + ;; + ;; hole_i_contrib = 1 + (Li-1)^(1/tightness) + ;; + ;; , for each hole "i" of length "Li", where tightness + ;; is given by `flex-score-match-tightness'. The + ;; final value for the denominator is then given by: + ;; + ;; (SUM_across_i(hole_i_contrib) + 1) * len + ;; + ;; , where "len" is the string's length. + (score-numerator 0) + (score-denominator 0) + (last-b 0)) + (while (and md-groups (car md-groups)) + (let ((a from) + (b (pop md-groups))) + (setq + score-numerator (+ score-numerator (- b a))) + (unless (or (= a last-b) + (zerop last-b) + (= a len)) + (setq + score-denominator (+ score-denominator + 1 + (expt (- a last-b 1) + (/ 1.0 + flex-score-match-tightness))))) + (setq + last-b b)) + (setq from (pop md-groups))) + ;; If `pattern' doesn't have an explicit trailing any, the + ;; regex `re' won't produce match data representing the + ;; region after the match. We need to account to account + ;; for that extra bit of match (bug#42149). + (unless (= from match-end) + (let ((a from) + (b match-end)) + (setq + score-numerator (+ score-numerator (- b a))) + (unless (or (= a last-b) + (zerop last-b) + (= a len)) + (setq + score-denominator (+ score-denominator + 1 + (expt (- a last-b 1) + (/ 1.0 + flex-score-match-tightness))))) + (setq + last-b b))) + (/ score-numerator (* len (1+ score-denominator)) 1.0))) + +(defvar completion--flex-score-last-md nil + "Helper variable for `completion--flex-score'.") + +(defun completion--flex-score (str re &optional dont-error) + "Compute flex score of completion STR based on RE. +If DONT-ERROR, just return nil if RE doesn't match STR." + (cond ((string-match re str) + (let* ((match-end (match-end 0)) + (md (cddr + (setq + completion--flex-score-last-md + (match-data t completion--flex-score-last-md))))) + (completion--flex-score-1 md match-end (length str)))) + ((not dont-error) + (error "Internal error: %s does not match %s" re str)))) + (defun completion-pcm--hilit-commonality (pattern completions) "Show where and how well PATTERN matches COMPLETIONS. PATTERN, a list of symbols and strings as seen `completion-pcm--merge-completions', is assumed to match every -string in COMPLETIONS. Return a deep copy of COMPLETIONS where -each string is propertized with `completion-score', a number -between 0 and 1, and with faces `completions-common-part', -`completions-first-difference' in the relevant segments." +string in COMPLETIONS. + +If `completion-lazy-hilit' is nil, return a deep copy of +COMPLETIONS where each string is propertized with +`completion-score', a number between 0 and 1, and with faces +`completions-common-part', `completions-first-difference' in the +relevant segments. + +Else, if `completion-lazy-hilit' is t, return COMPLETIONS where +each string now has a `completion-score' property and no +highlighting." (cond ((and completions (cl-loop for e in pattern thereis (stringp e))) (let* ((re (completion-pcm--pattern->regex pattern 'group)) - (point-idx (completion-pcm--pattern-point-idx pattern)) - (case-fold-search completion-ignore-case) - last-md) - (mapcar - (lambda (str) - ;; Don't modify the string itself. - (setq str (copy-sequence str)) - (unless (string-match re str) - (error "Internal error: %s does not match %s" re str)) - (let* ((pos (if point-idx (match-beginning point-idx) (match-end 0))) - (match-end (match-end 0)) - (md (cddr (setq last-md (match-data t last-md)))) - (from 0) - (end (length str)) - ;; To understand how this works, consider these simple - ;; ascii diagrams showing how the pattern "foo" - ;; flex-matches "fabrobazo", "fbarbazoo" and - ;; "barfoobaz": - - ;; f abr o baz o - ;; + --- + --- + - - ;; f barbaz oo - ;; + ------ ++ - - ;; bar foo baz - ;; +++ - - ;; "+" indicates parts where the pattern matched. A - ;; "hole" in the middle of the string is indicated by - ;; "-". Note that there are no "holes" near the edges - ;; of the string. The completion score is a number - ;; bound by (0..1] (i.e., larger than (but not equal - ;; to) zero, and smaller or equal to one): the higher - ;; the better and only a perfect match (pattern equals - ;; string) will have score 1. The formula takes the - ;; form of a quotient. For the numerator, we use the - ;; number of +, i.e. the length of the pattern. For - ;; the denominator, it first computes - ;; - ;; hole_i_contrib = 1 + (Li-1)^(1/tightness) - ;; - ;; , for each hole "i" of length "Li", where tightness - ;; is given by `flex-score-match-tightness'. The - ;; final value for the denominator is then given by: - ;; - ;; (SUM_across_i(hole_i_contrib) + 1) * len - ;; - ;; , where "len" is the string's length. - (score-numerator 0) - (score-denominator 0) - (last-b 0) - (update-score-and-face - (lambda (a b) - "Update score and face given match range (A B)." - (add-face-text-property a b - 'completions-common-part - nil str) - (setq - score-numerator (+ score-numerator (- b a))) - (unless (or (= a last-b) - (zerop last-b) - (= a (length str))) - (setq - score-denominator (+ score-denominator - 1 - (expt (- a last-b 1) - (/ 1.0 - flex-score-match-tightness))))) - (setq - last-b b)))) - (while md - (funcall update-score-and-face from (pop md)) - (setq from (pop md))) - ;; If `pattern' doesn't have an explicit trailing any, the - ;; regex `re' won't produce match data representing the - ;; region after the match. We need to account to account - ;; for that extra bit of match (bug#42149). - (unless (= from match-end) - (funcall update-score-and-face from match-end)) - (if (> (length str) pos) - (add-face-text-property - pos (1+ pos) - 'completions-first-difference - nil str)) - (unless (zerop (length str)) - (put-text-property - 0 1 'completion-score - (/ score-numerator (* end (1+ score-denominator)) 1.0) str))) - str) - completions))) + (score (lambda (str) + (put-text-property 0 1 'completion-score + (completion--flex-score str re) + str)))) + (cond (completion-lazy-hilit + (setq completion-lazy-hilit-fn + (lambda (str) (completion--hilit-from-re str re))) + (mapc score completions)) + (t + (mapcar + (lambda (str) + (setq str (copy-sequence str)) + (funcall score str) + (completion--hilit-from-re str re) + str) + completions))))) (t completions))) (defun completion-pcm--find-all-completions (string table pred point --=-=-=--