aboutsummaryrefslogtreecommitdiff
path: root/autoload/fall.vim
blob: feccc1a8354f45019681faa31628d1ec9b265b7c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
" Falling motions for Vim. "Falling" means moving the cursor down until it hits
" text that doesn't match a pattern.

" Returns an expression to be used in normal mode that results in the falling
" motion. The semantics are as follows:
"
" The motion moves in the direction given by a:dir (which must be 'j' or 'k').
" The motion moves while the character under the cursor does not match the
" pattern, then it moves while the character under the cursor matches the
" pattern stopping when the character under the cursor does not match the
" pattern once again.
"
" ex.
"
" fall#fall('j', '^\s*$')
"
" for
"
" | t⌷is is some text
" | this is some other text
" |
" |
" | this is some bottom text
" | this is some other bottom text
"
" will return an expression to update the cursor position to:
"
" | this is some text
" | this is some other text
" |
" |
" | t⌷is is some bottom text
" | this is some other bottom text
"
" This works pretty well for navigating around languages that use indentation
" and lack braces for bodies, i.e. vimscript, ruby. Less well for python, but
" still okay.
function! fall#fall(dir, pattern) abort
  let i = 0
  let n = 0

  if a:dir == 'j'
    let delta = 1
  else
    let delta = -1
  endif

  let line = line('.')
  let column = col('.')
  let c = v:count
  if c == 0
    let c = 1
  endif

  while i < c
    let match = matchstr(getline(line), '\%' . column . 'c.')

    while ! (match =~ a:pattern)
      let n += 1
      let line += delta
      if line <= 0 || line >= line('$')
        return "m'" . n . a:dir
      endif
      let match = matchstr(getline(line), '\%' . column . 'c.')
    endwhile

    while (match =~ a:pattern)
      let n += 1
      let line += delta
      if line <= 0 || line >= line('$')
        return "m'" . n . a:dir
      endif
      let match = matchstr(getline(line), '\%' . column . 'c.')
    endwhile

    let i += 1
  endwhile

  return "m'" . n . a:dir
endfunction

function! fall#visual_same_character(dir)
  let start_line = line(".")
  let line = start_line
  let column = col(".")
  let char_to_match = matchstr(getline(line), '\%' . column . 'c.')

  let expr = ""

  if a:dir =~ 'k'
    while line > 1
      let line -= 1
      if matchstr(getline(line), '\%' . column . 'c.') != char_to_match
        break
      endif
      let expr .= 'k'
    endwhile
  endif

  if a:dir == 'jk'
    let expr .= 'o'
    let line = start_line
  endif

  if a:dir =~ 'j'
    while line <= line("$")
      let line += 1
      if matchstr(getline(line), '\%' . column . 'c.') != char_to_match
        break
      endif
      let expr .= 'j'
    endwhile
  endif

  return expr
endfunction