[js] 代码片段实现代码块最近使用的语言置顶

介绍

如题,在代码块顶部筛选语言时,每次都是按 a-z 排列,能不能按照最近使用的排在上面呢?

当然,这个代码片段就是实现这个功能的。

还增加了默认语言,设置了默认语言后,将不受上次选择的影响,始终是这个默认语言。

还增加了置顶语言功能,加入置顶列表的语言,始终置顶,不管是否最近使用过。

排序顺序是:置顶 》最近使用 》a-z 排序。

新增支持自定义语言,即使列表中不存在的语言也支持。

效果

Snipaste20240808113432.png

代码片段

注意事项

由于手机不支持代码片段,所以不支持手机版,但手机伺服支持。

使用说明,请参考代码注释。

其他

另外,代码块如果有行号,当选择代码时,鼠标超过代码区域到达左侧行号区域及以外区域后选择框会消失。

这个 bug,可以在 css 片段中增加以下样式解决

/* 取消行号列占用的空间 */
.code-block .protyle-linenumber__rows {
    position: absolute;
    left: 0;
}
/* 当有行号时,代码块右移,给行号列让空间 */
.code-block .hljs.protyle-linenumber {
    margin-left: 3.6em;
}

效果

r11.gif

详情请查看帖子: 代码块功能优化建议

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    22989 引用 • 92450 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    89 引用 • 556 回帖
5 操作
wilsons 在 2024-12-18 19:58:06 更新了该帖
wilsons 在 2024-08-09 21:13:33 更新了该帖
wilsons 在 2024-08-08 19:54:30 更新了该帖
wilsons 在 2024-08-08 13:50:09 更新了该帖 wilsons 在 2024-08-08 13:21:59 更新了该帖

相关帖子

优质回帖
  • wilsons 2

    @EmberSky @JeffreyChen 已经完美修复了。

    主要修改了 addLanguage 函数,修改内容如下

    // 传入的language参数仅供参考,真正获取最后一次使用的语言得从思源存储中或数据库查询中获取,
    // 这样可以防止块元素被意外触发,添加非最后一次使用的语言,导致混乱
    // 这样无论哪个代码块被触发都以getLastLang为准
    language = getLastLang();
    

    这里默认并没有采用从数据库读取最后一次使用的数据(代码留在那里仅供备用),我发现这个数据思源已经存储了,干脆用思源存储的。

    为了达到一致的体验,第一次运行时,默认会把上次使用的语言置顶。

    新增了支持自定义语言,即使列表中不存在的语言也支持。

  • 我这里会报错

  • Achuan-2 1

    就是 mutation 监控的问题,因为我确实测试了,新增代码块没有检测到 code-block 变化,,所以 if 判断里面的代码没有执行

    code-block 新增,不覆盖思源的上一个选择代码语言,所以下一次新增代码,就变为了上一次选择的代码语言,而不是代码片段设置的代码语言了

    我新建空白工作空间测试了,依然检测不到 code-block 变化

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • EmberSky

    为什么你的效率这么高, 哈哈哈

    1 回复
  • wilsons

    哈哈哈,怎么说呢,前期也做了一些调研,昨天还研究了一些细节,做一些简单测试。有时候难点在于未知的,比如,为了实现这个默认语言,追着代码跟踪了很久才找到踪迹,不过对于熟悉思源的大佬来说,这些都是小儿科了。

  • 我这里会报错

    2 回复
  • EmberSky

    哈哈哈, 我也是点击会报错, 本来以为是我自己的问题, 准备私下研究一下的

  • EmberSky

    看你的截图, 我突然发现, 第一个代码语言, 竟然是 1c, 而不是 lc, 我一直以为是 lc 来着, 还在想为啥 lc 会排最前面

  • wilsons

    已修复,这个是因为第一次运行缺少存储文件引起,记得之前处理过,可能我撤销代码时撤没了。

    1 操作
    wilsons 在 2024-08-08 20:13:34 更新了该回帖
  • 希望大佬写成插件

    1 回复
  • EmberSky

    可以用了, 666

    1 回复
  • wilsons

    已修复意外触发代码块导致的添加历史语言错乱情况。

    1 操作
    wilsons 在 2024-08-08 20:01:51 更新了该回帖
  • wilsons 2

    @EmberSky @JeffreyChen 已经完美修复了。

    主要修改了 addLanguage 函数,修改内容如下

    // 传入的language参数仅供参考,真正获取最后一次使用的语言得从思源存储中或数据库查询中获取,
    // 这样可以防止块元素被意外触发,添加非最后一次使用的语言,导致混乱
    // 这样无论哪个代码块被触发都以getLastLang为准
    language = getLastLang();
    

    这里默认并没有采用从数据库读取最后一次使用的数据(代码留在那里仅供备用),我发现这个数据思源已经存储了,干脆用思源存储的。

    为了达到一致的体验,第一次运行时,默认会把上次使用的语言置顶。

    新增了支持自定义语言,即使列表中不存在的语言也支持。

    1 回复
  • EmberSky

    image.png

  • wilsons

    感谢建议,插件开发我也在学习中,还有实在不想安装动辄几百兆的 node 库,我想找寻一套原生 js 开发插件的方法,这样可以做到轻量级开发,目前,原生情况下,主要热加载较麻烦。

    不知社区是否已有原生下的热加载方案?类似 obsidian 的热加载插件。

    2 回复
  • 抱歉 ob 的热加载我也不太清楚是什么。

    至于 node_modules 的话原则上只需要装 siyuan. 目前的插件模板还装了其他的主要是因为模板作者用了其他框架。

    (只是按我个人理解,因为我不是写前端的,我的思源插件全是现学现卖)

  • 谢谢大佬,这个很好用!👍

    代码写的真好
    js 小白,学习了 observeDomChangewhenElementExist 的使用

    1 操作
    Achuan-2 在 2024-08-09 11:28:28 更新了该回帖
  • vite-svelte 模板提供了热重载,但是这个热重载本质是检测到文件发生变动就自动 window-reload。和无需刷新的热重载可能还有点差距,但其实日常使用也能用。

    你如果不想要使用 node 环境开发,可以试一下‘开发 API’或者‘Run JS’。

    这两个插件都把一些前端的 API 透传到了 globalThis 下面(RunJS 还透传了一些对 kernel api 的封装),这样直接在思源里面就可以调用一些给插件用的 api 了。

    image.pngimage.png

    1 回复
  • wilsons

    感谢 F 佬的指点!

    感谢 @zxkmm 的建议!

    也不是非不装 node 环境,大项目或者偶尔还行,有时小插件的话,感觉有点杀鸡用牛刀了。

    确实你提供的 API 非常棒,涵盖了思源的 api 还有扩充。

    不过非插件自用还好,分享的话,对不了解的人使用费事。

    关于热加载根据 F 佬的指导,我有一点思路,也参考 obsidian 的插件。主要原理还是监控文件变化,然后把项目和插件关联起来,如果文件有变动,进入思源时就自动关闭和开启下插件,这样基本能满足绝大多数的热加载需要。

    @zxkmm ob 的热加载也是这个原理。

    只是限于水平,时间和精力,完善的热加载插件估计还得考虑诸多因素,不过偶尔开发,写段代码模拟下估计还是可以的。😄

    1 回复
  • Achuan-2 2 评论

    if(mutation.target.classList.contains("code-block")) {

    这个好像有点问题,貌似是不执行的,新增代码块的 mutation.target.classList 只有 hljs

    我设置默认语言为 python,新增代码块确实是 python 语法高亮,再新增一个 matlab 代码块,之后再新增不指定语言的代码块,默认就是 matlab 代码块了

    2 回复
    3 操作
    Achuan-2 在 2024-08-09 16:40:09 更新了该回帖
    Achuan-2 在 2024-08-09 16:31:07 更新了该回帖
    Achuan-2 在 2024-08-09 15:55:09 更新了该回帖
    你这个回帖看起来很奇怪啊,需要重新编辑一下
    JeffreyChen
    @JeffreyChen 因为涉及 ```, 链滴对这个输入没优化,代码块和行内代码都没法输入
    Achuan-2
  • 你是不是想输入这种: ```matlab ```Python ,用两个 ` 来包裹

    是的,原来可以
    Achuan-2
  • wilsons

    你好,我按照你的操作没能重现你的问题,能否录屏看下你的操作步骤?

    我这边的测试如下

    r122.gif

    2 回复
  • 创建默认的 python 高亮代码块,用的是、、、,用这个方法是不会监听到 code-block 变化的

  • 我用你的方式(即斜杆命令创建不指定语言的代码块),也是一样的结果,监听 mutation,也是只有 hljs 变化,没有 code-block 变化

    思源是最新版

    系统是 windows11

    代码

    // 功能:代码块最近使用的语言置顶
    (async ()=>{
        // 配置默认的代码语言,注意如果设置了这个参数,则会覆盖上一次使用的语言。始终默认用这个语言,为空不设置
        const defaultCodeLang = "python";
    
        // 配置最近代码语言最大显示个数
        const recentlyCodeLangLength = 10;
    
        // 配置置顶的代码语言,这个配置里的语言始终置顶,比如 ["js", "java", "php"]
        let topCodeLang = [];
    
        // 配置持久化文件的存储路径
        const storagePath = "/data/storage/recently_code_lang.json";
    
       
    
    

    录屏

    思源文档设置默认语言.gif

    1 回复
  • image.png
    我想要插件的目的主要是理论上代码片段的性能应该不如插件(因为插件“编译”过)。

    1 回复
  • wilsons

    抱歉,我 Mac 系统,思源 3.1.2, 3.1.3,都尝试了,无法重现你的问题。

    具体原因可能需要你自己根据自己的环境进行调试。

    defaultCodeLang 是通过 window.siyuan.storage["local-codelang"] = defaultCodeLang 实现的,你每次输入代码块前,先检查这个是否设置成功,或者手动设置试试。

    如果不是这个原因,再检查 mutation 监控中,为什么没检测到 code-block 元素的变化?如果能解决这两个问题,这个问题应该就解决了。

    2 回复
  • Achuan-2 1

    就是 mutation 监控的问题,因为我确实测试了,新增代码块没有检测到 code-block 变化,,所以 if 判断里面的代码没有执行

    code-block 新增,不覆盖思源的上一个选择代码语言,所以下一次新增代码,就变为了上一次选择的代码语言,而不是代码片段设置的代码语言了

    我新建空白工作空间测试了,依然检测不到 code-block 变化

    1 回复
  • wilsons

    理论上代码片段的性能应该不如插件(因为插件“编译”过)

    你说的很对,确实会好那么一点点,不过这里的编译不是真正意义上的编译成二进制,js 本身是解释性语言,插件无非进行了压缩,可能在加载速度上快了点。另外,代码片段是字符串,先要把字符串解析为 js 代码再执行,而插件不用这一过程。

    不过,这些在代码量很小时可以忽略不计,只有在逻辑复杂,代码量大的时候才比较明显。

    1 回复
  • 所以我改成了这样就没问题了

                // 监听新增代码
                if(mutation.target.classList.contains("hljs")) {
                    if(codeTimer) clearTimeout(codeTimer);
                    codeTimer = setTimeout(() => {
                        const langText = mutation.previousSibling.textContent;
                        // 添加语言
                        addLanguage(langText);
                        // 设置默认语言
                        setDefaultLang();
                        // console.log(mutation.target.querySelector(".protyle-action__language")?.textContent, 'selected');
                    }, 40);
                }
    
  • wilsons

    感谢反馈!

    已经修复了这个问题,就像你上面说的,有时是 hljs,所以我加了兼容性代码。

    刚才下载了最新版,用新空间测试,确实会有这个问题,但老空间却是 code-block,不清楚什么原因。

    1 回复
  • 原来这样呀。
    ps:如果出现的是 hljs,querySelector 选择代码语言要更改下,因为代码语言是兄弟节点不是子节点,会查找不到

    1 回复
  • wilsons

    确实,已更改。

    更改成了这个

    const codeBlock = mutation.target.closest(".code-block");
    const langText = codeBlock?.querySelector(".protyle-action__language")?.textContent;
    
    1 回复
  • 才知道有 closest 方法,学习了哈哈

  • 是的所以我打了双引号

  • /**
     * 一旦文件发生变化,就触发相应的回调 
     * 要是想完善一下就弄成跟fs一致吧懒得弄了
     */
    function watch(path,callback){
            let changedFiles = [];
            let isWalking = false
            const walkDir = async (currentPath) => {
                const response = await fetch('/api/file/readDir', {
                    method: 'POST',
                    body: JSON.stringify({ path: currentPath }),
                });
                const json = await response.json();
                const stats = json.data;
              
                for (const entry of stats) {
                    const fullPath = `${currentPath}/${entry.name}`;
                    if (entry.isDir) {
                        await walkDir(fullPath);
                    } else {
    
                        if (watchStats[fullPath] && watchStats[fullPath] !== entry.updated) {
                            changedFiles.push(fullPath);
                        }
                        watchStats[fullPath] = entry.updated;
                    }
                }
            };
            const idleCallBack =async()=>{
                if(isWalking)return
                isWalking = true
                changedFiles = [];
                await walkDir(path)
                if(changedFiles.length>0){
                    callback(changedFiles)
                }
                isWalking = false
                setTimeout(idleCallBack,1000)
            }
            setTimeout(idleCallBack,1000)
    }
    
    
    1 回复
  • wilsons

    新增智能版

    注意: 智能版默认并未开启智能模式,默认还是非智能版 😄 要使用智能版,先要把 const enableSmartMode = false; 改为 true 才行。

    智能版通过检测用户的操作行为去判断是否用户触发的代码块,如果不是则不添加最近的历史。这样有效避免了意外触发,但缺点是一旦检测失败,脚本将失效,兼容性不如非智能版。

    而非智能版的缺点是,会在第一次加载含有代码块的文档时,会把上一次用户的使用语言添加到最近历史中,虽然这影响不大,但这是意外情况,非用户手动触发,所以为了让体验一致,脚本会在第一次运行时把用户上一次使用的语言添加到最近历史中(不过,这样去理解也没错,最近嘛,上次使用的也是最近嘛 😄 )。

    下面把二者做个详细对比

    功能对比 智能版 非智能版
    优点 1. 有效避免意外触发
    2. 仅用户操作时才触发添加语言历史
    1. 虽然会有意外触发,但无影响
    缺点 1. 兼容性不如非智能版
    2. 用户行为检测失败,脚本失效
    1. 第一次加载会把用户上次使用的语言加到历史
    推荐
    适合用户 极客,有调试能力的用户 普通用户
    用户感知 无感知 除了第一次加载添加上次语言外,无感知
    重置方法 删除 recently_code_lang.json 文件后,
    刷新或重启思源
    删除 recently_code_lang.json 文件后,
    刷新或重启思源

    智能版完整代码如下

    代码不同点对比(左侧智能版,右侧非智能版)

    image.png

  • wilsons

    非常感谢你的代码!给我很大的启发。

    我想可以根据这个原理,针对插件自身做个热加载,即自身含带热加载。😄

  • 代码片段是不是没上代码块啊,好像被直接当作 js 渲染了

    不对,这里是上了个啥能直接运行的东西,我这边一直 502。

    建议还是用原生代码块,别引入这种直接让文章里面的代码全都不可用的第三方工具吧……您这边贴的所有代码我这里就只有一个 502 的框,如果一直加载不出来,那您这篇文章对于很多读者来说就是无用的文章了。

    image.png

    image.png

    1 回复
    1 操作
    muxue 在 2024-09-13 06:51:36 更新了该回帖
  • wilsons

    感谢反馈!

    jsrun 网站一般早上 6 点左右会无法访问,可能这个时间官方在代码维护吧,一般过会就好了,影响不大。jsrun 网站已稳定运行了 8 年。

    后面再分享代码,我会同时附上 github 备份地址,以防万一。

    之所以选第三方代码托管,1 是方便随时修改代码,2 是有时链滴有字数限制,代码过长导致无法发布。

    1 回复
  • 该回帖仅作者和楼主可见
    1 回复
  • wilsons

    你说的对,附上备份代码地址:gitee 备份代码地址

    后续更换一个代码语言以后,又会变成上一次使用的代码语言了

    没太理解你的意思,这个脚本并不会默认使用最近的语言,只是会把最近使用的语言置顶到语言选择列表的上面。

    1 回复
    1. 创建代码快,js 代码片段里面默认语言配置成 c 了,新创建的代码块是 c。
    2. 修改代码语言为 sh
    3. 再次创建一个代码块,新创建的代码块语言变成 sh 而不是 js 代码片段里面配置的默认 c。

    我遇到了这个问题,本来以为这个 js 能让默认创建的代码快一直都是配置的默认语言的,请问可以实现吗?

    2 回复
  • wilsons

    js 代码片段里面默认语言配置成 c 了

    你这里是指配置了 defaultCodeLang 这个参数 const defaultCodeLang = "c"; 了吗?

    参数配置后或者说脚本代码被修改后,需要重启思源,新的配置才能生效。

    你是配置并重启后测试的吗?

    另外,即使配置了,如果键入的代码是```sh,也只会按 sh 来处理,只有未明确指定语言时才使用默认语言,即 defaultCodeLang 所配置的语言。

    我是 3.1.6 环境测试没问题,3.1.5 环境我这边没有这个环境,不方便测试,理论上应该一样。如果以上都没问题,建议升级 3.1.6 试试。

    1 回复
  • 不是的,我没有明确指定语言,就是直接三个波浪线创建的代码块。

    按我这个方法测试可以在 3.1.5 中稳定复现这个问题。只要手动设置过一次代码块语言后,后续新创建的代码快又是刚刚设置过的语言,而不是 js 里面配置的默认语言了。

    我重启过思源,每次都只有第一次创建代码块的时候 js 里面的默认语言有效,换过一次语言后又变成了使用上次的语言的逻辑了。

    本身配置这个就是想让他默认弄成固定的某个语言而不是每次更改后都得换一下 😂

    1 回复
    1. 修改代码语言为 sh
    2. 再次创建一个代码块,新创建的代码块语言变成 sh

    思源的功能,新创建的代码块默认使用上次使用的代码块语言

  • wilsons

    正如楼上所说,代码块默认使用上次使用的代码块语言。

    这个 js 代码不会改变思源的这种使用习惯。

    但,如果你想代码块一直默认使用 c 语言的话,可以设置 js 代码片段中的参数 const defaultCodeLang = "c" 即可。

    但,如果你输入代码时明确指定语言,比如```sh 的话,只会使用你指定的语言 sh,其他情况,未明确指定语言的情况下,则默认使用 defaultCodeLang 设定的语言,而忽略上次使用的语言。

    注意,修改 defaultCodeLang 参数后,需要重启下思源生效。

    1 回复
  • 我懂了,你这个代码片段不能手动切换语言,不然就没用了。我说的手动切换指的是在本来创建的代码块中更改语言。

    我想要的是即便手动切换了也能让默认 ``` 创建的代码块一直锁在 c 的这种功能。现在这个代码片段没有办法实现。我还以为可以绕过思源那个功能呢……

    不过还是感谢分享了。

    1 回复
  • wilsons

    你要的是这种功能吗?如果是,则通过设置 const defaultCodeLang = "c" 可以实现,否则则不支持。

    r54.gif

请输入回帖内容 ...