opencode.nvim

repository·main·Indexed 25 days ago

https://github.com/nickjvandyke/opencode.nvim

A Neovim plugin that integrates with the OpenCode AI service to provide AI pair programming directly within the editor. It features context placeholders (e.g., @this, @buffer, @diagnostics), built-in prompts for tasks like fixing and optimizing code, and support for managing AI-requested file edits via :diffpatch. The plugin integrates with snacks.nvim, blink.cmp, and lualine.nvim, and can automatically start the required opencode server binary.

Tokens
2.9K
Snippets
10
Records
15
Agent score
37%

What's inside opencode.nvim

  1. Manage OpenCode Edit Permissions and Diffpatch

    main

    When OpenCode requests a file edit, opencode.nvim opens the target file in a new tab using Neovim's :diffpatch to show changes side-by-side.

    Keymaps for managing edits:

    KeymapAction
    daAccept the entire edit request
    drReject the entire edit request
    ]c / [cNext / previous change
    dpNatively accept only the hunk under the cursor, and reject the edit request
    doNatively reject only the hunk under the cursor, and reject the edit request
    qClose the diff
  2. Install opencode.nvim

    main

    You can install opencode.nvim using vim.pack (recommended), lazy.nvim, or nixvim.

    Important: After installation, run :checkhealth opencode to verify your setup.

    -- Using vim.pack
    vim.pack.add({
      {
        src = "https://github.com/nickjvandyke/opencode.nvim",
        version = vim.version.range("*"), -- Latest stable release
      },
    })
    
    ---@type opencode.Opts
    vim.g.opencode_opts = {
      -- Your configuration, if any
    }
  3. Configure opencode.nvim keymaps

    main

    The following keymaps are recommended for common interactions like asking questions, selecting prompts, and controlling the OpenCode session.

    -- Recommended/example keymaps
    vim.keymap.set({ "n", "x" }, "<C-a>",   function() require("opencode").ask("@this: ") end,                    { desc = "Ask OpenCode…" })
    vim.keymap.set({ "n", "x" }, "<C-x>",   function() require("opencode").select() end,                          { desc = "Select OpenCode…" })
    vim.keymap.set({ "n", "x" }, "go",      function() return require("opencode").operator("@this ") end,         { desc = "Append range to OpenCode", expr = true })
    vim.keymap.set({ "n" },      "goo",     function() return require("opencode").operator("@this ") .. "_" end,  { desc = "Append line to OpenCode", expr = true })
    vim.keymap.set({ "n" },      "<S-C-u>", function() require("opencode").command("session.half.page.up") end,   { desc = "Scroll OpenCode up" })
    vim.keymap.set({ "n" },      "<S-C-d>", function() require("opencode").command("session.half.page.down") end, { desc = "Scroll OpenCode down" })
  4. Configure the OpenCode Server

    main

    opencode.nvim connects to an OpenCode server. You can point to a specific server via vim.g.opencode_opts.server.url.

    Requirements:

    • You must run the opencode binary with the --port flag to expose its server.

    Automatic Startup: If no server is found, opencode.nvim can start one automatically using vim.g.opencode_opts.server.start. By default, it uses term://opencode --port.

  5. Integrate opencode.nvim with snacks.nvim

    main

    You can enhance opencode.nvim by integrating it with snacks.nvim to improve the Ask and Select interfaces. This example shows how to create a custom action in the snacks.picker to send selected items to an OpenCode prompt.

    require("snacks").setup({
      input = {
        enabled = true, -- Enhances Ask
      },
      picker = {
        enabled = true, -- Enhances Select
        win = {
          input = {
            keys = {
              ["<a-o>"] = { "opencode_send", mode = { "n", "i" } },
            },
          },
        },
        actions = {
          opencode_send = function(picker) ---@param picker snacks.Picker
            local items = vim.tbl_map(function(item) ---@param item snacks.picker.Item
              return item.file
                and require("opencode").format({ path = item.file, from = item.pos, to = item.end_pos })
                or item.text
            end, picker:selected({ fallback = true }))
    
            require("opencode").prompt(table.concat(items, ", ") .. " ")
          end,
        },
      },
    })
  6. Integrate opencode.nvim with lualine.nvim

    main

    Display the currently connected OpenCode server and its status in your statusline using lualine.nvim.

    require("lualine").setup({
      sections = {
        lualine_z = {
          {
            -- Show the currently connected server and its status
            require("opencode").statusline,
          },
        }
      }
    })
  7. Integrate opencode.nvim with blink.cmp

    main

    To show completions from opencode.nvim's in-process LSP within the Ask interface, configure blink.cmp to include the lsp source for the opencode_ask filetype. This requires snacks.input to be enabled.

    -- Configure blink.cmp to show completions in Ask from opencode.nvim's in-process LSP.
    -- Only applicable when using snacks.input.
    require("blink.cmp").setup({
      sources = {
        -- Either enable LSP (and optionally buffer) source globally
        default = { 'lsp', 'buffer' },
        -- Or only for Ask
        per_filetype = {
          opencode_ask = { 'lsp', 'buffer' },
        },
        -- Display buffer completions (if included above) when no LSP completions are available
        providers = { lsp = { fallbacks = {} } },
      },
    })
  8. Use `require("opencode").ask()` to input prompts

    main

    Use ask() to input a prompt for OpenCode. This method:

    • Passes the text to Prompt.
    • Allows browsing recent asks with <Up>.
    • Supports highlighting and completing contexts and OpenCode subagents using <Tab> (requires snacks.input).
    require("opencode").ask()
  9. Handle OpenCode Server-Sent-Events via `OpencodeEvent` autocmd

    main

    The plugin forwards connected OpenCode Server-Sent-Events as OpencodeEvent user autocmds. You can filter these by pattern (e.g., OpencodeEvent:*) to react to session status updates or other events.

    Data provided in args.data includes:

    • event: An opencode.server.Event object.
    • url: The event URL.
    -- Handle OpenCode events
    vim.api.nvim_create_autocmd("User", {
      pattern = "OpencodeEvent:*", -- Optionally filter event types
      callback = function(args)
        ---@type opencode.server.Event
        local event = args.data.event
        ---@type string
        local url = args.data.url
    
        -- See the available event types and their properties
        vim.notify(vim.inspect(event))
        -- Do something useful
        if event.type == "session.status" then
          vim.notify("OpenCode status updated: " .. event.properties.status.type)
        end
      end,
    })
  10. Use `require("opencode").prompt()` to prompt OpenCode

    main

    Use prompt() to send a prompt to OpenCode. This method:

    • Injects configured contexts.
    • Appends a trailing space if you add one.
    • Opens in ask() mode if the prompt ends with ....
    • Interprets references to files or subagents.
    require("opencode").prompt()