Mastering-the-Vim-Language

参考 Youtube

1 Hello World

输出函数:

  • echo
  • echon
  • echomsg
  • echohl

注意 echon, 其将输出合并到前一个输出中.

echomsg 有 message history.

可用 :message 查看, 也可以用 :message clear 清除.

输出颜色用 echohl, 如:

1
2
3
4
5
6
7
8
echohl PmenuSel
echomsg "Hello"

echohl None
echon " "

echohl IncSearch
echon "World"

:highlight 来查看.

2 定义自己的 highlight group

如:

1
2
3
4
5
6
7
8
9
10
11
highlight hello guifg=red gui=bold, italic
highlight world guifg=lightblue gui=bold,italic

echohl hello
echomsg "Hello"

echohl None
echon " "

echohl world
echon "World"

属性可以查看 :h attr-list

3 变量

任何时候改变变量的值, 都需要 let.

可以用 exists() 函数来检查一个变量是否被声明.

设置一个 option 有两种写法, 如:

1
2
let &textwidth = 50
set textwidth = 50

4 if/elseif/else 语句

如:

1
2
3
4
5
6
7
8
9
10
11
12
let number = 1
let string = ""

if number > 0
let string = "positive value"
elseif number == 0
let string = "the value is 0"
else
let string = "negative value"
endif

echo string

5 真值和假值

1
2
3
4
let number = -1
let is_positive = number > 0

echo is_positive

除 0 以外的都是 true.

空字符串 是 false

6 数和浮点数

vimscript 中没有幂运算的 ** 符号, 其他的运算都有:

1
2
3
4
5
let plus     = 10 + 3
let minus = 10 - 3
let multiply = 10 * 3
let divide = 10 / 3
let modulo = 10 % 3

二元运算的一侧出现浮点数, 其值就会变为浮点数.

如:

1
let divide = 10.0 / 3

将 float 强制转换为 integer 可以用:

1
echo float2nr(divide)

7 数学运算

vimscript 没有自增和自减符号.

但有:

1
2
let number = 1
let number += 1

这类符号.

vimscript 有三元运算符 condition ? expression1 : expression2

如:

1
2
let number = 1
echo if number > 0 ? "positive" : "negative"

8 字符串

注意双引号和单引号的区别, 单引号中只有 ' 为特殊字符. (和其他语言有点不同)

如:

1
2
3
4
5
highlight blue guifg=LightBlue gui=bold,italic
echohl blue

let double = "I asked \"why?\""
let single = 'I''m fine'

在单引号中输出单引号用 ''

9 字符串连接

... 都可以.

1
2
3
let string = "hello"
let string .= " world"
echo string

也可以用 join 函数:

1
let string = join([string, "world"], "\n")

第二个参数为连接符号.

10 去除字符串中的空白字符

使用 trim 函数, 有两种形式.

使用一个参数:

1
2
let string = "   trim   "
echo trim(string)

三个参数:

1
2
3
echo trim("   trim   ", " ", 0)
echo trim(" trim ", " ", 1)
echo trim(" trim ", " ", 2)

第二个参数为要去除的字符 (不只是空白字符), 第三个参数决定去除的 side, 0 表示两边, 1 表示 beginning, 2 表示 ending.

第二个参数并不支持正则表达式.

11 提取子字符串

1
2
let string = "hello world"
echo string[0:4]

也就是切片. 可以为负数.

超出 index 则会返回空字符串.

也可以用 strcharpart 函数:

1
2
3
4
5
let string = "hello world"
echo string[0:4]

let result = strcharpart(string, 6, 2)
echo result

第二个参数是开始位置的 index, 第三个参数是要提取的长度.

12 list 的创建和获取

1
2
3
4
let list = [1, 2, 3, 4, 5]
echo list
echo list[-1]
let list[0] = 3

可以使用 get 函数来获取 list 的内容:

1
echo get(list, 2, 0)

13 list 的比较

数值数组用 ==

1
2
3
4
let list1 = [1, 2, 3]
let list2 = [1, 2, 3]

echo list1 == list2

字符串数组:

1
2
3
4
5
let simple = ['hello', 'salam']
let simple = ['Hello', 'Salam']

set ignorecase
echo simple == capital

14 提取子 list

1
2
let list = [0, 1, 2, 3, 4, 5]
let sublist = list[0:4]

可以用变量标识范围:

1
2
3
4
let start = 2
let end = 4
let sublist = list[start:end]
echo sublist

15 unpacking 一个 list

1
2
let list = [1, 2, 3, 4]
let [first, second, third, fourth] = list

16 探查 list

1
2
3
4
5
6
7
8
9
10
11
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]

echo len(numbers)

echo max(numbers)

echo min(numbers)

echo count(numbers, 0)

echo index(numbers, 9)

17 将一个 element appending to 一个 list

1
2
3
let first = [0, 1, 2]
call add(first, 3)
echo first

合并两个 list:

1
2
3
4
let second = [3, 4, 5]
let first = first + second
" or
call extend(first, second)

18 将一个 element prepending to 一个 list

1
2
3
let numbers = [7, 8, 9]

call insert(numbers, 6)

19 连接两个 lists

+extend:

1
2
3
4
5
6
let first  = [1, 2, 3]
let second = [4, 5, 6]

let plug = first + second

call extend(first, second)

20 根据 index 删除一个 list 中的元素

remove:

1
2
3
4
let numbers = ['zero', 'one', 'two']

call remove(numbers, 1, 2)
echo numbers

第二个参数为起始 index, 第三个为结束 index.

或者用 unlet:

1
2
3
4
let numbers = ['zero', 'one', 'two']

unlet numbers[1]
echo numbers

21 利用循环遍历 list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let list = [1, 2, 3, 4, 5]

let index = 0
while index < len(list)
let number = list[index]
echo number

let index += 1
endwhile

" or

for number in list
echo number
endfor

unpack 的例子:

1
2
3
4
5
6
7
8
let address = [
\['Ichinomiya', 'Aichi', 'Japan'],
\['Gombak', 'KL', 'Malaysia']
]

for [city, state, country] in address
echo city state country
endfor

22 获取一个范围

1
2
3
echo range(10)
echo range(1,10)
echo range(1,10,2)

23 复制一个 list

使用 copy

1
2
3
4
5
6
let original = ['outer1', ['inner1', 'inner2']]

let copy = copy(original)

echo original
echo copy

修改 copy 会影响原列表.

使用 deepcopy 则不会影响.

24 将 list 转换为 string

1
2
3
let list = [1, 2, 3, 4, 5]

let string = join(list, "\n")

25 过滤一个 list

使用 filter. 其会修改 list.

第二个参数是一个 string (包含筛选方式).

1
2
3
4
5
let list = range(-10,10)
echo list

call filter(list, 'v:var > 0')
echo list

第二个参数为 funcref:

1
2
3
4
5
6
function! Negative(key, value) abort
return a:value < 0
endfunc

call filter(list, function('Negative'))
echo list

26 排序一个 list

使用 sort.

1
2
3
4
let string = ['apple', 'orange', 'durian', 'mango', 'Banana', 'jackfruit']
let numbers = [5, 2, 1, 6, 3, 4]

echo sort(numbers)

可以查看第二个参数.

27 用自建函数来 sort list

同样需要传递 function reference.

1
2
3
4
5
6
7
8
9
10
let string = ['apple', 'orange', 'durian', 'mango', 'Banana', 'jackfruit']
let numbers = [5, 2, 1, 6, 3, 4]

function! Reverse(a, b) abort
return a:a - a:b
endfunc

let FuncR = function('Reverse')

echo sort(numbers, FuncR)

28 reverse 一个 list

使用 reverse.

1
2
3
4
let string = ['apple', 'orange', 'durian', 'mango', 'Banana', 'jackfruit']
let numbers = [5, 2, 1, 6, 3, 4]

call reverse(numbers)

29 移除 list 中重复的元素

使用 uniq:

1
2
3
4
5
6
7
8
let numbers = [1, 2, 3, 4, 4, 4, 5, 7, 8, 9]
let strings = ['orange', 'apple', 'mango', 'banana', 'orange']

call sort(numbers)
call uniq(numbers)

call sort(strings)
call uniq(strings)

需要先 sort.

30 创建字典

1
2
let uthman = { 'name': 'uthman', 'age': 30, }
echo uthman

值可以是变量.

在 hash 之前加上 # 则不需要添加引号:

1
let uthman = #{ name: name, age: age }

字典可以嵌套.

31 访问和修改字典

两种写法来访问:

1
2
3
let uthman = #{ name: name, age: age }
echo uthman['name']
echo uthman.name

添加或修改:

1
uthman.age = 10

32 删除一对键值对

使用 removeunlet:

1
2
3
let uthman = #{ name: name, age: age }
call remove(uthman, 'name')
call unlet uthman.age

33 合并两个字典

使用 extend:

1
2
3
4
let dict1 = #{ 1: '1 from dict1', 2: '2 from dict1', }
let dict2 = #{ 3: '3 from dict2', 4: '4 from dict1', }

call extend(dict1, dict2)

注意相同的 key 所在的键值对会被重写. (可以改变 extend 的第三个参数改变行为)

34 用循环遍历字典

keys 可以获取全部键名.

values 可以获取全部值.

items 获取全部 pairs.

35 过滤字典

使用 filter

1
2
3
4
let days = #{ 1: 'Sunday', 2: 'Monday', 3: 'Tuesday', 4: 'Wednesday', 5: 'Thursday', 6: 'Friday', 7: 'Saturday' }

call filter(days, 'v:key < 4')
call filter(days, 'v:value =~ "t"')

同样可以传递 function reference.

36 将字典视为对象

1
2
3
4
5
6
7
8
9
10
11
let person = #{
\name: 'uthman',
\age: 30,
\introduce: function('Introduce')
\}

function Introduce() abort
echo "My name is" self.name
endfunc

call person.introduce()

也可以写为:

1
2
3
4
5
6
7
8
9
10
let person = #{
\name: 'uthman',
\age: 30,
\}

function person.introduce() abort
echo "My name is" self.name "from person.introduce()"
endfunc

call person.introduce()

37 使用 map() 处理 list 和 dict

同样可以传入 function reference 或 使用 lambda 函数:

1
2
3
let numbers = range(1,100)

call map(numbers, { index, value -> value * value })

function reference 会自动被传入参数, 如果是 list 就是 index 和 value, dict 就是 key 和 value:

1
2
3
4
5
function! WithIndex(key, value) abort
return a:key . ' = ' . a:value
endfunc

call map(list, function('WithIndex'))

38 定义和调用函数

函数名首字母需大写.

定义和调用如:

1
2
3
4
5
function Greet()
echo "Hello"
endfunc

call Greet()

override 之前的函数可以加上 !:

1
2
3
4
5
function! Greet()
echo "Hello"
endfunc

call Greet()

查看一个函数在哪里定义的可以用 :verbose, 如 :verbose function Greet.

传递和使用参数 (注意 a:):

1
2
3
4
5
function! Greet(name)
echo "Hello" a:name
endfunc

call Greet('Jie')

可以设置默认参数值:

1
2
3
4
5
function! Greet(name = 'bro')
echo "Hello" a:name
endfunc

call Greet()

39 函数有不定数量的参数

..., 访问时用 a:000:

1
2
3
4
5
6
function Basket(...)
echo a:000
echo a:000[0]
endfunc

call Basket('apple', 'banana', 'orange')

可以写为:

1
2
3
4
5
6
function Basket(...)
let args = a:000
echo args
endfunc

call Basket('apple', 'banana', 'orange')

同时 a:1 也表示第一个参数.

40 函数引用

函数引用的变量也需要首字母大写.

使用 function 来获取引用:

1
2
3
4
5
6
7
8
function Basket(...)
let args = a:000
echo args
endfunc

let FuncR = function('Basket')

call FuncR

41 lambda 表达式

本质上和 function reference 类似.

格式:

1
2
let Add = { num1, num2 -> num1 + num2 }
echo Add(5, 5)

前面的 num1, num2 表示有两个参数, num1 + num2 表示函数体.

注意, lambda 表达式中只能包含表达式, 不能包含命令.

42 local 作用域

添加 s: (script):

1
2
3
4
5
function! s:hello() abort
echo "Hello"
endfunc

call s:hello()

43 在 normal 中查看 function

输入 :function <TAB>

或者 :function! <TAB>

或者 :verbose function! <TAB>

<SID>:scriptnames 的输出相关.

44 Buffer, 获取行号

使用 line 函数.

显示当前行的行号:

1
echo line('.')

显示当前文件最后一行的行号:

1
echo line('$')

显示当前 window 的第一行

1
echo line('w0')

显示当前 window 的最后一行

1
echo line('w$')

显示某一个 mark 的行号:

1
echo line("'a")

45 提取当前 Buffer 的行

使用 getline, 其参数和 line 的使用一样.

1
2
3
4
5
6
let line = getline('.')
echo line

if line == ''
echo 'empty string'
endif

两个参数的形式, 获取一个范围的行:

1
2
3
4
5
let lines = getline(1, 3)

for i in lines
echo i
endfor

46 替换当前 Buffer 里的行

使用 setline

1
call setline(1, "  haha")

替换一个范围的行, 传递一个 list:

1
call setline(1, ['line 1', 'line 2', 'line 3'])

47 向当前 Buffer 添加行

使用 append:

1
call append('.', ' append line')

同样可以添加几行:

1
call append($, ['line 1', 'line 2', 'line 3'])

48 在不打开一个 Buffer 的情况下创建和删除 Buffer

创建一个新的 buffer, 用 :badd name (buffer add),

1
badd new

加载一个 buffer, 用 bufload

1
call bufload('new')

bufloaded 来判断一个 buffer 是否被 loaded.

setbufline 以及 appendbufline.

如:

1
2
3
let lines = [ '# title: new file', '', 'the content here!', '', '# end of file' ]
call setbufline('new', 1, lines)
call appendbufline('new', 4, [ 'some more content', '' ])

读取 buffer 内容:

1
echo getbufline('new', 3, '$')

删除 buffer 中的内容:

1
call deletebufline('new', 2, 4)

49 创建 prompt buffer

1
2
3
4
5
6
7
8
9
10
function! TextEntered(text) abort
echo "You entered: " a:text
endfunc

new
set buftype=prompt
let buf = bufnr()
call prompt_setcallback(buf, function("TextEntered"))

nnoremap <buffer> <silent> <Escape> :bw!<CR>

关于 prompt buffer

在 Vim 编辑器中,set buftype=prompt 命令将当前缓冲区的类型设置为 prompt。这将告诉 Vim 编辑器将当前缓冲区视为命令行提示符,而不是普通的文本文件或其他类型的缓冲区。

具体而言,set buftype=prompt 命令的作用包括:

  1. 将当前缓冲区标记为命令行提示符缓冲区,这意味着 Vim 将使用特定的方式处理此缓冲区。例如,不会保存文件,不会使用自动缩进等功能,不会进行撤销操作等。

  2. 允许 Vim 将当前缓冲区作为交互式 shell 的输入和输出,这可以让你在 Vim 中直接运行命令并查看命令输出,就像在命令行界面中一样。

  3. 允许 Vim 将当前缓冲区作为外部程序的输入和输出。这对于与其他程序的集成非常有用,例如使用 Vim 作为 Git 编辑器时,Git 可以将 Git 命令的输出发送到 Vim 的命令行提示符缓冲区,并在其中接收用户输入。

总的来说,使用 set buftype=prompt 命令将缓冲区类型设置为命令行提示符可以让 Vim 在某些情况下更加灵活和强大。

50 append text 到 prompt buffer

1
2
3
4
5
6
7
8
9
10
11
12
function! TextEntered(text) abort

call append(line('$') - 1, "You entered: " . a:text)

endfunc

new
set buftype=prompt
let buf = bufnr()
call prompt_setcallback(buf, function("TextEntered"))

nnoremap <buffer> <silent> <Escape> :bw!<CR>

51 制作终端模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function! s:on_stdout(id, data, event) dict
let str = join(a:data, "\n")
echo str

call append(line('$') - 1, a:data)
endfunc

let opts = #{ on_stdout: function('s:on_stdout') }

let g:job_id = jobstart(['/bin/sh'], opts)

function! s:callback(input) abort
call chansend(g:job_id, [a:input, ''])
endfunc

new
set buftype=prompt
call prompt_setcallback(bufnr(), function('s:callback'))
nnoremap <buffer> <silent> <Escape> :bw!<CR>
startinsert

52 面向对象编程

构造器:

1
2
3
4
5
6
7
8
let person = {}

function! person.init(name, age) abort
let object = { name: a:name, age: a:age }
return object
endfunc

let uthman = person.init('uthman', 30)

53 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
let person = {}

function! person.init(name, age) abort
let object = { name: a:name, age: a:age }
function! object.introduce() abort
echo "My name is" self.name
echo "I am" self.age "years old"
echo "Nice to meet you"
endfunc
return object
endfunc

let uthman = person.init('uthman', 30)

54 修改对象属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let person = {}

function! person.init(name, age) abort
let object = { name: a:name, age: a:age }
function! object.introduce() abort
echo "My name is" self.name
echo "I am" self.age "years old"
echo "Nice to meet you"
endfunc

function! object.aged(years = 1) abort
let self.age += a:years
endfunc

return object
endfunc

let uthman = person.init('uthman', 30)

Mastering-the-Vim-Language
http://example.com/2023/03/10/Mastering-the-Vim-Language/
作者
Jie
发布于
2023年3月10日
许可协议