diff options
Diffstat (limited to 'extension')
| -rw-r--r-- | extension/package-lock.json | 4 | ||||
| -rw-r--r-- | extension/package.json | 2 | ||||
| -rw-r--r-- | extension/react-app/src/components/HeaderButtonWithText.tsx | 6 | ||||
| -rw-r--r-- | extension/react-app/src/components/index.ts | 2 | ||||
| -rw-r--r-- | extension/react-app/src/index.css | 1 | ||||
| -rw-r--r-- | extension/react-app/src/tabs/gui.tsx | 34 | ||||
| -rw-r--r-- | extension/scripts/continuedev-0.1.1-py3-none-any.whl | bin | 83961 -> 84291 bytes | |||
| -rw-r--r-- | extension/src/activation/activate.ts | 6 | ||||
| -rw-r--r-- | extension/src/continueIdeClient.ts | 2 | ||||
| -rw-r--r-- | extension/src/terminal/terminalEmulator.ts | 104 | ||||
| -rw-r--r-- | extension/src/util/lcs.ts | 30 | 
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.whlBinary files differ 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 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; +} | 
