Skip to content

neo451/feed.nvim

Folders and files

NameName
Last commit message
Last commit date
Feb 24, 2025
Feb 23, 2025
Feb 27, 2025
Feb 27, 2025
Feb 27, 2025
Feb 19, 2025
Feb 24, 2025
Feb 18, 2025
Feb 26, 2025
Dec 9, 2024
Oct 10, 2024
Feb 27, 2025
Aug 24, 2024
Feb 25, 2025
Feb 27, 2025
Feb 10, 2025
Feb 24, 2025

Repository files navigation

๐Ÿ“ป feed.nvim

Static Badge Static Badge GitHub Actions Workflow Status LuaRocks Discord

feed.nvim is a web feed reader in Neovim.

image

image

Warning

This project is young, expect breaking changes, and for now there's a nasty bug if you are on neovim stable #125, recommand to use nightly or wait for the coming release of 0.11

other than that usage should be fun and smooth, go ahead and enjoy!

๐ŸŒŸ Features

  • ๐ŸŒฒ Fast and reliable rss/atom/json feed feed parsing, powered by tree-sitter
  • ๐Ÿ“ View entries as beautiful markdown powered by pandoc
  • ๐Ÿช Lua database with no extra dependency
  • ๐Ÿ“š Powerful entry searching by date, tag, feed, regex, and fulltext
  • ๐Ÿ“‚ OPML support to import and export all your feeds and podcasts
  • ๐Ÿงก RSShub integration to discover and track everything
  • :octocat: Github integration to subscrbe to the new commits/release of your favorite repo/plugin
  • ๐Ÿ“ถ libuv powered feed server with a web interface
  • ๐Ÿ“ก support for popular feed sync services like Tiny Tiny RSS and Fresh RSS

๐Ÿš€ Installation

Requirements

  • neovim 0.10+
  • curl
  • pandoc
  • (optional) rg

Basic Installation

For rocks.nvim:

:Rocks install feed.nvim

For lazy.nvim:

{ "neo451/feed.nvim", cmd = "Feed" }

Initial Steps

  • run :checkhealth feed to see your installation status
  • read tutorial below or read :h feed.txt

Commands

Sub commands and arguments

To execute actions available in the current context, or give arguments to the command, use the following syntax:

Use :Feed <Tab>, :Feed update_feed <Tab> to get the completion

Use :Feed<Enter>, :Feed update_feed<Enter> to open menu and select

List of commands

name desc bang
Feed <query> opens a index buffer, a list view based on the filter you passed in no
Feed index opens a index buffer, a list view based on your default filter no
Feed entry opens a entry buffer, a markdown view of the entry content no
Feed update updates all feeds in the database no
Feed update_feed <feed_url> update a single feed in the database no
Feed sync sync the database with your config, removes feeds and entries (if bang) not present yes
Feed search <query> opens picker to live search your database no
Feed grep opens picker to live grep your database (experimental) no
Feed list opens a split to show info about all your feeds in database no
Feed log opens a split to show log no
Feed load_opml <filepath/url> import from an OPML file or url no
Feed export_opml <filepath> export to an OPML file no
Feed web <port> opens a server on a port and opens the web interface in browser no
Feed urlview opens a select UI to picker urls in entry buffer, to open in browser no

Keymaps

By default, feed.nvim will not set any keymaps for you, press ? in to see buffer-local keymaps.

Index buffer

action key
hints ?
dot_repeat .
undo u
redo <C-r>
entry <CR>
split <M-CR>
browser b
refresh r
update R
search s
yank_url Y
untag -
tag +
quit q

Entry buffer

action key
hints ?
browser b
next }
prev {
full f
search s
untag -
tag +
urlview r
yank_url Y
quit q

Manage

From lua

Pass your feeds as list of links and tags in setup

Use Feed update to update all

Use Feed update_feed to update one feed

require("feed").setup({
   feeds = {
      -- These two styles both work
      "https://neovim.io/news.xml",
      {
         "https://neovim.io/news.xml",
         name = "Neovim News",
         tags = { "tech", "news" }, -- tags given are inherited by all its entries
      },

      -- three link formats:
      "https://neovim.io/news.xml", -- Regular links
      "rsshub://rsshub://apnews/topics/apf-topnews" -- RSSHub links
      "neovim/neovim/releases" -- GitHub links
   },
})

From OPML

Use Feed load_opml to import your OPML file

Use Feed export_opml to export your OPML file to load in other readers

Link formats

Regular links

Must start with http or https

RSSHub links

RSSHub links are first class citizens, format is rsshub://{route}

rsshub://{route} will be resolved when fetching according to your config

Discover available {route} in RSSHub documentation rsshub://apnews/topics/apf-topnews will be resolved to https://rsshub.app/apnews/topics/apf-topnews by default

Config example:

require("feed").setup({
   rsshub = {
      instance = "127.0.0.1:1200", -- or any public instance listed here https://rsshub.netlify.app/instances
      export = "https://rsshub.app", -- used in export_opml
   },
})

GitHub links

GitHub user/repo links are also first class citizens,format is [github://]{user/repo}[{/releases|/commits}], so following four all work:

  • neo451/feed.nvim
  • github://neo451/feed.nvim
  • neo451/feed.nvim/releases
  • github://neo451/feed.nvim/releases

For now it defaults to subscribing to the commits

So first two is resolved into https://github.com/neo451/feed.nvim/commits.atom

Latter two is resolved into https://github.com/neo451/feed.nvim/releases.atom

Search

  • use Feed search to filter your feeds
  • you can also pass the query like Feed =neovim +read
  • the default query when you open up the index buffer is +unread @2-weeks-ago

Regex

  • no modifier matches entry title or entry url
  • ! is negative match with entry title or url
  • = is matching feed name and feed url
  • ~ is not matching feed name and feed url
  • these all respect your ignorecase option

Tags

  • + means must_have, searches entries' tags
  • - means must_not_have, searches entries' tags

Date

  • @ means date, searches entries' date
  • 2015-8-10 searches only entries after the date
  • 2-months-ago searches only entries within two months from now
  • 1-year-ago--6-months-ago means entries in the period

Limit

  • # means limit, limits the number of entries

Examples

  • +blog +unread -star @6-months-ago #10 zig !rust

Only Shows 10 entries with tags blog and unread, without tag star, and are published within 6 month, making sure they have zig but not rust in the title.

  • @6-months-ago +unread

Only show unread entries of the last six months. This is the default filter.

  • linu[xs] @1-year-old

Only show entries about Linux or Linus from the last year.

  • -unread +youtube ##10

Only show the most recent 10 previously-read entries tagged as youtube.

  • +unread !n\=vim

Only show unread entries not having vim or nvim in the title or link.

  • +emacs =http://example.org/feed/

Only show entries tagged as emacs from a specific feed.

Grep

Use Feed grep to live grep all entries in your database, requires rg and one of the search backends:

  • telescope
  • fzf-lua
  • mini.pick

Recipes

Change the highlight of the tags section and use emojis and mini.icons for tags
require("feed").setup({
   ui = {
      tags = {
         color = "String",
         format = function(id, db)
            local icons = {
               news = "๐Ÿ“ฐ",
               tech = "๐Ÿ’ป",
               movies = "๐ŸŽฌ",
               games = "๐ŸŽฎ",
               music = "๐ŸŽต",
               podcast = "๐ŸŽง",
               books = "๐Ÿ“š",
               unread = "๐Ÿ†•",
               read = "โœ…",
               junk = "๐Ÿšฎ",
               star = "โญ",
            }

            local get_icon = function(name)
               if icons[name] then
                  return icons[name]
               end
               local has_mini, MiniIcons = pcall(require, "mini.icons")
               if has_mini then
                  local icon = MiniIcons.get("filetype", name)
                  if icon then
                     return icon .. " "
                  end
               end
               return name
            end

            local tags = vim.tbl_map(get_icon, db:get_tags(id))
            table.sort(tags)
            return "[" .. table.concat(tags, ", ") .. "]"
         end,
      },
   },
})
Custom function & keymap for podcast and w3m
local function play_podcast()
   local link = require("feed").get_entry().link
   if link:find("mp3") then
      vim.ui.open(link)
   -- any other player like:
   -- vim.system({ "vlc.exe", link })
   else
      vim.notify("not a podcast episode")
   end
end

local function show_in_w3m()
   if not vim.fn.executable("w3m") then
      vim.notify("w3m not installed")
      return
   end
   local link = require("feed").get_entry().link
   local w3m = require("feed.ui.window").new({
      relative = "editor",
      col = math.floor(vim.o.columns * 0.1),
      row = math.floor(vim.o.lines * 0.1),
      width = math.floor(vim.o.columns * 0.8),
      height = math.floor(vim.o.lines * 0.8),
      border = "rounded",
      style = "minimal",
      title = "Feed w3m",
      zindex = 10,
   })
   vim.keymap.set({ "n", "t" }, "q", "<cmd>q<cr>", { silent = true, buffer = w3m.buf })
   vim.fn.jobstart({ "w3m", link }, { term = true })
   vim.cmd("startinsert")
end

require("feed").setup({
   keys = {
      index = {
         [play_podcast] = "p",
         [show_in_w3m] = "w",
      },
   },
})
Custom colorscheme only set when viewing feeds
local og_color

vim.api.nvim_create_autocmd("User", {
   pattern = "FeedShowIndex",
   callback = function()
      if not og_color then
         og_color = vim.g.colors_name
      end
      vim.cmd.colorscheme("kanagawa-lotus")
   end,
})

vim.api.nvim_create_autocmd("User", {
   pattern = "FeedQuitIndex",
   callback = function()
      vim.cmd.colorscheme(og_color)
   end,
})

Lua API

:TODO:

โค๏ธ Related Projects