Skip to content

Commit

Permalink
show types of context in @-mention menu (#4188)
Browse files Browse the repository at this point in the history
  • Loading branch information
sqs committed May 17, 2024
1 parent 4def42e commit 7ab723d
Show file tree
Hide file tree
Showing 60 changed files with 1,745 additions and 697 deletions.
2 changes: 1 addition & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Tests:
- [ ] Enable "Enhanced Context" and verify that Cody used code files as context.
- [ ] At the top of the chat transcript, use the arrow dropdown to display the code that Cody used as context.
- [ ] From the list of files that Cody used as context, select one of the `@` files and verify that the correct file opens in a new tab at the correct line number.
- [ ] In the chat input, verify that typing `@` suggests files to add as context, and typing `@#` suggests symbols to add as context.
- [ ] In the chat input, verify that typing `@` suggests files to add as context, and selecting Symbols and then typing a symbol name suggests symbols to add as context.
- [ ] Verify that you can use a relative file path to choose a file to add as context (e.g., `src/util/my-file`).

### Enterprise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.sourcegraph.cody.protocol_generated

data class ConfigParams(
val experimentalGuardrails: Boolean,
val experimentalNoodle: Boolean,
val experimentalURLContext: Boolean,
val serverEndpoint: String,
val uiKindIsWeb: Boolean,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ object Constants {
const val abort = "abort"
const val `web-sign-in-token` = "web-sign-in-token"
const val getUserContext = "getUserContext"
const val queryContextItems = "queryContextItems"
const val `show-search-result` = "show-search-result"
const val reset = "reset"
const val `attribution-search` = "attribution-search"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.protocol_generated

typealias ContextMentionProviderID = String // One of:

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.protocol_generated

data class MentionQuery(
val provider: ContextMentionProviderID? = null,
val text: String,
val range: RangeData? = null,
val maybeHasRangeSuffix: Boolean? = null,
)

Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ sealed class WebviewMessage {
"abort" -> context.deserialize<AbortWebviewMessage>(element, AbortWebviewMessage::class.java)
"simplified-onboarding" -> context.deserialize<`simplified-onboardingWebviewMessage`>(element, `simplified-onboardingWebviewMessage`::class.java)
"getUserContext" -> context.deserialize<GetUserContextWebviewMessage>(element, GetUserContextWebviewMessage::class.java)
"queryContextItems" -> context.deserialize<QueryContextItemsWebviewMessage>(element, QueryContextItemsWebviewMessage::class.java)
"search" -> context.deserialize<SearchWebviewMessage>(element, SearchWebviewMessage::class.java)
"show-search-result" -> context.deserialize<`show-search-resultWebviewMessage`>(element, `show-search-resultWebviewMessage`::class.java)
"reset" -> context.deserialize<ResetWebviewMessage>(element, ResetWebviewMessage::class.java)
Expand Down Expand Up @@ -353,6 +354,16 @@ data class GetUserContextWebviewMessage(
}
}

data class QueryContextItemsWebviewMessage(
val command: CommandEnum, // Oneof: queryContextItems
val query: MentionQuery,
) : WebviewMessage() {

enum class CommandEnum {
@SerializedName("queryContextItems") QueryContextItems,
}
}

data class SearchWebviewMessage(
val command: CommandEnum, // Oneof: search
val query: String,
Expand Down
2 changes: 2 additions & 0 deletions lib/shared/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export interface Configuration {
experimentalSupercompletions: boolean
experimentalGithubAccessToken: string
experimentalCommitMessage: boolean
experimentalNoodle: boolean
experimentalURLContext: boolean

/**
* Unstable Features for internal testing only
Expand Down
6 changes: 6 additions & 0 deletions lib/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ export {
CONTEXT_MENTION_PROVIDERS,
type ContextMentionProvider,
type ContextItemProps,
allMentionProvidersMetadata,
FILE_CONTEXT_MENTION_PROVIDER,
SYMBOL_CONTEXT_MENTION_PROVIDER,
type ContextMentionProviderMetadata,
} from './mentions/api'
export { TokenCounter } from './token/counter'
export { ENHANCED_CONTEXT_ALLOCATION } from './token/constants'
Expand All @@ -290,6 +294,8 @@ export * from './token/constants'
export * from './configuration'
export * from './mentions/providers/packageMentions'
export * from './mentions/providers/sourcegraphSearch'
export { GITHUB_CONTEXT_MENTION_PROVIDER } from './mentions/providers/githubMentions'
export { URL_CONTEXT_MENTION_PROVIDER } from './mentions/providers/urlMentions'
export * from './githubClient'
export {
setOpenCtxExtensionAPI,
Expand Down
55 changes: 51 additions & 4 deletions lib/shared/src/mentions/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ContextItem, ContextItemWithContent } from '../codebase-context/messages'
import type { Configuration } from '../configuration'
import type { PromptString } from '../prompt/prompt-string'
import { GITHUB_CONTEXT_MENTION_PROVIDER } from './providers/githubMentions'
import { OPENCTX_CONTEXT_MENTION_PROVIDER } from './providers/openctxMentions'
Expand Down Expand Up @@ -35,16 +36,26 @@ export interface ContextMentionProvider<ID extends ContextMentionProviderID = Co
id: ID

/**
* Prefix strings for the user input after the `@` that trigger this provider. For example, a
* context mention provider with prefix `npm:` would be triggered when the user types `@npm:`.
* A short, human-readable display title for the provider, such as "Google Docs". If not given,
* `id` is used instead.
*/
triggerPrefixes: string[]
title?: string

/**
* Human-readable display string for when the user is querying items from this provider.
*/
queryLabel?: string

/**
* Human-readable display string for when the provider has no items for the query.
*/
emptyLabel?: string

/**
* Get a list of possible context items to show (in a completion menu) when the user triggers
* this provider while typing `@` in a chat message.
*
* {@link query} omits the `@` but includes the trigger prefix from {@link triggerPrefixes}.
* {@link query} omits the `@`.
*/
queryContextItems(
query: string,
Expand Down Expand Up @@ -77,3 +88,39 @@ export type ContextItemFromProvider<ID extends ContextMentionProviderID> = Conte
*/
provider: ID
}

/**
* Metadata about a {@link ContextMentionProvider}.
*/
export interface ContextMentionProviderMetadata<
ID extends ContextMentionProviderID = ContextMentionProviderID,
> extends Pick<ContextMentionProvider<ID>, 'id' | 'title' | 'queryLabel' | 'emptyLabel'> {}

export const FILE_CONTEXT_MENTION_PROVIDER: ContextMentionProviderMetadata<'file'> = {
id: 'file',
title: 'Files',
queryLabel: 'Search for a file to include...',
emptyLabel: 'No files found',
}

export const SYMBOL_CONTEXT_MENTION_PROVIDER: ContextMentionProviderMetadata<'symbol'> = {
id: 'symbol',
title: 'Symbols',
queryLabel: 'Search for a symbol to include...',
emptyLabel: 'No symbols found',
}

/** Metadata for all registered {@link ContextMentionProvider}s. */
export function allMentionProvidersMetadata(
config: Pick<Configuration, 'experimentalNoodle' | 'experimentalURLContext'>
): ContextMentionProviderMetadata[] {
return [
FILE_CONTEXT_MENTION_PROVIDER,
SYMBOL_CONTEXT_MENTION_PROVIDER,
...CONTEXT_MENTION_PROVIDERS.filter(
({ id }) =>
config.experimentalNoodle ||
(id === URL_CONTEXT_MENTION_PROVIDER.id && config.experimentalURLContext)
),
]
}
20 changes: 9 additions & 11 deletions lib/shared/src/mentions/providers/githubMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@ import { XMLBuilder } from 'fast-xml-parser'
import { URI } from 'vscode-uri'
import { ContextItemSource, type ContextItemWithContent } from '../../codebase-context/messages'
import { githubClient } from '../../githubClient'
import type {
ContextItemFromProvider,
ContextItemProps,
ContextMentionProvider,
ContextMentionProviderID,
} from '../api'
import type { ContextItemFromProvider, ContextItemProps, ContextMentionProvider } from '../api'

const GithubContextId: ContextMentionProviderID = 'github'
const GithubContextId = 'github' as const

const xmlBuilder = new XMLBuilder({
format: true,
})

class GithubContextMentionProvider implements ContextMentionProvider<typeof GithubContextId> {
public id = GithubContextId
public triggerPrefixes = ['github:', 'gh:']
public title = 'GitHub Issues & PRs'

public queryLabel = 'Search for a GitHub issue or PR...'
public emptyLabel = 'No issues or PRs found'

async queryContextItems(query: string, props: ContextItemProps, signal?: AbortSignal) {
/* supported query formats:
Expand Down Expand Up @@ -102,7 +100,7 @@ class GithubContextMentionProvider implements ContextMentionProvider<typeof Gith
uri: URI.parse(pullRequest.html_url),
title: `#${pullRequest.number} ${pullRequest.title}`,
source: ContextItemSource.Github,
provider: 'github',
provider: GITHUB_CONTEXT_MENTION_PROVIDER.id,
}))
} catch (error) {
return []
Expand Down Expand Up @@ -136,7 +134,7 @@ class GithubContextMentionProvider implements ContextMentionProvider<typeof Gith
uri: URI.parse(issue.html_url),
title: `#${issue.number} ${issue.title}`,
source: ContextItemSource.Github,
provider: 'github',
provider: GITHUB_CONTEXT_MENTION_PROVIDER.id,
}))
} catch {
return []
Expand Down Expand Up @@ -297,7 +295,7 @@ class GithubContextMentionProvider implements ContextMentionProvider<typeof Gith
uri: URI.parse(issue.html_url),
title: issue.title,
source: ContextItemSource.Github,
provider: 'github',
provider: GITHUB_CONTEXT_MENTION_PROVIDER.id,
},
]
} catch {
Expand Down
6 changes: 2 additions & 4 deletions lib/shared/src/mentions/providers/openctxMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import { isDefined } from '../../common'
import { getOpenCtxExtensionAPI } from '../../context/openctx/api'
import type { ContextItemFromProvider, ContextMentionProvider } from '../api'

const TRIGGER_PREFIX = 'octx:'

export const OPENCTX_CONTEXT_MENTION_PROVIDER: ContextMentionProvider<'openctx'> = {
id: 'openctx',
triggerPrefixes: [TRIGGER_PREFIX],
title: 'OpenCtx',
async queryContextItems(query) {
const openctxAPI = await getOpenCtxExtensionAPI()
if (!openctxAPI) {
return []
}
const results = await openctxAPI.getItems({ query: query.slice(TRIGGER_PREFIX.length) })
const results = await openctxAPI.getItems({ query: query })
const items =
results
?.map((result, i) =>
Expand Down
6 changes: 4 additions & 2 deletions lib/shared/src/mentions/providers/packageMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ function toPackageKind(value: PackageEcosystem): PackageKind {
const MAX_PAKCAGE_LIST_CANDIDATES = 10
export const PACKAGE_CONTEXT_MENTION_PROVIDER: ContextMentionProvider<'package'> = {
id: 'package',
triggerPrefixes: Object.values(PackageEcosystem).map(prefix => prefix + ':'),
title: 'NPM Packages',
queryLabel: 'Search for a package (npm:, go:, etc)...',
emptyLabel: 'No packages found',

async queryContextItems(query, _, signal) {
const [ecosystemName = '', name = ''] = query.split(':')
Expand Down Expand Up @@ -73,7 +75,7 @@ export const PACKAGE_CONTEXT_MENTION_PROVIDER: ContextMentionProvider<'package'>
content: undefined,
source: ContextItemSource.Package,
repoID: node.repository.id,
provider: 'package',
provider: PACKAGE_CONTEXT_MENTION_PROVIDER.id,
name: node.name,
ecosystem,
} as ContextItemPackage)
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/src/mentions/providers/sourcegraphSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { ContextMentionProvider } from '../api'

export const SOURCEGRAPH_SEARCH_CONTEXT_MENTION_PROVIDER: ContextMentionProvider<'src-search'> = {
id: 'src-search',
triggerPrefixes: ['src:', '?'],
title: 'Code Search',

async queryContextItems(query, _, signal) {
const searchQuery = query.startsWith('?')
Expand Down
5 changes: 3 additions & 2 deletions lib/shared/src/mentions/providers/urlMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import type { ContextMentionProvider } from '../api'

export const URL_CONTEXT_MENTION_PROVIDER: ContextMentionProvider<'url'> = {
id: 'url',
triggerPrefixes: ['http://', 'https://'],
title: 'Web URL',
queryLabel: 'Type a URL to add a web page as context',

/**
* Given a possibly incomplete URL from user input (that the user may be typing), return context
Expand All @@ -28,7 +29,7 @@ export const URL_CONTEXT_MENTION_PROVIDER: ContextMentionProvider<'url'> = {
content,
title: tryGetHTMLDocumentTitle(content),
source: ContextItemSource.Uri,
provider: 'url',
provider: URL_CONTEXT_MENTION_PROVIDER.id,
},
]
} catch (error) {
Expand Down
Loading

0 comments on commit 7ab723d

Please sign in to comment.