Repository: axieax/urlview.nvim
Branch: main
Commit: 813736e6891b
Files: 31
Total size: 76.4 KB
Directory structure:
gitextract_1w34gv51/
├── .github/
│ └── workflows/
│ └── default.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── doc/
│ └── urlview.txt
├── lua/
│ └── urlview/
│ ├── actions.lua
│ ├── command.lua
│ ├── config/
│ │ ├── constants.lua
│ │ ├── default.lua
│ │ ├── helpers.lua
│ │ └── init.lua
│ ├── init.lua
│ ├── jump.lua
│ ├── pickers.lua
│ ├── search/
│ │ ├── helpers.lua
│ │ ├── init.lua
│ │ └── validation.lua
│ └── utils.lua
├── selene.toml
├── stylua.toml
├── tests/
│ ├── init.vim
│ └── urlview/
│ ├── capture_custom_searches_spec.lua
│ ├── capture_jump_spec.lua
│ ├── capture_mock_spec.lua
│ ├── capture_multiple_spec.lua
│ ├── capture_process_spec.lua
│ ├── capture_single_spec.lua
│ ├── helpers.lua
│ └── jump_helpers.lua
└── vim.toml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/default.yml
================================================
name: default
on:
pull_request:
push:
branches: [main]
jobs:
stylua:
name: Check code style
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: JohnnyMorganz/stylua-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --color always --check .
selene:
name: Lint code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: NTBBloodbath/selene-action@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --color always .
test:
name: Neovim test runner
runs-on: ubuntu-latest
strategy:
matrix:
neovim-version: [v0.9.0, v0.8.0, v0.7.0, stable, nightly]
steps:
- uses: actions/checkout@v2
with:
path: urlview.nvim
- uses: actions/checkout@v2
with:
repository: nvim-lua/plenary.nvim
path: plenary.nvim
- uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.neovim-version }}
- run: make test
working-directory: urlview.nvim
timeout-minutes: 1
================================================
FILE: .gitignore
================================================
doc/tags
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Andrew Xie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
test:
nvim --headless --noplugin -u tests/init.vim -c "PlenaryBustedDirectory tests/urlview {minimal_init = 'tests/init.vim'}"
================================================
FILE: README.md
================================================
<h1 align="center">🔎 urlview.nvim</h1>
<p align="center"><i>Find and display URLs from a variety of search contexts</i></p>
<p align="center">
<a href="https://github.com/neovim/neovim">
<img alt="Neovim Version" src="https://img.shields.io/static/v1?label=&message=%3E%3D0.7&style=for-the-badge&logo=neovim&color=green&labelColor=302D41"/>
</a>
<a href="https://github.com/axieax/urlview.nvim/stargazers">
<img alt="Repo Stars" src="https://img.shields.io/github/stars/axieax/urlview.nvim?style=for-the-badge&color=yellow&label=%E2%AD%90&labelColor=302D41"/>
</a>
<a href="https://github.com/axieax/urlview.nvim">
<img alt="Repo Size" src="https://img.shields.io/github/repo-size/axieax/urlview.nvim?label=&color=orange&logo=hackthebox&style=for-the-badge&logoColor=lightgray&labelColor=302D41"/>
</a>
</p>
✨ UrlView is an extensible plugin for the [Neovim](https://neovim.io) text editor which essentially:
1. Finds URLs from a variety of **search contexts** (e.g. from a buffer, file, plugin URLs)
2. Displays these URLs in a **picker**, such as the built-in `vim.ui.select`, [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim), or [fzf-lua](https://github.com/ibhagwan/fzf-lua).
3. Performs **actions** on selected URLs, such as navigating to the URL in your preferred browser, or copying the link to your clipboard
🎯 Additional features and example use cases include:
- Easily visualise all the URLs in a buffer or file (e.g. links in your Markdown documents)
- Quickly accessing repo webpages for installed Neovim plugins (life-saver for config updates or browsing plugin documentation)
- Ability to register custom searchers (e.g. Jira ticket numbers), pickers and actions (please see [docs](doc/urlview.txt) or `:h urlview.search-custom`)
- Jumping to the previous or next URL in the active buffer (and opening the URL in your browser)
> Please note that currently, this plugin only detects URLs beginning with a `http(s)` or `www` prefix for buffer and file search, but there are plans to support a more general pattern (see [🗺️ Roadmap](https://github.com/axieax/urlview.nvim/issues/3)).
## 📸 Screenshots
### 📋 Buffer Links
`:UrlView` or `:UrlView buffer`

### 🔌 Plugin Links
`:UrlView lazy`, `:UrlView packer`, or `:UrlView vimplug` depending on your plugin manager of choice

## ⚡ Requirements
- This plugin supports **Neovim v0.7** or later.
- Please find the appropriate _\*-compat_ Git tag if you need legacy support for previous Neovim versions, such as [v0.6-compat](https://github.com/axieax/urlview.nvim/tree/v0.6-compat) for nvim v0.6, although these versions will no longer receive any new updates or features.
- For Neovim versions prior to v0.6 or Vanilla [Vim](https://www.vim.org/) support, please check out [urlview.vim](https://github.com/strboul/urlview.vim) as an alternative plugin.
## 🚀 Usage
### Searching contexts
1. Use the command `:UrlView` to see all the URLs in the current buffer.
- For your convenience, feel free to setup a keybind for this using `vim.keymap.set`:
```lua
vim.keymap.set("n", "\\u", "<Cmd>UrlView<CR>", { desc = "View buffer URLs" })
vim.keymap.set("n", "\\U", "<Cmd>UrlView packer<CR>", { desc = "View Packer plugin URLs" })
```
- You can also hit `:UrlView <tab>` to see additional contexts that you can search from
- e.g. `:UrlView packer` to view links for installed [packer.nvim](https://github.com/wbthomason/packer.nvim) plugins
2. You can optionally select a link to bring it up in your browser.
### Buffer URL navigation
1. You can use `[u` and `]u` (default bindings) to jump to the previous and next URL in the buffer respectively.
2. If desired, you can now press `gx` to open the URL under the cursor in your browser, with netrw.
3. This keymap can be altered under the `jump` config option.
## 📦 Installation
Install this plugin with your package manager of choice. You can lazy load this plugin by the `UrlView` command if desired.
- [packer.nvim](https://github.com/wbthomason/packer.nvim)
```lua
use("axieax/urlview.nvim")
```
- [lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
"axieax/urlview.nvim"
```
## ⚙️ Configuration
This plugin supports plug-n-play, meaning you can get it up and running without any additional setup.
However, you can customise the [default options](lua/urlview/config/default.lua) using the `setup` function:
```lua
require("urlview").setup({
-- custom configuration options --
})
```
Please check out the [documentation](doc/urlview.txt) for configuration options and details.
## 🎨 Pickers
### ✔️ Native (vim.ui.select)
You can customise the appearance of `vim.ui.select` with plugins such as [dressing.nvim](https://github.com/stevearc/dressing.nvim) and [telescope-ui-select.nvim](https://github.com/nvim-telescope/telescope-ui-select.nvim). In the demo images above, I used [dressing.nvim](https://github.com/stevearc/dressing.nvim)'s Telescope option, which allows me to further filter and fuzzy search through my entries.
### 🔭 Telescope
- Optional picker option
- Additional requirements (only if you're using this picker): [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim)
- You can use Telescope as your `default_picker` using the `require("urlview").setup` function
- Alternatively, you can specify a picker dynamically with `:UrlView <ctx> picker=telescope`
### 📑 fzf-lua
- Optional picker option
- Additional requirements (only if you're using this picker): [fzf-lua](https://github.com/ibhagwan/fzf-lua)
- You can use fzf-lua as your `default_picker` using the `require("urlview").setup` function
- Alternatively, you can specify a picker dynamically with `:UrlView <ctx> picker=fzf_lua`
## 🚧 Stay Updated
More features are continually being added to this plugin (see [🗺️ Roadmap](https://github.com/axieax/urlview.nvim/issues/3)). Feel free to file an issue or create a PR for any features / fixes :)
It is recommended to subscribe to the [🙉 Breaking Changes](https://github.com/axieax/urlview.nvim/issues/37) thread to be updated on potentially breaking changes to this plugin, as well as resolution strategies.
================================================
FILE: doc/urlview.txt
================================================
*urlview.txt* Find and display URLs from a variety of search contexts
================================================================================
Table of Contents *urlview.contents*
INTRODUCTION .......................................... |urlview|
CONFIGURATION ......................................... |urlview.config|
USAGE ................................................. |urlview.usage|
SEARCH ................................................ |urlview.search|
PICKERS ............................................... |urlview.pickers|
ACTIONS ............................................... |urlview.actions|
================================================================================
INTRODUCTION *urlview*
✨ urlview.nvim is essentially a plugin which:
1. Finds URLs from a variety of |urlview.search| contexts
2. Displays these URLs in one of the |urlview.pickers|
3. Performs |urlview.actions| on selected URLs from the pickers
================================================================================
CONFIGURATION *urlview.config*
urlview.nvim supports plug-n-play, meaning the default config is automatically
set up. However, the default options can be configured using the
`require("urlview").setup` function, with the configuration options below.
*urlview.config-default_title*
{default_title} string (default: "Links:")
Forms part of the prompt title for the picker (`<context> <default_title>`). For
example, for the buffer search context with a default_title of "Links:", the
picker title becomes "Buffer Links:". The capitalisation of the first letter
in the default_title determines the capitalisation of the search context in
the prompt title as well. For example, a default_title of "links" will result
in a prompt title of "buffer links".
*urlview.config-default_picker*
{default_picker} string (default: "native")
Default picker for `:UrlView` commands. This should be any one of the pickers in
|urlview.pickers|.
*urlview.config-default_prefix*
{default_prefix} string (default: "https://")
Default prefix for URLs missing a HTTP protocol (e.g. "www.google.com" becomes
"https://www.google.com"). Such a protocol is required for |urlview.actions|
to be able to navigate to URLs. Another suggested option is "http://",
although it is less secure than the default HTTPS protocol.
*urlview.config-default_action*
{default_action} string (default: "netrw")
Default action to take upon selecting a URL from a picker. This should be any
one of the actions in |urlview.actions|.
*urlview.config-default_register*
{default_register} string (default: "+")
Default register to use when yankying.
*urlview.config-default_include_branch*
{default_include_branch} boolean (default: false)
Default option for whether plugin URLs should link to the branch used by your
package manager, for |urlview.search-lazy|, |urlview.search-packer| or
|urlview.search-vimplug|. When this option is enabled, navigated links will
open the plugin's repository to the specific branch specified to your plugin
manager.
*urlview.config-unique*
{unique} boolean (default: true)
Enable to ensure links shown in the picker are unique (i.e. no duplicates).
*urlview.config-sorted*
{sorted} boolean (default: true)
Enable to ensure links shown in the picker are sorted alphabetically.
*urlview.config-log_level_min*
{log_level_min} `vim.log.levels` enum or int (default: `vim.log.levels.INFO`)
Minimum log level for output from this plugin. Lower logging levels will be
ignored. >lua
vim.log.levels = {
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3
ERROR = 4,
OFF = 5, -- Neovim v0.8+
}
<
The default value of `vim.log.levels.INFO` means that INFO, WARN and ERROR
logs will all be displayed. Similarly, a log_level_min value of
`vim.log.levels.WARN` means that only WARN and ERROR logs will be displayed.
It is recommended for this value to be at least `vim.log.levels.WARN` to
ensure warnings are appropriately logged. Setting this value to
`vim.log.levels.OFF` (requires Neovim 0.8+) or `5` will effectively suppress
all logs.
*urlview.config-jump*
{jump} table (map)
Registers keymaps for jumps to the previous or next URL in the buffer.
Fields:
{prev} string (default: "[u")
Mapping to jump to the previous URL in the buffer. Set to "" to disable.
{next} string (default: "]u")
Mapping to jump to the previous URL in the buffer. Set to "" to disable.
================================================================================
USAGE *urlview.usage*
For normal usage, interaction with this plugin is mostly achieved through the
`:UrlView` command. This command provides completion to assist with specifying
appropriate search contexts and respective options.
Calling `:UrlView` without any additional arguments is the same as calling
`:UrlView buffer`, which finds URLs in the current buffer with the default
options configured ( |default_title|, |default_picker|, |default_action|,
|unique|, |sorted|, etc. ).
The command expects arguments in the format `:UrlView <ctx> <options...>`,
e.g. >lua
:UrlView buffer bufnr=1
:UrlView file filepath=/etc/hosts picker=telescope
:UrlView packer sorted=false
<
================================================================================
SEARCH *urlview.search*
`urlview.search` provides functions which finds and extracts URLs from a
particular search context. The default search contexts can be found by the
exposed functions in the `urlview.search` module (or `urlview/search/init.lua`).
Buffer Search Context *urlview.search-buffer*
The buffer search context finds URLs in a specific buffer (current by
default). To search a particular buffer, provide the desired buffer number
with `:UrlView buffer bufnr=<bufnr>`.
File Search Context *urlview.search-file*
The file search context allows a user to find URLs in a particular file. This
requires passing in the parameter `filepath` for URLs in the desired file to be
searched, for example with `:UrlView file filepath="/etc/hosts"`.
Lazy Search Context *urlview.search-lazy*
This search context resolves Git repository URLs for plugins installed with
the lazy.nvim plugin manager. Invoked with `:UrlView lazy`.
Packer Search Context *urlview.search-packer*
This search context resolves Git repository URLs for plugins installed with
the packer.nvim plugin manager. Invoked with `:UrlView packer`.
vim-plug Search Context *urlview.search-vimplug*
This search context resolves Git repository URLs for plugins installed with
the vim-plug plugin manager. Invoked with `:UrlView vimplug`.
Registering a custom search context *urlview.search-custom*
Custom search contexts can be registered for searching with `:UrlView <ctx>`.
The `generate_custom_search` function from the `urlview.search.helpers` module
can be used to quickly generate a function for capturing a Lua pattern and
optionally formatting it with `string.format`. The following resource is very
helpful for understanding basic Lua patterns:
- https://riptutorial.com/lua/example/20315/lua-pattern-matching
This function can then be assigned to the `urlview.search` module for use as a
search context to be selected with the `:UrlView` command (with completion).
Here is an example which finds Jira ticket numbers in the format of "AXIE-"
followed by any number (capture field). This entire pattern gets captured and
can then be embedded into the format string in the format field, allowing
a user to directly navigate to the Jira ticket in their browser. >lua
local search = require("urlview.search")
local search_helpers = require("urlview.search.helpers")
search["jira"] = search_helpers.generate_custom_search({
capture = "AXIE%-%d+",
format = "https://jira.axieax.com/browse/%s",
})
This allows for captures such as "AXIE-1", "AXIE-17", "AXIE-132". Feel free to
adjust the `capture` field to suit your needs.
A custom function can also be registered for a search context, as well as
additional parameters with something like >lua
local search = require("urlview.search")
search["fruits"] = function(opts)
local fruits = { "apple", "banana", "watermelon" }
if opts.include_tomato then
table.insert(fruits, "tomato")
end
return fruits
end
Additional parameters can be passed into the custom search context using the
`:UrlView` command, for example `:UrlView fruits include_tomato` or `:UrlView
fruits include_tomato=true`. Other types also work.
Please see `urlview/search/init.lua` for more examples. If you have a useful
custom search context, feel free to share it in
https://github.com/axieax/urlview.nvim/discussions/40 for others to use (and
potentially make it a built-in search context)!
================================================================================
PICKERS *urlview.pickers*
`urlview.pickers` are used to display the results from |urlview.search|.
vim.ui.select *urlview.pickers-native*
This picker uses the built-in function `vim.ui.select` to display results. It
is recommended to use a UI-extension for `vim.ui.select` for enhanced
functionality (similar to |urlview.pickers-telescope|), including customising
popup locations and behaviour, and searching (even fuzzy-searching) through
results. An example of such a plugin is dressing.nvim
(https://github.com/stevearc/dressing.nvim).
telescope *urlview.pickers-telescope*
This picker uses the Telescope plugin
(https://github.com/nvim-telescope/telescope.nvim) as a picker to display
results. Please note that this requires installing Telescope as a plugin in
order for it to be used as a picker.
Registering a custom picker *urlview.pickers-custom*
A recommendation for registering a custom picker is to first check out a
`vim.ui.select` UI-extension such as dressing.nvim
(https://github.com/stevearc/dressing.nvim) to see if your desired picker can
be used for the native picker (e.g. nui, fzf).
In the case that you want to write your own picker, you can register it by
adding it as a function to the `urlview.pickers` module. >lua
local pickers = require("urlview.pickers")
pickers["fzf"] = function(items, opts)
-- TODO
end
<
Please check out `urlview/pickers.lua` for examples on how to do this.
================================================================================
ACTIONS *urlview.actions*
`urlview.actions` determine the behaviour when a URL is selected with one of
the |urlview.pickers|.
netrw *urlview.actions-netrw*
The `netrw` action uses the built-in netrw feature to open a given URL in your
browser. However, some users may have this feature disabled, either explicitly
or due to "netrw hijack" behaviour from file-explorer Neovim plugins. Due to
this, if urlview detects that netrw is disabled, it will use
|urlview.actions-system| as a fallback by default.
system *urlview.actions-system*
This action opens URL in your system's default browser depending on your
operating system. If your system is not supported, please raise a GitHub issue
to have it included as a built-in options. Otherwise, specify a custom action
( |urlview.actions-custom|) for your use case.
clipboard *urlview.actions-clipboard*
This action copies selected URLs to the system clipboard (specifically to
Neovim's {+} register).
Registering a custom action *urlview.actions-custom*
There are two main types of custom actions:
1. Execute a shell command which takes in your URL as an argument
This is the main use case for custom actions - specifying a browser such as
`chromium` or `firefox` to open your URL with. By default, this executes the
provided shell command and passes in the URL as an argument, such as >bash
$ chromium 'https://www.google.com'
<
which launches Google in the chromium browser. This can be any executable (be
sure to find the correct path to your desired application).
2. Lua function
With urlview.nvim's principle of extensibility in mind, you can also register a
custom action as a Lua function by adding it to the `urlview.actions` module,
like so: >lua
local actions = require("urlview.actions")
actions["spectate"] = function(raw_url)
-- TODO
end
<
Please refer to `urlview/actions.lua` for examples, and feel free to create a
GitHub issue or pull request to add your custom Lua function action as a
built-in action if you think others can use it as well!
vim:tw=78:ts=8:noet:ft=help:norl:
================================================
FILE: lua/urlview/actions.lua
================================================
local M = {}
local utils = require("urlview.utils")
local config = require("urlview.config")
--- Use command to open the URL
---@param cmd string @name of executable to run
---@param args string|table @arg(s) to pass into cmd (unescaped URL string or table of args)
local function shell_exec(cmd, args)
if cmd and vim.fn.executable(cmd) == 1 then
-- NOTE: `vim.fn.system` shellescapes arguments
local cmd_args = { cmd }
vim.list_extend(cmd_args, type(args) == "table" and args or { args })
local err = vim.fn.system(cmd_args)
if vim.v.shell_error ~= 0 or err ~= "" then
utils.log(
string.format("Failed to navigate link with cmd `%s` and args `%s`\n%s", cmd, args, err),
vim.log.levels.ERROR
)
end
else
utils.log(
string.format("Cannot use command `%s` to navigate links (either empty or non-executable)", cmd),
vim.log.levels.ERROR
)
end
end
--- Use `netrw` to navigate to a URL
---@param raw_url string @unescaped URL
function M.netrw(raw_url)
local url = vim.fn.shellescape(raw_url)
local ok, err = pcall(vim.cmd, string.format("call netrw#BrowseX(%s, netrw#CheckIfRemote(%s))", url, url))
if not ok and vim.startswith(err, "Vim(call):E117: Unknown function") then
-- lazily use system action if netrw is disabled
M.system(raw_url)
end
end
--- Use the user's default browser to navigate to a URL
---@param raw_url string @unescaped URL
function M.system(raw_url)
local os = utils.os
if os == "Darwin" then -- MacOS
shell_exec("open", raw_url)
elseif os == "Linux" or os == "FreeBSD" then -- Linux and FreeBSD
shell_exec("xdg-open", raw_url)
elseif os:match("Windows") then -- Windows
-- HACK: `start` cmd itself doesn't exist but lives under `cmd`
shell_exec("cmd", { "/C", "start", raw_url })
else
utils.log(
"Unsupported operating system for `system` action. Please raise a GitHub issue for " .. os,
vim.log.levels.WARN
)
end
end
--- Copy URL to clipboard
---@param raw_url string @unescaped URL
function M.clipboard(raw_url)
vim.fn.setreg(config.default_register, raw_url)
utils.log(string.format("URL %s copied to clipboard", raw_url), vim.log.levels.INFO)
end
return setmetatable(M, {
-- execute action as command if it is not one of the above module keys
__index = function(_, k)
if k ~= nil then
return function(raw_url)
return shell_exec(k, raw_url)
end
end
end,
})
================================================
FILE: lua/urlview/command.lua
================================================
local M = {}
local utils = require("urlview.utils")
local search_contexts = require("urlview.search")
local search_validation = require("urlview.search.validation")
--- Processes arguments provided through the `UrlView` command for `M.search`
local function command_search(res)
local opts = {}
local context = res.fargs[1]
local option_args = vim.list_slice(res.fargs, 2)
-- process provided options
for _, arg in ipairs(option_args) do
local split = vim.split(arg, "=", { plain = true })
if #split == 1 then
opts[arg] = true
elseif #split == 2 then
local key, value = unpack(split)
-- remove beginning and trailing quotes from value if present
local inner = value:match([[^['"](.*)['"]$]])
if inner ~= nil then
value = inner
end
-- type conversion
if vim.tbl_contains({ "true", "false" }, value:lower()) then
value = utils.string_to_boolean(value)
end
opts[key] = value
else
utils.log("Unable to parse argument " .. arg, vim.log.levels.ERROR)
return
end
end
require("urlview").search(context, opts)
end
local additional_opts = { "title", "picker", "action", "sorted" }
local function command_completion(_, line)
local args = vim.split(line, "%s+")
local nargs = #args - 2
if nargs == 0 then
-- search context completion
local contexts = vim.tbl_keys(search_contexts)
utils.alphabetical_sort(contexts)
return contexts
else
-- opts completion
local context = args[2]
local context_opts = search_validation[context]()
local accepted_opts = vim.list_extend(context_opts, additional_opts)
utils.alphabetical_sort(accepted_opts)
return vim.tbl_map(function(value)
return value .. "="
end, accepted_opts)
end
end
function M.register_command()
vim.api.nvim_create_user_command("UrlView", command_search, {
desc = "Find URLs in the current buffer or another search context",
complete = command_completion,
nargs = "*",
})
end
return M
================================================
FILE: lua/urlview/config/constants.lua
================================================
local constants = {
-- SEE: lua pattern matching (https://riptutorial.com/lua/example/20315/lua-pattern-matching)
-- regex equivalent: [A-Za-z0-9@:%._+~#=/\-?&]*
pattern = "[%w@:%%._+~#=/%-?&]*",
http_pattern = "https?://",
www_pattern = "www%.",
}
return constants
================================================
FILE: lua/urlview/config/default.lua
================================================
local default_config = {
-- Prompt title (`<context> <default_title>`, e.g. `Buffer Links:`)
default_title = "Links:",
-- Default picker to display links with
-- Options: "native" (vim.ui.select) or "telescope"
default_picker = "native",
-- Set the default protocol for us to prefix URLs with if they don't start with http/https
default_prefix = "https://",
-- Command or method to open links with
-- Options: "netrw", "system" (default OS browser), "clipboard"; or "firefox", "chromium" etc.
-- By default, this is "netrw", or "system" if netrw is disabled
default_action = "netrw",
-- Set the register to use when yanking
-- Default: + (system clipboard)
default_register = "+",
-- Whether plugin URLs should link to the branch used by your package manager
default_include_branch = false,
-- Ensure links shown in the picker are unique (no duplicates)
unique = true,
-- Ensure links shown in the picker are sorted alphabetically
sorted = true,
-- Minimum log level (recommended at least `vim.log.levels.WARN` for error detection warnings)
log_level_min = vim.log.levels.INFO,
-- Keymaps for jumping to previous / next URL in buffer
jump = {
prev = "[u",
next = "]u",
},
}
return default_config
================================================
FILE: lua/urlview/config/helpers.lua
================================================
local M = {}
local config = require("urlview.config")
local default_config = require("urlview.config.default")
function M.reset_defaults()
config._options = default_config
end
function M.update_config(user_config)
config._options = vim.tbl_deep_extend("force", config._options, user_config)
end
return M
================================================
FILE: lua/urlview/config/init.lua
================================================
local M = {
_options = {},
}
return setmetatable(M, {
-- general get and set operations refer to the internal table `_options`
__index = function(_, k)
return M._options[k]
end,
__newindex = function(_, k, v)
M._options[k] = v
end,
})
================================================
FILE: lua/urlview/init.lua
================================================
local M = {}
local actions = require("urlview.actions")
local command = require("urlview.command")
local config = require("urlview.config")
local config_helpers = require("urlview.config.helpers")
local jump = require("urlview.jump")
local search = require("urlview.search")
local search_validation = require("urlview.search.validation")
local pickers = require("urlview.pickers")
local utils = require("urlview.utils")
--- Searchs the provided context for links
---@param ctx string where to search (default: search buffer)
---@param opts table (map, optional)
function M.search(ctx, opts)
ctx = utils.fallback(ctx, "buffer")
opts = utils.fallback(opts, {})
opts.action = utils.fallback(opts.action, config.default_action)
local picker = utils.fallback(opts.picker, config.default_picker)
if not opts.title then
local should_capitalise = string.match(config.default_title, "^%u")
local ctx_title = utils.ternary(should_capitalise, ctx:gsub("^%l", string.upper), ctx)
opts.title = string.format("%s %s", ctx_title, config.default_title)
end
-- search ctx for links and display with picker
opts = search_validation[ctx](opts)
local links = search[ctx](opts)
links = utils.process_links(links, opts)
if links and not vim.tbl_isempty(links) then
if type(opts.action) == "string" then
opts.action = actions[opts.action]
end
pickers[picker](links, opts)
else
utils.log("No links found in context " .. ctx, vim.log.levels.INFO)
end
end
local function autoload()
config_helpers.reset_defaults()
command.register_command()
end
autoload()
--- Custom setup function
--- Not required to be called unless user wants to modify the default config
---@param user_config table (optional)
function M.setup(user_config)
user_config = utils.fallback(user_config, {})
config_helpers.update_config(user_config)
jump.register_mappings(config.jump)
end
return M
================================================
FILE: lua/urlview/jump.lua
================================================
local M = {}
-- NOTE: line numbers are 1-indexed, column numbers are 0-indexed
local utils = require("urlview.utils")
local search_helpers = require("urlview.search.helpers")
local END_COL = -1
--- Return the starting positions of `match` in `line`
---@param line string
---@param match string
---@param offset number @added to each position
---@return table (list) of offsetted starting indicies
function M.line_match_positions(line, match, offset)
local res = {}
local init = 1
while init <= #line do
local start, finish = line:find(match, init, true)
if start == nil then
return res
end
table.insert(res, start + offset - 1)
init = finish
end
return res
end
--- Returns a starting column position not on a URL
---@param line_start number @line number at cursor
---@param col_start number @column number at cursor
---@param reversed boolean @direction
---@return number @corrected starting column
function M.correct_start_col(line_start, col_start, reversed)
local full_line = vim.fn.getline(line_start)
local matches = search_helpers.content(full_line)
for _, match in ipairs(matches) do
local positions = M.line_match_positions(full_line, match, 0)
for _, position in ipairs(positions) do
local url_end = position + #match
local on_url = col_start >= position and col_start < url_end
-- edge case for going backwards with cursor at start of URL
if on_url and reversed and position == col_start then
return math.max(col_start - 1, 0)
-- generally if on a URL, move column to be after the URL
elseif on_url then
return url_end
end
end
end
return col_start
end
local reversed_sort_function_lookup = {
-- reversed == true: descending sort
[true] = function(a, b)
return a > b
end,
-- reversed == false: ascending sort
[false] = function(a, b)
return a < b
end,
}
--- Finds the position of the previous / next URL
---@param winnr number @id of current window
---@param reversed boolean @direction false for forward, true for backwards
---@return table|nil @position
function M.find_url(winnr, reversed)
local line_no, col_no = unpack(vim.api.nvim_win_get_cursor(winnr))
local total_lines = vim.api.nvim_buf_line_count(0)
col_no = M.correct_start_col(line_no, col_no, reversed)
local sort_function = reversed_sort_function_lookup[reversed]
local line_last = utils.ternary(reversed, 0, total_lines + 1)
while line_no ~= line_last do
local full_line = vim.fn.getline(line_no)
col_no = utils.ternary(col_no == END_COL, #full_line, col_no)
local line = utils.ternary(reversed, full_line:sub(1, col_no), full_line:sub(col_no + 1))
local matches = search_helpers.content(line)
if not vim.tbl_isempty(matches) then
-- sorted table(list) of starting column numbers for URLs in line
-- normal order: ascending, reversed order: descending
local indices = {}
for _, match in ipairs(matches) do
local offset = utils.ternary(reversed, 0, col_no)
vim.list_extend(indices, M.line_match_positions(line, match, offset))
end
table.sort(indices, sort_function)
-- find first valid (before or after current column)
for _, index in ipairs(indices) do
local valid = utils.ternary(reversed, index <= col_no, index >= col_no)
if valid then
return { line_no, index }
end
end
end
line_no = utils.ternary(reversed, line_no - 1, line_no + 1)
col_no = utils.ternary(reversed, END_COL, 0)
end
end
--- Forward / backward jump generator
---@param reversed boolean @direction false for forward, true for backwards
---@return function @when called, jumps to the URL in the given direction
local function goto_url(reversed)
return function()
local direction = utils.ternary(reversed, "previous", "next")
local winnr = vim.api.nvim_get_current_win()
local pos = M.find_url(winnr, reversed)
if not pos then
utils.log(string.format("Cannot find any %s URLs in buffer", direction), vim.log.levels.INFO)
return
end
if vim.api.nvim_win_is_valid(winnr) then
vim.cmd("normal! m'") -- add to jump list
vim.api.nvim_win_set_cursor(winnr, pos)
else
utils.log(
string.format("The %s URL was found in window number %s, which is no longer valid", direction, winnr),
vim.log.levels.WARN
)
end
end
end
--- Jump to the next URL
M.next_url = goto_url(false)
--- Jump to the previous URL
M.prev_url = goto_url(true)
--- Register URL jump mappings
---@param jump_opts table
function M.register_mappings(jump_opts)
if type(jump_opts) ~= "table" then
utils.log(
"Invalid type for option `jump` (expected: table with `prev` and `next` key mappings)",
vim.log.levels.WARN
)
else
if jump_opts.prev ~= "" then
vim.keymap.set("n", jump_opts.prev, function()
require("urlview.jump").prev_url()
end, { desc = "Previous URL" })
end
if jump_opts.next ~= "" then
vim.keymap.set("n", jump_opts.next, function()
require("urlview.jump").next_url()
end, { desc = "Next URL" })
end
end
end
return M
================================================
FILE: lua/urlview/pickers.lua
================================================
local M = {}
local utils = require("urlview.utils")
--- Displays items using the vim.ui.select picker
---@param items table (list) of strings
---@param opts table (map) of options
function M.native(items, opts)
local options = { prompt = opts.title }
local function on_choice(item, _)
if item then
opts.action(item)
end
end
vim.ui.select(items, options, on_choice)
end
--- Displays items using the fzf-lua picker
---@param items table (list) of strings
---@param opts table (map) of options
function M.fzf_lua(items, opts)
local fzf = pcall(require, "fzf-lua")
if not fzf then
utils.log("fzf-lua is not installed, defaulting to native vim.ui.select picker.", vim.log.levels.INFO)
return M.native(items, opts)
end
require("fzf-lua").fzf_exec(items, {
prompt = opts.title,
fzf_opts = { ["--multi"] = true },
actions = {
["default"] = function(selected)
for _, entry in ipairs(selected) do
opts.action(entry)
end
end,
},
})
end
--- Displays items using the Telescope picker
---@param items table (list) of strings
---@param opts table (map) of options
function M.telescope(items, opts)
local telescope = pcall(require, "telescope")
if not telescope then
utils.log("Telescope is not installed, defaulting to native vim.ui.select picker.", vim.log.levels.INFO)
return M.native(items, opts)
end
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local conf = require("telescope.config").values
local finders = require("telescope.finders")
local pickers = require("telescope.pickers")
pickers
.new(opts, {
prompt_title = opts.title,
finder = finders.new_table({
results = items,
}),
sorter = conf.generic_sorter(opts),
attach_mappings = function(prompt_bufnr, _)
actions.select_default:replace(function()
local picker = action_state.get_current_picker(prompt_bufnr)
local multi = picker:get_multi_selection()
local single = picker:get_selection()
actions.close(prompt_bufnr)
if #multi > 0 then
for _, entry in ipairs(multi) do
opts.action(entry[1])
end
elseif single[1] then
opts.action(single[1])
end
end)
return true
end,
})
:find()
end
return setmetatable(M, {
-- use default `vim.ui.select` when provided an invalid picker
__index = function(_, k)
if k ~= nil then
utils.log(k .. " is not a valid picker, defaulting to native vim.ui.select picker.", vim.log.levels.INFO)
return M.native
end
end,
})
================================================
FILE: lua/urlview/search/helpers.lua
================================================
local M = {}
local constants = require("urlview.config.constants")
local utils = require("urlview.utils")
--- Extracts content from a given buffer
---@param bufnr number (optional)
---@return string @content of buffer
function M.get_buffer_content(bufnr)
bufnr = utils.fallback(bufnr, 0)
if not vim.api.nvim_buf_is_valid(bufnr) then
utils.log(string.format("Invalid buffer number provided: %s", bufnr), vim.log.levels.ERROR)
return ""
end
return table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), "\n")
end
--- Extracts content from a given file
---@param filepath string @path to file
---@return string @content of file (or empty string if file cannot be open)
function M.read_file(filepath)
local f, err = io.open(vim.fn.expand(filepath), "r")
if f == nil then
utils.log(err, vim.log.levels.ERROR)
return ""
end
local content = f:read("*all")
f:close()
return content
end
--- Extracts urls from the given content
---@param content string
---@return table (list) of strings (extracted links)
function M.content(content)
---@type table (map) of string (url base pattern) to string (prefix / uri protocol)
local captures = {}
-- NOTE: this method enforces unique matches regardless of config (before a general pattern is implemented)
-- Extract URLs starting with http:// or https://
for capture in content:gmatch(constants.http_pattern .. "%w" .. constants.pattern) do
local prefix = capture:match(constants.http_pattern)
local url = capture:gsub(constants.http_pattern, "")
captures[url] = prefix
end
-- Extract URLs starting with www, excluding already extracted http(s) URLs
for capture in content:gmatch(constants.www_pattern .. "%w" .. constants.pattern) do
if not captures[capture] then
captures[capture] = ""
end
end
-- Combine captures
local links = {}
for url, prefix in pairs(captures) do
local link = prefix .. url
if link ~= "" then
table.insert(links, link)
end
end
return links
end
--- Extract @captures from @content and display them as @formats
---@param content string @content to extract from
---@param capture string @capture pattern to extract
---@param format string @Optional format pattern to display
---@return table @list of extracted links
function M.extract_pattern(content, capture, format)
local captures = {}
for c in content:gmatch(capture) do
local fmt = format and string.format(format, c) or c
table.insert(captures, fmt)
end
return captures
end
--- Extract Git links from @plugins_spec
---@param plugins_spec table @map of specs for plugins
---@param uri_key string @key in plugin_spec containing the Git URL
---@param include_branch boolean @whether to include the branch in the URL
---@return table @list of extracted links
function M.extract_plugins_spec(plugins_spec, uri_key, include_branch)
local function filter_files(plugin_url)
if plugin_url == nil then
return false
end
local fs_stat = vim.loop.fs_stat(plugin_url)
return not fs_stat or vim.tbl_isempty(fs_stat)
end
local function extract_key(plugin_spec)
if plugin_spec[uri_key] == nil then
return nil
end
local uri = plugin_spec[uri_key]:gsub("%.git$", "") -- remove `.git` suffix
if include_branch and plugin_spec.branch then
uri = string.format("%s/tree/%s", uri, plugin_spec.branch)
end
return uri
end
local plugins = vim.tbl_map(extract_key, vim.tbl_values(plugins_spec or {}))
return vim.tbl_filter(filter_files, plugins)
end
--- Generates a simple search function from a template table
---@param pattern table (map) with `capture` key and optional `format` key
---@return function|nil
function M.generate_custom_search(pattern)
if not pattern.capture then
utils.log("Unable to generate custom search: please ensure that the table has 'capture' field", vim.log.levels.WARN)
return nil
end
return function(opts)
local content = opts.content or M.get_buffer_content(opts.bufnr)
return M.extract_pattern(content, pattern.capture, pattern.format)
end
end
return M
================================================
FILE: lua/urlview/search/init.lua
================================================
local M = {}
local utils = require("urlview.utils")
local search_helpers = require("urlview.search.helpers")
-- NOTE: make sure to add accepted params for `opts` to `urlview.search.validation` as well if needed
--- Extracts urls from the current buffer or a given buffer
---@param opts table (map)
---@return table (list) of strings (extracted links)
function M.buffer(opts)
local content = search_helpers.get_buffer_content(opts.bufnr)
return search_helpers.content(content)
end
--- Extracts urls from a given file
---@param opts table (map)
---@return table (list) of strings (extracted links)
function M.file(opts)
local content = search_helpers.read_file(opts.filepath)
return search_helpers.content(content)
end
--- Extracts urls of packer.nvim plugins
---@param opts table (map)
---@return table (list) of strings (extracted links)
function M.packer(opts)
-- selene: allow(global_usage)
local plugins = _G.packer_plugins or {}
return search_helpers.extract_plugins_spec(plugins, "url", opts.include_branch)
end
--- Extracts urls of lazy.nvim plugins
---@param opts table (map)
---@return table (list) of strings (extracted links)
function M.lazy(opts)
local ok, lazy = pcall(require, "lazy")
local plugins = ok and lazy.plugins() or {}
return search_helpers.extract_plugins_spec(plugins, "url", opts.include_branch)
end
--- Extracts urls of vim-plug plugins
---@param opts table (map)
---@return table (list) of strings (extracted links)
function M.vimplug(opts)
local plugins = vim.g.plugs or {}
return search_helpers.extract_plugins_spec(plugins, "uri", opts.include_branch)
end
return setmetatable(M, {
-- error check for invalid searcher (still allow function calls, but return empty table)
__index = function(_, k)
if k ~= nil then
utils.log("Cannot search context " .. k, vim.log.levels.WARN)
return function()
return {}
end
end
end,
})
================================================
FILE: lua/urlview/search/validation.lua
================================================
local M = {}
local utils = require("urlview.utils")
local config = require("urlview.config")
--- Validates `opts` and sets default values if `opts` is provided, otherwise returns all possible accepted options
---@param opts table (map) of user options
---@param rules table (map) of vim.validate rules (with an optional fourth tuple member for default value)
--- (option_key (string) -> { type (string), optional: boolean, default: any })
---@return table (map) of updated user options if `opts` is provided, otherwise returns all possible accepted options as a table (list)
local function verify_or_accept(opts, rules)
if not opts then
-- return valid options if `opts` not provided
return vim.tbl_keys(rules)
end
local new_opts = vim.deepcopy(opts)
for key, value in pairs(rules) do
if #value == 4 and value[3] and opts[key] == nil then
local default = value[4]
new_opts[key] = default
rules[key][1] = default
end
end
vim.validate(rules)
return new_opts
end
-- NOTE: validation for `urlview.search.init` functions go below
--- Validation for the "buffer" search context
---@param opts table (map, optional) of user options
---@return table (map) of updated user options if `opts` is provided, otherwise returns all possible accepted options as a table (list)
function M.buffer(opts)
return verify_or_accept(opts, {
bufnr = { opts.bufnr, "number", true, 0 },
})
end
--- Validation for the "file" search context
---@param opts table (map, optional) of user options
---@return table (map) of updated user options if `opts` is provided, otherwise returns all possible accepted options as a table (list)
function M.file(opts)
return verify_or_accept(opts, {
filepath = { opts.filepath, "string", false },
})
end
--- Validation for the "packer" search context
---@param opts table (map, optional) of user options
---@return table (map) of updated user options if `opts` is provided, otherwise returns all possible accepted options as a table (list)
function M.packer(opts)
return verify_or_accept(opts, {
include_branch = { opts.include_branch, "boolean", true, config.default_include_branch },
})
end
--- Validation for the "lazy" search context
---@param opts table (map, optional) of user options
---@return table (map) of updated user options if `opts` is provided, otherwise returns all possible accepted options as a table (list)
function M.lazy(opts)
return verify_or_accept(opts, {
include_branch = { opts.include_branch, "boolean", true, config.default_include_branch },
})
end
--- Validation for the "vimplug" search context
---@param opts table (map, optional) of user options
---@return table (map) of updated user options if `opts` is provided, otherwise returns all possible accepted options as a table (list)
function M.vimplug(opts)
return verify_or_accept(opts, {
include_branch = { opts.include_branch, "boolean", true, config.default_include_branch },
})
end
return setmetatable(M, {
__index = function(_, _)
return function(opts)
-- if `opts` provided, return `opts`, otherwise return all possible accepted options
return utils.fallback(opts, {})
end
end,
})
================================================
FILE: lua/urlview/utils.lua
================================================
local M = {}
local config = require("urlview.config")
local constants = require("urlview.config.constants")
M.os = vim.loop.os_uname().sysname
function M.alphabetical_sort(tbl)
table.sort(tbl, function(a, b)
return a:lower() < b:lower()
end)
end
--- Processes links before being displayed
---@param links table @list of extracted links
---@param opts table @Optional options
---@return table @list of prepared links
function M.process_links(links, opts)
opts = M.fallback(opts, {})
local new_links = {}
-- Attach missing HTTP(s) protocol
for _, link in ipairs(links) do
if not link:match("^" .. constants.http_pattern) then
link = config.default_prefix .. link
end
table.insert(new_links, link)
end
-- Filter duplicate links
-- NOTE: links with different protocols / www prefix / trailing slashes are not filtered to ensure links do not break
if M.fallback(opts.unique, config.unique) then
local map = {}
for _, link in ipairs(new_links) do
map[link] = true
end
new_links = vim.tbl_keys(map)
end
-- Sort links alphabetically (case insensitive)
if M.fallback(opts.sorted, config.sorted) then
M.alphabetical_sort(new_links)
end
return new_links
end
--- Determines whether to accept the current value or use a fallback value
---@param value any @value to check
---@param fallback_value any @fallback value to use
---@param fallback_comparison any @fallback comparison, defaults to nil
---@return any @value, or @fallback if @value is @fallback_comparison
function M.fallback(value, fallback_value, fallback_comparison)
return (value == fallback_comparison and fallback_value) or value
end
--- Mimics the ternary operator
---@param condition boolean @condition to check
---@param if_true any @value to return if @condition is true
---@param if_false any @value to return if @condition is false
---@return any @condition ? if_true : if_false
function M.ternary(condition, if_true, if_false)
return (condition and if_true) or if_false
end
--- Logs user warnings
---@param message string @message to log
---@param level integer|nil @log level, defaults to "warning"
function M.log(message, level)
level = M.fallback(level, vim.log.levels.WARN)
if level >= config.log_level_min then
vim.notify("[urlview.nvim] " .. message, level)
end
end
--- Converts a boolean to a string
---@param value string @value to convert
---@return boolean|nil @value as a boolean, or nil if not a boolean
function M.string_to_boolean(value)
value = value:lower()
local bool_map = { ["true"] = true, ["false"] = false }
if not bool_map[value] then
M.log("Could not convert " .. value .. " to boolean")
end
return bool_map[value]
end
return M
================================================
FILE: selene.toml
================================================
std = "vim"
================================================
FILE: stylua.toml
================================================
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
no_call_parentheses = false
================================================
FILE: tests/init.vim
================================================
set noswapfile
set rtp+=../plenary.nvim
set rtp+=../urlview.nvim
runtime! plugin/plenary.vim
================================================
FILE: tests/urlview/capture_custom_searches_spec.lua
================================================
local search = require("urlview.search")
local search_helpers = require("urlview.search.helpers")
local assert_tbl_same_any_order = require("tests.urlview.helpers").assert_tbl_same_any_order
describe("custom Jira searcher (template table)", function()
before_each(function()
search.jira = search_helpers.generate_custom_search({
capture = "AXIE%-%d+",
format = "https://jira.axieax.com/browse/%s",
})
assert.is_not.Nil(search.jira)
end)
after_each(function()
search.jira = nil
end)
it("capture single", function()
local content = "AXIE-1"
local links = search.jira({ content = content })
assert_tbl_same_any_order({
"https://jira.axieax.com/browse/AXIE-1",
}, links)
end)
it("capture multiple", function()
local content = [[
AXIE-1
AXIE-12
AXIE-123
AXIE-1234
AXIE-12345
AXIE-123456
AXIE-1234567
AXIE-12345678
AXIE-123456789
AXIE-1234567890
]]
local links = search.jira({ content = content })
assert_tbl_same_any_order({
"https://jira.axieax.com/browse/AXIE-1",
"https://jira.axieax.com/browse/AXIE-12",
"https://jira.axieax.com/browse/AXIE-123",
"https://jira.axieax.com/browse/AXIE-1234",
"https://jira.axieax.com/browse/AXIE-12345",
"https://jira.axieax.com/browse/AXIE-123456",
"https://jira.axieax.com/browse/AXIE-1234567",
"https://jira.axieax.com/browse/AXIE-12345678",
"https://jira.axieax.com/browse/AXIE-123456789",
"https://jira.axieax.com/browse/AXIE-1234567890",
}, links)
end)
it("invalid captures ignored", function()
local content = [[
AXIE
AXIE1
AXIE--123
AXIE%-1
AXIE-!
AXIE-AXIE
AXIE-ax
]]
local links = search.jira({ content = content })
assert_tbl_same_any_order({}, links)
end)
end)
describe("overwrite default searcher", function()
local builtin_search_buffer = search.buffer
before_each(function()
search.buffer = search_helpers.generate_custom_search({
capture = "l.ve",
format = "i-%s-testing",
})
end)
after_each(function()
search.buffer = builtin_search_buffer
end)
it("capture single", function()
local content = "i love testing"
local result = search.buffer({ content = content })
assert_tbl_same_any_order({
"i-love-testing",
}, result)
end)
it("capture multiple", function()
local content = "I live to see another day, I love Neovim"
local result = search.buffer({ content = content })
assert_tbl_same_any_order({
"i-love-testing",
"i-live-testing",
}, result)
end)
end)
describe("custom function", function()
before_each(function()
search.test = function(opts)
return { opts.a or "default", opts.b or "default", opts.c or "default" }
end
end)
after_each(function()
search.test = nil
end)
it("capture one", function()
local result = search.test({ a = "a" })
assert_tbl_same_any_order({ "a", "default", "default" }, result)
end)
it("capture two", function()
local result = search.test({ a = "a", c = "c" })
assert_tbl_same_any_order({ "a", "c", "default" }, result)
end)
it("capture three", function()
local result = search.test({ a = "a", b = "b", c = "c" })
assert_tbl_same_any_order({ "a", "b", "c" }, result)
end)
it("ignore extra", function()
local result = search.test({ a = "a", b = "b", c = "c", d = "d" })
assert_tbl_same_any_order({ "a", "b", "c" }, result)
end)
end)
describe("register custom search", function()
it("captures git uris", function()
search.git = search_helpers.generate_custom_search({
capture = "git@[^%s]+%.git",
})
local content = [[
git@github.com:axieax/urlview.nvim.git
git@github.com:axieax/typo.nvim.git
]]
local links = search.git({ content = content })
assert_tbl_same_any_order({
"git@github.com:axieax/urlview.nvim.git",
"git@github.com:axieax/typo.nvim.git",
}, links)
search.git = nil
end)
it("captures ssh uris iwthout the prefix", function()
search.ssh = search_helpers.generate_custom_search({
capture = "ssh://([^%s]+)",
})
local content = [[
ssh://git@github.com:axieax/urlview.nvim.git
ssh://git@github.com:axieax/typo.nvim.git
ssh://192.168.1.1
ssh://192.168.1.1:4000
]]
local links = search.ssh({ content = content })
assert_tbl_same_any_order({
"git@github.com:axieax/urlview.nvim.git",
"git@github.com:axieax/typo.nvim.git",
"192.168.1.1",
"192.168.1.1:4000",
}, links)
search.ssh = nil
end)
it("captures ftp uris", function()
search.ftp = search_helpers.generate_custom_search({
capture = "ftp://[^%s]+",
})
local content = [[
ftp://ftp.example.com
ftp://user@host/%2Ffoo/bar.txt
ftp://user:password@server/pathname;type=a
]]
local links = search.ftp({ content = content })
assert_tbl_same_any_order({
"ftp://ftp.example.com",
"ftp://user@host/%2Ffoo/bar.txt",
"ftp://user:password@server/pathname;type=a",
}, links)
search.ftp = nil
end)
end)
================================================
FILE: tests/urlview/capture_jump_spec.lua
================================================
local jump = require("urlview.jump")
local jump_helpers = require("tests.urlview.jump_helpers")
local set_cursor = jump_helpers.set_cursor
local create_buffer = jump_helpers.create_buffer
local teardown_windows = jump_helpers.teardown_windows
local jump_forwards = jump_helpers.jump_forwards
local jump_backwards = jump_helpers.jump_backwards
-- ASSUMPTION(cursor_pos): line numbers are 1-indexed, column numbers are 0-indexed
local examples = {
empty = "",
invalid = "hello",
single_line_middle = "abc https://www.google.com def",
standard_url = "https://www.google.com",
multi_line_just_links = [[
https://www.google.com
https://www.github.com
https://www.amazon.com
https://www.reddit.com]],
multi_line_sandwich = [[
https://www.google.com
]],
}
describe("line_match_positions unit tests", function()
it("multiple substrings", function()
local line = "abc abc abc"
local expected_no_offset = { 0, 4, 8 }
for offset = 0, 100 do
local res = jump.line_match_positions(line, "abc", offset)
local expected = vim.tbl_map(function(x)
return x + offset
end, expected_no_offset)
vim.deep_equal(expected, res)
end
end)
it("single URL", function()
local url = examples.standard_url
local res = jump.line_match_positions(url, url, 0)
vim.deep_equal({ 0 }, res)
end)
it("correct single index", function()
local url = examples.standard_url
local line = examples.single_line_middle
local res = jump.line_match_positions(line, url, 0)
vim.deep_equal({ 4 }, res)
end)
end)
describe("correct starting column", function()
it("backwards no URL", function()
local line = examples.invalid
create_buffer(line)
for col = 0, #line do
local new_col = jump.correct_start_col(1, col, true)
assert.equals(col, new_col)
end
end)
it("backwards before URL", function()
local line = examples.single_line_middle
create_buffer(line)
local url_start = 4
for col = 0, url_start - 1 do
local new_col = jump.correct_start_col(1, col, true)
assert.equals(col, new_col)
end
end)
it("backwards on start of URL", function()
local line = examples.single_line_middle
local url_start = 4
create_buffer(line)
local new_col = jump.correct_start_col(1, url_start, true)
assert.equals(url_start - 1, new_col)
end)
it("backwards on rest of URL", function()
local line = examples.single_line_middle
create_buffer(line)
local url_start = 4
local url_end = url_start + #examples.standard_url
for col = url_start + 1, url_end - 1 do
local new_col = jump.correct_start_col(1, col, true)
assert.equals(url_end, new_col)
end
end)
it("backwards after URL", function()
local line = examples.single_line_middle
local url_start = 4
local url_end = url_start + #examples.standard_url
for col = url_end, #line do
local new_col = jump.correct_start_col(1, col, true)
assert.equals(col, new_col)
end
end)
it("forwards no URL", function()
local line = examples.invalid
create_buffer(line)
for col = 0, #line do
local new_col = jump.correct_start_col(1, col, false)
assert.equals(col, new_col)
end
end)
it("forwards before URL", function()
local line = examples.single_line_middle
create_buffer(line)
local url_start = 4
for col = 0, url_start - 1 do
local new_col = jump.correct_start_col(1, col, false)
assert.equals(col, new_col)
end
end)
it("forwards on URL", function()
local line = examples.single_line_middle
create_buffer(line)
local url_start = 4
local url_end = url_start + #examples.standard_url
for col = url_start, url_end - 1 do
local new_col = jump.correct_start_col(1, col, false)
assert.equals(url_end, new_col)
end
end)
it("forwards after URL", function()
local line = examples.single_line_middle
local url_start = 4
local url_end = url_start + #examples.standard_url
for col = url_end, #line do
local new_col = jump.correct_start_col(1, col, false)
assert.equals(col, new_col)
end
end)
end)
describe("backwards jump", function()
after_each(teardown_windows)
it("empty buffer", function()
local content = examples.empty
create_buffer(content, { 1, 0 })
local res = jump_backwards()
assert.is_nil(res)
end)
it("no URL", function()
local content = examples.invalid
create_buffer(content)
for col = 0, #content do
set_cursor({ 1, col })
local res = jump_backwards()
assert.is_nil(res)
end
end)
it("just URL", function()
local content = examples.standard_url
create_buffer(content, { 1, 0 })
local res = jump_backwards()
assert.is_nil(res)
for col = 1, #content do
set_cursor({ 1, col })
res = jump_backwards()
vim.deep_equal({ 1, 0 }, res)
end
end)
it("invalid jump before URL and start of URL", function()
local content = examples.single_line_middle
local url_start = 4
create_buffer(content)
for col = 0, url_start do
set_cursor({ 1, col })
local res = jump_backwards()
assert.is_nil(res)
end
end)
it("simple jump to start of URL", function()
local content = examples.single_line_middle
local url_start = 4
create_buffer(content)
for col = url_start + 1, #content do
set_cursor({ 1, col })
local res = jump_backwards()
vim.deep_equal({ 1, url_start }, res)
end
end)
it("multiline jump from anywhere in line", function()
local content = examples.multi_line_just_links
local url_length = #examples.standard_url
create_buffer(content)
-- invalid jump
set_cursor({ 1, 0 })
local res = jump_backwards()
assert.is_nil(res)
-- jump to start of previous URL
local line_last = 4
for line = 1, line_last do
local expected = { line, 0 }
for col = 1, url_length do
set_cursor({ line, col })
res = jump_backwards()
vim.deep_equal(expected, res)
end
if line ~= line_last then
set_cursor({ line + 1, 0 })
vim.deep_equal(expected, res)
end
end
end)
it("multiline sandwich", function()
local content = examples.multi_line_sandwich
local url_length = #examples.standard_url
create_buffer(content)
-- invalid jumps
for line = 1, 2 do
set_cursor({ line, 0 })
local res = jump_backwards()
assert.is_nil(res)
end
-- jumps to correct position
local expected = { 2, 0 }
for col = 1, url_length do
set_cursor({ 2, col })
local res = jump_backwards()
vim.deep_equal(expected, res)
end
-- line 3 jumps to line 2
set_cursor({ 3, 0 })
local res = jump_backwards()
vim.deep_equal(expected, res)
end)
it("jump chain", function()
local content = examples.multi_line_just_links
local url_length = #examples.standard_url
create_buffer(content, { 4, url_length })
for line = 4, 1, -1 do
local res = jump_backwards()
vim.deep_equal({ line, 0 }, res)
end
local res = jump_backwards()
assert.is_nil(res)
end)
end)
describe("forwards jump", function()
after_each(teardown_windows)
it("empty buffer", function()
local content = examples.empty
create_buffer(content, { 1, 0 })
local res = jump_forwards()
assert.is_nil(res)
end)
it("no URL", function()
local content = examples.invalid
create_buffer(content)
for col = 0, #content do
set_cursor({ 1, col })
local res = jump_forwards()
assert.is_nil(res)
end
end)
it("just URL", function()
local content = examples.standard_url
create_buffer(content)
for col = 0, #content do
set_cursor({ 1, col })
local res = jump_forwards()
assert.is_nil(res)
end
end)
it("same line single", function()
local content = examples.single_line_middle
local url_start = 4
create_buffer(content)
for col = 0, url_start - 1 do
set_cursor({ 1, col })
local res = jump_forwards()
vim.deep_equal({ 1, url_start }, res)
end
end)
it("on last URL + no more", function()
local content = examples.single_line_middle
local url_start = 4
create_buffer(content)
for col = url_start, #content do
set_cursor({ 1, col })
local res = jump_forwards()
assert.is_nil(res)
end
end)
it("multiline jump from anywhere in line", function()
local content = examples.multi_line_just_links
local url_length = #examples.standard_url
create_buffer(content)
-- jumps to next line
for line = 1, 3 do
for col = 0, url_length do
set_cursor({ line, col })
local res = jump_forwards()
vim.deep_equal({ line + 1, 0 }, res)
end
end
-- last line
for col = 0, url_length do
set_cursor({ 4, col })
local res = jump_forwards()
assert.is_nil(res)
end
end)
it("multiline sandwich", function()
local content = examples.multi_line_sandwich
local url_length = #examples.standard_url
create_buffer(content, { 1, 0 })
-- line 1 jumps to line 2
local res = jump_forwards()
vim.deep_equal({ 2, 0 }, res)
-- line 2 onwards invalid
for col = 0, url_length do
set_cursor({ 2, col })
res = jump_forwards()
assert.is_nil(res)
end
set_cursor({ 3, 0 })
res = jump_forwards()
assert.is_nil(res)
end)
it("jump chain", function()
local content = examples.multi_line_just_links
create_buffer(content, { 1, 0 })
for line = 1, 3 do
local res = jump_forwards()
vim.deep_equal({ line + 1, 0 }, res)
end
local res = jump_forwards()
assert.is_nil(res)
end)
end)
================================================
FILE: tests/urlview/capture_mock_spec.lua
================================================
local urlview = require("urlview")
local reset_config = require("urlview.config.helpers").reset_defaults
local search = require("urlview.search")
local search_helpers = require("urlview.search.helpers")
describe("mock vim.ui.select", function()
local default_prefix = "test-"
before_each(function()
urlview.setup({ default_prefix = default_prefix })
search.test = search_helpers.generate_custom_search({
capture = "%w+",
format = "%s",
})
assert.is_not.Nil(search.test)
end)
after_each(function()
search.test = nil
reset_config()
end)
local original_ui_select = vim.ui.select
it("unique, sorted", function()
local content = "pears watermelon banana apple apple peach apricot watermelon"
local expected = vim.tbl_map(function(v)
return default_prefix .. v
end, { "apple", "apricot", "banana", "peach", "pears", "watermelon" })
vim.ui.select = function(items)
assert.same(expected, items)
end
urlview.search("test", { content = content, unique = true, sorted = true })
vim.ui.select = original_ui_select
end)
end)
describe("mock telescope", function() end)
================================================
FILE: tests/urlview/capture_multiple_spec.lua
================================================
local urlview = require("urlview")
local extract_links_from_content = require("urlview.search.helpers").content
local reset_config = require("urlview.config.helpers").reset_defaults
local assert_tbl_same_any_order = require("tests.urlview.helpers").assert_tbl_same_any_order
describe("multiple captures", function()
it("separate lines", function()
local content = [[
http://google.com
https://www.google.com
]]
local result = extract_links_from_content(content)
assert_tbl_same_any_order({ "http://google.com", "https://www.google.com" }, result)
end)
it("same line", function()
local content = "http://google.com https://www.github.com"
local result = extract_links_from_content(content)
assert_tbl_same_any_order({ "http://google.com", "https://www.github.com" }, result)
end)
end)
describe("unique captures", function()
it("same link", function()
local content = [[
http://google.com
http://google.com
]]
local result = extract_links_from_content(content)
assert_tbl_same_any_order({ "http://google.com" }, result)
end)
it("different prefix / uri protocol, same default prefix", function()
urlview.setup({
default_prefix = "https://",
})
local content = [[
https://www.google.com
www.google.com
]]
local result = extract_links_from_content(content)
assert_tbl_same_any_order({ "https://www.google.com" }, result)
reset_config()
end)
it("different prefix / uri protocol, prefer specified", function()
urlview.setup({
default_prefix = "http://",
})
local content = [[
https://www.google.com
www.google.com
]]
local result = extract_links_from_content(content)
assert_tbl_same_any_order({ "https://www.google.com" }, result)
reset_config()
end)
it("different paths", function()
local content = [[
https://www.google.com/search?q=vim
https://www.google.com/search?q=nvim
]]
local result = extract_links_from_content(content)
assert_tbl_same_any_order({ "https://www.google.com/search?q=vim", "https://www.google.com/search?q=nvim" }, result)
end)
end)
================================================
FILE: tests/urlview/capture_process_spec.lua
================================================
local urlview = require("urlview")
local search = require("urlview.search")
local search_helpers = require("urlview.search.helpers")
local reset_config = require("urlview.config.helpers").reset_defaults
local assert_tbl_same_any_order = require("tests.urlview.helpers").assert_tbl_same_any_order
local prepare_links = require("urlview.utils").process_links
local extract_links_from_content = search_helpers.content
describe("HTTP(s) protocol fill in", function()
local default_prefix = "https://"
before_each(function()
urlview.setup({ default_prefix = default_prefix })
end)
after_each(function()
reset_config()
end)
it("link with http protocol", function()
local url = "http://example.com"
local links = extract_links_from_content(url)
local prepared_links = prepare_links(links)
assert.same({ url }, prepared_links)
end)
it("link with https protocol", function()
local url = "https://example.com"
local links = extract_links_from_content(url)
local prepared_links = prepare_links(links)
assert.same({ url }, prepared_links)
end)
it("link missing either protocol", function()
local url = "www.example.com"
local links = extract_links_from_content(url)
local prepared_links = prepare_links(links)
assert.same({ default_prefix .. url }, prepared_links)
end)
end)
describe("unique links", function()
before_each(function()
urlview.setup({
default_prefix = "",
})
search.test = search_helpers.generate_custom_search({
capture = "%d",
format = "%s",
})
assert.is_not.Nil(search.test)
end)
after_each(function()
search.test = nil
reset_config()
end)
local content = "1 1 2 2 3 3 4 4 5 5"
it("keep duplicates", function()
local links = search.test({ content = content })
local prepared_links = prepare_links(links, { unique = false })
assert_tbl_same_any_order({ "1", "1", "2", "2", "3", "3", "4", "4", "5", "5" }, prepared_links)
end)
it("filter duplicates", function()
local links = search.test({ content = content })
local prepared_links = prepare_links(links, { unique = true })
assert.same({ "1", "2", "3", "4", "5" }, prepared_links)
end)
end)
describe("sorted links", function()
local default_prefix = "https://"
before_each(function()
urlview.setup({ default_prefix = default_prefix, sort = true })
end)
after_each(function()
reset_config()
end)
it("URLs missing protocol fixed and sorted alphabetically", function()
local content = [[
www.google.com
https://google.com
https://github.com/axieax/urlview.nvim
www.example.com
http://github.com/helloM
http://github.com/helloP
http://github.com/hellon
]]
local links = extract_links_from_content(content)
local prepared_links = prepare_links(links)
local expected = {
"http://github.com/helloM",
"http://github.com/hellon",
"http://github.com/helloP",
"https://github.com/axieax/urlview.nvim",
"https://google.com",
default_prefix .. "www.example.com",
default_prefix .. "www.google.com",
}
assert.same(expected, prepared_links)
end)
end)
================================================
FILE: tests/urlview/capture_single_spec.lua
================================================
local urlview = require("urlview")
local reset_config = require("urlview.config.helpers").reset_defaults
local assert_no_match = require("tests.urlview.helpers").assert_no_match
local assert_single_match = require("tests.urlview.helpers").assert_single_match
describe("no capture", function()
it("empty string", function()
assert_no_match("")
end)
it("random", function()
assert_no_match("asdfwiueyfksdlckvj")
end)
it("com only", function()
assert_no_match("test.com")
end)
it("com path", function()
assert_no_match("test.com/idk")
end)
end)
describe("url-only simple capture", function()
local default_prefix = "https://"
before_each(function()
urlview.setup({
default_prefix = default_prefix,
})
end)
after_each(function()
reset_config()
end)
it("http capture", function()
local url = "http://google.com"
assert_single_match(url, url)
end)
it("https capture", function()
local url = "https://google.com"
assert_single_match(url, url)
end)
it("www capture", function()
local url = "www.google.com"
assert_single_match(url, url)
end)
it("http www capture", function()
local url = "http://www.google.com"
assert_single_match(url, url)
end)
it("https www capture", function()
local url = "https://www.google.com"
assert_single_match(url, url)
end)
it("trailing slash", function()
local url = "www.google.com/"
assert_single_match(url, url)
end)
end)
describe("url-only path capture", function()
local default_prefix = "http://"
before_each(function()
urlview.setup({
default_prefix = default_prefix,
})
end)
after_each(function()
reset_config()
end)
it("lol php capture", function()
local url = "https://who.even.uses/index.php"
assert_single_match(url, url)
end)
it("https path capture", function()
local url = "https://google.com/path/to/idk"
assert_single_match(url, url)
end)
it("www path capture", function()
local url = "www.google.com/path/to/idk"
assert_single_match(url, url)
end)
it("url-encoded path query capture", function()
local url = "www.google.com/P%40%2Bh%20t35T%2F/1dk%3F?q=%3Da%25%3B"
assert_single_match(url, url)
end)
it("query capture", function()
local url = "https://example.com/path/to/idk?q=axie&ax"
assert_single_match(url, url)
end)
end)
================================================
FILE: tests/urlview/helpers.lua
================================================
local M = {}
local extract_links_from_content = require("urlview.search.helpers").content
function M.assert_no_match(content)
local result = extract_links_from_content(content)
assert.equals(0, #result)
end
function M.assert_single_match(url, expected_url)
local result = extract_links_from_content(url)
assert.same({ expected_url }, result)
end
function M.assert_tbl_same_any_order(expected, actual)
assert.same(#expected, #actual)
for _, e in ipairs(expected) do
assert.truthy(vim.tbl_contains(actual, e))
end
end
return M
================================================
FILE: tests/urlview/jump_helpers.lua
================================================
local M = {}
local jump = require("urlview.jump")
local utils = require("urlview.utils")
local active_windows = {}
function M.set_cursor(pos)
vim.api.nvim_win_get_cursor = function()
return pos
end
end
function M.create_buffer(content, cursor_pos)
local lines = vim.split(content, "\n")
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
local winnr = vim.api.nvim_open_win(bufnr, false, {
relative = "editor",
row = 0,
col = 0,
width = 1,
height = 1,
zindex = 1,
})
active_windows[winnr] = true
vim.fn.getline = function(line_no)
return lines[line_no]
end
vim.api.nvim_buf_line_count = function()
return #lines
end
utils.fallback(cursor_pos, { 1, 0 })
M.set_cursor(cursor_pos)
end
function M.teardown_windows()
local windows = vim.tbl_keys(active_windows)
for _, winnr in ipairs(windows) do
if vim.api.nvim_win_is_valid(winnr) then
vim.api.nvim_win_close(winnr, true)
active_windows[winnr] = nil
end
end
end
function M.jump_forwards()
local res = jump.find_url(0, false)
if res then
M.set_cursor(res)
end
return res
end
function M.jump_backwards()
local res = jump.find_url(0, true)
if res then
M.set_cursor(res)
end
return res
end
return M
================================================
FILE: vim.toml
================================================
# SOURCE: https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/vim.toml
[selene]
base = "lua51"
name = "vim"
[vim]
any = true
[[describe.args]]
type = "string"
[[describe.args]]
type = "function"
[[it.args]]
type = "string"
[[it.args]]
type = "function"
[[before_each.args]]
type = "function"
[[after_each.args]]
type = "function"
[assert.is_not]
any = true
[assert.matches]
any = true
[assert.has_error]
any = true
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
required = false
[[assert.same.args]]
type = "any"
[[assert.same.args]]
type = "any"
[[assert.truthy.args]]
type = "any"
[[assert.falsy.args]]
type = "any"
[[assert.spy.args]]
type = "any"
[[assert.stub.args]]
type = "any"
[[assert.is_nil.args]]
type = "any"
gitextract_1w34gv51/ ├── .github/ │ └── workflows/ │ └── default.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc/ │ └── urlview.txt ├── lua/ │ └── urlview/ │ ├── actions.lua │ ├── command.lua │ ├── config/ │ │ ├── constants.lua │ │ ├── default.lua │ │ ├── helpers.lua │ │ └── init.lua │ ├── init.lua │ ├── jump.lua │ ├── pickers.lua │ ├── search/ │ │ ├── helpers.lua │ │ ├── init.lua │ │ └── validation.lua │ └── utils.lua ├── selene.toml ├── stylua.toml ├── tests/ │ ├── init.vim │ └── urlview/ │ ├── capture_custom_searches_spec.lua │ ├── capture_jump_spec.lua │ ├── capture_mock_spec.lua │ ├── capture_multiple_spec.lua │ ├── capture_process_spec.lua │ ├── capture_single_spec.lua │ ├── helpers.lua │ └── jump_helpers.lua └── vim.toml
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (83K chars).
[
{
"path": ".github/workflows/default.yml",
"chars": 1151,
"preview": "name: default\n\non:\n pull_request:\n push:\n branches: [main]\n\njobs:\n stylua:\n name: Check code style\n runs-on:"
},
{
"path": ".gitignore",
"chars": 9,
"preview": "doc/tags\n"
},
{
"path": "LICENSE",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2022 Andrew Xie\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "Makefile",
"chars": 128,
"preview": "test:\n\tnvim --headless --noplugin -u tests/init.vim -c \"PlenaryBustedDirectory tests/urlview {minimal_init = 'tests/init"
},
{
"path": "README.md",
"chars": 6379,
"preview": "<h1 align=\"center\">🔎 urlview.nvim</h1>\n<p align=\"center\"><i>Find and display URLs from a variety of search contexts</i><"
},
{
"path": "doc/urlview.txt",
"chars": 14261,
"preview": "*urlview.txt* Find and display URLs from a variety of search contexts\n\n====================================="
},
{
"path": "lua/urlview/actions.lua",
"chars": 2463,
"preview": "local M = {}\n\nlocal utils = require(\"urlview.utils\")\nlocal config = require(\"urlview.config\")\n\n--- Use command to open t"
},
{
"path": "lua/urlview/command.lua",
"chars": 2033,
"preview": "local M = {}\n\nlocal utils = require(\"urlview.utils\")\nlocal search_contexts = require(\"urlview.search\")\nlocal search_vali"
},
{
"path": "lua/urlview/config/constants.lua",
"chars": 277,
"preview": "local constants = {\n -- SEE: lua pattern matching (https://riptutorial.com/lua/example/20315/lua-pattern-matching)\n --"
},
{
"path": "lua/urlview/config/default.lua",
"chars": 1255,
"preview": "local default_config = {\n -- Prompt title (`<context> <default_title>`, e.g. `Buffer Links:`)\n default_title = \"Links:"
},
{
"path": "lua/urlview/config/helpers.lua",
"chars": 312,
"preview": "local M = {}\n\nlocal config = require(\"urlview.config\")\nlocal default_config = require(\"urlview.config.default\")\n\nfunctio"
},
{
"path": "lua/urlview/config/init.lua",
"chars": 256,
"preview": "local M = {\n _options = {},\n}\n\nreturn setmetatable(M, {\n -- general get and set operations refer to the internal table"
},
{
"path": "lua/urlview/init.lua",
"chars": 1917,
"preview": "local M = {}\n\nlocal actions = require(\"urlview.actions\")\nlocal command = require(\"urlview.command\")\nlocal config = requi"
},
{
"path": "lua/urlview/jump.lua",
"chars": 5199,
"preview": "local M = {}\n\n-- NOTE: line numbers are 1-indexed, column numbers are 0-indexed\n\nlocal utils = require(\"urlview.utils\")\n"
},
{
"path": "lua/urlview/pickers.lua",
"chars": 2701,
"preview": "local M = {}\n\nlocal utils = require(\"urlview.utils\")\n\n--- Displays items using the vim.ui.select picker\n---@param items "
},
{
"path": "lua/urlview/search/helpers.lua",
"chars": 4107,
"preview": "local M = {}\n\nlocal constants = require(\"urlview.config.constants\")\nlocal utils = require(\"urlview.utils\")\n\n--- Extracts"
},
{
"path": "lua/urlview/search/init.lua",
"chars": 1921,
"preview": "local M = {}\n\nlocal utils = require(\"urlview.utils\")\nlocal search_helpers = require(\"urlview.search.helpers\")\n\n-- NOTE: "
},
{
"path": "lua/urlview/search/validation.lua",
"chars": 3204,
"preview": "local M = {}\n\nlocal utils = require(\"urlview.utils\")\nlocal config = require(\"urlview.config\")\n\n--- Validates `opts` and "
},
{
"path": "lua/urlview/utils.lua",
"chars": 2730,
"preview": "local M = {}\n\nlocal config = require(\"urlview.config\")\nlocal constants = require(\"urlview.config.constants\")\n\nM.os = vim"
},
{
"path": "selene.toml",
"chars": 12,
"preview": "std = \"vim\"\n"
},
{
"path": "stylua.toml",
"chars": 142,
"preview": "column_width = 120\nline_endings = \"Unix\"\nindent_type = \"Spaces\"\nindent_width = 2\nquote_style = \"AutoPreferDouble\"\nno_cal"
},
{
"path": "tests/init.vim",
"chars": 93,
"preview": "set noswapfile\nset rtp+=../plenary.nvim\nset rtp+=../urlview.nvim\nruntime! plugin/plenary.vim\n"
},
{
"path": "tests/urlview/capture_custom_searches_spec.lua",
"chars": 5204,
"preview": "local search = require(\"urlview.search\")\nlocal search_helpers = require(\"urlview.search.helpers\")\nlocal assert_tbl_same_"
},
{
"path": "tests/urlview/capture_jump_spec.lua",
"chars": 9826,
"preview": "local jump = require(\"urlview.jump\")\nlocal jump_helpers = require(\"tests.urlview.jump_helpers\")\nlocal set_cursor = jump_"
},
{
"path": "tests/urlview/capture_mock_spec.lua",
"chars": 1154,
"preview": "local urlview = require(\"urlview\")\nlocal reset_config = require(\"urlview.config.helpers\").reset_defaults\nlocal search = "
},
{
"path": "tests/urlview/capture_multiple_spec.lua",
"chars": 2173,
"preview": "local urlview = require(\"urlview\")\nlocal extract_links_from_content = require(\"urlview.search.helpers\").content\nlocal re"
},
{
"path": "tests/urlview/capture_process_spec.lua",
"chars": 3191,
"preview": "local urlview = require(\"urlview\")\nlocal search = require(\"urlview.search\")\nlocal search_helpers = require(\"urlview.sear"
},
{
"path": "tests/urlview/capture_single_spec.lua",
"chars": 2405,
"preview": "local urlview = require(\"urlview\")\nlocal reset_config = require(\"urlview.config.helpers\").reset_defaults\nlocal assert_no"
},
{
"path": "tests/urlview/helpers.lua",
"chars": 548,
"preview": "local M = {}\n\nlocal extract_links_from_content = require(\"urlview.search.helpers\").content\n\nfunction M.assert_no_match(c"
},
{
"path": "tests/urlview/jump_helpers.lua",
"chars": 1320,
"preview": "local M = {}\n\nlocal jump = require(\"urlview.jump\")\nlocal utils = require(\"urlview.utils\")\n\nlocal active_windows = {}\n\nfu"
},
{
"path": "vim.toml",
"chars": 811,
"preview": "# SOURCE: https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/vim.toml\n[selene]\nbase = \"lua51\"\nname = \"vim\"\n\n[v"
}
]
About this extraction
This page contains the full source code of the axieax/urlview.nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (76.4 KB), approximately 20.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.