jedrzejboczar/toggletasks.nvim

github github
code-runner
star 37
stars
alert-circle 0
open issues
users 2
subscribers
git-branch 0
forks
CREATED

2022-05-05

UPDATED

14 days ago


Lint

toggletasks.nvim

Neovim task runner: JSON/YAML + toggleterm.nvim + telescope.nvim.

Features

  • Define task via JSON, similarly to VS Code Tasks
  • Support for YAML-based configuration using lyaml
  • Collect configs from multiple directories: global/tab/win CWD, LSP root dir
  • Run tasks in terminals managed by toggleterm
  • Use telescope to spawn single/multiple tasks
  • Filter tasks based on #tags defined in config files
  • Use telescope to view/open/kill tasks
  • Automatic spawning on e.g. SessionLoadPost (see Automatic task spawning)

usage video example

Overview

The main idea behind this plugin is to be able to easily define build/setup commands for different projects, independently of your global editor configuration and to easily manage multiple background tasks.

This task management plugin is heavily inspired by plugins such as yabs.nvim and projectlaunch.nvim, as well as by VS Code Tasks. In fact, initially I planned to just extend projectlaunch.nvim but after some work I decided that it would require to much changes and it will be easier to write a separate plugin.

The main difference between toggletasks.nvim and other plugins is that toggletasks strictly integrates with existing solutions instead of writing things from scratch, i.e.

Other differences:

  • yabs.nvim (currently, see blog post) sources arbitrary Lua files; toggletasks uses only JSON which is much safer, though less powerfull
  • yabs.nvim has the concept of different outputs/runners; toggletasks always uses toggleterm.nvim
  • projectlaunch.nvim has a dedicated UI for managing tasks and running groups of tasks; toggletasks uses telescope to achieve similar results, allowing for multi-selection when spawning/selecting tasks
  • projectlaunch.nvim has builtin runners for things like package.json/Makefile; this is not supported, but maybe something to consider in the future

Installation

Example using packer.nvim:

use {
    'jedrzejboczar/toggletasks.nvim',
    requires = {
        'nvim-lua/plenary.nvim',
        'akinsho/toggleterm.nvim',
        'nvim-telescope/telescope.nvim/',
    },
    -- To enable YAML config support
    rocks = 'lyaml',
}

Configuration

Run require('toggletasks').setup { ... } in your init.lua to configure this plugin. Available options (with default values):

require('toggletasks').setup {
    debug = false,
    silent = false,  -- don't show "info" messages
    short_paths = true,  -- display relative paths when possible
    -- Paths (without extension) to task configuration files (relative to scanned directory)
    -- All supported extensions will be tested, e.g. '.toggletasks.json', '.toggletasks.yaml'
    search_paths = {
        'toggletasks',
        '.toggletasks',
        '.nvim/toggletasks',
    },
    -- Directories to consider when searching for available tasks for current window
    scan = {
        global_cwd = true,  -- vim.fn.getcwd(-1, -1)
        tab_cwd = true,     -- vim.fn.getcwd(-1, tab)
        win_cwd = true,     -- vim.fn.getcwd(win)
        lsp_root = true,    -- root_dir for first LSP available for the buffer
        dirs = {},          -- explicit list of directories to search
    },
    -- Language server priorities when selecting lsp_root (default is 0)
    lsp_priorities = {
        ['null-ls'] = -10,
    },
    -- Default values for task configuration options (available options described later)
    defaults = {
        close_on_exit = false,
        hidden = true,
    },
    -- Configuration of telescope pickers
    telescope = {
        spawn = {
            open_single = true,  -- auto-open terminal window when spawning a single task
            show_running = false, -- include already running tasks in picker candidates
            -- Replaces default select_* actions to spawn task (and change toggleterm
            -- direction for select horiz/vert/tab)
            mappings = {
                select_float = '<C-f>',
                spawn_smart = '<C-a>',  -- all if no entries selected, else use multi-select
                spawn_all = '<M-a>',    -- all visible entries
                spawn_selected = nil,   -- entries selected via multi-select (default <tab>)
            },
        },
        -- Replaces default select_* actions to open task terminal (and change toggleterm
        -- direction for select horiz/vert/tab)
        select = {
            mappings = {
                select_float = '<C-f>',
                open_smart = '<C-a>',
                open_all = '<M-a>',
                open_selected = nil,
                kill_smart = '<C-q>',
                kill_all = '<M-q>',
                kill_selected = nil,
                respawn_smart = '<C-s>',
                respawn_all = '<M-s>',
                respawn_selected = nil,
            },
        },
    },
}

To load telescope pickers:

require('telescope').load_extension('toggletasks')

Config format

JSON configuration files are supported out-of-the-box via vim.json module. To enable YAML support, lyaml must be installed. It is possible to use packer to install luarocks, see Installation.

Available fields:

Field Type Description
name string descriptive name for the task; pair (name, config_file) is used to uniquely identify a task (to kill existing if re-running or to filter out already running tasks in picker)
id string? Optional ID to use instead of the default pair (name, config_file)
cmd string Command to run
cwd string? Task working directory
tags table<string>? Tags used to group and filter tasks
env table<string, string>? Additional environmental variables passed to task
clear_env boolean? If set to true, only environmental variables from env will be passed to task
close_on_exit boolean? Auto-close terminal when task job exists (see toggleterm)
hidden boolean? Don't include this task in toggleterm tasks list (see toggleterm)
count number? Use given terminal number (see toggleterm)

Variable expansion is supported using the syntax ${VAR} (escaped by double $, e.g. $${VAR} will expand to ${VAR}).

Environmental variables will be expanded in fields: cwd, env. Additionally some special variables will be expanded in fields: cmd, cwd, env. Available special variables (snake case to minimize collisions with env):

  • ${config_dir} - location of the config file from which the task has been loaded
  • ${lsp_root} - root_dir of a language server with highest priority for current buffer
  • ${win_cwd} - Vim's window-local CWD
  • ${tab_cwd} - Vim's tab-local CWD
  • ${global_cwd} - Vim's global CWD
  • ${file} - absolute path to the current buffer's file

Vim filename-modifiers can be used inside the expansion to modify the paths (by default all paths are absoulte), e.g. ${file:t:r} will transform /path/to/my-file.txt into my-file.

JSON configuration example file:

{
    "tasks": [
        {
            "name": "Echo example",
            "cmd": "echo 'Current file = ${file}'"
        },
        {
            "name": "System logs",
            "cmd": "journalctl -b --follow",
            "tags": ["dev"]
        },
        {
            "name": "Makefile build",
            "cmd": "make -j",
            "cwd": "${config_dir}",
            "tags": ["build", "make"]
        },
        {
            "name": "CMake setup",
            "cmd": "mkdir -p build && cd build && cmake ..",
            "cwd": "${config_dir}",
            "tags": ["cmake"]
        },
        {
            "name": "CMake build",
            "cmd": "cmake --build build -j",
            "cwd": "${config_dir}",
            "tags": ["build", "cmake"]
        },
        {
            "name": "django runserver",
            "cmd": "python manage.py runserver",
            "cwd": "${config_dir}",
            "env": {
                "PATH": "${config_dir}/venv/bin:${PATH}"
            },
            "tags": ["dev"]
        },
        {
            "name": "frontend",
            "cmd": "npm run serve",
            "cwd": "${config_dir}/frontend",
            "tags": ["dev"]
        }
    ]
}

YAML configuration example:

tasks:
- name: Echo example
  cmd: echo 'Current file = ${file}'
- name: django runserver
  cmd: python manage.py runserver
  cwd: ${config_dir}
  env:
    PATH: ${config_dir}/venv/bin:${PATH}
  tags:
  - dev

Usage

To use this plugin use the included telescope pickers:

  • spawn tasks: Telescope toggletasks spawn
  • select running tasks (open/kill/respawn): Telescope toggletasks select
  • edit config files: Telescope toggletasks edit

These commands can be mapped to keybindings, e.g.

vim.keymap.set('n', '<space>ts', require('telescope').extensions.toggletasks.spawn,
    { desc = 'toggletasks: spawn' })

When selecting tasks to be spawned, one can just type #tagname in the prompt to filter based on tag. This is currently using the default string based matching but should work correctly in most cases. To select all tasks that are currently visible press <C-a> (default). To manually pick tasks use <Tab>/<S-Tab> (telescope defaults) to perform multi-selection and press <C-a> to spawn selected tasks.

Commands

The following commands are available:

  • ToggleTasksInfo - show current configuration
  • ToggleTasksConvert <from_file> <to_file> - convert between configuration file formats (by file extension)

Automatic task spawning

It is possible to automatically launch tasks on autocmd events, e.g. to launch tasks on VimEnter or SessionLoadPost. This plugin exposes convenient function to achieve that.

For example, to launch all tasks marked with the auto tag whenever a session is loaded use:

require('toggletasks').auto_spawn('SessionLoadPost', 'auto')

The first argument (event) is the same as for vim.api.nvim_create_autocmd. For more fine grained auto_spawn can take a function as the second argument:

require('toggletasks').auto_spawn({'VimEnter', 'SessionLoadPost'}, function(tasks)
    return tasks
        :with_tag('auto')
        :not_tag('test')
        :from_file('/some/path/toggletasks.json')
        :name_matches('^/some/path.*$')
        :filter(function(task)
            return task.config.name ~= 'Hello world'
        end)
end)

TODO

  • Integration with possession.nvim by marking tasks with possession tag - no changes required in this plugin
  • Option to define some tasks from Lua in setup
  • Task "templates": one task could inherit options from another ("extends")