summaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
Diffstat (limited to 'extension')
-rw-r--r--extension/package-lock.json4
-rw-r--r--extension/package.json2
-rw-r--r--extension/react-app/src/components/HeaderButtonWithText.tsx6
-rw-r--r--extension/react-app/src/components/index.ts2
-rw-r--r--extension/react-app/src/index.css1
-rw-r--r--extension/react-app/src/tabs/gui.tsx34
-rw-r--r--extension/scripts/continuedev-0.1.1-py3-none-any.whlbin83961 -> 84291 bytes
-rw-r--r--extension/src/activation/activate.ts6
-rw-r--r--extension/src/continueIdeClient.ts2
-rw-r--r--extension/src/terminal/terminalEmulator.ts104
-rw-r--r--extension/src/util/lcs.ts30
11 files changed, 154 insertions, 37 deletions
diff --git a/extension/package-lock.json b/extension/package-lock.json
index 7e8da126..86c816e0 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "continue",
- "version": "0.0.44",
+ "version": "0.0.47",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "continue",
- "version": "0.0.44",
+ "version": "0.0.47",
"license": "Apache-2.0",
"dependencies": {
"@electron/rebuild": "^3.2.10",
diff --git a/extension/package.json b/extension/package.json
index ec348aa5..56a522ac 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -14,7 +14,7 @@
"displayName": "Continue",
"pricing": "Free",
"description": "Refine code 10x faster",
- "version": "0.0.44",
+ "version": "0.0.47",
"publisher": "Continue",
"engines": {
"vscode": "^1.74.0"
diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx
index d70a3d70..c4f22211 100644
--- a/extension/react-app/src/components/HeaderButtonWithText.tsx
+++ b/extension/react-app/src/components/HeaderButtonWithText.tsx
@@ -14,7 +14,11 @@ const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => {
<HeaderButton
style={{ padding: "3px" }}
onMouseEnter={() => setHover(true)}
- onMouseLeave={() => setHover(false)}
+ onMouseLeave={() => {
+ setTimeout(() => {
+ setHover(false);
+ }, 100);
+ }}
onClick={props.onClick}
>
<span hidden={!hover}>{props.text}</span>
diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts
index 525989af..d99b4d96 100644
--- a/extension/react-app/src/components/index.ts
+++ b/extension/react-app/src/components/index.ts
@@ -48,7 +48,7 @@ export const Pre = styled.pre`
max-height: 150px;
overflow-y: scroll;
margin: 0;
- background-color: ${secondaryDark};
+ background-color: ${vscBackground};
border: none;
/* text wrapping */
diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css
index 32a92d0e..db8afab9 100644
--- a/extension/react-app/src/index.css
+++ b/extension/react-app/src/index.css
@@ -22,6 +22,7 @@ html,
body,
#root {
height: 100%;
+ background-color: var(--vsc-background);
}
body {
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 279d052b..994cb896 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -194,7 +194,7 @@ function GUI(props: GUIProps) {
if (topGuiDivRef.current) {
const timeout = setTimeout(() => {
window.scrollTo({
- top: window.outerHeight,
+ top: topGuiDivRef.current!.offsetHeight,
behavior: "smooth",
});
}, 200);
@@ -206,7 +206,9 @@ function GUI(props: GUIProps) {
console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);
client?.onStateUpdate((state) => {
// Scroll only if user is at very bottom of the window.
- const shouldScrollToBottom = window.outerHeight - window.scrollY < 200;
+ const shouldScrollToBottom =
+ topGuiDivRef.current &&
+ topGuiDivRef.current?.offsetHeight - window.scrollY < 100;
setWaitingForSteps(state.active);
setHistory(state.history);
setUserInputQueue(state.user_input_queue);
@@ -347,12 +349,12 @@ function GUI(props: GUIProps) {
</div>
<ComboBox
- disabled={
- history?.timeline.length
- ? history.timeline[history.current_index].step.name ===
- "Waiting for user confirmation"
- : false
- }
+ // disabled={
+ // history?.timeline.length
+ // ? history.timeline[history.current_index].step.name ===
+ // "Waiting for user confirmation"
+ // : false
+ // }
ref={mainTextInputRef}
onEnter={(e) => {
onMainTextInput();
@@ -365,6 +367,14 @@ function GUI(props: GUIProps) {
<ContinueButton onClick={onMainTextInput} />
</TopGUIDiv>
<Footer>
+ <HeaderButtonWithText
+ onClick={() => {
+ client?.sendClear();
+ }}
+ text="Clear History"
+ >
+ <Trash size="1.6em" />
+ </HeaderButtonWithText>
<a href="https://continue.dev/docs" className="no-underline">
<HeaderButtonWithText text="Continue Docs">
<BookOpen size="1.6em" />
@@ -379,14 +389,6 @@ function GUI(props: GUIProps) {
>
<ChatBubbleOvalLeftEllipsis size="1.6em" />
</HeaderButtonWithText>
- <HeaderButtonWithText
- onClick={() => {
- client?.sendClear();
- }}
- text="Clear History"
- >
- <Trash size="1.6em" />
- </HeaderButtonWithText>
</Footer>
</>
);
diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl
index 614190c7..b0b84230 100644
--- a/extension/scripts/continuedev-0.1.1-py3-none-any.whl
+++ b/extension/scripts/continuedev-0.1.1-py3-none-any.whl
Binary files differ
diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts
index 77010241..32726c86 100644
--- a/extension/src/activation/activate.ts
+++ b/extension/src/activation/activate.ts
@@ -59,6 +59,9 @@ export function activateExtension(
name: "Continue",
});
terminal.dispose();
+ if (!ideProtocolClient.continueTerminal) {
+ ideProtocolClient.continueTerminal = capturedTerminal;
+ }
});
// If any terminals are open to start, replace them
@@ -77,6 +80,9 @@ export function activateExtension(
}
);
terminal.dispose();
+ if (!ideProtocolClient.continueTerminal) {
+ ideProtocolClient.continueTerminal = capturedTerminal;
+ }
});
extensionContext = context;
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index a889d3dc..9a93a4ef 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -323,7 +323,7 @@ class IdeProtocolClient {
return rangeInFiles;
}
- private continueTerminal: CapturedTerminal | undefined;
+ public continueTerminal: CapturedTerminal | undefined;
async runCommand(command: string) {
if (!this.continueTerminal || this.continueTerminal.isClosed()) {
diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts
index 35f02ac0..8e49737e 100644
--- a/extension/src/terminal/terminalEmulator.ts
+++ b/extension/src/terminal/terminalEmulator.ts
@@ -3,6 +3,7 @@
import * as vscode from "vscode";
import os = require("os");
import stripAnsi from "strip-ansi";
+import { longestCommonSubsequence } from "../util/lcs";
function loadNativeModule<T>(id: string): T | null {
try {
@@ -70,12 +71,21 @@ export class CapturedTerminal {
private hasRunCommand: boolean = false;
private dataEndsInPrompt(strippedData: string): boolean {
- const lines = this.dataBuffer.split("\n");
+ const lines = strippedData.split("\n");
+ const lastLine = lines[lines.length - 1];
+
return (
lines.length > 0 &&
- (lines[lines.length - 1].includes("bash-") ||
- lines[lines.length - 1].includes(") $ ")) &&
- lines[lines.length - 1].includes("$")
+ (((lastLine.includes("bash-") || lastLine.includes(") $ ")) &&
+ lastLine.includes("$")) ||
+ (lastLine.includes("]> ") && lastLine.includes(") [")) ||
+ (lastLine.includes(" (") && lastLine.includes(")>")) ||
+ (typeof this.commandPromptString !== "undefined" &&
+ (lastLine.includes(this.commandPromptString) ||
+ this.commandPromptString.length -
+ longestCommonSubsequence(lastLine, this.commandPromptString)
+ .length <
+ 3)))
);
}
@@ -94,12 +104,6 @@ export class CapturedTerminal {
}
async runCommand(command: string): Promise<string> {
- if (!this.hasRunCommand) {
- this.hasRunCommand = true;
- // Let the first bash- prompt appear and let python env be opened
- await this.waitForCommandToFinish();
- }
-
if (this.commandQueue.length === 0) {
return new Promise(async (resolve, reject) => {
this.commandQueue.push([command, resolve]);
@@ -107,8 +111,12 @@ export class CapturedTerminal {
while (this.commandQueue.length > 0) {
const [command, resolve] = this.commandQueue.shift()!;
+ // Refresh the command prompt string every time in case it changes
+ await this.refreshCommandPromptString();
+
this.terminal.sendText(command);
- resolve(await this.waitForCommandToFinish());
+ const output = await this.waitForCommandToFinish();
+ resolve(output);
}
});
} else {
@@ -127,15 +135,33 @@ export class CapturedTerminal {
// Split the output by commands so it can be sent to Continue Server
const strippedData = stripAnsi(data);
- this.splitByCommandsBuffer += strippedData;
+ this.splitByCommandsBuffer += data;
if (this.dataEndsInPrompt(strippedData)) {
if (this.onCommandOutput) {
- this.onCommandOutput(this.splitByCommandsBuffer);
+ this.onCommandOutput(stripAnsi(this.splitByCommandsBuffer));
}
- this.dataBuffer = "";
+ this.splitByCommandsBuffer = "";
}
}
+ private runningClearToGetPrompt: boolean = false;
+ private seenClear: boolean = false;
+ private commandPromptString: string | undefined = undefined;
+ private resolveMeWhenCommandPromptStringFound:
+ | ((_: unknown) => void)
+ | undefined = undefined;
+
+ private async refreshCommandPromptString(): Promise<string | undefined> {
+ // Sends a message that will be received by the terminal to get the command prompt string, see the onData method below in constructor.
+ this.runningClearToGetPrompt = true;
+ this.terminal.sendText("echo");
+ const promise = new Promise((resolve, reject) => {
+ this.resolveMeWhenCommandPromptStringFound = resolve;
+ });
+ await promise;
+ return this.commandPromptString;
+ }
+
constructor(
options: { name: string } & Partial<vscode.ExtensionTerminalOptions>,
onCommandOutput?: (output: string) => void
@@ -153,7 +179,7 @@ export class CapturedTerminal {
// Create the pseudo terminal
this.ptyProcess = pty.spawn(this.shellCmd, [], {
name: "xterm-256color",
- cols: 160, // TODO: Get size of vscode terminal, and change with resize
+ cols: 250, // No way to get the size of VS Code terminal, or listen to resize, so make it just bigger than most conceivable VS Code widths
rows: 26,
cwd: getRootDir(),
env,
@@ -163,9 +189,57 @@ export class CapturedTerminal {
this.writeEmitter = new vscode.EventEmitter<string>();
this.ptyProcess.onData((data: any) => {
+ if (this.runningClearToGetPrompt) {
+ if (
+ stripAnsi(data)
+ .split("\n")
+ .flatMap((line) => line.split("\r"))
+ .find((line) => line.trim() === "echo") !== undefined
+ ) {
+ this.seenClear = true;
+ return;
+ } else if (this.seenClear) {
+ const strippedLines = stripAnsi(data)
+ .split("\r")
+ .filter(
+ (line) =>
+ line.trim().length > 0 &&
+ line.trim() !== "%" &&
+ line.trim() !== "⏎"
+ );
+ const lastLine = strippedLines[strippedLines.length - 1] || "";
+ const lines = lastLine
+ .split("\n")
+ .filter(
+ (line) =>
+ line.trim().length > 0 &&
+ line.trim() !== "%" &&
+ line.trim() !== "⏎"
+ );
+ const commandPromptString = (lines[lines.length - 1] || "").trim();
+ if (
+ commandPromptString.length > 0 &&
+ !commandPromptString.includes("echo")
+ ) {
+ this.runningClearToGetPrompt = false;
+ this.seenClear = false;
+ this.commandPromptString = commandPromptString;
+ console.log(
+ "Found command prompt string: " + this.commandPromptString
+ );
+ if (this.resolveMeWhenCommandPromptStringFound) {
+ this.resolveMeWhenCommandPromptStringFound(undefined);
+ }
+ }
+ return;
+ }
+ }
+
// Pass data through to terminal
+ data = data.replace("⏎", "");
this.writeEmitter.fire(data);
+ this.splitByCommandsListener(data);
for (let listener of this.onDataListeners) {
listener(data);
}
diff --git a/extension/src/util/lcs.ts b/extension/src/util/lcs.ts
new file mode 100644
index 00000000..17ea63f9
--- /dev/null
+++ b/extension/src/util/lcs.ts
@@ -0,0 +1,30 @@
+export function longestCommonSubsequence(a: string, b: string) {
+ const lengths: number[][] = [];
+ for (let i = 0; i <= a.length; i++) {
+ lengths[i] = [];
+ for (let j = 0; j <= b.length; j++) {
+ if (i === 0 || j === 0) {
+ lengths[i][j] = 0;
+ } else if (a[i - 1] === b[j - 1]) {
+ lengths[i][j] = lengths[i - 1][j - 1] + 1;
+ } else {
+ lengths[i][j] = Math.max(lengths[i - 1][j], lengths[i][j - 1]);
+ }
+ }
+ }
+ let result = "";
+ let x = a.length;
+ let y = b.length;
+ while (x !== 0 && y !== 0) {
+ if (lengths[x][y] === lengths[x - 1][y]) {
+ x--;
+ } else if (lengths[x][y] === lengths[x][y - 1]) {
+ y--;
+ } else {
+ result = a[x - 1] + result;
+ x--;
+ y--;
+ }
+ }
+ return result;
+}