Skip to content

Commit

Permalink
Introduce filterStrict option
Browse files Browse the repository at this point in the history
FEATURE: The new `filterStrict` option can be used to turn off fuzzy
matching of completions.

https://discuss.codemirror.net/t/search-adjustment-for-autocomplete/7975
  • Loading branch information
marijnh committed Mar 13, 2024
1 parent e244d51 commit 8eea911
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 4 deletions.
9 changes: 8 additions & 1 deletion src/config.ts
Expand Up @@ -72,6 +72,11 @@ export interface CompletionConfig {
/// match score. Defaults to using
/// [`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
compareCompletions?: (a: Completion, b: Completion) => number
/// When set to true (the default is false), turn off fuzzy matching
/// of completions and only show those that start with the text the
/// user typed. Only takes effect for results where
/// [`filter`](#autocomplete.CompletionResult.filter) isn't false.
filterStrict?: boolean
/// By default, commands relating to an open completion only take
/// effect 75 milliseconds after the completion opened, so that key
/// presses made before the user is aware of the tooltip don't go to
Expand Down Expand Up @@ -100,6 +105,7 @@ export const completionConfig = Facet.define<CompletionConfig, Required<Completi
icons: true,
addToOptions: [],
positionInfo: defaultPositionInfo as any,
filterStrict: false,
compareCompletions: (a, b) => a.label.localeCompare(b.label),
interactionDelay: 75,
updateSyncTime: 100
Expand All @@ -109,7 +115,8 @@ export const completionConfig = Facet.define<CompletionConfig, Required<Completi
icons: (a, b) => a && b,
tooltipClass: (a, b) => c => joinClass(a(c), b(c)),
optionClass: (a, b) => c => joinClass(a(c), b(c)),
addToOptions: (a, b) => a.concat(b)
addToOptions: (a, b) => a.concat(b),
filterStrict: (a, b) => a || b,
})
}
})
Expand Down
21 changes: 21 additions & 0 deletions src/filter.ts
Expand Up @@ -143,3 +143,24 @@ export class FuzzyMatcher {
return this.ret(score - word.length, result)
}
}


export class StrictMatcher {
matched: readonly number[] = []
score: number = 0
folded: string

constructor(readonly pattern: string) {
this.folded = pattern.toLowerCase()
}

match(word: string): {score: number, matched: readonly number[]} | null {
if (word.length < this.pattern.length) return null
let start = word.slice(0, this.pattern.length)
let match = start == this.pattern ? 0 : start.toLowerCase() == this.folded ? Penalty.CaseFold : null
if (match == null) return null
this.matched = [0, start.length]
this.score = match + (word.length == this.pattern.length ? 0 : Penalty.NotFull)
return this
}
}
8 changes: 5 additions & 3 deletions src/state.ts
Expand Up @@ -4,7 +4,7 @@ import {Option, CompletionSource, CompletionResult, cur, asSource,
Completion, ensureAnchor, CompletionContext, CompletionSection,
startCompletionEffect, closeCompletionEffect,
insertCompletionText, pickedCompletion} from "./completion"
import {FuzzyMatcher} from "./filter"
import {FuzzyMatcher, StrictMatcher} from "./filter"
import {completionTooltip} from "./tooltip"
import {CompletionConfig, completionConfig} from "./config"

Expand All @@ -28,14 +28,16 @@ function sortOptions(active: readonly ActiveSource[], state: EditorState) {
}
}

let conf = state.facet(completionConfig)
for (let a of active) if (a.hasResult()) {
let getMatch = a.result.getMatch
if (a.result.filter === false) {
for (let option of a.result.options) {
addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length))
}
} else {
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match
let pattern = state.sliceDoc(a.from, a.to), match
let matcher = conf.filterStrict ? new StrictMatcher(pattern) : new FuzzyMatcher(pattern)
for (let option of a.result.options) if (match = matcher.match(option.label)) {
let matched = !option.displayLabel ? match.matched : getMatch ? getMatch(option, match.matched) : []
addOption(new Option(option, a.source, matched, match.score + (option.boost || 0)))
Expand All @@ -57,7 +59,7 @@ function sortOptions(active: readonly ActiveSource[], state: EditorState) {
}

let result = [], prev = null
let compare = state.facet(completionConfig).compareCompletions
let compare = conf.compareCompletions
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
let cur = opt.completion
if (!prev || prev.label != cur.label || prev.detail != cur.detail ||
Expand Down

0 comments on commit 8eea911

Please sign in to comment.