我有一些 JSON 文件,我正在编写一种模式,允许独立于其余部分编辑 JSON 对象的单个属性。例如:
foo.json:
{
"creation_timestamp": "1411210038.000000",
"description": "lorem ipsum.\ndolor sit amet.",
"version": 4
}
打开 foo.json 会产生这个缓冲区:
lorem ipsum.
dolor sit amet.
将第一行更改为“foo bar”并保存文件会导致
foo.json
只有 description
字段更新:{
"creation_timestamp": "1411210038.000000",
"description": "foo bar.\ndolor sit amet.",
"version": 4
}
最好的策略是什么?我目前的尝试是这样的:
description
的值点属性,创建“ View ”local-write-file
钩子(Hook)杀死“ View ”,更新覆盖中的json,并保存文件。 after-save
钩子(Hook)重新创建“ View ”,以便用户可以继续编辑。这是冗长而脆弱的。有没有更好的方法来处理屏幕表示应该不同于磁盘表示的数据?
最佳答案
您可以在 format-alist
中定义自己的编码和解码。为了这个目的。
您的示例可以通过以下方式实现:
(defvar-local my-head nil
"Header of json file cut off by json-descr format.")
(defvar-local my-tail nil
"Tail of json file cut off by json-descr format.")
(defun my-from-fn (BEGIN END)
"`format-alist'"
(save-restriction
(narrow-to-region BEGIN END)
(goto-char (point-min))
(let* ((b (re-search-forward "^[[:blank:]]*\"description\":[[:blank:]]*\"" nil t))
(e (ignore-errors (1- (scan-sexps (1- b) 1)))))
(unless (and b e)
(error "Error in original mode")) ;;< TODO some more sensible error message
;; Save head and tail and delete corresponding buffer regions:
(setq-local my-head (buffer-substring-no-properties (point-min) b))
(setq-local my-tail (buffer-substring-no-properties e (point-max)))
(delete-region e (point-max))
(delete-region (point-min) b)
;; Formatting:
(goto-char (point-min))
(while (search-forward "\\n" nil t)
(replace-match "\n"))
)
(point-max) ;;< required by `format-alist'
))
(defun my-to-fn (BEGIN END BUFFER)
"`format-alist'"
(save-restriction
(narrow-to-region BEGIN END)
;; Formatting:
(goto-char (point-min))
(while (search-forward "\n" nil t)
(replace-match "\\\\n"))
;; Insert head and tail:
(let ((head (with-current-buffer BUFFER my-head))
(tail (with-current-buffer BUFFER my-tail)))
(goto-char (point-min))
(insert head)
(goto-char (point-max))
(insert tail))
(point-max)))
(add-to-list 'format-alist
'(json-descr
"File format for editing a single property of a json object."
nil
my-from-fn
my-to-fn
t ; MODIFY: my-to-fn modifies the buffer
nil
nil))
(define-derived-mode my-mode fundamental-mode "JDescr"
"Major mode for editing json description properties."
(format-decode-buffer 'json-descr))
实际上,也可以将其解释为更普遍的问题。将文件加载到隐藏缓冲区中。使用另一个可见缓冲区来编辑其转换后的内容。在保存可见缓冲区时,实际上将内容转换回原始格式并保存隐藏缓冲区。
我现在没有时间实现上述一般情况。
以下代码大致涵盖了您的特殊情况。 (请注意,这是一个快速破解,仅用于演示目的。)
(defvar-local original-mode-other nil
"Other buffer related to the current one.")
(define-derived-mode original-mode special-mode ""
"Opens file in invisible auxiliary buffer."
(let* ((b (re-search-forward "^[[:blank:]]*\"description\":[[:blank:]]*\"" nil t))
(e (ignore-errors (1- (scan-sexps (1- b) 1))))
(original-name (buffer-name))
(original-buffer (current-buffer))
str)
(unless (and b e)
(error "Error in original mode")) ;; TODO some more sensible error message
(narrow-to-region b e)
(setq str (buffer-substring-no-properties b e))
(rename-buffer (concat " *" original-name))
(with-current-buffer (switch-to-buffer (get-buffer-create original-name))
;; Set-up the clone buffer for editing the transformed content:
(set-visited-file-name (buffer-file-name original-buffer) t)
(setq original-mode-other original-buffer)
(insert str)
(set-buffer-modified-p nil)
;; Transform content to the format of the clone buffer:
(goto-char (point-min))
(while (search-forward "\\n" nil t) ;; TODO: Skip escaped \n.
(replace-match "\n"))
(add-to-list 'write-contents-functions (lambda ()
;; Transfer content to original buffer
(let ((str (buffer-substring-no-properties (point-min) (point-max))))
(with-current-buffer original-mode-other
(let ((inhibit-read-only t))
(delete-region (point-min) (point-max))
(insert str)
(goto-char (point-min))
;; Transform content to the format of the original buffer:
(while (search-forward "\n" nil t)
(replace-match "\\\\n"))
(save-buffer)
)))
(set-buffer-modified-p nil)
t))
(add-hook 'kill-buffer-hook (lambda ()
(kill-buffer original-mode-other)) t t)
)))
关于json - Emacs 中类似模型/ View 的编辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26488808/