Some checks failed
		
		
	
	Detach Plugins / check (FlyGrep.vim) (push) Has been cancelled
				
			Detach Plugins / check (GitHub.vim) (push) Has been cancelled
				
			Detach Plugins / check (JavaUnit.vim) (push) Has been cancelled
				
			Detach Plugins / check (SourceCounter.vim) (push) Has been cancelled
				
			Detach Plugins / check (cpicker.nvim) (push) Has been cancelled
				
			Detach Plugins / check (dein-ui.vim) (push) Has been cancelled
				
			Detach Plugins / check (git.vim) (push) Has been cancelled
				
			Detach Plugins / check (iedit.vim) (push) Has been cancelled
				
			Detach Plugins / check (scrollbar.vim) (push) Has been cancelled
				
			Detach Plugins / check (vim-chat) (push) Has been cancelled
				
			Detach Plugins / check (vim-cheat) (push) Has been cancelled
				
			Detach Plugins / check (vim-todo) (push) Has been cancelled
				
			Detach Plugins / check (xmake.vim) (push) Has been cancelled
				
			test / Linux (nvim, nightly) (push) Has been cancelled
				
			test / Linux (nvim, v0.3.8) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.2) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.3) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.4) (push) Has been cancelled
				
			test / Linux (nvim, v0.5.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.5.1) (push) Has been cancelled
				
			test / Linux (nvim, v0.6.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.6.1) (push) Has been cancelled
				
			test / Linux (nvim, v0.7.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.7.2) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.1) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.2) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.3) (push) Has been cancelled
				
			test / Linux (nvim, v0.9.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.9.1) (push) Has been cancelled
				
			test / Linux (true, vim, v7.4.052) (push) Has been cancelled
				
			test / Linux (true, vim, v7.4.1689) (push) Has been cancelled
				
			test / Linux (true, vim, v7.4.629) (push) Has been cancelled
				
			test / Linux (true, vim, v8.0.0027) (push) Has been cancelled
				
			test / Linux (true, vim, v8.0.0183) (push) Has been cancelled
				
			test / Linux (vim, nightly) (push) Has been cancelled
				
			test / Linux (vim, v8.0.0184) (push) Has been cancelled
				
			test / Linux (vim, v8.0.1453) (push) Has been cancelled
				
			test / Linux (vim, v8.1.2269) (push) Has been cancelled
				
			test / Linux (vim, v8.2.2434) (push) Has been cancelled
				
			test / Linux (vim, v8.2.3995) (push) Has been cancelled
				
			test / Windows (nvim, nightly) (push) Has been cancelled
				
			test / Windows (nvim, v0.3.8) (push) Has been cancelled
				
			test / Windows (nvim, v0.4.2) (push) Has been cancelled
				
			test / Windows (nvim, v0.4.3) (push) Has been cancelled
				
			test / Windows (nvim, v0.4.4) (push) Has been cancelled
				
			test / Windows (nvim, v0.5.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.5.1) (push) Has been cancelled
				
			test / Windows (nvim, v0.6.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.6.1) (push) Has been cancelled
				
			test / Windows (nvim, v0.7.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.7.2) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.1) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.2) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.3) (push) Has been cancelled
				
			test / Windows (nvim, v0.9.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.9.1) (push) Has been cancelled
				
			test / Windows (vim, nightly) (push) Has been cancelled
				
			test / Windows (vim, v7.4.1185) (push) Has been cancelled
				
			test / Windows (vim, v7.4.1689) (push) Has been cancelled
				
			test / Windows (vim, v8.0.0027) (push) Has been cancelled
				
			test / Windows (vim, v8.0.1453) (push) Has been cancelled
				
			test / Windows (vim, v8.1.2269) (push) Has been cancelled
				
			test / Windows (vim, v8.2.2434) (push) Has been cancelled
				
			test / Windows (vim, v8.2.3995) (push) Has been cancelled
				
			docker / docker (push) Has been cancelled
				
			mirror / check (coding) (push) Has been cancelled
				
			mirror / check (gitee) (push) Has been cancelled
				
			mirror / check (gitlab) (push) Has been cancelled
				
			
		
			
				
	
	
		
			700 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
			
		
		
	
	
			700 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 | 
						|
" File:         autoload/pythonsense.vim
 | 
						|
" Author:       Jeet Sukumaran
 | 
						|
"
 | 
						|
" Copyright:    (C) 2018 Jeet Sukumaran
 | 
						|
"
 | 
						|
" License:      Permission is hereby granted, free of charge, to any person obtaining
 | 
						|
"               a copy of this software and associated documentation files (the
 | 
						|
"               "Software"), to deal in the Software without restriction, including
 | 
						|
"               without limitation the rights to use, copy, modify, merge, publish,
 | 
						|
"               distribute, sublicense, and/or sell copies of the Software, and to
 | 
						|
"               permit persons to whom the Software is furnished to do so, subject to
 | 
						|
"               the following conditions:
 | 
						|
"
 | 
						|
"               The above copyright notice and this permission notice shall be included
 | 
						|
"               in all copies or substantial portions of the Software.
 | 
						|
"
 | 
						|
"               THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | 
						|
"               OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
						|
"               MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | 
						|
"               IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 | 
						|
"               CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 | 
						|
"               TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 | 
						|
"
 | 
						|
"               SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
						|
"
 | 
						|
" Credits:      - pythontextobj.vim by Nat Williams (https://github.com/natw/vim-pythontextobj)
 | 
						|
"               - chapa.vim by Alfredo Deza (https://github.com/alfredodeza/chapa.vim)
 | 
						|
"               - indentobj by Austin Taylor (https://github.com/austintaylor/vim-indentobject)
 | 
						|
"               - Python Docstring Text Objects by gfixler (https://pastebin.com/u/gfixler)
 | 
						|
"               - vim-indent-object by Michael Smith (http://github.com/michaeljsmith/vim-indent-object)
 | 
						|
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 | 
						|
 | 
						|
" Support Functions {{{1
 | 
						|
function! pythonsense#trawl_search(pattern, start_line, fwd)
 | 
						|
    let current_line = a:start_line
 | 
						|
    let lastline = line('$')
 | 
						|
    if a:fwd
 | 
						|
        let stepvalue = 1
 | 
						|
    else
 | 
						|
        let stepvalue = -1
 | 
						|
    endif
 | 
						|
    while (current_line > 0 && current_line <= lastline)
 | 
						|
        let line_text = getline(current_line)
 | 
						|
        let m = match(line_text, a:pattern)
 | 
						|
        if m >= 0
 | 
						|
            return current_line
 | 
						|
        endif
 | 
						|
        let current_line = current_line + stepvalue
 | 
						|
    endwhile
 | 
						|
    return 0
 | 
						|
endfunction
 | 
						|
" }}}1
 | 
						|
 | 
						|
" Python (Statement) Text Objects {{{1
 | 
						|
" Based on:
 | 
						|
"   - https://github.com/natw/vim-pythontextobj
 | 
						|
"   - https://github.com/alfredodeza/chapa.vim
 | 
						|
"   - https://github.com/austintaylor/vim-indentobject
 | 
						|
"   - http://github.com/michaeljsmith/vim-indent-object
 | 
						|
 | 
						|
"   Select an object ("class"/"function")
 | 
						|
let s:pythonsense_obj_start_line = -1
 | 
						|
let s:pythonsense_obj_end_line = -1
 | 
						|
function! pythonsense#select_named_object(obj_name, inner, range)
 | 
						|
    " Is this a new selection?
 | 
						|
    let new_vis = 0
 | 
						|
    let new_vis = new_vis || s:pythonsense_obj_start_line != a:range[0]
 | 
						|
    let new_vis = new_vis || s:pythonsense_obj_end_line != a:range[1]
 | 
						|
 | 
						|
    " store current range
 | 
						|
    let s:pythonsense_obj_start_line = a:range[0]
 | 
						|
    let s:pythonsense_obj_end_line = a:range[1]
 | 
						|
 | 
						|
    " Repeatedly increase the scope of the selection.
 | 
						|
    let cnt = 1
 | 
						|
    let scan_start_line = s:pythonsense_obj_start_line
 | 
						|
    while scan_start_line > 0
 | 
						|
        if getline(scan_start_line) !~ '^\s*$'
 | 
						|
            break
 | 
						|
        endif
 | 
						|
        let scan_start_line -= 1
 | 
						|
    endwhile
 | 
						|
    if scan_start_line == 0
 | 
						|
        return [-1, -1]
 | 
						|
    endif
 | 
						|
    let obj_max_indent_level = -1
 | 
						|
 | 
						|
    while cnt > 0
 | 
						|
        let current_line_nr = scan_start_line
 | 
						|
 | 
						|
        let [obj_start_line, obj_end_line] = pythonsense#get_object_line_range(a:obj_name, obj_max_indent_level, current_line_nr, s:pythonsense_obj_end_line, a:inner)
 | 
						|
        if obj_start_line == -1
 | 
						|
            return [-1, -1]
 | 
						|
        endif
 | 
						|
 | 
						|
        let is_changed = 0
 | 
						|
        let is_changed = is_changed || s:pythonsense_obj_start_line != obj_start_line
 | 
						|
        let is_changed = is_changed || s:pythonsense_obj_end_line != obj_end_line
 | 
						|
        if new_vis
 | 
						|
            let is_changed = 1
 | 
						|
        endif
 | 
						|
 | 
						|
        let s:pythonsense_obj_start_line = obj_start_line
 | 
						|
        let s:pythonsense_obj_end_line = obj_end_line
 | 
						|
 | 
						|
        " If there was no change, then don't decrement the count (it didn't
 | 
						|
        " count because it didn't do anything).
 | 
						|
        if is_changed
 | 
						|
            let cnt = cnt - 1
 | 
						|
        else
 | 
						|
            " no change to selection;
 | 
						|
            " move to line above selection and try again
 | 
						|
            if scan_start_line == 0
 | 
						|
                return [-1, -1]
 | 
						|
            endif
 | 
						|
            let [min_indent, max_indent] = pythonsense#get_minmax_indent_count('\(class\|def\|async def\)', scan_start_line, obj_end_line)
 | 
						|
            if min_indent == 0
 | 
						|
                return [-1, -1]
 | 
						|
            endif
 | 
						|
            let obj_max_indent_level = min_indent - 1
 | 
						|
            let scan_start_line -= 1
 | 
						|
        endif
 | 
						|
    endwhile
 | 
						|
 | 
						|
    " select range
 | 
						|
    if obj_end_line >= obj_start_line
 | 
						|
        exec obj_start_line
 | 
						|
        execute "normal! V" . obj_end_line . "G"
 | 
						|
        return [obj_start_line, obj_end_line]
 | 
						|
    else
 | 
						|
        return [-1, -1]
 | 
						|
    endif
 | 
						|
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_object_line_range(obj_name, obj_max_indent_level, line_range_start, line_range_end, inner)
 | 
						|
    " find definition line
 | 
						|
    let current_line_nr = a:line_range_start
 | 
						|
    if a:line_range_start == a:line_range_end
 | 
						|
        let search_past_decorator_last_line = line("$")
 | 
						|
    else
 | 
						|
        let search_past_decorator_last_line = a:line_range_end
 | 
						|
    endif
 | 
						|
 | 
						|
    while current_line_nr <= search_past_decorator_last_line
 | 
						|
        if getline(current_line_nr) !~ '^\s*@.*$'
 | 
						|
            break
 | 
						|
        end
 | 
						|
        let current_line_nr += 1
 | 
						|
    endwhile
 | 
						|
    if current_line_nr > search_past_decorator_last_line
 | 
						|
        return [-1, -1]
 | 
						|
    endif
 | 
						|
    if current_line_nr > a:line_range_end
 | 
						|
        let effective_line_range_end = current_line_nr
 | 
						|
    else
 | 
						|
        let effective_line_range_end = a:line_range_end
 | 
						|
    endif
 | 
						|
    let obj_start_line = pythonsense#get_named_python_obj_start_line_nr(a:obj_name, a:obj_max_indent_level, current_line_nr, 0)
 | 
						|
    " no object definition line in file
 | 
						|
    if (! obj_start_line)
 | 
						|
        return [-1, -1]
 | 
						|
    endif
 | 
						|
    let obj_header_line = obj_start_line
 | 
						|
    let obj_header_indent = pythonsense#get_line_indent_count(obj_header_line)
 | 
						|
    if obj_header_indent > 0
 | 
						|
        let obj_header_indent -= 1
 | 
						|
    endif
 | 
						|
 | 
						|
    let obj_end_line = pythonsense#get_object_end_line_nr(obj_start_line, obj_start_line, a:inner)
 | 
						|
 | 
						|
    " in case of a class definition, the parentheses are optional
 | 
						|
    if a:obj_name == "def"
 | 
						|
      let pattern = '^[^#]*)[^#]*:\(\s*$\|\s*#.*$\)'
 | 
						|
    else
 | 
						|
      let pattern = '^[^#]*)\?[^#]*:\(\s*$\|\s*#.*$\)'
 | 
						|
    endif
 | 
						|
 | 
						|
    if (a:inner)
 | 
						|
        " find class/function body
 | 
						|
        let inner_start_line = obj_start_line
 | 
						|
        while inner_start_line <= line('$')
 | 
						|
            if getline(inner_start_line) =~# pattern
 | 
						|
                break
 | 
						|
            endif
 | 
						|
            let inner_start_line += 1
 | 
						|
        endwhile
 | 
						|
        if inner_start_line <= line('$')
 | 
						|
            let obj_start_line = inner_start_line + 1
 | 
						|
        endif
 | 
						|
    else
 | 
						|
        " include decorators
 | 
						|
        let dec_line = pythonsense#get_start_decorators_line_nr(obj_start_line)
 | 
						|
        if dec_line < obj_start_line
 | 
						|
            let obj_start_line = dec_line
 | 
						|
        endif
 | 
						|
    endif
 | 
						|
 | 
						|
    " This is an ugly hack to deal with (some) specially indented cases
 | 
						|
    " (especially when searching for a 'class' while inside a non-class member
 | 
						|
    " function, or when searching for a 'def' with nothing but class
 | 
						|
    " definitions above)
 | 
						|
    " Make sure there is no statement line with a lower indentation than the
 | 
						|
    " definition line in between the current line and the definition line
 | 
						|
    if a:obj_name == 'class'
 | 
						|
        let pattern = 'def\|async def'
 | 
						|
    else
 | 
						|
        let pattern = 'class'
 | 
						|
    endif
 | 
						|
    let pattern = '^\s*[^#]*\s*\k'
 | 
						|
    if pythonsense#is_statement_encountered_between_two_lines(pattern, obj_header_indent, obj_start_line, current_line_nr)
 | 
						|
        return [-1, -1]
 | 
						|
    endif
 | 
						|
 | 
						|
    return [obj_start_line, obj_end_line]
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_object_end_line_nr(obj_start, search_start, inner)
 | 
						|
    let obj_indent = pythonsense#get_line_indent_count(a:obj_start)
 | 
						|
    let obj_end = pythonsense#get_next_indent_line_nr(a:search_start, obj_indent)
 | 
						|
    if a:inner
 | 
						|
        let obj_end = prevnonblank(obj_end)
 | 
						|
    endif
 | 
						|
    return obj_end
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_next_indent_line_nr(search_start, obj_indent)
 | 
						|
    let line = a:search_start
 | 
						|
 | 
						|
    " Handle multiline definition 
 | 
						|
    let saved_cursor = getcurpos()
 | 
						|
    call cursor(line, 0)
 | 
						|
    normal! f(%
 | 
						|
    let line = line('.')
 | 
						|
    call setpos('.', saved_cursor)
 | 
						|
 | 
						|
    let lastline = line('$')
 | 
						|
    while (line > 0 && line <= lastline)
 | 
						|
        let line = line + 1
 | 
						|
        if (pythonsense#get_line_indent_count(line) <= a:obj_indent && getline(line) !~ '^\s*$')
 | 
						|
            return line - 1
 | 
						|
        endif
 | 
						|
    endwhile
 | 
						|
    return lastline
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_start_decorators_line_nr(start)
 | 
						|
    " Returns the line of the first decorator line above the starting line,
 | 
						|
    " counting only decorators with the same level.
 | 
						|
    let start_line_indent = pythonsense#get_line_indent_count(a:start)
 | 
						|
    let last_non_blank_line = a:start
 | 
						|
    let current_line = a:start - 1
 | 
						|
    while current_line > 0
 | 
						|
        if getline(current_line) !~ '^\s*$'
 | 
						|
            if pythonsense#get_line_indent_count(current_line) != start_line_indent
 | 
						|
                break
 | 
						|
            endif
 | 
						|
            if getline(current_line) !~ '^\s*@'
 | 
						|
                break
 | 
						|
            endif
 | 
						|
            let last_non_blank_line = current_line
 | 
						|
        endif
 | 
						|
        let current_line -= 1
 | 
						|
    endwhile
 | 
						|
    return last_non_blank_line
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_line_indent_count(line_nr)
 | 
						|
    if b:pythonsense_is_tab_indented
 | 
						|
        let indent_count = matchstrpos(getline(a:line_nr), '^\t\+')[2]
 | 
						|
    else
 | 
						|
        let indent_count = indent(a:line_nr)
 | 
						|
    endif
 | 
						|
    return indent_count
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_minmax_indent_count(pattern, line_range_start, line_range_end)
 | 
						|
    let current_line = a:line_range_start
 | 
						|
    let min_indent_count = -1
 | 
						|
    let max_indent_count = -1
 | 
						|
    while current_line <= a:line_range_end && current_line <= line('$')
 | 
						|
        if getline(current_line) !~ '^\s*$'
 | 
						|
            if getline(current_line) =~# a:pattern
 | 
						|
                let current_indent = pythonsense#get_line_indent_count(current_line)
 | 
						|
                if min_indent_count < 0 || current_indent < min_indent_count
 | 
						|
                    let min_indent_count = current_indent
 | 
						|
                endif
 | 
						|
                if max_indent_count < 0 || current_indent > max_indent_count
 | 
						|
                    let max_indent_count = current_indent
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
        let current_line = current_line + 1
 | 
						|
    endwhile
 | 
						|
    return [min_indent_count, max_indent_count]
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#is_statement_encountered_between_two_lines(pattern, max_indent, line_range_start, line_range_end)
 | 
						|
    let current_line = a:line_range_start
 | 
						|
    while current_line <= a:line_range_end && current_line <= line('$')
 | 
						|
        if getline(current_line) !~ '^\s*$'
 | 
						|
            if getline(current_line) =~# a:pattern
 | 
						|
                let current_indent = pythonsense#get_line_indent_count(current_line)
 | 
						|
                if a:max_indent > -1 && current_indent < a:max_indent
 | 
						|
                    return 1
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
        let current_line += 1
 | 
						|
    endwhile
 | 
						|
    return 0
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_indent_char()
 | 
						|
    if b:pythonsense_is_tab_indented
 | 
						|
        let indent_char = '\t'
 | 
						|
    else
 | 
						|
        let indent_char = " "
 | 
						|
    endif
 | 
						|
    return indent_char
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#get_named_python_obj_start_line_nr(obj_name, obj_max_indent_level, start_line, fwd)
 | 
						|
    let lastline = line('$')
 | 
						|
    if a:fwd
 | 
						|
        let stepvalue = 1
 | 
						|
    else
 | 
						|
        let stepvalue = -1
 | 
						|
    endif
 | 
						|
    let indent_char = pythonsense#get_indent_char()
 | 
						|
 | 
						|
    let current_line = a:start_line
 | 
						|
    while (current_line > 0 && current_line <= lastline)
 | 
						|
        " if getline(current_line) !~ '\(^\s*$\|^\s*[#@].*$\)'
 | 
						|
        if getline(current_line) !~ '^\s*$'
 | 
						|
            break
 | 
						|
        endif
 | 
						|
        let current_line = current_line + stepvalue
 | 
						|
    endwhile
 | 
						|
    if a:obj_max_indent_level > -1
 | 
						|
        let pattern = '^' . indent_char . '\{0,' . a:obj_max_indent_level . '}' . '\(class\|def\|async def\)'
 | 
						|
    else
 | 
						|
        let pattern = '^\s*' . '\(class\|def\|async def\)'
 | 
						|
    endif
 | 
						|
    if getline(current_line) =~# pattern
 | 
						|
        if getline(current_line) =~# a:obj_name
 | 
						|
            return current_line
 | 
						|
        endif
 | 
						|
    endif
 | 
						|
 | 
						|
    let target_line_indent = pythonsense#get_line_indent_count(current_line) - 1
 | 
						|
    if target_line_indent < 0
 | 
						|
        let target_line_indent = 0
 | 
						|
    endif
 | 
						|
    if a:obj_max_indent_level > -1 && target_line_indent > a:obj_max_indent_level
 | 
						|
        let target_line_indent = a:obj_max_indent_level
 | 
						|
    endif
 | 
						|
    let max_indent = target_line_indent
 | 
						|
    while (current_line > 0 && current_line <= lastline)
 | 
						|
        let pattern = '^' . indent_char . '\{0,' . max_indent . '}' . '\(class\|def\|async def\)'
 | 
						|
        if getline(current_line) =~# pattern
 | 
						|
            if getline(current_line) =~# a:obj_name
 | 
						|
                return current_line
 | 
						|
            else
 | 
						|
                if a:obj_name != 'class' && pythonsense#get_line_indent_count(current_line) <= max_indent
 | 
						|
                    " encountered a scope block at a lower indent level before
 | 
						|
                    " encountering object definition
 | 
						|
                    return 0
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
        " let m = match(getline(current_line), pattern)
 | 
						|
        " if m >= 0
 | 
						|
        "     return current_line
 | 
						|
        " endif
 | 
						|
 | 
						|
        let target_line_indent = pythonsense#get_line_indent_count(current_line) - 1
 | 
						|
        " Special case for multiline argument lines, with the parameter being
 | 
						|
        " indented one step more than the open def/class and the closing
 | 
						|
        " parenthesis.
 | 
						|
        let closing_pattern = '^' . indent_char . '*)'
 | 
						|
        if target_line_indent > 0 && target_line_indent < max_indent && getline(current_line) !~# closing_pattern
 | 
						|
            let max_indent = target_line_indent
 | 
						|
        endif
 | 
						|
        if a:obj_max_indent_level > -1 && target_line_indent > a:obj_max_indent_level
 | 
						|
            let target_line_indent = a:obj_max_indent_level
 | 
						|
        endif
 | 
						|
 | 
						|
        let current_line = current_line + stepvalue
 | 
						|
    endwhile
 | 
						|
    return 0
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#python_text_object(obj_name, inner, mode)
 | 
						|
    if a:mode == "o"
 | 
						|
        let lnrange = [line("."), line(".")]
 | 
						|
    else
 | 
						|
        let lnrange = [line("'<"), line("'>")]
 | 
						|
    endif
 | 
						|
    let nreps_left = 1 "v:count1
 | 
						|
    while nreps_left > 0
 | 
						|
        let lnrange = pythonsense#select_named_object(a:obj_name, a:inner, lnrange)
 | 
						|
        if lnrange[0] == -1
 | 
						|
            break
 | 
						|
        endif
 | 
						|
        let s:pythonsense_obj_start_line = lnrange[0]
 | 
						|
        let s:pythonsense_obj_end_line = lnrange[1]
 | 
						|
        let nreps_left -= 1
 | 
						|
    endwhile
 | 
						|
    if lnrange[0] != -1
 | 
						|
        if has("folding") && foldclosed(line('.')) != -1
 | 
						|
            " try
 | 
						|
            "     execute "normal! zO"
 | 
						|
            " catch /E490/ " no fold found
 | 
						|
            " endtry
 | 
						|
            execute "normal! zO"
 | 
						|
        endif
 | 
						|
        " let s:pythonsense_obj_start_line = -1
 | 
						|
        " let s:pythonsense_obj_end_line = -1
 | 
						|
        " execute "normal! \<ESC>gv"
 | 
						|
    endif
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#python_function_text_object(inner, mode)
 | 
						|
    call pythonsense#python_text_object('def', a:inner, a:mode)
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#python_class_text_object(inner, mode)
 | 
						|
    call pythonsense#python_text_object('class', a:inner, a:mode)
 | 
						|
endfunction
 | 
						|
 | 
						|
" }}}1
 | 
						|
 | 
						|
" Python Docstring Text Objects {{{1
 | 
						|
" From: https://pastebin.com/u/gfixler
 | 
						|
function! pythonsense#python_docstring_text_object (inner)
 | 
						|
    " get current line number
 | 
						|
    let s = line('.')
 | 
						|
    " climb up to first def/class line, or first line of buffer
 | 
						|
    while s > 0 && getline(s) !~# '^\s*\(def\|async def\|class\)'
 | 
						|
        let s = s - 1
 | 
						|
    endwhile
 | 
						|
    " set search start to just after def/class line, or on first buffer line
 | 
						|
    let s = s + 1
 | 
						|
    " descend lines obj_end_line end of buffer or def/class line
 | 
						|
    while s < line('$') && getline(s) !~# '^\s*\(def\|async def\|class\)'
 | 
						|
        " if line begins with optional whitespace followed by '''
 | 
						|
        if getline(s) =~ "^\\s*'''" || getline(s) =~ '^\s*"""'
 | 
						|
            if getline(s) =~ "^\\s*'''"
 | 
						|
                let close_pattern = "'''\\s*$"
 | 
						|
            else
 | 
						|
                let close_pattern = '"""\s*$'
 | 
						|
            endif
 | 
						|
            " set search end to just after found start line
 | 
						|
            let e = s + 1
 | 
						|
            " descend lines obj_end_line end of buffer or def/class line
 | 
						|
            while e <= line('$') && getline(e) !~# '^\s*\(def\|async def\|class\)'
 | 
						|
                " if line ends with ''' followed by optional whitespace
 | 
						|
                if getline(e) =~ close_pattern
 | 
						|
                    " TODO check first for blank lines above to select instead
 | 
						|
                    " for 'around', extend search end through blank lines
 | 
						|
                    if a:inner
 | 
						|
                        let e -= 1
 | 
						|
                        let s += 1
 | 
						|
                    else
 | 
						|
                        let x = e + 1
 | 
						|
                        while x <= line('$') && getline(x) =~ '^\s*$'
 | 
						|
                            let e = x
 | 
						|
                            let x = x + 1
 | 
						|
                        endwhile
 | 
						|
                    endif
 | 
						|
                    " visual line select from start to end (first cursor move)
 | 
						|
                    exe 'norm '.s.'ggV'.e.'gg'
 | 
						|
                    return
 | 
						|
                endif
 | 
						|
                " move search end down a line
 | 
						|
                let e = e + 1
 | 
						|
            endwhile
 | 
						|
        endif
 | 
						|
        " move search start down a line
 | 
						|
        let s = s + 1
 | 
						|
    endwhile
 | 
						|
endfunction
 | 
						|
" }}}1
 | 
						|
 | 
						|
" Python Movements {{{1
 | 
						|
 | 
						|
function! pythonsense#move_to_python_object(obj_name, to_end, fwd, vim_mode) range
 | 
						|
    if a:fwd
 | 
						|
        let initial_search_start_line = a:lastline
 | 
						|
    else
 | 
						|
        let initial_search_start_line = a:firstline
 | 
						|
    endif
 | 
						|
    if a:to_end
 | 
						|
        let target_line = pythonsense#find_end_of_python_object_to_move_to(a:obj_name, initial_search_start_line, a:fwd, v:count1)
 | 
						|
    else
 | 
						|
        let target_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, initial_search_start_line, a:fwd, -1, v:count1)
 | 
						|
    endif
 | 
						|
    if target_line < 0 || target_line > line('$')
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    let current_column = col('.')
 | 
						|
    let preserve_col_pos = get(b:, "pythonsense_preserve_col_pos", get(g:, "pythonsense_preserve_col_pos", 0))
 | 
						|
    let fold_open = ""
 | 
						|
    if a:vim_mode == "v"
 | 
						|
        normal! gv
 | 
						|
    endif
 | 
						|
    if has("folding") && foldclosed(line('.')) != -1
 | 
						|
        let fold_open = "zO"
 | 
						|
    else
 | 
						|
        let fold_open = ""
 | 
						|
    endif
 | 
						|
    try
 | 
						|
        if preserve_col_pos
 | 
						|
            execute "normal! " . target_line . "G" . current_column . "|" . preserve_col_pos . fold_open
 | 
						|
        else
 | 
						|
            execute "normal! " . target_line . "G^" . fold_open
 | 
						|
        endif
 | 
						|
    catch /E490/ " no fold found
 | 
						|
    endtry
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#find_end_of_python_object_to_move_to(obj_name, start_line, fwd, nreps)
 | 
						|
    let initial_search_start_line = a:start_line
 | 
						|
    let effective_start_line = initial_search_start_line
 | 
						|
    let niters = 0
 | 
						|
    while niters < 2
 | 
						|
        let [start_line, nreps_remaining] = pythonsense#find_start_line_for_end_movement(a:obj_name, effective_start_line, a:fwd, a:nreps)
 | 
						|
        if start_line <= 0
 | 
						|
            let start_line = 1
 | 
						|
        endif
 | 
						|
        let start_of_object_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, start_line, a:fwd, -1, nreps_remaining)
 | 
						|
        if start_of_object_line < 0 || start_of_object_line > line('$')
 | 
						|
            return -1
 | 
						|
        endif
 | 
						|
        let target_line = pythonsense#get_object_end_line_nr(start_of_object_line, start_of_object_line, 1)
 | 
						|
        if target_line > 0
 | 
						|
                    \ && niters == 0
 | 
						|
                    \ && (
 | 
						|
                    \   target_line == initial_search_start_line
 | 
						|
                    \   || (a:fwd && target_line < initial_search_start_line)
 | 
						|
                    \   || (!a:fwd && target_line > initial_search_start_line)
 | 
						|
                    \    )
 | 
						|
            " no change; possibly because we are already at an end boundary;
 | 
						|
            " make ONE more attempt at trying again
 | 
						|
            let niters += 1
 | 
						|
            if a:fwd
 | 
						|
                let effective_start_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, target_line, a:fwd, -1, 1)
 | 
						|
                if effective_start_line <= 0
 | 
						|
                    break
 | 
						|
                endif
 | 
						|
            else
 | 
						|
                let prev_obj_indent = pythonsense#get_line_indent_count(start_of_object_line)
 | 
						|
                let [new_start_line, nreps_remaining] = pythonsense#find_start_line_for_end_movement(a:obj_name, start_of_object_line, a:fwd, a:nreps)
 | 
						|
                while new_start_line > 0 && getline(new_start_line) =~ '^\s*$'
 | 
						|
                    let new_start_line -= 1
 | 
						|
                endwhile
 | 
						|
                if new_start_line <= 0
 | 
						|
                    break
 | 
						|
                endif
 | 
						|
                let start_of_object_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, new_start_line, a:fwd, prev_obj_indent, nreps_remaining)
 | 
						|
                let target_line = pythonsense#get_object_end_line_nr(start_of_object_line, start_of_object_line, 1)
 | 
						|
                break
 | 
						|
            endif
 | 
						|
        else
 | 
						|
            break
 | 
						|
        endif
 | 
						|
    endwhile
 | 
						|
    if a:fwd && target_line < initial_search_start_line
 | 
						|
        return -1
 | 
						|
    elseif !a:fwd && target_line > initial_search_start_line
 | 
						|
        return -1
 | 
						|
    else
 | 
						|
        return target_line
 | 
						|
    endif
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#find_start_of_python_object_to_move_to(obj_name, start_line, fwd, max_indent, nreps)
 | 
						|
    let current_line = a:start_line
 | 
						|
    if a:fwd
 | 
						|
        let stepvalue = 1
 | 
						|
    else
 | 
						|
        let stepvalue = -1
 | 
						|
    endif
 | 
						|
    let target_pattern = '^\s*' . a:obj_name . '\s\+'
 | 
						|
    let nreps_left = a:nreps
 | 
						|
    let start_line = current_line
 | 
						|
    let target_line = current_line
 | 
						|
    let scope_block_indent = a:max_indent
 | 
						|
    if getline(start_line) =~# target_pattern
 | 
						|
        let start_line += stepvalue
 | 
						|
    endif
 | 
						|
    while nreps_left > 0
 | 
						|
        while start_line > 0 && start_line <= line("$")
 | 
						|
            if getline(start_line) =~ '^\s*\(class\|def\|async def\)'
 | 
						|
                let current_line_indent = pythonsense#get_line_indent_count(start_line)
 | 
						|
                if getline(start_line) =~# target_pattern
 | 
						|
                    if a:max_indent < 0 || current_line_indent < scope_block_indent
 | 
						|
                        let target_line = start_line
 | 
						|
                        break
 | 
						|
                    endif
 | 
						|
                endif
 | 
						|
                if scope_block_indent == -1 || current_line_indent < scope_block_indent
 | 
						|
                    let scope_block_indent = current_line_indent
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
            let start_line += stepvalue
 | 
						|
        endwhile
 | 
						|
        if start_line < 1 || start_line > line("$")
 | 
						|
            break
 | 
						|
        endif
 | 
						|
        let start_line += stepvalue
 | 
						|
        let nreps_left -= 1
 | 
						|
    endwhile
 | 
						|
    return target_line
 | 
						|
endfunction
 | 
						|
 | 
						|
function! pythonsense#find_start_line_for_end_movement(obj_name, initial_search_start_line, fwd, nreps_requested)
 | 
						|
    let start_line = a:initial_search_start_line
 | 
						|
    let nreps_remaining = a:nreps_requested
 | 
						|
    let target_pattern = '^\s*' . a:obj_name . '\s\+'
 | 
						|
    let scope_block_indent = -1
 | 
						|
    let is_found = 0
 | 
						|
    while start_line > 0
 | 
						|
        if getline(start_line) =~ '^\s*\(class\|def\|async def\)'
 | 
						|
            let current_line_indent = pythonsense#get_line_indent_count(start_line)
 | 
						|
            if getline(start_line) =~ target_pattern
 | 
						|
                if scope_block_indent == -1 || current_line_indent < scope_block_indent
 | 
						|
                    let is_found = 1
 | 
						|
                    break
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
            if scope_block_indent == -1 || current_line_indent < scope_block_indent
 | 
						|
                let scope_block_indent = current_line_indent
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
        let start_line -= 1
 | 
						|
    endwhile
 | 
						|
    if is_found
 | 
						|
        if !a:fwd
 | 
						|
            let start_line -= 1
 | 
						|
        else
 | 
						|
            let nreps_remaining -= 1 " skip finding this block
 | 
						|
        endif
 | 
						|
    endif
 | 
						|
    return [start_line, nreps_remaining]
 | 
						|
endfunction
 | 
						|
 | 
						|
" }}}1
 | 
						|
 | 
						|
" Python Location Information {{{1
 | 
						|
function! pythonsense#echo_python_location()
 | 
						|
    let indent_char = pythonsense#get_indent_char()
 | 
						|
    let pyloc = []
 | 
						|
    let current_line = line('.')
 | 
						|
    let obj_pattern = '\(class\|def\|async def\)'
 | 
						|
    while current_line > 0
 | 
						|
        if getline(current_line) !~ '^\s*$'
 | 
						|
            break
 | 
						|
        endif
 | 
						|
        let current_line = current_line - 1
 | 
						|
    endwhile
 | 
						|
    if current_line == 0
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    let target_line_indent = pythonsense#get_line_indent_count(current_line)
 | 
						|
    if target_line_indent < 0
 | 
						|
        break
 | 
						|
    endif
 | 
						|
    let previous_line = current_line
 | 
						|
    while current_line > 0
 | 
						|
        let pattern = '^' . indent_char . '\{0,' . target_line_indent . '}' . obj_pattern
 | 
						|
        let current_line_text = getline(current_line)
 | 
						|
        if current_line_text =~# pattern
 | 
						|
            let obj_name = matchstr(current_line_text, '^\s*\(class\|def\|async def\)\s\+\zs\k\+')
 | 
						|
            if get(g:, "pythonsense_extended_location_info", 1)
 | 
						|
                let obj_type = matchstr(current_line_text, '^\s*\zs\(class\|def\|async def\)')
 | 
						|
                call add(pyloc, "(" . obj_type . ":)" . obj_name)
 | 
						|
            else
 | 
						|
                call add(pyloc, obj_name)
 | 
						|
            endif
 | 
						|
            let target_line_indent = pythonsense#get_line_indent_count(current_line) - 1
 | 
						|
        endif
 | 
						|
        if target_line_indent < 0
 | 
						|
            break
 | 
						|
        endif
 | 
						|
        let previous_line = current_line
 | 
						|
        let current_line = current_line - 1
 | 
						|
    endwhile
 | 
						|
    if get(g:, "pythonsense_extended_location_info", 1)
 | 
						|
        let joiner = " > "
 | 
						|
    else
 | 
						|
        let joiner = "."
 | 
						|
    endif
 | 
						|
    echo join(reverse(pyloc), joiner)
 | 
						|
    return
 | 
						|
endfunction
 | 
						|
" }}}1
 |