菜鸟求帮忙elisp怎么实现如下功能

现在 在改一个模块的功能,实现如下,从 gerrit服务器上拉去 评审数据,数据是json的格式

    "currentPatchSet": {
        "number": 1,
        "revision": "994361fe09fc8dd2c57ff1a7807777d31baf6cbb",
        "parents": [
            "b16533668dd56cfb8749bbd7ab51c55da5443e56"
        ],
        "ref": "refs/changes/49/305649/1",
        "createdOn": 1681976338,
        "kind": "REWORK",
        "approvals": [
            {
                "type": "Verified",
                "description": "Verified",
                "value": "1",
                "grantedOn": 1681976600,
            },
            {
                "type": "Code-Review",
                "description": "Code-Review",
                "value": "1",
                "grantedOn": 1681979445,
            },
            {
                "type": "Code-Review",
                "description": "Code-Review",
                "value": "2",
                "grantedOn": 1681979508,
            }
        ],
        "sizeInsertions": 16,
        "sizeDeletions": 3
    },

现在想要做的功能,就是解析里面的type对应的 value,整合成一行显示。

显示规则如下:

如果相同type中 value 值存在负数(-1 -2)则不管是否有+1 +2 都显示 x ,

如果相同type中最大 value = +1 则显示 +1,如果最大value 是+2 则显示 √

如果对应的type,最大值是 0 或没有 则显示空,,

然后把所有的type类型现实在一个行内。期望实现的效果如gerrit 首页显示的样子

现在显示的 样子 是这样,想把 这个review 信息 加在尾巴上,

本人 elisp 实在太菜,有人帮忙指点下吗? :joy: :joy: :joy: :joy:

项目地址 https://github.com/darcylee/magit-gerrit/tree/newfeature

解析json 用 (json-read-from-string STRING) 返回的是一个alist.

解析json 倒不是最大的问题,原始项目有解析的方法,主要是 不知道怎么合并和计算 json arry里面取到的值

简单写了个,不保证边界判断以及性能

{
  "approvals": [
    {
      "type": "acr",
      "value": "-1"
    },
    {
      "type": "pc",
      "value": "0"
    },
    {
      "type": "Verified",
      "value": "1"
    },
    {
      "type": "Code-Review",
      "value": "1"
    },
    {
      "type": "Code-Review",
      "value": "2"
    }
  ]
}
(defun trans (value-list)
  (if (and value-list (length> value-list 0))
      (let ((min-v (apply 'min value-list))
            (max-v (apply 'max value-list)))
        (cond
         ((< min-v 0) "x")
         ((= max-v 2) "√")
         ((= max-v 1) "+1")
         (t " ")))
    " "))

(defun test ()
  (let* ((json (with-temp-buffer
                 (insert-file-contents "/tmp/test.json")
                 (json-parse-buffer :object-type 'plist :false-object :json-false :array-type 'list)))
         (approvals (plist-get json :approvals))
         type-values)
    (dolist (elt approvals type-values)
      (let* ((type (intern (concat ":" (plist-get elt :type))))
             (value (string-to-number (plist-get elt :value)))
             (values (or (plist-get type-values type) '())))
        (push value values)
        (setq type-values (plist-put type-values type values))))
    (format "acr:%s cr:%s pc:%s v:%s"
            (trans (plist-get type-values :acr))
            (trans (plist-get type-values :Code-Review))
            (trans (plist-get type-values :pc))
            (trans (plist-get type-values :Verified)))))


(test) ;; => "acr:x cr:√ pc:  v:+1"

(or (plist-get type-values type) '())

(or X nil) 等同于 X,可以直接替换为(plist-get type-values type)

感谢 :smiling_face_with_three_hearts:,初步以json 文件读取 解析,可以达到效果了 但是原来代码运行到这里的时候,已经被解析成lisp对象了,如下这种

[
((type . "pc") (description . "PCheck") (value . "-1") (grantedOn . 1679646873) (by (name . "cf") (email . "[email protected]") (username . "cmf")))
((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1679647276) (by (name . "sv") (email . "[email protected]") (username . "svmm")))
((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1680272580) (by (name . "name1") (email . "[email protected]") (username . "name1")))
((type . "Code-Review") (description . "Code-Review") (value . "1") (grantedOn . 1679888276) (by (name . "yangx") (email . "[email protected]") (username . "yangx")))

需要怎么替换这个解析过程呢?

(defun trans (type value-list)
  (if (and value-list (length> value-list 0))
      (let ((min-v (apply 'min value-list))
            (max-v (apply 'max value-list))
            (t-max-v 1) (t-min-v -1))
        (if (string= "Code-Review" type)
            (progn (setq t-max-v 2) (setq t-min-v -2)))
        (cond
         ((<= min-v t-min-v) "x")
         (t (cond
             ((>= max-v t-max-v) "√")
             ((> min-v 0) (format "+%d" min-v))
             (t (format "%d" min-v))))
         ))
    " "))

trans 函数 按照gerrit 显示规则改了下 :smile:

如果value-list是list的话不需要length>这部分,非nil就够了。

里面的progn setq没必要,一个setq可以带多个赋值,另外这可以直接放在底下的cond里。

另外底下的两层cond也可以简化一下。

我看原始工程用的assoc函数,配合 mapc 去遍历这个list,应该是可以 但是不知道要怎么写 :joy: ,实在太菜了

dolist, 不一定用mapc, 基本的循环语句emacs lisp 都支持。

嗯,试着写下

尴尬 搞半天搞不定

(defun magit-gerrit-wash-approvals-online (approvals)
  (let* (type-values)
    ;; (dolist (labeltuple magit-gerrit-review-labels)
    (dolist (approval approvals type-values)
      (let* ((type (cdr-safe (assoc 'type approval)))
             (value (cdr-safe (assoc 'value approval)))
             (values (plist-get type-values type)))
        ;; (push (and (string= type (car-safe labeltuple)) value) values)
        (push  value values)
        (setq type-values (plist-put type-values type values))))

    (format "CR:%-2s PC:%-2s Vr:%-2s"
            (magit-gerrit-trans-score "Code-Review" (plist-get type-values :Code-Review))
            (magit-gerrit-trans-score "PSTL-Check" (plist-get type-values :PSTL-Check))
            (magit-gerrit-trans-score "Verified" (plist-get type-values :Verified))))
  ;; )
  )

(defun magit-gerrit-wash-review ()
  (let* ((beg (point))
         (jobj (json-read))
         (end (point))
         (num (cdr-safe (assoc 'number jobj)))
         (subj (cdr-safe (assoc 'subject jobj)))
         (br (cdr-safe (assoc 'branch jobj)))
         (owner (cdr-safe (assoc 'owner jobj)))
         (owner-name (cdr-safe (assoc 'name owner)))
         ;; (owner-email (cdr-safe (assoc 'email owner)))
         (patchsets (cdr-safe (assoc 'currentPatchSet jobj)))
         (patchset-num (cdr-safe (assoc 'number patchsets)))
         (last-update (cdr-safe (assoc 'lastUpdated jobj)))
         (size-insert (cdr-safe (assoc 'sizeInsertions patchsets)))
         (size-delete (cdr-safe (assoc 'sizeDeletions patchsets)))
         ;; compare w/t since when false the value is => :json-false
         (isdraft (eq (cdr-safe (assoc 'isDraft patchsets)) t))
         (approvs (cdr-safe (if (listp patchsets)
                                (assoc 'approvals patchsets)
                              (assoc 'approvals (aref patchsets 0)))))
         (scoreinfo (magit-gerrit-wash-approvals-online (ensure-list approvs)))
         )
    (if (and beg end)
        (delete-region beg end))
    (when (and num subj owner-name)
      (magit-insert-section (section subj)
        (insert (propertize
                 (magit-gerrit-pretty-print-review num
                                                   patchset-num
                                                   subj
                                                   owner-name
                                                   br
                                                   size-insert
                                                   size-delete
                                                   last-update
                                                   ;; "CR:√  PC:x  Vr:√ "
                                                   scoreinfo
                                                   isdraft)
                 'magit-gerrit-jobj
                 jobj))
        (add-text-properties beg (point) (list 'magit-gerrit-jobj jobj)))
      t)))

出现错误

Debugger entered--Lisp error: (wrong-type-argument listp [((type . "PSTL-Check") (description . "PSTL-Check") (value . "-1") (grantedOn . 1679646873) (by (name . "gcf") (email . "[email protected]") (username . "gcf"))) ((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1679647276) (by (name . "ssr") (email . "[email protected]") (username . "ssr"))) ((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1680272580) (by (name . "smy") (email . "[email protected]") (username . "sunmingyang"))) ((type . "Code-Review") (description . "Code-Review") (value . "1") (grantedOn . 1679888276) (by (name . "yxy") (email . "[email protected]") (username . "yxy")))])
  assoc(type [((type . "PSTL-Check") (description . "PSTL-Check") (value . "-1") (grantedOn . 1679646873) (by (name . "gcf") (email . "[email protected]") (username . "gcf"))) ((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1679647276) (by (name . "ssr") (email . "[email protected]") (username . "ssr"))) ((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1680272580) (by (name . "smy") (email . "[email protected]") (username . "sunmingyang"))) ((type . "Code-Review") (description . "Code-Review") (value . "1") (grantedOn . 1679888276) (by (name . "yxy") (email . "[email protected]") (username . "yxy")))])
  (cdr-safe (assoc 'type approval))
  (let* ((type (cdr-safe (assoc 'type approval))) (value (cdr-safe (assoc 'value approval))) (values (plist-get type-values type))) (setq values (cons value values)) (setq type-values (plist-put type-values type values)))
  (let ((approval (car --dolist-tail--))) (let* ((type (cdr-safe (assoc 'type approval))) (value (cdr-safe (assoc 'value approval))) (values (plist-get type-values type))) (setq values (cons value values)) (setq type-values (plist-put type-values type values))) (setq --dolist-tail-- (cdr --dolist-tail--)))
  (while --dolist-tail-- (let ((approval (car --dolist-tail--))) (let* ((type (cdr-safe (assoc 'type approval))) (value (cdr-safe (assoc 'value approval))) (values (plist-get type-values type))) (setq values (cons value values)) (setq type-values (plist-put type-values type values))) (setq --dolist-tail-- (cdr --dolist-tail--))))
  (let ((--dolist-tail-- approvals)) (while --dolist-tail-- (let ((approval (car --dolist-tail--))) (let* ((type (cdr-safe (assoc ... approval))) (value (cdr-safe (assoc ... approval))) (values (plist-get type-values type))) (setq values (cons value values)) (setq type-values (plist-put type-values type values))) (setq --dolist-tail-- (cdr --dolist-tail--)))) type-values)
  (let* (type-values) (let ((--dolist-tail-- approvals)) (while --dolist-tail-- (let ((approval (car --dolist-tail--))) (let* ((type (cdr-safe ...)) (value (cdr-safe ...)) (values (plist-get type-values type))) (setq values (cons value values)) (setq type-values (plist-put type-values type values))) (setq --dolist-tail-- (cdr --dolist-tail--)))) type-values) (format "CR:%-2s PC:%-2s Vr:%-2s" (magit-gerrit-trans-score "Code-Review" (plist-get type-values :Code-Review)) (magit-gerrit-trans-score "PSTL-Check" (plist-get type-values :PSTL-Check)) (magit-gerrit-trans-score "Verified" (plist-get type-values :Verified))))
  magit-gerrit-wash-approvals-online(([((type . "PSTL-Check") (description . "PSTL-Check") (value . "-1") (grantedOn . 1679646873) (by (name . "gcf") (email . "[email protected]") (username . "gcf"))) ((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1679647276) (by (name . "ssr") (email . "[email protected]") (username . "ssr"))) ((type . "Verified") (description . "Verified") (value . "1") (grantedOn . 1680272580) (by (name . "smy") (email . "[email protected]") (username . "sunmingyang"))) ((type . "Code-Review") (description . "Code-Review") (value . "1") (grantedOn . 1679888276) (by (name . "yxy") (email . "[email protected]") (username . "yxy")))]))

dolist 不能传 array list吗?

看报错里说approval是一个vector啊?看最外面的方括号

[] 是 vector, () 才是 list, dolist 显然只能操作 list

1 个赞

dolist的确不能传array,虽然你传的也不是array,在magit-gerrit-wash-review里,approvs是array,(ensure-list approvs)是list of array。不需要用ensure-list,直接传approvs

那么在magit-gerrit-wash-approvals-online里,approvals就是一个array,按照你的描述,是一个array of alist。所以dolist不能用,相对应的,你可以用seq-doseq或者mapc

按照上面给出的transvalue-list是list of number,所以value要用string-to-number

由于type是string,而plist-geteq做comparison,所以(plist-get type-values type)可能会是nil。这里可以用type-values可以用alist,而不是plist。然后用assoc,或者alist-get指定testfn。另,push是destructive的,之后不用再setq

所以:

(defun magit-gerrit-wash-approvals-online (approvals)
  (let* (type-values)
    (seq-doseq (approval approvals)
      (let ((type (cdr (assq 'type approval)))
            (value (string-to-number (cdr (assq 'value approval)))))
        (push value (alist-get type type-values nil nil #'string-equal))))
    (format "CR:%-2s PC:%-2s Vr:%-2s"
            (magit-gerrit-trans-score "Code-Review"
                                      (cdr (assoc "Code-Review" type-values)))
            (magit-gerrit-trans-score "PSTL-Check"
                                      (cdr (assoc "PSTL-Check" type-values)))
            (magit-gerrit-trans-score "Verified"
                                      (cdr (assoc "Verified" type-values))))))
1 个赞

底下format那一块有点重复代码,感觉可以用apply + mapcar改写一下。

厉害了。。学习消化下,现在用了这个代码显示结果是ok了,非常感谢! :smiling_face_with_three_hearts: :smiling_face_with_three_hearts:

后续得再继续优化下

  1. √、x、-1、+1 这些标志赋上颜色属性,这个可以参考现有的改改。
  2. “Code-Review”,“PSTL-Check”,“Verified” 这些type,实际来自custom变量magit-gerrit-review-labels里面指定的,相当于可配置用户关心的type,这部分后续还得优化下。
magit-gerrit-review-labels is a variable defined in ‘magit-gerrit.el’.
Its value is
(("Code-Review" "CR")
 ("Verified" "Ve")
 ("PSTL-Check" "PC"))

恩,这块是要再改下,具体type也是来自custom变量magit-gerrit-review-labels,不是写死的。

既然是这样的结构,那就是用mapconcat之类的函数,里面用个format写每一个部分的内容