【插件推荐】增强复制功能的插件:Easy Copy

嗨,推荐一下我写的 Easy Copy 插件,它的作用是:
根据当前编辑光标所在位置,智能复制不同内容

基础功能

举例来说,如果你指针在一个 inline code 内部,它能复制内部的代码文本(不带两边的符号),这在某些时候——例如复制函数名称——会非常有用。
同理,对于高亮、加粗、斜体……等等格式文本,它都能干净地复制出格式符号内部的文本。

demo-copy

对于像是 [text](url) 这样的链接,它也能根据光标位置来复制链接的标题或是链接 URL。

复制笔记链接

除此之外,它还提供了快速复制笔记链接的功能。

例如,当你的光标放在 ## 标题 行的时候,它能直接复制出 [[笔记#标题|标题]] 格式的链接,这个链接可以直接跳转到对应标题。
并且它使用标题文本作为显示文本,因此比起原始标题链接会简洁很多。

如果你经常使用「块链接」,那你还可以在设置中启用“自动生成块 ID”,这样一来,如果光标位置没有任何内容可以复制,它将自动创建一个该段落的块 ID 并复制指向本段的链接。

auto-generate-block-id

用法

虽然看起来很复杂,但是它用起来很简单——事实上,它只有一个命令: 智能复制

不管你想复制什么内容,只要把光标放在对应位置,执行这个命令,就行了。
(我更建议给这个命令分配一个快捷键,这样会更方便)

如果你想的话,你也可以用右键菜单的“智能复制”来执行操作,这样可以把“放置光标”和“复制内容”两个步骤二合一。

安装

插件已经上架插件商城,搜索 Easy Copy 即可安装。

你也可以前往 Github 查看:Moyf/easy-copy
(如果喜欢的话,欢迎点右上角的星星支持,感谢!)

3 个赞

能否快捷复制当前段落或者当前句子。这个需求我需求挺多的。

默认支持来着,什么文本都不选中的情况下按 Ctrl+C 就是复制当前行。

是不是可以替代原始的ctrl +c了,如果选中一段文本,那应该就是不做处理?

我自己是分配给 Ctrl+Alt+C 的,因为原生的 Ctrl+C (就像上面那个回复所说)本身也具有一定的自适应复制功能。

想要完全替代 Ctrl+C 意味着我还得再多写一些功能,但我懒x

正经说的话,我会尽量不用“低频使用”的特性去覆盖或影响“高频使用”的特性;
而且这个插件的执行依赖 Editor 上下文,如果用它覆盖 Ctrl+C 可能导致在一些奇奇怪怪的地方(比如设置界面)的复制失效,所以也不建议这么做。

1 个赞

更新:
1.2.1 支持自动提取文本作为 Block Link 的显示文本

1e226a7a-dd64-45e8-ab7f-0b6dc1afa603

line commands

太有用了,尤其是在手机端,操作方便很多
双链还不支持复制?
代码块、callout 之类的也还不行

  • 双链:[[链接]] 这种?暂时没想到需要用到的场景 。插件本身就是为了复制出“不带两侧符号”的文本,双链的话这样处理有点怪怪的 :thinking:
  • 代码块:可以直接点击代码块右上角的语言标记来进行复制,这个似乎会更方便?
  • Callout:这个应该可以,回头我加一下 (不过启用之后就没法复制指向 Callout 的 Block Link 了,这两个行为互斥)

作者您好,快速复制的功能我也做过,只不过是局限于使用 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 个赞

WOW 感谢这么详尽的分享!

关于链接

其实我也觉得默认情况下的链接操作比较多样而且复杂。
尤其是我用了 隐藏链接文本的CSS 去用 emoji 替换过长的链接文本。

因此,前两天写了个新的脚本 【脚本】快速编辑链接文本

用法是:

  • 在链接后面的文本时:跳转到 ]( 的前面,也就是链接文本的最后一个字后方
  • 在链接文本内时:全选链接文本
  • 已经选中链接文本时:选中当前整行

我一般不会改动 URL 内容,所以直接跳转编辑文本会用到比较多。

(其实也可以再拓展一下,第三步改成“选中完整链接”,第四步才是选中当前行)

然后 Easy Copy 插件本身做了一些区分处理,会根据光标在 () 内还是 [] 内去分别复制链接的文本或 URL。
两者结合起来可以「复制URL」或者「先跳转到链接文本处,然后复制链接文本」。

然后只要在链接内(任意位置,不管是链接文本还是 URL),按 Alt+Enter 可以打开——这个是原生快捷键。

不过鉴于这些都是特定的快捷键,实际操作起来也会感觉还挺麻烦的,哈哈。

关于 LatexSuite

我用了很多的 LatexSuite 转换,但是从来没想过用在「选中文本的情况」!
感谢开拓思路 :handshake:t2:

trigger 设置为 c 在打字输入字母c的时候会有干扰吗?

(正则丑归丑,用到它的时候也是真的不可或缺 :joy:

关于复制

另外,FYI,Obsidian 里有一个现成的函数 navigator.clipboard.writeText(inlineCode) 用来设置剪贴板。

我不太确定 require('obsidian') 会不会有额外的性能负担 :thinking:
可能对于重复引用的包体会有优化!总之如果没有感觉卡顿的话应该也没必要替换~

1 个赞

噢对,对于列出来的这几种常用格式:

  • [text](link) :white_check_mark:
  • [[inline link]]
  • $...$ :white_check_mark:
  • inline code :white_check_mark:

这三种在 EasyCopy 内也支持!把光标放在对应文本内(不用选中)就可以复制内部的内容。

双链的话也想问一下,复制双链的情况多吗,一般是什么时候会用到?


看了一下,你的代码是直接从选中文本中进行特定提取,那其实只要大概框选一下范围就行,从另一个层面来说也会更方便!

顺便分享一下,EasyTyping 插件提供了一个选项,可以让 Ctrl+A 也变成分段操作:第一次选中当前行,第二次按选中当前段落(连续行),第三次选中全文。配合起来的话会很丝滑

1 个赞

你写的 【实用技巧】如何改善笔记的链接编辑体验 帖子,感觉创意真的不错!我去学习一下。

1 个赞

关于 c 这种 Visual 触发器

对于输入打字的情况,c 触发器不会有干扰,因为在 options 中有 "v"Visual 模式,因此只有在选中文字的时候才会触发这段代码条。

不过对于 LaTeX Suite 的 Visual 模式的触发器,个人有几条锐评:

  1. 个人的使用上会尽量避免「选中文本 + 键入替换文本」的操作,一般是直接用 Ctrl + BackSpace/Delete 去删除,再加上使用自定义的「删除行」「删除到行首」「删除到行尾」的快捷键去操作。因此真的需要「选中文本 + 键入替换文本」的时候就是真的需要需要执行 Visual 代码条。
  2. 对于中文用户来说有一个尴尬的点,每次执行 Visual 代码条的时候都需要确定输入法的状态。比如:选中一段网址,然后输入 c 想要获取选中文字中的某些字符串,但是因为处于中文输入法,这个时候会导致选中文字被替换成「吃」:sweat_smile:。因此优先使用 0-9 来当做出发键位。
  3. 我写这几个 snippets 的背景是我经常「左手放在键盘上,右手放在鼠标上」,因此触发字符都是“左利手键位”。如果要是学会了 vim 相关的技能,那可以再进一步优化相关自己写的触发器。(总之就是触发器的设计充满需要长期磨合的个性)
  4. 使用正则属于是下下策,因为只要存在一个基础变换的改动,基本上要微调很久。。。

另外提一个算是我的“遗留问题”,可能你的这个插件应该不会遇到,我觉得这个是 LaTeX Suite 插件的问题。如果以后要开发「选中文本的情况」:

如果选中的文字中存在如 $1 这种 $<number> 字符串,至少在 LaTeX Suite 插件中,都会被当做占位符看待:sweat_smile:,因为我肯定需要使用正则去匹配 $\LaTeX$ 行内字符串。

至少对于不精通 JavaScript 的我来说,这个问题,目前我目前的想法是,如果存在这样的 “sensitive math”,那就把 $1$ 先变成 ${{ 1 }}$,至少在最后效果呈现上没有区别。

关于链接的一个别的想法

关于链接,我也一般不会改动 text,顶多是对于那些不怎么遵守“中英文之间添加空格”的行为有些极度反感罢了,因此对于这种情况,我希望的是在我创建 [text](link) 的时候就解决掉。

比如说:

这个页面原文的标题,中英文不加空格。创建链接的一般操作是:

  1. 复制 text
  2. 返回到 Obsidian 中,粘贴,(快捷键)调整空格;
  3. 返回到浏览器,复制 link
  4. 返回到 Obsidian 中,选中 text,粘贴得到 [text](link)

但是我能不能直接:

  1. 复制 textlink(次序争取可以不用管)
  2. 回到 Obsidian,敲击 lll 或者 lkk 直接触发 [text](link) 呢?

也就是通过触发触发器,帮我:

  1. 自动识别历史剪贴板中第一个和第二个内容,其中一个一定是 purelink,即 https://...,另外一个是 text
  2. text 做文本微调,即 ([\\u4E00-\\u9FA5])([A-Za-z0-9])([A-Za-z0-9])([\\u4E00-\\u9FA5]) replaceAll 为 $0 $1
  3. 还可以实现一些附加功能,比如添加一个 from 字段,根据 link 的来源,switch case 不同的平台后缀 Bilibili YouTube 知乎 Quora 微信 等。
  4. 最终输出 [text - from](link) 即可。

后面几点都不算难,问题其实是第一点:[在 LaTeX Suite 插件中,如何通过 Javascript,并借助历史剪贴板,快速实现创建 text 形式的链接?](在 LaTeX Suite 插件中,如何通过 Javascript,并借助历史剪贴板,快速实现创建 [text](link) 形式的链接?)。

最后的这一段代码很神奇,有的时候是可行的,有的时候是不行的。

我不怎么了解 JavaScript 相关编程语言,或者可以说我是因为使用 Obsidian 才开始学习使用 JavaScript,因此不知道错误在哪里。

关于 require

我一开始使用的也是 navigator.clipboard,后来是在尝试实现上面这个需求的时候根据 AI 的回答,用了 const { clipboard } = require('electron'); 和一系列代码,只不过没有成功,把相关代码删了之后然后忘记改回来了。之后就没有管了,还是老老实实地常规操作处理,不想太折腾了,哈哈。

不知道你是不是 Windows 平台,如果是的话,我写过一个 Quicker 软件的脚本,专门用来快速粘贴链接,流程是:

  1. 在浏览器里切换到对应标签页
  2. 回到 Obsidian 里,按快捷键(我用的 Alt+V )粘贴 [text](link) 格式的文本

就这两个步骤,而且 Quicker 动作里还会自动处理一些常见的格式(比如去掉某些网站自带的后缀,或者替换 # 之类的字符)

b7096400-9c1d-40a1-bb88-a8354b8ae976

有兴趣的话之后可以写篇单独的文章介绍一下。

此前在 OB 内调用 QK 文章里有简单提及。
Quicker 动作: 视频笔记 - by Moy

1 个赞

原来如此!之前对 visual 模式啥的都不是很了解,原来是这个用处hhh

输入法的状态确实,有一些小的应用(比如 ImTip)可以实时在输入指针的位置显示输入法状态:
image

不过来回切换还是很麻烦。

而且在使用 VIM 时,这个情况也不会改善……甚至会更加麻烦 (:з」∠)
中文输入语境基本就很难舒服地用这类“命令”了。


然后关于「选中字符」,在我上一个回复里提到的「粘贴链接」的应用里其实也有遇到:
我希望在选中字符的情况下,直接把链接粘贴进这个文本 ( [选中文本](link)

这个时候是通过 ShellCommands 插件把选中的文本作为参数发送给 Quicker 动作,然后 Quicker 动作读取到之后,根据它来替换生成的链接中的 text 部分。


你现在的这个做法其实就是挺常用的方案(有点类似转义字符),先替换成别的字符,然后处理完再替换回来,没啥问题~

Release 1.3.0 New commands and options · Moyf/easy-copy

在这个更新里,双链和 Callout 的复制都支持了。 @awakening @YYoung

1 个赞

多谢,已加入手机上的工具栏。还有个小问题,没有对应的图标,只是个问号

1.3.1 版本更新了图标