├── .gitignore ├── README.md ├── README_mac.md ├── doc └── vim-geeknote.txt ├── img └── explorer.png ├── plugin ├── change.py ├── conn.py ├── enml.py ├── explorer.py ├── utils.py ├── view.py ├── vim_geeknote.py └── vim_geeknote.vim ├── powerline ├── config_files │ ├── plugin_geeknote-explorer.json │ └── plugin_geeknote.json ├── matchers │ └── geeknote.py └── segments │ └── geeknote.py └── syntax └── geeknote.vim /.gitignore: -------------------------------------------------------------------------------- 1 | plugin/*.pyc 2 | doc/tags 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-geeknote 2 | 3 | [Geeknote](http://www.geeknote.me) Plugin for Vim. Under active development. 4 | 5 | ## Intro 6 | 7 | Integrates Geeknote/Evernote into Vim and Neovim. 8 | - Notebook navigation, creation, and renaming 9 | - Note viewing, renaming, editing, and creation. 10 | 11 | ## Screenshots 12 | 13 | ![img](https://github.com/neilagabriel/vim-geeknote/blob/master/img/explorer.png) 14 | 15 | ## Dependencies/Requirements 16 | 17 | - Vim 7.4.364 or newer (issues observed with earlier versions) 18 | - Linux or OSX (not tested on Windows) 19 | 20 | ## Installation 21 | 22 | 1. If you have not done so already, install [Geeknote](http://www.geeknote.me) 23 | and login to make sure it is functional. You must login before attempting to 24 | use the plugin. 25 | 26 | 2. Use your plugin manager of choice to install plugin. 27 | 28 | * [Vundle](https://github.com/gmarik/vundle) 29 | * Add `Bundle 'https://github.com/neilagabriel/vim-geeknote'` to .vimrc 30 | * Run `:BundleInstall` 31 | * [Pathogen](https://github.com/tpope/vim-pathogen) 32 | * `git clone https://github.com/neilagabriel/vim-geeknote ~/.vim/bundle/vim-geeknote` 33 | 34 | 3. If using macvim, refer to [README_mac.md](README_mac.md) for additional instructions. 35 | 36 | ## Optional Setup and Configuration 37 | 38 | ### Quick toggle 39 | 40 | noremap :Geeknote 41 | 42 | ### Note format 43 | 44 | Use the option `g:GeeknoteFormat` to set the format mode used when saving notes 45 | to Geeknote. This is the equivalent of the `--format` options that Geeknote 46 | supports. E.g.: 47 | 48 | let g:GeeknoteFormat="markdown" 49 | 50 | **Warning:** It is not advised that you use this option, use it at your own 51 | risk. The issue is that in the process of converting your notes from markdown 52 | to HTML and back, content is often reformatted. Worse is that if you edit and 53 | save a note that has been reformatted, there is even a large potential to lose 54 | content! This is a side-effect of the various tools/libs that Geeknote uses in 55 | performing the conversions. With the default settings, notes saved with 56 | vim-geeknote bypass these format conversions. This means that notes will be 57 | saved exactly as they appear within Vim. It also means that the notes will 58 | appear in plain-text when viewed outside of Vim in Evernote. If you plan on 59 | creating, editing, and viewing your notes in Vim, this trade-off is well worth 60 | it. And of course it does not mean that you cannot use the markdown format in 61 | your notes. 62 | 63 | ### Navigation Window Behavior 64 | 65 | #### Limit Width 66 | 67 | By default, vim-geeknote will attempt to resize the navigation window based on 68 | its current content, up to 40 columns. If you have notebooks or notes with very 69 | long names you may want to use the following option: 70 | 71 | let g:GeeknoteMaxExplorerWidth= 72 | 73 | Where `` is replaced with the max width of the window. Depending on the 74 | value you specify, this could be used to increase the cap in order to view 75 | longer names or decrease it to keep the navigation window even smaller. 76 | 77 | #### Fix Width 78 | 79 | Use the following option to fix the width of the window to a specific value: 80 | 81 | let g:GeeknoteExplorerWidth= 82 | 83 | Where `` is replaced with the desired width of the window. This option 84 | overrides all other width-related options. 85 | 86 | #### Limit View to Specific Notebooks 87 | 88 | ##### By GUID 89 | 90 | By default, all notebooks will be shown in the navigation window. Depending on 91 | the number notebooks you have, this can add a non-trivial amount of time to the 92 | load time of the plugin. You may also simply not want to see your full set of 93 | notebooks when working in Vim. Use the following option to limit the display to 94 | a specific notebook or set of notebooks: 95 | 96 | let g:GeeknoteNotebooks= 97 | \ [ 98 | \ 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee', 99 | \ 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee', 100 | \ ] 101 | 102 | ##### By Expression 103 | 104 | Notebooks can also be filtered by expressions. Use the following option to 105 | specify a list of regular expressions to apply to each notebook to decide if it 106 | should be included in the navigation window. Unlike filtering by GUID, this 107 | option does *not* yield a performance improvement. In fact, it may slightly 108 | degrade it depending on the number of expressions you specify. 109 | 110 | let g:GeeknoteNotebookFilters= 111 | \ [ 112 | \ 'Unsorted' , 113 | \ '^Status - WW(\d+)$', 114 | \ ] 115 | 116 | ### Explorer Special Characters 117 | 118 | The following options may be used to customize the characters used to denote 119 | opened/closed notebooks and tags: 120 | 121 | let g:GeeknoteExplorerNodeClosed = '+' 122 | let g:GeeknoteExplorerNodeOpened = '-' 123 | 124 | Both unicode and ascii characters are supported. 125 | 126 | ### Launching 127 | 128 | It may sometimes be convenient to launch geeknote in a new instance of Vim. An 129 | alias can be helpful for this. Here an example for `bash`: 130 | 131 | alias vim-geeknote='vi -c Geeknote' 132 | 133 | ### Powerline 134 | 135 | Powerline may be used to improve the look of the navigation window as well as 136 | any notes that you open. At this time however, if you'd like this support, 137 | you'll need to use my personal powerline fork locate here: 138 | 139 | https://github.com/neilagabriel/powerline.git 140 | 141 | Just install it in the normal fashion and everything should just work. 142 | 143 | ### Geeknote Autocommands 144 | 145 | vim-geeknote uses FileType `geeknote` for the navigation window. This may be 146 | used to set your own custom behavior. For example, the following disables line 147 | numbers in the navigation window: 148 | 149 | autocmd FileType geeknote setlocal nonumber 150 | 151 | ### Filesystem 152 | 153 | vim-geeknote uses temporary files to display the navigation window and for any 154 | notes that are opened. By default, files are created in the system temp 155 | directory (ie. `TMPDIR`, `TEMP`, or `TMP`). The following option allows the 156 | user to specify where all temp (or scratch) files should be maintained. Note 157 | that vim-geeknote will attempt to cleanup after itself when it is proper to do 158 | so. 159 | 160 | let g:GeeknoteScratchDirectory= 161 | 162 | Where `` is replaced with the desired filesystem location for any and all 163 | temp files created by vim-geeknote. *Note that the path must exist.* The plugin 164 | will not attempt to create it. 165 | 166 | ## Usage 167 | 168 | ### Toggle Geeknote Navigation Window 169 | 170 | Use `:Geeknote` to open/toggle the geeknote navigation window. If the 171 | navigation window is not visible, this command will split the current window 172 | vertically and display the navigation window on the left-side. If visible, the 173 | navigation window will be hidden. Notebooks can be expanded to show the notes 174 | they contain. To expand a notebook, simply navigate to the name of the 175 | notebook and hit ``. Hit `` again to close the notebook. To 176 | open/view a note, navigate to the note and hit ``. The note will be 177 | displayed in the previous window if it is possible to do so or in a new 178 | vertical split. To save changes to the note, simply save the buffer (e.g. 179 | `:w`). The title of the note will be shown on the first line. The title line 180 | should not be deleted from the buffer but *may* be modified to rename the note. 181 | 182 | ### Creating Notebooks 183 | 184 | Use `:GeeknoteCreateNotebook ` to create a new notebook. 185 | 186 | ### Creating Notes 187 | 188 | Use `:GeeknoteCreateNote ` to create a new note. The note will be created 189 | in the notebook currently selected in the navigation window. If a notebook is 190 | not selected, the note will be created in the default notebook. A new buffer 191 | for the note will be displayed in the previous window if it is possible to do 192 | so or in a new vertical split. 193 | 194 | Use `:GeeknoteSaveAsNote` to create a new note using the content of the current 195 | buffer. The first line in the buffer will be used as the note's title. The 196 | remainder of the buffer will be saved as note content. Note this this command 197 | will create and switch to a new buffer. 198 | 199 | ### Renaming Notebooks and Notes 200 | 201 | To rename notebooks or notes, simply modify the name of the notebook/note in 202 | the navigation window and save the bugger (e.g. `:w`). Any number of changes 203 | can be made before saving, but be sure not to modify an item's GUID. 204 | 205 | ### Moving Notes 206 | 207 | To move a note into a different notebook, simply move the note's text (includes 208 | title and GUID) under the desired notebook in the navigation window and save 209 | the buffer. Similar to renaming, any number notes can be moved before saving 210 | the buffer. 211 | 212 | ### Synchronization 213 | 214 | Use `:GeeknoteSync` to update the navigation with the latest data on the 215 | Evernote server. Warning, any notes that are opened when this command is issued 216 | will not be updated. Support for this will be added in future releases. 217 | 218 | ### Searching 219 | 220 | Use `:GeeknoteSearch ` to search for notes with specific `text` in their 221 | titles' and/or content. All results will be added to the nagivation window. 222 | 223 | ## Acknowledgments 224 | 225 | - [Geeknote](http://www.geeknote.me) 226 | - [The Nerd Tree](https://github.com/scrooloose/nerdtree) 227 | - [Vim Plugin Starter Kit](https://github.com/JarrodCTaylor/vim-plugin-starter-kit) 228 | 229 | ## Todo 230 | 231 | - Refresh open notes upon `:GeeknoteSync` 232 | - Complete tag support (adding tags, applying/removing to/from notes) 233 | - Customizable note display 234 | - Improved notebook creation process 235 | 236 | ## License 237 | 238 | Copyright (c) Neil Gabriel. Distributed under the same terms as Vim itself. 239 | See `:help license`. 240 | -------------------------------------------------------------------------------- /README_mac.md: -------------------------------------------------------------------------------- 1 | # Macvim Installation Instructions 2 | 3 | (Instructions provided by [Nathan Farrar](https://github.com/nfarrar) - Thanks!) 4 | 5 | On OSX it's common for users to install python using homebrew and set it as the 6 | default python interpreter in their shellrc files. Then, if you install 7 | vgeeknote, the libs & geeknote run using that python, not the system python. 8 | 9 | When installing macvim, it will use the system's python interpreter by default 10 | rather than the homebrew installed python. If you attempt to run a python 11 | script that that requires dependencies that were installed to the homebrew 12 | python rather than the system python, you just get nasty error messages. 13 | 14 | To see if this is the issue, you can run: 15 | 16 | vim --version 17 | 18 | And if you see: 19 | 20 | -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 21 | 22 | Then the problem is the one described above. The fix is to uninstall macvim, 23 | then reinstall with the following flags: 24 | 25 | brew install macvim --with-lua --override-system-vim 26 | 27 | And now vim --version should show: 28 | 29 | -I/usr/local/Cellar/python/2.7.8_2/Frameworks/Python.framework/Versions/2.7/include/python2.7 30 | 31 | -------------------------------------------------------------------------------- /doc/vim-geeknote.txt: -------------------------------------------------------------------------------- 1 | *vim-geeknote.txt* A short multi line description of your plugin 2 | 3 | =============================================================================== 4 | CONTENTS *vim-geeknote* 5 | 6 | 1. Intro ............................................. |vim-geeknote-intro| 7 | 2. Requirements ............................... |vim-geeknote-requirements| 8 | 3. Installation ............................... |vim-geeknote-installation| 9 | 4. Usage ............................................. |vim-geeknote-usage| 10 | 5. Acknowledgements ........................ |vim-geeknote-acknowlegements| 11 | 6. Todo ............................................... |vim-geeknote-todo| 12 | 7. License ......................................... |vim-geeknote-license| 13 | =============================================================================== 14 | 15 | 1. Intro *vim-geeknote-intro* 16 | 17 | Integrates Geeknote/Evernote into Vim. 18 | - Notebook navigation, creation, and renaming 19 | - Note viewing, renaming, editing, and creation. 20 | 21 | 2. Dependencies/Requirements *vim-geeknote-requirements* 22 | 23 | - Vim 7.4.364 or newer (issues observed with earlier versions) 24 | - Linux (not tested on operating systems) 25 | 26 | 3. Installation *vim-geeknote-installation* 27 | 28 | - If you have not done so already, install [Geeknote](http://www.geeknote.me) 29 | and login to make sure it is functional. You must login before attempting to 30 | use the plugin. 31 | 32 | - Use your plugin manager of choice to install plugin. 33 | 34 | * [Vundle](https://github.com/gmarik/vundle) 35 | * Add `Bundle 'https://github.com/neilagabriel/vim-geeknote'` to .vimrc 36 | * Run `:BundeInstall` 37 | * [Pathogen](https://github.com/tpope/vim-pathogen) 38 | * `git clone https://github.com/neilagabriel/vim-geeknote ~/.vim/bundle/vim-geeknote` 39 | 40 | OPTIONAL SETUP AND CONFIGURATION 41 | 42 | 1. Quick toggle 43 | 44 | noremap :Geeknote 45 | 46 | 2. Note format 47 | 48 | Use the option `g:GeeknoteFormat` to set the format mode used when saving notes 49 | to Geeknote. This is the equivalent of the `--format` options that Geeknote 50 | supports. E.g.: 51 | 52 | let g:GeeknoteFormat="markdown" 53 | 54 | **Warning:** It is not advised that you use this option, use it at your own 55 | risk. The issue is that in the process of converting your notes from markdown 56 | to HTML and back, content is often reformatted. Worse is that if you edit and 57 | save a note that has been reformatted, there is even a large potential to lose 58 | content! This is a side-effect of the various tools/libs that Geeknote uses in 59 | performing the conversions. With the default settings, notes saved with 60 | vim-geeknote bypass these format conversions. This means that notes will be 61 | saved exactly as they appear within Vim. It also means that the notes will 62 | appear in plain-text when viewed outside of Vim in Evernote. If you plan on 63 | creating, editing, and viewing your notes in Vim, this trade-off is well worth 64 | it. And of course it does not mean that you cannot use the markdown format in 65 | your notes. 66 | 67 | 3. Navigation Window Behavior 68 | 69 | 3.a. Limit Width 70 | 71 | By default, vim-geeknote will attempt to resize the navigation window based on 72 | its current content. If you have notebooks or notes with very long names you 73 | may want to use the following option to cap the size of the window: 74 | 75 | let g:GeeknoteMaxExplorerWidth= 76 | 77 | Where `` is replaced with the max width of the window. 78 | 79 | 3.b. Fix Width 80 | 81 | Use the following option to fix the width of the window to a specific value: 82 | 83 | let g:GeeknoteExplorerWidth= 84 | 85 | Where `` is replaced with the desired width of the window. This option 86 | overrides all other width-related options. 87 | 88 | 3.c. Limit View to Specific Notebooks 89 | 90 | 3.c.1 By GUID 91 | 92 | By default, all notebooks will be shown in the navigation window. Depending on 93 | the number notebooks you have, this can add a non-trivial amount of time to the 94 | load time of the plugin. You may also simply not want to see your full set of 95 | notebooks when working in Vim. Use the following option to limit the display to 96 | a specific notebook or set of notebooks: 97 | 98 | let g:GeeknoteNotebooks= 99 | \ [ 100 | \ 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee', 101 | \ 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee', 102 | \ ] 103 | 104 | 3.c.2 By Expression 105 | 106 | Notebooks can also be filtered by expressions. Use the following option to 107 | specify a list of regular expressions to apply to each notebook to decide if it 108 | should be included in the navigation window. Unlike filtering by GUID, this 109 | option does *not* yield a performance improvement. In fact, it may slightly 110 | degrade it depending on the number of expressions you specify. 111 | 112 | let g:GeeknoteNotebookFilters= 113 | \ [ 114 | \ 'Unsorted' , 115 | \ '^Status - WW(\d+)$', 116 | \ ] 117 | 118 | 3.d Explorer Special Characters 119 | 120 | The following options may be used to customize the characters used to denote 121 | opened/closed notebooks and tags: 122 | 123 | let g:GeeknoteExplorerNodeClosed = '+' 124 | let g:GeeknoteExplorerNodeOpened = '-' 125 | 126 | Both unicode and ascii characters are supported. 127 | 128 | 4. Launching 129 | 130 | It may sometimes be convenient to launch geeknote in a new instance of Vim. An 131 | alias can be helpful for this. Here an example for `bash`: 132 | 133 | alias vim-geeknote='vi -c Geeknote' 134 | 135 | 5. Powerline 136 | 137 | Powerline may be used to improve the look of the navigation window as well as 138 | any notes that you open. At this time however, if you'd like this support, 139 | you'll need to use my personal powerline fork locate here: 140 | 141 | https://github.com/neilagabriel/powerline.git 142 | 143 | Just install it in the normal fashion and everything should just work. 144 | 145 | 5. Geeknote Autocommands 146 | 147 | vim-geeknote uses FileType `geeknote` for the navigation window. This may be 148 | used to set your own custom behavior. For example, the following disables line 149 | numbers in the navigation window: 150 | 151 | autocmd FileType geeknote setlocal nonumber 152 | 153 | 4. Usage *vim-geeknote-usage* 154 | 155 | NAVIGATION *:Geeknote* 156 | 157 | Use `:Geeknote` to open/toggle the geeknote navigation window. If the 158 | navigation window is not visible, this command will split the current window 159 | vertically and display the navigation window on the left-side. If visible, the 160 | navigation window will be hidden. Notebooks can be expanded to show the notes 161 | they contain. To expand a notebook, simply navigate to the name of the 162 | notebook and hit ``. Hit `` again to close the notebook. To 163 | open/view a note, navigate to the note and hit ``. The note will be 164 | displayed in the previous window if it is possible to do so or in a new 165 | vertical split. To save changes to the note, simply save the buffer (e.g. 166 | `:w`). The title of the note will be shown on the first line. The title line 167 | should not be deleted from the buffer but *may* be modified to rename the note. 168 | 169 | CREATING NOTEBOOKS *:GeeknoteCreateNotebook* 170 | 171 | Use `:GeeknoteCreateNotebook ` to create a new notebook. 172 | 173 | CREATING NOTES *:GeeknoteCreateNote* 174 | 175 | Use `:GeeknoteCreateNote ` to create a new note. The note will be created 176 | in the notebook currently selected in the navigation window. If a notebook is 177 | not selected, the note will be created in the default notebook. A new buffer 178 | for the note will be displayed in the previous window if it is possible to do 179 | so or in a new vertical split. 180 | 181 | Use `:GeeknoteSaveAsNote` to create a new note using the content of the current 182 | buffer. The first line in the buffer will be used as the note's title. The 183 | remainder of the buffer will be saved as note content. Note this this command 184 | will create and switch to a new buffer. 185 | 186 | RENAMING NOTEBOOKS AND NOTES 187 | 188 | To rename notebooks or notes, simply modify the name of the notebook/note in 189 | the navigation window and save the bugger (e.g. `:w`). Any number of changes 190 | can be made before saving, but be sure not to modify an item's GUID. 191 | 192 | MOVING NOTES *vim-geeknote-moving-notes* 193 | 194 | To move a note into a different notebook, simply move the note's text (includes 195 | title and GUID) under the desired notebook in the navigation window and save 196 | the buffer. Similar to renaming, any number notes can be moved before saving 197 | the buffer. 198 | 199 | SYNCHRONIZATION *:GeeknoteSync* 200 | 201 | Use `:GeeknoteSync` to update the navigation with the latest data on the 202 | Evernote server. Warning, any notes that are opened when this command is issued 203 | will not be updated. Support for this will be added in future releases. 204 | 205 | SEARCHING *:GeeknoteSearch* 206 | 207 | Use `:GeeknoteSearch ` to search for notes with specific `text` in their 208 | titles' and/or content. All results will be added to the nagivation window. 209 | 210 | 5. Acknowledgements *vim-geeknote-acknowlegements* 211 | 212 | - [Geeknote](http://www.geeknote.me) 213 | - [The Nerd Tree](https://github.com/scrooloose/nerdtree) 214 | - [Vim Plugin Starter Kit](https://github.com/JarrodCTaylor/vim-plugin-starter-kit) 215 | 216 | 6. Todo *vim-geeknote-todo* 217 | 218 | - Refresh open notes upon `:GeeknoteSync` 219 | - Complete tag support (adding tags, applying/removing to/from notes) 220 | - Customizable note display 221 | - Improved notebook creation process 222 | 223 | 7. License *vim-geeknote-license* 224 | 225 | Copyright (c) Neil Gabriel. Distributed under the same terms as Vim itself. 226 | See `:help license`. 227 | 228 | vim:tw=78:ts=8:ft=help:norl: 229 | -------------------------------------------------------------------------------- /img/explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilagabriel/vim-geeknote/7f040f655bc5a8f98185c1e161bd4cba977f5664/img/explorer.png -------------------------------------------------------------------------------- /plugin/change.py: -------------------------------------------------------------------------------- 1 | from conn import * 2 | 3 | #======================== Change =============================================# 4 | 5 | class Change(object): 6 | def apply(self): 7 | pass 8 | 9 | #======================== NoteRenamed ========================================# 10 | 11 | class NoteRenamed(Change): 12 | def __init__(self, note, newTitle): 13 | self.note = note 14 | self.newTitle = newTitle 15 | 16 | def apply(self): 17 | self.note.title = self.newTitle 18 | GeeknoteUpdateNote(self.note) 19 | 20 | #======================== NoteMoved ==========================================# 21 | 22 | class NoteMoved(Change): 23 | def __init__(self, note, newNotebookGuid): 24 | self.note = note 25 | self.newNotebookGuid = newNotebookGuid 26 | 27 | def apply(self): 28 | self.note.notebookGuid = self.newNotebookGuid 29 | GeeknoteUpdateNote(self.note) 30 | 31 | #======================== NotebookRenamed ====================================# 32 | 33 | class NotebookRenamed(Change): 34 | def __init__(self, notebook, newName): 35 | self.notebook = notebook 36 | self.newName = newName 37 | 38 | def apply(self): 39 | self.notebook.name = self.newName 40 | GeeknoteUpdateNotebook(self.notebook) 41 | 42 | -------------------------------------------------------------------------------- /plugin/conn.py: -------------------------------------------------------------------------------- 1 | import evernote.edam.limits.constants as Limits 2 | 3 | from geeknote.geeknote import * 4 | 5 | # Connection to Geeknote 6 | geeknote = GeekNote() 7 | authToken = geeknote.authToken 8 | noteStore = geeknote.getNoteStore() 9 | 10 | def GeeknoteCreateNewNote(note): 11 | return noteStore.createNote(authToken, note) 12 | 13 | def GeeknoteCreateNewNotebook(notebook): 14 | return noteStore.createNotebook(authToken, notebook) 15 | 16 | def GeeknoteFindNoteCounts(): 17 | return noteStore.findNoteCounts(authToken, NoteStore.NoteFilter(), False) 18 | 19 | def GeeknoteGetDefaultNotebook(): 20 | return noteStore.getDefaultNotebook(authToken) 21 | 22 | def GeeknoteGetNotes(searchWords=""): 23 | filter = NoteStore.NoteFilter(order = Types.NoteSortOrder.UPDATED) 24 | filter.words = searchWords 25 | 26 | meta = NoteStore.NotesMetadataResultSpec() 27 | meta.includeTitle = True 28 | meta.includeNotebookGuid = True 29 | meta.includeTagGuids = True 30 | 31 | count = Limits.EDAM_USER_NOTES_MAX 32 | result = noteStore.findNotesMetadata(authToken, filter, 0, count, meta) 33 | update_count = lambda c: max(c - len(result.notes), 0) 34 | count = update_count(count) 35 | 36 | while ((result.totalNotes != len(result.notes)) and count != 0): 37 | offset = len(result.notes) 38 | result.notes += noteStore.findNotesMetadata( 39 | authToken, filter, offset, count, meta).notes 40 | count = update_count(count) 41 | 42 | notes = [] 43 | for key, note in enumerate(result.notes): 44 | notes.append(note) 45 | 46 | return notes 47 | 48 | def GeeknoteGetNotebook(guid): 49 | try: 50 | return noteStore.getNotebook(authToken, guid) 51 | except: 52 | return None 53 | 54 | def GeeknoteGetNotebooks(): 55 | return noteStore.listNotebooks(authToken) 56 | 57 | def GeeknoteGetTags(): 58 | return noteStore.listTags(authToken) 59 | 60 | def GeeknoteLoadNote(note): 61 | return noteStore.getNote(authToken, note.guid, True, False, False, False) 62 | 63 | def GeeknoteRefreshNoteMeta(note): 64 | return noteStore.getNote(authToken, note.guid, False, False, False, False) 65 | 66 | def GeeknoteUpdateNote(note): 67 | noteStore.updateNote(authToken, note) 68 | 69 | def GeeknoteUpdateNotebook(notebook): 70 | noteStore.updateNotebook(authToken, notebook) 71 | 72 | -------------------------------------------------------------------------------- /plugin/enml.py: -------------------------------------------------------------------------------- 1 | import vim 2 | import os 3 | import re 4 | 5 | from geeknote.out import * 6 | from geeknote.editor import Editor 7 | from bs4 import BeautifulSoup 8 | 9 | def ENMLtoText(contentENML): 10 | format = 'vim-default' 11 | if int(vim.eval('exists("g:GeeknoteFormat")')): 12 | format = vim.eval('g:GeeknoteFormat') 13 | 14 | if format == 'pre': 15 | print 'WARNING: g:GeeknoteFormat=pre is deprecated.' 16 | 17 | if format == 'vim-default' or format == 'pre': 18 | try: 19 | soup = BeautifulSoup(contentENML.decode('utf-8')) 20 | sections = soup.select('pre') 21 | if len(sections) >= 1: 22 | content = '' 23 | for c in sections[0].contents: 24 | content = u''.join((content, c)) 25 | content = re.sub(r' *\n', os.linesep, content) 26 | content = content.replace('<', '<') 27 | content = content.replace('>', '>') 28 | content = content.replace('&', '&') 29 | return content.encode('utf-8') 30 | except: 31 | pass 32 | # fall-through 33 | return Editor.ENMLtoText(contentENML) 34 | 35 | def textToENML(content): 36 | format = 'vim-default' 37 | if int(vim.eval('exists("g:GeeknoteFormat")')): 38 | format = vim.eval('g:GeeknoteFormat') 39 | 40 | if format == 'pre': 41 | print 'WARNING: g:GeeknoteFormat=pre is deprecated.' 42 | 43 | if format != 'vim-default' and format != 'pre': 44 | return Editor.textToENML(content, True, format) 45 | 46 | content = content.replace('<', '<') 47 | content = content.replace('>', '>') 48 | content = content.replace('&', '&') 49 | content = unicode(content, "utf-8") 50 | contentHTML = u''.join(('
', content, '
')).encode("utf-8") 51 | 52 | enml = Editor.wrapENML(contentHTML) 53 | return enml 54 | -------------------------------------------------------------------------------- /plugin/explorer.py: -------------------------------------------------------------------------------- 1 | import vim 2 | import re 3 | 4 | from view import * 5 | from utils import * 6 | from conn import * 7 | from change import * 8 | 9 | #======================== Global Setup/Config ================================# 10 | 11 | ExplorerCharOpened = u'\u25bd' 12 | ExplorerCharClosed = u'\u25b6' 13 | 14 | if int(vim.eval('exists("g:GeeknoteExplorerNodeOpened")')): 15 | ExplorerCharOpened = vim.eval( 16 | 'g:GeeknoteExplorerNodeOpened').decode('utf8') 17 | 18 | if int(vim.eval('exists("g:GeeknoteExplorerNodeClosed")')): 19 | ExplorerCharClosed = vim.eval( 20 | 'g:GeeknoteExplorerNodeClosed').decode('utf8') 21 | 22 | #======================== Registry ===========================================# 23 | 24 | # 25 | # A dictionary containing an entry for all nodes contained in the explorer 26 | # window, keyed by guid. 27 | # 28 | registry = {} 29 | 30 | # 31 | # Maps GUIDs to instance numbers. Each node represents an object. Objects are 32 | # unique but nodes are not. There can be any number nodes instanciated for an 33 | # object. An instance number is used to distinguish nodes for the same object. 34 | # This container maps an object's GUID to the maximum instance number of any 35 | # node representing the object (i.e. object node count minus one). 36 | # 37 | instanceMap = {} 38 | 39 | def registerNode(node): 40 | guid = node.getGuid() 41 | if guid not in instanceMap: 42 | instance = 0 43 | else: 44 | instance = instanceMap[guid] + 1 45 | 46 | instanceMap[guid] = instance 47 | 48 | key = guid + "(" + str(instance) + ")" 49 | node.setKey(key) 50 | registry[key] = node 51 | 52 | def deleteNodes(): 53 | registry.clear() 54 | 55 | def getNode(key): 56 | if key in registry: 57 | return registry[key] 58 | return None 59 | 60 | def getNodeByInstance(guid, instance): 61 | key = guid + "(" + str(instance) + ")" 62 | return getNode(key) 63 | 64 | #======================== Node ===============================================# 65 | 66 | class Node(object): 67 | def __init__(self, indent=0): 68 | self.parent = None 69 | self.children = [] 70 | self.changes = [] 71 | self.row = -1 72 | self.indent = indent 73 | self.prefWidth = 0 74 | self.key = "" 75 | self.close() 76 | 77 | def activate(self): 78 | self.toggle() 79 | 80 | def adapt(self, line): 81 | return False 82 | 83 | def addChild(self, node): 84 | node.parent = self 85 | self.children.append(node) 86 | 87 | def close(self): 88 | self.expanded = False 89 | 90 | def commitChanges(self): 91 | for change in self.changes: 92 | change.apply() 93 | del self.changes[:] 94 | 95 | def expand(self): 96 | self.expanded = True 97 | 98 | def getGuid(self): 99 | return "None" 100 | 101 | def getKey(self): 102 | return self.key 103 | 104 | def getPreferredWidth(self): 105 | if self.parent is None or self.parent.isExpanded(): 106 | return self.prefWidth 107 | return 0 108 | 109 | def isExpanded(self): 110 | return self.expanded 111 | 112 | def isVisible(self): 113 | return self.row != -1 114 | 115 | def refresh(self): 116 | pass 117 | 118 | def setKey(self, key): 119 | self.key = key 120 | 121 | def removeChild(self, node): 122 | if node in self.children: 123 | self.children.remove(node) 124 | 125 | def setName(self, name): 126 | self.name = name 127 | 128 | def toggle(self): 129 | if self.expanded: 130 | self.close() 131 | else: 132 | self.expand() 133 | 134 | #======================== NotebookNode =======================================# 135 | 136 | class NotebookNode(Node): 137 | def __init__(self, notebook): 138 | super(NotebookNode, self).__init__() 139 | 140 | self.notebook = notebook 141 | self.loaded = False 142 | self.setName(notebook.name) 143 | 144 | def adapt(self, line): 145 | if len(self.children) > 0: 146 | r = re.compile("^\S+" # match leading non-whitespace characters 147 | "(?:\s+)?" # optional whitespace 148 | "(.*)" # notebook name 149 | "\(\d+\)" # note count 150 | "(?:\s+)?" # optional whitespace 151 | "N\[.*\]" # key 152 | ".*$") # everything else till end of line 153 | else: 154 | r = re.compile("^\S+" # match leading non-whitespace characters 155 | "(?:\s+)?" # optional whitespace 156 | "(.*)" # notebook name 157 | "N\[.*\]" # key 158 | ".*$") # everything else till end of line 159 | 160 | m = r.match(line) 161 | if m: 162 | name = m.group(1).strip() 163 | if self.name != name: 164 | change = NotebookRenamed(self.notebook, name) 165 | self.changes.append(change) 166 | 167 | self.setName(name) 168 | return True 169 | return False 170 | 171 | def addNote(self, note): 172 | node = NoteNode(note, self.indent + 1) 173 | registerNode(node) 174 | 175 | self.addChild(node) 176 | return node 177 | 178 | def expand(self): 179 | if self.loaded is False: 180 | del self.children[:] 181 | 182 | notes = self.getNotes() 183 | for note in notes: 184 | self.addNote(note) 185 | 186 | self.loaded = True 187 | 188 | super(NotebookNode, self).expand() 189 | 190 | def getGuid(self): 191 | return self.notebook.guid 192 | 193 | def getNotes(self): 194 | searchWords = 'notebook:"%s"' % self.notebook.name 195 | return GeeknoteGetNotes(searchWords) 196 | 197 | def render(self, buffer, attribs): 198 | numNotes = len(self.children) 199 | 200 | if self.expanded: 201 | line = ExplorerCharOpened 202 | else: 203 | if self.loaded and numNotes == 0: 204 | line = ExplorerCharOpened 205 | else: 206 | line = ExplorerCharClosed 207 | 208 | line += ' ' + self.name.decode('utf8') 209 | if numNotes != 0: 210 | line += ' (%d)' % numNotes 211 | 212 | line = line.encode('utf8') 213 | self.prefWidth = len(line) 214 | 215 | fmt = '{:<%d} N[{}]' % attribs['keyCol'] 216 | buffer.append(fmt.format(line, self.getKey())) 217 | self.row = len(buffer) 218 | 219 | if self.expanded: 220 | for noteNode in self.children: 221 | noteNode.render(buffer, attribs) 222 | 223 | def setName(self, name): 224 | self.name = name 225 | 226 | #======================== NoteNode ===========================================# 227 | 228 | class NoteNode(Node): 229 | def __init__(self, note, indent=1): 230 | super(NoteNode, self).__init__(indent) 231 | 232 | self.note = note 233 | self.title = None 234 | self.refresh() 235 | 236 | def adapt(self, line): 237 | # Was the note renamed? 238 | r = re.compile("^\s+" # leading whitespace 239 | "(.*)" # note title 240 | "n\[.*\]" # key 241 | ".*$") # everything else till end of line 242 | m = r.match(line) 243 | if m: 244 | title = m.group(1).strip() 245 | if self.title != title: 246 | change = NoteRenamed(self.note, title) 247 | self.changes.append(change) 248 | 249 | self.setTitle(title) 250 | return True 251 | return False 252 | 253 | def activate(self): 254 | super(NoteNode, self).activate() 255 | 256 | GeeknoteOpenNote(self.note) 257 | 258 | def getGuid(self): 259 | return self.note.guid 260 | 261 | def refresh(self): 262 | if self.title is not None: 263 | self.note = GeeknoteRefreshNoteMeta(self.note) 264 | 265 | self.notebookGuid = self.note.notebookGuid 266 | self.setTitle(self.note.title) 267 | 268 | def getGuid(self): 269 | return self.note.guid 270 | 271 | def render(self, buffer, attribs): 272 | line = ' ' * (self.indent * 4) + self.title.decode('utf8') 273 | 274 | line = line.encode('utf8') 275 | self.prefWidth = len(line) 276 | 277 | fmt = '{:<%d} n[{}]' % attribs['keyCol'] 278 | buffer.append(fmt.format(line, self.getKey())) 279 | self.row = len(buffer) 280 | 281 | def setTitle(self, title): 282 | self.title = title 283 | 284 | #======================== TagNode ============================================# 285 | 286 | class TagNode(Node): 287 | def __init__(self, tag, indent=0): 288 | super(TagNode, self).__init__(indent) 289 | 290 | self.tag = tag 291 | self.loaded = False 292 | self.setName(tag.name) 293 | 294 | def addNote(self, note): 295 | node = NoteNode(note, self.indent + 1) 296 | registerNode(node) 297 | 298 | self.addChild(node) 299 | return node 300 | 301 | def expand(self): 302 | if self.loaded is False: 303 | notes = self.getNotes() 304 | notes.sort(key=lambda n: n.title) 305 | for note in notes: 306 | self.addNote(note) 307 | self.loaded = True 308 | 309 | super(TagNode, self).expand() 310 | 311 | def getGuid(self): 312 | return self.tag.guid 313 | 314 | def getNotes(self): 315 | searchWords = 'tag:"%s"' % self.tag.name 316 | return GeeknoteGetNotes(searchWords) 317 | 318 | def render(self, buffer, attribs): 319 | numNotes = len(self.children) 320 | 321 | if self.expanded: 322 | line = ExplorerCharOpened 323 | else: 324 | if self.loaded and numNotes == 0: 325 | line = ExplorerCharOpened 326 | else: 327 | line = ExplorerCharClosed 328 | 329 | line += ' ' + self.name.decode('utf8') 330 | if numNotes != 0: 331 | line += ' (%d)' % numNotes 332 | 333 | line = line.encode('utf8') 334 | self.prefWidth = len(line) 335 | 336 | fmt = '{:<%d} T[{}]' % attribs['keyCol'] 337 | buffer.append(fmt.format(line, self.getKey())) 338 | self.row = len(buffer) 339 | 340 | if self.expanded: 341 | for noteNode in self.children: 342 | noteNode.render(buffer, attribs) 343 | 344 | #======================== Explorer ===========================================# 345 | 346 | class Explorer(object): 347 | def __init__(self): 348 | self.hidden = True 349 | self.selectedNode = None 350 | self.notebooks = [] 351 | self.tags = [] 352 | self.modifiedNodes = [] 353 | self.dataFile = None 354 | self.buffer = None 355 | self.expandState = {} 356 | self.searchResults = [] 357 | 358 | self.refresh() 359 | 360 | self.dataFile = createTempFile(prefix='__GeeknoteExplorer__') 361 | 362 | autocmd('VimLeave', '*', ':call Vim_GeeknoteTerminate()') 363 | 364 | def __del__(self): 365 | try: 366 | self.dataFile.close() 367 | except: 368 | pass 369 | 370 | def activateNode(self, line): 371 | key = self.getNodeKey(line) 372 | if key is not None: 373 | node = getNode(key) 374 | node.activate() 375 | 376 | # Rerender the navigation window. Keep the current cursor postion. 377 | row, col = vim.current.window.cursor 378 | self.render() 379 | vim.current.window.cursor = (row, col) 380 | 381 | def addNote(self, note): 382 | notebook = getNodeByInstance(note.notebookGuid, 0) 383 | node = notebook.addNote(note) 384 | 385 | # 386 | # Expand the notebook so that the new node can be selected. This must 387 | # occur after the node is added to the notebook. If done before, it is 388 | # possible for the node to get added twice. 389 | # 390 | notebook.expand() 391 | 392 | # 393 | # Re-render the explorer window. This ensures that the new node will 394 | # assigned a row number so that it can be selected. 395 | # 396 | self.render() 397 | self.selectNode(node) 398 | 399 | def addNotebook(self, notebook): 400 | node = NotebookNode(notebook) 401 | registerNode(node) 402 | 403 | self.notebooks.append(node) 404 | self.notebooks.sort(key=lambda n: n.notebook.name.lower()) 405 | 406 | # 407 | # Re-render the explorer window. This ensures that the new node will 408 | # assigned a row number so that it can be selected. 409 | # 410 | self.render() 411 | self.selectNode(node) 412 | 413 | def addSearchResults(self, results): 414 | for note in results: 415 | node = NoteNode(note, 0) 416 | registerNode(node) 417 | self.searchResults.append(node) 418 | 419 | def addTag(self, tag): 420 | tagNode = TagNode(tag) 421 | self.tags.append(tagNode) 422 | self.tags.sort(key=lambda t: t.tag.name.lower()) 423 | 424 | registerNode(tagNode) 425 | 426 | def applyChanges(self): 427 | # 428 | # It is possible that user has rearranged (moved) the nodes since the 429 | # last time the changes were applied. Refresh them now. 430 | # 431 | self.updateNodeLineNumbers() 432 | 433 | # Look for nodes that were renamed 434 | for key in registry: 435 | node = getNode(key) 436 | if node.isVisible(): 437 | if node.adapt(self.buffer[node.row]): 438 | if node not in self.modifiedNodes: 439 | self.modifiedNodes.append(node) 440 | 441 | # Look for nodes that were moved 442 | for key in registry: 443 | node = getNode(key) 444 | if node.isVisible(): 445 | if isinstance(node , NoteNode) and \ 446 | isinstance(node.parent, NotebookNode): 447 | parent = self.getNodeParent(node.row) 448 | if (node.parent != parent): 449 | change = NoteMoved(node.note, parent.notebook.guid) 450 | node.changes.append(change) 451 | 452 | parent.expand() 453 | node.parent.removeChild(node) 454 | parent.addChild(node) 455 | 456 | if node not in self.modifiedNodes: 457 | self.modifiedNodes.append(node) 458 | 459 | def clearSearchResults(self): 460 | del self.searchResults[:] 461 | 462 | def commitChanges(self): 463 | if isBufferModified(self.buffer.number): 464 | self.applyChanges() 465 | 466 | for node in self.modifiedNodes: 467 | node.commitChanges() 468 | 469 | for node in self.modifiedNodes: 470 | for key in registry: 471 | tempNode = getNode(key) 472 | if tempNode.getGuid() == node.getGuid(): 473 | if tempNode.getKey() != node.getKey(): 474 | tempNode.refresh() 475 | 476 | del self.modifiedNodes[:] 477 | 478 | def getNodeParent(self, row): 479 | key = self.getNodeKey(self.buffer[row]) 480 | node = getNode(key) 481 | 482 | # Only notes have parents 483 | if not isinstance(node, NoteNode): 484 | return None 485 | 486 | while row > 0: 487 | key = self.getNodeKey(self.buffer[row]) 488 | if key is not None: 489 | node = getNode(key) 490 | if not isinstance(node, NoteNode): 491 | return node 492 | row -= 1 493 | 494 | return None 495 | 496 | def getSelectedNode(self): 497 | if self.buffer is None: 498 | return None 499 | 500 | prevWin = getActiveWindow() 501 | setActiveBuffer(self.buffer) 502 | text = vim.current.line 503 | setActiveWindow(prevWin) 504 | 505 | key = self.getNodeKey(text) 506 | if key is not None: 507 | return getNode(key) 508 | return None 509 | 510 | def getSelectedNotebook(self): 511 | node = self.getSelectedNode() 512 | if isinstance(node, NotebookNode): 513 | return node.notebook 514 | if isinstance(node, NoteNode): 515 | if isinstance(node.parent, NotebookNode): 516 | node = getNode(node.parent.getKey()) 517 | return node.notebook 518 | return None 519 | 520 | def getMinWidth(self): 521 | maxWidth = 0 522 | for key in registry: 523 | width = getNode(key).getPreferredWidth() 524 | if width > maxWidth: 525 | maxWidth = width 526 | 527 | hpad = numberwidth() + foldcolumn() + 1 528 | return maxWidth + hpad 529 | 530 | def getNodeKey(self, nodeText): 531 | r = re.compile('^.+\[(.+)\]$') 532 | m = r.match(nodeText) 533 | if m: 534 | return m.group(1) 535 | return None 536 | 537 | # 538 | # Hide the navigation buffer. This closes the window it is displayed in but 539 | # does not destroy the buffer itself. 540 | # 541 | def hide(self): 542 | vim.command('{}bunload'.format(self.buffer.number)) 543 | self.hidden = True 544 | 545 | def initView(self): 546 | origWin = getActiveWindow() 547 | setActiveBuffer(self.buffer) 548 | 549 | wnum = getActiveWindow() 550 | bnum = self.buffer.number 551 | 552 | autocmd('BufWritePre' , 553 | '', 554 | ':call Vim_GeeknoteCommitStart()') 555 | 556 | autocmd('BufWritePost', 557 | '', 558 | ':call Vim_GeeknoteCommitComplete()') 559 | 560 | setWindowVariable(wnum, 'winfixwidth', True) 561 | setWindowVariable(wnum, 'wrap' , False) 562 | setWindowVariable(wnum, 'cursorline' , True) 563 | setBufferVariable(bnum, 'swapfile' , False) 564 | setBufferVariable(bnum, 'bufhidden' , 'hide') 565 | 566 | vim.command('setfiletype geeknote') 567 | setActiveWindow(origWin) 568 | 569 | # 570 | # Is the navigation buffer hidden? When hidden, the buffer exists but is 571 | # not active in any window. 572 | # 573 | def isHidden(self): 574 | return self.hidden 575 | 576 | def refresh(self): 577 | self.saveExpandState() 578 | deleteNodes() 579 | 580 | self.noteCounts = GeeknoteFindNoteCounts() 581 | 582 | del self.notebooks[:] 583 | self.refreshNotebooks() 584 | 585 | del self.tags[:] 586 | tags = GeeknoteGetTags() 587 | for tag in tags: 588 | self.addTag(tag) 589 | self.restoreExpandState() 590 | 591 | def refreshNotebooks(self): 592 | # 593 | # If the user already specified which notebooks to load, load just 594 | # those notebooks. 595 | # 596 | if int(vim.eval('exists("g:GeeknoteNotebooks")')): 597 | guids = vim.eval('g:GeeknoteNotebooks') 598 | for guid in guids: 599 | notebook = GeeknoteGetNotebook(guid) 600 | if notebook is not None: 601 | self.addNotebook(notebook) 602 | return 603 | 604 | # 605 | # Otherwise, load all notebooks and apply any filters that the user 606 | # specified (if any). 607 | # 608 | notebooks = GeeknoteGetNotebooks() 609 | if not int(vim.eval('exists("g:GeeknoteNotebookFilters")')): 610 | for notebook in notebooks: 611 | self.addNotebook(notebook) 612 | else: 613 | regex = [] 614 | filters = vim.eval('g:GeeknoteNotebookFilters') 615 | for filter in filters: 616 | try: 617 | r = re.compile(filter) 618 | regex.append(r) 619 | except: 620 | pass 621 | 622 | for notebook in notebooks: 623 | for r in regex: 624 | if r.search(notebook.name): 625 | self.addNotebook(notebook) 626 | break 627 | 628 | # Render the navigation buffer in the navigation window.. 629 | def render(self): 630 | if self.buffer is None: 631 | return 632 | 633 | origWin = getActiveWindow() 634 | setActiveBuffer(self.buffer) 635 | 636 | # Save the selected node before redrawing the explorer. 637 | self.selectedNode = self.getSelectedNode() 638 | if self.selectedNode is None: 639 | notebook = GeeknoteGetDefaultNotebook() 640 | self.selectNotebook(notebook) 641 | 642 | # 643 | # Before overwriting the navigation window, look for any changes made 644 | # by the user. Do not synchronize them yet with the server, just make 645 | # sure they are not lost. 646 | # 647 | if isBufferModified(self.buffer.number): 648 | self.applyChanges() 649 | 650 | # Clear the navigation buffer to get rid of old content (if any). 651 | del self.buffer[:] 652 | 653 | # Prepare rendering attributes 654 | attribs = {} 655 | attribs['keyCol'] = self.getMinWidth() + 1 656 | 657 | # Create separator 658 | fmt = '{:=^%d}' % (attribs['keyCol'] + 41) 659 | sep = fmt.format('=') 660 | 661 | # Prepare the new content and append it to the navigation buffer. 662 | content = [] 663 | content.append('Notebooks:') 664 | content.append(sep) 665 | 666 | # Render notebooks, notes, tags, and search results 667 | for node in self.notebooks: 668 | node.render(content, attribs) 669 | 670 | if len(self.tags) > 0: 671 | content.append('') 672 | content.append('Tags:') 673 | content.append(sep) 674 | 675 | for node in self.tags: 676 | node.render(content, attribs) 677 | 678 | if len(self.searchResults) > 0: 679 | content.append('') 680 | content.append('Search Results:') 681 | content.append(sep) 682 | 683 | for node in self.searchResults: 684 | node.render(content, attribs) 685 | 686 | # Write the content list to the buffer starting at row zero. 687 | self.buffer.append(content, 0) 688 | 689 | # Move the cursor over the selected node (if any) 690 | if self.selectedNode is not None: 691 | if self.selectedNode.row != -1: 692 | vim.current.window.cursor = (self.selectedNode.row, 0) 693 | 694 | # Resize the window as appropriate. 695 | self.resize() 696 | 697 | # 698 | # Write the navigation window but disable BufWritePre events before 699 | # doing so. We only want to check for user changes when the user was 700 | # the one that saved the buffer. 701 | # 702 | ei = vim.eval('&ei') 703 | vim.command('set ei=BufWritePre') 704 | vim.command("write!") 705 | vim.command('set ei={}'.format(ei)) 706 | 707 | setActiveWindow(origWin) 708 | 709 | def resize(self): 710 | # Fix the width if requested. 711 | if int(vim.eval('exists("g:GeeknoteExplorerWidth")')): 712 | width = int(vim.eval('g:GeeknoteExplorerWidth')) 713 | vim.command("vertical resize %d" % width) 714 | return 715 | 716 | # Get the max allowable width (default is 40 columns) 717 | if int(vim.eval('exists("g:GeeknoteMaxExplorerWidth")')): 718 | maxWidth = int(vim.eval('g:GeeknoteMaxExplorerWidth')) 719 | else: 720 | maxWidth = 40 721 | 722 | # Get the minimum width needed to see all names/titles 723 | minWidth = self.getMinWidth() 724 | 725 | # Fix the width to the minimum of what is required vs. what is needed. 726 | width = min(minWidth, maxWidth) 727 | vim.command("vertical resize %d" % width) 728 | 729 | def restoreExpandState(self): 730 | for key in self.expandState: 731 | node = getNode(key) 732 | if node is not None: 733 | if self.expandState[key]: 734 | node.expand() 735 | else: 736 | node.close() 737 | 738 | def saveExpandState(self): 739 | for node in self.notebooks: 740 | self.expandState[node.getKey()] = node.expanded 741 | 742 | for node in self.tags: 743 | self.expandState[node.getKey()] = node.expanded 744 | 745 | def selectNode(self, node): 746 | self.selectedNode = node 747 | 748 | # Move the cursor over the node if the node has been rendered. 749 | if node.row != -1: 750 | origWin = getActiveWindow() 751 | setActiveBuffer(self.buffer) 752 | vim.current.window.cursor = (node.row, 0) 753 | setActiveWindow(origWin) 754 | 755 | def selectNotebook(self, notebook): 756 | # 757 | # Notebooks never have more than one assoicated node, therefore, use 758 | # zero for the instance number. 759 | # 760 | node = getNodeByInstance(notebook.guid, 0) 761 | if node is not None: 762 | self.selectNode(node) 763 | 764 | def selectNotebookIndex(self, index): 765 | if index < len(self.notebooks): 766 | node = self.notebooks[index] 767 | self.selectNode(node) 768 | 769 | # Switch to the navigation buffer in the currently active window. 770 | def show(self): 771 | vim.command('topleft 50 vsplit {}'.format(self.dataFile.name)) 772 | self.buffer = vim.current.buffer 773 | 774 | self.initView() 775 | self.render() 776 | 777 | noremap(" ", 778 | ":call Vim_GeeknoteActivateNode()") 779 | 780 | self.hidden = False 781 | 782 | def updateNodeLineNumbers(self): 783 | for key in registry: 784 | getNode(key).row = -1 785 | 786 | for row in xrange(len(self.buffer)): 787 | line = self.buffer[row] 788 | key = self.getNodeKey(line) 789 | if key is not None: 790 | getNode(key).row = row 791 | -------------------------------------------------------------------------------- /plugin/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import vim 3 | import tempfile 4 | 5 | GeeknoteNeovimMode = False 6 | if int(vim.eval('exists("g:GeeknoteNeovimMode")')): 7 | GeeknoteNeovimMode = vim.eval('g:GeeknoteNeovimMode') 8 | 9 | def createTempFile(**kwargs): 10 | if 'prefix' not in kwargs: 11 | kwargs['prefix'] = '__Geeknote__' 12 | 13 | if 'suffix' not in kwargs: 14 | geeknoteformat = 'markdown' 15 | if int(vim.eval('exists("g:GeeknoteFormat")')): 16 | geeknoteformat = vim.eval('g:GeeknoteFormat') 17 | kwargs['suffix'] = '.{}'.format(geeknoteformat) 18 | 19 | if 'dir' not in kwargs: 20 | if int(vim.eval('exists("g:GeeknoteScratchDirectory")')): 21 | kwargs['dir'] = vim.eval('g:GeeknoteScratchDirectory') 22 | 23 | return tempfile.NamedTemporaryFile(**kwargs) 24 | 25 | #======================== Vim Helper Functions ==============================# 26 | 27 | def autocmd(event, pattern, cmd): 28 | vim.command('autocmd {} {} {}'.format(event, pattern, cmd)) 29 | 30 | def noremap(lhs, rhs): 31 | vim.command("nnoremap {} {}".format(lhs, rhs)) 32 | 33 | def winnr(number=None): 34 | if number: 35 | vim.command("let l:num = winnr('{}')".format(number)) 36 | else: 37 | vim.command('let l:num = winnr()') 38 | return int(vim.eval('l:num')) 39 | 40 | def winbufnr(wnum): 41 | vim.command("let l:num = winbufnr('{}')".format(wnum)) 42 | return int(vim.eval('l:num')) 43 | 44 | # Return a list of windows numbers currently displaying the given buffer. 45 | def bufwinnr(bnum): 46 | windows = [] 47 | wnum = 1 48 | while wnum <= winnr('$'): 49 | buf = vim.windows[wnum-1].buffer 50 | if buf.number == bnum: 51 | windows.append(wnum) 52 | wnum += 1 53 | return windows 54 | 55 | def numberwidth(): 56 | return int(vim.eval('&numberwidth')) 57 | 58 | def foldcolumn(): 59 | return int(vim.eval('&foldcolumn')) 60 | 61 | def getActiveWindow(): 62 | return winnr() 63 | 64 | def getPreviousWindow(): 65 | return winnr('#') 66 | 67 | def setActiveWindow(wnum): 68 | vim.command('exec {} . "wincmd w"'.format(wnum)) 69 | 70 | def setActiveBuffer(buf): 71 | bnum = buf.number 72 | windows = bufwinnr(bnum) 73 | if len(windows) > 0: 74 | wnum = windows[0] 75 | vim.command('exec {} . "wincmd w"'.format(wnum)) 76 | 77 | def hidden(): 78 | isHidden = vim.eval('&hidden') 79 | return isHidden == '1' 80 | 81 | def bufInWindows(bnum): 82 | cnt = 0 83 | winnum = 1 84 | while True: 85 | bufnum = winbufnr(winnum) 86 | if bufnum < 0: 87 | break 88 | if bufnum == bnum: 89 | cnt = cnt + 1 90 | winnum = winnum + 1 91 | return cnt 92 | 93 | def getBufferName(bnum): 94 | if GeeknoteNeovimMode: 95 | bnum -= 1 96 | return vim.buffers[bnum].name 97 | 98 | def getBufferVariable(bnum, var): 99 | if GeeknoteNeovimMode: 100 | bnum -= 1 101 | return vim.buffers[bnum].options[var] 102 | 103 | def getWindowVariable(wnum, var): 104 | return vim.windows[wnum-1].options[var] 105 | 106 | def setBufferVariable(bnum, var, value): 107 | if GeeknoteNeovimMode: 108 | bnum -= 1 109 | vim.buffers[bnum].options[var] = value 110 | 111 | def setWindowVariable(wnum, var, value): 112 | vim.windows[wnum-1].options[var] = value 113 | 114 | def isBufferModified(bnum): 115 | return getBufferVariable(bnum, 'modified') 116 | -------------------------------------------------------------------------------- /plugin/view.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import vim 3 | 4 | from enml import * 5 | from utils import * 6 | from conn import * 7 | 8 | # Maps buffer names to NoteTracker objects. 9 | openNotes = {} 10 | 11 | # 12 | # Holds all information that needs to be tracked for any note that has been 13 | # opened. 14 | # 15 | class NoteTracker(object): 16 | def __init__(self, note, buffer): 17 | self.note = note 18 | self.buffer = buffer 19 | self.modified = False 20 | 21 | # Close all opened notes. 22 | def GeeknoteCloseAllNotes(): 23 | # 24 | # Try to delete any temp files that still exist (is is possible that 25 | # some/all were already garbage collected by the OS. 26 | # 27 | try: 28 | for filename in openNotes: 29 | os.remove(filename) 30 | except: 31 | pass 32 | 33 | openNotes.clear() 34 | 35 | # Close the note associated with the given buffer name. 36 | def GeeknoteCloseNote(filename): 37 | if filename in openNotes: 38 | os.remove(filename) 39 | del openNotes[filename] 40 | 41 | # Commit any changes that were made to the note in the buffer to the note. 42 | def GeeknoteCommitChangesToNote(note): 43 | tracker = GeeknoteGetNoteTracker(note) 44 | 45 | # If the note has not been modified, there's nothing more to do. 46 | if tracker.modified is False: 47 | return False 48 | 49 | # 50 | # Now that we know the note has been modified, read the note's buffer and 51 | # pull out the note's title and content. 52 | # 53 | content = '' 54 | title = tracker.note.title 55 | lines = open(tracker.buffer.name, 'r').readlines() 56 | if len(lines) > 0: 57 | title = lines.pop(0).strip() 58 | while len(lines) > 0: 59 | if lines[0].strip() == '': 60 | lines.pop(0) 61 | else: 62 | break 63 | for r in lines: 64 | content += r 65 | 66 | # Update the note's title and content from what was read from the buffer. 67 | tracker.note.title = title 68 | tracker.note.content = textToENML(content) 69 | 70 | return True 71 | 72 | # Find the object that is tracking the given note (None if note opened). 73 | def GeeknoteGetNoteTracker(note): 74 | for filename in openNotes: 75 | if openNotes[filename].note.guid == note.guid: 76 | return openNotes[filename] 77 | return None 78 | 79 | # Given the name of a buffer, find the note that the buffer represents. 80 | def GeeknoteGetOpenNote(filename): 81 | if filename in openNotes: 82 | return openNotes[filename].note 83 | return None 84 | 85 | # Determine if the note has been modified since since it was last saved. 86 | def GeeknoteNoteIsModified(note): 87 | tracker = GeeknoteGetNoteTracker(note) 88 | return tracker.modified 89 | 90 | # Determine if the user has already opened the given note. 91 | def GeeknoteNoteIsOpened(note): 92 | tracker = GeeknoteGetNoteTracker(note) 93 | return True if tracker is not None else False 94 | 95 | # Open a note in the active window. 96 | def GeeknoteOpenNote(note): 97 | # 98 | # Determine which window to display the note in (creating one if necessary) 99 | # and switch to that window. 100 | # 101 | origWin = getActiveWindow() 102 | prevWin = getPreviousWindow() 103 | 104 | setActiveWindow(prevWin) 105 | isPrevUsable = GeeknoteIsWindowUsable(prevWin) 106 | if isPrevUsable is False: 107 | firstUsableWin = GeeknoteGetFirstUsableWindow() 108 | if firstUsableWin != -1: 109 | setActiveWindow(firstUsableWin) 110 | else: 111 | vim.command('vertical new') 112 | 113 | # 114 | # Check to see if the note is already opened before opening it in a new 115 | # buffer. 116 | # 117 | opened = GeeknoteNoteIsOpened(note) 118 | if opened is False: 119 | # Load the note's content 120 | note = GeeknoteLoadNote(note) 121 | content = ENMLtoText(note.content) 122 | content = tools.stdoutEncode(content) 123 | 124 | # Write the note's title and content to a temporary file. 125 | f = createTempFile(delete=False) 126 | f.write(note.title + '\n\n') 127 | 128 | isNoteEmpty = not content.strip() 129 | if isNoteEmpty is False: 130 | f.write(content) 131 | else: 132 | f.write("\n") 133 | f.flush() 134 | 135 | # Now edit the file in a new buffer within the active window. 136 | vim.command('edit {}'.format(f.name)) 137 | 138 | # Close the file now that it is open in the buffer. 139 | f.close() 140 | 141 | # Position the cursor at a convenient location if opening an empty note 142 | if isNoteEmpty: 143 | vim.current.window.cursor = (3, 0) 144 | 145 | # 146 | # Create an object to keep track of the note and all associated 147 | # information while it's opened. 148 | # 149 | openNotes[f.name] = NoteTracker(note, vim.current.buffer) 150 | 151 | # Register callbacks for the buffer events that affect the note. 152 | autocmd('BufWritePre', 153 | '', 154 | ':call Vim_GeeknotePrepareToSaveNote("{}")'.format(f.name)) 155 | 156 | autocmd('BufWritePost', 157 | '', 158 | ':call Vim_GeeknoteSaveNote("{}")'.format(f.name)) 159 | 160 | autocmd('BufDelete', 161 | '', 162 | ':call Vim_GeeknoteCloseNote("{}")'.format(f.name)) 163 | 164 | vim.command("let b:GeeknoteTitle=\"%s\"" % note.title) 165 | notebook = GeeknoteGetNotebook(note.notebookGuid) 166 | vim.command("let b:GeeknoteNotebook=\"%s\"" % notebook.name) 167 | # 168 | # Otherwise, the note has aleady been opened. Simply switch the active window 169 | # to the note's buffer. 170 | # 171 | else: 172 | tracker = GeeknoteGetNoteTracker(note) 173 | vim.command("buffer {}".format(tracker.buffer.name)) 174 | 175 | # 176 | # By default, Geeknote expects to receive notes with markdown-formated 177 | # content. Set the buffer's 'filetype' and 'syntax' options. 178 | # 179 | # TODO: Figure out why setting the 'syntax' buffer option alone does not 180 | # enable syntax highlighting and why setlocal is needed instead. 181 | # 182 | # vim.current.buffer.options['filetype'] = 'markdown' 183 | # vim.command('setlocal syntax=markdown') 184 | 185 | # Now restore the original window. 186 | setActiveWindow(origWin) 187 | 188 | def GeeknotePrepareToSaveNote(filename): 189 | filename = os.path.abspath(filename) 190 | tracker = openNotes[filename] 191 | tracker.modified = tracker.buffer.options['modified'] 192 | 193 | def GeeknoteGetFirstUsableWindow(): 194 | wnum = 1 195 | while wnum <= winnr('$'): 196 | bnum = winbufnr(wnum) 197 | buftype = getBufferVariable(bnum, 'buftype') 198 | isModified = getBufferVariable(bnum, 'modified') 199 | isPreviewWin = getWindowVariable(wnum, 'previewwindow') 200 | name = getBufferName(bnum) 201 | 202 | if ((bnum != -1) and 203 | (buftype == '') and 204 | (name == '') and 205 | (isPreviewWin is False) and 206 | ((isModified is False) or 207 | hidden())): 208 | return wnum 209 | wnum += 1 210 | return -1 211 | 212 | 213 | def GeeknoteIsWindowUsable(wnum): 214 | if winnr('$') == 1: 215 | return False 216 | 217 | bnum = vim.windows[wnum-1].buffer.number 218 | buftype = getBufferVariable(bnum, 'buftype') 219 | preview = getWindowVariable(wnum, 'previewwindow') 220 | 221 | # 222 | # If the window's buffer has a special type or is the preview window, it is 223 | # not usable. 224 | # 225 | if (buftype != '') or (preview is True): 226 | return False 227 | 228 | # If the user has the 'hidden' option set, the window is usable. 229 | if hidden(): 230 | return True 231 | 232 | # 233 | # If the window's buffer belongs to an unmodified note, the window is 234 | # usable. 235 | # 236 | name = getBufferName(bnum) 237 | if name in openNotes: 238 | isModified = getBufferVariable(bnum, 'modified') 239 | if isModified is False: 240 | return True 241 | 242 | # If the buffer is open in more than one window, the window is usable. 243 | return bufInWindows(winbufnr(wnum)) > 1 244 | 245 | -------------------------------------------------------------------------------- /plugin/vim_geeknote.py: -------------------------------------------------------------------------------- 1 | import vim 2 | import re 3 | 4 | from explorer import Explorer 5 | from view import * 6 | from utils import * 7 | from enml import * 8 | 9 | import evernote.edam.type.ttypes as Types 10 | import evernote.edam.error.ttypes as Errors 11 | 12 | # 13 | # +----------+---------------------------+ 14 | # | | | 15 | # | | | 16 | # | explorer | view | 17 | # | | | 18 | # | | | 19 | # +----------+---------------------------+ 20 | # 21 | # vim_geeknote.vim --> vim_geeknote.py --> explorer.py 22 | # | | 23 | # | | 24 | # | V 25 | # +-------> view.py 26 | # 27 | 28 | #======================== Globals ============================================# 29 | 30 | explorer = Explorer() 31 | 32 | #======================== Geeknote Functions ================================# 33 | 34 | def GeeknoteActivateNode(): 35 | explorer.activateNode(vim.current.line) 36 | 37 | def GeeknoteCommitStart(): 38 | explorer.commitChanges() 39 | 40 | def GeeknoteCommitComplete(): 41 | explorer.render() 42 | 43 | def GeeknoteCreateNote(title): 44 | # 45 | # Figure out what notebook to place the note in. Give preference to the 46 | # notebook selected in the explorer window (if one is selected). Otherwise, 47 | # place it into the default notebook. 48 | # 49 | notebook = explorer.getSelectedNotebook() 50 | if notebook is None: 51 | notebook = GeeknoteGetDefaultNotebook() 52 | 53 | if notebook is None: 54 | vim.command('echoerr "Please select a notebook first."') 55 | return 56 | 57 | # Cleanup the title of the note. 58 | title = title.strip('"\'') 59 | 60 | # Finally, create and open a blank note. 61 | note = Types.Note() 62 | note.title = title 63 | note.guid = None 64 | note.created = None 65 | note.notebookGuid = notebook.guid 66 | 67 | note = GeeknoteCreateNewNote(note) 68 | GeeknoteOpenNote(note) 69 | 70 | # Add the note to the navigation window. 71 | explorer.addNote(note) 72 | 73 | def GeeknoteCreateNotebook(name): 74 | notebook = Types.Notebook() 75 | notebook.name = name.strip('"\'') 76 | try: 77 | notebook = GeeknoteCreateNewNotebook(notebook) 78 | except: 79 | vim.command('echoerr "Failed to create notebook."') 80 | 81 | explorer.addNotebook(notebook) 82 | 83 | def GeeknoteHandleNoteSaveFailure(note, e): 84 | print e 85 | msg = '+------------------- WARNING -------------------+\n' 86 | msg += '| |\n' 87 | msg += '| Failed to save note (see error above) |\n' 88 | msg += '| |\n' 89 | msg += '| Save buffer to a file to avoid losing content |\n' 90 | msg += '| |\n' 91 | msg += '+------------------- WARNING -------------------+\n' 92 | vim.command('echoerr "%s"' % msg) 93 | 94 | def GeeknoteSaveAsNote(): 95 | global explorer 96 | 97 | # 98 | # Figure out what notebook to place the note in. Give preference 99 | # to the notebook selected in the explorer window (if one is 100 | # selected). Otherwise, place it into the default notebook. 101 | # 102 | notebook = None 103 | if explorer is not None: 104 | notebook = explorer.getSelectedNotebook() 105 | 106 | if notebook is None: 107 | notebook = GeeknoteGetDefaultNotebook() 108 | 109 | if notebook is None: 110 | vim.command('echoerr "Please select a notebook first."') 111 | return 112 | 113 | title = '' 114 | rows = len(vim.current.buffer) 115 | if rows > 0: 116 | title = vim.current.buffer[0].strip() 117 | else: 118 | vim.command('echoerr "Cannot save empty note."') 119 | return 120 | 121 | content = '' 122 | if rows > 1: 123 | start = 1 124 | while start < rows: 125 | if vim.current.buffer[start].strip() != '': 126 | break 127 | start += 1 128 | for r in range(start, len(vim.current.buffer)): 129 | content += vim.current.buffer[r] + '\n' 130 | 131 | note = Types.Note() 132 | note.title = title 133 | note.content = textToENML(content) 134 | note.created = None 135 | note.notebookGuid = notebook.guid 136 | 137 | try: 138 | note = GeeknoteCreateNewNote(note) 139 | note = GeeknoteLoadNote(note) 140 | except Exception as e: 141 | GeeknoteHandleNoteSaveFailure(note, e) 142 | return 143 | 144 | GeeknoteOpenNote(note) 145 | 146 | # Add the note to the navigation window. 147 | explorer.addNote(note) 148 | 149 | def GeeknoteSaveNote(filename): 150 | note = GeeknoteGetOpenNote(filename) 151 | changed = GeeknoteCommitChangesToNote(note) 152 | if changed: 153 | try: 154 | GeeknoteUpdateNote(note) 155 | except Exception as e: 156 | GeeknoteHandleNoteSaveFailure(note, e) 157 | 158 | def GeeknoteSearch(args): 159 | notes = GeeknoteGetNotes(args) 160 | 161 | explorer.clearSearchResults() 162 | explorer.addSearchResults(notes) 163 | explorer.render() 164 | 165 | def GeeknoteSync(): 166 | explorer.commitChanges() 167 | explorer.refresh() 168 | explorer.render() 169 | 170 | def GeeknoteTerminate(): 171 | GeeknoteCloseAllNotes() 172 | 173 | def GeeknoteToggle(): 174 | global explorer 175 | 176 | if explorer.isHidden(): 177 | explorer.show() 178 | else: 179 | explorer.hide() 180 | 181 | -------------------------------------------------------------------------------- /plugin/vim_geeknote.vim: -------------------------------------------------------------------------------- 1 | python import sys 2 | python import vim 3 | python sys.path.append(vim.eval('expand(":h")')) 4 | 5 | " ---------------------- Configuration ---------------------------------------- 6 | 7 | if has('nvim') 8 | let g:GeeknoteNeovimMode="True" 9 | endif 10 | 11 | " ---------------------- Functions -------------------------------------------- 12 | 13 | function! Vim_GeeknoteTerminate() 14 | python << endOfPython 15 | from vim_geeknote import GeeknoteTerminate 16 | GeeknoteTerminate() 17 | endOfPython 18 | endfunction 19 | 20 | function! Vim_GeeknoteToggle() 21 | python << endOfPython 22 | from vim_geeknote import GeeknoteToggle 23 | GeeknoteToggle() 24 | endOfPython 25 | endfunction 26 | 27 | function! Vim_GeeknoteActivateNode() 28 | python << endOfPython 29 | from vim_geeknote import GeeknoteActivateNode 30 | GeeknoteActivateNode() 31 | endOfPython 32 | endfunction 33 | 34 | function! Vim_GeeknoteCloseNote(arg1) 35 | python << endOfPython 36 | from vim_geeknote import GeeknoteCloseNote 37 | filename = vim.eval("a:arg1") 38 | GeeknoteCloseNote(filename) 39 | endOfPython 40 | endfunction 41 | 42 | function! Vim_GeeknoteCreateNotebook(arg1) 43 | python << endOfPython 44 | from vim_geeknote import GeeknoteCreateNotebook 45 | name = vim.eval("a:arg1") 46 | GeeknoteCreateNotebook(name) 47 | endOfPython 48 | endfunction 49 | 50 | function! Vim_GeeknoteCreateNote(arg1) 51 | python << endOfPython 52 | from vim_geeknote import GeeknoteCreateNote 53 | name = vim.eval("a:arg1") 54 | GeeknoteCreateNote(name) 55 | endOfPython 56 | endfunction 57 | 58 | function! Vim_GeeknoteSaveAsNote() 59 | python << endOfPython 60 | from vim_geeknote import GeeknoteSaveAsNote 61 | GeeknoteSaveAsNote() 62 | endOfPython 63 | endfunction 64 | 65 | function! Vim_GeeknoteSearch(arg1) 66 | python << endOfPython 67 | from vim_geeknote import GeeknoteSearch 68 | args = vim.eval("a:arg1") 69 | GeeknoteSearch(args) 70 | endOfPython 71 | endfunction 72 | 73 | function! Vim_GeeknotePrepareToSaveNote(arg1) 74 | python << endOfPython 75 | from vim_geeknote import GeeknotePrepareToSaveNote 76 | filename = vim.eval("a:arg1") 77 | GeeknotePrepareToSaveNote(filename) 78 | endOfPython 79 | endfunction 80 | 81 | function! Vim_GeeknoteSaveNote(arg1) 82 | python << endOfPython 83 | from vim_geeknote import GeeknoteSaveNote 84 | filename = vim.eval("a:arg1") 85 | GeeknoteSaveNote(filename) 86 | endOfPython 87 | endfunction 88 | 89 | function! Vim_GeeknoteSync() 90 | python << endOfPython 91 | from vim_geeknote import GeeknoteSync 92 | GeeknoteSync() 93 | endOfPython 94 | endfunction 95 | 96 | function! Vim_GeeknoteCommitStart() 97 | python << endOfPython 98 | from vim_geeknote import GeeknoteCommitStart 99 | GeeknoteCommitStart() 100 | endOfPython 101 | endfunction 102 | 103 | function! Vim_GeeknoteCommitComplete() 104 | python << endOfPython 105 | from vim_geeknote import GeeknoteCommitComplete 106 | GeeknoteCommitComplete() 107 | endOfPython 108 | endfunction 109 | 110 | " ---------------------- User Commands ---------------------------------------- 111 | 112 | command! Geeknote call Vim_GeeknoteToggle() 113 | command! -nargs=1 GeeknoteCreateNotebook call Vim_GeeknoteCreateNotebook() 114 | command! -nargs=1 GeeknoteCreateNote call Vim_GeeknoteCreateNote() 115 | command! GeeknoteSaveAsNote call Vim_GeeknoteSaveAsNote() 116 | command! -nargs=* GeeknoteSearch call Vim_GeeknoteSearch() 117 | command! GeeknoteSync call Vim_GeeknoteSync() 118 | -------------------------------------------------------------------------------- /powerline/config_files/plugin_geeknote-explorer.json: -------------------------------------------------------------------------------- 1 | { 2 | "segments": { 3 | "left": [ 4 | { 5 | "type": "string", 6 | "contents": "Geeknote", 7 | "highlight_group": ["file_name"] 8 | }, 9 | { 10 | "type": "string", 11 | "highlight_group": ["background"], 12 | "draw_soft_divider": false, 13 | "draw_hard_divider": false, 14 | "width": "auto" 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /powerline/config_files/plugin_geeknote.json: -------------------------------------------------------------------------------- 1 | { 2 | "segments": { 3 | "left": [ 4 | { 5 | "function": "mode", 6 | "exclude_modes": ["nc"] 7 | }, 8 | { 9 | "function": "visual_range", 10 | "include_modes": ["v", "V", "^V", "s", "S", "^S"], 11 | "priority": 10 12 | }, 13 | { 14 | "function": "paste_indicator", 15 | "exclude_modes": ["nc"], 16 | "priority": 10 17 | }, 18 | { 19 | "function": "powerline.segments.vim.plugin.capslock.capslock_indicator", 20 | "include_modes": ["i", "R", "Rv"], 21 | "priority": 10 22 | }, 23 | { 24 | "function": "powerline.segments.vim.plugin.geeknote.geeknote_get_notebook_name", 25 | "priority": 30 26 | }, 27 | { 28 | "function": "readonly_indicator", 29 | "draw_soft_divider": false, 30 | "after": " " 31 | }, 32 | { 33 | "function": "file_scheme", 34 | "priority": 20 35 | }, 36 | { 37 | "function": "powerline.segments.vim.plugin.geeknote.geeknote_get_note_title", 38 | "draw_soft_divider": false 39 | }, 40 | { 41 | "function": "file_vcs_status", 42 | "before": " ", 43 | "draw_soft_divider": false 44 | }, 45 | { 46 | "function": "modified_indicator", 47 | "before": " " 48 | }, 49 | { 50 | "exclude_modes": ["i", "R", "Rv"], 51 | "function": "trailing_whitespace", 52 | "display": false, 53 | "priority": 60 54 | }, 55 | { 56 | "exclude_modes": ["nc"], 57 | "function": "powerline.segments.vim.plugin.syntastic.syntastic", 58 | "priority": 50 59 | }, 60 | { 61 | "exclude_modes": ["nc"], 62 | "function": "powerline.segments.vim.plugin.tagbar.current_tag", 63 | "draw_soft_divider": false, 64 | "priority": 50 65 | }, 66 | { 67 | "type": "string", 68 | "highlight_group": ["background"], 69 | "draw_soft_divider": false, 70 | "draw_hard_divider": false, 71 | "width": "auto" 72 | } 73 | ], 74 | "right": [ 75 | { 76 | "function": "file_format", 77 | "draw_soft_divider": false, 78 | "exclude_modes": ["nc"], 79 | "priority": 60 80 | }, 81 | { 82 | "function": "file_encoding", 83 | "exclude_modes": ["nc"], 84 | "priority": 60 85 | }, 86 | { 87 | "function": "file_type", 88 | "exclude_modes": ["nc"], 89 | "priority": 60 90 | }, 91 | { 92 | "function": "line_percent", 93 | "priority": 50, 94 | "width": 4, 95 | "align": "r" 96 | }, 97 | { 98 | "function": "csv_col_current", 99 | "priority": 30 100 | }, 101 | { 102 | "type": "string", 103 | "name": "line_current_symbol", 104 | "highlight_group": ["line_current_symbol", "line_current"] 105 | }, 106 | { 107 | "function": "line_current", 108 | "draw_soft_divider": false, 109 | "width": 3, 110 | "align": "r" 111 | }, 112 | { 113 | "function": "virtcol_current", 114 | "draw_soft_divider": false, 115 | "priority": 20, 116 | "before": ":", 117 | "width": 3, 118 | "align": "l" 119 | } 120 | ] 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /powerline/matchers/geeknote.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8:noet 2 | from __future__ import (unicode_literals, division, absolute_import, print_function) 3 | 4 | import os 5 | import re 6 | 7 | from powerline.bindings.vim import buffer_name 8 | 9 | GEEKNOTE_RE = re.compile('__Geeknote__') 10 | 11 | def geeknote(matcher_info): 12 | name = buffer_name(matcher_info) 13 | return name and GEEKNOTE_RE.match(os.path.basename(name)) 14 | 15 | GEEKNOTE_EXPLORER_RE = re.compile('__GeeknoteExplorer__') 16 | 17 | def geeknote_explorer(matcher_info): 18 | name = buffer_name(matcher_info) 19 | return name and GEEKNOTE_EXPLORER_RE.match(os.path.basename(name)) 20 | -------------------------------------------------------------------------------- /powerline/segments/geeknote.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8:noet 2 | from __future__ import (unicode_literals, division, absolute_import, print_function) 3 | 4 | try: 5 | import vim 6 | except ImportError: 7 | vim = object() 8 | 9 | from powerline.bindings.vim import bufvar_exists 10 | from powerline.segments.vim import window_cached 11 | 12 | 13 | @window_cached 14 | def geeknote_get_note_title(pl): 15 | if not bufvar_exists(None, 'GeeknoteTitle'): 16 | return None 17 | 18 | title = vim.eval('getbufvar("%", "GeeknoteTitle")') 19 | return [{ 20 | 'contents': title, 21 | 'highlight_group': ['file_name'], 22 | }] 23 | 24 | def geeknote_get_notebook_name(pl): 25 | if not bufvar_exists(None, 'GeeknoteNotebook'): 26 | return None 27 | 28 | name = vim.eval('getbufvar("%", "GeeknoteNotebook")') 29 | return [{ 30 | 'contents': name, 31 | 'highlight_group': ['file_name'], 32 | }] 33 | -------------------------------------------------------------------------------- /syntax/geeknote.vim: -------------------------------------------------------------------------------- 1 | syn match GeeknoteSep #=\+# 2 | 3 | syn match GeeknoteNotebookKey #N\[.\+\]# 4 | syn match GeeknoteNoteKey #n\[.\+\]# 5 | syn match GeeknoteTagKey #T\[.\+\]# 6 | 7 | syn match GeeknoteNotebook #^.\+N\[.\+\]# contains=GeeknoteNotebookKey 8 | syn match GeeknoteNote #^.\+n\[.\+\]# contains=GeeknoteNoteKey 9 | syn match GeeknoteTag #^.\+T\[.\+\]# contains=GeeknoteTagKey 10 | 11 | hi def link GeeknoteNotebookKey ignore 12 | hi def link GeeknoteNoteKey ignore 13 | hi def link GeeknoteTagKey ignore 14 | hi def link GeeknoteSep Question 15 | hi def link GeeknoteNotebook Title 16 | hi def link GeeknoteTag Title 17 | hi def link GeeknoteNote Type 18 | --------------------------------------------------------------------------------