diff options
| author | Nate Sesti <sestinj@gmail.com> | 2023-06-25 12:24:45 -0700 | 
|---|---|---|
| committer | Nate Sesti <sestinj@gmail.com> | 2023-06-25 12:24:45 -0700 | 
| commit | b72cbb40276b811bebbe3a82c43c71d3cfe1a147 (patch) | |
| tree | 812757fabd0b4f27d3e36e170d5841ebcc093514 /extension/src | |
| parent | 8cbfc4a3713f2ed79d6d99890dee84173fcfa166 (diff) | |
| parent | 3f1123095d2ee652b1db239d325aa74f5acfb0cf (diff) | |
| download | sncontinue-b72cbb40276b811bebbe3a82c43c71d3cfe1a147.tar.gz sncontinue-b72cbb40276b811bebbe3a82c43c71d3cfe1a147.tar.bz2 sncontinue-b72cbb40276b811bebbe3a82c43c71d3cfe1a147.zip | |
Merge branch 'main' into function-calling
Diffstat (limited to 'extension/src')
| -rw-r--r-- | extension/src/activation/activate.ts | 40 | ||||
| -rw-r--r-- | extension/src/commands.ts | 94 | ||||
| -rw-r--r-- | extension/src/continueIdeClient.ts | 16 | ||||
| -rw-r--r-- | extension/src/debugPanel.ts | 174 | ||||
| -rw-r--r-- | extension/src/decorations.ts | 97 | ||||
| -rw-r--r-- | extension/src/lang-server/codeLens.ts | 34 | ||||
| -rw-r--r-- | extension/src/languages/index.d.ts | 13 | ||||
| -rw-r--r-- | extension/src/languages/index.ts | 19 | ||||
| -rw-r--r-- | extension/src/languages/javascript/index.ts | 16 | ||||
| -rw-r--r-- | extension/src/languages/notImplemented.ts | 10 | ||||
| -rw-r--r-- | extension/src/languages/python/index.ts | 74 | 
11 files changed, 18 insertions, 569 deletions
| diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 32726c86..c4bdbaa9 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -2,13 +2,12 @@ import * as vscode from "vscode";  import { registerAllCommands } from "../commands";  import { registerAllCodeLensProviders } from "../lang-server/codeLens";  import { sendTelemetryEvent, TelemetryEvent } from "../telemetry"; -import { getExtensionUri } from "../util/vscode"; -import * as path from "path";  // import { openCapturedTerminal } from "../terminal/terminalEmulator";  import IdeProtocolClient from "../continueIdeClient";  import { getContinueServerUrl } from "../bridge"; -import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel";  import { CapturedTerminal } from "../terminal/terminalEmulator"; +import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel"; +// import { CapturedTerminal } from "../terminal/terminalEmulator";  export let extensionContext: vscode.ExtensionContext | undefined = undefined; @@ -49,41 +48,10 @@ export function activateExtension(    })();    // All opened terminals should be replaced by our own terminal -  vscode.window.onDidOpenTerminal((terminal) => { -    if (terminal.name === "Continue") { -      return; -    } -    const options = terminal.creationOptions; -    const capturedTerminal = new CapturedTerminal({ -      ...options, -      name: "Continue", -    }); -    terminal.dispose(); -    if (!ideProtocolClient.continueTerminal) { -      ideProtocolClient.continueTerminal = capturedTerminal; -    } -  }); +  // vscode.window.onDidOpenTerminal((terminal) => {});    // If any terminals are open to start, replace them -  vscode.window.terminals.forEach((terminal) => { -    if (terminal.name === "Continue") { -      return; -    } -    const options = terminal.creationOptions; -    const capturedTerminal = new CapturedTerminal( -      { -        ...options, -        name: "Continue", -      }, -      (commandOutput: string) => { -        ideProtocolClient.sendCommandOutput(commandOutput); -      } -    ); -    terminal.dispose(); -    if (!ideProtocolClient.continueTerminal) { -      ideProtocolClient.continueTerminal = capturedTerminal; -    } -  }); +  // vscode.window.terminals.forEach((terminal) => {}    extensionContext = context;  } diff --git a/extension/src/commands.ts b/extension/src/commands.ts index 22e15c43..77273343 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -3,7 +3,6 @@ import {    decorationManager,    showAnswerInTextEditor,    showGutterSpinner, -  writeAndShowUnitTest,  } from "./decorations";  import {    acceptSuggestionCommand, @@ -12,19 +11,8 @@ import {    suggestionUpCommand,  } from "./suggestions";  import * as bridge from "./bridge"; -import { debugPanelWebview, setupDebugPanel } from "./debugPanel"; -// import { openCapturedTerminal } from "./terminal/terminalEmulator"; -import { getRightViewColumn } from "./util/vscode"; -import { -  findSuspiciousCode, -  runPythonScript, -  writeUnitTestForFunction, -} from "./bridge"; +import { debugPanelWebview } from "./debugPanel";  import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; -import { getLanguageLibrary } from "./languages"; -import { SerializedDebugContext } from "./client"; -import { addFileSystemToDebugContext } from "./util/util"; -import { ideProtocolClient } from "./activation/activate";  // Copy everything over from extension.ts  const commandsMap: { [command: string]: (...args: any) => any } = { @@ -67,72 +55,9 @@ const commandsMap: { [command: string]: (...args: any) => any } = {        type: "focusContinueInput",      });    }, -  "continue.openCapturedTerminal": () => { -    // Happens in webview resolution function -    // openCapturedTerminal(); -  }, -  "continue.findSuspiciousCode": async ( -    debugContext: SerializedDebugContext -  ) => { -    vscode.window.withProgress( -      { -        location: vscode.ProgressLocation.Notification, -        title: "Finding suspicious code", -        cancellable: false, -      }, -      async (progress, token) => { -        let suspiciousCode = await findSuspiciousCode(debugContext); -        debugContext.rangesInFiles = suspiciousCode; -        let { filesystem } = addFileSystemToDebugContext(debugContext); -        debugPanelWebview?.postMessage({ -          type: "findSuspiciousCode", -          codeLocations: suspiciousCode, -          filesystem, -        }); -      } -    ); -  }, -  "continue.debugTest": async (fileAndFunctionSpecifier: string) => { -    sendTelemetryEvent(TelemetryEvent.AutoDebugThisTest); -    let editor = vscode.window.activeTextEditor; -    if (editor) editor.document.save(); -    let { stdout } = await runPythonScript("run_unit_test.py", [ -      fileAndFunctionSpecifier, -    ]); -    let traceback = getLanguageLibrary( -      fileAndFunctionSpecifier.split("::")[0] -    ).parseFirstStacktrace(stdout); -    if (!traceback) { -      vscode.window.showInformationMessage("The test passes!"); -      return; -    } -    vscode.commands.executeCommand("continue.openContinueGUI").then(() => { -      setTimeout(() => { -        debugPanelWebview?.postMessage({ -          type: "traceback", -          value: traceback, -        }); -      }, 500); -    }); -  },  };  const textEditorCommandsMap: { [command: string]: (...args: any) => {} } = { -  "continue.writeUnitTest": async (editor: vscode.TextEditor) => { -    let position = editor.selection.active; - -    let gutterSpinnerKey = showGutterSpinner(editor, position.line); -    try { -      let test = await writeUnitTestForFunction( -        editor.document.fileName, -        position -      ); -      writeAndShowUnitTest(editor.document.fileName, test); -    } catch { -    } finally { -      decorationManager.deleteDecoration(gutterSpinnerKey); -    } -  },    "continue.writeDocstring": async (editor: vscode.TextEditor, _) => {      sendTelemetryEvent(TelemetryEvent.GenerateDocstring);      let gutterSpinnerKey = showGutterSpinner( @@ -199,20 +124,3 @@ async function answerQuestion(      }    );  } - -// async function suggestFixForAllWorkspaceProblems() { -// Something like this, just figure out the loops for diagnostics vs problems -// let problems = vscode.languages.getDiagnostics(); -// let codeSuggestions = await Promise.all(problems.map((problem) => { -//   return bridge.suggestFixForProblem(problem[0].fsPath, problem[1]); -// })); -// for (const [uri, diagnostics] of problems) { -//   for (let i = 0; i < diagnostics.length; i++) { -//     let diagnostic = diagnostics[i]; -//     let suggestedCode = codeSuggestions[i]; -//     // If you're going to do this for a bunch of files at once, it will show the unsaved icon in the tab -//     // BUT it would be better to have a single window to review all edits -//     showSuggestion(uri.fsPath, diagnostic.range, suggestedCode) -//   } -// } -// } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index fbad5f5d..3a77e348 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -12,7 +12,6 @@ import { debugPanelWebview, setupDebugPanel } from "./debugPanel";  import { FileEditWithFullContents } from "../schema/FileEditWithFullContents";  import fs = require("fs");  import { WebsocketMessenger } from "./util/messenger"; -import { CapturedTerminal } from "./terminal/terminalEmulator";  import { decorationManager } from "./decorations";  class IdeProtocolClient { @@ -326,17 +325,14 @@ class IdeProtocolClient {      return rangeInFiles;    } -  public continueTerminal: CapturedTerminal | undefined; -    async runCommand(command: string) { -    if (!this.continueTerminal || this.continueTerminal.isClosed()) { -      this.continueTerminal = new CapturedTerminal({ -        name: "Continue", -      }); +    if (vscode.window.terminals.length) { +      vscode.window.terminals[0].sendText(command); +    } else { +        const terminal = vscode.window.createTerminal(); +        terminal.show(); +        terminal.sendText(command);      } - -    this.continueTerminal.show(); -    return await this.continueTerminal.runCommand(command);    }    sendCommandOutput(output: string) { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index bb98eb46..b0db590a 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -1,21 +1,11 @@  import * as vscode from "vscode"; -import { -  debugApi, -  getContinueServerUrl, -  runPythonScript, -  unittestApi, -} from "./bridge"; -import { writeAndShowUnitTest } from "./decorations"; -import { showSuggestion } from "./suggestions"; -import { getLanguageLibrary } from "./languages"; +import { getContinueServerUrl } from "./bridge";  import {    getExtensionUri,    getNonce,    openEditorAndRevealRange,  } from "./util/vscode"; -import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; -import { RangeInFile, SerializedDebugContext } from "./client"; -import { addFileSystemToDebugContext } from "./util/util"; +import { RangeInFile } from "./client";  const WebSocket = require("ws");  class StreamManager { @@ -146,6 +136,9 @@ export function setupDebugPanel(    let extensionUri = getExtensionUri();    let scriptUri: string;    let styleMainUri: string; +  let vscMediaUrl: string = debugPanelWebview +    .asWebviewUri(vscode.Uri.joinPath(extensionUri, "react-app/dist")) +    .toString();    const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development;    if (!isProduction) { @@ -236,6 +229,7 @@ export function setupDebugPanel(            vscMachineId: vscode.env.machineId,            apiUrl: getContinueServerUrl(),            sessionId, +          vscMediaUrl,          });          // // Listen for changes to server URL in settings @@ -273,71 +267,6 @@ export function setupDebugPanel(          connection.send(data.message);          break;        } -      case "listTenThings": { -        sendTelemetryEvent(TelemetryEvent.GenerateIdeas); -        let resp = await debugApi.listtenDebugListPost({ -          serializedDebugContext: data.debugContext, -        }); -        panel.webview.postMessage({ -          type: "listTenThings", -          value: resp.completion, -        }); -        break; -      } -      case "suggestFix": { -        let completion: string; -        let codeSelection = data.debugContext.rangesInFiles?.at(0); -        if (codeSelection) { -          completion = ( -            await debugApi.inlineDebugInlinePost({ -              inlineBody: { -                filecontents: await vscode.workspace.fs -                  .readFile(vscode.Uri.file(codeSelection.filepath)) -                  .toString(), -                startline: codeSelection.range.start.line, -                endline: codeSelection.range.end.line, -                traceback: data.debugContext.traceback, -              }, -            }) -          ).completion; -        } else if (data.debugContext.traceback) { -          completion = ( -            await debugApi.suggestionDebugSuggestionGet({ -              traceback: data.debugContext.traceback, -            }) -          ).completion; -        } else { -          break; -        } -        panel.webview.postMessage({ -          type: "suggestFix", -          value: completion, -        }); -        break; -      } -      case "findSuspiciousCode": { -        let traceback = getLanguageLibrary(".py").parseFirstStacktrace( -          data.debugContext.traceback -        ); -        if (traceback === undefined) return; -        vscode.commands.executeCommand( -          "continue.findSuspiciousCode", -          data.debugContext -        ); -        break; -      } -      case "queryEmbeddings": { -        let { results } = await runPythonScript("index.py query", [ -          data.query, -          2, -          vscode.workspace.workspaceFolders?.[0].uri.fsPath, -        ]); -        panel.webview.postMessage({ -          type: "queryEmbeddings", -          results, -        }); -        break; -      }        case "openFile": {          openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One);          break; @@ -351,20 +280,6 @@ export function setupDebugPanel(          streamManager.closeStream();          break;        } -      case "explainCode": { -        sendTelemetryEvent(TelemetryEvent.ExplainCode); -        let debugContext: SerializedDebugContext = addFileSystemToDebugContext( -          data.debugContext -        ); -        let resp = await debugApi.explainDebugExplainPost({ -          serializedDebugContext: debugContext, -        }); -        panel.webview.postMessage({ -          type: "explainCode", -          value: resp.completion, -        }); -        break; -      }        case "withProgress": {          // This message allows withProgress to be used in the webview          if (data.done) { @@ -395,83 +310,6 @@ export function setupDebugPanel(          );          break;        } -      case "makeEdit": { -        sendTelemetryEvent(TelemetryEvent.SuggestFix); -        let suggestedEdits = data.edits; - -        if ( -          typeof suggestedEdits === "undefined" || -          suggestedEdits.length === 0 -        ) { -          vscode.window.showInformationMessage( -            "Continue couldn't find a fix for this error." -          ); -          return; -        } - -        for (let i = 0; i < suggestedEdits.length; i++) { -          let edit = suggestedEdits[i]; -          await showSuggestion( -            edit.filepath, -            new vscode.Range( -              edit.range.start.line, -              edit.range.start.character, -              edit.range.end.line, -              edit.range.end.character -            ), -            edit.replacement -          ); -        } -        break; -      } -      case "generateUnitTest": { -        sendTelemetryEvent(TelemetryEvent.CreateTest); -        vscode.window.withProgress( -          { -            location: vscode.ProgressLocation.Notification, -            title: "Generating Unit Test...", -            cancellable: false, -          }, -          async () => { -            for (let i = 0; i < data.debugContext.rangesInFiles?.length; i++) { -              let codeSelection = data.debugContext.rangesInFiles?.at(i); -              if ( -                codeSelection && -                codeSelection.filepath && -                codeSelection.range -              ) { -                try { -                  let filecontents = ( -                    await vscode.workspace.fs.readFile( -                      vscode.Uri.file(codeSelection.filepath) -                    ) -                  ).toString(); -                  let resp = -                    await unittestApi.failingtestUnittestFailingtestPost({ -                      failingTestBody: { -                        fp: { -                          filecontents, -                          lineno: codeSelection.range.end.line, -                        }, -                        description: data.debugContext.description || "", -                      }, -                    }); - -                  if (resp.completion) { -                    let decorationKey = await writeAndShowUnitTest( -                      codeSelection.filepath, -                      resp.completion -                    ); -                    break; -                  } -                } catch {} -              } -            } -          } -        ); - -        break; -      }      }    }); diff --git a/extension/src/decorations.ts b/extension/src/decorations.ts index d2c94135..0587110c 100644 --- a/extension/src/decorations.ts +++ b/extension/src/decorations.ts @@ -1,7 +1,5 @@  import * as vscode from "vscode"; -import { getRightViewColumn, getTestFile } from "./util/vscode";  import * as path from "path"; -import { getLanguageLibrary } from "./languages";  export function showAnswerInTextEditor(    filename: string, @@ -223,98 +221,3 @@ export function highlightCode(    return key;  } - -// Show unit test -const pythonImportDistinguisher = (line: string): boolean => { -  if (line.startsWith("from") || line.startsWith("import")) { -    return true; -  } -  return false; -}; -const javascriptImportDistinguisher = (line: string): boolean => { -  if (line.startsWith("import")) { -    return true; -  } -  return false; -}; -const importDistinguishersMap: { -  [fileExtension: string]: (line: string) => boolean; -} = { -  js: javascriptImportDistinguisher, -  ts: javascriptImportDistinguisher, -  py: pythonImportDistinguisher, -}; -function getImportsFromFileString( -  fileString: string, -  importDistinguisher: (line: string) => boolean -): Set<string> { -  let importLines = new Set<string>(); -  for (let line of fileString.split("\n")) { -    if (importDistinguisher(line)) { -      importLines.add(line); -    } -  } -  return importLines; -} -function removeRedundantLinesFrom( -  fileContents: string, -  linesToRemove: Set<string> -): string { -  let fileLines = fileContents.split("\n"); -  fileLines = fileLines.filter((line: string) => { -    return !linesToRemove.has(line); -  }); -  return fileLines.join("\n"); -} - -export async function writeAndShowUnitTest( -  filename: string, -  test: string -): Promise<DecorationKey> { -  return new Promise((resolve, reject) => { -    let testFilename = getTestFile(filename, true); -    vscode.workspace.openTextDocument(testFilename).then((doc) => { -      let fileContent = doc.getText(); -      let fileEmpty = fileContent.trim() === ""; -      let existingImportLines = getImportsFromFileString( -        fileContent, -        importDistinguishersMap[doc.fileName.split(".").at(-1) || ".py"] -      ); - -      // Remove redundant imports, make sure pytest is there -      test = removeRedundantLinesFrom(test, existingImportLines); -      test = -        (fileEmpty -          ? `${getLanguageLibrary(".py").writeImport( -              testFilename, -              filename -            )}\nimport pytest\n\n` -          : "\n\n") + -        test.trim() + -        "\n"; - -      vscode.window -        .showTextDocument(doc, getRightViewColumn()) -        .then((editor) => { -          let lastLine = editor.document.lineAt(editor.document.lineCount - 1); -          let testRange = new vscode.Range( -            lastLine.range.end, -            new vscode.Position( -              test.split("\n").length + lastLine.range.end.line, -              0 -            ) -          ); -          editor -            .edit((edit) => { -              edit.insert(lastLine.range.end, test); -              return true; -            }) -            .then((success) => { -              if (!success) reject("Failed to insert test"); -              let key = highlightCode(editor, testRange); -              resolve(key); -            }); -        }); -    }); -  }); -} diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 2a362b62..26528d96 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -1,5 +1,4 @@  import * as vscode from "vscode"; -import { getLanguageLibrary } from "../languages";  import { editorToSuggestions } from "../suggestions";  class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { @@ -52,40 +51,9 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {    }  } -class PytestCodeLensProvider implements vscode.CodeLensProvider { -  public provideCodeLenses( -    document: vscode.TextDocument, -    token: vscode.CancellationToken -  ): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> { -    let codeLenses: vscode.CodeLens[] = []; -    let lineno = 1; -    let languageLibrary = getLanguageLibrary(document.fileName); -    for (let line of document.getText().split("\n")) { -      if ( -        languageLibrary.lineIsFunctionDef(line) && -        languageLibrary.parseFunctionDefForName(line).startsWith("test_") -      ) { -        let functionToTest = languageLibrary.parseFunctionDefForName(line); -        let fileAndFunctionNameSpecifier = -          document.fileName + "::" + functionToTest; -        codeLenses.push( -          new vscode.CodeLens(new vscode.Range(lineno, 0, lineno, 1), { -            title: "Debug This Test", -            command: "continue.debugTest", -            arguments: [fileAndFunctionNameSpecifier], -          }) -        ); -      } -      lineno++; -    } - -    return codeLenses; -  } -} -  const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } =    { -    python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], +    python: [new SuggestionsCodeLensProvider()],    };  export function registerAllCodeLensProviders(context: vscode.ExtensionContext) { diff --git a/extension/src/languages/index.d.ts b/extension/src/languages/index.d.ts deleted file mode 100644 index be7ddfbc..00000000 --- a/extension/src/languages/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface LanguageLibrary { -  language: string; -  fileExtensions: string[]; -  parseFirstStacktrace: (stdout: string) => string | undefined; -  lineIsFunctionDef: (line: string) => boolean; -  parseFunctionDefForName: (line: string) => string; -  lineIsComment: (line: string) => boolean; -  writeImport: ( -    sourcePath: string, -    pathToImport: string, -    namesToImport?: string[] | undefined -  ) => string; -} diff --git a/extension/src/languages/index.ts b/extension/src/languages/index.ts deleted file mode 100644 index 31d73a0b..00000000 --- a/extension/src/languages/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import pythonLanguageLibrary from "./python"; -import javascriptLanguageLibrary from "./javascript"; -import { LanguageLibrary } from "./index.d"; - -export const languageLibraries: LanguageLibrary[] = [ -  pythonLanguageLibrary, -  javascriptLanguageLibrary, -]; - -export function getLanguageLibrary(filepath: string): LanguageLibrary { -  for (let languageLibrary of languageLibraries) { -    for (let fileExtension of languageLibrary.fileExtensions) { -      if (filepath.endsWith(fileExtension)) { -        return languageLibrary; -      } -    } -  } -  throw new Error(`No language library found for file ${filepath}`); -} diff --git a/extension/src/languages/javascript/index.ts b/extension/src/languages/javascript/index.ts deleted file mode 100644 index 1c21a2fc..00000000 --- a/extension/src/languages/javascript/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { LanguageLibrary } from "../index.d"; -import { notImplemented } from "../notImplemented"; - -const NI = (propertyName: string) => notImplemented(propertyName, "javascript"); - -const javascriptLangaugeLibrary: LanguageLibrary = { -  language: "javascript", -  fileExtensions: [".js", ".jsx", ".ts", ".tsx"], -  parseFirstStacktrace: NI("parseFirstStacktrace"), -  lineIsFunctionDef: NI("lineIsFunctionDef"), -  parseFunctionDefForName: NI("parseFunctionDefForName"), -  lineIsComment: NI("lineIsComment"), -  writeImport: NI("writeImport"), -}; - -export default javascriptLangaugeLibrary; diff --git a/extension/src/languages/notImplemented.ts b/extension/src/languages/notImplemented.ts deleted file mode 100644 index bbba2382..00000000 --- a/extension/src/languages/notImplemented.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function notImplemented( -  propertyName: string, -  langauge: string -): (...args: any[]) => never { -  return (...args: any[]) => { -    throw new Error( -      `Property ${propertyName} not implemented for language ${langauge}.` -    ); -  }; -} diff --git a/extension/src/languages/python/index.ts b/extension/src/languages/python/index.ts deleted file mode 100644 index 50282b45..00000000 --- a/extension/src/languages/python/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -import path = require("path"); -import { LanguageLibrary } from "../index.d"; - -const tracebackStart = "Traceback (most recent call last):"; -const tracebackEnd = (buf: string): string | undefined => { -  let lines = buf -    .split("\n") -    .filter((line: string) => line.trim() !== "~~^~~") -    .filter((line: string) => line.trim() !== ""); -  for (let i = 0; i < lines.length; i++) { -    if ( -      lines[i].startsWith("  File") && -      i + 2 < lines.length && -      lines[i + 2][0] !== " " -    ) { -      return lines.slice(0, i + 3).join("\n"); -    } -  } -  return undefined; -}; - -function parseFirstStacktrace(stdout: string): string | undefined { -  let startIdx = stdout.indexOf(tracebackStart); -  if (startIdx < 0) return undefined; -  stdout = stdout.substring(startIdx); -  return tracebackEnd(stdout); -} - -function lineIsFunctionDef(line: string): boolean { -  return line.startsWith("def "); -} - -function parseFunctionDefForName(line: string): string { -  return line.split("def ")[1].split("(")[0]; -} - -function lineIsComment(line: string): boolean { -  return line.trim().startsWith("#"); -} - -function writeImport( -  sourcePath: string, -  pathToImport: string, -  namesToImport: string[] | undefined = undefined -): string { -  let segs = path.relative(sourcePath, pathToImport).split(path.sep); -  let importFrom = ""; -  for (let seg of segs) { -    if (seg === "..") { -      importFrom = "." + importFrom; -    } else { -      if (!importFrom.endsWith(".")) { -        importFrom += "."; -      } -      importFrom += seg.split(".").slice(0, -1).join("."); -    } -  } - -  return `from ${importFrom} import ${ -    namesToImport ? namesToImport.join(", ") : "*" -  }`; -} - -const pythonLangaugeLibrary: LanguageLibrary = { -  language: "python", -  fileExtensions: [".py"], -  parseFirstStacktrace, -  lineIsFunctionDef, -  parseFunctionDefForName, -  lineIsComment, -  writeImport, -}; - -export default pythonLangaugeLibrary; | 
