Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 16f7b8a

Browse files
authored
do not require full editor reload after setting access token (#61)
Also makes the extension react to other configuration changes immediately.
1 parent d2a66af commit 16f7b8a

File tree

4 files changed

+254
-96
lines changed

4 files changed

+254
-96
lines changed

vscode-codegen/package.json

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
{
3838
"id": "cody",
3939
"title": "Cody",
40-
"icon": "resources/cody.svg"
40+
"icon": "resources/cody.svg",
41+
"when": "sourcegraph.cody.activated"
4142
}
4243
]
4344
},
@@ -46,53 +47,59 @@
4647
{
4748
"type": "webview",
4849
"id": "cody.chat",
49-
"name": "Chat"
50+
"name": "Chat",
51+
"when": "sourcegraph.cody.activated"
5052
}
5153
]
5254
},
5355
"commands": [
56+
{
57+
"command": "sourcegraph.cody.toggleEnabled",
58+
"title": "Cody: Toggle Enabled/Disabled"
59+
},
5460
{
5561
"command": "cody.experimental.suggest",
56-
"title": "Ask Cody: View suggestions"
62+
"title": "Ask Cody: View Suggestions"
5763
},
5864
{
5965
"command": "cody.recipe.explain-code",
60-
"title": "Ask Cody: Explain code in detail"
66+
"title": "Ask Cody: Explain Code in Detail"
6167
},
6268
{
6369
"command": "cody.recipe.explain-code-high-level",
64-
"title": "Ask Cody: Explain code at a high level"
70+
"title": "Ask Cody: Explain Code at a High Level"
6571
},
6672
{
6773
"command": "cody.recipe.generate-unit-test",
68-
"title": "Ask Cody: Generate unit test"
74+
"title": "Ask Cody: Generate Unit Test"
6975
},
7076
{
7177
"command": "cody.recipe.generate-docstring",
72-
"title": "Ask Cody: Generate docstring"
78+
"title": "Ask Cody: Generate Docstring"
7379
},
7480
{
7581
"command": "cody.recipe.translate-to-language",
76-
"title": "Ask Cody: Translate to language"
82+
"title": "Ask Cody: Translate to Language"
7783
},
7884
{
7985
"command": "cody.recipe.git-history",
80-
"title": "Cody: Recent history"
86+
"title": "Cody: Recent History"
8187
},
8288
{
8389
"command": "cody.set-access-token",
84-
"title": "Cody: Set access token"
90+
"title": "Cody: Set Access Token"
8591
},
8692
{
8793
"command": "cody.delete-access-token",
88-
"title": "Cody: Delete access token"
94+
"title": "Cody: Delete Access Token"
8995
}
9096
],
9197
"keybindings": [
9298
{
9399
"command": "cody.recipe.explain-code-high-level",
94100
"key": "ctrl+alt+c",
95-
"mac": "ctrl+alt+c"
101+
"mac": "ctrl+alt+c",
102+
"when": "sourcegraph.cody.activated"
96103
}
97104
],
98105
"submenus": [
@@ -102,10 +109,50 @@
102109
}
103110
],
104111
"menus": {
112+
"commandPalette": [
113+
{
114+
"command": "sourcegraph.cody.toggleEnabled"
115+
},
116+
{
117+
"command": "cody.experimental.suggest",
118+
"when": "config.cody.experimental.suggest && sourcegraph.cody.activated"
119+
},
120+
{
121+
"command": "cody.recipe.explain-code",
122+
"when": "sourcegraph.cody.activated"
123+
},
124+
{
125+
"command": "cody.recipe.explain-code-high-level",
126+
"when": "sourcegraph.cody.activated"
127+
},
128+
{
129+
"command": "cody.recipe.generate-unit-test",
130+
"when": "sourcegraph.cody.activated"
131+
},
132+
{
133+
"command": "cody.recipe.generate-docstring",
134+
"when": "sourcegraph.cody.activated"
135+
},
136+
{
137+
"command": "cody.recipe.translate-to-language",
138+
"when": "sourcegraph.cody.activated"
139+
},
140+
{
141+
"command": "cody.recipe.git-history",
142+
"when": "sourcegraph.cody.activated"
143+
},
144+
{
145+
"command": "cody.set-access-token"
146+
},
147+
{
148+
"command": "cody.delete-access-token"
149+
}
150+
],
105151
"editor/context": [
106152
{
107153
"submenu": "cody.submenu",
108-
"group": "7_modification"
154+
"group": "7_modification",
155+
"when": "sourcegraph.cody.activated"
109156
}
110157
],
111158
"cody.submenu": [
@@ -130,6 +177,11 @@
130177
"type": "object",
131178
"title": "Cody settings",
132179
"properties": {
180+
"sourcegraph.cody.enable": {
181+
"type": "boolean",
182+
"default": true,
183+
"description": "Enable Cody"
184+
},
133185
"cody.serverEndpoint": {
134186
"type": "string"
135187
},
@@ -150,6 +202,10 @@
150202
"none"
151203
],
152204
"default": "embeddings"
205+
},
206+
"cody.experimental.suggest": {
207+
"type": "boolean",
208+
"default": false
153209
}
154210
}
155211
}

vscode-codegen/src/configuration.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as vscode from 'vscode'
2+
3+
const CODY_ENDPOINT = 'cody.sgdev.org'
4+
5+
export type ConfigurationUseContext = 'embeddings' | 'keyword' | 'none'
6+
7+
export interface Configuration {
8+
enable: boolean
9+
serverEndpoint: string
10+
embeddingsEndpoint: string
11+
codebase?: string
12+
debug: boolean
13+
useContext: ConfigurationUseContext
14+
experimentalSuggest: boolean
15+
}
16+
17+
export function getConfiguration(config: vscode.WorkspaceConfiguration): Configuration {
18+
return {
19+
enable: config.get('sourcegraph.cody.enable', true),
20+
serverEndpoint: config.get('cody.serverEndpoint') || CODY_ENDPOINT,
21+
embeddingsEndpoint: config.get('cody.embeddingsEndpoint') || CODY_ENDPOINT,
22+
codebase: config.get('cody.codebase'),
23+
debug: config.get('cody.debug', false),
24+
useContext: config.get<ConfigurationUseContext>('cody.useContext') || 'embeddings',
25+
experimentalSuggest: config.get('cody.experimental.suggest', false),
26+
}
27+
}

vscode-codegen/src/extension.ts

Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,65 +3,129 @@ import * as vscode from 'vscode'
33
import { ChatViewProvider } from './chat/view'
44
import { WSChatClient } from './chat/ws'
55
import { WSCompletionsClient, fetchAndShowCompletions } from './completions'
6+
import { Configuration, ConfigurationUseContext, getConfiguration } from './configuration'
67
import { CompletionsDocumentProvider } from './docprovider'
78
import { EmbeddingsClient } from './embeddings-client'
89
import { History } from './history'
910

10-
const CODY_ENDPOINT = 'cody.sgdev.org'
1111
const CODY_ACCESS_TOKEN_SECRET = 'cody.access-token'
1212

1313
export async function activate(context: vscode.ExtensionContext): Promise<void> {
1414
console.log('Cody extension activated')
15-
const isDevelopment = process.env.NODE_ENV === 'development'
1615

17-
const settings = vscode.workspace.getConfiguration()
18-
const documentProvider = new CompletionsDocumentProvider()
19-
const history = new History()
20-
history.register(context)
16+
context.subscriptions.push(
17+
vscode.commands.registerCommand('sourcegraph.cody.toggleEnabled', async () => {
18+
const config = vscode.workspace.getConfiguration()
19+
await config.update(
20+
'sourcegraph.cody.enable',
21+
!config.get('sourcegraph.cody.enable'),
22+
vscode.ConfigurationTarget.Global
23+
)
24+
}),
25+
vscode.commands.registerCommand('cody.set-access-token', async () => {
26+
const tokenInput = await vscode.window.showInputBox()
27+
if (tokenInput === undefined || tokenInput === '') {
28+
return
29+
}
30+
await context.secrets.store(CODY_ACCESS_TOKEN_SECRET, tokenInput)
31+
}),
32+
vscode.commands.registerCommand('cody.delete-access-token', async () =>
33+
context.secrets.delete(CODY_ACCESS_TOKEN_SECRET)
34+
)
35+
)
2136

22-
const serverAddr = settings.get('cody.serverEndpoint') || CODY_ENDPOINT
23-
const wsUrl = `${isDevelopment ? 'ws' : 'wss'}://${serverAddr}`
24-
const httpUrl = `${isDevelopment ? 'http' : 'https'}://${serverAddr}`
37+
let disposable: vscode.Disposable | undefined
38+
context.subscriptions.push({
39+
dispose: () => disposable?.dispose(),
40+
})
41+
const doConfigure = async (): Promise<void> => {
42+
disposable?.dispose()
43+
const config = getConfiguration(vscode.workspace.getConfiguration())
44+
const accessToken = (await context.secrets.get(CODY_ACCESS_TOKEN_SECRET)) ?? null
45+
disposable = configure(context, config, accessToken)
46+
}
2547

26-
const embeddingsAddr = settings.get('cody.embeddingsEndpoint') || CODY_ENDPOINT
27-
const embeddingsUrl = `${isDevelopment ? 'http' : 'https'}://${embeddingsAddr}`
48+
// Watch all relevant configuration and secrets for changes.
49+
context.subscriptions.push(
50+
vscode.workspace.onDidChangeConfiguration(async event => {
51+
if (event.affectsConfiguration('cody') || event.affectsConfiguration('sourcegraph')) {
52+
await doConfigure()
53+
}
54+
})
55+
)
56+
context.subscriptions.push(
57+
context.secrets.onDidChange(async event => {
58+
if (event.key === CODY_ACCESS_TOKEN_SECRET) {
59+
await doConfigure()
60+
}
61+
})
62+
)
2863

29-
const codebaseId: string = settings.get('cody.codebase', '')
64+
await doConfigure()
65+
}
3066

31-
const accessToken = (await context.secrets.get('cody.access-token')) ?? ''
32-
if (!accessToken) {
33-
vscode.window.showWarningMessage(
34-
'Cody needs an access token to work. Please set the token using the "Cody: Set access token" command and reload the editor.'
35-
)
67+
function configure(
68+
context: Pick<vscode.ExtensionContext, 'extensionPath' | 'secrets'>,
69+
config: Configuration,
70+
accessToken: string | null
71+
): vscode.Disposable {
72+
if (!config.enable || !accessToken) {
73+
if (config.enable) {
74+
const SET_ACCESS_TOKEN_ITEM = 'Set Access Token' as const
75+
vscode.window
76+
.showWarningMessage('Cody requires an access token.', SET_ACCESS_TOKEN_ITEM)
77+
.then(async item => {
78+
if (item === SET_ACCESS_TOKEN_ITEM) {
79+
await vscode.commands.executeCommand('cody.set-access-token')
80+
}
81+
}, undefined)
82+
}
83+
setContextActivated(false)
84+
return NOOP_DISPOSABLE
3685
}
3786

87+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
88+
const subscriptions: { dispose(): any }[] = []
89+
90+
const isDevelopment = process.env.NODE_ENV === 'development'
91+
92+
const documentProvider = new CompletionsDocumentProvider()
93+
const history = new History()
94+
subscriptions.push(history)
95+
96+
const wsUrl = `${isDevelopment ? 'ws' : 'wss'}://${config.serverEndpoint}`
97+
const httpUrl = `${isDevelopment ? 'http' : 'https'}://${config.serverEndpoint}`
98+
99+
const embeddingsUrl = `${isDevelopment ? 'http' : 'https'}://${config.embeddingsEndpoint}`
100+
38101
const wsCompletionsClient = WSCompletionsClient.new(`${wsUrl}/completions`, accessToken)
39102
const wsChatClient = WSChatClient.new(`${wsUrl}/chat`, accessToken)
40-
const embeddingsClient = codebaseId ? new EmbeddingsClient(embeddingsUrl, accessToken, codebaseId) : null
103+
const embeddingsClient = config.codebase ? new EmbeddingsClient(embeddingsUrl, accessToken, config.codebase) : null
41104

42-
let contextType: 'embeddings' | 'keyword' | 'none' = settings.get('cody.useContext') || 'embeddings'
43-
if (!embeddingsClient && contextType === 'embeddings') {
105+
let useContext: ConfigurationUseContext = config.useContext
106+
if (!embeddingsClient && config.useContext === 'embeddings') {
107+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
44108
vscode.window.showInformationMessage(
45109
'Embeddings were not available (is `cody.codebase` set?), falling back to keyword context'
46110
)
47-
contextType = 'keyword'
111+
useContext = 'keyword'
48112
}
49113

50114
const chatProvider = new ChatViewProvider(
51115
context.extensionPath,
52116
httpUrl,
53117
wsChatClient,
54118
embeddingsClient,
55-
contextType,
56-
settings.get('cody.debug') || false
119+
useContext,
120+
config.debug
57121
)
58122

59-
const executeRecipe = async (recipe: string) => {
123+
const executeRecipe = async (recipe: string): Promise<void> => {
60124
await vscode.commands.executeCommand('cody.chat.focus')
61-
return chatProvider.executeRecipe(recipe)
125+
await chatProvider.executeRecipe(recipe)
62126
}
63127

64-
context.subscriptions.push(
128+
subscriptions.push(
65129
vscode.workspace.registerTextDocumentContentProvider('codegen', documentProvider),
66130
vscode.languages.registerHoverProvider({ scheme: 'codegen' }, documentProvider),
67131

@@ -78,30 +142,30 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
78142
vscode.commands.registerCommand('cody.recipe.translate-to-language', async () =>
79143
executeRecipe('translateToLanguage')
80144
),
81-
vscode.commands.registerCommand('cody.recipe.git-history', async => executeRecipe('gitHistory')),
82-
83-
vscode.window.registerWebviewViewProvider('cody.chat', chatProvider),
145+
vscode.commands.registerCommand('cody.recipe.git-history', () => executeRecipe('gitHistory')),
84146

85-
vscode.commands.registerCommand('cody.set-access-token', async () => {
86-
const tokenInput = await vscode.window.showInputBox()
87-
if (tokenInput === undefined) {
88-
return
89-
}
90-
return context.secrets.store(CODY_ACCESS_TOKEN_SECRET, tokenInput)
91-
}),
92-
93-
vscode.commands.registerCommand('cody.delete-access-token', async () =>
94-
context.secrets.delete(CODY_ACCESS_TOKEN_SECRET)
95-
)
147+
vscode.window.registerWebviewViewProvider('cody.chat', chatProvider)
96148
)
97149

98-
if (settings.get('cody.experimental.suggest')) {
99-
context.subscriptions.push(
150+
if (config.experimentalSuggest) {
151+
subscriptions.push(
100152
vscode.commands.registerCommand('cody.experimental.suggest', async () => {
101153
await fetchAndShowCompletions(wsCompletionsClient, documentProvider, history)
102154
})
103155
)
104156
}
157+
158+
setContextActivated(true)
159+
160+
return vscode.Disposable.from(...subscriptions)
105161
}
106162

107-
export function deactivate() {}
163+
const NOOP_DISPOSABLE: vscode.Disposable = {
164+
dispose: () => {
165+
/* noop */
166+
},
167+
}
168+
169+
function setContextActivated(activated: boolean): void {
170+
vscode.commands.executeCommand('setContext', 'sourcegraph.cody.activated', activated).then(undefined, undefined)
171+
}

0 commit comments

Comments
 (0)