diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..aa56c5f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +[*.lua] +indent_style = space +indent_size = 3 diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml deleted file mode 100644 index 1f569f6..0000000 --- a/.github/workflows/stylua.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Run stylua -on: - pull_request: ~ - push: - branches: - - main - -jobs: - check: - name: Run stylua format check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: JohnnyMorganz/stylua-action@v4 - with: - token: ${{ secrets.PAT }} - version: latest # NOTE: we recommend pinning to a specific version in case of formatting changes - # CLI arguments - args: --check . diff --git a/.stylua.toml b/.stylua.toml deleted file mode 100755 index 738754a..0000000 --- a/.stylua.toml +++ /dev/null @@ -1,10 +0,0 @@ -column_width = 180 -line_endings = "Unix" -indent_type = "Spaces" -indent_width = 3 -quote_style = "AutoPreferDouble" -call_parentheses = "None" -collapse_simple_statement = "Never" - -[sort_requires] -enabled = false diff --git a/lua/feed/commands.lua b/lua/feed/commands.lua index 250d9ca..5fe824e 100644 --- a/lua/feed/commands.lua +++ b/lua/feed/commands.lua @@ -66,22 +66,7 @@ M.export_opml = { M.search = { doc = "query the database by time, tags or regex", - impl = function(query) - local backend = ut.choose_backend(Config.search.backend) - if query then - ui.refresh { query = query } - elseif ut.in_index() or not backend then - vim.ui.input({ prompt = "Feed query: " }, function(val) - if not val then - return - end - ui.refresh { query = val } - end) - else - local engine = require("feed.ui." .. backend) - pcall(engine.feed_search) - end - end, + impl = ui.search, context = { all = true }, } @@ -384,14 +369,13 @@ end function M._sync_feedlist() for _, v in ipairs(Config.feeds) do local url = type(v) == "table" and v[1] or v - local name = type(v) == "table" and v.name or nil + local title = type(v) == "table" and v.name or nil local tags = type(v) == "table" and v.tags or nil if not feeds[url] then - feeds[url] = { - title = name, - tags = tags, - } + feeds[url] = {} end + feeds[url].title = title or feeds[url].title + feeds[url].tags = tags or feeds[url].tags end db:save_feeds() end diff --git a/lua/feed/config.lua b/lua/feed/config.lua index 7a278e6..0278a58 100644 --- a/lua/feed/config.lua +++ b/lua/feed/config.lua @@ -48,7 +48,6 @@ local default = { number = false, relativenumber = false, modifiable = false, - filetype = "markdown", }, }, ---@type table[] diff --git a/lua/feed/fetch.lua b/lua/feed/fetch.lua index 666023c..51508ae 100755 --- a/lua/feed/fetch.lua +++ b/lua/feed/fetch.lua @@ -1,5 +1,4 @@ local Feedparser = require "feed.parser" -local ut = require "feed.utils" ---@type feed.db local db = require "feed.db" local Config = require "feed.config" @@ -22,55 +21,59 @@ local valid_response = { ---@param url string ---@param opts { force: boolean } function M.update_feed(url, opts, cb) - local tags, last_modified, etag + local tags, last_modified, etag, name if feeds[url] and not opts.force then last_modified = feeds[url].last_modified etag = feeds[url].etag tags = vim.deepcopy(feeds[url].tags) + name = feeds[url].title end - Feedparser.parse(url, { timeout = 10, etag = etag, last_modified = last_modified, cmds = Config.curl_params }, function(d) - local ok = true - if d then - if d.status == 301 then -- permenantly moved - feeds[url] = d.href -- to keep config consistent - url = d.href - elseif not valid_response[d.status] or encoding_blacklist[d.encoding] then - feeds[url] = nil -- TODO: set to false - ok = false - end - if d.entries and not vim.tbl_isempty(d.entries) then - for _, entry in ipairs(d.entries) do - db:add(entry, tags) + Feedparser.parse(url, { timeout = 10, etag = etag, last_modified = last_modified, cmds = Config.curl_params }, + function(d) + local ok = true + if d then + if d.status == 301 then -- permenantly moved + feeds[url] = d.href -- to keep config consistent + url = d.href + elseif not valid_response[d.status] or encoding_blacklist[d.encoding] then + feeds[url] = nil -- TODO: set to false + ok = false + end + if d.entries and not vim.tbl_isempty(d.entries) then + for _, entry in ipairs(d.entries) do + -- TODO: + -- if d.title ~= name then + -- entry.feed = name + -- end + db:add(entry, tags) + end end + feeds[url] = feeds[url] or {} + -- TODO: tags and htmlUrl can change? -- + feeds[url].htmlUrl = feeds[url].htmlUrl or d.link + feeds[url].title = feeds[url].title or d.title + feeds[url].description = feeds[url].desc or d.desc + feeds[url].version = feeds[url].version or d.version + feeds[url].tags = feeds[url].tags or tags -- TDOO: feed tags -- FIX: compare new tgs + feeds[url].last_modified = d.last_modified + feeds[url].etag = d.etag + db:save_feeds() + else + ok = false end - feeds[url] = feeds[url] or {} - -- TODO: tags and htmlUrl can change? -- - feeds[url].htmlUrl = feeds[url].htmlUrl or d.link - feeds[url].title = feeds[url].title or d.title - feeds[url].description = feeds[url].desc or d.desc - feeds[url].version = feeds[url].version or d.version - feeds[url].tags = feeds[url].tags or tags -- TDOO: feed tags -- TODO: compare new tgs - feeds[url].last_modified = d.last_modified - feeds[url].etag = d.etag - db:save_feeds() - else - ok = false - end - return cb(ok) - end) + return cb(ok) + end) end function M.update_all() + local ut = require "feed.utils" local Promise = require "feed.lib.promise" - local fetch = require "feed.fetch" - - local feeds = require("feed.db").feeds local list = require("feed.utils").feedlist(feeds) local c = 0 Promise.map(function(url) - fetch.update_feed(url, {}, function(ok) + require("feed.fetch").update_feed(url, {}, function(ok) local name = ut.url2name(url, feeds) io.write(table.concat({ name, (ok and " success" or " failed"), "\n" }, " ")) diff --git a/lua/feed/ui/format.lua b/lua/feed/ui/format.lua index 011aef3..34a1c3d 100644 --- a/lua/feed/ui/format.lua +++ b/lua/feed/ui/format.lua @@ -8,8 +8,6 @@ local tag2icon = Config.tag2icon -- TODO: this whole module should be user definable --- TODO: move to config - local function cleanup(str) return vim.trim(str:gsub("\n", "")) end @@ -37,29 +35,38 @@ end ---@param entry feed.entry ---@return string +---@return string M.title = function(entry) - return cleanup(entry.title) + return cleanup(entry.title), "@markup.heading" end ---@param entry feed.entry ---@return string +---@return string M.feed = function(entry) - return cleanup(entry.feed) + return cleanup(entry.feed), "@markup.heading" end +---@param entry feed.entry +---@return string +---@return string M.author = function(entry) - return cleanup(entry.author) + return cleanup(entry.author), "@markup.heading" end +---@param entry feed.entry +---@return string +---@return string M.link = function(entry) - return entry.link + return entry.link, "@markup.link.url" end ---@param entry feed.entry ---@return string +---@return string function M.date(entry) ---@diagnostic disable-next-line: return-type-mismatch - return os.date(Config.date_format, entry.time) + return os.date(Config.date_format, entry.time), "@markup.heading" end ---@param entry feed.entry diff --git a/lua/feed/ui/fzf.lua b/lua/feed/ui/fzf.lua new file mode 100644 index 0000000..294724f --- /dev/null +++ b/lua/feed/ui/fzf.lua @@ -0,0 +1,60 @@ +local fzf = require "fzf-lua" +local ui = require "feed.ui" +local db = require "feed.db" +local format = require "feed.ui.format" +local builtin = require "fzf-lua.previewer.builtin" + +local MyPreviewer = builtin.base:extend() + +function MyPreviewer:new(o, opts, fzf_win) + MyPreviewer.super.new(self, o, opts, fzf_win) + setmetatable(self, MyPreviewer) + return self +end + +function MyPreviewer:populate_preview_buf(entry_str) + local tmpbuf = self:get_tmp_buffer() + self:set_preview_buf(tmpbuf) + local id = entry_str:sub(-64, -1) + ui.show_entry { buf = tmpbuf, id = id } + vim.treesitter.start(tmpbuf, "markdown") + self.win:update_scrollbar() +end + +-- Disable line numbering and word wrap +function MyPreviewer:gen_winopts() + local new_winopts = { + wrap = true, + number = false, + conceallevel = 3, + } + return vim.tbl_extend("force", self.winopts, new_winopts) +end + +-- TODO: overide the default .. s +-- TODO: grep + +local function feed_search() + fzf.fzf_live(function(query) + local on_display = ui.refresh { query = query, show = false } + local ret = {} + for i, id in ipairs(on_display) do + ret[i] = format.entry(db[id]) .. (" "):rep(100) .. id + end + return ret + end, { + prompt = "> ", + exec_empty_query = true, + previewer = MyPreviewer, + actions = { + ["ctrl-y"] = function(selected) + local id = selected[1]:sub(-64, -1) + ui.show_entry { id = id } + end, + }, + }) +end + +return { + feed_search = feed_search, +} diff --git a/lua/feed/ui/init.lua b/lua/feed/ui/init.lua index 059ff18..90edb9b 100755 --- a/lua/feed/ui/init.lua +++ b/lua/feed/ui/init.lua @@ -92,7 +92,7 @@ local function get_entry(opts) id = opts.id if ut.in_index() then current_index = ut.get_cursor_row() - else + elseif on_display then for i, v in ipairs(on_display) do if v == id then current_index = i @@ -130,11 +130,15 @@ local function render_entry(buf, lines, id, is_preview) if not api.nvim_buf_is_valid(buf) then return end + + vim.wo.winbar = nil + vim.bo[buf].filetype = "markdown" vim.bo[buf].modifiable = true for i, v in ipairs(lines) do if type(v) == "table" then v:render(buf, -1, i) elseif type(v) == "string" then + v = v:gsub("\n", "") api.nvim_buf_set_lines(buf, i - 1, i, false, { v }) end end @@ -153,7 +157,7 @@ end ---@field row? integer default to cursor row ---@field id? string db_id ---@field buf? integer buffer to populate ----@field fp? string path to raw html +---@field link? string url to raw html ---temparay solution for getting rid of junks and get clean markdown ---@param lines string[] @@ -200,13 +204,12 @@ local function show_entry(opts) local lines = {} for i, v in ipairs { "title", "author", "feed", "link", "date" } do - lines[i] = NuiLine { NuiText(capticalize(v) .. ": ", "title"), NuiText(Format[v](entry)) } + lines[i] = NuiLine { NuiText(capticalize(v) .. ": ", "@markup.bold"), NuiText(Format[v](entry)) } end table.insert(lines, "") - vim.wo.winbar = nil Markdown.convert( - opts.fp or DB.dir .. "/data/" .. id, + opts.link or DB.dir .. "/data/" .. id, vim.schedule_wrap(function(markdown_lines) vim.list_extend(lines, entry_filter(markdown_lines)) render_entry(buf, lines, id, opts.buf ~= nil) @@ -218,7 +221,7 @@ local function show_full() local entry = get_entry() if entry and entry.link then vim.schedule(function() - show_entry { fp = entry.link } + show_entry { link = entry.link } end) else vim.notify "no link to fetch" @@ -247,7 +250,6 @@ end local function restore_state() vim.cmd "set cmdheight=1" - vim.wo.winbar = "" -- TODO: restore the user's old winbar is there is pcall(vim.cmd.colorscheme, og_colorscheme) end @@ -387,6 +389,26 @@ local function show_feeds() tree:render() end +---In Index: prompt for input and refresh +---Everywhere else: openk search backend +---@param q string +local function search(q) + local backend = ut.choose_backend(Config.search.backend) + if q then + refresh { query = q } + elseif ut.in_index() or not backend then + vim.ui.input({ prompt = "Feed query: ", default = query .. " " }, function(val) + if not val then + return + end + refresh { query = val } + end) + else + local engine = require("feed.ui." .. backend) + pcall(engine.feed_search) + end +end + return { show_index = show_index, show_entry = show_entry, @@ -402,5 +424,6 @@ return { get_entry = get_entry, open_url = open_url, quit = quit, + search = search, refresh = refresh, } diff --git a/lua/feed/ui/markdown.lua b/lua/feed/ui/markdown.lua index a208a9e..1d1b198 100644 --- a/lua/feed/ui/markdown.lua +++ b/lua/feed/ui/markdown.lua @@ -1,6 +1,8 @@ local health = require "feed.health" local ut = require "feed.utils" +--- FIX: wrap with current window width, and config.opt wrap false + ---@param fp string ---@param cb fun(lines: string[]) local function convert(fp, cb) @@ -17,6 +19,7 @@ local function convert(fp, cb) "-t", filter, "--wrap=none", + -- "--columns=" .. vim.api.nvim_win_get_width(0), fp, } vim.system(cmd, { text = true }, function(obj) diff --git a/lua/feed/ui/pick.lua b/lua/feed/ui/pick.lua index 40d0f0a..f0f7494 100644 --- a/lua/feed/ui/pick.lua +++ b/lua/feed/ui/pick.lua @@ -1,6 +1,6 @@ local MiniPick = require "mini.pick" -local db = require "feed.db" local ui = require "feed.ui" +local db = require "feed.db" local format = require "feed.ui.format" local config = require "feed.config" @@ -46,11 +46,12 @@ local function feed_search() match = match, show = show, preview = function(buf_id, id) - ui.show_entry { buf = buf_id, id = id, untag = false } + ui.show_entry { buf = buf_id, id = id } local win = vim.api.nvim_get_current_win() for key, value in pairs(config.options.entry) do pcall(vim.api.nvim_set_option_value, key, value, { buf = buf_id }) pcall(vim.api.nvim_set_option_value, key, value, { win = win }) + vim.treesitter.start(buf_id, "markdown") end end, choose = function(id) diff --git a/lua/feed/utils/url.lua b/lua/feed/utils/url.lua index 6102dba..3fe92b3 100644 --- a/lua/feed/utils/url.lua +++ b/lua/feed/utils/url.lua @@ -60,8 +60,6 @@ M.get_buf_urls = function(buf, cur_link) local text = vim.treesitter.get_node_text(node:child(2), buf, { metadata = metadata[url] }) local row = node:child(1):range() + 1 ret[#ret + 1] = { text, link } - local sub_pattern = row .. "s/(" .. fn.escape(link, "/~") .. ")//g" -- TODO: add e flag in final - vim.cmd(sub_pattern) else ret[#ret + 1] = { link, link } end