"Initialization: {{{ if !exists("s:unstack_signs") let s:unstack_signs = {} endif "When the user switches tabs, check if it's due to an unstack tab being closed. "If so, remove signs from the stack trace that was in that tab. augroup unstack_sign_clear autocmd! autocmd TabEnter * call unstack#RemoveSignsFromClosedTabs() augroup end "}}} "unstack#Unstack(selection_type) called by hotkeys {{{ function! unstack#Unstack(selection_type) abort let stack = unstack#ExtractFiles(a:selection_type) if len(stack) > 0 if g:unstack_populate_quickfix call unstack#PopulateQuickfix(stack) endif if g:unstack_open_tab call unstack#OpenStackTrace(stack) endif else echohl Error echo "No stack trace found!" echohl None endif endfunction "}}} "unstack#UnstackFromText(text) call unstack with text as input {{{ function! unstack#UnstackFromText(text) abort let stack = unstack#ExtractFilesFromText(a:text) if len(stack) > 0 if g:unstack_populate_quickfix call unstack#PopulateQuickfix(stack) endif if g:unstack_open_tab call unstack#OpenStackTrace(stack) endif else echohl WarningMsg echo "No stack trace found!" echohl None endif endfunction "}}} "unstack#UnstackFromTmuxPasteBuffer() use tmux paste buffer as input for unstack {{{ function! unstack#UnstackFromTmuxPasteBuffer() if executable('tmux') && $TMUX != '' let text = system('tmux show-buffer') call unstack#UnstackFromText(l:text) else echoerr "No tmux session is running!" endif endfunction "}}} "Extraction: "unstack#ExtractFiles(selection_type) extract files and line numbers {{{ function! unstack#ExtractFiles(selection_type) if &buftype == "quickfix" let fileList = unstack#ExtractFilesFromQuickfix(a:selection_type) else let text = unstack#GetSelectedText(a:selection_type) let fileList = unstack#ExtractFilesFromText(text) endif return fileList endfunction "}}} "unstack#ExtractFilesFromQuickfix(type) extract files from selected text or normal cmd range {{{ function! unstack#ExtractFilesFromQuickfix(type) if a:type ==# "v" || a:type ==# "V" let marks = ["'<", "'>"] else let marks = ["'[", "']"] endif let start_line = line(marks[0]) - 1 "lines are 0-indexed in quickfix list let stop_line = line(marks[1]) - 1 "lines are 0-indexed in quickfix list let file_list = [] while start_line <= stop_line let qfline = getqflist()[start_line] let fname = bufname(qfline["bufnr"]) let lineno = qfline["lnum"] call add(file_list, [fname, lineno]) let start_line = start_line + 1 endwhile return file_list endfunction "}}} "unstack#GetSelectedText(selection_type) extract selected text {{{ function! unstack#GetSelectedText(selection_type) "save these values because we have to change them let sel_save = &selection let reg_save = @@ let &selection = "inclusive" "yank the text if a:selection_type ==# 'V' execute "normal! `y" elseif a:selection_type ==# 'v' execute "normal! `y" elseif a:selection_type ==# 'char' execute "normal! `[v`]y" elseif a:selection_type ==# 'line' execute "normal! `[V`]y" else "unknown selection type; reset vars and return "" let &selection = sel_save let @@ = reg_save return "" endif "get the text we just yanked let selected_text = @@ "reset vars let &selection = sel_save let @@ = reg_save "return the text return selected_text endfunction "}}} "unstack#ExtractFilesFromText(stacktrace) extract files and lines from a stacktrace {{{ "return [[file1, line1], [file2, line2] ... ] from a stacktrace "tries each extractor in order and stops when an extractor returns a non-empty "stack function! unstack#ExtractFilesFromText(text) for extractor in g:unstack_extractors let stack = extractor.extract(a:text) if(!empty(stack)) return stack endif endfor return [] endfunction "}}} "Opening: "unstack#PopulateQuickfix(stack) set quickfix list to extracted files{{{ function! unstack#PopulateQuickfix(stack) let qflist = [] for [filepath, lineno] in a:stack call add(qflist, {"filename": filepath, "lnum": lineno}) endfor call setqflist(qflist) endfunction "}}} "unstack#OpenStackTrace(files) open extracted files in new tab {{{ "files: [[file1, line1], [file2, line2] ... ] from a stacktrace function! unstack#OpenStackTrace(files) "disable redraw when opening files "still redraws when a split occurs but might *slightly* improve performance let lazyredrawSet = &lazyredraw set lazyredraw tabnew if (g:unstack_showsigns) sign define errline text=>> linehl=Error texthl=Error "sign ID's should be unique. If you open a stack trace with 5 levels, "you'd have to wait 5 seconds before opening another or risk signs "colliding. let signId = localtime() let t:unstack_tabId = signId let s:unstack_signs[t:unstack_tabId] = [] endif if g:unstack_scrolloff let old_scrolloff = &scrolloff let &scrolloff = g:unstack_scrolloff endif for [filepath, lineno] in a:files if filereadable(filepath) || (match(filepath, "://") > -1) execute "edit" filepath call unstack#MoveToLine(lineno) if (g:unstack_showsigns) execute "sign place" signId "line=".lineno "name=errline" "buffer=".bufnr('%') "store the signs so they can be removed later call add(s:unstack_signs[t:unstack_tabId], signId) let signId += 1 endif call unstack#SplitWindow() endif endfor "after adding the last file, the loop splits again. "delete this last empty vertical split quit if (!lazyredrawSet) set nolazyredraw endif if g:unstack_scrolloff let &scrolloff = old_scrolloff endif endfunction "}}} "unstack#GetOpenTabIds() get unstack id's for current tabs {{{ function! unstack#GetOpenTabIds() let curTab = tabpagenr() "determine currently open tabs let open_tab_ids = [] tabdo if exists('t:unstack_tabId') | call add(open_tab_ids, string(t:unstack_tabId)) | endif "jump back to prev. tab execute "tabnext" curTab return open_tab_ids endfunction "}}} "unstack#RemoveSigns(tabId) remove signs from the files initially opened in a tab {{{ function! unstack#RemoveSigns(tabId) for sign_id in s:unstack_signs[a:tabId] execute "sign unplace" sign_id endfor unlet s:unstack_signs[a:tabId] endfunction "}}} "unstack#RemoveSignsFromClosedTabs() remove signs that were placed in tabs that are {{{ "now closed function! unstack#RemoveSignsFromClosedTabs() let openTabIds = unstack#GetOpenTabIds() "for each tab with signs for tabId in keys(s:unstack_signs) "if this tab no longer exists, remove the signs if index(openTabIds, tabId) == -1 call unstack#RemoveSigns(tabId) endif endfor endfunction "}}} "unstack#GetLayout() returns layout setting ("portrait"/"landscape") {{{ function! unstack#GetLayout() let layout = get(g:, "unstack_layout", "landscape") if layout == "landscape" || layout == "portrait" return layout else throw "g:unstack_layout must be portrait or landscape" endif endfunction "}}} "unstack#MoveToLine move cursor to the line and put it in the right part of the screen {{{ let s:movement_cmd = {} let s:movement_cmd["top"] = "z+" let s:movement_cmd["middle"] = "z." let s:movement_cmd["bottom"] = "z-" function! unstack#MoveToLine(lineno) execute "normal!" a:lineno . s:movement_cmd[g:unstack_vertical_alignment] endfunction "}}} "unstack#SplitWindow() split window horizontally/vertically based on layout{{{ function! unstack#SplitWindow() let layout = unstack#GetLayout() if layout == "landscape" let split_cmd = "vnew" else let split_cmd = "new" endif execute "botright" split_cmd endfunction "}}} " vim: et sw=2 sts=2 foldmethod=marker foldmarker={{{,}}}