├── LICENSE ├── README.md ├── autoload └── so.vim └── doc └── so.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Christopher Allen Lane 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-so 2 | ====== 3 | `vim-so` wraps the [so][] executable to provide a plain-language search interface 4 | for code snippets on Stack Overflow. 5 | 6 | 7 | Example 8 | ------- 9 | Assume that you're building a Go application, and have forgotten how to 10 | reverse an array. You may run the following ex command: 11 | 12 | `:So reverse an array` 13 | 14 | The following will occur: 15 | 16 | 1. `vim-so` will detect the current `filetype` 17 | 2. `vim` will execute the following shell command: `so -t go "reverse an array"` 18 | 3. The first applicable code snippet will be pasted into the current buffer 19 | 4. You may cycle among snippets via `so#next()` and `so#prev()` 20 | 21 | 22 | Installation 23 | ------------ 24 | `vim-so` can be installed as described in `:help packages`, or by using a 25 | package manager like Pathogen, Vundle, or Plug. 26 | 27 | 28 | Functions 29 | --------- 30 | #### so#search #### 31 | Search the Stack Overflow API for code snippets matching a phrase. It's 32 | recommended that you map this function to a convenient command: 33 | 34 | ```vim 35 | command -nargs=1 So call so#search() 36 | ``` 37 | 38 | #### so#next #### 39 | Cycle to the next code snippet. It's recommended that you map this function to 40 | a convenient hotkey: 41 | 42 | ```vim 43 | nnoremap :call so#next() 44 | ``` 45 | 46 | #### so#prev #### 47 | Cycle to the previous code snippet. It's recommended that you map this function 48 | to a convenient hotkey: 49 | 50 | ```vim 51 | nnoremap :call so#prev() 52 | ``` 53 | 54 | #### so#view #### 55 | Open the Stack Overflow thread associated with the current code snippet in 56 | your web browser: 57 | 58 | ```vim 59 | command SoView :call so#view() 60 | ``` 61 | 62 | 63 | Options 64 | ------- 65 | #### g:so_browser #### 66 | The browser executable that should be used in conjunction with the `so#view` 67 | function. 68 | 69 | 70 | Configuring 71 | ----------- 72 | Here's a practical configuration example. Add these lines to your `.vimrc`: 73 | 74 | ```vim 75 | let g:so_browser = "firefox" 76 | command -nargs=1 So call so#search() 77 | command SoView call so#view() 78 | nnoremap :call so#next() 79 | nnoremap :call so#prev() 80 | ``` 81 | 82 | 83 | [so]: https://github.com/cheat/so 84 | -------------------------------------------------------------------------------- /autoload/so.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================= 2 | " File: vim-so.vim 3 | " Description: Query the Stack Overflow API from Vim. 4 | " Mantainer: Chris Allen Lane (https://chris-allen-lane.com) 5 | " Url: https://github.com/cheat/so.vim 6 | " License: MIT 7 | " Version: 1.0.0 8 | " Last Changed: August 8, 2020 9 | " ============================================================================= 10 | 11 | " track answers received from Stack Overflow 12 | let s:answers = [] 13 | 14 | " track answer index 15 | let s:answer_idx = 0 16 | 17 | " send a search query to the Stack Overflow API 18 | "{{{ 19 | function! so#search(search) 20 | " ensure that the plugin is properly configured 21 | if s:configured() == 0 22 | return 23 | endif 24 | 25 | " new search: reset the answer index 26 | let s:answer_idx = 0 27 | 28 | " fail if the filetype is unknown 29 | if &filetype == "" 30 | call s:warn('query aborted: `filetype` is unknown') 31 | return 32 | endif 33 | 34 | " query the Stack Overflow API 35 | let json = system(printf("%s --json -t %s '%s'", s:bin(), &filetype, a:search)) 36 | 37 | " decode and store the JSON response 38 | let s:answers = json_decode(json) 39 | 40 | " paste the answer 41 | call s:paste(s:answers[s:answer_idx]['code']) 42 | endfunction 43 | "}}} 44 | 45 | " paste the next answer 46 | "{{{ 47 | function! so#next() 48 | " ensure that the plugin is properly configured 49 | if s:configured() == 0 50 | return 51 | endif 52 | 53 | " if the answers list is empty, display a warning 54 | if len(s:answers) == 0 55 | let s:answer_idx = 0 56 | call s:warn('no answers available') 57 | return 58 | endif 59 | 60 | " increment the answer counter 61 | let s:answer_idx += 1 62 | 63 | " if the answer counter is OOB, point to the last answer 64 | if len(s:answers) <= s:answer_idx 65 | let s:answer_idx = len(s:answers) - 1 66 | call s:warn('no next answer') 67 | return 68 | endif 69 | 70 | " paste the answer 71 | silent undo 72 | call s:paste(s:answers[s:answer_idx]['code']) 73 | endfunction 74 | "}}} 75 | 76 | " paste the previous answer 77 | "{{{ 78 | function! so#prev() 79 | " ensure that the plugin is properly configured 80 | if s:configured() == 0 81 | return 82 | endif 83 | 84 | " if the answers list is empty, display a warning 85 | if len(s:answers) == 0 86 | let s:answer_idx = 0 87 | call s:warn('no answers available') 88 | return 89 | endif 90 | 91 | " decrement the answer counter 92 | let s:answer_idx -= 1 93 | 94 | " if the answer counter is OOB, point to the first answer 95 | if s:answer_idx < 0 96 | let s:answer_idx = 0 97 | call s:warn('no previous answer') 98 | return 99 | endif 100 | 101 | " paste the answer 102 | silent undo 103 | call s:paste(s:answers[s:answer_idx]['code']) 104 | endfunction 105 | "}}} 106 | 107 | " opens the stack overflow thread associated with the current answer in a 108 | " browser 109 | "{{{ 110 | function! so#view() 111 | " ensure that the plugin is properly configured 112 | if s:configured() == 0 113 | return 114 | endif 115 | 116 | " fail if the browser is unset 117 | if exists('g:so_browser') == 0 118 | call s:warn('`g:so_browser` is not set: cannot view URL') 119 | return 120 | endif 121 | 122 | " fail if the specified browser is not on the PATH 123 | if executable(g:so_browser) == 0 124 | call s:warn('browser `' . g:so_browser . '` is not available on the $PATH') 125 | return 0 126 | endif 127 | 128 | " if the answers list is empty, display a warning 129 | if len(s:answers) == 0 130 | let s:answer_idx = 0 131 | call s:warn('no answers available') 132 | return 133 | endif 134 | 135 | silent execute "!" g:so_browser shellescape(s:answers[s:answer_idx]['link'], '#') 136 | endfunction 137 | "}}} 138 | 139 | " helper function that returns the path to the so executable 140 | "{{{ 141 | function! s:bin() 142 | return get(g:, 'so_bin', 'so') 143 | endfunction 144 | "}}} 145 | 146 | " helper function that pastes answer text into the buffer 147 | "{{{ 148 | function! s:paste(text) 149 | call s:info("answer", s:answer_idx + 1, "of", len(s:answers)) 150 | silent put = a:text 151 | endfunction 152 | "}}} 153 | 154 | " helper that ensurs that the plugin has been properly configured 155 | "{{{ 156 | function! s:configured() 157 | " ensure that `so` is available on the `$PATH` 158 | if executable(s:bin()) == 0 159 | call s:warn(s:bin() . ' is not available on the $PATH') 160 | return 0 161 | endif 162 | 163 | " if we make it here, we're properly configured 164 | return 1 165 | endfunction 166 | "}}} 167 | 168 | " helper function that displays info 169 | "{{{ 170 | function! s:info(...) 171 | echo 'so:' join(a:000) 172 | endfunction 173 | "}}} 174 | 175 | " helper function that displays a warning 176 | "{{{ 177 | function! s:warn(...) 178 | echohl WarningMsg 179 | echo 'so:' join(a:000) 180 | echohl None 181 | endfunction 182 | "}}} 183 | -------------------------------------------------------------------------------- /doc/so.txt: -------------------------------------------------------------------------------- 1 | *vim-so.txt* Search for Stack Overflow code snippets within Vim 2 | 3 | vim-so 4 | ~ 5 | Reference Manual 6 | 7 | 8 | ============================================================================== 9 | CONTENTS *vim-so-contents* 10 | 11 | 1. Overview ........................................|vim-so-overview| 12 | 2. Example .........................................|vim-so-example| 13 | 3. Installation ....................................|vim-so-installation| 14 | 4. Functions .......................................|vim-so-functions| 15 | 5. Options .........................................|vim-so-options| 16 | 6. Config ..........................................|vim-so-config| 17 | 7. Changelog .......................................|vim-so-changelog| 18 | 8. Contributing ....................................|vim-so-contributing| 19 | 20 | 21 | ============================================================================== 22 | 1. Overview *vim-so-overview* 23 | 24 | 25 | 26 | `vim-so` wraps the `so` executable to provide a plain-language search interface 27 | for code snippets on Stack Overflow. 28 | 29 | 30 | ============================================================================== 31 | 2. Example *vim-so-example* 32 | 33 | 34 | 35 | Assume that you're building a Go application, and have forgotten how to 36 | reverse an array. You may run the following ex command: 37 | 38 | `:So reverse an array` 39 | 40 | The following will occur: 41 | 42 | 1. `vim-so` will detect the current `filetype` 43 | 2. `vim` will execute the following shell command: `so -t go "reverse an array"` 44 | 3. The first applicable code snippet will be pasted into the current buffer 45 | 4. You may cycle among snippets via `so#next()` and `so#prev()` 46 | 47 | 48 | ============================================================================== 49 | 3. Installation *vim-so-installation* 50 | 51 | 52 | 53 | `vim-so` can be installed as described in |packages|, or by using a 54 | package manager like Pathogen, Vundle, or Plug. 55 | 56 | 57 | ============================================================================== 58 | 4. Functions *vim-so-functions* 59 | 60 | 61 | 62 | `so#search` *so#search* 63 | 64 | Search the Stack Overflow API for code snippets matching a phrase. It's 65 | recommended that you map this function to a convenient command: 66 | 67 | `command -nargs=1 So call so#search()` 68 | 69 | 70 | `so#next` *so#next* 71 | 72 | Cycle to the next code snippet. It's recommended that you map this function to 73 | a convenient hotkey: 74 | 75 | `nnoremap :call so#next()` 76 | 77 | 78 | `so#prev` *so#prev* 79 | 80 | Cycle to the previous code snippet. It's recommended that you map this 81 | function to a convenient hotkey: 82 | 83 | `nnoremap :call so#prev()` 84 | 85 | 86 | `so#view` *so#view* 87 | 88 | Open the Stack Overflow thread associated with the current code snippet in 89 | your web browser: 90 | 91 | `command SoView :call so#view()` 92 | 93 | 94 | ============================================================================== 95 | 5. Options *vim-so-options* 96 | 97 | 98 | 99 | `g:so_bin` *g:so_bin* 100 | 101 | The path to the `so` executable. Defaults to `so`. 102 | 103 | 104 | `g:so_browser` *g:so_browser* 105 | 106 | The browser executable that should be used in conjunction with the `so#view` 107 | function. 108 | 109 | 110 | ============================================================================== 111 | 6. Config *vim-so-config* 112 | 113 | 114 | 115 | Here's a practical configuration example. Add these lines to your |vimrc|: 116 | 117 | `let g:so_browser = "firefox"` 118 | `command -nargs=1 So call so#search()` 119 | `command SoView call so#view()` 120 | `nnoremap :call so#next()` 121 | `nnoremap :call so#prev()` 122 | 123 | ============================================================================== 124 | 7. Changelog *vim-so-changelog* 125 | 126 | 127 | 128 | * v1.0.0: Initial release 129 | 130 | 131 | ============================================================================== 132 | 8. Contributing *vim-so-contributing* 133 | 134 | 135 | 136 | If you would like to contribute to the development of this plugin, please fork 137 | its repository on Github and open a pull-request: 138 | 139 | https://github.com/chrisallenlane/vim-so 140 | 141 | If you would like to report a bug or make a feature request, please do so in 142 | the Github issue tracker: 143 | 144 | https://github.com/chrisallenlane/vim-so/issues 145 | 146 | You may also contact the author of this plugin directly: 147 | 148 | Chris Allen Lane 149 | 150 | vim:tw=78:ts=8:noet:ft=help:norl: 151 | --------------------------------------------------------------------------------