From 458d2837163489ebaab0b21b3d6f3ab89c6a45d2 Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Tue, 4 Jul 2023 21:14:35 -0700 Subject: new stop --- extension/package-lock.json | 27 ++++++++++++++++++++++ extension/package.json | 1 + .../react-app/src/components/StepContainer.tsx | 5 +++- 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'extension') diff --git a/extension/package-lock.json b/extension/package-lock.json index c9b8b757..4a3e01f2 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -15,6 +15,7 @@ "@segment/analytics-node": "^0.0.1-beta.16", "@sentry/node": "^7.57.0", "@styled-icons/heroicons-outline": "^10.47.0", + "@styled-icons/heroicons-solid": "^10.47.0", "@vitejs/plugin-react-swc": "^3.3.2", "axios": "^1.2.5", "downshift": "^7.6.0", @@ -2238,6 +2239,23 @@ "styled-components": "*" } }, + "node_modules/@styled-icons/heroicons-solid": { + "version": "10.47.0", + "resolved": "https://registry.npmjs.org/@styled-icons/heroicons-solid/-/heroicons-solid-10.47.0.tgz", + "integrity": "sha512-j+tJx2NzLG2tc91IXJVwKNjsI/osxmak+wmLfnfBsB+49srpxMYjuLPMtl9ZY/xgbNsWO36O+/N5Zf5bkgiKcQ==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "@styled-icons/styled-icon": "^10.7.0" + }, + "funding": { + "type": "GitHub", + "url": "https://github.com/sponsors/jacobwgillespie" + }, + "peerDependencies": { + "react": "*", + "styled-components": "*" + } + }, "node_modules/@styled-icons/styled-icon": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/@styled-icons/styled-icon/-/styled-icon-10.7.0.tgz", @@ -13120,6 +13138,15 @@ "@styled-icons/styled-icon": "^10.7.0" } }, + "@styled-icons/heroicons-solid": { + "version": "10.47.0", + "resolved": "https://registry.npmjs.org/@styled-icons/heroicons-solid/-/heroicons-solid-10.47.0.tgz", + "integrity": "sha512-j+tJx2NzLG2tc91IXJVwKNjsI/osxmak+wmLfnfBsB+49srpxMYjuLPMtl9ZY/xgbNsWO36O+/N5Zf5bkgiKcQ==", + "requires": { + "@babel/runtime": "^7.20.7", + "@styled-icons/styled-icon": "^10.7.0" + } + }, "@styled-icons/styled-icon": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/@styled-icons/styled-icon/-/styled-icon-10.7.0.tgz", diff --git a/extension/package.json b/extension/package.json index 184d16e2..f39e7bd7 100644 --- a/extension/package.json +++ b/extension/package.json @@ -242,6 +242,7 @@ "@segment/analytics-node": "^0.0.1-beta.16", "@sentry/node": "^7.57.0", "@styled-icons/heroicons-outline": "^10.47.0", + "@styled-icons/heroicons-solid": "^10.47.0", "@vitejs/plugin-react-swc": "^3.3.2", "axios": "^1.2.5", "downshift": "^7.6.0", diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index ab0d307f..492857b5 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -13,6 +13,9 @@ import { XMark, ArrowPath, } from "@styled-icons/heroicons-outline"; +import { + Stop, +} from "@styled-icons/heroicons-solid"; import { HistoryNode } from "../../../schema/HistoryNode"; import ReactMarkdown from "react-markdown"; import HeaderButtonWithText from "./HeaderButtonWithText"; @@ -208,7 +211,7 @@ function StepContainer(props: StepContainerProps) { }} text="Delete" > - + {props.historyNode.observation?.error ? ( Date: Tue, 4 Jul 2023 22:23:04 -0700 Subject: stop inside of circle --- extension/react-app/package-lock.json | 27 ++++++++++++++++++++++ extension/react-app/package.json | 1 + .../src/components/HeaderButtonWithText.tsx | 4 +++- .../react-app/src/components/StepContainer.tsx | 7 +++--- 4 files changed, 35 insertions(+), 4 deletions(-) (limited to 'extension') diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json index 85b8633b..fb13dffd 100644 --- a/extension/react-app/package-lock.json +++ b/extension/react-app/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@styled-icons/heroicons-outline": "^10.47.0", + "@styled-icons/heroicons-solid": "^10.47.0", "@types/vscode-webview": "^1.57.1", "downshift": "^7.6.0", "posthog-js": "^1.58.0", @@ -691,6 +692,23 @@ "styled-components": "*" } }, + "node_modules/@styled-icons/heroicons-solid": { + "version": "10.47.0", + "resolved": "https://registry.npmjs.org/@styled-icons/heroicons-solid/-/heroicons-solid-10.47.0.tgz", + "integrity": "sha512-j+tJx2NzLG2tc91IXJVwKNjsI/osxmak+wmLfnfBsB+49srpxMYjuLPMtl9ZY/xgbNsWO36O+/N5Zf5bkgiKcQ==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "@styled-icons/styled-icon": "^10.7.0" + }, + "funding": { + "type": "GitHub", + "url": "https://github.com/sponsors/jacobwgillespie" + }, + "peerDependencies": { + "react": "*", + "styled-components": "*" + } + }, "node_modules/@styled-icons/styled-icon": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/@styled-icons/styled-icon/-/styled-icon-10.7.0.tgz", @@ -3937,6 +3955,15 @@ "@styled-icons/styled-icon": "^10.7.0" } }, + "@styled-icons/heroicons-solid": { + "version": "10.47.0", + "resolved": "https://registry.npmjs.org/@styled-icons/heroicons-solid/-/heroicons-solid-10.47.0.tgz", + "integrity": "sha512-j+tJx2NzLG2tc91IXJVwKNjsI/osxmak+wmLfnfBsB+49srpxMYjuLPMtl9ZY/xgbNsWO36O+/N5Zf5bkgiKcQ==", + "requires": { + "@babel/runtime": "^7.20.7", + "@styled-icons/styled-icon": "^10.7.0" + } + }, "@styled-icons/styled-icon": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/@styled-icons/styled-icon/-/styled-icon-10.7.0.tgz", diff --git a/extension/react-app/package.json b/extension/react-app/package.json index e46fdc8c..12701906 100644 --- a/extension/react-app/package.json +++ b/extension/react-app/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@styled-icons/heroicons-outline": "^10.47.0", + "@styled-icons/heroicons-solid": "^10.47.0", "@types/vscode-webview": "^1.57.1", "downshift": "^7.6.0", "posthog-js": "^1.58.0", diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index 3ddac93c..72a653c5 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -8,15 +8,17 @@ interface HeaderButtonWithTextProps { children: React.ReactNode; disabled?: boolean; inverted?: boolean; + active?: boolean; } const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { const [hover, setHover] = useState(false); + const paddingLeft = (props.disabled ? (props.active ? "3px" : "1px"): (hover ? "4px" : "1px")); return ( { if (!props.disabled) { setHover(true); diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 311f68cf..183ffeef 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -10,8 +10,8 @@ import { import { ChevronDown, ChevronRight, - XMark, ArrowPath, + XMark } from "@styled-icons/heroicons-outline"; import { Stop, @@ -210,9 +210,10 @@ function StepContainer(props: StepContainerProps) { e.stopPropagation(); props.onDelete(); }} - text="Delete" + text={props.historyNode.active ? "Stop" : "Delete"} + active={props.historyNode.active} > - + {props.historyNode.active ? :} {props.historyNode.observation?.error ? ( Date: Tue, 4 Jul 2023 22:39:52 -0700 Subject: side-by-side diff editor --- continuedev/src/continuedev/steps/core/core.py | 82 +++++++++++------- extension/package.json | 10 +++ extension/src/commands.ts | 4 + extension/src/continueIdeClient.ts | 38 ++------- extension/src/diffs.ts | 114 +++++++++++++++++++++++++ extension/src/lang-server/codeLens.ts | 40 ++++++++- 6 files changed, 221 insertions(+), 67 deletions(-) create mode 100644 extension/src/diffs.ts (limited to 'extension') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index b9f0da35..b215b317 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -295,6 +295,17 @@ class DefaultModelEditCodeStep(Step): prompt = self.compile_prompt(file_prefix, contents, file_suffix, sdk) full_file_contents_lines = full_file_contents.split("\n") + async def sendDiffUpdate(lines: List[str], sdk: ContinueSDK): + nonlocal full_file_contents_lines, rif + + completion = "\n".join(lines) + + full_prefix_lines = full_file_contents_lines[:rif.range.start.line] + full_suffix_lines = full_file_contents_lines[rif.range.end.line + 1:] + new_file_contents = "\n".join( + full_prefix_lines) + "\n" + completion + "\n" + "\n".join(full_suffix_lines) + await sdk.ide.showDiff(rif.filepath, new_file_contents) + # Important state variables # ------------------------- original_lines = [] if rif.contents == "" else rif.contents.split("\n") @@ -435,16 +446,16 @@ class DefaultModelEditCodeStep(Step): chunk_lines.pop() # because this will be an empty string else: unfinished_line = chunk_lines.pop() - lines.extend(chunk_lines) + lines.extend(map(lambda l: common_whitespace + l, chunk_lines)) + + if True: + await sendDiffUpdate(lines, sdk) # Deal with newly accumulated lines for line in chunk_lines: # Trailing whitespace doesn't matter line = line.rstrip() - # Add the common whitespace that was removed before prompting - line = common_whitespace + line - # Lines that should signify the end of generation if self.is_end_line(line): break @@ -463,7 +474,9 @@ class DefaultModelEditCodeStep(Step): break # If none of the above, insert the line! - await handle_generated_line(line) + if False: + await handle_generated_line(line) + completion_lines_covered += 1 current_line_in_file += 1 @@ -475,34 +488,37 @@ class DefaultModelEditCodeStep(Step): completion_lines_covered += 1 current_line_in_file += 1 - # If the current block isn't empty, add that suggestion - if len(current_block_lines) > 0: - # We have a chance to back-track here for blank lines that are repeats of the end of the original - # Don't want to have the same ending in both the original and the generated, can just leave it there - num_to_remove = 0 - for i in range(-1, -len(current_block_lines) - 1, -1): - if len(original_lines_below_previous_blocks) == 0: - break - if current_block_lines[i] == original_lines_below_previous_blocks[-1]: - num_to_remove += 1 - original_lines_below_previous_blocks.pop() - else: - break - current_block_lines = current_block_lines[:- - num_to_remove] if num_to_remove > 0 else current_block_lines - - # It's also possible that some lines match at the beginning of the block - # while len(current_block_lines) > 0 and len(original_lines_below_previous_blocks) > 0 and current_block_lines[0] == original_lines_below_previous_blocks[0]: - # current_block_lines.pop(0) - # original_lines_below_previous_blocks.pop(0) - # current_block_start += 1 - - await sdk.ide.showSuggestion(FileEdit( - filepath=rif.filepath, - range=Range.from_shorthand( - current_block_start, 0, current_block_start + len(original_lines_below_previous_blocks), 0), - replacement="\n".join(current_block_lines) - )) + await sendDiffUpdate(lines, sdk) + + if False: + # If the current block isn't empty, add that suggestion + if len(current_block_lines) > 0: + # We have a chance to back-track here for blank lines that are repeats of the end of the original + # Don't want to have the same ending in both the original and the generated, can just leave it there + num_to_remove = 0 + for i in range(-1, -len(current_block_lines) - 1, -1): + if len(original_lines_below_previous_blocks) == 0: + break + if current_block_lines[i] == original_lines_below_previous_blocks[-1]: + num_to_remove += 1 + original_lines_below_previous_blocks.pop() + else: + break + current_block_lines = current_block_lines[:- + num_to_remove] if num_to_remove > 0 else current_block_lines + + # It's also possible that some lines match at the beginning of the block + # while len(current_block_lines) > 0 and len(original_lines_below_previous_blocks) > 0 and current_block_lines[0] == original_lines_below_previous_blocks[0]: + # current_block_lines.pop(0) + # original_lines_below_previous_blocks.pop(0) + # current_block_start += 1 + + await sdk.ide.showSuggestion(FileEdit( + filepath=rif.filepath, + range=Range.from_shorthand( + current_block_start, 0, current_block_start + len(original_lines_below_previous_blocks), 0), + replacement="\n".join(current_block_lines) + )) # Record the completion completion = "\n".join(lines) diff --git a/extension/package.json b/extension/package.json index 87dd7ba6..461f5721 100644 --- a/extension/package.json +++ b/extension/package.json @@ -86,6 +86,16 @@ "category": "Continue", "title": "Reject Suggestion" }, + { + "command": "continue.acceptDiff", + "category": "Continue", + "title": "Accept Diff" + }, + { + "command": "continue.rejectDiff", + "category": "Continue", + "title": "Reject Diff" + }, { "command": "continue.acceptAllSuggestions", "category": "Continue", diff --git a/extension/src/commands.ts b/extension/src/commands.ts index 8072353b..4414a171 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -12,6 +12,8 @@ import { acceptAllSuggestionsCommand, rejectAllSuggestionsCommand, } from "./suggestions"; + +import { acceptDiffCommand, rejectDiffCommand } from "./diffs"; import * as bridge from "./bridge"; import { debugPanelWebview } from "./debugPanel"; import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; @@ -51,6 +53,8 @@ const commandsMap: { [command: string]: (...args: any) => any } = { "continue.suggestionUp": suggestionUpCommand, "continue.acceptSuggestion": acceptSuggestionCommand, "continue.rejectSuggestion": rejectSuggestionCommand, + "continue.acceptDiff": acceptDiffCommand, + "continue.rejectDiff": rejectDiffCommand, "continue.acceptAllSuggestions": acceptAllSuggestionsCommand, "continue.rejectAllSuggestions": rejectAllSuggestionsCommand, "continue.focusContinueInput": async () => { diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index b9969858..90547edc 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -15,6 +15,10 @@ import { import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; +import * as path from "path"; +import * as os from "os"; +import { diffManager } from "./diffs"; + class IdeProtocolClient { private messenger: WebsocketMessenger | null = null; private readonly context: vscode.ExtensionContext; @@ -239,40 +243,8 @@ class IdeProtocolClient { ); } - contentProvider: vscode.Disposable | null = null; - showDiff(filepath: string, replacement: string) { - const myProvider = new (class - implements vscode.TextDocumentContentProvider - { - onDidChangeEmitter = new vscode.EventEmitter(); - onDidChange = this.onDidChangeEmitter.event; - provideTextDocumentContent = (uri: vscode.Uri) => { - return replacement; - }; - })(); - this.contentProvider = vscode.workspace.registerTextDocumentContentProvider( - "continueDiff", - myProvider - ); - - // Call the event fire - const diffFilename = `continueDiff://${filepath}`; - myProvider.onDidChangeEmitter.fire(vscode.Uri.parse(diffFilename)); - - const leftUri = vscode.Uri.file(filepath); - const rightUri = vscode.Uri.parse(diffFilename); - const title = "Continue Diff"; - vscode.commands - .executeCommand("vscode.diff", leftUri, rightUri, title) - .then( - () => { - console.log("Diff view opened successfully"); - }, - (error) => { - console.error("Error opening diff view:", error); - } - ); + diffManager.writeDiff(filepath, replacement); } openFile(filepath: string) { diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts new file mode 100644 index 00000000..4bd072cf --- /dev/null +++ b/extension/src/diffs.ts @@ -0,0 +1,114 @@ +import * as os from "os"; +import * as path from "path"; +import * as fs from "fs"; +import * as vscode from "vscode"; + +interface DiffInfo { + originalFilepath: string; + newFilepath: string; + editor?: vscode.TextEditor; +} + +export const DIFF_DIRECTORY = path.join(os.homedir(), ".continue", "diffs"); + +class DiffManager { + // Create a temporary file in the global .continue directory which displays the updated version + // Doing this because virtual files are read-only + private diffs: Map = new Map(); + + constructor() { + // Make sure the diff directory exists + if (!fs.existsSync(DIFF_DIRECTORY)) { + fs.mkdirSync(DIFF_DIRECTORY, { + recursive: true, + }); + } + } + + private escapeFilepath(filepath: string): string { + return filepath.replace(/\\/g, "_").replace(/\//g, "_"); + } + + private openDiffEditor( + originalFilepath: string, + newFilepath: string, + newContent: string + ): vscode.TextEditor { + const rightUri = vscode.Uri.parse(newFilepath); + const leftUri = vscode.Uri.file(originalFilepath); + const title = "Continue Diff"; + vscode.commands.executeCommand("vscode.diff", leftUri, rightUri, title); + + const editor = vscode.window.activeTextEditor; + if (!editor) { + throw new Error("No active text editor found for Continue Diff"); + } + return editor; + } + + writeDiff(originalFilepath: string, newContent: string): string { + // Create or update existing diff + const newFilepath = path.join( + DIFF_DIRECTORY, + this.escapeFilepath(originalFilepath) + ); + fs.writeFileSync(newFilepath, newContent); + + // Open the diff editor if this is a new diff + if (!this.diffs.has(originalFilepath)) { + const diffInfo: DiffInfo = { + originalFilepath, + newFilepath, + }; + diffInfo.editor = this.openDiffEditor( + originalFilepath, + newFilepath, + newContent + ); + this.diffs.set(originalFilepath, diffInfo); + } + return newFilepath; + } + + cleanUpDiff(diffInfo: DiffInfo) { + // Close the editor, remove the record, delete the file + if (diffInfo.editor) { + vscode.window.showTextDocument(diffInfo.editor.document); + vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + } + this.diffs.delete(diffInfo.originalFilepath); + fs.unlinkSync(diffInfo.newFilepath); + } + + acceptDiff(originalFilepath: string) { + // Get the diff info, copy new file to original, then delete from record and close the corresponding editor + const diffInfo = this.diffs.get(originalFilepath); + if (!diffInfo) { + return; + } + fs.writeFileSync( + diffInfo.originalFilepath, + fs.readFileSync(diffInfo.newFilepath) + ); + this.cleanUpDiff(diffInfo); + } + + rejectDiff(originalFilepath: string) { + const diffInfo = this.diffs.get(originalFilepath); + if (!diffInfo) { + return; + } + + this.cleanUpDiff(diffInfo); + } +} + +export const diffManager = new DiffManager(); + +export async function acceptDiffCommand(originalFilepath: string) { + diffManager.acceptDiff(originalFilepath); +} + +export async function rejectDiffCommand(originalFilepath: string) { + diffManager.rejectDiff(originalFilepath); +} diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 3bd4f153..08435a3b 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -1,6 +1,8 @@ import * as vscode from "vscode"; import { editorToSuggestions, editorSuggestionsLocked } from "../suggestions"; - +import * as path from "path"; +import * as os from "os"; +import { DIFF_DIRECTORY } from "../diffs"; class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { public provideCodeLenses( document: vscode.TextDocument, @@ -60,15 +62,51 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { } } +class DiffViewerCodeLensProvider implements vscode.CodeLensProvider { + public provideCodeLenses( + document: vscode.TextDocument, + token: vscode.CancellationToken + ): vscode.CodeLens[] | Thenable { + if (path.dirname(document.uri.fsPath) !== DIFF_DIRECTORY) { + return []; + } else { + const codeLenses: vscode.CodeLens[] = []; + const range = new vscode.Range(0, 0, 0, 0); + codeLenses.push( + new vscode.CodeLens(range, { + title: "Accept ✅", + command: "continue.acceptDiff", + arguments: [document.uri.fsPath], + }), + new vscode.CodeLens(range, { + title: "Reject ❌", + command: "continue.rejectDiff", + arguments: [document.uri.fsPath], + }) + ); + return codeLenses; + } + } +} + +let diffsCodeLensDisposable: vscode.Disposable | undefined = undefined; let suggestionsCodeLensDisposable: vscode.Disposable | undefined = undefined; export function registerAllCodeLensProviders(context: vscode.ExtensionContext) { if (suggestionsCodeLensDisposable) { suggestionsCodeLensDisposable.dispose(); } + if (diffsCodeLensDisposable) { + diffsCodeLensDisposable.dispose(); + } suggestionsCodeLensDisposable = vscode.languages.registerCodeLensProvider( "*", new SuggestionsCodeLensProvider() ); + diffsCodeLensDisposable = vscode.languages.registerCodeLensProvider( + "*", + new DiffViewerCodeLensProvider() + ); context.subscriptions.push(suggestionsCodeLensDisposable); + context.subscriptions.push(diffsCodeLensDisposable); } -- cgit v1.2.3-70-g09d2 From bf34e1d6a01214a6977a2932b4ee2b413d524957 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 4 Jul 2023 22:55:53 -0700 Subject: small fixes to side-by-side diff --- extension/src/diffs.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'extension') diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 4bd072cf..19ec80ab 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -55,7 +55,7 @@ class DiffManager { fs.writeFileSync(newFilepath, newContent); // Open the diff editor if this is a new diff - if (!this.diffs.has(originalFilepath)) { + if (!this.diffs.has(newFilepath)) { const diffInfo: DiffInfo = { originalFilepath, newFilepath, @@ -65,7 +65,7 @@ class DiffManager { newFilepath, newContent ); - this.diffs.set(originalFilepath, diffInfo); + this.diffs.set(newFilepath, diffInfo); } return newFilepath; } @@ -76,13 +76,13 @@ class DiffManager { vscode.window.showTextDocument(diffInfo.editor.document); vscode.commands.executeCommand("workbench.action.closeActiveEditor"); } - this.diffs.delete(diffInfo.originalFilepath); + this.diffs.delete(diffInfo.newFilepath); fs.unlinkSync(diffInfo.newFilepath); } - acceptDiff(originalFilepath: string) { + acceptDiff(newFilepath: string) { // Get the diff info, copy new file to original, then delete from record and close the corresponding editor - const diffInfo = this.diffs.get(originalFilepath); + const diffInfo = this.diffs.get(newFilepath); if (!diffInfo) { return; } @@ -93,8 +93,8 @@ class DiffManager { this.cleanUpDiff(diffInfo); } - rejectDiff(originalFilepath: string) { - const diffInfo = this.diffs.get(originalFilepath); + rejectDiff(newFilepath: string) { + const diffInfo = this.diffs.get(newFilepath); if (!diffInfo) { return; } @@ -105,10 +105,10 @@ class DiffManager { export const diffManager = new DiffManager(); -export async function acceptDiffCommand(originalFilepath: string) { - diffManager.acceptDiff(originalFilepath); +export async function acceptDiffCommand(newFilepath: string) { + diffManager.acceptDiff(newFilepath); } -export async function rejectDiffCommand(originalFilepath: string) { - diffManager.rejectDiff(originalFilepath); +export async function rejectDiffCommand(newFilepath: string) { + diffManager.rejectDiff(newFilepath); } -- cgit v1.2.3-70-g09d2 From 36f3845a18ed65ff6784b99cb34d9f6c4a7e0143 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 5 Jul 2023 01:04:33 -0700 Subject: highlighted code improvements --- continuedev/src/continuedev/core/autopilot.py | 11 ++++++++- continuedev/src/continuedev/core/main.py | 1 + continuedev/src/continuedev/server/gui.py | 7 ++++++ continuedev/src/continuedev/steps/core/core.py | 1 + extension/react-app/src/components/ComboBox.tsx | 27 ++++++++++++++++++---- extension/react-app/src/components/PillButton.tsx | 27 +++++++--------------- .../src/components/UserInputContainer.tsx | 4 +++- .../src/hooks/ContinueGUIClientProtocol.ts | 2 ++ .../react-app/src/hooks/useContinueGUIProtocol.ts | 4 ++++ extension/react-app/src/tabs/gui.tsx | 6 +++++ 10 files changed, 65 insertions(+), 25 deletions(-) (limited to 'extension') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 313ceded..29be3b79 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -69,7 +69,8 @@ class Autopilot(ContinueBaseModel): user_input_queue=self._main_user_input_queue, default_model=self.continue_sdk.config.default_model, highlighted_ranges=self._highlighted_ranges, - slash_commands=self.get_available_slash_commands() + slash_commands=self.get_available_slash_commands(), + adding_highlighted_code=self._adding_highlighted_code, ) def get_available_slash_commands(self) -> List[Dict]: @@ -140,8 +141,12 @@ class Autopilot(ContinueBaseModel): await self._run_singular_step(step) _highlighted_ranges: List[RangeInFileWithContents] = [] + _adding_highlighted_code: bool = False async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]): + if not self._adding_highlighted_code: + return + workspace_path = self.continue_sdk.ide.workspace_directory for rif in range_in_files: rif.filepath = os.path.basename(rif.filepath) @@ -186,6 +191,10 @@ class Autopilot(ContinueBaseModel): self._highlighted_ranges = kept_ranges await self.update_subscribers() + async def toggle_adding_highlighted_code(self): + self._adding_highlighted_code = not self._adding_highlighted_code + await self.update_subscribers() + async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: # Allow config to set disallowed steps if step.__class__.__name__ in self.continue_sdk.config.disallowed_steps: diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 8bad09d1..28fd964e 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -207,6 +207,7 @@ class FullState(ContinueBaseModel): default_model: str highlighted_ranges: List[RangeInFileWithContents] slash_commands: List[SlashCommandDescription] + adding_highlighted_code: bool class ContinueSDK: diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 4e960f7c..fa573b37 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -85,6 +85,8 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.on_delete_at_index(data["index"]) elif message_type == "delete_context_at_indices": self.on_delete_context_at_indices(data["indices"]) + elif message_type == "toggle_adding_highlighted_code": + self.on_toggle_adding_highlighted_code() except Exception as e: print(e) @@ -128,6 +130,11 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.session.autopilot.delete_context_at_indices(indices) ) + def on_toggle_adding_highlighted_code(self): + asyncio.create_task( + self.session.autopilot.toggle_adding_highlighted_code() + ) + @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)): diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index b215b317..c74412ba 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -286,6 +286,7 @@ class DefaultModelEditCodeStep(Step): return "```" in line or "" in line or "" in line or "" in line or "" in line or "" in line or "" in line or "" in line or "" in line async def stream_rif(self, rif: RangeInFileWithContents, sdk: ContinueSDK): + await sdk.ide.saveFile(rif.filepath) full_file_contents = await sdk.ide.readFile(rif.filepath) file_prefix, contents, file_suffix, model_to_use = await self.get_prompt_parts( diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 3e1f3e16..97f5b57e 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -11,7 +11,12 @@ import CodeBlock from "./CodeBlock"; import { RangeInFile } from "../../../src/client"; import PillButton from "./PillButton"; import HeaderButtonWithText from "./HeaderButtonWithText"; -import { Trash, LockClosed, LockOpen } from "@styled-icons/heroicons-outline"; +import { + Trash, + LockClosed, + LockOpen, + Plus, +} from "@styled-icons/heroicons-outline"; // #region styled components const mainInputFontSize = 16; @@ -100,6 +105,8 @@ interface ComboBoxProps { highlightedCodeSections: (RangeInFile & { contents: string })[]; deleteContextItems: (indices: number[]) => void; onTogglePin: () => void; + onToggleAddContext: () => void; + addingHighlightedCode: boolean; } const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { @@ -249,6 +256,19 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
+ {highlightedCodeSections.length === 0 && ( + { + props.onToggleAddContext(); + }} + inverted={props.addingHighlightedCode} + > + + + )} {highlightedCodeSections.length > 0 && ( <> { ))} - Highlight code to include as context.{" "} - {highlightedCodeSections.length === 0 && - "Otherwise using entire currently open file."} + Highlight code to include as context. Currently open file included by + default. {highlightedCodeSections.length === 0 && ""}
{ props.onHover(false); } }} + onClick={() => { + if (props.onDelete) { + props.onDelete(); + } + }} > -
- { - props.onDelete?.(); - props.onHover?.(false); - }} - > - - - {props.title} -
+ {props.title} ); }; diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx index 44fdba38..a72f6098 100644 --- a/extension/react-app/src/components/UserInputContainer.tsx +++ b/extension/react-app/src/components/UserInputContainer.tsx @@ -14,7 +14,7 @@ interface UserInputContainerProps { historyNode: HistoryNode; } -const StyledDiv = styled.div` +const StyledDiv = styled.div<{ hidden: boolean }>` background-color: rgb(50 50 50); padding: 8px; padding-left: 16px; @@ -24,6 +24,8 @@ const StyledDiv = styled.div` font-size: 13px; display: flex; align-items: center; + visibility: ${(props) => (props.hidden ? "hidden" : "visible")}; + height: ${(props) => (props.hidden ? "0px" : "auto")}; `; const UserInputContainer = (props: UserInputContainerProps) => { diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 96ea7ab3..f123bb2b 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -22,6 +22,8 @@ abstract class AbstractContinueGUIClientProtocol { abstract deleteAtIndex(index: number): void; abstract deleteContextAtIndices(indices: number[]): void; + + abstract toggleAddingHighlightedCode(): void; } export default AbstractContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts index e950387c..49f200ae 100644 --- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -74,6 +74,10 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { deleteContextAtIndices(indices: number[]) { this.messenger.send("delete_context_at_indices", { indices }); } + + toggleAddingHighlightedCode(): void { + this.messenger.send("toggle_adding_highlighted_code", {}); + } } export default ContinueGUIClientProtocol; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index bbf0b126..851045d5 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -71,6 +71,7 @@ function GUI(props: GUIProps) { const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState([]); const [highlightedRanges, setHighlightedRanges] = useState([]); + const [addingHighlightedCode, setAddingHighlightedCode] = useState(false); const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] >([]); @@ -157,6 +158,7 @@ function GUI(props: GUIProps) { setHistory(state.history); setHighlightedRanges(state.highlighted_ranges); setUserInputQueue(state.user_input_queue); + setAddingHighlightedCode(state.adding_highlighted_code); setAvailableSlashCommands( state.slash_commands.map((c: any) => { return { @@ -361,6 +363,10 @@ function GUI(props: GUIProps) { onTogglePin={() => { setPinned((prev: boolean) => !prev); }} + onToggleAddContext={() => { + client?.toggleAddingHighlightedCode(); + }} + addingHighlightedCode={addingHighlightedCode} /> -- cgit v1.2.3-70-g09d2 From f88c33041755bdf50a3a54c37cc38436a4a7c88f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 5 Jul 2023 01:17:58 -0700 Subject: test custom command, don't run shell command --- continuedev/src/continuedev/core/config.py | 5 ++++- continuedev/src/continuedev/steps/chat.py | 6 +----- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'extension') diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index 9e8541dc..8f7e0b8c 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -71,7 +71,10 @@ class ContinueConfig(BaseModel): allow_anonymous_telemetry: Optional[bool] = True default_model: Literal["gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4"] = 'gpt-4' - custom_commands: Optional[List[CustomCommand]] = [] + custom_commands: Optional[List[CustomCommand]] = [CustomCommand( + name="test", + prompt="Write a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.", + )] slash_commands: Optional[List[SlashCommand]] = DEFAULT_SLASH_COMMANDS on_traceback: Optional[List[OnTracebackSteps]] = [ OnTracebackSteps(step_name="DefaultOnTracebackStep")] diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index db3f9d7f..8f88244c 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -107,12 +107,8 @@ class RunTerminalCommandStep(Step): description: str = "Run a terminal command." command: str - async def describe(self, models: Models) -> Coroutine[Any, Any, Coroutine[str, None, None]]: - return f"Ran the terminal command `{self.command}`." - async def run(self, sdk: ContinueSDK): - await sdk.wait_for_user_confirmation(f"Run the following terminal command?\n\n```bash\n{self.command}\n```") - await sdk.run(self.command) + self.description = f"Copy this command and run in your terminal:\n\n```bash\n{self.command}\n```" class ViewDirectoryTreeStep(Step): diff --git a/extension/package-lock.json b/extension/package-lock.json index 2dd7d6d4..82c28a90 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.108", + "version": "0.0.109", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.108", + "version": "0.0.109", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index d4850671..001a696e 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.108", + "version": "0.0.109", "publisher": "Continue", "engines": { "vscode": "^1.67.0" -- cgit v1.2.3-70-g09d2 From b3eb9d934ef6e73ea4f43a9fb7584e948ea9e0b5 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 5 Jul 2023 01:51:46 -0700 Subject: details --- continuedev/src/continuedev/core/policy.py | 64 ++++++++++++---------- extension/package-lock.json | 4 +- extension/package.json | 2 +- extension/react-app/src/components/ComboBox.tsx | 2 +- .../react-app/src/components/StepContainer.tsx | 12 ++-- .../src/components/UserInputContainer.tsx | 10 ++-- extension/react-app/src/tabs/gui.tsx | 18 +++--- 7 files changed, 61 insertions(+), 51 deletions(-) (limited to 'extension') diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index b0853380..ef753ee4 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -1,5 +1,5 @@ from textwrap import dedent -from typing import List, Tuple, Type +from typing import List, Tuple, Type, Union from ..steps.welcome import WelcomeStep from .config import ContinueConfig @@ -22,6 +22,34 @@ from ..libs.util.step_name_to_steps import get_step_from_name from ..steps.custom_command import CustomCommandStep +def parse_slash_command(inp: str, config: ContinueConfig) -> Union[None, Step]: + """ + Parses a slash command, returning the command name and the rest of the input. + """ + if inp.startswith("/"): + command_name = inp.split(" ")[0] + after_command = " ".join(inp.split(" ")[1:]) + + for slash_command in config.slash_commands: + if slash_command.name == command_name[1:]: + params = slash_command.params + params["user_input"] = after_command + return get_step_from_name(slash_command.step_name, params) + return None + + +def parse_custom_command(inp: str, config: ContinueConfig) -> Union[None, Step]: + command_name = inp.split(" ")[0] + after_command = " ".join(inp.split(" ")[1:]) + for custom_cmd in config.custom_commands: + if custom_cmd.name == command_name[1:]: + slash_command = parse_slash_command(custom_cmd.prompt, config) + if slash_command is not None: + return slash_command + return CustomCommandStep(name=custom_cmd.name, prompt=custom_cmd.prompt, user_input=after_command) + return None + + class DemoPolicy(Policy): ran_code_last: bool = False @@ -46,34 +74,14 @@ class DemoPolicy(Policy): # This could be defined with ObservationTypePolicy. Ergonomics not right though. user_input = observation.user_input - if user_input.startswith("/"): - command_name = user_input.split(" ")[0] - after_command = " ".join(user_input.split(" ")[1:]) - for slash_command in config.slash_commands: - if slash_command.name == command_name[1:]: - params = slash_command.params - params["user_input"] = after_command - return get_step_from_name(slash_command.step_name, params) + slash_command = parse_slash_command(user_input) + if slash_command is not None: + return slash_command - for custom_cmd in config.custom_commands: - if custom_cmd.name == command_name[1:]: - return CustomCommandStep(name=custom_cmd.name, prompt=custom_cmd.prompt, user_input=after_command) + custom_command = parse_custom_command(user_input) + if custom_command is not None: + return custom_command - # return EditHighlightedCodeStep(user_input=user_input) return ChatWithFunctions(user_input=user_input) - return NLDecisionStep(user_input=user_input, steps=[ - (EditHighlightedCodeStep(user_input=user_input), - "Edit the highlighted code"), - # AnswerQuestionChroma(question=user_input), - # EditFileChroma(request=user_input), - (SimpleChatStep(user_input=user_input), - "Respond to the user with a chat message. Can answer questions about code or anything else."), - ], default_step=EditHighlightedCodeStep(user_input=user_input)) - - state = history.get_current() - if observation is not None and isinstance(observation, TracebackObservation): - self.ran_code_last = False - return SolveTracebackStep(traceback=observation.traceback) - else: - return None + return None diff --git a/extension/package-lock.json b/extension/package-lock.json index 82c28a90..ce1a42ee 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.109", + "version": "0.0.110", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.109", + "version": "0.0.110", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 001a696e..607c2ca6 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.109", + "version": "0.0.110", "publisher": "Continue", "engines": { "vscode": "^1.67.0" diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 97f5b57e..73b7cc2d 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -324,7 +324,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { /> ))} - + Highlight code to include as context. Currently open file included by default. {highlightedCodeSections.length === 0 && ""} diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 183ffeef..2aed2e72 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -11,11 +11,9 @@ import { ChevronDown, ChevronRight, ArrowPath, - XMark + XMark, } from "@styled-icons/heroicons-outline"; -import { - Stop, -} from "@styled-icons/heroicons-solid"; +import { Stop } from "@styled-icons/heroicons-solid"; import { HistoryNode } from "../../../schema/HistoryNode"; import ReactMarkdown from "react-markdown"; import HeaderButtonWithText from "./HeaderButtonWithText"; @@ -213,7 +211,11 @@ function StepContainer(props: StepContainerProps) { text={props.historyNode.active ? "Stop" : "Delete"} active={props.historyNode.active} > - {props.historyNode.active ? :} + {props.historyNode.active ? ( + + ) : ( + + )}
{props.historyNode.observation?.error ? ( ` - background-color: rgb(50 50 50); +const StyledDiv = styled.div` + background-color: rgb(45 45 45); padding: 8px; padding-left: 16px; padding-right: 16px; @@ -24,14 +24,12 @@ const StyledDiv = styled.div<{ hidden: boolean }>` font-size: 13px; display: flex; align-items: center; - visibility: ${(props) => (props.hidden ? "hidden" : "visible")}; - height: ${(props) => (props.hidden ? "0px" : "auto")}; `; const UserInputContainer = (props: UserInputContainerProps) => { return ( -