Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

语法树提供源码映射 #187

Closed
shuzijun opened this issue Feb 15, 2023 · 16 comments
Closed

语法树提供源码映射 #187

shuzijun opened this issue Feb 15, 2023 · 16 comments

Comments

@shuzijun
Copy link
Contributor

你在什么场景下需要该功能? In what scenarios do you need this function?

在编辑时使用markdown转换到html,保存时从html转换到markdown,经过这层转化,即时在未进行修改的情况下,markdown文本也会发生变化。根据文章Markdown 解析原理详解和 Markdown AST 描述中介绍,ast.Node中没有保存源码信息,在生成html时无法带上源码。

描述最优的解决方案 Describe the optimal solution

根据目前语法树的层级,是否可以在块级容器节点中增加此节点下对应的源码信息,在生成html时以隐藏节点或者属性保存。
从html转化为markdown时,优先取保存的源码信息,当发生变化时,再解析转换此块的html到markdown

@88250
Copy link
Owner

88250 commented Feb 18, 2023

这个应该很难做到,Markdown 和 HTML 相互转换本身是存在一些损失的。

对于编辑器层面,需要在细节处理上进行改进,但是我不太确定你目前遇到的问题到底是哪方面的,能否举个具体的例子我们分析一下,谢谢。

@shuzijun
Copy link
Contributor Author

shuzijun commented Feb 18, 2023

当一个被git管理的文件,在使用编辑器打开后,编辑或者新增了一点内容,重新获取markdown保存时,整个文件都被格式化了,这个在多人协同时体验有点差,相关的issues: shuzijun/markdown-editor#69, 在类似编辑marktext也存在这个问题marktext/marktext#2189

@shuzijun
Copy link
Contributor Author

我看了下目前markdown解析成语法树的代码,Node与markdown文本很难一一对应,很难做到还原成原格式。一个比较折中的方案就是,在目前语法树中,在顶级的块级节点上保存这一块内容的源码信息,在生成html和反解析html时,能够优先获取保存的源码信息,当然这个也需要编辑器能够在这一块内容发生更改事件时,直接移除携带的源码信息。

这样能够在一定程度上保留原格式。

@88250
Copy link
Owner

88250 commented Feb 18, 2023

编辑器需要有个 “Spin” 过程,将输入的 DOM 转换为 Markdown 然后再转换输出为 DOM,这个过程中需要格式化 Markdown,之前我们考虑过源码映射来实现,但是太复杂所以放弃了。

节点中保留的话可能会有问题,比如无法在 Markdown 文本上持久化,因为 Markdown 没这语法;对性能会有较大影响,因为需要解析这段元信息;就算以上两个问题解决了,还是无法解决解析上遇到的问题,比如对于输入能够打断行文本来生成块级节点的情况:

foo
* 

输入 * 后会彻底改变段落节点的内容,即从 foo\n* 转换为 foo,这个问题就涉及到之前保持的内容需要修改,如果这个情况发生在容器节点上就需要向上迭代更新。

所以,Lute 对于格式化这个问题估计是无解了,这是目前技术方案的局限。

@shuzijun
Copy link
Contributor Author

对于这几个问题,可能和我理解的有点出入,我描述一下目前编辑器的整体流程和相关改动点

  • 1: 初始化Vditor编辑器,设置初始Markdown文本
# 标题
文本
* 列表1
* 列表2
  • 2: 使用Md2VditorDOM方法,将Markdown文本转换为HTML
    • 2.1 解析成语法树 (可能涉及到的问题,是对性能的影响,会额外存储源码信息,但是不会影响整体的解析性能,只是在顶级块节点上增加一个冗余信息)
      > Root
      >> Type: NodeHeading
         Source:  '# 标题' # 新增的节点,保存着一块的源码
         Child: NodeHeadingC8hMarker
      >> Type: NodeParagraph
         Source:  '# 文本' 
            ...
      >> Type: NodeList
         Source:  '* 列表1\n* 列表2' 
            ...
           
      
    • 2.2 将语法树转换为HTML (可能涉及到的问题 无法在 Markdown 文本上持久化? 这一步不需要在Markdown上持久化,而是在HTML上持久化,示例中使用了属性)
      <h1 data-block="0"
          class="vditor-ir__node"
          id="ir-标题"
          data-marker="#" source="# 标题">
          <span class="vditor-ir__marker vditor-ir__marker--heading"
      	      data-type="heading-marker"># </span>标题
      </h1>
      <p data-block="0" source="文本">文本</p>
      <ul data-tight="true"
          data-marker="*"
          data-block="0"
             source="* 列表1\n* 列表2"
              >
          <li data-marker="*">* 列表1</li>
              <li data-marker="*">* 列表2</li>
      </ul>
  • 3: Vditor编辑器将HTML填充到页面的DIV里,接受输入事件等(可能涉及到的问题是在当前块节点发生变化,需要重新生成块文本等,这个想到的方案是对于发生变化或者修改事件,清空块级节点的source属性)
// 例如列表新增了一个,则清除
    <h1 data-block="0"
	    class="vditor-ir__node"
	    id="ir-标题"
	    data-marker="#" source="# 标题">
	    <span class="vditor-ir__marker vditor-ir__marker--heading"
		      data-type="heading-marker"># </span>标题
    </h1>
    <p data-block="0" source="文本">文本</p>
    <ul data-tight="true"
	    data-marker="*"
	    data-block="0"
            >
	    <li data-marker="*">* 列表1</li>
            <li data-marker="*">* 列表2</li>
           <li data-marker="*">* 列表3</li>
    </ul>
  • 4: 使用VditorDOM2Md生成Markdown,解析规则与原来一致,新增逻辑是如果节点上有source属性,则优先使用,不需要再迭代解析。

以上是对提到问题的解释,不知道是否正确?对于HTML上的修改事件可能会有问题,那块的代码没怎么理解。

@88250
Copy link
Owner

88250 commented Feb 18, 2023

Md2VditorDOM 这个函数是用于从源码模式切换至所见即所得模式,不是编辑时使用的。编辑时使用的是 SpinVditorDOM,这个是 IR 模式的。

@shuzijun
Copy link
Contributor Author

Md2VditorDOM 这个函数是用于从源码模式切换至所见即所得模式,不是编辑时使用的。编辑时使用的是 SpinVditorDOM,这个是 IR 模式的。

去看了下SpinVditorDOM,过程是先把HTML解析成Markdown,然后再把Markdown转成HTML,如果输入的HTML是已经把编辑区域的source去掉了,整体流程应该是不受影响的。

这个方案有没有可行性?

@88250
Copy link
Owner

88250 commented Feb 18, 2023

如果输入的HTML是已经把编辑区域的source去掉了

我没理解你的意思,去掉的话后面怎么在 spin 内部使用源 md 文本?

@shuzijun
Copy link
Contributor Author

shuzijun commented Feb 18, 2023

如果输入的HTML是已经把编辑区域的source去掉了

我没理解你的意思,去掉的话后面怎么在 spin 内部使用源 md 文本?

是产生修改的块去除source,没有编辑的块还是带着的
image

@88250
Copy link
Owner

88250 commented Feb 18, 2023

这个又回到前面提出的问题了,这种情况前端 DOM 里不会产生块的:

<p>
foo
* 
</p>

如果要在前端区分的话,细节处理可能会比较多,如果这个能实现的话,实际上 spin 就没有必要了,直接在前端实现就好了 😂

@shuzijun
Copy link
Contributor Author

shuzijun commented Feb 18, 2023

这种情况前端 DOM 里不会产生块的

我好像理解了,就是输入了新的文本,要追加到上一个节点,这时候HTML的内容是

 <h1 data-block="0"
	    class="vditor-ir__node"
	    id="ir-标题"
	    data-marker="#" source="# 标题">
	    <span class="vditor-ir__marker vditor-ir__marker--heading"
		      data-type="heading-marker"># </span>标题
    </h1>
    <p data-block="0" source="文本">文本</p>
    <ul data-tight="true"
	    data-marker="*"
	    data-block="0"
            source="* 列表1\n* 列表2"
            >
	    <li data-marker="*">* 列表1</li>
            <li data-marker="*">* 列表2</li>
    </ul>
<p>
* 列表3
</p>

在解析时做的事情是要把<p></p>的内容追加到上一个列表中。

不知道理解的对不对,我先按这种解释一下方案。

  1. 原列表块没有发生变化,所以在解析时是直接取的source的内容,并产生了块节点NodeList,并且在块节点NodeList上保存了Source
  2. 解析<p>内容是,没有source内容,所以按照文本解析,发现可以追加到上一个NodeList节点,这时候可以理解为NodeList已经发生了变化,把Source的内容去掉。

@88250
Copy link
Owner

88250 commented Feb 18, 2023

上面这个例子应该是生成两个列表,不是追加列表项。我意思就是类似的情况很多,不大可能在前端一一处理的,如果能一一处理的话,spin 就没有存在的必要了,也就是可以整体重构为新的方案了。

@shuzijun
Copy link
Contributor Author

这些能不能在spin里处理哪?

😄是想能够尽量不破坏现有结构的情况下实现功能,还是已经有重构方案能够满足这个功能了?

@88250
Copy link
Owner

88250 commented Feb 18, 2023

现在肯定是不能在 spin 中处理的,因为转换中间状态 Markdown 这个步骤没有对应的语法支持,会丢失元信息。如果要支持,需要使用能带属性的语法(比如 kramdown),这个工作量还是比较大的,编辑器前端也要做相应调整。

我们目前没有时间精力去改这个了,类似的方案可以参考思源笔记项目,它就是这样保留节点属性的。但是我个人还是建议你目前最好暂时不要在这个上面投入太多时间精力,因为如果只是为了解决全局格式化问题的话不太值得这么多投入,除非还有其他更有价值的需求可以一并解决。

@shuzijun
Copy link
Contributor Author

👌还是等其他有价值的需求一起处理吧

@88250
Copy link
Owner

88250 commented Feb 18, 2023

以后看情况吧,我关闭了,感谢讨论。

@88250 88250 closed this as completed Feb 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants