extension.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // This method is called when your extension is deactivated
  2. export function deactivate() {}
  3. import * as vscode from "vscode"
  4. const TERMINAL_NAME = "opencode"
  5. export function activate(context: vscode.ExtensionContext) {
  6. let openNewTerminalDisposable = vscode.commands.registerCommand("opencode.openNewTerminal", async () => {
  7. await openTerminal()
  8. })
  9. let openTerminalDisposable = vscode.commands.registerCommand("opencode.openTerminal", async () => {
  10. // An opencode terminal already exists => focus it
  11. const existingTerminal = vscode.window.terminals.find((t) => t.name === TERMINAL_NAME)
  12. if (existingTerminal) {
  13. existingTerminal.show()
  14. return
  15. }
  16. await openTerminal()
  17. })
  18. let addFilepathDisposable = vscode.commands.registerCommand("opencode.addFilepathToTerminal", async () => {
  19. const fileRef = getActiveFile()
  20. if (!fileRef) return
  21. const terminal = vscode.window.activeTerminal
  22. if (!terminal) return
  23. if (terminal.name === TERMINAL_NAME) {
  24. // @ts-ignore
  25. const port = terminal.creationOptions.env?.["_EXTENSION_OPENCODE_PORT"]
  26. port ? await appendPrompt(parseInt(port), fileRef) : terminal.sendText(fileRef)
  27. terminal.show()
  28. }
  29. })
  30. context.subscriptions.push(openTerminalDisposable, addFilepathDisposable)
  31. async function openTerminal() {
  32. // Create a new terminal in split screen
  33. const port = Math.floor(Math.random() * (65535 - 16384 + 1)) + 16384
  34. const terminal = vscode.window.createTerminal({
  35. name: TERMINAL_NAME,
  36. iconPath: {
  37. light: vscode.Uri.file(context.asAbsolutePath("images/button-dark.svg")),
  38. dark: vscode.Uri.file(context.asAbsolutePath("images/button-light.svg")),
  39. },
  40. location: {
  41. viewColumn: vscode.ViewColumn.Beside,
  42. preserveFocus: false,
  43. },
  44. env: {
  45. _EXTENSION_OPENCODE_PORT: port.toString(),
  46. },
  47. })
  48. terminal.show()
  49. terminal.sendText(`OPENCODE_CALLER=vscode opencode --port ${port}`)
  50. const fileRef = getActiveFile()
  51. if (!fileRef) return
  52. // Wait for the terminal to be ready
  53. let tries = 10
  54. let connected = false
  55. do {
  56. await new Promise((resolve) => setTimeout(resolve, 200))
  57. try {
  58. await fetch(`http://localhost:${port}/app`)
  59. connected = true
  60. break
  61. } catch (e) {}
  62. tries--
  63. } while (tries > 0)
  64. // If connected, append the prompt to the terminal
  65. if (connected) {
  66. await appendPrompt(port, `In ${fileRef}`)
  67. terminal.show()
  68. }
  69. }
  70. async function appendPrompt(port: number, text: string) {
  71. await fetch(`http://localhost:${port}/tui/append-prompt`, {
  72. method: "POST",
  73. headers: {
  74. "Content-Type": "application/json",
  75. },
  76. body: JSON.stringify({ text }),
  77. })
  78. }
  79. function getActiveFile() {
  80. const activeEditor = vscode.window.activeTextEditor
  81. if (!activeEditor) return
  82. const document = activeEditor.document
  83. const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri)
  84. if (!workspaceFolder) return
  85. // Get the relative path from workspace root
  86. const relativePath = vscode.workspace.asRelativePath(document.uri)
  87. let filepathWithAt = `@${relativePath}`
  88. // Check if there's a selection and add line numbers
  89. const selection = activeEditor.selection
  90. if (!selection.isEmpty) {
  91. // Convert to 1-based line numbers
  92. const startLine = selection.start.line + 1
  93. const endLine = selection.end.line + 1
  94. if (startLine === endLine) {
  95. // Single line selection
  96. filepathWithAt += `#L${startLine}`
  97. } else {
  98. // Multi-line selection
  99. filepathWithAt += `#L${startLine}-${endLine}`
  100. }
  101. }
  102. return filepathWithAt
  103. }
  104. }