diff options
Diffstat (limited to 'extension/src/activation')
| -rw-r--r-- | extension/src/activation/activate.ts | 58 | ||||
| -rw-r--r-- | extension/src/activation/environmentSetup.ts | 147 | 
2 files changed, 135 insertions, 70 deletions
diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index b03282e5..0c6e6009 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -1,7 +1,6 @@  import * as vscode from "vscode";  import { registerAllCommands } from "../commands";  import { registerAllCodeLensProviders } from "../lang-server/codeLens"; -import { sendTelemetryEvent, TelemetryEvent } from "../telemetry";  import IdeProtocolClient from "../continueIdeClient";  import { getContinueServerUrl } from "../bridge";  import { ContinueGUIWebviewViewProvider } from "../debugPanel"; @@ -10,6 +9,8 @@ import {    startContinuePythonServer,  } from "./environmentSetup";  import fetch from "node-fetch"; +import registerQuickFixProvider from "../lang-server/codeActions"; +import { get } from "http";  // import { CapturedTerminal } from "../terminal/terminalEmulator";  const PACKAGE_JSON_RAW_GITHUB_URL = @@ -35,21 +36,40 @@ export async function activateExtension(context: vscode.ExtensionContext) {      })      .catch((e) => console.log("Error checking for extension updates: ", e)); +  // Start the server and display loader if taking > 2 seconds    const sessionIdPromise = (async () => { -    // Start the Python server -    await new Promise((resolve, reject) => { -      vscode.window.withProgress( -        { -          location: vscode.ProgressLocation.Notification, -          title: -            "Starting Continue Server... (it may take a minute to download Python packages)", -          cancellable: false, -        }, -        async (progress, token) => { -          await startContinuePythonServer(); -          resolve(null); +    await new Promise((resolve) => { +      let serverStarted = false; + +      // Start the server and set serverStarted to true when done +      startContinuePythonServer().then(() => { +        serverStarted = true; +        resolve(null); +      }); + +      // Wait for 2 seconds +      setTimeout(() => { +        // If the server hasn't started after 2 seconds, show the notification +        if (!serverStarted) { +          vscode.window.withProgress( +            { +              location: vscode.ProgressLocation.Notification, +              title: +                "Starting Continue Server... (it may take a minute to download Python packages)", +              cancellable: false, +            }, +            async (progress, token) => { +              // Wait for the server to start +              while (!serverStarted) { +                await new Promise((innerResolve) => +                  setTimeout(innerResolve, 1000) +                ); +              } +              return Promise.resolve(); +            } +          );          } -      ); +      }, 2000);      });      // Initialize IDE Protocol Client @@ -58,11 +78,10 @@ export async function activateExtension(context: vscode.ExtensionContext) {        `${serverUrl.replace("http", "ws")}/ide/ws`,        context      ); - -    return ideProtocolClient.getSessionId(); +    return await ideProtocolClient.getSessionId();    })(); -  // Register the webview +  // Register Continue GUI as sidebar webview, and beging a new session    const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise);    context.subscriptions.push( @@ -74,9 +93,4 @@ export async function activateExtension(context: vscode.ExtensionContext) {        }      )    ); - -  // Register commands and providers -  sendTelemetryEvent(TelemetryEvent.ExtensionActivated); -  registerAllCodeLensProviders(context); -  registerAllCommands(context);  } diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 7bd08929..5a9345a6 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -9,7 +9,9 @@ import fetch from "node-fetch";  import * as vscode from "vscode";  import * as os from "os";  import fkill from "fkill"; -import { sendTelemetryEvent, TelemetryEvent } from "../telemetry"; + +const WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR = +  "A Python virtual enviroment cannot be activated because running scripts is disabled for this user. In order to use Continue, please enable signed scripts to run with this command in PowerShell: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`, reload VS Code, and then try again.";  const MAX_RETRIES = 3;  async function retryThenFail( @@ -17,17 +19,43 @@ async function retryThenFail(    retries: number = MAX_RETRIES  ): Promise<any> {    try { +    if (retries < MAX_RETRIES && process.platform === "win32") { +      let [stdout, stderr] = await runCommand("Get-ExecutionPolicy"); +      if (!stdout.includes("RemoteSigned")) { +        [stdout, stderr] = await runCommand( +          "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser" +        ); +        console.log("Execution policy stdout: ", stdout); +        console.log("Execution policy stderr: ", stderr); +      } +    } +      return await fn();    } catch (e: any) {      if (retries > 0) {        return await retryThenFail(fn, retries - 1);      } -    vscode.window.showInformationMessage( -      "Failed to set up Continue extension. Please email nate@continue.dev and we'll get this fixed ASAP!" -    ); -    sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, { -      error: e.message, -    }); + +    // Show corresponding error message depending on the platform +    let msg = +      "Failed to set up Continue extension. Please email hi@continue.dev and we'll get this fixed ASAP!"; +    try { +      switch (process.platform) { +        case "win32": +          msg = WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR; +          break; +        case "darwin": +          break; +        case "linux": +          const [pythonCmd] = await getPythonPipCommands(); +          msg = await getLinuxAptInstallError(pythonCmd); +          break; +      } +    } finally { +      console.log("After retries, failed to set up Continue extension", msg); +      vscode.window.showErrorMessage(msg); +    } +      throw e;    }  } @@ -51,12 +79,6 @@ async function runCommand(cmd: string): Promise<[string, string | undefined]> {      stdout = "";    } -  if (stderr) { -    sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, { -      error: stderr, -    }); -  } -    return [stdout, stderr];  } @@ -107,7 +129,7 @@ export async function getPythonPipCommands() {      if (!versionExists) {        vscode.window.showErrorMessage( -        "Continue requires Python3 version 3.8 or greater. Please update your Python3 installation, reload VS Code, and try again." +        "Continue requires Python version 3.8 or greater. Please update your Python installation, reload VS Code, and try again."        );        throw new Error("Python3.8 or greater is not installed.");      } @@ -186,16 +208,22 @@ async function checkRequirementsInstalled() {    return fs.existsSync(continuePath);  } -async function setupPythonEnv() { -  console.log("Setting up python env for Continue extension..."); - -  const [pythonCmd, pipCmd] = await getPythonPipCommands(); -  const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands( -    pythonCmd, -    pipCmd -  ); +async function getLinuxAptInstallError(pythonCmd: string) { +  // First, try to run the command to install python3-venv +  let [stdout, stderr] = await runCommand(`${pythonCmd} --version`); +  if (stderr) { +    await vscode.window.showErrorMessage( +      "Python3 is not installed. Please install from https://www.python.org/downloads, reload VS Code, and try again." +    ); +    throw new Error(stderr); +  } +  const version = stdout.split(" ")[1].split(".")[1]; +  const installVenvCommand = `apt-get install python3.${version}-venv`; +  await runCommand("apt-get update"); +  return `[Important] Continue needs to create a Python virtual environment, but python3.${version}-venv is not installed. Please run this command in your terminal: \`${installVenvCommand}\`, reload VS Code, and then try again.`; +} -  // First, create the virtual environment +async function createPythonVenv(pythonCmd: string) {    if (checkEnvExists()) {      console.log("Python env already exists, skipping...");    } else { @@ -210,32 +238,38 @@ async function setupPythonEnv() {        stderr &&        stderr.includes("running scripts is disabled on this system")      ) { -      await vscode.window.showErrorMessage( -        "A Python virtual enviroment cannot be activated because running scripts is disabled for this user. Please enable signed scripts to run with this command in PowerShell: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`, reload VS Code, and then try again." -      ); +      console.log("Scripts disabled error when trying to create env"); +      await vscode.window.showErrorMessage(WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR);        throw new Error(stderr);      } else if (        stderr?.includes("On Debian/Ubuntu systems") ||        stdout?.includes("On Debian/Ubuntu systems")      ) { -      // First, try to run the command to install python3-venv -      let [stdout, stderr] = await runCommand(`${pythonCmd} --version`); -      if (stderr) { -        await vscode.window.showErrorMessage( -          "Python3 is not installed. Please install from https://www.python.org/downloads, reload VS Code, and try again." -        ); -        throw new Error(stderr); -      } -      const version = stdout.split(" ")[1].split(".")[1]; -      const installVenvCommand = `apt-get install python3.${version}-venv`; -      await runCommand("apt-get update"); -      // Ask the user to run the command to install python3-venv (requires sudo, so we can't) -      // First, get the python version -      const msg = `[Important] Continue needs to create a Python virtual environment, but python3.${version}-venv is not installed. Please run this command in your terminal: \`${installVenvCommand}\`, reload VS Code, and then try again.`; +      const msg = await getLinuxAptInstallError(pythonCmd);        console.log(msg);        await vscode.window.showErrorMessage(msg);      } else if (checkEnvExists()) {        console.log("Successfully set up python env at ", `${serverPath()}/env`); +    } else if ( +      stderr?.includes("Permission denied") && +      stderr?.includes("python.exe") +    ) { +      // This might mean that another window is currently using the python.exe file to install requirements +      // So we want to wait and try again +      let i = 0; +      await new Promise((resolve, reject) => +        setInterval(() => { +          if (i > 5) { +            reject("Timed out waiting for other window to create env..."); +          } +          if (checkEnvExists()) { +            resolve(null); +          } else { +            console.log("Waiting for other window to create env..."); +          } +          i++; +        }, 5000) +      );      } else {        const msg = [          "Python environment not successfully created. Trying again. Here was the stdout + stderr: ", @@ -246,9 +280,22 @@ async function setupPythonEnv() {        throw new Error(msg);      }    } +} + +async function setupPythonEnv() { +  console.log("Setting up python env for Continue extension..."); + +  const [pythonCmd, pipCmd] = await getPythonPipCommands(); +  const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands( +    pythonCmd, +    pipCmd +  ); -  // Install the requirements    await retryThenFail(async () => { +    // First, create the virtual environment +    await createPythonVenv(pythonCmd); + +    // Install the requirements      if (await checkRequirementsInstalled()) {        console.log("Python requirements already installed, skipping...");      } else { @@ -343,6 +390,14 @@ function serverPath(): string {    return sPath;  } +export function devDataPath(): string { +  const sPath = path.join(getContinueGlobalPath(), "dev_data"); +  if (!fs.existsSync(sPath)) { +    fs.mkdirSync(sPath); +  } +  return sPath; +} +  function serverVersionPath(): string {    return path.join(serverPath(), "server_version.txt");  } @@ -409,21 +464,17 @@ export async function startContinuePythonServer() {            console.log(`stdout: ${data}`);            if (              data.includes("Uvicorn running on") || // Successfully started the server -            data.includes("address already in use") // The server is already running (probably a simultaneously opened VS Code window) +            data.includes("only one usage of each socket address") || // [windows] The server is already running (probably a simultaneously opened VS Code window) +            data.includes("address already in use") // [mac/linux] The server is already running (probably a simultaneously opened VS Code window)            ) {              console.log("Successfully started Continue python server");              resolve(null);            } else if (data.includes("ERROR") || data.includes("Traceback")) { -            sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, { -              error: data, -            }); +            console.log("Error starting Continue python server: ", data);            }          });          child.on("error", (error: any) => {            console.log(`error: ${error.message}`); -          sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, { -            error: error.message, -          });          });          // Write the current version of vscode to a file called server_version.txt  | 
