From b26779801040455b8c6ecfc1a9ab058edfd7f156 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 13 Aug 2018 19:15:42 +0100 Subject: [PATCH] Kill FuzzyMatcher This has been commented out for ages. Just remove it and make things use QueryMatcher directly rather than looking like they do fuzzy matching but not. --- src/autocomplete/CommandProvider.js | 4 +- src/autocomplete/CommunityProvider.js | 4 +- src/autocomplete/EmojiProvider.js | 6 +- src/autocomplete/FuzzyMatcher.js | 107 -------------------------- src/autocomplete/RoomProvider.js | 4 +- src/autocomplete/UserProvider.js | 8 +- 6 files changed, 14 insertions(+), 119 deletions(-) delete mode 100644 src/autocomplete/FuzzyMatcher.js diff --git a/src/autocomplete/CommandProvider.js b/src/autocomplete/CommandProvider.js index a35a31966a..609a8fa9a1 100644 --- a/src/autocomplete/CommandProvider.js +++ b/src/autocomplete/CommandProvider.js @@ -20,7 +20,7 @@ limitations under the License. import React from 'react'; import {_t} from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; -import FuzzyMatcher from './FuzzyMatcher'; +import QueryMatcher from './QueryMatcher'; import {TextualCompletion} from './Components'; import type {Completion, SelectionRange} from "./Autocompleter"; import {CommandMap} from '../SlashCommands'; @@ -32,7 +32,7 @@ const COMMAND_RE = /(^\/\w*)(?: .*)?/g; export default class CommandProvider extends AutocompleteProvider { constructor() { super(COMMAND_RE); - this.matcher = new FuzzyMatcher(COMMANDS, { + this.matcher = new QueryMatcher(COMMANDS, { keys: ['command', 'args', 'description'], }); } diff --git a/src/autocomplete/CommunityProvider.js b/src/autocomplete/CommunityProvider.js index 6bcf1a02fd..d164fab46a 100644 --- a/src/autocomplete/CommunityProvider.js +++ b/src/autocomplete/CommunityProvider.js @@ -19,7 +19,7 @@ import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; import MatrixClientPeg from '../MatrixClientPeg'; -import FuzzyMatcher from './FuzzyMatcher'; +import QueryMatcher from './QueryMatcher'; import {PillCompletion} from './Components'; import sdk from '../index'; import _sortBy from 'lodash/sortBy'; @@ -41,7 +41,7 @@ function score(query, space) { export default class CommunityProvider extends AutocompleteProvider { constructor() { super(COMMUNITY_REGEX); - this.matcher = new FuzzyMatcher([], { + this.matcher = new QueryMatcher([], { keys: ['groupId', 'name', 'shortDescription'], }); } diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 719550d59f..8c6495101f 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -20,7 +20,7 @@ import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; import {shortnameToUnicode, asciiRegexp, unicodeRegexp} from 'emojione'; -import FuzzyMatcher from './FuzzyMatcher'; +import QueryMatcher from './QueryMatcher'; import sdk from '../index'; import {PillCompletion} from './Components'; import type {Completion, SelectionRange} from './Autocompleter'; @@ -84,12 +84,12 @@ function score(query, space) { export default class EmojiProvider extends AutocompleteProvider { constructor() { super(EMOJI_REGEX); - this.matcher = new FuzzyMatcher(EMOJI_SHORTNAMES, { + this.matcher = new QueryMatcher(EMOJI_SHORTNAMES, { keys: ['aliases_ascii', 'shortname', 'aliases'], // For matching against ascii equivalents shouldMatchWordsOnly: false, }); - this.nameMatcher = new FuzzyMatcher(EMOJI_SHORTNAMES, { + this.nameMatcher = new QueryMatcher(EMOJI_SHORTNAMES, { keys: ['name'], // For removing punctuation shouldMatchWordsOnly: true, diff --git a/src/autocomplete/FuzzyMatcher.js b/src/autocomplete/FuzzyMatcher.js deleted file mode 100644 index 1aa0782c22..0000000000 --- a/src/autocomplete/FuzzyMatcher.js +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright 2017 Aviral Dasgupta - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -//import Levenshtein from 'liblevenshtein'; -//import _at from 'lodash/at'; -//import _flatMap from 'lodash/flatMap'; -//import _sortBy from 'lodash/sortBy'; -//import _sortedUniq from 'lodash/sortedUniq'; -//import _keys from 'lodash/keys'; -// -//class KeyMap { -// keys: Array; -// objectMap: {[String]: Array}; -// priorityMap: {[String]: number} -//} -// -//const DEFAULT_RESULT_COUNT = 10; -//const DEFAULT_DISTANCE = 5; - -// FIXME Until Fuzzy matching works better, we use prefix matching. - -import PrefixMatcher from './QueryMatcher'; -export default PrefixMatcher; - -//class FuzzyMatcher { // eslint-disable-line no-unused-vars -// /** -// * @param {object[]} objects the objects to perform a match on -// * @param {string[]} keys an array of keys within each object to match on -// * Keys can refer to object properties by name and as in JavaScript (for nested properties) -// * -// * To use, simply presort objects by required criteria, run through this function and create a FuzzyMatcher with the -// * resulting KeyMap. -// * -// * TODO: Handle arrays and objects (Fuse did this, RoomProvider uses it) -// * @return {KeyMap} -// */ -// static valuesToKeyMap(objects: Array, keys: Array): KeyMap { -// const keyMap = new KeyMap(); -// const map = {}; -// const priorities = {}; -// -// objects.forEach((object, i) => { -// const keyValues = _at(object, keys); -// console.log(object, keyValues, keys); -// for (const keyValue of keyValues) { -// if (!map.hasOwnProperty(keyValue)) { -// map[keyValue] = []; -// } -// map[keyValue].push(object); -// } -// priorities[object] = i; -// }); -// -// keyMap.objectMap = map; -// keyMap.priorityMap = priorities; -// keyMap.keys = _sortBy(_keys(map), [(value) => priorities[value]]); -// return keyMap; -// } -// -// constructor(objects: Array, options: {[Object]: Object} = {}) { -// this.options = options; -// this.keys = options.keys; -// this.setObjects(objects); -// } -// -// setObjects(objects: Array) { -// this.keyMap = FuzzyMatcher.valuesToKeyMap(objects, this.keys); -// console.log(this.keyMap.keys); -// this.matcher = new Levenshtein.Builder() -// .dictionary(this.keyMap.keys, true) -// .algorithm('transposition') -// .sort_candidates(false) -// .case_insensitive_sort(true) -// .include_distance(true) -// .maximum_candidates(this.options.resultCount || DEFAULT_RESULT_COUNT) // result count 0 doesn't make much sense -// .build(); -// } -// -// match(query: String): Array { -// const candidates = this.matcher.transduce(query, this.options.distance || DEFAULT_DISTANCE); -// // TODO FIXME This is hideous. Clean up when possible. -// const val = _sortedUniq(_sortBy(_flatMap(candidates, (candidate) => { -// return this.keyMap.objectMap[candidate[0]].map((value) => { -// return { -// distance: candidate[1], -// ...value, -// }; -// }); -// }), -// [(candidate) => candidate.distance, (candidate) => this.keyMap.priorityMap[candidate]])); -// console.log(val); -// return val; -// } -//} diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index 38e2ab8373..483506557f 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -21,7 +21,7 @@ import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; import MatrixClientPeg from '../MatrixClientPeg'; -import FuzzyMatcher from './FuzzyMatcher'; +import QueryMatcher from './QueryMatcher'; import {PillCompletion} from './Components'; import {getDisplayAliasForRoom} from '../Rooms'; import sdk from '../index'; @@ -43,7 +43,7 @@ function score(query, space) { export default class RoomProvider extends AutocompleteProvider { constructor() { super(ROOM_REGEX); - this.matcher = new FuzzyMatcher([], { + this.matcher = new QueryMatcher([], { keys: ['displayedAlias', 'name'], }); } diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index e9cbf7945b..ed9c8ee62b 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -23,7 +23,7 @@ import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; import {PillCompletion} from './Components'; import sdk from '../index'; -import FuzzyMatcher from './FuzzyMatcher'; +import QueryMatcher from './QueryMatcher'; import _sortBy from 'lodash/sortBy'; import MatrixClientPeg from '../MatrixClientPeg'; @@ -44,7 +44,7 @@ export default class UserProvider extends AutocompleteProvider { constructor(room) { super(USER_REGEX, FORCED_USER_REGEX); this.room = room; - this.matcher = new FuzzyMatcher([], { + this.matcher = new QueryMatcher([], { keys: ['name', 'userId'], shouldMatchPrefix: true, shouldMatchWordsOnly: false, @@ -104,7 +104,9 @@ export default class UserProvider extends AutocompleteProvider { const fullMatch = command[0]; // Don't search if the query is a single "@" if (fullMatch && fullMatch !== '@') { - completions = this.matcher.match(fullMatch).map((user) => { + // Don't include the '@' in our search query - it's only used as a way to trigger completion + const query = fullMatch.startsWith('@') ? fullMatch.substring(1) : fullMatch; + completions = this.matcher.match(query).map((user) => { const displayName = (user.name || user.userId || ''); return { // Length of completion should equal length of text in decorator. draft-js