Org-mode 中文行内格式化的问题

在org-mode中,有下标的时候会改变行高,这个可以设置吗?如何才能等高?

改变行高看着就不舒服了。

最新版的 org-mode 通过设置变量 org-emphasis-regexp-components 来支持中文行内格式化的方法貌似已经没用了。

Org mode version 9.2.3 (9.2.3-17-g4df705-elpaplus @ /home/james/.emacs.d/elpa/26.2/develop/org-plus-contrib-20190513/)

有同学碰到相同的问题吗?

org-chinese-utils 感觉好像是我写的,后来删库了,现在还能找到?

1 个赞

偶然在 Org-mode 的手册里发现了一个 zero width space,可以解决中文两边必须有空格的问题,同时不会影响展示效果(Spacemacs)。

使用方法:在需要插入空格的地方输入

C-x 8 <RET> zero width space <RET>

链接:Escape Character (The Org Manual)

4 个赞

在orgmode里开启goto-address-mode就可以高亮URL了

5年过去了……有更好的解决方案了吗?:joy:

我也想知道现在有没有更好的办法解决org-mode中文行内格式化的问题。

最近有了新思路:

先 selection 要 markup 的字段,然后键入相应的 markup 符号,自动在其前后插入 zero width space. 副作用是光标经过 zero width space 时会变细,不过不影响整体美观,操作也比较方便。

;;;###autoload
(defmacro org-surround-markup (&rest keys)
  "Define and bind interactive commands for each of KEYS that surround the region or insert text.
Commands are bound in `org-mode-map' to each of KEYS.  If the
region is active, commands surround it with the key character,
otherwise call `org-self-insert-command'."
  `(progn
     ,@(cl-loop for key in keys
                for name = (intern (concat "unpackaged/org-maybe-surround-" key))
                for docstring = (format "If region is active, surround it with \"%s\", otherwise call `org-self-insert-command'." key)
                collect `(defun ,name ()
                           ,docstring
                           (interactive)
                           (if (region-active-p)
                               (let ((beg (region-beginning))
                                     (end (region-end)))
                                 (save-excursion
                                   (goto-char end)
                                   (insert ,key)
                                   (insert-char #x200b) ;; Insert zero width space to make inline markup work.
                                   (goto-char beg)
                                   (insert-char #x200b)
                                   (insert ,key)))
                             (call-interactively #'org-self-insert-command)))
                collect `(define-key org-mode-map (kbd ,key) #',name))))

(org-surround-markup "~" "=" "*" "/" "_" "+" "$")

Evil 用户可以用 evil surround 来搞定。

(use-package evil-surround
  :after evil
  :config
  (global-evil-surround-mode 1)
  ;; Use non-spaced pairs when surrounding with an opening brace.
  ;; Insert zero width space for org inline markup.
  (evil-add-to-alist 'evil-surround-pairs-alist
                      ?\* '("\x200B*" . "*\x200B")
                      ?\+ '("\x200B+" . "+\x200B")
                      ?\/ '("\x200B/" . "/\x200B")
                      ?\~ '("\x200B~" . "~\x200B")
                      ?\= '("\x200B=" . "=\x200B")
                      ?\$ '("\x200B$" . "$\x200B")
                      ?\_ '("\x200B_" . "_\x200B")))

参考:

  1. GitHub - alphapapa/unpackaged.el: A collection of useful Emacs Lisp code that isn't substantial enough to be packaged
  2. org mode - Mark up only part of a word - Emacs Stack Exchange
3 个赞

一个思路:markdown-mode 里面就没有这个问题,可以试着参考 markdown-mode 里面处理特殊格式的机制修改 org-mode 代码。 2012年的时候有讨论这个问题的,当时有人说 org-mode 不再支持 emacs22 之后可以实现这个功能。https://list.orgmode.org/[email protected]/

1 个赞

我也觉得自动加零宽空格是最佳方案 (相对于修改 org-emphasis-regex-components 来说). 我做得更细致一些, 加了些判断:

  • 选择区域开始和结尾的字符是西文还是中文(如果两端有空白符号, 自动重新选择直到第一个非空白字符为止)
  • 起始字符的左边, 终止字符的右边是西文还是中文, 或者是空白符号

然后就可以设置中文碰到中文加零宽空格, 西文碰到中文加普通空格, 西文碰到西文也加零宽空格 (因为西文一般都手动输入空格了, 如果没有已经存在的空格, 一般是需要 markup 一个单词的一半的情况, 此时不适合加入空格, 比如如果我要强调 orgmode 是一个 “mode” 那么我可以写 orgmode), 如果原来就已经有空格了就什么都不加. 如果没有选择任何 region, 那么默认要输入的是中文(大部分情况), 然后按照中文内容的逻辑去判断左右是否要加零宽空格/普通空格/啥都不加

现在用得蛮爽的

2021-10-23T00:00:00Z 代码如下(因为有人要). 直接 copy paste了, 写给自己的东西所以肯定有各种不严谨, 但是我一直用得很愉快, 有时候发现有不对的地方也会自己改一下, 但是已经很长时间没动过了, 如果有需要的人也可以自己改. 代码注释大部分是有意义的, 但可能有少部分因为改了代码没改注释所以没意义. 举例来说最开始我只是想写一个对 org 选区进行自动斜体的函数, 因此注释里都说各种“添加 /”( org 的斜体 markup 符号), 但现在 me/org-emphasize-dwim 这个函数用来添加任意 markup 符号 (/*_+~=) 都可以.

下面代码中定义了两个非交互函数:

  • me/non-western-notation-p :: 自己写的一个对 cursor 前后的字符是否为“中文”(可能更好的说法是“非西文”)进行判定, 这个判定是根据我自己的需要写的. 这个函数在下面的 me/org-emphasize-dwim 中使用
  • me/org-emphasize-dwim :: 非交互函数, 接受一个参量, 即所要使用的 markup 字符 (/*_+~=) 中的任意一个, 然后根据选区两端以及两端左右的字符的类型, 添加 markup 符号, 并且分情况添加空格/零宽空格/啥都不加

最后有一系列利用 me/org-emphasize-dwim 定义的 emphasize 快捷键, 当然你可以改为自己用的快捷键.

(defun me/non-western-notation-p (str)
  "西文以及常用符号的补集, 所谓的“中文”符号判定"
  (let ((non-w-notation-regex "[^[:ascii:]éèêàîïµΩçπœ]"))
    (cond
     ((equal str "before")
      (if (looking-back non-w-notation-regex)
	  t nil))
     ((equal str "after")
      (if (looking-at non-w-notation-regex)
	  t nil))
     (t (ding) (message "me/non-western-notation-p 参量错误")))))

(defun me/org-emphasize-dwim (mark)
  (let (len-to-cover-until-end
	pos-cen-of-pair
	start
	end)    
    (if (org-region-active-p) ; 如果有选中文字
	(progn
	  (setq start (region-beginning)
		end (region-end))
	  (goto-char end)
	  (skip-chars-backward "[:blank:]​") ; 跳过所有的 SPC \t 和零宽空格. 注意这里非常非常地 tricky, looking-at/back 中的参量必须是 [[:blank:]], 而 skip-chars-forward/backward 中的参量必须是 [:blank:]
	  (setq end (point)) 
	  (goto-char end)
	  (when (<= end start)
	    (ding)
	    (cl-return-from 'me/org-emphasize-dwim "选区只有空格和零宽空格"))
	  ;; 此时 cursor 在 end 位置, 开始处理右半边
	  (if (me/non-western-notation-p "before") ; 当左边为“中文”时
	      (cond
	       ((looking-at "[[:blank:]​.,:!?;'\"]") ; 当右边为 SPC \t 零宽空格, 或常用的标点符号时
		(insert mark) ; 仅仅插入 /
		(cl-incf end))  
	       ((or (me/non-western-notation-p "after")
		    (looking-at "\n")) ; 当右边同为“中文”时 (当右边换行, 也假设接下来我们可能在之后继续输入中文)
		(insert (concat mark "​")) ;插入 / + 零宽空格
		(cl-incf end 2)) 
	       (t
		(insert (concat mark " ")) ; 剩余情况(主要是西文和常用字符), 插入 / + 空格
		(cl-incf end 2)))
	    (cond ; 当左边不为“中文”时 (“西文”以及常用字符)
	     ((looking-at "[[:blank:]​.,:!?;'\"]") ; 当右边为 SPC \t 零宽空格, 或常用的标点符号时
	      (insert mark) ; 仅仅插入 /
	      (cl-incf end))  
	     ((or (me/non-western-notation-p "after")
		  (looking-at "\n")) ; 当右边为“中文”时 (当右边换行, 也假设接下来我们可能在之后继续输入中文)
	      (insert (concat mark " "))  ;插入 / + 空格
	      (cl-incf end 2))
	     (t
	      (insert (concat mark "​")) ; 剩余情况(主要是非“中文”符号)时, 插入 / + 零宽空格
	      (cl-incf end 2))))  
	  (goto-char start)
	  (skip-chars-forward "[:blank:]​") ; 跳过所有的 SPC \t 和零宽空格
	  (setq len-to-cover-until-end (- end (point)))
	  ;; 此时 cursor 在 start 位置, 开始处理左半边
	  (if (me/non-western-notation-p "after") ; 当右为“中文”时
	      (cond
	       ((looking-back "[[:space:]]") ; 当左边为 SPC \n \t 或零宽空格时
		(insert mark))  ; 仅仅插入 /
	       ((me/non-western-notation-p "before") ; 当左边同为“中文”时
		(insert (concat "​" mark))) ;插入 零宽空格 + /
	       (t (insert (concat " " mark)))) ; 剩余情况(主要是西文和常用字符), 插入 空格 + /
	    (cond ; 当右边不为“中文”时 (“西文”以及常用字符)
	     ((looking-back "[[:space:]]") ; 当左边为 SPC \n 或零宽空格时
	      (insert mark))  ; 仅仅插入 /
	     ((me/non-western-notation-p "before") ; 当左边为“中文”时
	      (insert (concat " " mark))) ;插入 / + 空格
	     (t (insert (concat "​" mark))))) ; 剩余情况(主要是非“中文”符号)时, 插入 / + 零宽空格
	  (forward-char len-to-cover-until-end))
      (cond ; 当没有区域选中时, 当 markup symbol 为斜体、粗体、中横线时, 直接假设我们接下来要输入的是中文
       ((or (equal mark "/")
	    (equal mark "*")
	    (equal mark "+"))		
	 (cond		; 分情况插入左半边的 mark
	  ((looking-back "[[:space:]]")
	   (insert mark))
	  ((me/non-western-notation-p "before")
	   (insert (concat "​" mark)))
	  (t
	   (insert (concat " " mark))))
	 (setq pos-cen-of-pair (point))
	 (cond				; 分情况插入右半边的 mark
	  ((looking-at "[[:blank:]​.,:!?;'\"]") 
	   (insert mark))
	  ((or (me/non-western-notation-p "after")
	       (looking-at "\n")) ; 当出现换行符时, 依然假设我们可能会在之后补写中文
	   (insert (concat mark "​")))
	  (t
	   (insert (concat mark " "))))
	 (goto-char pos-cen-of-pair))
       (t ; 剩余情况 (markup symbol 为 code 和 verbatim 时), 直接假设我们接下来要输入的是英文
	(cond		; 分情况插入左半边的 mark
	  ((looking-back "[[:space:]]")
	   (insert mark))
	  ((me/non-western-notation-p "before")
	   (insert (concat " " mark)))
	  (t
	   (insert (concat "​" mark))))
	 (setq pos-cen-of-pair (point))
	 (cond				; 分情况插入右半边的 mark
	  ((looking-at "[[:blank:]​.,:!?;'\"]") 
	   (insert mark))
	  ((or (me/non-western-notation-p "after")
	       (looking-at "\n")) ; 当出现换行符时, 依然假设我们可能会在之后补写中文
	   (insert (concat mark " ")))
	  (t
	   (insert (concat mark "​"))))
	 (goto-char pos-cen-of-pair))))))

(define-key org-mode-map (kbd "s-b") (lambda () (interactive) (me/org-emphasize-dwim "*")))
(define-key org-mode-map (kbd "s-i") (lambda () (interactive) (me/org-emphasize-dwim "/")))
(define-key org-mode-map (kbd "s-k") (lambda () (interactive) (me/org-emphasize-dwim "~")))
(define-key org-mode-map (kbd "s-l") (lambda () (interactive) (me/org-emphasize-dwim "_")))
(define-key org-mode-map (kbd "s-=") (lambda () (interactive) (me/org-emphasize-dwim "=")))
(define-key org-mode-map (kbd "s-+") (lambda () (interactive) (me/org-emphasize-dwim "+")))
1 个赞

赞呐,方便分享下你的代码吗? :star_struck:

你可以参考一下这个

1 个赞

好东西,感谢分享!

代码我添加在我的第一个回复里了, 希望对你有用

1 个赞

非常实用,赞啊! :+1:

我有个简单的建议,既然zero width space可以用来解决这个问题,为什么不简单的绑定一个输入zero width space的快捷键,然后写orgmode的时候,插入就行了?比如,插入~的时候,直接插入zero width space ~?

像上面那样想办法自动插入不是更省心么

(defun my/insert-zero-width-space ()

(interactive)

(insert-char ?\u200B)) ;; code for ZERO WIDTH SPACE

(global-set-key (kbd “C-x 8 s”) ‘my/insert-zero-width-space)

网上找的:Emacs Org-mode Styling, Non-smart Quotes, Zero-width-space, and TeX Input Method | Sean Miller: The Wandering Coder

加空格也很难受。bibtex条目经常是 史永东2021中债估值识别了债券信用风险吗 这种,加了空格就出bug

我的思路是编辑 org 时,一换行就自动进行 search regexp 然后插入分隔符(中英用空格,中中用零宽空格),据此写了个小函数 ingtshan/separate-inline.el: Separating words automatically with given regexp screencapture

1 个赞