textDocument/documentSymbol + avy

發現documentSymbol結合avy很好用,可以快速跳轉到定義/引用。 剛剛給ccls增加了一個bool all參數,可以顯示所有符號,而不僅是默認的outline。

(defun +my/avy-document-symbol ()
  (interactive)
  (let (ranges point0 point1 (line 0) (col 0) (w (selected-window)) candidates)
    (save-excursion
      (goto-char 1)
      (dolist (loc
               (lsp--send-request (lsp--make-request
                                   "textDocument/documentSymbol"
;; :all t is specific to ccls . cquery does not have this parameter
                                   `(:textDocument ,(lsp--text-document-identifier) :all t))))
        (let ((range (->> loc (gethash "location") (gethash "range"))))
          (-let* [((&hash "line" l0 "character" c0) (gethash "start" range))
                  ((&hash "line" l1 "character" c1) (gethash "end" range))]
            (when (or (< line l0) (and (= line l0) (<= col c0)))
              (forward-line (- l0 line))
              (forward-char c0)
              (setq point0 (point))
              (forward-line (- l1 l0))
              (forward-char c1)
              (setq point1 (point))
              (setq line l1 col c1)
              (push `((,point0 . ,point1) . ,w) candidates))))))
    (avy-with avy-document-symbol
      (avy--process candidates
                    (avy--style-fn avy-style)))))

這個改進下感覺可以扔到lsp-ui裏去。

我把;绑定到

 :n ";"    (λ! (+my/avy-document-symbol) (+my/find-definitions))

效果:meow

2 个赞

初版用textDocument/documentSymbol發送整個buffer的SymbolInformation,發現Emacs裏處理效率很低(JSON deserialization有一定開銷,主要開銷在於用forward-line forward-char獲取point位置)。對於幾千行的buffer,延遲4秒,難以接受。

現在在ccls中,把:all t改成startLineendLine兩個,用戶端提供(window-start) (window-end),伺服端提供區間內的部分SymbolInformation。效率可以接受了

;;;###autoload
(defun +my/avy-document-symbol ()
  (interactive)
  (let ((line 0) (col 0) (w (selected-window))
        (ccls (memq major-mode '(c-mode c++-mode objc-mode)))
        (start-line (1- (line-number-at-pos (window-start))))
        (end-line (1- (line-number-at-pos (window-end))))
        ranges point0 point1
        candidates)
    (save-excursion
      (goto-char 1)
      (cl-loop for loc in
               (lsp--send-request (lsp--make-request
                                   "textDocument/documentSymbol"
                                   `(:textDocument ,(lsp--text-document-identifier)
                                                   :startLine ,start-line :endLine ,end-line)))
               for range = (if ccls loc (->> loc (gethash "location") (gethash "range")))
               for range_start = (gethash "start" range)
               for range_end = (gethash "end" range)
               for l0 = (gethash "line" range_start)
               for c0 = (gethash "character" range_start)
               for l1 = (gethash "line" range_end)
               for c1 = (gethash "character" range_end)
               while (<= l0 end-line)
               when (>= l0 start-line)
               do
               (forward-line (- l0 line))
               (forward-char c0)
               (setq point0 (point))
               (forward-line (- l1 l0))
               (forward-char c1)
               (setq point1 (point))
               (setq line l1 col c1)
               (push `((,point0 . ,point1) . ,w) candidates)))
    (avy-with avy-document-symbol
      (avy--process candidates
                    (avy--style-fn avy-style)))))
1 个赞

emacs里有个概念好像是叫syntax-table,用来表明当前的char的类型,比如说是不是字符串或注释。

它算是一个简单的tokenize,比forward–line等的效率高些。

而且他是自动生成的,如果能再扩展一下就好了。