跳到正文
editorNexus Editor · PR #66

多行列表的「整体取消 / 整体添加」

同一个 PR 的两种状态:左边是仓库 main 分支跑出来的旧 toolbar, 右边是 feat 分支带修复的 toolbar。下面并排对比, 你可以同时操作两个编辑器。

对照着看

两个编辑器初始内容相同:四行裸文本。试着在两个编辑器里都框选这四行, 然后点工具栏上的 无序列表 按钮。

  • 左边(main):旧实现只动第一行, 剩下三行不动,结果是「一行列表 + 三行散落段落」。
  • 右边(feat):新实现把选区当成一个整体, 四行要么全加 - ,要么全去;再点一次就整段消失。

也可以试试混合态:手动把其中一行改成1. ,再点无序列表。旧版会留下1. 行,新版会把它剥掉、统一成-

思路:把整段当成一个动作

新版把选区里的所有行(包含跨行的范围)作为整体来 toggle, 方向遵循「全有则去,否则加」:

  • 选区里非空的行已经全都有同种 marker(比如全是- ),就整段去掉。
  • 只要有一行没有、或者 marker 类型不一致,就把整段统一加上。
  • 有序模式从 1 开始重编;选区里夹的空白行直接跳过、保留为空。
  • 类型相反的 marker 现场转换,不堆叠。

伪代码

const lines = getSelectedLines(doc, from, to)
const nonEmpty = lines.filter((l) => l.line.trim().length > 0)
const ownMarker = mode === "ordered" ? /^\d+\.\s/ : /^[-*+]\s/

// 全有则去,否则加
const allMarked = nonEmpty.length > 0 &&
  nonEmpty.every((l) => ownMarker.test(l.line))
const shouldRemove = allMarked

let counter = 1
const transformed = lines.map((l) => {
  if (l.line.trim().length === 0) return l.line
  if (shouldRemove) return l.line.replace(ownMarker, "")
  const stripped = l.line
    .replace(ownMarker, "")
    .replace(/^[-*+]\s/, "")
    .replace(/^\d+\.\s/, "")
  return mode === "ordered"
    ? `${counter++}. ${stripped}`
    : `- ${stripped}`
})
改之前main 分支 · 仓库默认 npm 版本
改之后feat/toolbar-multi-line-list · 本地构建

左右两个工具栏的按钮完全一致,但点 无序列表 之后 行为不同:左边是「按行处理」,右边是「整体取消 / 整体添加」。