Skip to content

Commit

Permalink
make ActiveTextEditorViewControllers optional and improve usage of ty…
Browse files Browse the repository at this point in the history
…pes (#372)

Just a code refactor.
  • Loading branch information
sqs committed Jul 25, 2023
1 parent 3712d73 commit 491f030
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 53 deletions.
8 changes: 4 additions & 4 deletions lib/shared/src/chat/recipes/fixup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ export class Fixup implements Recipe {

public async getInteraction(humanChatInput: string, context: RecipeContext): Promise<Interaction | null> {
// TODO: Prompt the user for additional direction.
const selection = context.editor.getActiveTextEditorSelection() || context.editor.controllers?.inline.selection
const selection = context.editor.getActiveTextEditorSelection() || context.editor.controllers?.inline?.selection
if (!selection) {
await context.editor.controllers?.inline.error()
await context.editor.controllers?.inline?.error()
await context.editor.showWarningMessage('Select some code to fixup.')
return null
}
const quarterFileContext = Math.floor(MAX_CURRENT_FILE_TOKENS / 4)
if (truncateText(selection.selectedText, quarterFileContext * 2) !== selection.selectedText) {
const msg = "The amount of text selected exceeds Cody's current capacity."
await context.editor.controllers?.inline.error()
await context.editor.controllers?.inline?.error()
await context.editor.showWarningMessage(msg)
return null
}
Expand All @@ -42,7 +42,7 @@ export class Fixup implements Recipe {
'selection',
new BufferedBotResponseSubscriber(async content => {
if (!content) {
await context.editor.controllers?.inline.error()
await context.editor.controllers?.inline?.error()
await context.editor.showWarningMessage(
'Cody did not suggest any replacement.\nTry starting a new conversation with Cody.'
)
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/src/chat/recipes/inline-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class InlineChat implements Recipe {
return new Fixup().getInteraction(humanChatInput.replace(commandRegex.fix, ''), context)
}

const selection = context.editor.controllers?.inline.selection
const selection = context.editor.controllers?.inline?.selection
if (!humanChatInput || !selection) {
await context.editor.showWarningMessage('Failed to start Inline Chat: empty input or selection.')
return null
Expand Down
8 changes: 4 additions & 4 deletions lib/shared/src/chat/recipes/inline-touch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ export class InlineTouch implements Recipe {
constructor(private debug: (filterLabel: string, text: string, ...args: unknown[]) => void) {}

public async getInteraction(humanChatInput: string, context: RecipeContext): Promise<Interaction | null> {
const selection = context.editor.getActiveTextEditorSelection() || context.editor.controllers?.inline.selection
const selection = context.editor.getActiveTextEditorSelection() || context.editor.controllers?.inline?.selection
if (!selection || !this.workspacePath) {
await context.editor.controllers?.inline.error()
await context.editor.controllers?.inline?.error()
await context.editor.showWarningMessage('Failed to start Inline Chat: empty selection.')
return null
}
const humanInput = humanChatInput.trim() || (await this.getInstructionFromInput()).trim()
if (!humanInput) {
await context.editor.controllers?.inline.error()
await context.editor.controllers?.inline?.error()
await context.editor.showWarningMessage('Failed to start Inline Chat: empty input.')
return null
}
Expand Down Expand Up @@ -71,7 +71,7 @@ export class InlineTouch implements Recipe {
'selection',
new BufferedBotResponseSubscriber(async content => {
if (!content) {
await context.editor.controllers?.inline.error()
await context.editor.controllers?.inline?.error()
await context.editor.showWarningMessage(
'Cody did not suggest any code updates. Please try again with a different question.'
)
Expand Down
8 changes: 6 additions & 2 deletions lib/shared/src/chat/recipes/my-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export class MyPrompt implements Recipe {
await vscode.window.showErrorMessage('Please enter a valid prompt for the recipe.')
return null
}
const commandOutput = await context.editor.controllers?.prompt.get('output')
const commandOutput = (await context.editor.controllers?.prompt?.get('output')) ?? null
if (commandOutput === null) {
await vscode.window.showErrorMessage('No command output.')
return null
}
const note = ' Refer to the command output and shared code snippets to answer my quesiton.'
const truncatedText = truncateText(promptText + note, MAX_HUMAN_INPUT_TOKENS)
// Add selection file name as display when available
Expand Down Expand Up @@ -94,7 +98,7 @@ export class MyPrompt implements Recipe {
commandOutput?: string | null
): Promise<ContextMessage[]> {
const contextMessages: ContextMessage[] = []
const contextConfig = await editor.controllers?.prompt.get('context')
const contextConfig = await editor.controllers?.prompt?.get('context')
const isContextRequired = contextConfig
? (JSON.parse(contextConfig) as CodyPromptContext)
: defaultCodyPromptContext
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/src/chat/recipes/non-stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class NonStop implements Recipe {
if (!controllers) {
return null
}
const taskParameters = await controllers.fixups.getTaskRecipeData(taskId)
const taskParameters = await controllers.fixups?.getTaskRecipeData(taskId)
if (!taskParameters) {
// Nothing to do.
return null
Expand Down
26 changes: 17 additions & 9 deletions lib/shared/src/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ export interface ActiveTextEditorVisibleContent {
revision?: string
}

interface VsCodeInlineController {
export interface VsCodeInlineController {
selection: ActiveTextEditorSelection | null
error(): Promise<void>
}

interface VsCodeFixupController {
export interface VsCodeFixupController {
getTaskRecipeData(taskId: string): Promise<
| {
instruction: string
Expand All @@ -51,19 +51,27 @@ interface VsCodeFixupController {
>
}

interface VsCodeMyPromptController {
export interface VsCodeMyPromptController {
get(type?: string): Promise<string | null>
menu(): Promise<void>
}

export interface ActiveTextEditorViewControllers {
inline: VsCodeInlineController
fixups: VsCodeFixupController
prompt: VsCodeMyPromptController
export interface ActiveTextEditorViewControllers<
I extends VsCodeInlineController = VsCodeInlineController,
F extends VsCodeFixupController = VsCodeFixupController,
P extends VsCodeMyPromptController = VsCodeMyPromptController,
> {
readonly inline?: I
readonly fixups?: F
readonly prompt?: P
}

export interface Editor {
controllers?: ActiveTextEditorViewControllers
export interface Editor<
I extends VsCodeInlineController = VsCodeInlineController,
F extends VsCodeFixupController = VsCodeFixupController,
P extends VsCodeMyPromptController = VsCodeMyPromptController,
> {
controllers?: ActiveTextEditorViewControllers<I, F, P>
getWorkspaceRootPath(): string | null
getActiveTextEditor(): ActiveTextEditor | null
getActiveTextEditorSelection(): ActiveTextEditorSelection | null
Expand Down
39 changes: 21 additions & 18 deletions vscode/src/chat/ChatViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
private intentDetector: IntentDetector,
private codebaseContext: CodebaseContext,
private guardrails: Guardrails,
private editor: VSCodeEditor,
private readonly editor: VSCodeEditor,
private secretStorage: SecretStorage,
private localStorage: LocalStorage,
private rgPath: string | null,
Expand Down Expand Up @@ -355,7 +355,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
private sendPrompt(promptMessages: Message[], responsePrefix = ''): void {
this.cancelCompletion()
void vscode.commands.executeCommand('setContext', 'cody.reply.pending', true)
this.editor.controllers.inline.setResponsePending(true)
this.editor.controllers.inline?.setResponsePending(true)

let text = ''

Expand All @@ -364,7 +364,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
text += content
const displayText = reformatBotMessage(text, responsePrefix)
this.transcript.addAssistantResponse(displayText)
this.editor.controllers.inline.reply(displayText, 'streaming')
this.editor.controllers.inline?.reply(displayText, 'streaming')
this.sendTranscript()
return Promise.resolve()
},
Expand All @@ -387,7 +387,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
// TODO(keegancsmith) guardrails may be slow, we need to make this async update the interaction.
highlightedDisplayText = await this.guardrailsAnnotateAttributions(highlightedDisplayText)
this.transcript.addAssistantResponse(text || '', highlightedDisplayText)
this.editor.controllers.inline.reply(highlightedDisplayText, 'complete')
this.editor.controllers.inline?.reply(highlightedDisplayText, 'complete')
}
void this.onCompletionEnd()
},
Expand Down Expand Up @@ -423,7 +423,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
// We ignore embeddings errors in this instance because we're already showing an
// error message and don't want to overwhelm the user.
this.onCompletionEnd(true)
void this.editor.controllers.inline.error()
void this.editor.controllers.inline?.error()
console.error(`Completion request failed: ${err}`)
},
})
Expand All @@ -441,7 +441,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
void this.saveTranscriptToChatHistory()
this.sendChatHistory()
void vscode.commands.executeCommand('setContext', 'cody.reply.pending', false)
this.editor.controllers.inline.setResponsePending(false)
this.editor.controllers.inline?.setResponsePending(false)
if (!ignoreEmbeddingsError) {
this.logEmbeddingsSearchErrors()
}
Expand All @@ -462,9 +462,9 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp

private async executeChatCommands(text: string, recipeID: RecipeID = 'chat-question'): Promise<void> {
switch (true) {
case /^\/o(pen)?/i.test(text):
case /^\/o(pen)?/i.test(text) && this.editor.controllers.prompt !== undefined:
// open the user's ~/.vscode/cody.json file
await this.editor.controllers.prompt.open(text.split(' ')[1])
await this.editor.controllers.prompt?.open(text.split(' ')[1])
break
case /^\/r(eset)?/i.test(text):
await this.clearAndRestartSession()
Expand Down Expand Up @@ -499,7 +499,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp

this.codebaseContext = codebaseContext
await this.publishContextStatus()
this.editor.controllers.prompt.setCodebase(codebaseContext.getCodebase())
this.editor.controllers.prompt?.setCodebase(codebaseContext.getCodebase())
}

private async getPluginsContext(
Expand Down Expand Up @@ -616,7 +616,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
default: {
this.sendTranscript()

const myPremade = this.editor.controllers.prompt.getMyPrompts().premade
const myPremade = this.editor.controllers.prompt?.getMyPrompts().premade
const { prompt, contextFiles } = await this.transcript.getPromptForLastInteraction(
getPreamble(this.codebaseContext.getCodebase(), myPremade),
this.maxPromptTokens,
Expand Down Expand Up @@ -651,7 +651,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
}
transcript.addInteraction(interaction)

const myPremade = this.editor.controllers.prompt.getMyPrompts().premade
const myPremade = this.editor.controllers.prompt?.getMyPrompts().premade
const { prompt, contextFiles } = await transcript.getPromptForLastInteraction(
getPreamble(this.codebaseContext.getCodebase(), myPremade),
this.maxPromptTokens
Expand Down Expand Up @@ -746,23 +746,23 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
}
// Create a new recipe
if (title === 'menu') {
await this.editor.controllers.prompt.menu()
await this.editor.controllers.prompt?.menu()
await this.sendMyPrompts()
return
}
if (title === 'add-workspace-file' || title === 'add-user-file') {
const fileType = title === 'add-workspace-file' ? 'workspace' : 'user'
try {
// copy the cody.json file from the extension path and move it to the workspace root directory
await this.editor.controllers.prompt.addJSONFile(fileType)
await this.editor.controllers.prompt?.addJSONFile(fileType)
} catch (error) {
void vscode.window.showErrorMessage(`Could not create a new cody.json file: ${error}`)
}
return
}
// Get prompt details from controller by title then execute prompt's command
const prompt = this.editor.controllers.prompt.find(title)
await this.editor.controllers.prompt.get('command')
const prompt = this.editor.controllers.prompt?.find(title)
await this.editor.controllers.prompt?.get('command')
if (!prompt) {
debug('executeCustomRecipe:noPrompt', title)
return
Expand All @@ -778,16 +778,16 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
*/
private async sendMyPrompts(): Promise<void> {
const send = async (): Promise<void> => {
await this.editor.controllers.prompt.refresh()
const prompts = this.editor.controllers.prompt.getPromptList()
await this.editor.controllers.prompt?.refresh()
const prompts = this.editor.controllers.prompt?.getPromptList() ?? []
void this.webview?.postMessage({
type: 'my-prompts',
prompts,
isEnabled: this.config.experimentalCustomRecipes,
})
}
const init = (): void => {
this.editor.controllers.prompt.setMessager(send)
this.editor.controllers.prompt?.setMessager(send)
}
init()
await send()
Expand Down Expand Up @@ -1094,6 +1094,9 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
console.error('used ForTesting method without test support object')
return []
}
if (!this.editor.controllers.fixups) {
throw new Error('no fixup controller')
}
return this.editor.controllers.fixups.getTasks()
}

Expand Down
20 changes: 12 additions & 8 deletions vscode/src/editor/vscode-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as vscode from 'vscode'
import {
ActiveTextEditor,
ActiveTextEditorSelection,
ActiveTextEditorViewControllers,
ActiveTextEditorVisibleContent,
Editor,
} from '@sourcegraph/cody-shared/src/editor'
Expand All @@ -12,13 +13,13 @@ import { MyPromptController } from '../my-cody/MyPromptController'
import { FixupController } from '../non-stop/FixupController'
import { InlineController } from '../services/InlineController'

export class VSCodeEditor implements Editor {
export class VSCodeEditor implements Editor<InlineController, FixupController, MyPromptController> {
constructor(
public controllers: {
inline: InlineController
fixups: FixupController
prompt: MyPromptController
}
public readonly controllers: ActiveTextEditorViewControllers<
InlineController,
FixupController,
MyPromptController
>
) {}

public get fileName(): string {
Expand Down Expand Up @@ -58,7 +59,7 @@ export class VSCodeEditor implements Editor {
}

public getActiveTextEditorSelection(): ActiveTextEditorSelection | null {
if (this.controllers.inline.isInProgress) {
if (this.controllers.inline?.isInProgress) {
return null
}
const activeEditor = this.getActiveTextEditorInstance()
Expand Down Expand Up @@ -133,7 +134,7 @@ export class VSCodeEditor implements Editor {

public async replaceSelection(fileName: string, selectedText: string, replacement: string): Promise<void> {
const activeEditor = this.getActiveTextEditorInstance()
if (this.controllers.inline.isInProgress) {
if (this.controllers.inline?.isInProgress) {
await this.controllers.inline.replace(fileName, replacement, selectedText)
return
}
Expand Down Expand Up @@ -181,6 +182,9 @@ export class VSCodeEditor implements Editor {
// TODO: When Non-Stop Fixup doesn't depend directly on the chat view,
// move the recipe to vscode and remove this entrypoint.
public async didReceiveFixupText(id: string, text: string, state: 'streaming' | 'complete'): Promise<void> {
if (!this.controllers.fixups) {
throw new Error('no fixup controller')
}
await this.controllers.fixups.didReceiveFixupText(id, text, state)
}
}
3 changes: 1 addition & 2 deletions vscode/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ const register = async (
}
// Controller for Custom Recipes
const prompt = new MyPromptController(context, initialConfig.experimentalCustomRecipes)
const controllers = { inline: commentController, fixups: fixup, prompt }

const editor = new VSCodeEditor(controllers)
const editor = new VSCodeEditor({ inline: commentController, fixups: fixup, prompt })
// Could we use the `initialConfig` instead?
const workspaceConfig = vscode.workspace.getConfiguration()
const config = getConfiguration(workspaceConfig)
Expand Down
3 changes: 2 additions & 1 deletion vscode/src/my-cody/MyPromptController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as vscode from 'vscode'

import { Preamble } from '@sourcegraph/cody-shared/src/chat/preamble'
import { defaultCodyPromptContext } from '@sourcegraph/cody-shared/src/chat/recipes/my-prompt'
import { VsCodeMyPromptController } from '@sourcegraph/cody-shared/src/editor'

import { debug } from '../log'

Expand All @@ -22,7 +23,7 @@ import { CodyPrompt, CodyPromptType, MyPrompts } from './types'
* Provides additional prompt management and execution logic
* NOTE: Dogfooding - Internal s2 users only
*/
export class MyPromptController {
export class MyPromptController implements VsCodeMyPromptController {
private myPremade: Preamble | undefined = undefined
private myStarter = ''
private myPromptStore = new Map<string, CodyPrompt>()
Expand Down
Loading

0 comments on commit 491f030

Please sign in to comment.