File: quickmarks.lua

package info (click to toggle)
luakit 1%3A2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,844 kB
  • sloc: ansic: 12,519; makefile: 140; ruby: 79; sh: 48
file content (298 lines) | stat: -rw-r--r-- 10,587 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
--- Vimperator-style quickmarking.
--
-- Inspired by vimperator's quickmarks feature, this module allows you to
-- associate up to sixty-two websites with a set of easy-to-type keybindings.
-- Users can then type a three-keystroke command to open any of these websites
-- in the current tab, a new tab, or a new window.
--
-- # Adding a new quickmark
--
-- You can mark any url by pressing `M{a-zA-Z0-9}`. This will save the url
-- of the current page, creating a new shortcut or overwriting an existing
-- one.
--
-- Every quickmark mapping is saved in the `quickmarks` file in the luakit data
-- directory, and is shared between multiple luakit instances.
--
-- # Jumping to a marked url
--
-- After adding a quickmark, you can open it in the current window with
-- `go{a-zA-Z0-9}`, or in a new tab with `gn{a-zA-Z0-9}`. To list all
-- quickmarks, run `:qmarks`.
--
-- # Managing quickmarks
--
-- As well as using the included quickmarks manager and various commands, you
-- can directly edit the `quickmarks` file in the luakit data directory.
--
-- # Files and Directories
--
-- - The quickmarks file is called `quickmarks` and is in the luakit data
-- directory.
--
-- @module quickmarks
-- @author Piotr HusiatyƄski <phusiatynski@gmail.com>
-- @author Mason Larobina <mason.larobina@gmail.com>

-- Get luakit environment
local lousy = require("lousy")
local window = require("window")
local new_mode = require("modes").new_mode
local binds, modes = require("binds"), require("modes")
local add_binds, add_cmds = modes.add_binds, modes.add_cmds
local menu_binds = binds.menu_binds

local _M = {}

local qmarks
local quickmarks_file = luakit.data_dir .. '/quickmarks'

local function check_token(token)
    assert(string.match(tostring(token), "^(%w)$"), "invalid token: " .. tostring(token))
    return token
end

--- Load quick bookmarks from storage file into memory.
-- @tparam string fd_name Bookmarks storage file path or `nil` to use default one.
function _M.load(fd_name)
    if not qmarks then qmarks = {} end

    fd_name = fd_name or quickmarks_file
    if not os.exists(fd_name) then return end

    for line in io.lines(fd_name) do
        local token, uris = string.match(lousy.util.string.strip(line), "^(%w)%s+(.+)$")
        if token then
            qmarks [token] = lousy.util.string.split(uris, ",%s+")
        end
    end
end

--- Save quick bookmarks to file.
-- @tparam string fd_name Bookmarks storage file path or `nil` to use default one.
function _M.save(fd_name)
    -- Quickmarks init check
    if not qmarks then _M.load() end

    local fd = io.open(fd_name or quickmarks_file, "w")
    for _, token in ipairs(lousy.util.table.keys(qmarks )) do
        local uris = table.concat(qmarks [token], ", ")
        fd:write(string.format("%s %s\n", token, uris))
    end
    io.close(fd)
end

--- Return URI related to given key or nil if does not exist.
-- @tparam string token Quick bookmarks mapping token.
-- @tparam boolean load_file Call `quickmark.load()` before retrieving the URI.
function _M.get(token, load_file)
    -- Load quickmarks from other sessions
    if not qmarks or load_file ~= false then _M.load() end

    return qmarks[check_token(token)]
end

--- Return a list of all the tokens in the quickmarks table.
function _M.get_tokens()
    if not qmarks then _M.load() end
    return lousy.util.table.keys(qmarks )
end

--- Set new quick bookmarks mapping.
-- @tparam string token The token under which given uris will be available.
-- @tparam string|{string} uris List of locations to quickmark.
-- @tparam boolean load_file Call `quickmark.load()` before adding the mapping.
-- @tparam boolean save_file Call `quickmark.save()` after adding the mapping.
function _M.set(token, uris, load_file, save_file)
    -- Load quickmarks from other sessions
    if not qmarks or load_file ~= false then _M.load() end

    -- Parse uris: "http://forum1.com, google.com, imdb some artist"
    if uris and type(uris) == "string" then
        uris = lousy.util.string.split(uris, ",%s+")
    elseif uris and type(uris) ~= "table" then
        error("invalid locations type: ", type(uris))
    end

    qmarks[check_token(token)] = uris

    -- By default, setting new quickmark saves them to
    if save_file ~= false then _M.save() end
end

--- Delete a quickmark.
-- @tparam string token The quickmark token.
-- @tparam boolean load_file Call `quickmark.load()` before deletion.
-- @tparam boolean save_file Call `quickmark.save()` after deletion.
function _M.del(token, load_file, save_file)
    -- Load quickmarks from other sessions
    if not qmarks or load_file ~= false then _M.load() end

    qmarks[check_token(token)] = nil
    if save_file ~= false then _M.save() end
end

--- Delete all quickmarks.
-- @tparam boolean save_file Call quickmark.save() function.
function _M.delall(save_file)
    qmarks = {}
    if save_file ~= false then _M.save() end
end

local actions = {
    quickmark_delete = {
        desc = "Delete a quickmark or all quickmarks.",
        func = function (w, o)
            if o.bang then
                _M.delall()
                return
            end
            local a = o.arg
            if not a or a == "" then w:error("missing argument") return end
            -- Find and del all range specifiers
            string.gsub(a, "(%w%-%w)", function (range)
                range = "["..range.."]"
                for _, token in ipairs(_M.get_tokens()) do
                    if string.match(token, range) then _M.del(token, false) end
                end
            end)
            -- Delete lone tokens
            string.gsub(a, "(%w)", function (token) _M.del(token, false) end)
            _M.save()
        end,
    },
}

-- Add quickmarking binds to normal mode
add_binds("normal", {
    { "^g[onw][a-zA-Z0-9]$", [[Jump to quickmark in current tab with `go{a-zA-Z0-9}`,
            `gn{a-zA-Z0-9}` to open in new tab and or `gw{a-zA-Z0-9}` to open a
            quickmark in a new window.]],
            function (w, o, m)
                local mode, token = string.match(o.buffer, "^g(.)(.)$")
                local uris = lousy.util.table.clone(_M.get(token) or {})
                for c=1,m.count do
                    if mode == "w" then
                        window.new(uris)
                    else
                        for i, uri in ipairs(uris or {}) do
                            if mode == "o" and c == 1 and i == 1 then w:navigate(uri)
                        else w:new_tab(uri, {switch = i == 1}) end
                    end
                end
            end
        end, {count=1} },

    { "^M[a-zA-Z0-9]$", "Add quickmark for current URL.",
        function (w, o)
            local token = string.match(o.buffer, "^M(.)$")
            local uri = w.view.uri
            _M.set(token, {uri})
            w:notify(string.format("Quickmarked %q: %s", token, uri))
        end },
})

-- Add quickmarking commands
add_cmds({
    -- Quickmark add (`:qmark f http://forum1.com, forum2.com, imdb some artist`)
    { ":qma[rk]", "Add a quickmark.", function (w, o)
            local a = o.arg
            local token, uris = string.match(lousy.util.string.strip(a), "^(%w)%s+(.+)$")
            assert(token, "invalid token")
            uris = lousy.util.string.split(uris, ",%s+")
            _M.set(token, uris)
            w:notify(string.format("Quickmarked %q: %s", token, table.concat(uris, ", ")))
        end },

    -- Quickmark edit (`:qmarkedit f` -> `:qmark f furi1, furi2, ..`)
    { ":qmarkedit, :qme", "Edit a quickmark.", function (w, o)
        local a = o.arg
        local token = lousy.util.string.strip(a)
        assert(#token == 1, "invalid token length: " .. token)
        local uris = _M.get(token)
        w:enter_cmd(string.format(":qmark %s %s", token, table.concat(uris or {}, ", ")))
    end },

    -- Quickmark del (`:delqmarks b-p Aa z 4-9`)
    { ":delqm[arks]", actions.quickmark_delete },

    -- View all quickmarks in an interactive menu
    { ":qmarks", "List all quickmarks.", function (w) w:set_mode("qmarklist") end },
})

-- Add mode to display all quickmarks in an interactive menu
new_mode("qmarklist", {
    enter = function (w)
        local rows = {{ "Quickmarks", " URI(s)", title = true }}
        for _, qmark in ipairs(_M.get_tokens()) do
            local uris = lousy.util.escape(table.concat(_M.get(qmark, false), ", "))
            table.insert(rows, { "  " .. qmark, " " .. uris, qmark = qmark })
        end
        w.menu:build(rows)
        w:notify("Use j/k to move, d delete, e edit, t tabopen, w winopen.", false)
    end,

    leave = function (w)
        w.menu:hide()
    end,
})

-- Add additional binds to quickmarks menu mode
add_binds("qmarklist", lousy.util.table.join({
    -- Delete quickmark
    { "d", "Delete the currently highlighted quickmark entry.",
        function (w)
            local row = w.menu:get()
            if row and row.qmark then
                _M.del(row.qmark)
                w.menu:del()
            end
        end },

    -- Edit quickmark
    { "e", "Edit the currently highlighted quickmark entry.",
        function (w)
            local row = w.menu:get()
            if row and row.qmark then
                local uris = _M.get(row.qmark)
                w:enter_cmd(string.format(":qmark %s %s",
                    row.qmark, table.concat(uris or {}, ", ")))
            end
        end },

    -- Open quickmark
    { "<Return>", "Open the currently highlighted quickmark entry in the current tab.",
        function (w)
            local row = w.menu:get()
            if row and row.qmark then
                for i, uri in ipairs(_M.get(row.qmark) or {}) do
                    if i == 1 then w:navigate(uri) else w:new_tab(uri, { switch = false }) end
                end
            end
        end },

    -- Open quickmark in new tab
    { "t", "Open the currently highlighted quickmark entry in a new tab.",
        function (w)
            local row = w.menu:get()
            if row and row.qmark then
                for _, uri in ipairs(_M.get(row.qmark) or {}) do
                    w:new_tab(uri, { switch = false })
                end
            end
        end },

    -- Open quickmark in new window
    { "w", "Open the currently highlighted quickmark entry in a new window.",
        function (w)
            local row = w.menu:get()
            w:set_mode()
            if row and row.qmark then
                window.new(_M.get(row.qmark) or {})
            end
        end },
}, menu_binds))

return _M

-- vim: et:sw=4:ts=8:sts=4:tw=80