├── COPYING ├── README.md ├── autoload ├── bufmru.vim └── bufmru │ └── lightline.vim ├── doc └── bufmru.txt └── plugin └── bufmru.vim /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Mildred Ki'Lya 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation files 5 | (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, 8 | and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 18 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BufMRU: Switch buffers in most recently used order 2 | ================================================== 3 | 4 | Install this plugin using: 5 | 6 | Plug 'mildred/vim-bufmru' 7 | 8 | Set the mapping: 9 | 10 | " Alt-B or Alt-Shift-B to navigate buffers in insert mode and normal mode 11 | imap :BufMRUPrev 12 | imap :BufMRUNext 13 | map :BufMRUPrev 14 | map :BufMRUNext 15 | nmap B :BufMRUPrev 16 | nmap b :BufMRUNext 17 | 18 | " Key above escape (on french keyboards) to commit current buffer as last 19 | " used 20 | map ² :BufMRUCommit 21 | 22 | " Tab and Shift-Tab in normal mode to navigate buffers 23 | map :BufMRUNext 24 | map :BufMRUPrev 25 | 26 | You are supposed to be able to press multiple times the Alt-B or Alt-Shift-B 27 | key sequences to get to the next file. Like many editors have Ctrl-Tab and 28 | Ctrl-Shift-Tab. 29 | 30 | The list is reordered with the current buffer put in front when you make use of 31 | that buffer. This involves moving the cursor around, changing the buffer content 32 | or switching to and back from insert mode. 33 | 34 | This plugin works with [vim-airline](https://github.com/vim-airline/vim-airline/) 35 | and has a very nice plugin for [lightline.vim](https://github.com/itchyny/lightline.vim/). 36 | 37 | Don't forget this is a vim plugin and everything is in the documentation 38 | [`:help bufmru.txt`](doc/bufmru.txt). 39 | 40 | Note: I just learnt to use vim and created this plugin because I didn't find 41 | anything suitable for me. This is my first vim plugin ever and can probably be 42 | improved. 43 | 44 | Hacking 45 | ======= 46 | 47 | Regenerate documentation tags: 48 | 49 | :helptags doc 50 | -------------------------------------------------------------------------------- /autoload/bufmru.vim: -------------------------------------------------------------------------------- 1 | function! bufmru#sort(b1, b2) 2 | let t1 = str2float(reltimestr(BufMRUTime(a:b1))) 3 | let t2 = str2float(reltimestr(BufMRUTime(a:b2))) 4 | return t1 == t2 ? 0 : t1 > t2 ? -1 : 1 5 | endfunction 6 | 7 | function! bufmru#enter() 8 | let s:bufmru_entertime = reltime() 9 | if ! s:going 10 | call bufmru#save("enter()") 11 | endif 12 | let s:going = 0 13 | call bufmru#autocmd() 14 | endfunction 15 | 16 | function! bufmru#save(reason) 17 | "echo "save(" a:reason ")" 18 | let g:bufmru_reason = a:reason 19 | let i = bufnr("%") 20 | let s:going = 0 21 | if buflisted(i) 22 | let oldVal = BufMRUTime(i) 23 | let s:bufmru_files[i] = s:bufmru_entertime 24 | if reltimestr(oldVal) != reltimestr(s:bufmru_entertime) 25 | silent doautocmd User BufMRUChange 26 | if get(g:, 'airline#extensions#tabline#enabled', 0) 27 | call airline#extensions#tabline#buflist#invalidate() 28 | endif 29 | if !empty(get(g:, 'lightline', {})) 30 | call lightline#update() 31 | end 32 | " Change currect buffer to force updating the airline buffer list 33 | if bufnr("$") > 1 34 | " Toggle showing the tabline off and on to refresh it 35 | let stl=&showtabline 36 | set showtabline=0 37 | let &showtabline=stl 38 | endif 39 | endif 40 | endif 41 | "unmap 42 | endfunction 43 | 44 | function! bufmru#save_change(reason, timeout) 45 | let totaltime = str2float(reltimestr(reltime(s:bufmru_entertime))) 46 | if totaltime > a:timeout 47 | call bufmru#save(a:reason) 48 | endif 49 | endfunction 50 | 51 | function! bufmru#leave() 52 | let totaltime = str2float(reltimestr(reltime(s:bufmru_entertime))) 53 | if totaltime >= 1.0 54 | call bufmru#save("leave()") 55 | endif 56 | endfunction 57 | 58 | function! BufMRUTime(bufn) 59 | return has_key(s:bufmru_files, a:bufn) ? s:bufmru_files[a:bufn] : s:bufmru_starttime 60 | endfunction 61 | 62 | function! BufMRUList() 63 | let bufs = range(1, bufnr("$")) 64 | let res = [] 65 | call sort(bufs, "bufmru#sort") 66 | for nr in bufs 67 | if buflisted(nr) 68 | call add(res, nr) 69 | endif 70 | endfor 71 | return res 72 | endfunction 73 | 74 | function! bufmru#show() 75 | call bufmru#save("show()") 76 | let bufs = BufMRUList() 77 | for buf in bufs 78 | let bufn = bufname(str2nr(buf)) 79 | let buft = reltimestr(reltime(BufMRUTime(buf))) 80 | echom buf " | " buft "s | " bufn 81 | endfor 82 | endfunction 83 | 84 | function! bufmru#go(inc) 85 | "call bufmru#leave() 86 | let list = BufMRUList() 87 | let idx = index(list, bufnr("%")) 88 | let i = list[((idx < 0 ? 0 : idx) + a:inc) % len(list)] 89 | let s:going = 1 90 | call bufmru#noautocmd() 91 | execute "buffer" i 92 | "call bufmru#save("go") 93 | "noremap :BufMRUCommit 94 | endfunction 95 | 96 | 97 | function! bufmru#init() 98 | let s:bufmru_files = {} 99 | let s:bufmru_starttime = reltime() 100 | let s:bufmru_entertime = s:bufmru_starttime 101 | let s:going = 0 102 | 103 | call bufmru#autocmd() 104 | endfunction 105 | 106 | function bufmru#noautocmd() 107 | augroup bufmru_buffers 108 | autocmd! 109 | autocmd BufEnter * call bufmru#enter() 110 | augroup END 111 | endfunction 112 | 113 | function bufmru#autocmd() 114 | augroup bufmru_buffers 115 | autocmd! 116 | autocmd BufEnter * call bufmru#enter() 117 | "autocmd BufLeave * call bufmru#leave() 118 | "autocmd InsertEnter,InsertLeave * call bufmru#save("InsertEnter,InsertLeave") 119 | autocmd InsertEnter * call bufmru#save("InsertEnter") 120 | autocmd InsertLeave * call bufmru#save("InsertLeave") 121 | autocmd TextChanged * call bufmru#save("TextChanged") 122 | autocmd TextChangedI * call bufmru#save("TextChangedI") 123 | "autocmd CursorHold,CursorHoldI * call bufmru#save("CursorHold,CursorHoldI") 124 | "autocmd CursorMoved * call bufmru#save_change("CursorMoved", 0.1) 125 | autocmd CursorMovedI * call bufmru#save_change("CursorMovedI", 0.1) 126 | augroup END 127 | endfunction 128 | 129 | -------------------------------------------------------------------------------- /autoload/bufmru/lightline.vim: -------------------------------------------------------------------------------- 1 | 2 | hi BufMRULightlineActive cterm=underline 3 | 4 | let s:buffers = {} 5 | 6 | function! s:dirname(path) 7 | return fnamemodify(a:path.'a', ':h') 8 | endfunction 9 | 10 | function! s:basename(path) 11 | return fnamemodify(a:path.'a', ':t') 12 | endfunction 13 | 14 | function! s:tailfile(path, num) 15 | let num = a:num - 1 16 | let name = fnamemodify(a:path, ':t') 17 | let path = fnamemodify(a:path, ':h') 18 | while num > 0 19 | let name = fnamemodify(path, ':t') . '/' . name 20 | let path = fnamemodify(path, ':h') 21 | let num = num - 1 22 | endwhile 23 | return name 24 | endfunction 25 | 26 | function! bufmru#lightline#buffer_name(buf, bufs) 27 | if has_key(s:buffers, a:buf) 28 | return s:buffers[a:buf]['name'] 29 | endif 30 | let path = bufname(str2nr(a:buf)) 31 | if path == '' 32 | return '[no name]' 33 | endif 34 | 35 | let conflicts = 1 36 | let tailnum = 1 37 | while conflicts 38 | let conflicts = 0 39 | let name = s:tailfile(path, tailnum) 40 | for b in a:bufs 41 | if b == a:buf 42 | continue 43 | endif 44 | let bpath = bufname(str2nr(b)) 45 | let bname = s:tailfile(bpath, tailnum) 46 | if bname == name 47 | let conflicts = conflicts + 1 48 | if has_key(s:buffers, b) 49 | if s:buffers[b]['size'] < tailnum 50 | remove(s:buffers, b) 51 | endif 52 | endif 53 | endif 54 | endfor 55 | let tailnum = tailnum + 1 56 | endwhile 57 | 58 | let s:buffers[a:buf] = { 'name': name, 'size': tailnum } 59 | return name 60 | endfunction 61 | 62 | let g:bufmru_lightline_highlight = 'LightlineLeft_tabline_0' 63 | let g:bufmru_lightline_highlight_active = 'LightlineLeft_tabline_tabsel_0' 64 | 65 | function! bufmru#lightline#buffer_tag(buf, bufs, active) 66 | let name = bufmru#lightline#buffer_name(str2nr(a:buf), a:bufs) 67 | let name = substitute(name, '%', '%%', 'g') 68 | let name .= (getbufvar(a:buf, "&mod")?'*':'') 69 | "if a:active 70 | " let name = '['.name.']' 71 | "endif 72 | let text = bufmru#lightline#nr2superscript(a:buf) . name 73 | let markup = '%' . a:buf . '@bufmru#lightline#bufgo@' . text . '%T' 74 | "if a:active 75 | " let markup = '%#' . g:bufmru_lightline_highlight_active . '#' . markup . '%#' . g:bufmru_lightline_highlight . '#' 76 | "else 77 | " let markup = '%#' . g:bufmru_lightline_highlight . '#' . markup 78 | "endif 79 | return [a:buf.name, markup] 80 | endfunction 81 | 82 | function! bufmru#lightline#nr2superscript(nr) 83 | let res = "" 84 | let conv = { 85 | \ '0': '⁰', '1': '¹', '2': '²', '3': '³', '4': '⁴', 86 | \ '5': '⁵', '6': '⁶', '7': '⁷', '8': '⁸', '9': '⁹' 87 | \ } 88 | for digit in split(a:nr, '\zs') 89 | let res = res . conv[digit] 90 | endfor 91 | return res 92 | endfunction 93 | 94 | function! bufmru#lightline#firstbuffer() 95 | let bufs = BufMRUList() 96 | let buf = bufnr('%') 97 | if bufs[0] == buf 98 | let [t, m] = bufmru#lightline#buffer_tag(buf, bufs, 1) 99 | return m 100 | else 101 | let [t, m] = bufmru#lightline#buffer_tag(buf, bufs, 1) 102 | return m 103 | endif 104 | endfunction 105 | 106 | function! bufmru#lightline#close() 107 | return '%0@bufmru#lightline#bufclose@ x %X' 108 | endfunction 109 | 110 | function! bufmru#lightline#initvars() 111 | let el = '…' 112 | let ellen = -1 113 | let seplen = 1 114 | let reserve = 0 115 | if exists('g:bufmru_lightline_ellipsis') 116 | let el = g:bufmru_lightline_ellipsis 117 | else 118 | let el = '…' 119 | let ellen = 1 120 | endif 121 | if exists('g:bufmru_lightline_ellipsis_len') 122 | let ellen = g:bufmru_lightline_ellipsis_len 123 | elseif ellen < 0 124 | let ellen = strlen(el) 125 | endif 126 | if exists('g:bufmru_lightline_sep_len') 127 | let seplen = g:bufmru_lightline_sep_len 128 | endif 129 | if exists('g:bufmru_lightline_reserve') 130 | let reserve = g:bufmru_lightline_reserve 131 | endif 132 | return [ el, ellen, seplen, reserve ] 133 | endfunction 134 | 135 | function! bufmru#lightline#buffers() 136 | let res = [[], [], []] 137 | let lens = [[], [], []] 138 | let bufs = BufMRUList() 139 | let first = 1 140 | let active = bufnr('%') 141 | let i = 0 142 | for buf in bufs 143 | "if first && buf == bufnr('%') 144 | " continue 145 | "endif 146 | if buf == active && i < 2 147 | let i = 1 148 | elseif buf != active && i == 1 149 | let i = 2 150 | endif 151 | let [t, m] = bufmru#lightline#buffer_tag(buf, bufs, buf == active) 152 | let lens[i] += [ len(t)+2 ] 153 | let res[i] += [ ' '.m.' ' ] 154 | let first = 0 155 | endfor 156 | 157 | let [ ellipsis, ellen, seplen, reserve ] = bufmru#lightline#initvars() 158 | let seplen = 1 159 | let ellen1 = ellen + seplen 160 | let ellen2 = 2 * ellen1 161 | let res2 = [[], res[1], []] 162 | let width = &columns 163 | let maxw = width - reserve 164 | let curw = seplen 165 | for w in lens[1] 166 | let curw += w + seplen 167 | endfor 168 | let res200 = [] 169 | let firstellipsis = 0 170 | if len(res[0]) > 0 171 | if curw+lens[0][0]+seplen+ellen2 < maxw 172 | let res200 += [ res[0][0] ] 173 | let curw += lens[0][0] + seplen 174 | else 175 | let res200 += [ ellipsis ] 176 | let curw += 1 + seplen 177 | let firstellipsis = 1 178 | endif 179 | endif 180 | let i = len(lens[0])-1 181 | while i >= 1 && curw+lens[0][i]+seplen+ellen2 < maxw 182 | let curw += lens[0][i] + seplen 183 | let res2[0] = [ res[0][i] ] + res2[0] 184 | let i = i - 1 185 | endwhile 186 | if i > 1 && !firstellipsis 187 | let curw += 1 + seplen 188 | let res2[0] = [ ellipsis ] + res2[0] 189 | endif 190 | let res2[0] = res200 + res2[0] 191 | let i = 0 192 | let lenres2 = len(lens[2]) 193 | while i < lenres2 && curw+lens[2][i]+seplen+ellen1 < maxw 194 | let curw += lens[2][i] + seplen 195 | let res2[2] += [ res[2][i] ] 196 | let i = i + 1 197 | endwhile 198 | if i < lenres2 199 | let res2[2] += [ ellipsis ] 200 | let curw += 1 + seplen 201 | endif 202 | let g:bufmru_lightline_ellipsis_debug = 'maxw='.maxw.' curw='.curw.' width='.width.' reserve='.reserve 203 | 204 | return res2 205 | endfunction 206 | 207 | function! bufmru#lightline#bufgo(num, numclicks, mousebtn, modifiers) 208 | let active = bufnr('%') 209 | if a:mousebtn == 'm' 210 | if active == a:num 211 | call bufmru#go(1) 212 | end 213 | execute "bd" a:num 214 | else 215 | execute "buffer" a:num 216 | end 217 | endfunction 218 | 219 | function! bufmru#lightline#bufclose(num, numclicks, mousebtn, modifiers) 220 | let nr = bufnr('%') 221 | call bufmru#go(1) 222 | execute "bd" nr 223 | endfunction 224 | 225 | function! bufmru#lightline#tabnum() 226 | if tabpagenr('$') == 1 227 | return '' " no tabs 228 | endif 229 | let nr = tabpagenr() 230 | return '%0@bufmru#lightline#tabnum_click@ tab: '.nr.' %X' 231 | endfunction 232 | 233 | function! bufmru#lightline#tabnum_click(num, numclicks, mousebtn, modifiers) 234 | if a:mousebtn == 'r' 235 | tabprevious 236 | elseif a:mousebtn == 'm' 237 | tabclose 238 | elseif a:mousebtn == 'l' 239 | tabnext 240 | end 241 | endfunction 242 | 243 | -------------------------------------------------------------------------------- /doc/bufmru.txt: -------------------------------------------------------------------------------- 1 | *bufmru.txt* Order buffers in Most Recently Used order 2 | -------------------------------------------------------------------- 3 | ~ 4 | ____ __ __ __ ____ _ _ ~ 5 | | __ ) _ _ / _| \/ | _ \| | | | ~ 6 | | _ \| | | | |_| |\/| | |_) | | | | ~ 7 | | |_) | |_| | _| | | | _ <| |_| | ~ 8 | |____/ \__,_|_| |_| |_|_| \_\\___/ ~ 9 | ~ 10 | ~ 11 | ==================================================================== 12 | CONTENTS *BufMRUContents* 13 | 14 | 1. Usage ................ |BufMRUUsage| 15 | 2. Mappings ............. |BufMRUMappings| 16 | 3. License .............. |BufMRULicense| 17 | 4. Commands ............. |BufMRUCommands| 18 | 5. Functions ............ |BufMRUFunctions| 19 | 6. Lightline component .. |BufMRULightline| 20 | 21 | ==================================================================== 22 | Section 1: Usage *BufMRUUsage* 23 | 24 | This plugin records the last used buffers, and let you list the 25 | buffers, and navigate between the buffers 26 | 27 | ==================================================================== 28 | Section 2: Mappings *BufMRUMappings* 29 | 30 | The mapping is left to the user to do in the .vimrc. Recommended 31 | mapping is to use Alt-B for next buffer in list and Alt-Shift-B for 32 | previous buffer in list. 33 | 34 | map :BufMRUPrev 35 | map :BufMRUNext 36 | map B :BufMRUPrev 37 | map b :BufMRUNext 38 | 39 | ==================================================================== 40 | Section 3: License *BufMRULicense* 41 | 42 | Copyright (c) 2015 Mildred Ki'Lya 43 | 44 | Permission is hereby granted, free of charge, to any person 45 | obtaining a copy of this software and associated documentation files 46 | (the "Software"), to deal in the Software without restriction, 47 | including without limitation the rights to use, copy, modify, merge, 48 | publish, distribute, sublicense, and/or sell copies of the Software, 49 | and to permit persons to whom the Software is furnished to do so, 50 | subject to the following conditions: 51 | 52 | The above copyright notice and this permission notice shall be 53 | included in all copies or substantial portions of the Software. 54 | 55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 56 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 57 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 58 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 59 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 60 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 61 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 62 | SOFTWARE. 63 | 64 | ==================================================================== 65 | Section 4: Commands *BufMRUCommands* 66 | 67 | *:BufMRU* 68 | :BufMRU 69 | List the buffers in MRU order. 70 | 71 | *:BufMRUNext* 72 | :BufMRUNext 73 | Switch to the next buffer in MRU order. 74 | 75 | *:BufMRUPrev* 76 | :BufMRUPrev 77 | Switch to the previous buffer in MRU order. 78 | 79 | 80 | ==================================================================== 81 | Section 5: Functions *BufMRUFunctions* 82 | 83 | *BufMRUList()* 84 | :BufMRUList() 85 | Return the list of buffers tracked by BufMRU sorted in Most 86 | Recently Used order. Each buffer is represented by its 87 | identifier. 88 | 89 | *BufMRUTime()* 90 | :BufMRUTime(n) 91 | Return the time the buffer n was last used in reltime() 92 | format. 93 | 94 | 95 | ==================================================================== 96 | Section 6: Lightline component *BufMRULightline* 97 | 98 | A ligneline component can be installed by adding it to the 99 | |g:lightline| global variable. A recommended configuration is: 100 | 101 | *bufmru#lightline#buffers()* 102 | *bufmru#lightline#close()* 103 | let g:lightline = { 104 | \ 'tabline': { 105 | \ 'left': [ [ 'mrubuffers' ] ], 106 | \ 'right': [ [ 'bufferclose', 'tabnum' ] ] 107 | \ }, 108 | \ 'component_expand': { 109 | \ 'mrubuffers': 'bufmru#lightline#buffers', 110 | \ 'bufferclose': 'bufmru#lightline#close', 111 | \ 'tabnum': 'bufmru#lightline#tabnum', 112 | \ }, 113 | \ 'component_type': { 114 | \ 'buffers': 'tabsel', 115 | \ 'mrubuffers': 'tabsel', 116 | \ }, 117 | \ 'component_raw': { 118 | \ 'mrubuffers': 1, 119 | \ 'bufferclose': 1, 120 | \ 'tabnum': 1, 121 | \ }, 122 | \ 'enable': { 123 | \ 'statusline': 1, 124 | \ 'tabline': 1 125 | \ } 126 | \ ... 127 | \ } 128 | 129 | In order to have correct ellipsis working, you can configure the 130 | number of characters that the component should reserve on the 131 | tabline. The component will then take the window width minus this 132 | reserve. You can count the space needed by other components on your 133 | tabline and set the reserve size accordingly. 134 | 135 | *g:bufmru_lightline_reseve* 136 | g:bufmru_lightline_reseve = 5 137 | 138 | You can also configure the ellipsis format: 139 | 140 | *g:bufmru_lightline_ellipsis* 141 | g:bufmru_lightline_ellipsis = '…' 142 | 143 | In case the character is a unicode character that takes less columns 144 | than the number of unicode bytes, you might want to specify its size 145 | manually: 146 | 147 | *g:bufmru_lightline_ellipsis_len* 148 | g:bufmru_lightline_ellipsis_len = 1 149 | 150 | You can also tell the separator length. Set this to 0 if you have no 151 | separators for example: 152 | 153 | *g:bufmru_lightline_sep_len* 154 | g:bufmru_lightline_sep_len = 1 155 | 156 | 157 | -------------------------------------------------------------------- 158 | vim:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl:tw=68 159 | -------------------------------------------------------------------------------- /plugin/bufmru.vim: -------------------------------------------------------------------------------- 1 | " Reload and Compatibility Guard {{{1 2 | " ============================================================================ 3 | " Reload protection. 4 | if (exists('g:did_bufmru') && g:did_bufmru) || &cp || version < 700 5 | finish 6 | endif 7 | let g:did_bufmru = 1 8 | 9 | " avoid line continuation issues (see ':help user_41.txt') 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | " 1}}} 13 | 14 | 15 | call bufmru#init() 16 | 17 | command! -nargs=0 BufMRU :call bufmru#show() 18 | command! -nargs=0 BufMRUNext :call bufmru#go(1) 19 | command! -nargs=0 BufMRUPrev :call bufmru#go(-1) 20 | command! -nargs=0 BufMRUCommit :call bufmru#save("BufMRUCommit") 21 | 22 | --------------------------------------------------------------------------------