cbochs/portal.nvim

github github
pluginmotion
star 119
alert-circle 4
users 2
git-branch 3
CREATED

2022-10-31

UPDATED

7 days ago


Portal.nvim

Look at you, sailing through the [jumplist] majestically, like an eagle... piloting a blimp.


portal_jump mov

Theme: catppuccin

Introduction

Portal is a plugin that aims to build upon and enhance existing jumplist motions (i.e. <c-o> and <c-i>) by surfacing contextual information with the use of portals, and providing multiple jump options by means of queries.

To get started, install the plugin using your preferred package manager, setup the plugin, add the suggested keybindings for portals and tagging, and give it a go! Default settings for the plugin can be found in the settings section below

Features

  • Contextual jumping with portals to view available jump locations
  • Customizable jump queries to allow you to go anywhere you'd like in the jumplist
  • Persistent jump tags to flag important file you want to be able to get back to
  • Integration with grapple.nvim and harpoon to provide additional queries

Requirements

Installation

packer

use {
    "cbochs/portal.nvim",
    requires = {
        "cbochs/grapple.nvim",  -- Optional: provides the "grapple" query item
        "ThePrimeagen/harpoon", -- Optional: provides the "harpoon" query item
    },
}

Plug

Plug "cbochs/portal.nvim"

Default Settings

The following are the default settings for Portal. Setup is not required, but settings may be overridden by passing them as table arguments to the portal#setup function.

require("portal").setup({
    ---@type "debug" | "info" | "warn" | "error"
    log_level = "warn",

    ---The default queries used when searching the jumplist. An entry can
    ---be a name of a registered query item, an anonymous predicate, or
    ---a well-formed query item. See Queries section for more information.
    ---@type Portal.QueryLike[]
    query = { "modified", "different", "valid" },

    ---An ordered list of keys that will be used for labelling available jumps.
    ---Labels will be applied in same order as `query`.
    ---@type string[]
    labels = { "j", "k", "h", "l" },

    ---Keys used for exiting portal selection. To disable a key, set its value
    ---to `nil` or `false`.
    ---@type table<string, boolean | nil>
    escape = {
        ["<esc>"] = true,
    },

    ---The jumplist is fixed at 100 items, which has the possibility to impact
    ---portal performance. Set this to a value less than 100 to limit the number
    ---of jumps in the jumplist that will be queried.
    lookback = 100,

    portal = {
        title = {
            --- When a portal is empty, render an default portal title
            render_empty = true,

            --- The raw window options used for the portal title window
            options = {
                relative = "cursor",
                width = 80,
                height = 1,
                col = 2,
                style = "minimal",
                focusable = false,
                border = "single",
                noautocmd = true,
                zindex = 98,
            },
        },

        body = {
            -- When a portal is empty, render an empty buffer body
            render_empty = false,

            --- The raw window options used for the portal body window
            options = {
                relative = "cursor",
                width = 80,
                height = 3,
                col = 2,
                focusable = false,
                border = "single",
                noautocmd = true,
                zindex = 99,
            },
        },
    },
})

Portals

A portal is a window that displays the jump location, the label required to get to that jump location, and any addition contextual information (i.e. the jump's file name or matched query).

Suggested Keymaps

vim.keymap.set("n", "<leader>o", require("portal").jump_backward, {})
vim.keymap.set("n", "<leader>i", require("portal").jump_forward, {})

Queries

A query is a list of query items which are used to identify specifc jump locations in the jumplist. Each query item will attempt to match with a jump location based on a given criteria.

For example, a query of { "modified", "different" } will attempt to find two jump locations. The first is where a jump's buffer has been modified. The second is where a jump's buffer is different than the current buffer.

local query = { "modified", "different" }

-- A query can be used in the context of jumping and passed in as an option
-- or during setup
require("portal").jump_forward({ query = query })

-- A list of query-like items must be resolved into proper Portal.QueryItem's
local resolved_query = require("portal.query").resolve(query)

-- A search can be explicitly searched for, returning a list of Portal.Jump.
-- Invalid jumps will have their direction field set to types.direction.none
local available_jumps = require("portal.jump").search(query)

Available Query Items

All registered query items are available as table values from query. For example, the query item for "valid" would be:

require("portal.query").valid

valid

Matches jumps that have a valid buffer (see: :h nvim_buf_is_valid).

different

Matches jumps that have a buffer different than the current buffer.

modified

Matched jumps that are in a modified buffer (see :h 'modified').

custom

See how to create your own custom query items and available integrations for more information.

Custom Query Items

A query item found in the settings is in fact a "query-like" item. It may be either a string, Portal.Predicate, or Portal.QueryItem. A string may be used to specify a query item that has been registered. To register a query, use query.register and pass in a key, predicate, and optional name and name_short.

Registering query items

---Define the predicate
---@param jump Portal.Jump
---@return boolean
local function is_listed(jump)
    return require("portal.query").valid(jump)
        and vim.fn.buflisted(jump.buffer)
end

-- Register the predicate with an associated key
require("portal.query").register("listed", is_listed, {
    name = "Listed",
    name_short = "L",
})

-- Use the registered query item
require("portal").jump_backward({
    query = { "listed" }
})

Anonymous query items

Anonymous query items may also be used instead of explicitly registering a query item.

require("portal").jump_backward({
    query = {
        -- A query item may be an unnamed Portal.Predicate
        function(jump) ... end,

        -- A query item may be a well-formed, but unregistered, Portal.QueryItem
        {
            predicate = function(jump) ... end,
            type = "{type}" -- synomymous with a query item's "key"
            name = "{name}",
            name_short = "{name_short}"
        }
    }
})

Previewer

todo!(cbochs)

Highlight Groups

A number of highlight groups are available to let you style your portals:

PortalBorder

The default window border placed around any open portal.

Default: FloatBorder

PortalBorderBackward

The window border placed around an open portal when the jump direction is backward.

Default: PortalBorder

PortalBorderForward

The window border placed around an open portal when the jump direction is forward.

Default: PortalBorder

PortalBorderNone

The window border placed around an open portal when the jump direction is forward.

Default: PortalBorder

PortalLabel

The label (extmark) placed next to a portal jump location.

Default: { bg = "#a6e3a1", fg = "#1e1e2e" }

Example

-- Give window borders a "portal" feel
vim.api.nvim_set_hl(0, "PortalBorder", { fg = "#fab387" })
vim.api.nvim_set_hl(0, "PortalBorderNone", { fg = "#89b4fa" })

Integrations

The following additional queries are automatically registered if their associated plugin is found.

grapple.nvim

Query item: "grapple"

Matches jumps that are in a buffer tagged by grapple.nvim.

Usage

require("portal").setup({
    query = { "grapple", ... }
})

Jump to the first tagged buffer in the jumplist

Use Portal and Grapple to jump directly to the first tagged buffer navigating backwards in the jumplist, without opening any portals.

local query = require("portal.query").resolve({ "grapple" })
local jumps = require("portal.jump").search(query, "backward")
require("portal.jump").select(jumps[1])

harpoon

Query item: "harpoon"

Matches jumps that are in a buffer marked by harpoon.

Usage

require("portal").setup({
    query = { "harpoon", ... }
})

Inspiration