Skip to content

Commit

Permalink
Robustified and refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
ubaldot committed May 2, 2023
1 parent bc6dd72 commit fdfa7d2
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 62 deletions.
23 changes: 12 additions & 11 deletions doc/outline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ CONTENTS *OutlineContents*
==============================================================================
INTRODUCTION *OutlineIntroduction*

Vim-outline parse the current buffer through a set of user-defined regex or
parsing functions and slam the result in a side window. That's all!
Vim-outline parse the current buffer through a set of user-defined regex
and/or parsing functions and slam the result in a side window.
That's all!

Well, in reality it is not, in-fact Vim-outline further provides you with the
following features:

a. Locate your current position with respect to the outline,
b. Allow you to jump from the outline entries to the corresponding line in
the calling buffer,
c. Feed you with random motivational quote picked from our quotes
b. Jump from outline entries to the corresponding buffer lines,
c. Feed you with random motivational quotes picked from our quotes
database if a 'filetype' is not supported. [Cringe mode ON!]

At the time I was working with Python and Vim9script, hence only Python and
Expand Down Expand Up @@ -65,11 +65,13 @@ current buffer.
Run |:OutlineGoToOutline| to jump on the outline window.
This command comes handy when you have many windows open in the same tab
and you want to jump directly to the outline window with one key-press.
It is suggested that you run |:OutlineRefresh| before jumping on the outline
window.

Run |:OutlineRefresh| to update both the outline and the highlighting.

Note: The refresh is asynchronous. This means that you have to manually
run `:OutlineRefresh` to update outline & localization while you are editing
run |:OutlineRefresh| to update outline & localization while you are editing
your file.
However, outline & localization are automatically updated in response to a
newly opened Outline window.
Expand All @@ -78,9 +80,6 @@ I was thinking to continuously update the highlighting but that would require
calling a number of functions at every single text change: Mr laggy is just
behind the corner!

(*) This won't apply if a buffer is entered through mouse click. If you enter
a buffer through mouse click, then you have to refresh the outline manually.

You are not satisfied with the result?
No worries! Feel free to configure outline with your own regular expressions
by adjusting the dictionaries |g:outline_pattern_to_include|,
Expand Down Expand Up @@ -108,7 +107,8 @@ COMMANDS *OutlineCommands*

*:OutlineGoToOutline*
:OutlineGoToOutline Jump into the outline side-window independently
of the current buffer.
of the current buffer. You should refresh your
outline before using this command.

==============================================================================
MAPPINGS *OutlineMappings*
Expand Down Expand Up @@ -137,7 +137,8 @@ Name of the Outline buffer.
*g:outline_autoclose*
Type: 'bool'
Default: true
If true, automatically close the outline window.
If true, automatically close the outline window when you leave it.

*g:outline_win_size*
Type: 'number'
Default: 30
Expand Down
107 changes: 56 additions & 51 deletions lib/outline.vim
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ sign define CurrentItem linehl=CursorLine

# Script functions
def Locate(target_item: string)
# Highlight target_item in the outline window
# Highlight target_item (aka closest item) in the outline window
win_execute(outline_win_id, 'setlocal modifiable noreadonly')
win_execute(outline_win_id, "sign_unplace('', {'buffer':
\ g:outline_buf_name}) ")

# If target_item != "", then check if there are duplicates,
# If you have a valid target_item, then check if there are duplicates,
# and highlight the correct one.
if target_item !=# ""
# Check if the found target_item is a duplicate starting from the
# current line going backwards to line 1 in the current buffer
# Check first if the found target_item is a duplicate starting from
# the current line and going backwards to line 1 in the current
# buffer.
var curr_line_nr = line('.')
var num_duplicates = len(getline(1, '$')[0 : curr_line_nr - 1]
\ -> filter($"v:val ==# '{target_item}'"))
Expand All @@ -40,28 +41,29 @@ def Locate(target_item: string)
endfor
var line_nr = lines[num_duplicates - 1] + len(title) + 1

# Now you can highlight
# Now you know what you should highlight
setwinvar(win_id2win(outline_win_id), "line_nr", line_nr)
win_execute(outline_win_id, 'cursor(w:line_nr, 1) | norm! ^')
win_execute(outline_win_id, 'sign_place(w:line_nr, "",
\ ''CurrentItem'', g:outline_buf_name, {''lnum'':
\ w:line_nr})')
endif
# Lock window
win_execute(outline_win_id, 'setlocal nomodifiable readonly')
enddef

def FindClosestItem(): string
# Search the item at minimum distance with the cursor (from above)
# Note that the maximum distance is curr_line - 0 = curr_line
# Here the are in the caller-buffer coordinates
# Here we are in the caller-buffer coordinates
var curr_line_nr = line('.')
var curr_line = getline('.')
var dist_min = curr_line_nr
var dist = curr_line_nr
var found_item = ""

# OBS! You only find the item at minimum distance here,
# but you don't know if it is a duplicate.
# OBS! You only find the item at minimum distance,
# but you don't know if there is any duplicate of such an item.
for item in Outline
dist = curr_line_nr - search($'\V{item}', 'cnbW')
# OBS! dist is always >= 0
Expand All @@ -82,17 +84,17 @@ enddef


def GoToDefinition()
# Search item in the Outline side-window first!
# In two steps:
# 1. Search selected item in the Outline (including duplicates),
var curr_line_nr = max([1, line('.') - len(title)])
var target_item = getline('.')
# counter keeps track of the number of duplicated until this line.
var counter = len(Outline[0 : curr_line_nr - 1]
\ -> filter($"v:val ==# '{target_item}'"))

# 2. Jump back to the main buffer and search for the selected item.
# TODO: check if you can replace wincmd p with some builtin function
# OBS! This will trigger a BufEnter event! Watch out if you use some
# autocommand based on BufEnter.
wincmd p

# The number of jumps needed to reach the target item are counted from the
# beginning of the file
cursor(1, 1)
Expand All @@ -109,17 +111,19 @@ enddef

export def GoToOutline()
if IsOpen()
# You must refresh the outline window before jumping into it
# because once there, you won't be able to refresh
RefreshWindow()
win_gotoid(bufwinid($"^{g:outline_buf_name}$"))
endif
enddef


def Close()
if IsOpen()
wincmd p
win_execute(outline_win_id, 'wincmd c')
# In case there are erroneously other Outline windows open
for wind in win_findbuf(bufnr($'^{g:outline_buf_name}$'))
win_execute(wind, 'wincmd c')
endfor
endif
enddef

Expand Down Expand Up @@ -176,6 +180,7 @@ export def Toggle()
Close()
else
Open()
RefreshWindow()
GoToOutline()
endif
enddef
Expand All @@ -185,8 +190,9 @@ def UpdateOutline(): string
# This function only update the Outline script variable, even if the
# outline window is closed.
# If the current buffer is the outline itself then it does not make sense
# to update the outline. Also check the current buffer has supported
# filetype
# to update the outline.

# If supported filetype
if has_key(g:outline_include_before_exclude, &filetype)
\ && bufnr() != winbufnr(outline_win_id)
# -----------------------------------
Expand Down Expand Up @@ -239,47 +245,46 @@ enddef

export def RefreshWindow()
UpdateOutline()
# If Outline is open AND what is shown in the outline window is the
# Outline buffer.
# The last condition is very important because if it does not hold true,
# then you would overwrite a user buffer with an outline.
# The guy may be very annoyed!
if IsOpen() && winbufnr(outline_win_id) == bufnr(g:outline_buf_name)
# -----------------------------------------
# clean outline and unlock outline buffer
# -----------------------------------------
win_execute(outline_win_id, 'setlocal modifiable noreadonly')
deletebufline(winbufnr(outline_win_id), len(title) + 1, line('$',
\ outline_win_id))

# ----------------------------------------------
# Actually populate the window
# ----------------------------------------------
setbufline(winbufnr(outline_win_id), len(title) + 1, Outline)
# Set outline syntax the same as the caller buffer syntax.
win_execute(outline_win_id, 'setlocal syntax=' .. &syntax)
win_execute(outline_win_id, 'setlocal nomodifiable readonly')

# Locate
if g:outline_enable_highlight
Locate(FindClosestItem())
# To refresh a window such a window must be obviously open
if IsOpen()
# If what is shown in the outline window is the Outline buffer, then
# overwrite it. If it is shown a user-buffer DON'T overwrite it!
if winbufnr(outline_win_id) == bufnr(g:outline_buf_name)
# -----------------------------------------
# clean outline and unlock outline buffer
# -----------------------------------------
win_execute(outline_win_id, 'setlocal modifiable noreadonly')
deletebufline(winbufnr(outline_win_id), len(title) + 1, line('$',
\ outline_win_id))

# ----------------------------------------------
# Actually populate the window
# ----------------------------------------------
setbufline(winbufnr(outline_win_id), len(title) + 1, Outline)
# Set outline syntax the same as the caller buffer syntax.
win_execute(outline_win_id, 'setlocal syntax=' .. &syntax)
win_execute(outline_win_id, 'setlocal nomodifiable readonly')

# Locate
if g:outline_enable_highlight
Locate(FindClosestItem())
endif
# TODO: you may have a nasty recursion here that slow down everything.
# You may consider throwing an error message instead.
elseif winbufnr(outline_win_id) != bufnr(g:outline_buf_name)
# Close()
# Open()
# RefreshWindow()
echoerr "Previous outline content has gone!"
\ .. " Close and re-open the outline window to "
\ .. "re-create a new one."
endif
# Outline win is open but Outline buffer is not there.
elseif IsOpen() && winbufnr(outline_win_id) != bufnr(g:outline_buf_name)
Close()
Open()
GoToOutline()
endif
enddef


augroup Outline_autochange
autocmd!
# TODO: changing buffer with mouse it is tricky because it triggers two
# events: BufEnter + CursorMove
# Hence, you miss the current line when you enter the buffer.
# autocmd BufEnter * if bufwinid(bufnr()) != outline_win_id
# \| :call RefreshWindow() | endif
autocmd WinLeave * if bufwinid(bufnr()) == outline_win_id
\ && g:outline_autoclose | q | endif
augroup END

0 comments on commit fdfa7d2

Please sign in to comment.