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?