From 50adabaef68917a65b123dbf6026c410b8c5a006 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 11 Jul 2023 00:51:42 -0700 Subject: don't allow /edit in diff editor, save diff editor --- extension/src/diffs.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index b9ef8384..1dc292e1 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -132,11 +132,18 @@ class DiffManager { console.log("No corresponding diffInfo found for newFilepath"); return; } - fs.writeFileSync( - diffInfo.originalFilepath, - fs.readFileSync(diffInfo.newFilepath) - ); - this.cleanUpDiff(diffInfo); + + // Save the right-side file, then copy over to original + vscode.workspace.textDocuments + .find((doc) => doc.uri.fsPath === newFilepath) + ?.save() + .then(() => { + fs.writeFileSync( + diffInfo.originalFilepath, + fs.readFileSync(diffInfo.newFilepath) + ); + this.cleanUpDiff(diffInfo); + }); } rejectDiff(newFilepath?: string) { -- cgit v1.2.3-70-g09d2 From b1a39567addc511ac0b477aaedaae4e10d7f5d31 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 11 Jul 2023 11:31:41 -0700 Subject: explain insert at cursor, better diff streaming --- continuedev/src/continuedev/steps/core/core.py | 44 ++++++++++++++++++++++--- extension/react-app/src/components/ComboBox.tsx | 32 +++++++++++++++--- extension/src/diffs.ts | 7 +++- 3 files changed, 72 insertions(+), 11 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index b0d9d719..d4d067ba 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -265,6 +265,23 @@ class DefaultModelEditCodeStep(Step): return file_prefix, rif.contents, file_suffix, model_to_use, max_tokens def compile_prompt(self, file_prefix: str, contents: str, file_suffix: str, sdk: ContinueSDK) -> str: + if contents.strip() == "": + # Seperate prompt for insertion at the cursor, the other tends to cause it to repeat whole file + prompt = dedent(f"""\ + +{file_prefix} + + + +{file_suffix} + + +{self.user_input} + + +Please output the code to be inserted at the cursor in order to fulfill the user_request. Do NOT preface your answer or write anything other than code. You should not write any tags, just the code. Make sure to correctly indent the code:""") + return prompt + prompt = self._prompt if file_prefix.strip() != "": prompt += dedent(f""" @@ -306,15 +323,32 @@ 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 + lines_to_display = [] + + async def sendDiffUpdate(lines: List[str], sdk: ContinueSDK, final: bool = False): + nonlocal full_file_contents_lines, rif, lines_to_display 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:] + + # Don't do this at the very end, just show the inserted code + if final: + lines_to_display = [] + # Only recalculate at every new-line, because this is sort of expensive + elif completion.endswith("\n"): + contents_lines = rif.contents.split("\n") + rewritten_lines = 0 + for line in lines: + for i in range(rewritten_lines, len(contents_lines)): + if difflib.SequenceMatcher(None, line, contents_lines[i]).ratio() > 0.7 and contents_lines[i].strip() != "": + rewritten_lines = i + 1 + break + lines_to_display = contents_lines[rewritten_lines:] + new_file_contents = "\n".join( - full_prefix_lines) + "\n" + completion + "\n" + "\n".join(full_suffix_lines) + full_prefix_lines) + "\n" + completion + "\n" + "\n".join(lines_to_display) + "\n" + "\n".join(full_suffix_lines) step_index = sdk.history.current_index @@ -495,7 +529,7 @@ class DefaultModelEditCodeStep(Step): completion_lines_covered += 1 current_line_in_file += 1 - await sendDiffUpdate(lines + [common_whitespace + unfinished_line], sdk) + await sendDiffUpdate(lines + [common_whitespace if unfinished_line.startswith("<") else (common_whitespace + unfinished_line)], sdk) # Add the unfinished line if unfinished_line != "" and not self.line_to_be_ignored(unfinished_line, completion_lines_covered == 0) and not self.is_end_line(unfinished_line): @@ -505,7 +539,7 @@ class DefaultModelEditCodeStep(Step): completion_lines_covered += 1 current_line_in_file += 1 - await sendDiffUpdate(lines, sdk) + await sendDiffUpdate(lines, sdk, final=True) if False: # If the current block isn't empty, add that suggestion diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 801c3a03..585a0584 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -180,6 +180,26 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { useImperativeHandle(ref, () => downshiftProps, [downshiftProps]); + const [metaKeyPressed, setMetaKeyPressed] = useState(false); + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Meta") { + setMetaKeyPressed(true); + } + }; + const handleKeyUp = (e: KeyboardEvent) => { + if (e.key === "Meta") { + setMetaKeyPressed(false); + } + }; + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }); + useEffect(() => { if (!inputRef.current) { return; @@ -272,7 +292,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { - {/* - Highlight code to include as context. Currently open file included by - default. {highlightedCodeSections.length === 0 && ""} - */} + {highlightedCodeSections.length === 0 && + (downshiftProps.inputValue?.startsWith("/edit") || metaKeyPressed) && ( +
+ Inserting at cursor +
+ )} { setHoveringContextDropdown(true); diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 1dc292e1..3ea6b4f8 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -164,7 +164,12 @@ class DiffManager { // Stop the step at step_index in case it is still streaming ideProtocolClient.deleteAtIndex(diffInfo.step_index); - this.cleanUpDiff(diffInfo); + vscode.workspace.textDocuments + .find((doc) => doc.uri.fsPath === newFilepath) + ?.save() + .then(() => { + this.cleanUpDiff(diffInfo); + }); } } -- cgit v1.2.3-70-g09d2 From 19e79b9336fd9abe5776655ece25224d22d92ad8 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 11 Jul 2023 16:40:11 -0700 Subject: explain accept/reject, better edit summaries --- continuedev/src/continuedev/steps/core/core.py | 19 ++++++++++++----- extension/src/diffs.ts | 29 ++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 7 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index d4d067ba..9eddc03f 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -152,7 +152,8 @@ class DefaultModelEditCodeStep(Step): Main task: """) - + _previous_contents: str = "" + _new_contents: str = "" _prompt_and_completion: str = "" def _cleanup_output(self, output: str) -> str: @@ -167,13 +168,19 @@ class DefaultModelEditCodeStep(Step): return output async def describe(self, models: Models) -> Coroutine[str, None, None]: - if self._prompt_and_completion == "": + if self._previous_contents.strip() == self._new_contents.strip(): description = "No edits were made" else: description = await models.gpt3516k.complete(dedent(f"""\ - {self._prompt_and_completion} - - Please give brief a description of the changes made above using markdown bullet points. Be concise and only mention changes made to the commit before, not prefix or suffix:""")) + ```original + {self._previous_contents} + ``` + + ```new + {self._new_contents} + ``` + + Please give brief a description of the changes made above using markdown bullet points. Be concise:""")) name = await models.gpt3516k.complete(f"Write a very short title to describe this requested change (no quotes): '{self.user_input}'. This is the title:") self.name = self._cleanup_output(name) @@ -573,6 +580,8 @@ Please output the code to be inserted at the cursor in order to fulfill the user # Record the completion completion = "\n".join(lines) + self._previous_contents = "\n".join(original_lines) + self._new_contents = completion self._prompt_and_completion += prompt + completion async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 3ea6b4f8..28089fc6 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -2,7 +2,7 @@ import * as os from "os"; import * as path from "path"; import * as fs from "fs"; import * as vscode from "vscode"; -import { ideProtocolClient } from "./activation/activate"; +import { extensionContext, ideProtocolClient } from "./activation/activate"; interface DiffInfo { originalFilepath: string; @@ -70,6 +70,28 @@ class DiffManager { .getConfiguration("diffEditor", editor.document.uri) .update("codeLens", true, vscode.ConfigurationTarget.Global); + if ( + extensionContext?.globalState.get( + "continue.showDiffInfoMessage" + ) !== false + ) { + vscode.window + .showInformationMessage( + "Accept (⌘⇧↩) or reject (⌘⇧⌫) at the top of the file.", + "Got it", + "Don't show again" + ) + .then((selection) => { + if (selection === "Don't show again") { + // Get the global state + extensionContext?.globalState.update( + "continue.showDiffInfoMessage", + false + ); + } + }); + } + return editor; } @@ -152,7 +174,10 @@ class DiffManager { newFilepath = Array.from(this.diffs.keys())[0]; } if (!newFilepath) { - console.log("No newFilepath provided to reject the diff"); + console.log( + "No newFilepath provided to reject the diff, diffs.size was", + this.diffs.size + ); return; } const diffInfo = this.diffs.get(newFilepath); -- cgit v1.2.3-70-g09d2 From 01419c661430f6d100f086513876201cf9e0f0f6 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 13 Jul 2023 14:50:10 -0700 Subject: diff editor infer filepath, codelens in middle --- continuedev/src/continuedev/steps/core/core.py | 98 ++++++++++++++------------ extension/src/diffs.ts | 65 ++++++++++++++--- extension/src/lang-server/codeLens.ts | 8 ++- 3 files changed, 114 insertions(+), 57 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 5ea95104..787da316 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -486,58 +486,64 @@ Please output the code to be inserted at the cursor in order to fulfill the user completion_lines_covered = 0 repeating_file_suffix = False line_below_highlighted_range = file_suffix.lstrip().split("\n")[0] - async for chunk in model_to_use.stream_chat(messages, temperature=0, max_tokens=max_tokens): - # Stop early if it is repeating the file_suffix or the step was deleted - if repeating_file_suffix: - break - if sdk.current_step_was_deleted(): - return - # Accumulate lines - if "content" not in chunk: - continue - chunk = chunk["content"] - chunk_lines = chunk.split("\n") - chunk_lines[0] = unfinished_line + chunk_lines[0] - if chunk.endswith("\n"): - unfinished_line = "" - chunk_lines.pop() # because this will be an empty string - else: - unfinished_line = chunk_lines.pop() - - # Deal with newly accumulated lines - for i in range(len(chunk_lines)): - # Trailing whitespace doesn't matter - chunk_lines[i] = chunk_lines[i].rstrip() - chunk_lines[i] = common_whitespace + chunk_lines[i] - - # Lines that should signify the end of generation - if self.is_end_line(chunk_lines[i]): - break - # Lines that should be ignored, like the <> tags - elif self.line_to_be_ignored(chunk_lines[i], completion_lines_covered == 0): - continue - # Check if we are currently just copying the prefix - elif (lines_of_prefix_copied > 0 or completion_lines_covered == 0) and lines_of_prefix_copied < len(file_prefix.splitlines()) and chunk_lines[i] == full_file_contents_lines[lines_of_prefix_copied]: - # This is a sketchy way of stopping it from repeating the file_prefix. Is a bug if output happens to have a matching line - lines_of_prefix_copied += 1 - continue - # Because really short lines might be expected to be repeated, this is only a !heuristic! - # Stop when it starts copying the file_suffix - elif chunk_lines[i].strip() == line_below_highlighted_range.strip() and len(chunk_lines[i].strip()) > 4 and not (len(original_lines_below_previous_blocks) > 0 and chunk_lines[i].strip() == original_lines_below_previous_blocks[0].strip()): - repeating_file_suffix = True + generator = model_to_use.stream_chat( + messages, temperature=0, max_tokens=max_tokens) + + try: + async for chunk in generator: + # Stop early if it is repeating the file_suffix or the step was deleted + if repeating_file_suffix: break + if sdk.current_step_was_deleted(): + return - # If none of the above, insert the line! - if False: - await handle_generated_line(chunk_lines[i]) + # Accumulate lines + if "content" not in chunk: + continue + chunk = chunk["content"] + chunk_lines = chunk.split("\n") + chunk_lines[0] = unfinished_line + chunk_lines[0] + if chunk.endswith("\n"): + unfinished_line = "" + chunk_lines.pop() # because this will be an empty string + else: + unfinished_line = chunk_lines.pop() + + # Deal with newly accumulated lines + for i in range(len(chunk_lines)): + # Trailing whitespace doesn't matter + chunk_lines[i] = chunk_lines[i].rstrip() + chunk_lines[i] = common_whitespace + chunk_lines[i] + + # Lines that should signify the end of generation + if self.is_end_line(chunk_lines[i]): + break + # Lines that should be ignored, like the <> tags + elif self.line_to_be_ignored(chunk_lines[i], completion_lines_covered == 0): + continue + # Check if we are currently just copying the prefix + elif (lines_of_prefix_copied > 0 or completion_lines_covered == 0) and lines_of_prefix_copied < len(file_prefix.splitlines()) and chunk_lines[i] == full_file_contents_lines[lines_of_prefix_copied]: + # This is a sketchy way of stopping it from repeating the file_prefix. Is a bug if output happens to have a matching line + lines_of_prefix_copied += 1 + continue + # Because really short lines might be expected to be repeated, this is only a !heuristic! + # Stop when it starts copying the file_suffix + elif chunk_lines[i].strip() == line_below_highlighted_range.strip() and len(chunk_lines[i].strip()) > 4 and not (len(original_lines_below_previous_blocks) > 0 and chunk_lines[i].strip() == original_lines_below_previous_blocks[0].strip()): + repeating_file_suffix = True + break - lines.append(chunk_lines[i]) - completion_lines_covered += 1 - current_line_in_file += 1 + # If none of the above, insert the line! + if False: + await handle_generated_line(chunk_lines[i]) - await sendDiffUpdate(lines + [common_whitespace if unfinished_line.startswith("<") else (common_whitespace + unfinished_line)], sdk) + lines.append(chunk_lines[i]) + completion_lines_covered += 1 + current_line_in_file += 1 + await sendDiffUpdate(lines + [common_whitespace if unfinished_line.startswith("<") else (common_whitespace + unfinished_line)], sdk) + finally: + await generator.aclose() # Add the unfinished line if unfinished_line != "" and not self.line_to_be_ignored(unfinished_line, completion_lines_covered == 0) and not self.is_end_line(unfinished_line): unfinished_line = common_whitespace + unfinished_line diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 28089fc6..910c30f2 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -9,6 +9,7 @@ interface DiffInfo { newFilepath: string; editor?: vscode.TextEditor; step_index: number; + range: vscode.Range; } export const DIFF_DIRECTORY = path.join(os.homedir(), ".continue", "diffs"); @@ -18,6 +19,10 @@ class DiffManager { // Doing this because virtual files are read-only private diffs: Map = new Map(); + diffAtNewFilepath(newFilepath: string): DiffInfo | undefined { + return this.diffs.get(newFilepath); + } + private setupDirectory() { // Make sure the diff directory exists if (!fs.existsSync(DIFF_DIRECTORY)) { @@ -35,6 +40,10 @@ class DiffManager { return filepath.replace(/\\/g, "_").replace(/\//g, "_"); } + private getNewFilepath(originalFilepath: string): string { + return path.join(DIFF_DIRECTORY, this.escapeFilepath(originalFilepath)); + } + private openDiffEditor( originalFilepath: string, newFilepath: string @@ -103,18 +112,28 @@ class DiffManager { this.setupDirectory(); // Create or update existing diff - const newFilepath = path.join( - DIFF_DIRECTORY, - this.escapeFilepath(originalFilepath) - ); + const newFilepath = this.getNewFilepath(originalFilepath); fs.writeFileSync(newFilepath, newContent); // Open the diff editor if this is a new diff if (!this.diffs.has(newFilepath)) { + // Figure out the first line that is different + const oldContent = fs.readFileSync(originalFilepath).toString("utf-8"); + let line = 0; + const newLines = newContent.split("\n"); + const oldLines = oldContent.split("\n"); + for (let i = 0; i < newLines.length && i < oldLines.length; i++) { + if (newLines[i] !== oldLines[i]) { + line = i; + break; + } + } + const diffInfo: DiffInfo = { originalFilepath, newFilepath, step_index, + range: new vscode.Range(line, 0, line + 1, 0), }; this.diffs.set(newFilepath, diffInfo); } @@ -139,10 +158,38 @@ class DiffManager { fs.unlinkSync(diffInfo.newFilepath); } + private inferNewFilepath() { + const activeEditorPath = + vscode.window.activeTextEditor?.document.uri.fsPath; + if (activeEditorPath && path.dirname(activeEditorPath) === DIFF_DIRECTORY) { + return activeEditorPath; + } + const visibleEditors = vscode.window.visibleTextEditors.map( + (editor) => editor.document.uri.fsPath + ); + for (const editorPath of visibleEditors) { + if (path.dirname(editorPath) === DIFF_DIRECTORY) { + for (const otherEditorPath of visibleEditors) { + if ( + path.dirname(otherEditorPath) !== DIFF_DIRECTORY && + this.getNewFilepath(otherEditorPath) === editorPath + ) { + return editorPath; + } + } + } + } + + if (this.diffs.size === 1) { + return Array.from(this.diffs.keys())[0]; + } + return undefined; + } + acceptDiff(newFilepath?: string) { - // If no newFilepath is provided and there is only one in the dictionary, use that - if (!newFilepath && this.diffs.size === 1) { - newFilepath = Array.from(this.diffs.keys())[0]; + // When coming from a keyboard shortcut, we have to infer the newFilepath from visible text editors + if (!newFilepath) { + newFilepath = this.inferNewFilepath(); } if (!newFilepath) { console.log("No newFilepath provided to accept the diff"); @@ -170,8 +217,8 @@ class DiffManager { rejectDiff(newFilepath?: string) { // If no newFilepath is provided and there is only one in the dictionary, use that - if (!newFilepath && this.diffs.size === 1) { - newFilepath = Array.from(this.diffs.keys())[0]; + if (!newFilepath) { + newFilepath = this.inferNewFilepath(); } if (!newFilepath) { console.log( diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 778b98dc..5800a00e 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -2,7 +2,7 @@ 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"; +import { DIFF_DIRECTORY, diffManager } from "../diffs"; class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { public provideCodeLenses( document: vscode.TextDocument, @@ -53,7 +53,11 @@ class DiffViewerCodeLensProvider implements vscode.CodeLensProvider { ): vscode.CodeLens[] | Thenable { if (path.dirname(document.uri.fsPath) === DIFF_DIRECTORY) { const codeLenses: vscode.CodeLens[] = []; - const range = new vscode.Range(0, 0, 1, 0); + let range = new vscode.Range(0, 0, 1, 0); + const diffInfo = diffManager.diffAtNewFilepath(document.uri.fsPath); + if (diffInfo) { + range = diffInfo.range; + } codeLenses.push( new vscode.CodeLens(range, { title: "Accept ✅ (⌘⇧↩)", -- cgit v1.2.3-70-g09d2 From b19076ddb6d11acb5ffd54046d9e5cad549c00c1 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 11:01:06 -0700 Subject: command m reliable toggle --- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/react-app/src/components/ComboBox.tsx | 4 ++++ extension/src/commands.ts | 5 ++++- extension/src/debugPanel.ts | 5 +++++ extension/src/diffs.ts | 21 ++++++++++++--------- 6 files changed, 28 insertions(+), 13 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/extension/package-lock.json b/extension/package-lock.json index a79dd6b4..12aa27c9 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.164", + "version": "0.0.165", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.164", + "version": "0.0.165", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index de1f395d..05bd4d84 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.164", + "version": "0.0.165", "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 bd0d59b5..5d9b5109 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -12,6 +12,7 @@ import PillButton from "./PillButton"; import HeaderButtonWithText from "./HeaderButtonWithText"; import { DocumentPlus } from "@styled-icons/heroicons-outline"; import { HighlightedRangeContext } from "../../../schema/FullState"; +import { postVscMessage } from "../vscode"; // #region styled components const mainInputFontSize = 13; @@ -297,6 +298,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { // setShowContextDropdown(target.value.endsWith("@")); }, + onBlur: (e) => { + postVscMessage("blurContinueInput", {}); + }, onKeyDown: (event) => { if (event.key === "Enter" && event.shiftKey) { // Prevent Downshift's default 'Enter' behavior. diff --git a/extension/src/commands.ts b/extension/src/commands.ts index 0025340a..888f01ed 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -16,11 +16,14 @@ import { import { acceptDiffCommand, rejectDiffCommand } from "./diffs"; import * as bridge from "./bridge"; import { debugPanelWebview } from "./debugPanel"; -import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; import { ideProtocolClient } from "./activation/activate"; let focusedOnContinueInput = false; +export const setFocusedOnContinueInput = (value: boolean) => { + focusedOnContinueInput = value; +}; + // Copy everything over from extension.ts const commandsMap: { [command: string]: (...args: any) => any } = { "continue.suggestionDown": suggestionDownCommand, diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 5e1689d1..dd24a8d8 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -6,6 +6,7 @@ import { openEditorAndRevealRange, } from "./util/vscode"; import { RangeInFile } from "./client"; +import { setFocusedOnContinueInput } from "./commands"; const WebSocket = require("ws"); let websocketConnections: { [url: string]: WebsocketConnection | undefined } = @@ -226,6 +227,10 @@ export function setupDebugPanel( openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One); break; } + case "blurContinueInput": { + setFocusedOnContinueInput(false); + break; + } case "withProgress": { // This message allows withProgress to be used in the webview if (data.done) { diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 910c30f2..37943de4 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -104,6 +104,17 @@ class DiffManager { return editor; } + private _findFirstDifferentLine(contentA: string, contentB: string): number { + const linesA = contentA.split("\n"); + const linesB = contentB.split("\n"); + for (let i = 0; i < linesA.length && i < linesB.length; i++) { + if (linesA[i] !== linesB[i]) { + return i; + } + } + return 0; + } + writeDiff( originalFilepath: string, newContent: string, @@ -119,15 +130,7 @@ class DiffManager { if (!this.diffs.has(newFilepath)) { // Figure out the first line that is different const oldContent = fs.readFileSync(originalFilepath).toString("utf-8"); - let line = 0; - const newLines = newContent.split("\n"); - const oldLines = oldContent.split("\n"); - for (let i = 0; i < newLines.length && i < oldLines.length; i++) { - if (newLines[i] !== oldLines[i]) { - line = i; - break; - } - } + const line = this._findFirstDifferentLine(oldContent, newContent); const diffInfo: DiffInfo = { originalFilepath, -- cgit v1.2.3-70-g09d2 From c5102b0997baa81ce544514d6b5b4d5a2eae804f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 13:45:10 -0700 Subject: insidious client_state vs application_state err --- continuedev/src/continuedev/core/autopilot.py | 2 ++ continuedev/src/continuedev/server/gui.py | 2 +- continuedev/src/continuedev/server/ide.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 9 ++++++++- extension/react-app/src/components/ComboBox.tsx | 9 ++++++++- extension/react-app/src/components/StepContainer.tsx | 9 ++++++--- extension/src/continueIdeClient.ts | 8 ++++++-- extension/src/diffs.ts | 2 +- extension/src/util/messenger.ts | 2 +- 9 files changed, 34 insertions(+), 11 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index e1c8a076..82439f49 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -37,6 +37,8 @@ def get_error_title(e: Exception) -> str: return "The request failed. Please check your internet connection and try again. If this issue persists, you can use our API key for free by going to VS Code settings and changing the value of continue.OPENAI_API_KEY to \"\"" elif isinstance(e, openai_errors.InvalidRequestError): return 'Your API key does not have access to GPT-4. You can use ours for free by going to VS Code settings and changing the value of continue.OPENAI_API_KEY to ""' + elif e.__str__().startswith("Cannot connect to host"): + return "The request failed. Please check your internet connection and try again." return e.__str__() or e.__repr__() diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 238273b2..9a411fbe 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -53,7 +53,7 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.session = session async def _send_json(self, message_type: str, data: Any): - if self.websocket.client_state == WebSocketState.DISCONNECTED: + if self.websocket.application_state == WebSocketState.DISCONNECTED: return await self.websocket.send_json({ "messageType": message_type, diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 73cce201..7875c94d 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -149,7 +149,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): return other_msgs async def _send_json(self, message_type: str, data: Any): - if self.websocket.client_state == WebSocketState.DISCONNECTED: + if self.websocket.application_state == WebSocketState.DISCONNECTED: return await self.websocket.send_json({ "messageType": message_type, diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 787da316..75f8e460 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -9,7 +9,7 @@ from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder from ...models.filesystem_edit import EditDiff, FileEdit, FileEditWithFullContents, FileSystemEdit from ...models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents from ...core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation -from ...core.main import ChatMessage, Step, SequentialStep +from ...core.main import ChatMessage, ContinueCustomException, Step, SequentialStep from ...libs.util.count_tokens import MAX_TOKENS_FOR_MODEL, DEFAULT_MAX_TOKENS from ...libs.util.dedent import dedent_and_get_common_whitespace import difflib @@ -608,6 +608,13 @@ Please output the code to be inserted at the cursor in order to fulfill the user rif_dict[rif.filepath] = rif.contents for rif in rif_with_contents: + # If the file doesn't exist, ask them to save it first + if not os.path.exists(rif.filepath): + message = f"The file {rif.filepath} does not exist. Please save it first." + raise ContinueCustomException( + title=message, message=message + ) + await sdk.ide.setFileOpen(rif.filepath) await sdk.ide.setSuggestionsLocked(rif.filepath, True) await self.stream_rif(rif, sdk) diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 5d9b5109..754c9445 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -169,6 +169,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { useImperativeHandle(ref, () => downshiftProps, [downshiftProps]); const [metaKeyPressed, setMetaKeyPressed] = useState(false); + const [focused, setFocused] = useState(false); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Meta") { @@ -298,7 +299,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { // setShowContextDropdown(target.value.endsWith("@")); }, + onFocus: (e) => { + setFocused(true); + }, onBlur: (e) => { + setFocused(false); postVscMessage("blurContinueInput", {}); }, onKeyDown: (event) => { @@ -374,7 +379,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { {highlightedCodeSections.length === 0 && (downshiftProps.inputValue?.startsWith("/edit") || - (metaKeyPressed && downshiftProps.inputValue?.length > 0)) && ( + (focused && + metaKeyPressed && + downshiftProps.inputValue?.length > 0)) && (
Inserting at cursor
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 6fa4ba13..14e9b854 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -253,9 +253,12 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.observation?.error ? ( -
-              {props.historyNode.observation.error as string}
-            
+
+ View Traceback +
+                {props.historyNode.observation.error as string}
+              
+
) : ( void): void; abstract onClose(callback: () => void): void; - + abstract onError(callback: () => void): void; abstract sendAndReceive(messageType: string, data: any): Promise; -- cgit v1.2.3-70-g09d2 From abe77c56abd7aea66fa85bd1257f76dc2d435a15 Mon Sep 17 00:00:00 2001 From: sestinj Date: Sat, 15 Jul 2023 14:35:52 -0700 Subject: fix vscode uri parsing for diff --- extension/src/diffs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'extension/src/diffs.ts') diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index d04f9bdb..db6a6490 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -56,7 +56,7 @@ class DiffManager { return undefined; } - const rightUri = vscode.Uri.parse(newFilepath); + const rightUri = vscode.Uri.file(newFilepath); const leftUri = vscode.Uri.file(originalFilepath); const title = "Continue Diff"; console.log( -- cgit v1.2.3-70-g09d2 From 45ee33f7fd84c0bc49d35d9d1a7a3a8e9f6addd7 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 15 Jul 2023 15:06:32 -0700 Subject: use correct label for meta key --- extension/react-app/src/components/ComboBox.tsx | 3 ++- .../react-app/src/components/StepContainer.tsx | 7 +++++- extension/react-app/src/util/index.ts | 17 +++++++++++-- extension/src/diffs.ts | 3 ++- extension/src/lang-server/codeLens.ts | 7 +++--- extension/src/util/util.ts | 29 ++++++++++++++++++++++ 6 files changed, 58 insertions(+), 8 deletions(-) (limited to 'extension/src/diffs.ts') diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 754c9445..f11e07af 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -13,6 +13,7 @@ import HeaderButtonWithText from "./HeaderButtonWithText"; import { DocumentPlus } from "@styled-icons/heroicons-outline"; import { HighlightedRangeContext } from "../../../schema/FullState"; import { postVscMessage } from "../vscode"; +import { getMetaKeyLabel } from "../util"; // #region styled components const mainInputFontSize = 13; @@ -286,7 +287,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {