tpope / vim-obsession

obsession.vim: continuously updated session files
http://www.vim.org/scripts/script.php?script_id=4472
1.76k stars 70 forks source link

Add support for `g:obsession_prepend` #81

Closed kimabrandt-flx closed 4 months ago

kimabrandt-flx commented 6 months ago

Would you be willing to extend vim-obsession and add a g:obsession_prepend-variable; in addition to g:obsession_append?

My use-case is: Keep global marks for a project in the session-file.

I've made a change in my copy of vim-obsession, where I'm using a funcref that's used to call-back a function in my config. The callback-function returns a list of lines - related to restoring my global marks - which are added (by vim-obsession) to the top of the session-file; so that they don't interfere with badd and other commands.

Here's the change in obsession.vim, that I'm currently using:

diff --git a/plugin/obsession.vim b/plugin/obsession.vim
index e36729d..ec7426f 100644
--- a/plugin/obsession.vim
+++ b/plugin/obsession.vim
@@ -77,6 +77,21 @@ function! s:persist() abort
       let body = readfile(tmp)
       call insert(body, 'let g:this_session = v:this_session', -3)
       call insert(body, 'let g:this_obsession = v:this_session', -3)
+      if type(get(g:, 'obsession_prepend')) == type([])
+        for l:Item in g:obsession_prepend
+          if type(l:Item) == v:t_string
+            call insert(body, l:Item)
+          elseif type(l:Item) == v:t_list
+            for l:line in reverse(l:Item)
+              call insert(body, l:line)
+            endfor
+          elseif type(l:Item) == v:t_func
+            for l:line in reverse(l:Item())
+              call insert(body, l:line)
+            endfor
+          endif
+        endfor
+      endif
       if type(get(g:, 'obsession_append')) == type([])
         for line in g:obsession_append
           call insert(body, line, -3)

I'm not aware of a better way to restore global marks, other than editing a marked file, reapplying the cursor-position and then setting the mark!?

Here's the code - from my config - that gets called, by passing a funcref via the g:obsession_prepend-variable:

function! s:get_global_mark_lines() abort
  let l:marked_files = {}
  let l:ordered_files = []
  for l:mark_item in getmarklist()
    let l:mark = l:mark_item.mark
    if l:mark !~# "^'[A-Z]$"
      " ignore non-global marks
      continue
    else
      " remove the "'"-prefix from the mark
      let l:mark = substitute(l:mark, "'", "", "")
    endif

    let l:file = l:mark_item.file
    let l:pos = l:mark_item.pos
    let l:line_number = l:pos[1]
    let l:column = l:pos[2]

    if has_key(l:marked_files, l:file)
      " use existing list of marks, from an already discovered marked file
      let l:list = l:marked_files[l:file]
    else
      " create a new list of marks, for a newly discovered marked file
      let l:list = []
      let l:marked_files[l:file] = l:list
      call add(l:list, "badd +" . l:line_number . " " . fnameescape(l:file))
      call add(l:list, "keepjumps edit " . fnameescape(l:file)) " edit file
      call add(l:ordered_files, l:file) " keep the order of the files
    endif
    call add(l:list, l:line_number) " go to line
    call add(l:list, "normal! " . l:column . "|") " go to column
    call add(l:list, "normal! m" . l:mark) " set mark
  endfor

  let l:lines = []
  call add(l:lines, "delmarks A-Z") " delete marks in the range A to Z
  for l:file in l:ordered_files
    let l:list = l:marked_files[l:file]
    call add(l:lines, l:list)
    call add(l:lines, "bdelete " . fnameescape(l:file)) " don't interfere with badd
  endfor
  return flatten(l:lines)
endfunction

let g:obsession_prepend = [funcref("s:get_global_mark_lines")]

I'm using:

NVIM v0.10.0
Build type: Release
LuaJIT 2.1.1713773202
kimabrandt-flx commented 5 months ago

For my use-case, I needed the lines to be inserted after the cd-command and before the badd-commands; inside the session-file.

diff --git a/plugin/obsession.vim b/plugin/obsession.vim
index e36729d..851f2b4 100644
--- a/plugin/obsession.vim
+++ b/plugin/obsession.vim
@@ -77,6 +77,28 @@ function! s:persist() abort
       let body = readfile(tmp)
       call insert(body, 'let g:this_session = v:this_session', -3)
       call insert(body, 'let g:this_obsession = v:this_session', -3)
+      if type(get(g:, 'obsession_prepend')) == type([])
+        let l:idx = 0
+        for l:i in range(0, len(body) - 1)
+          if match(body[l:i], "if &shortmess =\\~ 'A'") == 0
+            let l:idx = l:i + 5 " place after the `shortmess` if-else-block
+            break
+          endif
+        endfor
+        for l:Item in g:obsession_prepend
+          if type(l:Item) == v:t_string
+            call insert(body, l:Item, l:idx)
+          elseif type(l:Item) == v:t_list
+            for l:line in reverse(l:Item)
+              call insert(body, l:line, l:idx)
+            endfor
+          elseif type(l:Item) == v:t_func
+            for l:line in reverse(l:Item())
+              call insert(body, l:line, l:idx)
+            endfor
+          endif
+        endfor
+      endif
       if type(get(g:, 'obsession_append')) == type([])
         for line in g:obsession_append
           call insert(body, line, -3)
kimabrandt-flx commented 4 months ago

I figured, I could trigger my function on the Obsession-event. Thank you, for this!

function! s:save_global_marks() abort
  " ... code from https://github.com/tpope/vim-obsession/issues/81#issue-2272389561

  let l:body = readfile(g:this_obsession)
  let l:idx = 0
  for l:i in range(0, len(l:body) - 1)
    if match(l:body[l:i], "if &shortmess =\\~ 'A'") == 0
      let l:idx = l:i + 5 " place after the `shortmess` if-else-block
      break
    endif
  endfor
  for l:line in reverse(flatten(l:lines))
    call insert(l:body, l:line, l:idx)
  endfor
  call writefile(l:body, g:this_obsession)
endfunction

augroup my_obsession
  autocmd!
  autocmd User Obsession call s:save_global_marks()
augroup END
tpope commented 4 months ago

That's probably a more robust solution. g:obsession_append is designed for static inclusions. For something like global marks you'd need to compute them each time, which is kind of awkward.