├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .stylua.toml ├── CHANGELOG.md ├── README.md ├── autoload └── prosession.vim ├── doc └── prosession.txt ├── lua └── telescope │ └── _extensions │ └── prosession.lua ├── plugin └── prosession.vim ├── rplugin └── python3 │ └── denite │ ├── kind │ └── session.py │ └── source │ └── prosession.py └── t └── autoload └── prosession_test.vim /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | concurrency: 12 | group: ${{ github.workflow }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | tests: 17 | name: Vim Table Mode Tests 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, macos-latest] 21 | runs-on: ${{ matrix.os }} 22 | steps: 23 | - uses: actions/checkout@v3 24 | with: 25 | path: pack/plugins/start/vim-prosession 26 | 27 | - name: Checkout vim-obsession 28 | uses: actions/checkout@v3 29 | with: 30 | repository: tpope/vim-obsession 31 | path: pack/plugins/start/vim-obsession 32 | 33 | - name: Checkout vim-testify 34 | uses: actions/checkout@v3 35 | with: 36 | repository: dhruvasagar/vim-testify 37 | path: pack/plugins/start/vim-testify 38 | 39 | - name: Install Vim or neovim 40 | uses: rhysd/action-setup-vim@v1 41 | id: vim 42 | with: 43 | neovim: true 44 | version: nightly 45 | 46 | - name: Extract Git Branch Name 47 | shell: bash 48 | run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT 49 | id: extract_branch 50 | 51 | - name: Run unit tests 52 | env: 53 | VIM: ${{ steps.vim.outputs.executable }} 54 | GIT_BRANCH: ${{ steps.extract_branch.outputs.branch }} 55 | run: | 56 | cd ${{ github.workspace }}/pack/plugins/start/vim-prosession 57 | echo "set packpath+=${{ github.workspace }}" > vimrc 58 | ${VIM} --headless -u vimrc +TestifySuite +qall 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" 2 | indent_width = 2 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 0.7.5 4 | 5 | - Fix issue with paths ending in `vim` correctly 6 | 7 | ## 0.7.3 8 | 9 | - Add telescope picker for integration with telescope 10 | 11 | ## 0.6.3 12 | 13 | Switch to `delete()` instead of `system('rm '.sname)` 14 | 15 | ## 0.6.2 16 | 17 | - Add `g:Prosession_ignore_expr` to allow more control over directories to be 18 | ignored by prosession 19 | 20 | ## 0.6.1 21 | 22 | - Switch `g:prosession_ignore_dirs` to be an array of paths instead of 23 | a string delimited by a comma 24 | 25 | ## 0.6.0 26 | 27 | - Add `g:prosession_ignore_dirs` to configure a list of comma separated 28 | directories to be ignored by prosession 29 | 30 | ## 0.5.6 31 | 32 | - Add `g:prosession_last_session_dir` to load last session from that path when 33 | launching vim 34 | 35 | ## 0.5.5 36 | 37 | - Added check for `g:prosession_dir` to create it if not already present. 38 | - Fix default session loading flow, instead of doing a recursive call 39 | just source the default session file, rest will work 40 | 41 | ## 0.5.4 42 | 43 | - Added `g:prosession_branch_cmd` to allow working with other vcs, but set to 44 | use git by default. 45 | - Added a check to ensure `g:prosession_branch_cmd` ignores errors, if any. 46 | 47 | ## 0.5.3 48 | 49 | - Moved optional git functions to an autoload file. 50 | 51 | ## 0.5.2 52 | 53 | - Added support for per git branch sessions 54 | 55 | ## Version 0.5.1 56 | 57 | - Added function for stripping trailing slash for directory names 58 | 59 | ## Version 0.5.0 60 | 61 | - Switched to undofile style naming for the session file 62 | 63 | ## Version 0.4.1 64 | 65 | - Disabled `g:prosession_default_session` by default 66 | 67 | ## Version 0.4.0: 68 | 69 | - Added `g:prosession_default_session` option to allow using of a default 70 | session instead of always creating a new one for a new directory. 71 | 72 | ## Version 0.3.0 73 | 74 | - Merged pull request #2. Handling case if using vim with stdin. 75 | 76 | ## Version 0.2.2 77 | 78 | - Updated augroup definition for loading session during vim startup 79 | 80 | ## Version 0.2.1 81 | 82 | - Fixed guard condition for existing vim session while switching to another 83 | 84 | ## Version 0.2 85 | 86 | - Switchined to using VimEnter autocmd for loading autocmd to do it more 87 | gracefully, otherwise there were side effects like skipping of -c commands. 88 | - Changed variable name `g:prosession_load_on_startup` to 89 | `g:prosession_on_startup`. 90 | - Added a guard condition within s:Prosession to ensure we stop a session only 91 | if it's already started to avoid side effects. 92 | 93 | ## Version 0.1.3 94 | 95 | - Updated `g:prosession_dir` default to `~/.vim/session/` in favor of 96 | common vim convention. 97 | 98 | ## Version 0.1.2 99 | 100 | - Fixed #1 101 | 102 | ## Version 0.1.1 103 | 104 | - Added `g:prosession_load_on_startup` to configure whether 105 | to load session on startup if vim is launch without any 106 | arguments. 107 | 108 | ## Version 0.1 109 | 110 | - First working version 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VIM ProSession v0.7.5 [![Build](https://github.com/dhruvasagar/vim-prosession/actions/workflows/ci.yml/badge.svg)](https://github.com/dhruvasagar/vim-prosession/actions/workflows/ci.yml) 2 | 3 | A VIM plugin to handle sessions like a pro. 4 | 5 | It leverages vim-obsession and allows switching between multiple sessions 6 | cleanly, the idea is to maintain one session per project (directory) and 7 | switch between them when we need to switch context, automatically loading 8 | along with it the various files, settings etc ensuring compete isolation 9 | between projects. Now there's little need to launch multiple vim instances for 10 | separate sessions (projects), you can simply switch between them with ease. 11 | 12 | ProSession uses a file name format similar to the `undofile` name format. 13 | `:Prosession` provides existing session paths from the sessions directory from 14 | `g:prosession_dir` (set to `~/.vim/session/` by default) or also completes 15 | paths from the file system which you can use to start new sessions for them. 16 | For more details check `:help prosession`. 17 | 18 | Prosession also provides a telescope extension, to list and switch to other 19 | sessions with Telescope, you can use `Telescope prosession` and that launches 20 | telescope picker with list of all existing sessions that you can find through 21 | and select to switch to it 22 | 23 | ## Change Log 24 | 25 | See [CHANGELOG.md](https://github.com/dhruvasagar/vim-prosession/blob/master/CHANGELOG.md) 26 | 27 | ## Requirements 28 | 29 | Vim ProSession depends on 30 | [tpope/vim-obsession](https://github.com/tpope/vim-obsession) 31 | 32 | ## Installation 33 | 34 | 1. With [NeoBundle](https://github.com/Shougo/neobundle.vim): 35 | 36 | ```vim 37 | NeoBundle 'dhruvasagar/vim-prosession', {'depends': 'tpope/vim-obsession'} 38 | ``` 39 | 40 | 2. With [Vundle](https://github.com/gmarik/Vundle.vim) 41 | 42 | ```vim 43 | Plugin 'tpope/vim-obsession' 44 | Plugin 'dhruvasagar/vim-prosession' 45 | ``` 46 | 47 | 3. With [Pathogen](https://github.com/tpope/vim-pathogen) 48 | 49 | ``` 50 | cd ~/.vim/bundle 51 | git clone git://github.com/tpope/vim-obsession.git 52 | git clone git://github.com/dhruvasagar/vim-prosession.git 53 | ``` 54 | 55 | 4. With [Lazy](https://github.com/folke/lazy.nvim) 56 | 57 | ```lua 58 | return { 59 | "dhruvasagar/vim-prosession", 60 | dependencies = { 61 | "tpope/vim-obsession", 62 | }, 63 | } 64 | ``` 65 | 66 | ### Telescope Picker 67 | 68 | `vim-prosession` now ships with a telescope picker that allows you to switch 69 | to existing sessions using telescope. Use the command : `Telescope prosession` 70 | to launch the picker. 71 | 72 | #### Load Telescope Extension 73 | 74 | ```lua 75 | require('telescope').load_extension('prosession') 76 | ``` 77 | 78 | #### Custom Mapping for Telescope Prosession Picker 79 | 80 | ```lua 81 | vim.keymap.set('n', 'fp', 'Telescope prosession') 82 | ``` 83 | -------------------------------------------------------------------------------- /autoload/prosession.vim: -------------------------------------------------------------------------------- 1 | function! prosession#ExecInDir(dir, cmd) "{{{1 2 | let iswin = has('win64') || has('win32') 3 | let pipe = iswin ? ' | ' : '; ' 4 | if iswin 5 | let ispowershell = &shell == 'powershell.exe' || &shell == 'powershell' 6 | let pipe = ispowershell ? ' ; ' : ' | ' 7 | endif 8 | return system('cd ' . fnameescape(a:dir) . pipe . a:cmd) 9 | endfunction 10 | 11 | function! prosession#GetCurrBranch(dir) "{{{1 12 | let branch = prosession#ExecInDir(a:dir, g:prosession_branch_cmd) 13 | if branch =~# "\n$" | let branch = branch[:-2] | endif 14 | let branch = substitute(branch, '/', '%', 'g') 15 | return branch 16 | endfunction 17 | 18 | function! prosession#ListSessions(...) "{{{1 19 | let ArgLead = a:0 >= 1 ? a:1 : '' 20 | let fldr = fnamemodify(expand(ArgLead), ':h') 21 | if !empty(ArgLead) && fldr != '.' && isdirectory(fldr) 22 | let flist = glob(ArgLead . '*', 0, 1) 23 | else 24 | let flead = empty(ArgLead) ? '' : '*' . ArgLead 25 | let flist = glob(fnamemodify(g:prosession_dir, ':p') . flead . '*.vim', 0, 1) 26 | let flist = map(flist, "fnamemodify(v:val, ':t:r')") 27 | endif 28 | let flist = map(flist, "substitute(v:val, '%', '/', 'g')") 29 | return flist 30 | endfunction 31 | 32 | function! prosession#Clean() "{{{1 33 | let files = glob(fnamemodify(g:prosession_dir, ':p') . '*.vim', 0, 1) 34 | for file in files 35 | let dir = substitute(fnamemodify(file, ':t:r'), '%', '/', 'g') 36 | if !isdirectory(dir) | call delete(file) | endif 37 | endfor 38 | endfunction 39 | 40 | function! prosession#ProsessionComplete(ArgLead, Cmdline, Cursor) "{{{1 41 | return prosession#ListSessions(a:ArgLead) 42 | endfunction 43 | -------------------------------------------------------------------------------- /doc/prosession.txt: -------------------------------------------------------------------------------- 1 | *prosession* VIM ProSession for easy session switching 2 | ------------------------------------------------------------------------------ 3 | VIM ProSession 4 | 5 | Handle sessions like a PRO. Version 0.7.5 6 | 7 | Repo: https://github.com/dhruvasagar/vim-prosession 8 | Author: Dhruva Sagar 9 | License: Same terms as Vim itself (see |license|) 10 | 11 | ------------------------------------------------------------------------------ 12 | REQUIREMENTS *prosession-requirements* 13 | 14 | VIM ProSession depends on tpope/vim-obsession, kindly ensure you have that 15 | installed before installing vim-prosession. 16 | 17 | ------------------------------------------------------------------------------ 18 | OPTIONS *prosession-options* 19 | 20 | *prosession-loaded* 21 | g:loaded_prosession Use this option to disable the plugin. Default: > 22 | let g:loaded_prosession = 1 23 | < 24 | *prosession-dir* 25 | g:prosession_dir Use this option to configure the path to store all 26 | session files. Default: > 27 | let g:prosession_dir = '~/.vim/session/' 28 | < 29 | *prosession-tmux-title* 30 | g:prosession_tmux_title 31 | Use this option to enable tmux window name updates as 32 | per current session. Default: > 33 | let g:prosession_tmux_title = 0 34 | < 35 | *prosession-tmux-title-format* 36 | g:prosession_tmux_title_format 37 | Use this option to format the tmux window name updates. 38 | Use "@@@" as placeholder for folder name. Default: > 39 | let g:prosession_tmux_title_format = "vim - @@@" 40 | < 41 | *prosession-on-startup* 42 | g:prosession_on_startup 43 | Use this option to enable auto loading of session on 44 | startup if no arguments are supplied to vim. Default: > 45 | let g:prosession_on_startup = 1 46 | < 47 | *g:prosession-default-session* 48 | g:prosession_default_session 49 | This option creates & uses a 'default' session to be 50 | used in case when launching vim and a corresponding 51 | session hasn't been found yet. > 52 | let g:prosession_default_session = 0 53 | < 54 | *g:prosession_per_branch* 55 | g:prosession_per_branch 56 | This is disabled by default. You can use this to enable 57 | per git branch sessions. > 58 | let g:prosession_per_branch = 0 59 | < 60 | 61 | If this is enabled, and you have `tpope/vim-fugitive` 62 | installed, prosession will also automatically switch to 63 | the git branch's session when you checkout a branch 64 | using the `:Git checkout` or `:Git switch` command. 65 | 66 | *g:prosession_branch_cmd* 67 | g:prosession_branch_cmd 68 | This defines the shell command to use to determine the 69 | current branch. By default this is set to work with git 70 | but can be changed for other vcs as well. > 71 | let g:prosession_branch_cmd = 'git rev-parse --abbrev-ref HEAD 2>/dev/null 72 | < 73 | *g:prosession_last_session_dir* 74 | g:prosession_last_session_dir 75 | This defines the directory where when vim launches 76 | instead of creating / starting the session for that 77 | directory we start the last used session. 78 | 79 | *g:prosession_ignore_dirs* 80 | g:prosession_ignore_dirs 81 | This is a comma (`,`) separated list of dirs we want 82 | prosession to ignore and not create / load a session for 83 | them. By default it's set to be empty. > 84 | let g:prosession_ignore_dirs = [ 85 | \ '/path1', 86 | \ '/path2', 87 | \] 88 | < 89 | *g:Prosession_ignore_expr* 90 | g:Prosession_ignore_expr 91 | This is a |Funcref| that must return a boolean value, 92 | either |v:true| or |v:false|, upon invocation. > 93 | let g:Prosession_ignore_expr = {->v:false} 94 | < 95 | This could be used to ignore directories by using some 96 | specific criteria. Following is an example to ignore all 97 | directories that are not git repos. > 98 | let g:Prosession_ignore_expr = {-> !isdirectory('.git')} 99 | < 100 | 101 | *g:prosession_viminfo_per_session* 102 | g:prosession_viminfo_per_session 103 | This is disabled by default. You can use this to enable 104 | a per session viminfo file. > 105 | let g:prosession_viminfo_per_session = v:true 106 | < 107 | 108 | ------------------------------------------------------------------------------ 109 | USAGE *:Prosession* 110 | 111 | :Prosession [{dir}] Switch to the session of the directory if one already 112 | exists otherwise start a new session, autocompletes 113 | paths. If no {dir} is specified, uses the current 114 | directory. 115 | 116 | *:ProsessionDelete* 117 | :ProsessionDelete [{dir}] 118 | Deletes the session attached to the specified {dir}. 119 | If no {dir} is specified, deletes the currently 120 | active session. 121 | 122 | *:ProsessionClean* 123 | :ProsessionClean 124 | Cleans up the session files deleting any files the 125 | directories of which no longer exist 126 | 127 | ------------------------------------------------------------------------------ 128 | FUNCTIONS *prosession-functions* 129 | 130 | 131 | *prosession#ListSessions()* 132 | prosession#ListSessions([{filter}]) 133 | Gets a list of the available sessions optionally 134 | filtered by {filter}. 135 | 136 | ------------------------------------------------------------------------------ 137 | DENITE SUPPORT *prosession-denite* 138 | 139 | 140 | Prosession includes support for Denite https://github.com/Shougo/denite.nvim 141 | if it's installed in your system, you should see a new prosession source which 142 | lists your available sessions. After selecting a session you can either switch 143 | to it (default) or delete it in order to tidy up your environment. 144 | 145 | You can control the formatting of the denite list using the format variable. 146 | Available options are: 147 | 148 | default 149 | Displays the full path of your session, highlighting the 150 | last directory. 151 | split 152 | Displays the last directory to the left and the base path to 153 | the right of the denite window. 154 | minimal 155 | Displays only the directory name of your session. 156 | 157 | Usage example: 158 | call denite#custom#var('prosession', 'format', 'split') 159 | 160 | ------------------------------------------------------------------------------ 161 | CONTRIBUTING *prosession-contributing* 162 | 163 | Contributions are welcome. Fork the repo and send a pull request or open an 164 | issue if you discover one at 165 | https://github.com/dhruvasagar/vim-prosession/issues. 166 | 167 | vim:tw=78:sw=2:ft=help:norl:ai:et 168 | -------------------------------------------------------------------------------- /lua/telescope/_extensions/prosession.lua: -------------------------------------------------------------------------------- 1 | local pickers = require("telescope.pickers") 2 | local finders = require("telescope.finders") 3 | local sorters = require("telescope.sorters") 4 | local actions = require("telescope.actions") 5 | local action_state = require("telescope.actions.state") 6 | 7 | return require("telescope").register_extension({ 8 | exports = { 9 | prosession = function(opts) 10 | opts = opts or {} 11 | 12 | pickers 13 | .new(opts, { 14 | prompt_title = "Prosession Sessions", 15 | finder = finders.new_dynamic({ 16 | entry_maker = function(line) 17 | local value = vim.fn.substitute(vim.fn.fnamemodify(line, ":t:r"), "%", "/", "g") 18 | return { 19 | value = value, 20 | display = value, 21 | ordinal = value, 22 | } 23 | end, 24 | fn = function(prompt) 25 | local flead = "" 26 | if prompt ~= "" then 27 | flead = "*" .. prompt 28 | end 29 | return vim.fn.glob(vim.fn.fnamemodify(vim.g.prosession_dir, ":p") .. flead .. "*.vim", 0, 1) 30 | end, 31 | }), 32 | sorter = sorters.get_generic_fuzzy_sorter(), 33 | attach_mappings = function(prompt_bufnr, _map) 34 | actions.select_default:replace(function() 35 | actions.close(prompt_bufnr) 36 | local selection = action_state.get_selected_entry() 37 | vim.api.nvim_command(":silent! Prosession " .. selection.value) 38 | end) 39 | return true 40 | end, 41 | }) 42 | :find() 43 | end, 44 | }, 45 | }) 46 | -------------------------------------------------------------------------------- /plugin/prosession.vim: -------------------------------------------------------------------------------- 1 | " Guards {{{1 2 | if exists('g:loaded_prosession') 3 | finish 4 | endif 5 | let g:loaded_prosession = 1 6 | 7 | let s:read_from_stdin = 0 8 | 9 | if !exists(':Obsession') 10 | echom 'vim-prosession depends on tpope/vim-obsession, please install/load that first.' 11 | finish 12 | endif 13 | 14 | " Set Global Defaults {{{1 15 | function! s:SetGlobalOptDefault(opt, val) 16 | if !exists('g:' . a:opt) | let g:{a:opt} = a:val | endif 17 | endfunction 18 | 19 | let s:default_branch_cmd = 'git rev-parse --abbrev-ref HEAD 2>/dev/null' 20 | 21 | if has('win64') || has('win32') 22 | let s:default_branch_cmd = 'git rev-parse --abbrev-ref HEAD 2>nul' 23 | endif 24 | 25 | call s:SetGlobalOptDefault('prosession_dir', expand('~/.vim/session/')) 26 | call s:SetGlobalOptDefault('prosession_tmux_title', 0) 27 | call s:SetGlobalOptDefault('prosession_on_startup', 1) 28 | call s:SetGlobalOptDefault('prosession_default_session', 0) 29 | call s:SetGlobalOptDefault('prosession_per_branch', 0) 30 | call s:SetGlobalOptDefault('prosession_branch_cmd', s:default_branch_cmd) 31 | call s:SetGlobalOptDefault('prosession_tmux_title_format', 'vim - @@@') 32 | call s:SetGlobalOptDefault('prosession_last_session_dir', '') 33 | call s:SetGlobalOptDefault('prosession_ignore_dirs', []) 34 | call s:SetGlobalOptDefault('Prosession_ignore_expr', {->v:false}) 35 | call s:SetGlobalOptDefault('prosession_viminfo_per_session', v:false) 36 | 37 | let s:save_last_on_leave = g:prosession_on_startup 38 | 39 | if !isdirectory(fnamemodify(g:prosession_dir, ':p')) 40 | call mkdir(fnamemodify(g:prosession_dir, ':p'), 'p') 41 | endif 42 | 43 | function! s:undofile(cwd) "{{{1 44 | if exists('+shellslash') && !&shellslash 45 | return substitute(a:cwd, '\', '%', 'g') 46 | else 47 | return substitute(a:cwd, '/', '%', 'g') 48 | endif 49 | endfunction 50 | 51 | function! s:StripTrailingSlash(name) "{{{1 52 | return a:name =~# '[\/]$' ? a:name[:-2] : a:name 53 | endfunction 54 | 55 | function! s:GetCWD() 56 | return exists('*ProjectRootGuess') ? ProjectRootGuess() : getcwd() 57 | endfunction 58 | 59 | function! s:IsLastSessionDir() 60 | return s:GetCWD() ==# expand(g:prosession_last_session_dir) 61 | endfunction 62 | 63 | function! s:throw(string) abort 64 | let v:errmsg = a:string 65 | throw 'prosession: '.v:errmsg 66 | endfunction 67 | 68 | function! s:error(str) abort 69 | echohl ErrorMsg 70 | echomsg a:str 71 | echohl None 72 | endfunction 73 | 74 | function! s:GetDirName(...) "{{{1 75 | let dir = a:0 && a:1 !=# '.' ? a:1 : s:GetCWD() 76 | let dir = s:StripTrailingSlash(fnamemodify(dir, ':p')) 77 | if !isdirectory(dir) 78 | call s:throw('Directory ' . dir . ' does not exist') 79 | endif 80 | if g:prosession_per_branch 81 | let dir .= '_' . prosession#GetCurrBranch(dir) 82 | endif 83 | return s:undofile(dir) 84 | endfunction 85 | 86 | function! s:GetSessionFileName(...) "{{{1 87 | let fname = a:0 && a:1 =~# '\.vim$' && !isdirectory(a:1) ? a:1 : call('s:GetDirName', a:000) 88 | let fname = s:StripTrailingSlash(fname) 89 | return fname =~# '\.vim$' ? fnamemodify(fname, ':t:r') : fnamemodify(fname, ':t') 90 | endfunction 91 | 92 | function! s:GetSessionFile(...) "{{{1 93 | let sname = call('s:GetSessionFileName', a:000) . '.vim' 94 | return fnamemodify(g:prosession_dir, ':p') . sname 95 | endfunction 96 | 97 | function! s:GetVimInfoFile(...) "{{{1 98 | let sname = call('s:GetSessionFileName', a:000) . '.viminfo' 99 | return fnamemodify(g:prosession_dir, ':p') . sname 100 | endfunction 101 | 102 | function! s:SetTmuxWindowName(name) "{{{1 103 | if g:prosession_tmux_title 104 | let sfname = s:GetSessionFileName(a:name) 105 | let sfname = sfname[strridx(sfname,'%')+1:] 106 | let title = substitute(g:prosession_tmux_title_format, '@@@', sfname, 'g') 107 | let title = substitute(title, '"', '\\"', 'g') 108 | call system('tmux rename-window -t ' . $TMUX_PANE . ' "' . title . '"') 109 | augroup ProsessionTmux 110 | autocmd! 111 | 112 | autocmd VimLeavePre * call system('tmux set-window-option -t ' . $TMUX_PANE . ' automatic-rename on') 113 | augroup END 114 | endif 115 | endfunction 116 | 117 | function! s:ProsessionDelete(...) "{{{1 118 | let name = a:0 >= 1 ? a:1 : '' 119 | 120 | if empty(name) 121 | let sname = g:prosession_last_session_file 122 | else 123 | try 124 | let sname = s:GetSessionFile(expand(name)) 125 | catch /^prosession/ 126 | call s:error(v:errmsg) 127 | return 128 | endtry 129 | endif 130 | 131 | if exists('#User#ProsessionDeletePre') 132 | execute 'doautocmd '.(v:version >= 704 || (v:version == 703 && has('patch442')) ? ' ' : '').'User ProsessionDeletePre' 133 | endif 134 | if g:prosession_last_session_file == sname && g:this_obsession == sname 135 | call s:error('Deleting active session') 136 | " Prevent saving the last session 137 | let s:save_last_on_leave = 0 138 | execute 'Obsession!' 139 | endif 140 | 141 | call delete(sname) 142 | 143 | if exists('#User#ProsessionDeletePost') 144 | execute 'doautocmd '.(v:version >= 704 || (v:version == 703 && has('patch442')) ? ' ' : '').'User ProsessionDeletePost' 145 | endif 146 | endfunction 147 | 148 | function! s:ProsessionIgnoreCWD() "{{{1 149 | if empty(g:prosession_ignore_dirs) | return v:false | end 150 | let cdir = s:GetCWD() 151 | for idir in g:prosession_ignore_dirs 152 | let idir = expand(idir) 153 | if isdirectory(idir) && cdir == idir 154 | return v:true 155 | endif 156 | endfor 157 | return v:false 158 | endfunction 159 | 160 | function! s:Prosession(...) "{{{1 161 | if s:read_from_stdin 162 | \ || s:ProsessionIgnoreCWD() == v:true 163 | \ || g:Prosession_ignore_expr() == v:true 164 | return 165 | endif 166 | let aname = a:0 && !empty(a:1) ? a:1 : s:GetCWD() 167 | try 168 | let sname = s:GetSessionFile(expand(aname)) 169 | catch /^prosession/ 170 | call s:error(v:errmsg) 171 | return 172 | endtry 173 | if exists('#User#ProsessionPre') 174 | execute 'doautocmd '.(v:version >= 704 || (v:version == 703 && has('patch442')) ? ' ' : '').'User ProsessionPre' 175 | endif 176 | if !empty(get(g:, 'this_obsession', '')) 177 | silent Obsession " Stop current session 178 | " Remove all current buffers. 179 | silent! %bwipe! 180 | endif 181 | if filereadable(sname) 182 | silent execute 'source' fnameescape(sname) 183 | elseif isdirectory(expand(aname)) 184 | execute 'cd' expand(aname) 185 | else 186 | if g:prosession_default_session 187 | let sname = s:GetSessionFile('default') 188 | silent execute 'source' fnameescape(sname) 189 | else 190 | let sname = s:GetSessionFile() 191 | endif 192 | endif 193 | call s:SetTmuxWindowName(aname) 194 | if !s:IsLastSessionDir() 195 | let g:prosession_last_session_file = sname 196 | endif 197 | silent execute 'Obsession' fnameescape(sname) 198 | if g:prosession_viminfo_per_session 199 | exec 'set viminfofile=' . s:GetVimInfoFile() 200 | endif 201 | " Restore last session saving 202 | let s:save_last_on_leave = g:prosession_on_startup 203 | if exists('#User#ProsessionPost') 204 | execute 'doautocmd '.(v:version >= 704 || (v:version == 703 && has('patch442')) ? ' ' : '').'User ProsessionPost' 205 | endif 206 | endfunction 207 | 208 | function! s:GetLastSessionFile() 209 | try 210 | return g:prosession_dir . trim(readfile(s:LastSession())[0]) 211 | catch 212 | return "" 213 | endtry 214 | endfunction 215 | 216 | function! s:LastSession() 217 | return expand(g:prosession_dir . "last_session.txt") 218 | endfunction 219 | 220 | function! s:save_last_session() 221 | if s:save_last_on_leave && exists("g:this_obsession") 222 | call writefile([fnamemodify(g:this_obsession, ":t")], s:LastSession()) 223 | endif 224 | endfunction 225 | 226 | " Start / Load session {{{1 227 | if !argc() && index(get(v:, 'argv', []), '-q') == -1 && g:prosession_on_startup 228 | augroup Prosession 229 | au! 230 | 231 | autocmd StdInReadPost * nested let s:read_from_stdin=1 232 | autocmd VimEnter * nested call s:AutoStart() 233 | autocmd VimLeave * call s:save_last_session() 234 | augroup END 235 | endif 236 | 237 | function! s:AutoStart() 238 | let sname = "" 239 | if s:IsLastSessionDir() 240 | let sname = s:GetLastSessionFile() 241 | endif 242 | if empty(sname) 243 | let sname = s:GetSessionFile() 244 | endif 245 | call s:Prosession(sname) 246 | endfunction 247 | 248 | function! s:AutoSwitch() 249 | if g:prosession_per_branch && exists('*FugitiveResult') 250 | let fresult = FugitiveResult() 251 | if !empty(fresult) 252 | \ && has_key(fresult, 'args') 253 | \ && !empty(fresult['args']) 254 | \ && (fresult['args'][0] ==? 'checkout' || fresult['args'][0] ==? 'switch') 255 | \ && len(fresult['args']) > 1 256 | call s:AutoStart() 257 | endif 258 | endif 259 | endfunction 260 | 261 | augroup ProsessionGit 262 | au! 263 | 264 | autocmd User FugitiveChanged call s:AutoSwitch() 265 | augroup END 266 | 267 | " Command Prosession {{{1 268 | command! -bar -nargs=? -complete=customlist,prosession#ProsessionComplete Prosession call s:Prosession() 269 | " 270 | " Command Prosession Delete{{{1 271 | command! -bar -nargs=? -complete=customlist,prosession#ProsessionComplete ProsessionDelete call s:ProsessionDelete() 272 | 273 | command! ProsessionClean call prosession#Clean() 274 | -------------------------------------------------------------------------------- /rplugin/python3/denite/kind/session.py: -------------------------------------------------------------------------------- 1 | from .base import Base 2 | from denite import util 3 | 4 | 5 | class Kind(Base): 6 | def __init__(self, vim): 7 | super().__init__(vim) 8 | 9 | self.name = 'session' 10 | self.default_action = 'switch' 11 | 12 | def exec_command(self, command, context): 13 | target = context['targets'][0] 14 | target = target['session'] 15 | command = command.format(target=target) 16 | output = self.vim.call( 17 | 'denite#util#execute_command', command, False 18 | ) 19 | 20 | output and self.debug(output) 21 | 22 | def action_switch(self, context): 23 | self.exec_command('Prosession {target}', context) 24 | 25 | def action_delete(self, context): 26 | self.exec_command('ProsessionDelete {target}', context) 27 | -------------------------------------------------------------------------------- /rplugin/python3/denite/source/prosession.py: -------------------------------------------------------------------------------- 1 | import re 2 | from .base import Base 3 | 4 | 5 | def format_split(match, ctx): 6 | """ Splits the match so the last part of the path is 7 | shown at the left part of the screen. 8 | """ 9 | width = int(ctx.get('winwidth')) 10 | parts = re.split(r'/(?=.)', match) 11 | last = parts[-1].replace('/', '') 12 | rest = '/'.join(parts[0:-1]) + '/' 13 | sep = ' ' * (width - len(rest+last)) 14 | 15 | return last + sep + rest 16 | 17 | 18 | def format_default(match, ctx): 19 | """ Does nothing 20 | """ 21 | return match 22 | 23 | 24 | def format_minimal(match, ctx): 25 | return re.split(r'/(?=.)', match)[-1] 26 | 27 | 28 | FORMATTERS = { 29 | 'default': format_default, 30 | 'split': format_split, 31 | 'minimal': format_minimal, 32 | } 33 | 34 | HIGHLIGHTERS = { 35 | 'default': [ 36 | {'name': 'File', 'link': 'Comment', 're': r'\/.*\/\(.\)\@='}, 37 | ], 38 | 'split': [ 39 | {'name': 'File', 'link': 'Comment', 're': r' \/.*'}, 40 | ], 41 | 'minimal': [], 42 | } 43 | 44 | 45 | class Source(Base): 46 | def __init__(self, vim): 47 | super().__init__(vim) 48 | self.name = 'prosession' 49 | self.kind = 'session' 50 | self.vars = { 51 | 'format': 'default' 52 | } 53 | 54 | def on_init(self, context): 55 | formatter = False 56 | 57 | if self.vars['format']: 58 | formatter = self.vars['format'] 59 | if formatter not in FORMATTERS: 60 | self.error_message(context, 'Invalid format ' + formatter) 61 | formatter = False 62 | 63 | formatter = formatter or 'default' 64 | context['format'] = formatter 65 | 66 | self.syntax = HIGHLIGHTERS[formatter] 67 | 68 | def gather_candidates(self, ctx): 69 | """ Populates the denite buffer with the available 70 | sessions. 71 | """ 72 | sessions = self.vim.eval("prosession#ListSessions()") 73 | formatter = FORMATTERS[ctx.get('format')] 74 | completions = [] 75 | 76 | for session in sessions: 77 | 78 | completions.append({ 79 | 'word': session, 80 | 'abbr': formatter(session, ctx), 81 | 'session': session 82 | }) 83 | 84 | return completions 85 | 86 | def highlight(self): 87 | """ Defines simple syntax for the matches in the 88 | denite window to highlight the last part of the path. 89 | """ 90 | 91 | for syn in self.syntax: 92 | self.vim.command( 93 | 'syntax match {0}_{1} /{2}/ contained containedin={0}'.format( 94 | self.syntax_name, syn['name'], syn['re'])) 95 | self.vim.command( 96 | 'highlight default link {0}_{1} {2}'.format( 97 | self.syntax_name, syn['name'], syn['link'])) 98 | -------------------------------------------------------------------------------- /t/autoload/prosession_test.vim: -------------------------------------------------------------------------------- 1 | let s:test_dir = "./tmp_test" 2 | function! s:setup() abort 3 | call mkdir(s:test_dir, 'p') 4 | endfunction 5 | 6 | function! s:teardown() abort 7 | call delete(s:test_dir, 'rf') 8 | endfunction 9 | 10 | function! s:TestProsessionExecInDir() 11 | let cmd = "echo -n true" 12 | let out = prosession#ExecInDir(s:test_dir, cmd) 13 | call testify#assert#equals(out, "true") 14 | endfunction 15 | call testify#setup(function('s:setup')) 16 | call testify#it('ProsessionExecInDir should work', function('s:TestProsessionExecInDir')) 17 | call testify#teardown(function('s:teardown')) 18 | 19 | function! s:TestGetCurrBranch() 20 | let out = prosession#GetCurrBranch(".") 21 | let branch = substitute($GIT_BRANCH,"/","%","g") 22 | call testify#assert#equals(out, branch) 23 | endfunction 24 | call testify#it('prosession#GetCurBranch should get the current git branch', function('s:TestGetCurrBranch')) 25 | --------------------------------------------------------------------------------