Moy
1
嗨,推荐一下我写的 Easy Copy 插件,它的作用是:
根据当前编辑光标所在位置,智能复制不同内容。
基础功能
举例来说,如果你指针在一个 inline code
内部,它能复制内部的代码文本(不带两边的符号),这在某些时候——例如复制函数名称——会非常有用。
同理,对于高亮、加粗、斜体……等等格式文本,它都能干净地复制出格式符号内部的文本。

对于像是 [text](url)
这样的链接,它也能根据光标位置来复制链接的标题或是链接 URL。
复制笔记链接
除此之外,它还提供了快速复制笔记链接的功能。
例如,当你的光标放在 ## 标题
行的时候,它能直接复制出 [[笔记#标题|标题]]
格式的链接,这个链接可以直接跳转到对应标题。
并且它使用标题文本作为显示文本,因此比起原始标题链接会简洁很多。
如果你经常使用「块链接」,那你还可以在设置中启用“自动生成块 ID”,这样一来,如果光标位置没有任何内容可以复制,它将自动创建一个该段落的块 ID 并复制指向本段的链接。

用法
虽然看起来很复杂,但是它用起来很简单——事实上,它只有一个命令: 智能复制
。
不管你想复制什么内容,只要把光标放在对应位置,执行这个命令,就行了。
(我更建议给这个命令分配一个快捷键,这样会更方便)
如果你想的话,你也可以用右键菜单的“智能复制”来执行操作,这样可以把“放置光标”和“复制内容”两个步骤二合一。
安装
插件已经上架插件商城,搜索 Easy Copy
即可安装。
你也可以前往 Github 查看:Moyf/easy-copy
(如果喜欢的话,欢迎点右上角的星星支持,感谢!)
3 个赞
能否快捷复制当前段落或者当前句子。这个需求我需求挺多的。
Moy
3
默认支持来着,什么文本都不选中的情况下按 Ctrl+C 就是复制当前行。
是不是可以替代原始的ctrl +c了,如果选中一段文本,那应该就是不做处理?
Moy
5
我自己是分配给 Ctrl+Alt+C
的,因为原生的 Ctrl+C (就像上面那个回复所说)本身也具有一定的自适应复制功能。
想要完全替代 Ctrl+C 意味着我还得再多写一些功能,但我懒x
正经说的话,我会尽量不用“低频使用”的特性去覆盖或影响“高频使用”的特性;
而且这个插件的执行依赖 Editor 上下文,如果用它覆盖 Ctrl+C
可能导致在一些奇奇怪怪的地方(比如设置界面)的复制失效,所以也不建议这么做。
1 个赞
Moy
6
更新:
1.2.1 支持自动提取文本作为 Block Link 的显示文本

太有用了,尤其是在手机端,操作方便很多
双链还不支持复制?
代码块、callout 之类的也还不行
YYoung
(YYYoung)
10
作者您好,快速复制的功能我也做过,只不过是局限于使用 LaTeX Suite 去写的一两个触发器,并且代码逻辑显得很丑陋。
因为个人使用情况上,对于粗体、斜体等的使用不算很多,因此没太关注这些形式;因为本人有些周刊记录各种信息的习惯,因此更注意的是链接、$\LaTeX$ 公式和 inline code 这几种形式。
purelink
[text](link)
[[inline link]]
$...$
inline code
触发的条件就是选中文字,在英文输入状态下敲击 c
即可实现复制。
// target => purelink | textlink | inline latex | inline code | inline link
{trigger: "c", replacement: (sel) => {
if (sel = sel.replaceAll(/\$(\d.*?)\$/g, "\${{ }}\$$")) ; // sensitive_math
// regex
let pure_links = /(?<emoji>(?:📌|📜|🌐)(?:rel|ref|via)\: )?(?<target>(?<!\[(.*)\]\()(?:https?|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|])/g;
let text_links = /(?<target>(?<emoji>(?:📌|📜|🌐)(?:rel|ref|via)\: )?(?<!!)\[(?<text>.*?)\]\((?<link>.*?)\))/g;
let inli_links = /(?<target>(?<emoji>(?:📌|📜|🌐)(?:rel|ref|via)\: )?\[\[([^\]]*?)\]\])/g;
let inli_LaTeX = /(?<target>\$.*?\$)/g;
let inli_codes = /(?<target>`.*?`)/g;
// matches & len
let pl_matches = Array.from(sel.matchAll(pure_links)) || []; // 1️⃣pure links
let tl_matches = Array.from(sel.matchAll(text_links)) || []; // 2️⃣text links
let il_matches = Array.from(sel.matchAll(inli_links)) || []; // 3️⃣inline links
let iT_matches = Array.from(sel.matchAll(inli_LaTeX)) || []; // 4️⃣inline LaTeX
let ic_matches = Array.from(sel.matchAll(inli_codes)) || []; // 5️⃣inline codes
// condition & output
let condition = (
pl_matches.length * 10000 +
tl_matches.length * 1000 +
il_matches.length * 100 +
iT_matches.length * 10 +
ic_matches.length * 1
);
let output = ``;
// 分情况讨论
switch(condition) {
// 没有 target
case 0: { break; }
// 一个 target -> 直接复制
case 10000: { for(let match of pl_matches) output += match.groups.target; break; } // 1️⃣pure links
case 1000: { for(let match of tl_matches) output += match.groups.target + ` `; break; } // 2️⃣text links
case 100: { for(let match of il_matches) output += match.groups.target; break; } // 3️⃣inline links
case 10: { for(let match of iT_matches) output += match.groups.target + ` `; break; } // 4️⃣inline LaTeX
case 1: { for(let match of ic_matches) output += match.groups.target + ` `; break; } // 5️⃣inline codes
// 多个 targets -> 按照自定义的格式复制
default: {
// 1️⃣pure links
for(let match of pl_matches) output += (
`${match.groups.emoji?match.groups.emoji:``}` +
`${match.groups.target}\n`
);
if(pl_matches.length > 0) output += `\n`;
// 2️⃣text links
for(let match of tl_matches) output += (
`- ${match.groups.emoji?match.groups.emoji:``}` +
`${match.groups.text} | ${match.groups.link}\n`
);
if(tl_matches.length > 0) output += `\n`;
// 3️⃣inline links
for(let match of il_matches) output += (
`${match.groups.emoji?match.groups.emoji:``}` +
`${match.groups.target}\n`
);
if(il_matches.length > 0) output += `\n`;
// 4️⃣inline LaTeX
for(let match of iT_matches) output += `${match.groups.target} \n`;
if(iT_matches.length > 0) output += `\n`;
// 5️⃣inline codes
for(let match of ic_matches) output += `${match.groups.target} \n`;
if(ic_matches.length > 0) output += `\n`;
}
}
// 如果在 sel 中成功匹配内容,则 [粘贴内容到剪贴板]
const { clipboard } = require('electron');
if (output != "") { clipboard.writeText(output); }
// sel 不改动
return sel;
}, options: "vt"},
另外,我认为对于 [text](link)
链接的复制可以有更多思考,对于 [text](link)
这种形式的链接我觉得可以有几种快速复制方式:
假定经常使用的是 [text](link)
格式,即 )
后面固定添加一个空格,那么可以在空格后面添加英文字母来行车触发器。
- 复制
text
- 复制
link
- 复制
[text](link)
- 同时还能快速访问这个链接
// 复制粘贴 & 访问
// c = copy link
// x|t = copy text
// f = copy full [text](link)
// v = visit link
// 1️⃣`[text](link) `
{trigger: /(?<textlink>\[(?<text>[^\]]*?)\]\((?<link>[^\]]*?)\)) (?<trigger>c|x|t|f|v)/g, replacement: (match) => {
const { clipboard } = require('electron');
switch(match.groups.trigger) {
case "x":
case "t": { clipboard.writeText(match.groups.text); break; } // copy `text`
case "c": { clipboard.writeText(match.groups.link); break; } // copy `link`
case "f": { clipboard.writeText(`[${match.groups.text}](${match.groups.link}) `); break; } // copy `[text](link) `
case "v": { window.open(match.groups.link); break; } // visit `link`
}
return `${match.groups.textlink} `; // return `link`
}, options: "rtA"},
以上是我个人的一点想法。
不得不说写这种代码就不可避免地和丑陋的正则表达式打交道。
1 个赞
Moy
11
WOW 感谢这么详尽的分享!
关于链接
其实我也觉得默认情况下的链接操作比较多样而且复杂。
尤其是我用了 隐藏链接文本的CSS 去用 emoji 替换过长的链接文本。
因此,前两天写了个新的脚本 【脚本】快速编辑链接文本
用法是:
- 在链接后面的文本时:跳转到
](
的前面,也就是链接文本的最后一个字后方
- 在链接文本内时:全选链接文本
- 已经选中链接文本时:选中当前整行
我一般不会改动 URL 内容,所以直接跳转编辑文本会用到比较多。
(其实也可以再拓展一下,第三步改成“选中完整链接”,第四步才是选中当前行)
然后 Easy Copy 插件本身做了一些区分处理,会根据光标在 ()
内还是 []
内去分别复制链接的文本或 URL。
两者结合起来可以「复制URL」或者「先跳转到链接文本处,然后复制链接文本」。
然后只要在链接内(任意位置,不管是链接文本还是 URL),按 Alt+Enter
可以打开——这个是原生快捷键。
不过鉴于这些都是特定的快捷键,实际操作起来也会感觉还挺麻烦的,哈哈。
关于 LatexSuite
我用了很多的 LatexSuite 转换,但是从来没想过用在「选中文本的情况」!
感谢开拓思路 
trigger
设置为 c
在打字输入字母c的时候会有干扰吗?
(正则丑归丑,用到它的时候也是真的不可或缺
)
关于复制
另外,FYI,Obsidian 里有一个现成的函数 navigator.clipboard.writeText(inlineCode)
用来设置剪贴板。
我不太确定 require('obsidian')
会不会有额外的性能负担 
可能对于重复引用的包体会有优化!总之如果没有感觉卡顿的话应该也没必要替换~
1 个赞
Moy
12
噢对,对于列出来的这几种常用格式:
[text](link)

[[inline link]]
$...$

inline code

这三种在 EasyCopy 内也支持!把光标放在对应文本内(不用选中)就可以复制内部的内容。
双链的话也想问一下,复制双链的情况多吗,一般是什么时候会用到?
看了一下,你的代码是直接从选中文本中进行特定提取,那其实只要大概框选一下范围就行,从另一个层面来说也会更方便!
顺便分享一下,EasyTyping 插件提供了一个选项,可以让 Ctrl+A
也变成分段操作:第一次选中当前行,第二次按选中当前段落(连续行),第三次选中全文。配合起来的话会很丝滑
1 个赞
YYoung
(YYYoung)
13
你写的 【实用技巧】如何改善笔记的链接编辑体验 帖子,感觉创意真的不错!我去学习一下。
1 个赞
YYoung
(YYYoung)
14
关于 c
这种 Visual
触发器
对于输入打字的情况,c
触发器不会有干扰,因为在 options
中有 "v"
即 Visual
模式,因此只有在选中文字的时候才会触发这段代码条。
不过对于 LaTeX Suite 的 Visual
模式的触发器,个人有几条锐评:
- 个人的使用上会尽量避免「选中文本 + 键入替换文本」的操作,一般是直接用
Ctrl + BackSpace/Delete
去删除,再加上使用自定义的「删除行」「删除到行首」「删除到行尾」的快捷键去操作。因此真的需要「选中文本 + 键入替换文本」的时候就是真的需要需要执行 Visual
代码条。
- 对于中文用户来说有一个尴尬的点,每次执行
Visual
代码条的时候都需要确定输入法的状态。比如:选中一段网址,然后输入 c
想要获取选中文字中的某些字符串,但是因为处于中文输入法,这个时候会导致选中文字被替换成「吃」
。因此优先使用 0-9
来当做出发键位。
- 我写这几个 snippets 的背景是我经常「左手放在键盘上,右手放在鼠标上」,因此触发字符都是“左利手键位”。如果要是学会了 vim 相关的技能,那可以再进一步优化相关自己写的触发器。(总之就是触发器的设计充满需要长期磨合的个性)
- 使用正则属于是下下策,因为只要存在一个基础变换的改动,基本上要微调很久。。。
另外提一个算是我的“遗留问题”,可能你的这个插件应该不会遇到,我觉得这个是 LaTeX Suite 插件的问题。如果以后要开发「选中文本的情况」:
如果选中的文字中存在如 $1
这种 $<number>
字符串,至少在 LaTeX Suite 插件中,都会被当做占位符看待
,因为我肯定需要使用正则去匹配 $\LaTeX$ 行内字符串。
至少对于不精通 JavaScript 的我来说,这个问题,目前我目前的想法是,如果存在这样的 “sensitive math”,那就把 $1$
先变成 ${{ 1 }}$
,至少在最后效果呈现上没有区别。
YYoung
(YYYoung)
15
关于链接的一个别的想法
关于链接,我也一般不会改动 text
,顶多是对于那些不怎么遵守“中英文之间添加空格”的行为有些极度反感罢了,因此对于这种情况,我希望的是在我创建 [text](link)
的时候就解决掉。
比如说:
这个页面原文的标题,中英文不加空格。创建链接的一般操作是:
- 复制
text
,
- 返回到 Obsidian 中,粘贴,(快捷键)调整空格;
- 返回到浏览器,复制
link
;
- 返回到 Obsidian 中,选中
text
,粘贴得到 [text](link)
。
但是我能不能直接:
- 复制
text
和 link
(次序争取可以不用管)
- 回到 Obsidian,敲击
lll
或者 lkk
直接触发 [text](link)
呢?
也就是通过触发触发器,帮我:
- 自动识别历史剪贴板中第一个和第二个内容,其中一个一定是
purelink
,即 https://...
,另外一个是 text
;
- 将
text
做文本微调,即 ([\\u4E00-\\u9FA5])([A-Za-z0-9])
和 ([A-Za-z0-9])([\\u4E00-\\u9FA5])
replaceAll 为 $0 $1
。
- 还可以实现一些附加功能,比如添加一个
from
字段,根据 link
的来源,switch case 不同的平台后缀 Bilibili
YouTube
知乎
Quora
微信
等。
- 最终输出
[text - from](link)
即可。
后面几点都不算难,问题其实是第一点:[在 LaTeX Suite 插件中,如何通过 Javascript,并借助历史剪贴板,快速实现创建 text 形式的链接?](在 LaTeX Suite 插件中,如何通过 Javascript,并借助历史剪贴板,快速实现创建 [text](link) 形式的链接?)。
最后的这一段代码很神奇,有的时候是可行的,有的时候是不行的。
我不怎么了解 JavaScript 相关编程语言,或者可以说我是因为使用 Obsidian 才开始学习使用 JavaScript,因此不知道错误在哪里。
关于 require
我一开始使用的也是 navigator.clipboard
,后来是在尝试实现上面这个需求的时候根据 AI 的回答,用了 const { clipboard } = require('electron');
和一系列代码,只不过没有成功,把相关代码删了之后然后忘记改回来了。之后就没有管了,还是老老实实地常规操作处理,不想太折腾了,哈哈。
Moy
16
不知道你是不是 Windows 平台,如果是的话,我写过一个 Quicker 软件的脚本,专门用来快速粘贴链接,流程是:
- 在浏览器里切换到对应标签页
- 回到 Obsidian 里,按快捷键(我用的
Alt+V
)粘贴 [text](link)
格式的文本
就这两个步骤,而且 Quicker 动作里还会自动处理一些常见的格式(比如去掉某些网站自带的后缀,或者替换 #
之类的字符)

有兴趣的话之后可以写篇单独的文章介绍一下。
此前在 OB 内调用 QK 文章里有简单提及。
Quicker 动作: 视频笔记 - by Moy
1 个赞
Moy
17
原来如此!之前对 visual
模式啥的都不是很了解,原来是这个用处hhh
输入法的状态确实,有一些小的应用(比如 ImTip)可以实时在输入指针的位置显示输入法状态:

不过来回切换还是很麻烦。
而且在使用 VIM 时,这个情况也不会改善……甚至会更加麻烦 (:з」∠)
中文输入语境基本就很难舒服地用这类“命令”了。
然后关于「选中字符」,在我上一个回复里提到的「粘贴链接」的应用里其实也有遇到:
我希望在选中字符的情况下,直接把链接粘贴进这个文本 ( [选中文本](link)
)
这个时候是通过 ShellCommands 插件把选中的文本作为参数发送给 Quicker 动作,然后 Quicker 动作读取到之后,根据它来替换生成的链接中的 text
部分。
你现在的这个做法其实就是挺常用的方案(有点类似转义字符),先替换成别的字符,然后处理完再替换回来,没啥问题~
Moy
18
1 个赞
多谢,已加入手机上的工具栏。还有个小问题,没有对应的图标,只是个问号