From 8c8d0eeea3e2003792f67c234b5d562be7f3f5c7 Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Wed, 25 Aug 2021 13:31:10 +0200 Subject: [PATCH] fix: fix issue where some dictionaries (nl-nl) could cause slow suggestions (#1583) Using compound words can be very very slow to generate suggestions. The current Dutch dictionary has compounds turned on. This caused very very slow suggestions to be generated. --- .../src/Settings/CSpellSettingsServer.test.ts | 2 + .../SpellingDictionaryCollection.test.ts | 49 ++++++++++++++++++- .../SpellingDictionaryFromTrie.ts | 5 +- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/packages/cspell-lib/src/Settings/CSpellSettingsServer.test.ts b/packages/cspell-lib/src/Settings/CSpellSettingsServer.test.ts index c356bcf78f78..83b1908a5190 100644 --- a/packages/cspell-lib/src/Settings/CSpellSettingsServer.test.ts +++ b/packages/cspell-lib/src/Settings/CSpellSettingsServer.test.ts @@ -525,6 +525,8 @@ describe('Validate search/load config files', () => { ${s('js-config/cspell-no-export.js')} | ${cfg(s('js-config/cspell-no-export.js'))} ${s('js-config/cspell-bad.js')} | ${cfg(readError(s('js-config/cspell-bad.js')))} `('ReadRawSettings from $file', async ({ file, expectedConfig }: TestLoadConfig) => { + // js-config/cspell-no-export.js logs a message. + jest.spyOn(console, 'log').mockImplementation(() => undefined); const searchResult = await readRawSettings(file); expect(searchResult).toEqual(expectedConfig ? expect.objectContaining(expectedConfig) : undefined); }); diff --git a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.test.ts b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.test.ts index a9e780524eac..9f91fffb1d4c 100644 --- a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.test.ts +++ b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.test.ts @@ -107,11 +107,56 @@ describe('Verify using multiple dictionaries', () => { const dictCollection = createCollection(dicts, 'test'); const sugResult = dictCollection.suggest('appletango', 10, CompoundWordsMethod.SEPARATE_WORDS); const sugs = sugResult.map((a) => a.word); - expect(sugs).toHaveLength(10); - expect(sugs).toContain('apple+mango'); + expect(sugs).not.toContain('apple+mango'); expect(sugs).toContain('apple mango'); }); + test('checks for compound NONE suggestions', async () => { + // Add "wordsA" twice, once as a compound dictionary and once as a normal dictionary. + const trie = new SpellingDictionaryFromTrie(Trie.Trie.create(wordsA), 'wordsA', {}); + trie.options.useCompounds = true; + const dicts = await Promise.all([ + trie, + createSpellingDictionary(wordsB, 'wordsB', 'test', undefined), + createSpellingDictionary(wordsA, 'wordsA', 'test', undefined), + createSpellingDictionary(wordsC, 'wordsC', 'test', undefined), + createForbiddenWordsDictionary(['Avocado'], 'flag_words', 'test', undefined), + ]); + + // cspell:ignore appletango applemango + const dictCollection = createCollection(dicts, 'test'); + const sugResult = dictCollection.suggest('applemango', 10, CompoundWordsMethod.NONE); + const sugs = sugResult.map((a) => a.word); + expect(sugs).not.toContain('apple+mango'); + expect(sugs).not.toContain('apple mango'); + expect(sugs).toContain('apple'); + expect(sugs).toContain('mango'); + }); + + test('checks for compound JOIN_WORDS suggestions', async () => { + // Add "wordsA" twice, once as a compound dictionary and once as a normal dictionary. + const trie = new SpellingDictionaryFromTrie(Trie.Trie.create(wordsA), 'wordsA', {}); + trie.options.useCompounds = true; + const dicts = await Promise.all([ + trie, + createSpellingDictionary(wordsB, 'wordsB', 'test', undefined), + createSpellingDictionary(wordsA, 'wordsA', 'test', undefined), + createSpellingDictionary(wordsC, 'wordsC', 'test', undefined), + createForbiddenWordsDictionary(['Avocado'], 'flag_words', 'test', undefined), + ]); + + // cspell:ignore appletango applemango + const dictCollection = createCollection(dicts, 'test'); + const sugResult = dictCollection.suggest('applemango', 10, CompoundWordsMethod.JOIN_WORDS); + const sugs = sugResult.map((a) => a.word); + expect(sugs).toContain('apple+mango'); + expect(sugs).not.toContain('apple mango'); + // possible word combinations + expect(sugs).toContain('apple'); + expect(sugs).toContain('apple+apple'); + expect(sugs).toContain('grape+mango'); + }); + test('checks for compound suggestions with numbChanges', async () => { const trie = new SpellingDictionaryFromTrie(Trie.Trie.create(wordsA), 'wordsA', {}); const dicts = await Promise.all([ diff --git a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts index 30d9aadf140d..146d9906dacb 100644 --- a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts +++ b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts @@ -170,8 +170,9 @@ export class SpellingDictionaryFromTrie implements SpellingDictionary { public genSuggestions(collector: SuggestionCollector, suggestOptions: SuggestOptions): void { if (this.options.noSuggest) return; - const { compoundMethod = CompoundWordsMethod.SEPARATE_WORDS } = suggestOptions; - const _compoundMethod = this.options.useCompounds ? CompoundWordsMethod.JOIN_WORDS : compoundMethod; + const _compoundMethod = + suggestOptions.compoundMethod ?? + (this.options.useCompounds ? CompoundWordsMethod.JOIN_WORDS : CompoundWordsMethod.NONE); wordSuggestFormsArray(collector.word).forEach((w) => this.trie.genSuggestions(impersonateCollector(collector, w), _compoundMethod) );