Skip to content

Commit 5131438

Browse files
cposture陈家启
authored andcommitted
init
1 parent 676f26e commit 5131438

File tree

9 files changed

+679
-1
lines changed

9 files changed

+679
-1
lines changed

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
# Controls when the workflow will run
4+
on:
5+
# Triggers the workflow on push or pull request events but only for the "main" branch
6+
push:
7+
branches: [ "main" ]
8+
pull_request:
9+
branches: [ "main" ]
10+
11+
# Allows you to run this workflow manually from the Actions tab
12+
workflow_dispatch:
13+
14+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
15+
jobs:
16+
# This workflow contains a single job called "build"
17+
test:
18+
# The type of runner that the job will run on
19+
runs-on: ubuntu-latest
20+
21+
# Steps represent a sequence of tasks that will be executed as part of the job
22+
steps:
23+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
24+
- uses: actions/checkout@v3
25+
26+
- name: Install dependency
27+
run: git clone https://github.com/kana/vim-textobj-user.git $GITHUB_WORKSPACE/vim-textobj-user
28+
29+
# Runs a single command using the runners shell
30+
- name: Run test_select.vim
31+
run: |
32+
vim --clean -S test/run_test.vim test/tests/test_select.vim || (vim_exit_code=$?; cat $GITHUB_WORKSPACE/test/tests/test_select.vim.failed.log; exit $vim_exit_code)
33+
34+
# Runs a set of commands using the runners shell
35+
- name: Run test_move.vim
36+
run: |
37+
vim --clean -S test/run_test.vim test/tests/test_move.vim || (vim_exit_code=$?; cat $GITHUB_WORKSPACE/test/tests/test_move.vim.failed.log; exit $vim_exit_code)

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023 陈家启
3+
Copyright (c) 2023 cposture
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

autoload/textobj/argument.vim

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
function! textobj#argument#select_a()
2+
return s:Select(g:argument_separator, 1, v:count1, visualmode())
3+
endfunction
4+
5+
function! textobj#argument#select_i()
6+
return s:Select(g:argument_separator, 0, v:count1, visualmode())
7+
endfunction
8+
9+
function! textobj#argument#select_I()
10+
return s:Select(g:argument_separator, 0, v:count1, visualmode())
11+
endfunction
12+
13+
function! textobj#argument#move_n()
14+
return s:Move(g:argument_separator, 1, v:count1, 0, visualmode())
15+
endfunction
16+
17+
function! textobj#argument#move_N()
18+
return s:Move(g:argument_separator, 1, v:count1, 1, visualmode())
19+
endfunction
20+
21+
function! textobj#argument#move_p()
22+
return s:Move(g:argument_separator, 0, v:count1, 0, visualmode())
23+
endfunction
24+
25+
function! textobj#argument#move_P()
26+
return s:Move(g:argument_separator, 0, v:count1, 1, visualmode())
27+
endfunction
28+
29+
"
30+
" 移动列表中的参数
31+
"
32+
" 列表使用括号括起来 (i.e. '()', '[]', or '{}').
33+
" 参数使用 a:sep 分隔(e.g. ',')
34+
"
35+
" 如果 a:direction 大于 0 则向右移动,否则向左移动
36+
" 如果设置了 a:to_end,则支持一键移动到最左端最右端的参数
37+
"
38+
function! s:Move(sep, direction, times, to_end, ...)
39+
let lbracket = g:argument_left_bracket
40+
let rbracket = g:argument_right_bracket
41+
let save_unnamed = @"
42+
let save_ic = &ic
43+
let &ic = 0
44+
45+
" 记录起始光标字符,用于判断是不是括号
46+
" 如果是括号,则 last 的跳数需要减一,用于跳到括号附近的参数
47+
exe "normal! vy"
48+
let oristart = @"
49+
50+
try
51+
" 向后搜索且到不使用文件环绕,
52+
" 如果 direction 为 true 多了个 flag c,差别:
53+
" 1. 如果停留在{start}上,那当前光标会作为结果,没有flag会返回0
54+
" 2. 如果停留在 {end} 上,那会被认为是嵌套组,返回 0,没有flag会跳到分割符
55+
" 3. 如果停留在分割符上,那当前光标会作为结果,没有flag会返回上个分割符
56+
" 4. 如果停留在分割符右边,那上个分割会作为结果,没有flag也一样
57+
"
58+
" 总结意图:向后搜索匹配的分割符或左括号
59+
" 正向搜索且起始位置位于 {start} 需要接受光标下的左括号,所以需要flag c
60+
let flags = a:direction ? 'bcW' : 'bW'
61+
if searchpair(lbracket, a:sep, rbracket, flags,
62+
\ 's:IsCursorOnStringOrComment()') <= 0
63+
" 不在列表内,则向前搜索是不是右左括号
64+
" 用于列表外能直接跳转到参数列表内
65+
let flags = a:direction ? 'W' : 'bW'
66+
if search(lbracket, flags, line('.')) <= 0
67+
return
68+
endif
69+
" 重新定位起始光标字符
70+
exe "normal! vy"
71+
let oristart = @"
72+
endif
73+
" 经过 searchpair 此时光标会移动到左括号或分割符上
74+
" 获取当前光标字符
75+
exe "normal! yl"
76+
let first = @"
77+
78+
" times 支持多跳
79+
let times = a:times
80+
" 如果起始位置是括号,为了能指向括号的下个参数,跳数减一
81+
if (a:direction && oristart =~ lbracket) || (!a:direction && oristart =~ rbracket)
82+
let times = times - 1
83+
endif
84+
" 根据 direction 确定是向前确定还是向后确定右边界
85+
" 搜索分割符或括号
86+
" 因为没有 flag c,所以即使当前光标为分割符也会跳过当前的
87+
let flags = a:direction ? 'W' : 'bW'
88+
" 如果是到最端,则不受 times 限制
89+
while (times > 0 || a:to_end) &&
90+
\ (@" =~ a:sep || (a:direction && @" =~ lbracket) || (!a:direction && @" =~ rbracket))
91+
\ && searchpair(lbracket, a:sep, rbracket, flags, 's:IsCursorOnStringOrComment()') > 0
92+
let times -= 1
93+
exe "normal! yl"
94+
endwhile
95+
" 记录最后一跳后,当前光标下的字符,可能是左右括号或分割符
96+
let last = @"
97+
98+
if a:sep =~ first && a:sep =~ last
99+
" 左右两边为分割符
100+
" (a, b /**/, c /* */, d
101+
" ·
102+
" 正向搜索
103+
" (a, b /**/, c /* */, d
104+
" ↑ ↑ ·
105+
" 反向搜索
106+
" (a, b /**/, c /* */, d
107+
" ↑ · ↑
108+
" 将右边界向右移动到下个参数,忽略注释
109+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
110+
elseif a:sep =~ first
111+
" 左边为分割符,右边为括号
112+
" a, b /**/, c , d/* */)
113+
" ·
114+
if a:direction
115+
" 正向搜索
116+
" a, b /**/, c , d/* */)
117+
" ↑ · ↑
118+
" 向后搜索匹配的分割符或左括号
119+
call searchpair(lbracket, a:sep, rbracket, 'bW', 's:IsCursorOnStringOrComment()')
120+
" 将右边界向右移动到下个参数,忽略注释
121+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
122+
else
123+
" 反向搜索
124+
" (a, b /**/, c , d/* */)
125+
" ↑· ↑ ·
126+
" 将右边界向右移动到下个参数,忽略注释
127+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
128+
endif
129+
elseif a:sep =~ last
130+
" 左边为括号,右边为分割符
131+
" 正向搜索
132+
" (a, b , c /**/, d)
133+
" ↑· ↑
134+
" (a, b , c /**/, d)
135+
" ↑· ↑ ·
136+
" 反向搜索
137+
" 左边为括号,右边肯定也是括号
138+
" 将右边界向右移动到下个参数,忽略注释
139+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
140+
else
141+
" 两边为括号
142+
if lbracket =~ first && lbracket =~ last
143+
" (a, b , c , d)
144+
"
145+
"
146+
" ·
147+
" (a, b , c , d)
148+
"
149+
"
150+
" ··
151+
" 将右边界向右移动到下个参数,忽略注释
152+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
153+
elseif rbracket =~ first && rbracket =~ last
154+
" (a, b , c , d)
155+
"
156+
"
157+
" ·
158+
" (a, b , c , d)
159+
"
160+
"
161+
" ··
162+
call searchpair(lbracket, a:sep, rbracket, 'bW', 's:IsCursorOnStringOrComment()')
163+
" 将右边界向右移动到下个参数,忽略注释
164+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
165+
elseif a:direction
166+
" (a, b , c , d)
167+
" ↑· ↑
168+
" (a, b , c , d)
169+
" ↑· ·↑
170+
call searchpair(lbracket, a:sep, rbracket, 'bW', 's:IsCursorOnStringOrComment()')
171+
" 将右边界向右移动到下个参数,忽略注释
172+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
173+
else
174+
" (a, b , c , d)
175+
" ↑ ·↑
176+
" (a, b , c , d)
177+
" ↑· ·↑
178+
" 将右边界向右移动到下个参数,忽略注释
179+
call searchpair('\%0l', '', '\S', 'W', 's:IsCursorOnComment()')
180+
endif
181+
endif
182+
183+
return ['v', getpos('.'), 0]
184+
185+
finally
186+
let @" = save_unnamed
187+
let &ic = save_ic
188+
endtry
189+
endfunction
190+
191+
"
192+
" 选择列表中的参数
193+
"
194+
" 列表使用括号括起来 (i.e. '()', '[]', or '{}').
195+
" 参数使用 a:sep 分隔(e.g. ',')
196+
"
197+
" 如果设置了 a:outer,则不止选择参数本身,还包括注释和分隔符
198+
"
199+
function! s:Select(sep, outer, times, ...)
200+
let lbracket = g:argument_left_bracket
201+
let rbracket = g:argument_right_bracket
202+
let save_mb = getpos("'b")
203+
let save_me = getpos("'e")
204+
let save_unnamed = @"
205+
let save_ic = &ic
206+
let &ic = 0
207+
let save_ww = &ww
208+
let &ww = '<,>'
209+
210+
try
211+
if searchpair(lbracket, a:sep, rbracket, 'bcW',
212+
\ 's:IsCursorOnStringOrComment()') <= 0
213+
if search(lbracket, 'W', line('.')) <= 0
214+
return
215+
endif
216+
endif
217+
218+
exe "normal! ylmb"
219+
let first = @"
220+
221+
let times = a:times
222+
while times > 0 && (@" =~ a:sep || @" =~ lbracket) && searchpair(lbracket, a:sep, rbracket,
223+
\ 'W', 's:IsCursorOnStringOrComment()') > 0
224+
let times -= 1
225+
exe "normal! yl"
226+
endwhile
227+
228+
let last = @"
229+
230+
" 只选择参数本身,忽略注释
231+
if !a:outer
232+
call search('\S', 'bW', '', '', 's:IsCursorOnComment()')
233+
exe "keepjumps normal! me`b"
234+
call search('\S', 'W', '', '', 's:IsCursorOnComment()')
235+
else
236+
" 在 outer 下,左右两边都是分割符的情况
237+
if a:sep =~ first && a:sep =~ last
238+
exe "normal \<Left>"
239+
exe "keepjumps normal! me`b"
240+
elseif a:sep =~ first
241+
" 左边为分割符,右边为括号
242+
exe "normal \<Left>"
243+
exe "keepjumps normal! me`b"
244+
elseif a:sep =~ last
245+
" 左边为括号,右边为分割符
246+
"
247+
call search('\S', 'W')
248+
exe "normal \<Left>"
249+
exe "keepjumps normal! me`b"
250+
exe "normal \<Right>"
251+
else
252+
" 两边为括号
253+
exe "keepjumps normal! me`b"
254+
exe "normal \<Right>"
255+
exe "normal! mb`e"
256+
exe "normal \<Left>"
257+
exe "keepjumps normal! me`b"
258+
endif
259+
endif
260+
261+
let head = getpos('.')
262+
263+
exe "keepjumps normal! `e"
264+
265+
" selection options 的值为 exclusive/inclusive
266+
" exclusive 模式意味着选择区的最后一个字符不包括在操作范围内,非闭区间
267+
" 所以用 <Space> 将右边界向右移动一个字符
268+
" 请注意,如果光标位于行尾,则 <space> 可以转到下一行,而 'l' 则不能
269+
if &sel == "exclusive"
270+
exe "keepjumps normal! \<Right>"
271+
endif
272+
273+
exe "keepjumps normal! \<Esc>"
274+
275+
return ['v', head, getpos('.')]
276+
277+
finally
278+
call setpos("'b", save_mb)
279+
call setpos("'e", save_me)
280+
let @" = save_unnamed
281+
let &ic = save_ic
282+
let &ww = save_ww
283+
endtry
284+
endfunction
285+
286+
" 判断当前光标下的文字是不是注释
287+
function! s:IsCursorOnComment()
288+
" synID 返回位置文本的语法 ID,再用 syncIDattr 根据语法 ID 获取语法信息
289+
" 最后根据 name 是否包含 comment 关键词判断是否为注释
290+
" 这种方式有点取巧,这个取决于是否有语法信息以及定义的语法信息名字是否为
291+
" comment
292+
return synIDattr(synID(line("."), col("."), 0), "name") =~? "comment"
293+
endfunction
294+
295+
function! s:IsCursorOnStringOrComment()
296+
let syn = synIDattr(synID(line("."), col("."), 0), "name")
297+
return syn =~? "string" || syn =~? "comment"
298+
endfunction

plugin/textobj/argument.vim

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
if exists("g:loaded_textobj_argument") || &cp || v:version < 700
2+
finish
3+
endif
4+
5+
let g:loaded_textobj_argument = 1
6+
7+
if !exists('g:argument_separator')
8+
let g:argument_separator = ','
9+
endif
10+
11+
if !exists('g:argument_left_bracket')
12+
let g:argument_left_bracket = '[[(]'
13+
endif
14+
15+
if !exists('g:argument_right_bracket')
16+
let g:argument_right_bracket = '[])]'
17+
endif
18+
19+
call textobj#user#plugin('argument', {
20+
\ '-': {
21+
\ 'select-a': 'aa', 'select-a-function': 'textobj#argument#select_a',
22+
\ 'select-i': 'ia', 'select-i-function': 'textobj#argument#select_i',
23+
\ 'move-n': ']a', 'move-n-function': 'textobj#argument#move_n',
24+
\ 'move-N': ']A', 'move-N-function': 'textobj#argument#move_N',
25+
\ 'move-p': '[a', 'move-p-function': 'textobj#argument#move_p',
26+
\ 'move-P': '[A', 'move-P-function': 'textobj#argument#move_P',
27+
\ 'sfile': expand('<sfile>:p'),
28+
\ },
29+
\ })

0 commit comments

Comments
 (0)