Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzy search for @-ing files and symbols #1889

Merged
merged 40 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2b401b7
Fuzzy matches
toolmantim Nov 23, 2023
ab3d2b1
Fix symbol search
toolmantim Nov 23, 2023
9bb616e
UI cleanups
toolmantim Nov 23, 2023
b56d630
Style & format
toolmantim Nov 24, 2023
3e89638
Fix flash of empty results due to cancellation
toolmantim Nov 24, 2023
5e7446c
Code
toolmantim Nov 24, 2023
4966b4f
Story
toolmantim Nov 26, 2023
eb3869d
Storybook
toolmantim Nov 26, 2023
272dc9e
Storybook
toolmantim Nov 26, 2023
160504c
Debounce
toolmantim Nov 26, 2023
d51deaa
Merge branch 'main' into tl/fuzzy-search-results
toolmantim Nov 26, 2023
36474b8
Remove the config getting for now
toolmantim Nov 26, 2023
a01b534
Tests
toolmantim Nov 26, 2023
04baac4
WIP
toolmantim Nov 26, 2023
f66cd2f
Tests and formatting
toolmantim Nov 27, 2023
b3eedff
Remove comment
toolmantim Nov 27, 2023
9387803
Formatting
toolmantim Nov 27, 2023
614800a
Labels and positions
toolmantim Nov 27, 2023
4540752
Lint
toolmantim Nov 27, 2023
5760dc2
Fix height
toolmantim Nov 27, 2023
eae604c
Fix text overflow
toolmantim Nov 27, 2023
3fffd23
Fix cancellation and sync chat providers
toolmantim Nov 27, 2023
cb38d37
Overflow styles
toolmantim Nov 27, 2023
83eece4
Dedupe
toolmantim Nov 27, 2023
e39eff8
Lint
toolmantim Nov 27, 2023
d7e55c5
Changelog
toolmantim Nov 27, 2023
03319a8
Merge branch 'main' into tl/fuzzy-search-results
toolmantim Nov 27, 2023
2a5c531
Copy to SimpleChatPanelProvider
toolmantim Nov 27, 2023
b43d8f4
Avoid OS paths
toolmantim Nov 27, 2023
c16ece6
No need to test transcript click here
toolmantim Nov 27, 2023
b039f73
Lint
toolmantim Nov 27, 2023
c0d5209
Remove debug
toolmantim Nov 27, 2023
93bb8a3
Revert pnpm
toolmantim Nov 27, 2023
abf04cc
Revert playwright update
toolmantim Nov 27, 2023
f611a3b
Remove unused editorTab
toolmantim Nov 27, 2023
d95ec52
Lint
toolmantim Nov 27, 2023
645c306
Remove arg
toolmantim Nov 27, 2023
c360a3f
Sort after fuzzy returns results
toolmantim Nov 28, 2023
61b34d5
Use fsPath
toolmantim Nov 28, 2023
3b8c12d
Inline collator for this few results
toolmantim Nov 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/ui/src/Chat.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}

.text-area-container {
position: relative;
width: 100%;
display: flex;
align-items: flex-end;
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/src/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ interface ChatProps extends ChatClassNames {
filterChatCommands?: (chatCommands: [string, CodyPrompt][], input: string) => [string, CodyPrompt][]
ChatCommandsComponent?: React.FunctionComponent<ChatCommandsProps>
isTranscriptError?: boolean
contextSelection?: ContextFile[]
contextSelection?: ContextFile[] | null
UserContextSelectorComponent?: React.FunctionComponent<UserContextSelectorProps>
chatModels?: ChatModelSelection[]
EnhancedContextToggler?: React.FunctionComponent<{
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Starting from `0.2.0`, Cody is using `major.EVEN_NUMBER.patch` for release versi

- Chat: New chat preview models `claude-2.1` is now avaliable for sourcegraph.com users. [pull/1860](https://github.com/sourcegraph/cody/pull/1860)
- Edit: Added context-aware code actions for "Generate", "Edit" and "Document" commands. [pull/1724](https://github.com/sourcegraph/cody/pull/1724)
- Chat: @'ing files now uses a case insensitive fuzzy search. [pull/1889](https://github.com/sourcegraph/cody/pull/1889)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,7 @@
"dedent": "^0.7.0",
"envalid": "^7.3.1",
"express": "^4.18.2",
"fuzzysort": "^2.0.4",
"mocha": "^10.2.0",
"ovsx": "^0.8.2",
"path-browserify": "^1.0.1",
Expand Down
62 changes: 41 additions & 21 deletions vscode/src/chat/chat-view/ChatPanelProvider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { debounce } from 'lodash'
import * as vscode from 'vscode'

import { ContextFile } from '@sourcegraph/cody-shared'
Expand All @@ -7,7 +6,7 @@ import { ChatMessage, UserLocalHistory } from '@sourcegraph/cody-shared/src/chat
import { ChatSubmitType } from '@sourcegraph/cody-ui/src/Chat'

import { View } from '../../../webviews/NavBar'
import { getFileContextFile, getOpenTabsContextFile, getSymbolContextFile } from '../../editor/utils/editor-context'
import { getFileContextFiles, getOpenTabsContextFile, getSymbolContextFiles } from '../../editor/utils/editor-context'
import { logDebug } from '../../log'
import { telemetryService } from '../../services/telemetry'
import { telemetryRecorder } from '../../services/telemetry-v2'
Expand Down Expand Up @@ -41,6 +40,7 @@ export interface ChatPanelProviderOptions extends MessageProviderOptions {

export class ChatPanelProvider extends MessageProvider {
private extensionUri: vscode.Uri
private contextFilesQueryCancellation?: vscode.CancellationTokenSource
public webview?: ChatViewProviderWebview
public webviewPanel: vscode.WebviewPanel | undefined = undefined
public treeView: TreeViewProvider
Expand Down Expand Up @@ -286,26 +286,46 @@ export class ChatPanelProvider extends MessageProvider {
return
}

const debouncedContextFileQuery = debounce(async (query: string): Promise<void> => {
try {
const MAX_RESULTS = 10
const fileResultsPromise = getFileContextFile(query, MAX_RESULTS)
const symbolResultsPromise = getSymbolContextFile(query, MAX_RESULTS)

const [fileResults, symbolResults] = await Promise.all([fileResultsPromise, symbolResultsPromise])
const context = [...new Set([...fileResults, ...symbolResults])]

await this.webview?.postMessage({
type: 'userContextFiles',
context,
})
} catch (error) {
// Handle or log the error as appropriate
console.error('Error retrieving context files:', error)
const cancellation = new vscode.CancellationTokenSource()

try {
const MAX_RESULTS = 20
if (query.startsWith('#')) {
// It would be nice if the VS Code symbols API supports
// cancellation, but it doesn't
const symbolResults = await getSymbolContextFiles(query.slice(1), MAX_RESULTS)
// Check if cancellation was requested while getFileContextFiles
// was executing, which means a new request has already begun
// (i.e. prevent race conditions where slow old requests get
// processed after later faster requests)
if (!cancellation.token.isCancellationRequested) {
await this.webview?.postMessage({
type: 'userContextFiles',
context: symbolResults,
})
}
} else {
const fileResults = await getFileContextFiles(query, MAX_RESULTS, cancellation.token)
// Check if cancellation was requested while getFileContextFiles
// was executing, which means a new request has already begun
// (i.e. prevent race conditions where slow old requests get
// processed after later faster requests)
if (!cancellation.token.isCancellationRequested) {
await this.webview?.postMessage({
type: 'userContextFiles',
context: fileResults,
})
}
}
}, 100)

await debouncedContextFileQuery(query)
} catch (error) {
// Handle or log the error as appropriate
console.error('Error retrieving context files:', error)
} finally {
// Cancel any previous search request after we update the UI
// to avoid a flash of empty results as you type
this.contextFilesQueryCancellation?.cancel()
this.contextFilesQueryCancellation = cancellation
}
}

/**
Expand Down
63 changes: 42 additions & 21 deletions vscode/src/chat/chat-view/SidebarChatProvider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { debounce } from 'lodash'
import * as vscode from 'vscode'

import { ContextFile } from '@sourcegraph/cody-shared'
Expand All @@ -8,7 +7,7 @@ import { DOTCOM_URL } from '@sourcegraph/cody-shared/src/sourcegraph-api/environ
import { ChatSubmitType } from '@sourcegraph/cody-ui/src/Chat'

import { View } from '../../../webviews/NavBar'
import { getFileContextFile, getOpenTabsContextFile, getSymbolContextFile } from '../../editor/utils/editor-context'
import { getFileContextFiles, getOpenTabsContextFile, getSymbolContextFiles } from '../../editor/utils/editor-context'
import { logDebug } from '../../log'
import { AuthProviderSimplified } from '../../services/AuthProviderSimplified'
import { LocalAppWatcher } from '../../services/LocalAppWatcher'
Expand Down Expand Up @@ -42,6 +41,7 @@ export interface SidebarChatOptions extends MessageProviderOptions {

export class SidebarChatProvider extends MessageProvider implements vscode.WebviewViewProvider {
private extensionUri: vscode.Uri
private contextFilesQueryCancellation?: vscode.CancellationTokenSource
public webview?: SidebarChatWebview
public webviewPanel: vscode.WebviewPanel | undefined = undefined

Expand Down Expand Up @@ -306,27 +306,48 @@ export class SidebarChatProvider extends MessageProvider implements vscode.Webvi
return
}

const debouncedContextFileQuery = debounce(async (query: string): Promise<void> => {
try {
const MAX_RESULTS = 10
const fileResultsPromise = getFileContextFile(query, MAX_RESULTS)
const symbolResultsPromise = getSymbolContextFile(query, MAX_RESULTS)

const [fileResults, symbolResults] = await Promise.all([fileResultsPromise, symbolResultsPromise])
const context = [...new Set([...fileResults, ...symbolResults])]

await this.webview?.postMessage({
type: 'userContextFiles',
context,
})
} catch (error) {
// Handle or log the error as appropriate
console.error('Error retrieving context files:', error)
const cancellation = new vscode.CancellationTokenSource()

try {
const MAX_RESULTS = 20
if (query.startsWith('#')) {
// It would be nice if the VS Code symbols API supports
// cancellation, but it doesn't
const symbolResults = await getSymbolContextFiles(query.slice(1), MAX_RESULTS)
// Check if cancellation was requested while getFileContextFiles
// was executing, which means a new request has already begun
// (i.e. prevent race conditions where slow old requests get
// processed after later faster requests)
if (!cancellation.token.isCancellationRequested) {
await this.webview?.postMessage({
type: 'userContextFiles',
context: symbolResults,
})
}
} else {
const fileResults = await getFileContextFiles(query, MAX_RESULTS, cancellation.token)
// Check if cancellation was requested while getFileContextFiles
// was executing, which means a new request has already begun
// (i.e. prevent race conditions where slow old requests get
// processed after later faster requests)
if (!cancellation.token.isCancellationRequested) {
await this.webview?.postMessage({
type: 'userContextFiles',
context: fileResults,
})
}
}
}, 100)

await debouncedContextFileQuery(query)
} catch (error) {
// Handle or log the error as appropriate
console.error('Error retrieving context files:', error)
} finally {
// Cancel any previous search request after we update the UI
// to avoid a flash of empty results as you type
this.contextFilesQueryCancellation?.cancel()
this.contextFilesQueryCancellation = cancellation
}
}

/**
*
* @param notice Triggers displaying a notice.
Expand Down
60 changes: 41 additions & 19 deletions vscode/src/chat/chat-view/SimpleChatPanelProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as path from 'path'

import { debounce } from 'lodash'
import * as uuid from 'uuid'
import * as vscode from 'vscode'

Expand All @@ -21,7 +20,7 @@ import { isError } from '@sourcegraph/cody-shared/src/utils'

import { View } from '../../../webviews/NavBar'
import { getFullConfig } from '../../configuration'
import { getFileContextFile, getOpenTabsContextFile, getSymbolContextFile } from '../../editor/utils/editor-context'
import { getFileContextFiles, getOpenTabsContextFile, getSymbolContextFiles } from '../../editor/utils/editor-context'
import { VSCodeEditor } from '../../editor/vscode-editor'
import { logDebug } from '../../log'
import { AuthProvider } from '../../services/AuthProvider'
Expand Down Expand Up @@ -81,6 +80,8 @@ export class SimpleChatPanelProvider implements vscode.Disposable, IChatPanelPro

private prompter: IPrompter = new DefaultPrompter()

private contextFilesQueryCancellation?: vscode.CancellationTokenSource

// HACK: for now, we need awkwardly need to keep this in sync with chatModel.sessionID,
// as it is necessary to satisfy the IChatPanelProvider interface.
public sessionID: string
Expand Down Expand Up @@ -470,25 +471,46 @@ export class SimpleChatPanelProvider implements vscode.Disposable, IChatPanelPro
return
}

const debouncedContextFileQuery = debounce(async (query: string): Promise<void> => {
try {
const MAX_RESULTS = 10
const fileResultsPromise = getFileContextFile(query, MAX_RESULTS)
const symbolResultsPromise = getSymbolContextFile(query, MAX_RESULTS)

const [fileResults, symbolResults] = await Promise.all([fileResultsPromise, symbolResultsPromise])
const context = [...new Set([...fileResults, ...symbolResults])]
const cancellation = new vscode.CancellationTokenSource()

await this.webview?.postMessage({
type: 'userContextFiles',
context,
})
} catch (error) {
this.postError(`Error retrieving explicit context: ${error}`)
try {
const MAX_RESULTS = 20
if (query.startsWith('#')) {
// It would be nice if the VS Code symbols API supports
// cancellation, but it doesn't
const symbolResults = await getSymbolContextFiles(query.slice(1), MAX_RESULTS)
// Check if cancellation was requested while getFileContextFiles
// was executing, which means a new request has already begun
// (i.e. prevent race conditions where slow old requests get
// processed after later faster requests)
if (!cancellation.token.isCancellationRequested) {
await this.webview?.postMessage({
type: 'userContextFiles',
context: symbolResults,
})
}
} else {
const fileResults = await getFileContextFiles(query, MAX_RESULTS, cancellation.token)
// Check if cancellation was requested while getFileContextFiles
// was executing, which means a new request has already begun
// (i.e. prevent race conditions where slow old requests get
// processed after later faster requests)
if (!cancellation.token.isCancellationRequested) {
await this.webview?.postMessage({
type: 'userContextFiles',
context: fileResults,
})
}
}
}, 100)

await debouncedContextFileQuery(query)
} catch (error) {
// Handle or log the error as appropriate
console.error('Error retrieving context files:', error)
} finally {
// Cancel any previous search request after we update the UI
// to avoid a flash of empty results as you type
this.contextFilesQueryCancellation?.cancel()
this.contextFilesQueryCancellation = cancellation
}
}

private async postViewTranscript(messageInProgress?: ChatMessage): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion vscode/src/chat/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export type ExtensionMessage =
| { type: 'notice'; notice: { key: string } }
| { type: 'custom-prompts'; prompts: [string, CodyPrompt][] }
| { type: 'transcript-errors'; isTranscriptError: boolean }
| { type: 'userContextFiles'; context: ContextFile[]; kind?: ContextFileType }
| { type: 'userContextFiles'; context: ContextFile[] | null; kind?: ContextFileType }
| { type: 'chatModels'; models: ChatModelSelection[] }
| { type: 'update-search-results'; results: SearchPanelFile[]; query: string }
| { type: 'index-updated'; scopeDir: string }
Expand Down
46 changes: 46 additions & 0 deletions vscode/src/editor/utils/editor-context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import * as vscode from 'vscode'

import { getFileContextFiles } from './editor-context'

vi.mock('lodash', () => ({
throttle: vi.fn(fn => fn),
}))

afterEach(() => {
vi.clearAllMocks()
})

describe('getFileContextFiles', () => {
it('fuzzy filters results', async () => {
vscode.workspace.findFiles = vi
.fn()
.mockResolvedValueOnce([
vscode.Uri.parse('foo/bar/baz/file.go'),
vscode.Uri.parse('foo/bar/File/go-has-parts'),
vscode.Uri.parse('foo/bar/baz/FileWontMatch.ts'),
])

expect(
(await getFileContextFiles('filego', 5, new vscode.CancellationTokenSource().token)).map(
uri => uri.path?.basename
)
).toMatchInlineSnapshot(`
[
"go-has-parts",
"file.go",
]
`)

expect(vscode.workspace.findFiles).toBeCalledTimes(1)
})

it('cancels previous requests', async () => {
vscode.workspace.findFiles = vi.fn().mockResolvedValueOnce([])
const cancellation = new vscode.CancellationTokenSource()
await getFileContextFiles('search', 5, cancellation.token)
await getFileContextFiles('search', 5, new vscode.CancellationTokenSource().token)
expect(cancellation.token.isCancellationRequested)
expect(vscode.workspace.findFiles).toBeCalledTimes(2)
})
})
Loading