From 86fdd4488d0696c6e1f1fb62607e337c864ea930 Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Thu, 29 Jun 2023 22:28:22 -0700
Subject: walkthrough start

---
 extension/package.json | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

diff --git a/extension/package.json b/extension/package.json
index 8c9905dd..9d0ef3ac 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -155,7 +155,55 @@
           "visibility": "visible"
         }
       ]
-    }
+    },
+    "walkthroughs": [
+      {
+        "id": "continue",
+        "title": "Getting Started with Continue",
+        "description": "Learn how to effectively use Continue",
+        "steps": [
+          {
+            "id": "edit",
+            "title": "Highlight and Edit",
+            "description": "This step will run a command and check off once it has been run.\n[Run Command](command:getting-started-sample.runCommand)",
+            "media": {
+              "image": "media/image.png",
+              "altText": "Empty image"
+            },
+            "completionEvents": [
+              "onCommand:continue.acceptSuggestion"
+            ]
+          },
+          {
+            "id": "explain",
+            "title": "Ask Questions",
+            "description": "This step will change a setting and check off when the setting has changed\n[Change Setting](command:getting-started-sample.changeSetting)",
+            "media": {
+              "markdown": "media/walkthrough.md"
+            },
+            "completionEvents": []
+          },
+          {
+            "id": "generate",
+            "title": "Generate Files from Scratch",
+            "description": "Ask Continue to create a file from scratch.",
+            "media": {
+              "markdown": "media/walkthrough.md"
+            },
+            "completionEvents": []
+          },
+          {
+            "id": "commands",
+            "title": "Use a Slash Command",
+            "description": "Highlight some code then type '/explain' followed by a question. Slash commands let you select exactly the function you'd like Continue to perform. Others include '/edit' (to edit code), '/comment' (to comment code), and '/feedback' (to share with us what you think of Continue).",
+            "media": {
+              "markdown": "media/walkthrough.md"
+            },
+            "completionEvents": []
+          }
+        ]
+      }
+    ]
   },
   "scripts": {
     "vscode:prepublish": "npm run esbuild-base -- --minify",
-- 
cgit v1.2.3-70-g09d2


From 1da66af471329204dec9749451d533a47212c7fa Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Thu, 29 Jun 2023 22:46:56 -0700
Subject: lock suggestions until done streaming

---
 continuedev/src/continuedev/server/ide.py          |  9 +++++++++
 continuedev/src/continuedev/server/ide_protocol.py |  4 ++++
 continuedev/src/continuedev/steps/chat.py          | 22 +++++++++++++---------
 continuedev/src/continuedev/steps/core/core.py     |  3 ++-
 extension/src/continueIdeClient.ts                 |  9 +++++++++
 extension/src/debugPanel.ts                        | 18 +++++++++++++-----
 extension/src/lang-server/codeLens.ts              |  7 ++++---
 extension/src/suggestions.ts                       |  1 +
 8 files changed, 55 insertions(+), 18 deletions(-)

diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py
index cc8cb15e..65f3ee74 100644
--- a/continuedev/src/continuedev/server/ide.py
+++ b/continuedev/src/continuedev/server/ide.py
@@ -132,6 +132,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
             await self.openGUI()
         elif message_type == "setFileOpen":
             await self.setFileOpen(data["filepath"], data["open"])
+        elif message_type == "setSuggestionsLocked":
+            await self.setSuggestionsLocked(data["filepath"], data["locked"])
         elif message_type == "fileEdits":
             fileEdits = list(
                 map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"]))
@@ -158,6 +160,13 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
             "open": open
         })
 
+    async def setSuggestionsLocked(self, filepath: str, locked: bool = True):
+        # Lock suggestions in the file so they don't ruin the offset before others are inserted
+        await self._send_json("setSuggestionsLocked", {
+            "filepath": filepath,
+            "locked": locked
+        })
+
     async def openGUI(self):
         session_id = self.session_manager.new_session(self)
         await self._send_json("openGUI", {
diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py
index 79820c36..d2dafa9a 100644
--- a/continuedev/src/continuedev/server/ide_protocol.py
+++ b/continuedev/src/continuedev/server/ide_protocol.py
@@ -23,6 +23,10 @@ class AbstractIdeProtocolServer(ABC):
     async def setFileOpen(self, filepath: str, open: bool = True):
         """Set whether a file is open"""
 
+    @abstractmethod
+    async def setSuggestionsLocked(self, filepath: str, locked: bool = True):
+        """Set whether suggestions are locked"""
+
     @abstractmethod
     async def openGUI(self):
         """Open a GUI"""
diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py
index 5b4318c3..6a2c136e 100644
--- a/continuedev/src/continuedev/steps/chat.py
+++ b/continuedev/src/continuedev/steps/chat.py
@@ -152,8 +152,8 @@ class ChatWithFunctions(Step):
         ))
 
         last_function_called_index_in_history = None
-        # GPT keeps wanting to call the non-existent 'python' function repeatedly, so limiting to once
-        already_called_python = False
+        last_function_called_name = None
+        last_function_called_params = None
         while True:
             was_function_called = False
             func_args = ""
@@ -196,10 +196,8 @@ class ChatWithFunctions(Step):
                 ))
                 break
             else:
+                last_function_called = func_name
                 if func_name == "python" and "python" not in step_name_step_class_map:
-                    if already_called_python:
-                        return
-                    already_called_python = True
                     # GPT must be fine-tuned to believe this exists, but it doesn't always
                     func_name = "EditHighlightedCodeStep"
                     func_args = json.dumps({"user_input": self.user_input})
@@ -239,8 +237,6 @@ class ChatWithFunctions(Step):
                 if func_name not in step_name_step_class_map:
                     raise Exception(
                         f"The model tried to call a function ({func_name}) that does not exist. Please try again.")
-                step_to_run = step_name_step_class_map[func_name](
-                    **fn_call_params)
 
                 # if func_name == "AddFileStep":
                 #     step_to_run.hide = True
@@ -251,9 +247,17 @@ class ChatWithFunctions(Step):
                 # else:
                 #     self.description += f"\n`Running function {func_name}`\n\n"
                 if func_name == "EditHighlightedCodeStep":
-                    step_to_run.user_input = self.user_input
+                    fn_call_params["user_input"] = self.user_input
                 elif func_name == "EditFile":
-                    step_to_run.instructions = self.user_input
+                    fn_call_params["instructions"] = self.user_input
+
+                step_to_run = step_name_step_class_map[func_name](
+                    **fn_call_params)
+                if last_function_called_name is not None and last_function_called_name == func_name and last_function_called_params is not None and last_function_called_params == fn_call_params:
+                    # If it's calling the same function more than once in a row, it's probably looping and confused
+                    return
+                last_function_called_name = func_name
+                last_function_called_params = fn_call_params
 
                 await sdk.run_step(step_to_run)
                 await sdk.update_ui()
diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py
index e9420ea9..46c6a615 100644
--- a/continuedev/src/continuedev/steps/core/core.py
+++ b/continuedev/src/continuedev/steps/core/core.py
@@ -490,8 +490,9 @@ class DefaultModelEditCodeStep(Step):
 
         for rif in rif_with_contents:
             await sdk.ide.setFileOpen(rif.filepath)
+            await sdk.ide.setSuggestionsLocked(rif.filepath, True)
             await self.stream_rif(rif, sdk)
-            # await sdk.ide.saveFile(rif.filepath)
+            await sdk.ide.setSuggestionsLocked(rif.filepath, False)
 
 
 class EditFileStep(Step):
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index 2e641054..1ccc070c 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -1,5 +1,6 @@
 // import { ShowSuggestionRequest } from "../schema/ShowSuggestionRequest";
 import {
+  editorSuggestionsLocked,
   showSuggestion as showSuggestionInEditor,
   SuggestionRanges,
 } from "./suggestions";
@@ -119,6 +120,9 @@ class IdeProtocolClient {
         this.openFile(data.filepath);
         // TODO: Close file if False
         break;
+      case "setSuggestionsLocked":
+        this.setSuggestionsLocked(data.filepath, data.locked);
+        break;
       case "showSuggestion":
         this.showSuggestion(data.edit);
         break;
@@ -204,6 +208,11 @@ class IdeProtocolClient {
     openEditorAndRevealRange(filepath, undefined, vscode.ViewColumn.One);
   }
 
+  setSuggestionsLocked(filepath: string, locked: boolean) {
+    editorSuggestionsLocked.set(filepath, locked);
+    // TODO: Rerender?
+  }
+
   async getUserSecret(key: string) {
     // Check if secret already exists in VS Code settings (global)
     let secret = vscode.workspace.getConfiguration("continue").get(key);
diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts
index 4f3d097c..b176eee7 100644
--- a/extension/src/debugPanel.ts
+++ b/extension/src/debugPanel.ts
@@ -113,7 +113,13 @@ class WebsocketConnection {
     if (typeof message !== "string") {
       message = JSON.stringify(message);
     }
-    this._ws.send(message);
+    if (this._ws.readyState === WebSocket.OPEN) {
+      this._ws.send(message);
+    } else {
+      this._ws.addEventListener("open", () => {
+        this._ws.send(message);
+      });
+    }
   }
 
   public close() {
@@ -231,7 +237,9 @@ export function setupDebugPanel(
           apiUrl: getContinueServerUrl(),
           sessionId,
           vscMediaUrl,
-          dataSwitchOn: vscode.workspace.getConfiguration("continue").get<boolean>("dataSwitch")
+          dataSwitchOn: vscode.workspace
+            .getConfiguration("continue")
+            .get<boolean>("dataSwitch"),
         });
 
         // // Listen for changes to server URL in settings
@@ -249,10 +257,10 @@ export function setupDebugPanel(
         break;
       }
       case "toggleDataSwitch": {
-        // Set the setting in vscode 
+        // Set the setting in vscode
         await vscode.workspace
-        .getConfiguration("continue")
-        .update("dataSwitch", data.on, vscode.ConfigurationTarget.Global);
+          .getConfiguration("continue")
+          .update("dataSwitch", data.on, vscode.ConfigurationTarget.Global);
         break;
       }
       case "websocketForwardingOpen": {
diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts
index 5b55589c..03a9a0a7 100644
--- a/extension/src/lang-server/codeLens.ts
+++ b/extension/src/lang-server/codeLens.ts
@@ -1,5 +1,5 @@
 import * as vscode from "vscode";
-import { editorToSuggestions } from "../suggestions";
+import { editorToSuggestions, editorSuggestionsLocked } from "../suggestions";
 
 class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
   public provideCodeLenses(
@@ -10,6 +10,7 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
     if (!suggestions) {
       return [];
     }
+    const locked = editorSuggestionsLocked.get(document.uri.fsPath.toString());
 
     const codeLenses: vscode.CodeLens[] = [];
     for (const suggestion of suggestions) {
@@ -20,12 +21,12 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
       codeLenses.push(
         new vscode.CodeLens(range, {
           title: "Accept ✅",
-          command: "continue.acceptSuggestion",
+          command: locked ? "" : "continue.acceptSuggestion",
           arguments: [suggestion],
         }),
         new vscode.CodeLens(range, {
           title: "Reject ❌",
-          command: "continue.rejectSuggestion",
+          command: locked ? "" : "continue.rejectSuggestion",
           arguments: [suggestion],
         })
       );
diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts
index 8bed202c..e269f38a 100644
--- a/extension/src/suggestions.ts
+++ b/extension/src/suggestions.ts
@@ -17,6 +17,7 @@ export const editorToSuggestions: Map<
   string, // URI of file
   SuggestionRanges[]
 > = new Map();
+export const editorSuggestionsLocked: Map<string, boolean> = new Map(); // Map from editor URI to whether the suggestions are locked
 export const currentSuggestion: Map<string, number> = new Map(); // Map from editor URI to index of current SuggestionRanges in editorToSuggestions
 
 // When tab is reopened, rerender the decorations:
-- 
cgit v1.2.3-70-g09d2