overseer.nvim

repository·master·Indexed 23 days ago

https://github.com/stevearc/overseer.nvim

A task runner and job management plugin for Neovim that allows users to run, manage, and automate complex workflows through a customizable UI and API. It features built-in support for frameworks like make, npm, cargo, and .vscode/tasks.json, and utilizes an Entity Component System (ECS) architecture to attach custom logic to tasks, such as output parsing, automatic restarts on save, and integration with vim.diagnostic, quickfix, and nvim-dap.

Tokens
24.4K
Snippets
53
Records
90
Agent score
83%

What's inside overseer.nvim

  1. Overview of overseer.nvim features

    master

    overseer.nvim is a task runner and job management plugin for Neovim. Key features include:

    • Built-in support for frameworks like make, npm, cargo, and .vscode/tasks.json.
    • Integration with vim.diagnostic and quickfix.
    • UI for viewing and managing tasks.
    • Customizability: Attach custom logic to tasks and define complex multi-stage workflows.
    • DAP Support: Supports preLaunchTask when used with nvim-dap.
  2. Use Component Aliases

    master

    A component alias is a string that resolves to a list of components.

    • default: The standard group of components used for all tasks if none are specified.
    • default_vscode: The standard group for VS Code task integration.

    You can define your own aliases in setup({ component_aliases = { ... } }).

    Important: When adding components to a task, if a component already exists, adding it again is a no-op. To override parameters of a component within an alias, list your specific component before the alias.

    local task = require("overseer").new_task({
        cmd = "g++ " .. vim.fn.expand("%"),
        components = {
            -- Add on_complete_notify first with customized 'statuses' parameter
            { "on_complete_notify", statuses = { "SUCCESS" } },
            -- The default group also adds on_complete_notify, but it will be ignored because it's second
            "default"
        }
    })
  3. Run tasks sequentially

    master

    There are two primary ways to handle task sequencing in Overseer:

    1. Using the dependencies component

    Add a dependencies component to a task. Setting sequential = true ensures the listed tasks run one after another before the main task proceeds.

    2. Using the orchestrator strategy

    Define a task with the orchestrator strategy. This creates a single orchestration task that manages a list of tasks (which can be strings or task objects) in the specified order. This is useful for complex workflows (e.g., Clean $\rightarrow$ Build $\rightarrow$ Serve).

    Note: You can also use VS Code's .vscode/tasks.json dependsOn keyword, which Overseer will automatically translate into one of these methods.

    -- Method 1: dependencies component
    overseer.run_task({ name = "npm serve", autostart = false }, function(task)
      if task then
        task:add_component({
          "dependencies",
          tasks = {
            "npm build",
            { cmd = "sleep 10" },
          },
          sequential = true,
        })
        task:start()
      end
    end)
    
    -- Method 2: orchestrator strategy
    local task = overseer.new_task({
      name = "Build and serve app",
      strategy = {
        "orchestrator",
        tasks = {
          "make clean",
          {
            "npm build",
            { cmd = { "lessc", "styles.less", "styles.css" },
          },
          "npm serve",
        },
      },
    })
    task:start()
  4. Understand the Task Result structure

    master

    The Task result is a table used by components and actions. While there is no strict schema, two specific keys have built-in functionality:

    • diagnostics: A list of quickfix items (compatible with :help setqflist) used for displaying errors/warnings.
    • error: Used to store internal Overseer errors encountered while running the task.
  5. How tasks and components work in Overseer

    master

    Overseer uses an Entity Component System (ECS) architecture to manage jobs:

    • Tasks: Represent a single command being run. They can be managed (start/stop/restart/edit/open terminal) via the task list. You can create them via the new_task() method or, more commonly, via Templates.
    • Components: These add functionality to a task. By default, a task only runs a command. Components can be added to parse output, show notifications upon completion, or trigger re-runs when files change. Components are designed to be easily removed, customized, or replaced.

    To customize behavior, you should use components rather than modifying the core task logic.

  6. Generate dynamic tasks with Template Providers

    master

    Template providers allow you to generate multiple templates at runtime (e.g., generating a task for every target in a Makefile). They are defined similarly to templates but use a generator function instead of a builder.

    • Synchronous: The generator returns a list of tasks.
    • Asynchronous: Use the callback argument in the generator to perform async work (like vim.system) and return results via the callback.
    • Caching: Use cache_key to provide a function that returns a key (like a config file path). Overseer will cache the results and clear them when the file changes.
    • Conditions: Use condition to restrict when the provider is active (e.g., specific filetypes).
    ---@type overseer.TemplateFileProvider
    return {
      generator = function(search, callback)
        do_some_work(function(err)
          if err then
            callback(err)
            return
          end
          callback({...})
        end)
      end,
      condition = {
        filetype = { "c" },
      },
      cache_key = function(opts)
        return vim.fs.find("Makefile", { upward = true, type = "file", path = opts.dir })[1]
      end,
    }
  7. What are Overseer strategies?

    master
    A strategy defines how a task is executed. The default strategy is terminal, which passes the task cmd to vim.fn.termopen(). Strategies can be used as drop-in replacements for terminal with different features (like jobstart) or can fundamentally change task behavior (like orchestrator).
  8. Implement a complex custom OutputParser

    master

    For advanced parsing requirements that a simple function cannot handle, implement the overseer.OutputParser interface. An OutputParser requires the following methods:

    • parse(line): Processes a single line of output.
    • get_result(): Returns a table of results. To interact with diagnostics-related components, ensure your table includes a diagnostics key containing the list of parsed entries.
    • reset(): Resets the parser's internal state (e.g., clearing the results list).
    local parser = {
      _result = {},
      ---Parse a single line of output
      ---@param line string
      parse = function(self, line)
        local fname, lnum, msg = line:match("^(.*):(%d+): (.*)$")
        if fname
          table.insert(self._result, {
            filename = fname,
            lnum = tonumber(lnum),
            text = msg
          })
        end
      end,
      ---Get the results for the task
      ---@return table<string, any>
      get_result = function(self)
        -- The task result is an arbitrary key-value table, but most of the time for output parsing
        -- you will want to set the `diagnostics` key. This is the special key that interacts with
        -- all the diagnostics-related components.
        -- Note that the other parser types (function, problem matcher, errorformat) automatically put
        -- their results in the `diagnostics` key.
        return { diagnostics = self._result }
      end,
      ---This is called when the task is reset
      reset = function(self)
        self._result = {}
      end,
    }
  9. How templates are used to define tasks

    master
    Templates are the primary way to define tasks in Overseer. They provide the instructions for constructing a task along with metadata used for selection and starting. When you use the :OverseerRun command, the available options are populated from your defined templates. Use templates instead of manual new_task() calls for most use cases to ensure tasks are easily discoverable and repeatable.
  10. Edit task components using the Task Editor

    master

    The task editor allows you to manually tweak or experiment with a task's components on the fly.

    How to use it:

    1. Open the task list.
    2. Move to the desired task and press <CR>.
    3. Select the edit action.

    Editing workflow:

    • Values: Edit values like a normal buffer.
    • Enums: Use omnicomplete (<C-x><C-o>) to autocomplete possible values.
    • Delete a component: Delete the component's name (e.g., using dd).
    • Add a component: Create a new blank line (e.g., using o).
    • Save: Use :w to confirm and submit your changes.
  11. Use VS Code tasks.json in Overseer

    master

    Overseer can read and execute tasks defined in .vscode/tasks.json. When you run :OverseerRun, these tasks will appear in the list.

    Supported Features:

    • Task types: process, shell, typescript, node.
    • Standard and Input variables (e.g., ${input:variableID}).
    • Problem matchers (including built-in library like $tsc, $jshint-stylish).
    • Compound tasks (including dependsOrder = sequence).
    • Background tasks.
    • group (supports BUILD, RUN, TEST, CLEAN) and isDefault.
    • Integration with launch.json via DAP.

    Unsupported Features:

    • Task types: gulp, grunt, jake.
    • Custom shell specification.
    • Specific variables: ${workspacefolder:*}, ${config:*}, ${command:*}, ${defaultBuildTask}.
    • Certain regex behaviors in custom problem matchers due to differences between JS and Vim regex.
  12. Manage tasks using the Task List

    master

    The task list displays all created tasks, including their status, name, and a summary of output.

    Commands:

    • :OverseerOpen: Open the task list.
    • :OverseerClose: Close the task list.
    • :OverseerToggle: Toggle the task list visibility.

    Interactions within the list:

    • ?: Show a list of all available keybindings.
    • <CR> (Enter): Open a menu of available actions for the selected task.

    Customization: You can customize the display of the task list by providing a custom task_list.render function in your overseer.setup() configuration.