[POC] Emacs buffer 文本块渲染及布局实现

先发布 POC 版本,感兴趣的同学可以先看 tests 里面 demo 代码了解用法。

目前实现了:

  1. 单个 block 渲染的完整功能 (block-string) – 已稳定
  2. 多个 block 的简单的布局功能 (block-concat, block-stack)。 – 后续会变动

后续计划:

  1. 目前布局的方式只是简单的在水平和垂直方向连接文本块实现的,代码写起来比较繁琐,后续会优化为基于 ”座标“ 的任意布局。
  2. 支持多个文本块的部分重叠覆盖的效果。
  3. 修改为 声明式的S表达式 来定义结构和布局,而非当前”命令式“的使用函数生成。

该包依赖 emacs-kp: GitHub - Kinneyzhang/emacs-kp: Implement of Knuth-plass algorithm in emacs-lisp, support for mixed typesetting of CJK and Latin languages.

另外我一直在思考 ”block“ 这个包名称是否合适,暂时没有想到更合适的名字。

下面是demo图片:

36 个赞

multiline 这个方法未定义

提交了,这个是之前的命名,现在用 block-string-duplines

神奇。感觉可以用来改改 eww 浏览简单网页了。

不过我在第一次允许测试时遇到了变量未定义的问题,手动 C-c C-e 求值 block-utils.el 后问题消除,可能是编译后导致的问题,不过不太清楚是否是 native-comp 而不是 byte-comp。

此外,block.el 里面也许最好添加 (require 'ekp)

已修改。这个变量 it 是 dash 里面的 --map 默认使用的 lambda 内的参数,刚刚移除了,现在不依赖 dash.el。(require 'ekp) 在 block-pixel.el 里面添加了。

果然还是得做自适应布局,屏幕尺寸变化,都会乱掉啊 :joy:

1 个赞

可用了。

qs,不过我有点畅想 ETML(Emacs Text Markup Language),ECSS(Emacs Cascading Style Sheets)和 Elisp(Emacs Lisp)的未来了 :rofl:

ETML 这个名字不错诶,含义清晰明了,最终我就是要用 S表达式的结构来表达类似 HTML 的结构和布局,多年前,我写过一个 pp-html,就是做的类似的翻译工作。

然后 html 的基础元素的组合和布局就可以定位为 组件,可以在 ETML 结构中嵌入组件,复用相同的结构和功能;最后再加上按键交互的功能 EJS,还有 EORM 对于数据库操作的封装用作后端存储,完美了,哈哈哈。这些都是 ETAF 要做的事情。

5 个赞

强无敌 :nerd_face:

1 个赞

使用体验

方法理解

  1. class block 用于定义一个展示块,返回值是一个 class 对象
  2. block-string 渲染一个展示块,返回值是一个字符串
  3. block-stack 垂直渲染多个块,返回值是一个字符串
  4. block-concat 水平渲染多个块,返回值是一个字符串

问题

  1. 嵌套使用时,容易出错。提供的各个方法,入参、出参,有的时候是字符串,有的时候是 block。容易混淆
  2. 嵌套使用时,emacs-kp 可能会对字符串反复格式化,导致对齐不准。

个人建议

  1. 把 class block 本身就做成嵌套的,就最好不过了。他的 content 既可以是 string 也可以是嵌套的 block
  2. block-stack 和 block-concat 不 render 成字符串,而是组合成嵌套的 block。
  3. 等组合完嵌套的 block 之后,使用 (block-render BLOCK),生成字符串。
  4. 不过这样可能会大大增加 block-render 的复杂度

例子

当前的实现逻辑:

  (block-render
  (block
   (block-stack
    (block (block-concat (block "A") (block "B")))
    (block (block-concat (block "C") (block "D"))))))

如果 block 本身可以变成嵌套的,那么可以如下实现

  (block-render
   (block
    (list
     (list (block "A") (block "B"))
     (list (block "C") (block "D"))
    ))

如果 block-stack 和 block-concat 不 render 成字符串,而是组合成嵌套的 block。可以:

  (block-render
   (block-stack
    (block-concat (block "A") (block "B"))
    (block-concat (block "C") (block "D"))))
2 个赞

很赞:+1::+1:,正有此意!在最终生成字符串之前,一切都是 block 对象,block 对象可以理解为 doom 树中每个节点,节点可以嵌套其他节点,最基础的节点就类似于基础的html元素,入参是字符串。

后续我计划优化为,使用下面的 sexp 列表的形式来表示结构布局

'(div
  (div "this is header")
  (row (col-8 "Forum   Book   Elpa   FAQ   Donate   Fork Us")
       (col-4 "注册 登录"))
  (row (col-2 (div "sidebar content"))
       (col-10
        (row "banner info")
        (row (col-5 "left string...")
             (col-7
              (div "right-top string")
              (div "right-middle string")
              (row (col-6 "bottom left string")
                   (col-6 "bottom left string"))))))
  (div "this is bottom line..."))

该 sexp 被解析之后,div,row,col-n 都是 block 对象。区别是 div 就是普通的 block 节点;row 会是其中的多个block 先 block-concat,再生成 block 节点;col-n 则是先 block-stack 再生成节点,数字表示宽度比例用来计算当前列的宽度(借鉴经典的12栅格布局)。标签和内容之间用 plist 形式指定各种 :width, :border, :padding, :margin 等属性。

3 个赞

我觉得 ginqi7 的变量名,会更加容易理解一些。

什么变量名?你是说 block-concat,block-stack 这些嘛,这是底层的函数,还会存在,只不过是在上层做了一层封装,简化名称方便使用。这两种的本质区别是:一个是 “命令式”的(我要做什么),一个是 “声明式”的(我要呈现什么效果)。我觉得声明式的写法对用户更加友好(jquery vs vue/react ..),这也是前端的趋势。当然如果用户不习惯这种写法,也可以直接使用底层的函数。

非常强大呀,看来文本浏览器又要复兴了。

1 个赞

没,我单纯变量名还是 block-xxx 会比较好理解。毕竟你虽然借用了一些前端的概念,然而行为和表现未必和前端的一致。

牛啊,炫酷又实用,赞赞

利用这个是否可能提高org-mode table的功能, 比如突破其cell不能多行, 行列cell不能span的局限? 或者说有没有可能定义一种新的table形式内嵌到org-mode中?

理论上可以,但需要许多额外的工作来适配各种无法预知的操作和交互,以及 text properties 的存储和还原的问题

这个包只是做了展示层面的工作,更适合在一个只读的buffer中展示表格,然后通过代码的方式来操作数据,也可以实现类似excel的完全的交互方式。让这一切体验很好的前提是:数据单独存储,代码负责渲染属性来实现布局。

在实时编辑的文件中使用的问题是:数据和属性是绑定在一块的,而且用户的行为无法预期,可能会破坏属性,体验不会很好。

2 个赞

大佬,能否实现如图,在边框上加一个 “title” ?