├── .gitignore
├── .travis.yml
├── Gemfile
├── Rakefile
├── VimFlavor
├── autoload
└── smartinput.vim
├── doc
└── smartinput.txt
├── plugin
└── smartinput.vim
└── t
├── api.vim
├── beep-on-empty-line.t
├── beep-on-empty-line.vim
├── break-undo.vim
├── map_trigger_keys.vim
├── misc.vim
├── proper-initialization.vim
├── startup-no-default-key-mappings.vim
└── startup-preserving-existing-key-mappings.vim
/.gitignore:
--------------------------------------------------------------------------------
1 | .vim-flavor
2 | Gemfile.lock
3 | VimFlavor.lock
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: vim
2 | cache:
3 | directories:
4 | - $HOME/.rvm
5 | - $HOME/.vvm
6 | before_install:
7 | - curl https://raw.githubusercontent.com/kana/vim-version-manager/master/bin/vvm | python - setup; true
8 | - source ~/.vvm/etc/login
9 | - vvm update_itself
10 | - vvm use vimorg--v8.0.1529 --install --with-features=huge
11 | -
12 | - rvm use 2.5.0 --install --binary --fuzzy
13 | script: rake ci
14 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'vim-flavor', '~> 2.1'
4 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 |
3 | task :ci => [:dump, :test]
4 |
5 | task :dump do
6 | sh 'vim --version'
7 | end
8 |
9 | task :test do
10 | sh 'bundle exec vim-flavor test'
11 | end
12 |
--------------------------------------------------------------------------------
/VimFlavor:
--------------------------------------------------------------------------------
1 | # No dependencies.
2 |
--------------------------------------------------------------------------------
/autoload/smartinput.vim:
--------------------------------------------------------------------------------
1 | " smartinput - Provide smart input assistant
2 | " Version: 0.1.0
3 | " Copyright (C) 2012-2018-2018 Kana Natsuno
4 | " License: MIT license {{{
5 | " Permission is hereby granted, free of charge, to any person obtaining
6 | " a copy of this software and associated documentation files (the
7 | " "Software"), to deal in the Software without restriction, including
8 | " without limitation the rights to use, copy, modify, merge, publish,
9 | " distribute, sublicense, and/or sell copies of the Software, and to
10 | " permit persons to whom the Software is furnished to do so, subject to
11 | " the following conditions:
12 | "
13 | " The above copyright notice and this permission notice shall be included
14 | " in all copies or substantial portions of the Software.
15 | "
16 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | " OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | " IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | " CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | " TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | " SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 | " }}}
24 | " Naming guidelines "{{{1
25 | " Rules "{{{2
26 | "
27 | " "urule" stands for "Unnormalized RULE".
28 | " urules are rules written by users.
29 | " Optional items may be omitted from urules.
30 | "
31 | " "nrule" stands for "Normalized RULE".
32 | " nrules are rules completed with all optional items and internal items.
33 | "
34 | " "snrule" stands for "SemiNormalized RULE".
35 | " snrules are mostly same as nrules, the only one difference is that
36 | " "priority" items may be omitted from snrules.
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | " Variables "{{{1
46 | let s:available_nrules = [] "{{{2
47 | " :: [NRule] -- it is ALWAYS sorted by priority in descending order.
48 |
49 |
50 |
51 |
52 | "{{{2
53 |
54 |
55 |
56 |
57 | " Interface "{{{1
58 | function! smartinput#clear_rules() "{{{2
59 | let s:available_nrules = []
60 | endfunction
61 |
62 |
63 |
64 |
65 | function! smartinput#define_default_rules() "{{{2
66 | " urules "{{{
67 | let urules = {}
68 | let urules.names = []
69 | let urules.table = {}
70 | function! urules.add(name, urules)
71 | call add(self.names, a:name)
72 | let self.table[a:name] = a:urules
73 | endfunction
74 | if get(g:, 'smartinput_break_undo', 0) || v:version < 800
75 | let left = ''
76 | let right = ''
77 | else
78 | let left = 'U'
79 | let right = 'U'
80 | endif
81 | call urules.add('()', [
82 | \ {'at': '\%#', 'char': '(', 'input': '()'.left},
83 | \ {'at': '\%#\_s*)', 'char': ')', 'input': '=smartinput#_leave_block('')'')'.right},
84 | \ {'at': '(\%#)', 'char': '', 'input': ''},
85 | \ {'at': '()\%#', 'char': '', 'input': ''},
86 | \ {'at': '\\\%#', 'char': '(', 'input': '('},
87 | \ {'at': '(\%#)', 'char': '', 'input': '"_S'},
88 | \ ])
89 | call urules.add('[]', [
90 | \ {'at': '\%#', 'char': '[', 'input': '[]'.left},
91 | \ {'at': '\%#\_s*\]', 'char': ']', 'input': '=smartinput#_leave_block('']'')'.right},
92 | \ {'at': '\[\%#\]', 'char': '', 'input': ''},
93 | \ {'at': '\[\]\%#', 'char': '', 'input': ''},
94 | \ {'at': '\\\%#', 'char': '[', 'input': '['},
95 | \ ])
96 | call urules.add('{}', [
97 | \ {'at': '\%#', 'char': '{', 'input': '{}'.left},
98 | \ {'at': '\%#\_s*}', 'char': '}', 'input': '=smartinput#_leave_block(''}'')'.right},
99 | \ {'at': '{\%#}', 'char': '', 'input': ''},
100 | \ {'at': '{}\%#', 'char': '', 'input': ''},
101 | \ {'at': '\\\%#', 'char': '{', 'input': '{'},
102 | \ {'at': '{\%#}', 'char': '', 'input': '"_S'},
103 | \ ])
104 | call urules.add('''''', [
105 | \ {'at': '\%#', 'char': '''', 'input': ''''''.left},
106 | \ {'at': '\%#''\ze', 'char': '''', 'input': ''.right},
107 | \ {'at': '''\%#''', 'char': '', 'input': ''},
108 | \ {'at': '''''\%#', 'char': '', 'input': ''},
109 | \ {'at': '\\\%#\ze', 'char': '''', 'input': ''''},
110 | \ ])
111 | " Though strong quote is a useful feature and it is supported in several
112 | " languages, \ is usually used to escape next charcters in most languages.
113 | " So that rules for strong quote are written as additional ones for specific
114 | " 'filetype's which override the default behavior.
115 | call urules.add(''''' as strong quote', [
116 | \ {'at': '\%#''', 'char': '''', 'input': ''.right},
117 | \ ])
118 | call urules.add('''''''', [
119 | \ {'at': '''''\%#', 'char': '''', 'input': ''''''''''.left.left.left},
120 | \ {'at': '\%#''''''\ze', 'char': '''', 'input': ''.right.right.right},
121 | \ {'at': '''''''\%#''''''', 'char': '', 'input': ''},
122 | \ {'at': '''''''''''''\%#', 'char': '', 'input': ''},
123 | \ ])
124 | call urules.add('""', [
125 | \ {'at': '\%#', 'char': '"', 'input': '""'.left},
126 | \ {'at': '\%#"', 'char': '"', 'input': ''.right},
127 | \ {'at': '"\%#"', 'char': '', 'input': ''},
128 | \ {'at': '""\%#', 'char': '', 'input': ''},
129 | \ {'at': '\\\%#', 'char': '"', 'input': '"'},
130 | \ ])
131 | call urules.add('"""', [
132 | \ {'at': '""\%#', 'char': '"', 'input': '""""'.left.left.left},
133 | \ {'at': '\%#"""', 'char': '"', 'input': ''.right.right.right},
134 | \ {'at': '"""\%#"""', 'char': '', 'input': ''},
135 | \ {'at': '""""""\%#', 'char': '', 'input': ''},
136 | \ ])
137 | call urules.add('``', [
138 | \ {'at': '\%#', 'char': '`', 'input': '``'.left},
139 | \ {'at': '\%#`', 'char': '`', 'input': ''.right},
140 | \ {'at': '`\%#`', 'char': '', 'input': ''},
141 | \ {'at': '``\%#', 'char': '', 'input': ''},
142 | \ {'at': '\\\%#', 'char': '`', 'input': '`'},
143 | \ ])
144 | call urules.add('```', [
145 | \ {'at': '``\%#', 'char': '`', 'input': '````'.left.left.left},
146 | \ {'at': '\%#```', 'char': '`', 'input': ''.right.right.right},
147 | \ {'at': '```\%#```', 'char': '', 'input': ''},
148 | \ {'at': '``````\%#', 'char': '', 'input': ''},
149 | \ ])
150 | call urules.add('English', [
151 | \ {'at': '\w\%#', 'char': '''', 'input': ''''},
152 | \ ])
153 | call urules.add('Lisp quote', [
154 | \ {'at': '\%#', 'char': '''', 'input': ''''},
155 | \ {'at': '\%#', 'char': '''', 'input': ''''''.left.'',
156 | \ 'syntax': ['Constant']},
157 | \ ])
158 | " Unfortunately, the space beyond the end of a comment line is not
159 | " highlighted as 'Comment'. So that it is necessary to define one more rule
160 | " to cover the edge case with only 'at'.
161 | call urules.add('Python string', [
162 | \ {'at': '\v\c<([bu]|[bu]?r)>%#', 'char': '''', 'input': ''''''.left},
163 | \ {'at': '\v\c<([bu]|[bu]?r)>%#', 'char': '''', 'input': '''',
164 | \ 'syntax': ['Comment', 'Constant']},
165 | \ {'at': '\v\c\#.*<([bu]|[bu]?r)>%#$', 'char': '''', 'input': ''''},
166 | \ ])
167 | call urules.add('Vim script comment', [
168 | \ {'at': '^\s*\%#', 'char': '"', 'input': '"'},
169 | \ ])
170 | "}}}
171 |
172 | " ft_urule_sets_table... "{{{
173 | let ft_urule_sets_table = {
174 | \ '*': [
175 | \ urules.table['()'],
176 | \ urules.table['[]'],
177 | \ urules.table['{}'],
178 | \ urules.table[''''''],
179 | \ urules.table[''''''''],
180 | \ urules.table['""'],
181 | \ urules.table['"""'],
182 | \ urules.table['``'],
183 | \ urules.table['```'],
184 | \ urules.table['English'],
185 | \ ],
186 | \ 'clojure': [
187 | \ urules.table['Lisp quote'],
188 | \ ],
189 | \ 'csh': [
190 | \ urules.table[''''' as strong quote'],
191 | \ ],
192 | \ 'lisp': [
193 | \ urules.table['Lisp quote'],
194 | \ ],
195 | \ 'perl': [
196 | \ urules.table[''''' as strong quote'],
197 | \ ],
198 | \ 'python': [
199 | \ urules.table['Python string'],
200 | \ ],
201 | \ 'ruby': [
202 | \ urules.table[''''' as strong quote'],
203 | \ ],
204 | \ 'scheme': [
205 | \ urules.table['Lisp quote'],
206 | \ ],
207 | \ 'sh': [
208 | \ urules.table[''''' as strong quote'],
209 | \ ],
210 | \ 'tcsh': [
211 | \ urules.table[''''' as strong quote'],
212 | \ ],
213 | \ 'vim': [
214 | \ urules.table[''''' as strong quote'],
215 | \ urules.table['Vim script comment'],
216 | \ ],
217 | \ 'zsh': [
218 | \ urules.table[''''' as strong quote'],
219 | \ ],
220 | \ }
221 | "}}}
222 |
223 | for urule_set in ft_urule_sets_table['*']
224 | for urule in urule_set
225 | call smartinput#define_rule(urule)
226 | endfor
227 | endfor
228 |
229 | let overlaied_urules = {}
230 | let overlaied_urules.pairs = [] " [(URule, [FileType])]
231 | function! overlaied_urules.add(urule, ft)
232 | for [urule, fts] in self.pairs
233 | if urule is a:urule
234 | call add(fts, a:ft)
235 | return
236 | endif
237 | endfor
238 | call add(self.pairs, [a:urule, [a:ft]])
239 | endfunction
240 | for ft in filter(keys(ft_urule_sets_table), 'v:val != "*"')
241 | for urule_set in ft_urule_sets_table[ft]
242 | for urule in urule_set
243 | call overlaied_urules.add(urule, ft)
244 | endfor
245 | endfor
246 | endfor
247 | for [urule, fts] in overlaied_urules.pairs
248 | let completed_urule = copy(urule)
249 | let completed_urule.filetype = fts
250 | call smartinput#define_rule(completed_urule)
251 | endfor
252 |
253 | " Add more useful rules?
254 | endfunction
255 |
256 | function! s:_operator_key_from(operator_name)
257 | let k = a:operator_name
258 | let k = substitute(k, '\V<', '', 'g')
259 | let k = substitute(k, '\V|', '', 'g')
260 | return k
261 | endfunction
262 |
263 | function! s:_operator_pattern_from(operator_name)
264 | let k = a:operator_name
265 | return k
266 | endfunction
267 |
268 | function! smartinput#_leave_block(end_char)
269 | " NB: Originally was used to execute search(), but in
270 | " Visual-block Insert acts as if a, so visually selected lines will be
271 | " updated and the current mode will be shifted to Insert mode. It means
272 | " that there is no timing to execute a Normal mode command. Therefore we
273 | " have to use = instead.
274 | call search(a:end_char, 'cW')
275 | return ''
276 | endfunction
277 |
278 |
279 |
280 |
281 | function! smartinput#define_rule(urule) "{{{2
282 | let nrule = s:normalize_rule(a:urule)
283 | call s:insert_or_replace_a_rule(s:available_nrules, nrule)
284 | endfunction
285 |
286 |
287 |
288 |
289 | function! smartinput#map_to_trigger(mode, lhs, rhs_char, rhs_fallback) "{{{2
290 | " According to :help 'autoindent' --
291 | "
292 | " > Copy indent from current line when starting a new line
293 | " > (typing in Insert mode or when using the "o" or "O" command).
294 | " > If you do not type anything on the new line except or CTRL-D
295 | " > and then type , CTRL-O or , the indent is deleted again.
296 | "
297 | " So that a:rhs_fallback MUST be mapped from a:lhs without leaving from
298 | " Insert mode to keep the behavior on automatic indentation when
299 | " a:rhs_fallback == '',
300 | let char_expr = s:_encode_for_map_char_expr(a:rhs_char)
301 | let fallback_expr = s:_encode_for_map_char_expr(a:rhs_fallback)
302 | execute printf('%snoremap %s %s _trigger_or_fallback(%s, %s)',
303 | \ a:mode,
304 | \ '