Skip to content

Commit

Permalink
Add support for a prescient.el completion style. (#125)
Browse files Browse the repository at this point in the history
This completion style can optionally modify a completion table's
metadata to use `prescient.el` sorting, including
`prescient-sort-full-matches-first`.

- Update the CHANGELOG.
- Update the README.

- Prescient changes:
  - Add faces `prescient-primary-highlight` and
    `prescient-secondary-highlight`. Add a function to apply them.
  - Add user options `prescient-highlight-completions` and
    `prescient-completion-enable-sort`.
  - Make `prescient-filter` work with completion tables by using
    `all-completions` instead of assuming lists.
    - `prescient-filter` now propertizes all returned candidates
      with the regexps prescient used and whether it ignored case.
      This is needed for sorting after filtering.
  - Add filtering functions `prescient-all-completions`,
    `prescient-try-completion`, which use `prescient-filter`.
  - Add sorting functions `prescient--completion-modify-sort`,
    `prescient-sort-full-matches-first`, and
    `prescient-completion-sort`.
  - Define a completion style `prescient` that uses those functions.
  - The completion style can modify unsorted tables to use
    `prescient-completion-sort`, which wraps `prescient-sort` and
    `prescient-sort-full-matches-first`.
    - This requires Emacs 27+.
    - This is off by default to more easily work with multiple
      completion UIs. Some UIs (such as Vertico and Corfu) allow
      setting the sorting function to use after filtering.  Setting
      that option to the function `prescient-completion-sort` works
      and applies to the output of all completion styles and backends,
      not just `prescient`.

- Selectrum Prescient changes:
  - Old faces are now aliases of the completion faces.
  - Since Selectrum seems to remove the text properties of candidates
    after filtering, we use a function `selectrum-prescient--refine`
    that combines `prescient-filter` with
    `prescient-sort-full-matches-first`.
  - Move `selectrum-prescient--highlight` into Prescient for the
    completion style as `prescient--highlight-matches`. Use that
    instead.

- Company Prescient changes:
  - Change `company-prescient-transformer` so that uses
    `prescient-completion-sort` instead of just `prescient-sort`. This
    should allow for moving full matches when using CAPFs and will
    always at least do `prescient-sort`.

See also the PR #125. For various discussions about implementing a
`prescient` completion style, see also #125, #120, #112, #89, #58, and
#54.
  • Loading branch information
okamsn authored Sep 11, 2022
1 parent eb35fc0 commit 928cc72
Show file tree
Hide file tree
Showing 5 changed files with 514 additions and 106 deletions.
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,65 @@ The format is based on [Keep a Changelog].
literal when `prescient-use-char-folding` was nil. This bug was
added with that user option. See [#127].

### New features
* Add new function `prescient-sort-full-matches-first` which
implements the option. This feature already existed, but moving to a
separate function makes it easier to support in more UIs. See
[#125].
* Add a completion style `prescient`. This completion style can be
used in the variable `completion-styles`. This completion style
works with UIs like Emacs's built-in minibuffer completion,
Icomplete, and Vertico. See various discussions in [#125], [#120],
[#112], [#89], [#58], and [#54].
* In Emacs 27+, this completion style can optionally modify
unsorted completion tables to use `prescient.el` sorting instead.
This behavior can be enabled by setting the new user option
`prescient-completion-enable-sort` to `t`. Note that this might
lead to sorting candidates twice, such as when
`company-prescient-mode` is enabled and a Company backend filters
using the `prescient` completion style.
* The modification sets the sorting function to the new function
`prescient-completion-sort`, which combines `prescient-sort` with
the new function `prescient-sort-full-matches-first`. This
function is meant to be used after filtering.

Some completion UIs allow explicitly setting the sorting function.
Setting such an option to `prescient-completion-sort` is
recommended to use prescient.el's sorting with other completion
styles and backends. Note that sorting fully matched candidates
before others only works for candidates filtered by `prescient`.
* Added user option `prescient-completion-highlight-matches`, which
determines whether the completion style highlights the matching
parts of candidates with the above new faces ([#125]).
* Add faces `prescient-primary-highlight` and
`prescient-secondary-highlight` ([#125]). These faces are used with
the completion style and `selectrum-prescient.el`. The old faces
`selectrum-prescient-primary-highlight` and
`selectrum-prescient-secondary-highlight` are now obsolete aliases
of these faces.

### Enhancements
* `prescient-filter` now supports filtering candidates from Emacs's
more generic "completion tables", not just lists of strings ([#125]).
However, like with some other completion styles, it does not work
well with certain dynamic completion tables that use a prefix string
to produce candidates before filtering. To work around this, it is
recommended to include the `basic` style after the `prescient` style
in the user option `completion-styles`.

### Internal Changes
* `prescient-filter` now uses the C function `all-completions` instead
of being completely written in Emacs Lisp. This should make it a bit
faster. See [#125].

[#54]: https://github.com/raxod502/prescient.el/issues/54
[#58]: https://github.com/raxod502/prescient.el/issues/58
[#89]: https://github.com/raxod502/prescient.el/issues/89
[#112]: https://github.com/raxod502/prescient.el/issues/112
[#120]: https://github.com/raxod502/prescient.el/issues/120
[#123]: https://github.com/radian-software/prescient.el/issues/123
[#124]: https://github.com/radian-software/prescient.el/pull/124
[#125]: https://github.com/raxod502/prescient.el/pull/125
[#126]: https://github.com/radian-software/prescient.el/pull/126
[#127]: https://github.com/radian-software/prescient.el/pull/127

Expand Down
81 changes: 72 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ candidates, such as appear when you use a package like [Ivy] or
`company-prescient.el` adapt the library for usage with various
frameworks.

`prescient.el` also provides a completion style (`prescient`) for
filtering candidates via Emacs's generic minibuffer completion, such
as [Icomplete], though no package is provided for setting up sorting
with these more generic frameworks.

As compared to other packages which accomplish similar tasks,
including [IDO], [Ivy], [Helm], [Smex], [Flx], [Historian], and
[Company-Statistics], `prescient.el` aims to be simpler, more
Expand Down Expand Up @@ -45,6 +50,27 @@ prefer.
enable `selectrum-prescient-mode`.
* To cause your usage statistics to be saved between Emacs sessions,
enable `prescient-persist-mode`.
* To cause Emacs to use `prescient` completion style for filtering and
optionally sorting:
1. Add the symbol `prescient` to the user option `completion-styles`
to use `prescient.el` for filtering. It is recommended to keep
the `basic` style active and after the `prescient` style, as some
completion tables only work with that style. Each completion
style is tried in turn, such that the `basic` style would only be
used if the `prescient` style could not find any matching
candidates.

```elisp
(setq completion-styles '(prescient basic))
```
2. Configure your completion UI to call `prescient-remember` on the
chosen candidate so that candidates are sorted correctly.
3. Configure your completion UI to sort filtered candidates via
`prescient-completion-sort` or a custom sorting function as
needed. Optionally, on Emacs 27+, you can instead set
`prescient-completion-enable-sort` (see below) to `t` to allow
the `prescient` completion style to set sorting directly. This
option is off by default to better work with various UIs.
Please note that **you must load Counsel before `ivy-prescient.el`**.
This is because loading Counsel results in a number of changes being
Expand Down Expand Up @@ -105,18 +131,52 @@ command `prescient-forget`.
* `prescient-sort-full-matches-first`: Whether `prescient.el` sorts
candidates that are fully matched before candidates that are
partially matched.
partially matched. This user option affects:
- the `prescient` completion style and completion UIs configured to
use the function `prescient-completion-sort` after filtering
- `selectrum-prescient.el`
- `company-prescient.el` for Company backends that used the
`prescient` completion style for filtering
* `prescient-use-char-folding`: Whether the `literal` and
`literal-prefix` filter methods use character folding.
* `prescient-use-case-folding`: Whether filtering methods use case
folding (in non-Emacs terms, whether they are not case-sensitive).
This can be one of `nil`, `t`, or `smart` (the default). If
`smart`, then case folding is disabled when upper-case characters
are sought.

### Company-specific
folding (in non-Emacs terms, whether they are case insensitive).
This can be one of `nil`, `t`, or `smart` (the default). If `smart`,
then case folding is disabled when upper-case characters are used.
### For the completion style
The following user options are specific to using the `prescient`
completion style:
* `prescient-completion-highlight-matches`: Whether the completion
style should highlight matches in the filtered candidates.
* `prescient-completion-enable-sort`: Whether, in Emacs 27+, the
completion style should automatically modify unsorted candidates to
instead use the function `prescient-completion-sort`. This is
disabled by default to be more generic and to avoid users
accidentally sorting candidates more than once, such as when
`company-prescient-mode` is enabled and the `prescient` completion
style is used to filter candidates in the Company backend.
This user option only controls sorting done by the `prescient`
completion style. It does not affect sorting for the other styles in
the `completion-style` user option.
Some completion UIs, such as [Vertico][vertico] and [Corfu][corfu],
allow you to explicitly set a default sorting function for unsorted
candidates. In that case, you could set such an option to
`prescient-completion-sort`, which wraps the functions
`prescient-sort` and `prescient-sort-full-matches-first`. This would
also allow you to use prescient.el's sorting (excluding
`prescient-sort-full-matches-first`) with other completion styles
and completion backends.
### For Company
The following user options are specific to using `prescient.el`
sorting with Company:
* `company-prescient-sort-length-enable`: By default, the standard
`prescient.el` sorting algorithm is used for all Company completions
Expand All @@ -131,7 +191,7 @@ command `prescient-forget`.
frequently used candidates to the top of the completions list, but
otherwise leave candidate ordering alone.
### Ivy-specific
### For Ivy
The following user options are specific to using `prescient.el` with
Ivy:
Expand All @@ -155,7 +215,7 @@ Ivy:
the Ivy documentation for information on how Ivy sorts by default,
and how to customize it manually.
### Selectrum-specific
### For Selectrum
The following user options are specific to using `prescient.el` with
Selectrum:
Expand Down Expand Up @@ -245,12 +305,14 @@ Ivy, and copied them to be used for Selectrum as well:
Please see [the contributor guide for my
projects](https://github.com/raxod502/contributor-guide).

[corfu]: https://github.com/minad/corfu
[company]: https://github.com/company-mode/company-mode
[company-statistics]: https://github.com/company-mode/company-statistics
[counsel]: https://github.com/abo-abo/swiper#counsel
[flx]: https://github.com/lewang/flx
[helm]: https://github.com/emacs-helm/helm
[historian]: https://github.com/PythonNut/historian.el
[icomplete]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Icomplete.html
[ido]: https://www.gnu.org/software/emacs/manual/ido.html
[ivy]: https://github.com/abo-abo/swiper#ivy
[ivy-release]: https://github.com/abo-abo/swiper/issues/1664
Expand All @@ -259,3 +321,4 @@ projects](https://github.com/raxod502/contributor-guide).
[selectrum]: https://github.com/raxod502/selectrum
[smex]: https://github.com/nonsequitur/smex
[straight.el]: https://github.com/raxod502/straight.el
[vertico]: https://github.com/minad/vertico
7 changes: 6 additions & 1 deletion company-prescient.el
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,19 @@ this variable is `:default', then this binding is skipped."

;;;; Minor mode

(declare-function prescient-completion-sort "prescient" (candidates))
(defun company-prescient-transformer (candidates)
"Candidate transformer function that uses prescient.el to sort CANDIDATES.
This is for use in `company-transformers'."
;; Candidates are always sorted and de-duplicated before being
;; passed to transformers. Therefore, we are always trying to
;; at least apply prescient.el sorting on top the existing sort,
;; if not overwrite it entirely.
(let ((prescient-sort-length-enable
(if (eq company-prescient-sort-length-enable :default)
prescient-sort-length-enable
company-prescient-sort-length-enable)))
(prescient-sort candidates)))
(prescient-completion-sort candidates)))

(defalias 'company-prescient-completion-finished #'prescient-remember
"Hook function to remember selected Company candidate.
Expand Down
Loading

0 comments on commit 928cc72

Please sign in to comment.