From 22bb050a664ac0ac6f665b03a580379128c8c7e1 Mon Sep 17 00:00:00 2001 From: Pranshu Srivastava Date: Mon, 23 Aug 2021 11:25:40 +0530 Subject: [PATCH] Scaffolding. --- .gitignore | 1 + LICENSE | 25 +++++ README.md | 69 +++++++++++++ doc/samwise.txt | 73 ++++++++++++++ plugin/samwise.vim | 242 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 410 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 doc/samwise.txt create mode 100644 plugin/samwise.vim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a497f61 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2021, Pranshu Srivastava +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..768bc32 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# `samwise.nvim` + +## Introduction + + +https://user-images.githubusercontent.com/33557095/131362110-c5f1075e-3569-42c8-9c54-5905364caa34.mp4 + +`samwise` is a line-wise note-taking plugin for neovim that aims to provide a +better note-taking enviroment by employing vim's concepts. It allows you to +take notes in a different buffer (each samwise buffer is unique to its parent +buffer) which is created automatically based on the current buffer's path and +the current buffer's name. + +If the current line in the buffer does not have a corresponding `` +character as the only content in the samwise buffer, the user is dropped to +the corresponding line, and the samwise buffer scrolls sychronously with its +parent buffer and vice versa. Users can make changes, or add new text entries +for any line of the parent buffer in this manner and even commit those besides +the parent buffer, as a "comment file" so as to not populate the parent buffer +with extraneous comments and keep the codebase cleaner in general. This is just +one of the many use cases where samwise can help. + +It also allows highlighting hunks which correspond to the samwise buffer +entries, navigating between them, and seeing those entries either in a floating +window, or by echoing them. + +**To make it explicity clear, from this point on, a "samwise buffer" will mean the +autogenerated buffer that stores all the line-wise notes corresponding to its +companion buffer (or how I call it, it's Frodo).** + +## Configuration + +### Globals + +- `g:samwise_buffer_opts`: Options for opening the samwise buffers. Defaults to: +`"bo " . winheight(0)/10 . "sp"`. +- `g:samwise_dir`: Directory that stores all samwise buffers. +- `g:samwise_echo`: Set to a truthy value to echo samwise buffers' corresponding +contents. +- `g:samwise_float`: Set to a truthy value to show samwise buffers' +corresponding contents in a floating window. +- `g:samwise_floating_opts`: Options for opening the samwise floating windows. +Defaults to: + +```vim +" Refer `nvim_open_win` for more details. +let g:samwise_floating_opts = { + \ ... + \ 'relative': 'cursor', + \ 'focusable': v:false, + \ 'style': 'minimal', + \ 'border': 'shadow', + \ 'noautocmd': v:true + \ } +``` + +- `g:samwise_format`: Extension that the samwise buffers should default to. + +### Commands + +- `:SamwiseMoveBack`: Move to the previous samwise hunk; suggested binding, `[S`. +- `:SamwiseMoveFwd`: Move to the next samwise hunk; suggested binding, `]S`. +- `:SamwiseToggleBuffer`: Open or close the samwise buffer to add or review notes. +- `:SamwiseToggleHighlight`: Highlight all lines in the current buffer that have +corresponding non-empty lines in the samwise buffer. + +## License + +[BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause) diff --git a/doc/samwise.txt b/doc/samwise.txt new file mode 100644 index 0000000..8337f6a --- /dev/null +++ b/doc/samwise.txt @@ -0,0 +1,73 @@ +*samwise.txt* Line-wise notes for neovim. + +Author : Pranshu Srivastava +License : BSD-2-Clause License + English + + *samwise* + +============================================================================== +INDEX *samwise-index* + +INTRODUCTION |samwise-introduction| +COMMANDS |samwise-commands| +CONFIGURATION |samwise-configuration| + +============================================================================== +INTRODUCTION *samwise-introduction* + +|samwise| is a line-wise note-taking plugin for neovim that aims to provide a +better note-taking enviroment by employing vim's concepts. It allows you to +take notes in a different buffer (each samwise buffer is unique to its parent +buffer) which is created automatically based on the current buffer's path and +the current buffer's name. + +If the current line in the buffer does not have a corresponding +character as the only content in the samwise buffer, the user is dropped to +the corresponding line, and the samwise buffer scrolls sychronously with its +parent buffer and vice versa. Users can make changes, or add new text entries +for any line of the parent buffer in this manner and even commit those besides +the parent buffer, as a "comment file" so as to not populate the parent buffer +with extraneous comments and keep the codebase cleaner in general. This is just +one of the many use cases where |samwise| can help. + +It also allows highlighting hunks which correspond to the samwise buffer +entries, navigating between them, and seeing those entries either in a floating +window, or by echoing them. +============================================================================== +CONFIGURATION *samwise-configuration* + +*g:samwise_dir* Directory that stores all the samwise + buffers. +*g:samwise_format* Extension that the samwise buffers + should default to. +*g:samwise_buffer_opts* Options for opening the samwise + buffers. Defaults to: + `"bo " . winheight(0)/10 . "sp"` + +*g:samwise_floating_opts* Options for opening the samwise + floating windows. Defaults to: > + let g:samwise_floating_opts = { + \ ... + \ 'relative': 'cursor', + \ 'focusable': v:false, + \ 'style': 'minimal', + \ 'border': 'shadow', + \ 'noautocmd': v:true + \ } +*g:samwise_echo* Set to a truthy value to echo samwise + buffers corresponding contents. +*g:samwise_float* Set to a truthy value to echo samwise + buffers corresponding contents. +============================================================================== +COMMANDS *samwise-commands* + +*:SamwiseMoveBack* Move to the previous samwise hunk. +*:SamwiseMoveFwd* Move to the next samwise hunk. +*:SamwiseToggleBuffer* Open or close the samwise buffer to + add or review notes. +*:SamwiseToggleHighlight* Highlight all lines in the current + buffer that have corresponding non- + empty lines in the samwise buffer. +============================================================================== +vim:tw=78:ts=8:ft=help:norl: diff --git a/plugin/samwise.vim b/plugin/samwise.vim new file mode 100644 index 0000000..3d5f65a --- /dev/null +++ b/plugin/samwise.vim @@ -0,0 +1,242 @@ +" BSD 2-Clause License Copyright (c) 2021, Pranshu Srivastava et al. All rights reserved. + +scriptencoding utf-8 + +if exists("g:samwise_loaded") || !has("nvim") + finish +endif + +let g:samwise_loaded = v:true + +" Globals {{{ + +if !exists("g:samwise_buffer_opts") | let g:samwise_buffer_opts = "bo " . winheight(0)/10 . "sp" | endif +if !exists("g:samwise_dir") | let g:samwise_dir = $HOME . "/.samwise" | endif +if !exists("g:samwise_echo") | let g:samwise_echo = v:false | endif +if !exists("g:samwise_float") | let g:samwise_float = v:false | endif +if !exists("g:samwise_format") | let g:samwise_format = "txt" | endif +" }}} + +" Script-scoped {{{ + +let s:back_hunks = [] +let s:bufname = "" +let s:content = [] +let s:fwd_hunks = [] +let s:hl_active = v:false +let s:namespace_id = -1 +let s:path = "" +let s:win_id = -1 +" }}} + +function! s:generatePath() abort"{{{ + if expand("%:t") =~ ".*.samwise." . g:samwise_format . "$" + " Keep s:content updated at all times. + let s:content = systemlist("cat " . s:path) + return + endif + let s:bufname = expand("%:t") . "-" . sha256(expand("%:p:h")) . ".samwise." . g:samwise_format + call mkdir(g:samwise_dir, "p", 0700) + let s:path = g:samwise_dir . "/" . s:bufname + if filereadable(s:path) + " Does neovim have any interal API for this? + let s:content = systemlist("cat " . s:path) + endif + " Add hunk logic. + let counter = 0 + let offset = 1 + let content_len = len(s:content) + let s:back_hunks = [] + let s:fwd_hunks = [] + while counter < content_len - 1 + if s:content[counter] != "" && s:content[counter + 1] == "" + call add(s:back_hunks, counter + offset) + endif + if s:content[counter] == "" && s:content[counter + 1] != "" + call add(s:fwd_hunks, counter + offset + 1) + endif + let counter = counter + 1 + endwhile +endfunction"}}} + +function! s:openBuffer() abort"{{{ + setlocal scrollbind + exec g:samwise_buffer_opts . " " . s:path . " | norm " . line(".") . "gg" + call setbufvar(s:bufname, "&number", 1) + call setbufvar(s:bufname, "&scrollbind", v:true) + call setbufvar(s:bufname, "&relativenumber", 0) + call setbufvar(s:bufname, "&scrolloff", 999) +endfunction"}}} + +function! s:closeBuffer() abort"{{{ + if buflisted(s:path) + setlocal noscrollbind + exec "bdelete " . s:path + endif +endfunction"}}} + +"" +" Open or close the corresponding samwise buffer. Please note +" that samwise buffers, by design, do not have their corresponding +" samwise buffers, and so on. Furthermore, opening a samwise buffer +" for the current buffer will open it's own respective samwise buffer +" as defined by g:samwise_dir (defaults to "~/.samwise"), and upon +" closing the samwise buffer, will close *only* the corresponding +" samwise buffer of the current buffer. +function! s:toggleBuffer() abort"{{{ + if !buflisted(s:path) + call s:openBuffer() + else + call s:closeBuffer() + endif +endfunction"}}} + +function! s:highlight() abort"{{{ + let counter = 0 + let s:namespace_id = nvim_create_namespace("samwise") + for line in s:content + if line != "" + call nvim_buf_add_highlight(0, s:namespace_id, "CursorLine", counter, 0, -1) + endif + let counter = counter + 1 + endfor + let s:hl_active = v:true +endfunction"}}} + +function! s:syncHighlight() abort"{{{ + if !s:hl_active + return + endif + call nvim_buf_clear_namespace(0, s:namespace_id, 0, -1) + call s:highlight() +endfunction"}}} + +"" +" Highlight current buffer on the basis of it's corresponding samwise +" buffer. The highlights indicate where a non-null entry is present and +" these entries can be conveniently fetched using either :SamwiseEcho +" or :SamwiseFloat. +function! s:toggleHiglight() abort"{{{ + if exists("s:hl_active") + if s:hl_active + call nvim_buf_clear_namespace(0, s:namespace_id, 0, -1) + let s:hl_active = v:false + else + call s:highlight() + endif + endif +endfunction"}}} + +"" +" Echoes the corresponding line in the samwise buffer. +function! s:echo() abort"{{{ + if !g:samwise_echo + return + endif + if expand("%:t") =~ ".*.samwise." . g:samwise_format . "$" + return + endif + let linenr = line(".") - 1 + if linenr >= len(s:content) + return + endif + let content = s:content[linenr] + if content != "" + echohl Identifier + echon "Samwise.nvim: " + echohl None + echon content + endif +endfunction"}}} + +"" +" Floats the corresponding line in the samwise buffer. +function! s:float() abort"{{{ + if !g:samwise_float + return + endif + if expand("%:t") =~ ".*.samwise." . g:samwise_format . "$" + return + endif + if exists("s:win_id") && getwininfo(s:win_id) != [] + call nvim_win_close(s:win_id, v:false) + endif + if exists("s:content") && s:content != [] + let linenr = line(".") - 1 + if linenr >= len(s:content) + return + endif + let message = s:content[linenr] + let message_len = len(message) + " let factor = 25 + " let col = min([factor, message_len + (factor / 10)]) + " let row = max([1, message_len / col]) + let row = 1 + let col = message_len + let height = row + let width = col + if message == "" + return + endif + let buf_id = nvim_create_buf(v:false, v:false) + call setbufvar(buf_id, "&buftype", "nofile") + call setbufvar(buf_id, "&buflisted", 0) + call setbufvar(buf_id, "&bufhidden", "hide") + " FIXME + " call setbufvar(buf_id, "&wrap", v:true) + " exec bufnr(buf_id) . "bufdo " . "setlocal wrap" + call nvim_buf_set_lines(buf_id, 0, 0, v:true, [message]) + let default_samwise_floating_opts = { + \ 'relative': 'cursor', + \ 'row': row, + \ 'col': col, + \ 'width': width, + \ 'height': height, + \ 'focusable': v:false, + \ 'style': 'minimal', + \ 'border': 'shadow', + \ 'noautocmd': v:true + \ } + if !exists("g:samwise_floating_opts") + let g:samwise_floating_opts = default_samwise_floating_opts + endif + let s:win_id = nvim_open_win(buf_id, v:false, g:samwise_floating_opts) + endif +endfunction"}}} + +"" +" Move to the next samwise hunk. +function! s:moveFwd() abort"{{{ + let cur_pos = line(".") + for pos in s:fwd_hunks + if pos > cur_pos + call cursor(pos, 0) + return + endif + endfor +endfunction"}}} + +"" +" Move to the previous samwise hunk. +function! s:moveBack() abort"{{{ + let cur_pos = line(".") + for pos in reverse(copy(s:back_hunks)) + if pos < cur_pos + call cursor(pos, 0) + return + endif + endfor +endfunction"}}} + +augroup SAMWISE + autocmd BufEnter,BufLeave * :call s:generatePath() + autocmd BufLeave * :setlocal noscrollbind + autocmd FileWritePost * :call s:syncHighlight() + autocmd CursorHold * :call s:float() + autocmd CursorHold * :call s:echo() +augroup END + +command -bar SamwiseMoveBack call s:moveBack() +command -bar SamwiseMoveFwd call s:moveFwd() +command -bar SamwiseToggleBuffer call s:toggleBuffer() +command -bar SamwiseToggleHighlight call s:toggleHiglight()