一个类似于company-mode候选列表的列表是怎么显示出来的?

我想给kill-ring搞个类似的。。。

1 个赞

這個怎麼樣


單純說kill-ring的話,我用counsel-yank-pop配合ivy-posframe挺好的

company-mode 的应该是用 (elisp) Overlays 自己写的;auto-complete 用的是一个专门的库 popup.el,用的也是 Overlay。

等有机会我试试从头写个,我也好奇这个效果是怎么模拟出来的。

喔喔,我折腾了一个四不像出来,最懒的方法,没用 Overlay:

(global-set-key (kbd "C-c y")
                '(lambda ()
                   (interactive)
                   (let* ((trim (lambda (str)
                                  (when (stringp str)
                                    (setq str (string-trim str))
                                    (while (equal (string-to-char str)
                                                  (string-to-char "\n"))
                                      (setq str (string-trim-left (substring str 1)))))
                                  str))
                          (trim-kill-ring (lambda (tkr kr)
                                            (when (consp kr)
                                              (setcar kr (funcall trim (car kr)))
                                              (funcall tkr tkr (cdr kr))))))
                     (funcall trim-kill-ring trim-kill-ring kill-ring)
                     (setcdr yank-menu (mapcar (lambda (item)
                                                 (let ((str (funcall trim (car item))))
                                                   (cons str (cdr item))))
                                               (cdr yank-menu)))
                     (popup-menu 'yank-menu))))

其实lambda里只要最后一行的(popup-menu 'yank-menu)就行,不过在terminal环境有个问题,就是如果开头是换行符的话,字符串的内容显示不出来,所以多了这一大堆。不过这个问题还是有。。。

还有一个就是我不太想改kill-ringyank-menu的内容。

期待你的结果。喔,不对,我有空再看看 Overlay相关的。。。

1 个赞

里面有个make-popup,我没找到定义,应该是很关键的函数。。。

我个人是不太想就为kill-ring下一个包下来。

或者直接寫一個company的backend也行

(defun cm/company-kill-ring (command &optional arg &rest _)
  (interactive (list 'interactive))
  (let ((kill (progn
                (ignore-errors (current-kill 0))
                ;; Keep things consistent with the rest of Emacs
                (dolist (sym '(kill-ring kill-ring-yank-pointer))
                  (set sym (cl-delete-duplicates
                            (cl-delete-if-not counsel-yank-pop-filter (symbol-value sym))
                            :test #'equal-including-properties :from-end t)))
                kill-ring)))
    (cl-case command
      (interactive (company-begin-backend #'cm/company-kill-ring))
      (prefix (if kill (company-grab ".*")))
      (candidates kill)
      (no-cache t)
     )))

Update 如果candidates太長,適當截斷

(defun cm/company-kill-ring (command &optional arg &rest _)
  (interactive (list 'interactive))
  (let* ((kill (progn
                 (ignore-errors (current-kill 0))
                 ;; Keep things consistent with the rest of Emacs
                 (dolist (sym '(kill-ring kill-ring-yank-pointer))
                   (set sym (cl-delete-duplicates
                             (cl-delete-if-not counsel-yank-pop-filter (symbol-value sym))
                             :test #'equal-including-properties :from-end t)))
                 kill-ring))
         (shorten (mapcar (lambda (s)
                            (if (> (length s) 20)
                                (concat (substring s 0 16) "...")
                              s))
                          kill)))
    (cl-case command
      (interactive (company-begin-backend #'cm/company-kill-ring))
      (prefix (if kill (company-grab ".*")))
      (candidates shorten)
      (no-cache t)
      (require-match nil)
      (post-completion
       (save-match-data
         (when (search-backward arg nil t)
           (replace-match (nth (cl-position arg shorten :test #'string=)
                               kill) nil t)))))))
1 个赞

缺点是需要开company-mode。感觉在说废话,不过我经常在用ansi-term时复制粘贴,这种情况,开company-mode不太合适。。。

不是有个了吗?https://github.com/waymondo/popup-kill-ring

這個不但要用popup,還要用pos-tip的。依賴好重

楼主可以参考啊,其实也足够用了,虽然我现在已经不用了。

開了好像也沒什麼,我沒有觸發奇奇怪怪的backend。

另外可以make-variable-local改造company-backends company-backends本身就是buffer-local然後在ansi-term buffer設置爲nil手動屏蔽backend的觸發吧

现在会改列表里显示的内容了,虽然property没用(x-popup-menu是C函数),但还是先留着。几个使用 Overlay 的例子看着比较难受,一步步来吧。

(global-set-key (kbd "C-c y") ;; '(lambda () (interactive) (popup-menu 'yank-menu))
                '(lambda ()
                   (interactive)
                   (setcdr yank-menu
                           (mapcar (lambda (item)
                                     (let ((str-ori (car item)))
                                       (set-text-properties 0 (length str-ori) nil str-ori)
                                       (let* ((str (let ((newline "\\n")
                                                         (tab "\\t")
                                                         (nl (string-to-char "\n"))
                                                         (tb (string-to-char "\t")))
                                                     (add-face-text-property 0 2 '(:box t) nil newline)
                                                     (add-face-text-property 0 2 '(:box t) nil tab)
                                                     (apply 'concat
                                                            (mapcan (lambda (char)
                                                                      (cond ((char-equal char nl) `(,newline))
                                                                            ((char-equal char tb) `(,tab))
                                                                            (t `(,(char-to-string char)))))
                                                                    (string-to-list str-ori)))))
                                              (str (if (>= (window-width) (length str)) str
                                                     (let ((ell "..."))
                                                       (add-face-text-property 0 3 '(:underline t) nil ell)
                                                       (concat (substring str 0 (- (window-width) 3)) ell)))))
                                         (cons str-ori (cons str (cddr item))))))
                                   (cdr yank-menu)))
                   (popup-menu 'yank-menu)))