-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: intellisense > prioritize column items over others (#465)
This is a small workaround to prevent the Monaco editor from re-arranging the IntelliSense completion items based on the user's search term. Firstly, I trigger provideCompletionItems on every key press by setting incomplete=true so I can change the filterText dynamically. To avoid calling the language server on each key press, I implemented a caching mechanism that prevents unnecessary calls. Additionally, I prepend the user search term to column type items only when the search term is present in the filter text. Since the Monaco editor re-arranges items where the search term appears first in the filterText, this ensures that the column items are brought up in the IntelliSense order. Plus, added sorting logic for intellisense items.
- Loading branch information
Showing
17 changed files
with
2,759 additions
and
3,237 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ module.exports = { | |
transform: { | ||
'^.+\\.ts$': 'ts-jest', | ||
}, | ||
setupFilesAfterEnv: ['./jest.setup.ts'], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
require('@kusto/language-service/bridge.min'); | ||
require('@kusto/language-service/Kusto.JavaScript.Client.min'); | ||
require('@kusto/language-service/newtonsoft.json.min'); | ||
require('@kusto/language-service-next/Kusto.Language.Bridge.min'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
package/src/completionCacheManager/completionCacheManager.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { test, describe, expect, beforeEach } from '@jest/globals'; | ||
import { CompletionCacheManager, GetFromLanguageService, createCompletionCacheManager } from './completionCacheManager'; | ||
import * as monaco from 'monaco-editor'; | ||
import * as ls from 'vscode-languageserver-types'; | ||
|
||
describe('completionCacheManager', () => { | ||
let completionCacheManager: CompletionCacheManager; | ||
const mockGetFromLanguageService: jest.MockedFunction<GetFromLanguageService> = jest.fn(); | ||
const resource = {} as monaco.Uri; | ||
const position = {} as ls.Position; | ||
|
||
beforeEach(() => { | ||
mockGetFromLanguageService.mockClear(); | ||
completionCacheManager = createCompletionCacheManager(mockGetFromLanguageService); | ||
}); | ||
|
||
test('calls language service on single character', async () => { | ||
await completionCacheManager.getCompletionItems('a', resource, position); | ||
expect(mockGetFromLanguageService).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
describe('on subsequent calls', () => { | ||
beforeEach(async () => { | ||
await completionCacheManager.getCompletionItems('ab', resource, position); | ||
mockGetFromLanguageService.mockClear(); | ||
}); | ||
|
||
test('does not call language service again when the previous word is contained in the current word', async () => { | ||
await completionCacheManager.getCompletionItems('abc', resource, position); | ||
expect(mockGetFromLanguageService).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
test('calls language service for different non-contained words', async () => { | ||
await completionCacheManager.getCompletionItems('ba', resource, position); | ||
expect(mockGetFromLanguageService).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
test('calls language service when current word is undefined', async () => { | ||
await completionCacheManager.getCompletionItems(undefined, resource, position); | ||
expect(mockGetFromLanguageService).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
test('calls language service when the current word is contained in the previous word', async () => { | ||
await completionCacheManager.getCompletionItems('a', resource, position); | ||
expect(mockGetFromLanguageService).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
package/src/completionCacheManager/completionCacheManager.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; | ||
import * as ls from 'vscode-languageserver-types'; | ||
|
||
export const createCompletionCacheManager = (getFromLanguageService: GetFromLanguageService) => { | ||
let completionList: Promise<ls.CompletionList> | undefined; | ||
let lastWord: string | undefined; | ||
|
||
return { | ||
getCompletionItems: (word: string | undefined, resource: monaco.Uri, position: ls.Position) => { | ||
const shouldGetItems = !lastWord || !word || !word?.includes(lastWord); | ||
if (shouldGetItems) { | ||
completionList = getFromLanguageService(resource, position); | ||
} | ||
|
||
lastWord = word; | ||
return completionList; | ||
}, | ||
}; | ||
}; | ||
|
||
export type CompletionCacheManager = ReturnType<typeof createCompletionCacheManager>; | ||
export type GetFromLanguageService = (resource: monaco.Uri, position: ls.Position) => Promise<ls.CompletionList>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { test, describe, expect } from '@jest/globals'; | ||
import { createCompletionFilteredText } from './languageFeatures.utils'; | ||
import { completionItemBuilder } from '../tests/unit/builders/CompletionListBuilder'; | ||
|
||
describe('createCompletionFilteredText', () => { | ||
test('does not prepend prefix to result when search word is undefined', () => { | ||
const completionItem = completionItemBuilder().withForcePrecedence(true).build(); | ||
|
||
const filterText = createCompletionFilteredText(undefined, completionItem); | ||
expect(filterText).toBe(completionItem.filterText); | ||
}); | ||
|
||
describe('when the search word is included in the original filter text', () => { | ||
const originalFilterText = 'StartTime'; | ||
const searchWord = 'time'; | ||
|
||
test('prepends prefix to result when forcePrecedence=true', () => { | ||
const completionItem = completionItemBuilder() | ||
.withFilterText(originalFilterText) | ||
.withForcePrecedence(true) | ||
.build(); | ||
|
||
const filterText = createCompletionFilteredText(searchWord, completionItem); | ||
expect(filterText).toBe(`${searchWord}${completionItem.filterText}`); | ||
}); | ||
|
||
test('does not prepend prefix to result when forcePrecedence=false', () => { | ||
const completionItem = completionItemBuilder() | ||
.withFilterText(originalFilterText) | ||
.withForcePrecedence(false) | ||
.build(); | ||
|
||
const filterText = createCompletionFilteredText(searchWord, completionItem); | ||
expect(filterText).toBe(completionItem.filterText); | ||
}); | ||
}); | ||
|
||
describe('when the search word is NOT included in the original filter text', () => { | ||
const originalFilterText = 'StartTime'; | ||
const searchWord = 'end'; | ||
|
||
test('does not prepend prefix to result when forcePrecedence=true', () => { | ||
const completionItem = completionItemBuilder() | ||
.withFilterText(originalFilterText) | ||
.withForcePrecedence(true) | ||
.build(); | ||
|
||
const filterText = createCompletionFilteredText(searchWord, completionItem); | ||
expect(filterText).toBe(completionItem.filterText); | ||
}); | ||
|
||
test('does not prepend prefix to result when forcePrecedence=false', () => { | ||
const completionItem = completionItemBuilder() | ||
.withFilterText(originalFilterText) | ||
.withForcePrecedence(false) | ||
.build(); | ||
|
||
const filterText = createCompletionFilteredText(searchWord, completionItem); | ||
expect(filterText).toBe(completionItem.filterText); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import * as ls from 'vscode-languageserver-types'; | ||
|
||
export function createCompletionFilteredText( | ||
searchWord: string | undefined, | ||
completionItem: ls.CompletionItem | ||
): string { | ||
if (!searchWord) return completionItem.filterText; | ||
|
||
const containedInFilterText = completionItem.filterText.toLowerCase().includes(searchWord.toLowerCase()); | ||
const shouldPrependSearchWord = completionItem.data.forcePrecedence && containedInFilterText; | ||
|
||
return shouldPrependSearchWord ? `${searchWord}${completionItem.filterText}` : completionItem.filterText; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,31 @@ | ||
import k2 = Kusto.Language.Editor; | ||
|
||
export const createSortingText = (priority: number) => { | ||
return priority.toString().padStart(10, '0'); | ||
}; | ||
|
||
export function sortByMatchTextKeepingKindOrder(items: k2.CompletionItem[]) { | ||
const groupedByKind = groupContiguousByKind(items); | ||
const sortedGroupedItems = sortGroupedItems(groupedByKind); | ||
return sortedGroupedItems.flat(); | ||
} | ||
|
||
function sortGroupedItems(groupedItems: k2.CompletionItem[][]) { | ||
return groupedItems.map((group) => group.sort((i1, i2) => i1.MatchText.localeCompare(i2.MatchText))); | ||
} | ||
|
||
function groupContiguousByKind(items: k2.CompletionItem[]) { | ||
return items.reduce((result: k2.CompletionItem[][], item: k2.CompletionItem) => { | ||
const lastGroup = last(result); | ||
|
||
const shouldCreateNewGroup = !lastGroup || last(lastGroup)?.Kind !== item.Kind; | ||
if (shouldCreateNewGroup) result.push([]); | ||
last(result).push(item); | ||
|
||
return result; | ||
}, []); | ||
} | ||
|
||
function last<T>(array: T[]): T | undefined { | ||
return array.length > 0 ? array[array.length - 1] : undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.