From a1c6db92e38aa78f9603b3a16335c45b22dbf66c Mon Sep 17 00:00:00 2001 From: Brian Schulte Date: Fri, 25 May 2018 20:07:50 -0400 Subject: [PATCH 1/3] Allow for completions filter to be case insensitive --- .../Services/Completion/CompletionSelectors.ts | 18 ++++++++++++++++-- .../Configuration/DefaultConfiguration.ts | 5 ++++- .../Configuration/IConfigurationValues.ts | 3 +++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/browser/src/Services/Completion/CompletionSelectors.ts b/browser/src/Services/Completion/CompletionSelectors.ts index 127c46efcc..0f61ba539c 100644 --- a/browser/src/Services/Completion/CompletionSelectors.ts +++ b/browser/src/Services/Completion/CompletionSelectors.ts @@ -4,6 +4,7 @@ * Selectors are functions that take a state and derive a value from it. */ +import { configuration } from "./../Configuration" import { ICompletionState } from "./CompletionState" import * as types from "vscode-languageserver-types" @@ -72,11 +73,24 @@ export const filterCompletionOptions = ( return null } - const filterRegEx = new RegExp("^" + searchText.split("").join(".*") + ".*") + const shouldFilterBeCaseSensitive = configuration.getValue("editor.completions.caseSensitive") + + const filterRegEx = shouldFilterBeCaseSensitive + ? new RegExp("^" + searchText.split("").join(".*") + ".*") + : new RegExp( + "^" + + searchText + .toLowerCase() + .split("") + .join(".*") + + ".*", + ) const filteredOptions = items.filter(f => { const textToFilterOn = f.filterText || f.label - return textToFilterOn.match(filterRegEx) + return shouldFilterBeCaseSensitive + ? textToFilterOn.match(filterRegEx) + : textToFilterOn.toLowerCase().match(filterRegEx) }) return filteredOptions.sort((itemA, itemB) => { diff --git a/browser/src/Services/Configuration/DefaultConfiguration.ts b/browser/src/Services/Configuration/DefaultConfiguration.ts index 79bdae8a29..6253baf6ac 100644 --- a/browser/src/Services/Configuration/DefaultConfiguration.ts +++ b/browser/src/Services/Configuration/DefaultConfiguration.ts @@ -99,6 +99,7 @@ const BaseConfiguration: IConfigurationValues = { "editor.quickInfo.delay": 500, "editor.completions.mode": "oni", + "editor.completions.caseSensitive": true, "editor.errors.slideOnFocus": true, "editor.formatting.formatOnSwitchToNormalMode": false, @@ -459,7 +460,9 @@ const LinuxConfigOverrides: Partial = { const PlatformConfigOverride = Platform.isWindows() ? WindowsConfigOverrides - : Platform.isLinux() ? LinuxConfigOverrides : MacConfigOverrides + : Platform.isLinux() + ? LinuxConfigOverrides + : MacConfigOverrides export const DefaultConfiguration = { ...BaseConfiguration, diff --git a/browser/src/Services/Configuration/IConfigurationValues.ts b/browser/src/Services/Configuration/IConfigurationValues.ts index 60eb8ad501..c85ca56893 100644 --- a/browser/src/Services/Configuration/IConfigurationValues.ts +++ b/browser/src/Services/Configuration/IConfigurationValues.ts @@ -150,6 +150,9 @@ export interface IConfigurationValues { // a custom init.vim, as that may cause problematic behavior "editor.completions.mode": string + // Decide whether or not the completion matching should be case sensitive + "editor.completions.caseSensitive": boolean + // If true (default), ligatures are enabled "editor.fontLigatures": boolean "editor.fontSize": string From 21a3044a16311391657d3bd8e35d86cac638c1d7 Mon Sep 17 00:00:00 2001 From: Brian Schulte Date: Sat, 26 May 2018 13:19:24 -0400 Subject: [PATCH 2/3] Refactored to allow for smart case completion --- .../Completion/CompletionSelectors.ts | 68 +++++++++++++------ .../Configuration/IConfigurationValues.ts | 2 +- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/browser/src/Services/Completion/CompletionSelectors.ts b/browser/src/Services/Completion/CompletionSelectors.ts index 0f61ba539c..8992008bba 100644 --- a/browser/src/Services/Completion/CompletionSelectors.ts +++ b/browser/src/Services/Completion/CompletionSelectors.ts @@ -13,6 +13,31 @@ const EmptyCompletions: types.CompletionItem[] = [] import * as CompletionUtility from "./CompletionUtility" +export const shouldFilterBeCaseSensitive = (searchString: string): boolean => { + // TODO: Technically, this makes the reducer 'impure', + // which is not ideal - need to refactor eventually. + // + // One option is to plumb through the configuration setting + // from the top-level, but it might be worth extracting + // out the filter strategy in general. + const caseSensitivitySetting = configuration.getValue("editor.completions.caseSensitive") + + if (caseSensitivitySetting === false) { + return false + } else if (caseSensitivitySetting === true) { + return true + } else { + // "Smart" casing strategy + // If the string is all lower-case, not case sensitive.. + if (searchString === searchString.toLowerCase()) { + return false + // Otherwise, it is case sensitive.. + } else { + return true + } + } +} + export const getFilteredCompletions = (state: ICompletionState): types.CompletionItem[] => { if (!state.completionResults.completions || !state.completionResults.completions.length) { return EmptyCompletions @@ -73,25 +98,11 @@ export const filterCompletionOptions = ( return null } - const shouldFilterBeCaseSensitive = configuration.getValue("editor.completions.caseSensitive") - - const filterRegEx = shouldFilterBeCaseSensitive - ? new RegExp("^" + searchText.split("").join(".*") + ".*") - : new RegExp( - "^" + - searchText - .toLowerCase() - .split("") - .join(".*") + - ".*", - ) - - const filteredOptions = items.filter(f => { - const textToFilterOn = f.filterText || f.label - return shouldFilterBeCaseSensitive - ? textToFilterOn.match(filterRegEx) - : textToFilterOn.toLowerCase().match(filterRegEx) - }) + const isCaseSensitive = shouldFilterBeCaseSensitive(searchText) + + let filteredOptions = items + + filteredOptions = processSearchText(searchText, filteredOptions, isCaseSensitive) return filteredOptions.sort((itemA, itemB) => { const itemAFilterText = itemA.filterText || itemA.label @@ -103,3 +114,22 @@ export const filterCompletionOptions = ( return indexOfB - indexOfA }) } + +export const processSearchText = ( + searchText: string, + items: types.CompletionItem[], + isCaseSensitive: boolean, +): types.CompletionItem[] => { + const properCaseStr = isCaseSensitive ? searchText : searchText.toLowerCase() + const filterRegExp = new RegExp(".*" + properCaseStr.split("").join(".*") + ".*") + + return items.filter(f => { + let textToFilterOn = f.filterText || f.label + + if (!isCaseSensitive) { + textToFilterOn = textToFilterOn.toLowerCase() + } + + return textToFilterOn.match(filterRegExp) + }) +} diff --git a/browser/src/Services/Configuration/IConfigurationValues.ts b/browser/src/Services/Configuration/IConfigurationValues.ts index c85ca56893..a52f9bfa62 100644 --- a/browser/src/Services/Configuration/IConfigurationValues.ts +++ b/browser/src/Services/Configuration/IConfigurationValues.ts @@ -151,7 +151,7 @@ export interface IConfigurationValues { "editor.completions.mode": string // Decide whether or not the completion matching should be case sensitive - "editor.completions.caseSensitive": boolean + "editor.completions.caseSensitive": boolean | string // If true (default), ligatures are enabled "editor.fontLigatures": boolean From 9881538e1fd521003f3161e4e709c86a404cccca Mon Sep 17 00:00:00 2001 From: Brian Schulte Date: Sat, 26 May 2018 13:33:26 -0400 Subject: [PATCH 3/3] Small refactor to match code pattern elsewhere in the project --- browser/src/Services/Completion/CompletionSelectors.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/browser/src/Services/Completion/CompletionSelectors.ts b/browser/src/Services/Completion/CompletionSelectors.ts index 8992008bba..0d933e52bf 100644 --- a/browser/src/Services/Completion/CompletionSelectors.ts +++ b/browser/src/Services/Completion/CompletionSelectors.ts @@ -100,9 +100,11 @@ export const filterCompletionOptions = ( const isCaseSensitive = shouldFilterBeCaseSensitive(searchText) - let filteredOptions = items + if (!isCaseSensitive) { + searchText = searchText.toLocaleLowerCase() + } - filteredOptions = processSearchText(searchText, filteredOptions, isCaseSensitive) + const filteredOptions = processSearchText(searchText, items, isCaseSensitive) return filteredOptions.sort((itemA, itemB) => { const itemAFilterText = itemA.filterText || itemA.label @@ -120,8 +122,7 @@ export const processSearchText = ( items: types.CompletionItem[], isCaseSensitive: boolean, ): types.CompletionItem[] => { - const properCaseStr = isCaseSensitive ? searchText : searchText.toLowerCase() - const filterRegExp = new RegExp(".*" + properCaseStr.split("").join(".*") + ".*") + const filterRegExp = new RegExp(".*" + searchText.split("").join(".*") + ".*") return items.filter(f => { let textToFilterOn = f.filterText || f.label