emacs按行递增数字

背景

写demo代码时经常遇到递增的数据,大批量复制效率不高.
比如

1
2
3
4
5
6
7
8
{
"items" : [
{"a": "name1", "b": "label1"},
{"a": "name1", "b": "label1"},
{"a": "name1", "b": "label1"},
{"a": "name1", "b": "label1"}
]
}

希望变成

1
2
3
4
5
6
7
8
{
"items" : [
{"a": "name1", "b": "label1"},
{"a": "name2", "b": "label2"},
{"a": "name3", "b": "label3"},
{"a": "name4", "b": "label4"}
]
}

法一:rectangle-number-lines

在矩形操作里面的一个函数,负责输出被选择的行的行数,从1开始.
选定矩形区域后直接使用函数(或者默认快捷键为 C-x r N),会在 被选择的位置 之前生成行号.
因此需要

  1. 先删除原先的数字
  2. 所选择行的列长度为0,单独处理数字

不是专业用来递增数字的,因此有一些缺点

  1. 只能从1开始
  2. 数字后有空格,需要另外删除
  3. 序号有多位时,不足的位会在左边补空格
  4. 对于不在一行的数据无能为力

比如一个比较糟糕的结果:
先列选择数字,而后使用函数
在被列选择的位置之前出现了(补位用空格+序号+间隔用空格)

1
2
3
4
5
6
7
8
{
"items" : [
{"a": "name 9 1", "b": "label1"},
{"a": "name10 1", "b": "label1"},
{"a": "name11 1", "b": "label1"},
{"a": "name12 1", "b": "label1"}
]
}

改进

单纯使用 rectangle-number-lines 事实上只能使用该函数的默认参数值.
比如

  1. 从1开始
  2. 格式为 %1d, %2d%3d 等,视总行数而定.

可以在原方法基础上,在调用函数前使用 C-u,然后可以交互式输入参数.
spacemacs默认配置下,目前的开始快捷键是 C-u C-x r N

法二:运算替换

标准版

选定要替换的范围后,使用 replace-regexp,
将特定记号,比如 @, 原先已经是数字,可以替换 [0-9]+
替换为 \,(- (line-number-at-pos (point)) 开始行号).

其中

  1. \, 表示之后会跟一个lisp表达式,需要求值
  2. 有些地方会写 (1- xxx), 等效于 (- xxx 1)

优点:

  1. 快速方便,一次性替换
  2. 基于替换和函数,灵活度高
    1. 内容有更多自定义空间,替换前可以用记号,替换后数字也可以继续加工
    2. 可以使用 query-replace-regexp 来逐个修改

缺点:

  1. 使用麻烦,需要封装
    1. 过度依赖行号,一來对多行模板无效,二来还需要查看当前行号
    2. 书写复杂,容易出错
  2. 基于替换,有可能误伤
  3. 对lisp知识有一定的要求

极简但清新

替换特定记号且不限于数字,比如 @, 为 \#,
其中 \# 代表发现该记号的次数,从0开始.

简单场景下效率不错

  1. 循环体中的数字不多,一行中有一两个数字
  2. 从一个很小的数字开始,比如1或0

比如将

1
2
3
4
5
6
7
8
{
"items": [
{"a": "name@", "b": "label!"},
{"a": "name@", "b": "label!"},
{"a": "name@", "b": "label!"},
{"a": "name@", "b": "label!"}
]
}

通过执行独立的两批替换(主要为了防止多个字段交叉影响序号),
容易得变成

1
2
3
4
5
6
7
8
{
"items": [
{"a": "name0", "b": "label0"},
{"a": "name1", "b": "label1"},
{"a": "name2", "b": "label2"},
{"a": "name3", "b": "label3"}
]
}

优点

  1. 不受行号影响,可以分布在多行
  2. 使用占位符的方式,思维清晰

参考

  1. 法一法二
  2. 法一改进
  3. 小清新替换