flashcards.nvim

Flashcards neovim plugin
git clone git://git.laack.co/flashcards.nvim.git
Log | Files | Refs

init.lua (4721B)


      1 
      2 local state = {
      3     buf = nil,
      4     win = nil,
      5     cards = {},
      6     index = 1,
      7     showing_back = false,
      8 }
      9 
     10 local function shuffle(t)
     11     for i = #t, 2, -1 do
     12         local j = math.random(i)
     13         t[i], t[j] = t[j], t[i]
     14     end
     15     return t
     16 end
     17 
     18 local function formatCard(strArr)
     19 
     20     local result = {}
     21     local firstNonBlank = false
     22 
     23     for i = 2, #strArr do
     24         if strArr[i] == "" and firstNonBlank == false then
     25             goto continue
     26         end
     27 
     28         firstNonBlank = true
     29         table.insert(result, strArr[i])
     30 
     31         ::continue::
     32     end
     33 
     34     return result
     35 end
     36 
     37 local function formatH1(str)
     38     if not str then
     39         return ""
     40     end
     41 
     42     if #str >= 3 then
     43         return str:sub(3)
     44     end
     45 
     46     return ""
     47 end
     48 
     49 local function load_cards()
     50 
     51     -- clear state
     52     state.cards = {}
     53     state.index = 1
     54     state.showing_back = false
     55 
     56     local cwd = vim.fs.dirname(vim.api.nvim_buf_get_name(0))
     57     local files = vim.split(vim.fn.glob(cwd .. "/*.md"), "\n")
     58     files = shuffle(files)
     59 
     60     for index, _ in ipairs(files) do
     61         local file = vim.fn.readfile(files[index])
     62         -- add card to state
     63         table.insert(state.cards, {
     64             formatH1(file[1]), formatCard(file)
     65         })
     66     end
     67 end
     68 
     69 local function render()
     70     vim.bo[state.buf].modifiable = true
     71     vim.api.nvim_buf_set_lines(state.buf, 0, -1, false, {})
     72     if state.showing_back then
     73         vim.api.nvim_buf_set_lines(state.buf, 0, 10, false, state.cards[state.index][2])
     74     else
     75         vim.api.nvim_buf_set_lines(state.buf, 0, 10, false, {state.cards[state.index][1]})
     76     end
     77     vim.bo[state.buf].modifiable = false
     78 end
     79 
     80 
     81 local function previous_card()
     82     state.index = state.index - 1
     83     state.showing_back = false
     84 
     85     if state.index < 1 then
     86         state.index = #state.cards
     87     end
     88 
     89     render()
     90 end
     91 
     92 local function next_card()
     93     state.index = state.index + 1
     94     state.showing_back = false
     95 
     96     if state.index > #state.cards then
     97         state.index = 1
     98     end
     99 
    100     render()
    101 end
    102 
    103 local function flip_card()
    104     state.showing_back = not state.showing_back
    105     render()
    106 end
    107 
    108 local function flash()
    109 
    110     -- this also clears the card state.
    111     load_cards()
    112 
    113     -- if this was already opened close and reopen because that's easier than figuring out if the window
    114     -- or buffer is rendered right now.
    115 
    116 
    117     if state.buf ~= nil then
    118         vim.api.nvim_buf_delete(state.buf, { force = true })
    119         state.buf = nil
    120         state.win = nil -- do windows have to be closed? presumably not, I think the window abstraction is simply a view into a buffer
    121                         -- which would then be closed by this point thus requiring no cleanup.
    122     end
    123 
    124     state.win = vim.api.nvim_get_current_win()
    125     state.buf = vim.api.nvim_create_buf(false, true)
    126     
    127     vim.api.nvim_buf_set_keymap(state.buf, "n", "<Left>", ":PreviousCard<CR>", {})
    128     vim.api.nvim_buf_set_keymap(state.buf, "n", "<Right>", ":NextCard<CR>", {})
    129     vim.api.nvim_buf_set_keymap(state.buf, "n", "<Up>", ":Flip<CR>", {})
    130     vim.api.nvim_buf_set_keymap(state.buf, "n", "<Down>", ":Flip<CR>", {})
    131 
    132     vim.api.nvim_buf_set_keymap(state.buf, "n", "h", ":PreviousCard<CR>", {})
    133     vim.api.nvim_buf_set_keymap(state.buf, "n", "l", ":NextCard<CR>", {})
    134     vim.api.nvim_buf_set_keymap(state.buf, "n", "k", ":Flip<CR>", {})
    135     vim.api.nvim_buf_set_keymap(state.buf, "n", "j", ":Flip<CR>", {})
    136 
    137     local row = 0
    138     local col = 0
    139 
    140     local width = vim.fn.winwidth(state.win)
    141     local height = vim.fn.winheight(state.win)
    142 
    143     local opts = {
    144       width = width,
    145       height = height,
    146       row = row,
    147       col = col,
    148       border = "rounded",
    149       relative = "win",
    150       title = "Flashcards",
    151       title_pos = "center"
    152     }
    153 
    154     local _ = vim.api.nvim_open_win(state.buf, true, opts)
    155 
    156     render()
    157 
    158 
    159 end
    160 
    161 vim.api.nvim_create_user_command('Flash', flash, {})
    162 vim.api.nvim_create_user_command('Flip', flip_card, {})
    163 vim.api.nvim_create_user_command('NextCard', next_card, {}) -- defaults first side
    164 vim.api.nvim_create_user_command('PreviousCard', previous_card, {}) -- defaults first side
    165 
    166 -- TODO: Perhaps refactor 'Flash'
    167 -- TODO: Make commands more sensibly named
    168     -- how does vimwiki do it? Namespacing perhaps?
    169 -- TODO: Smarter selection
    170     -- would be cool to have a state directory that upweights based on some sort of recency / failure stuff
    171     -- would be cool to have better tagging; tag cards based on categories of things or something like that?
    172         -- perhaps you are expected to just put stuff in a directory for this?
    173 -- TODO: Pass in keybindings as opts
    174 -- TODO: Should I block the modification of flashcards or should I allow it with the ability to write the results
    175     -- back to the file?