cquery-project / emacs-cquery

Emacs client for cquery, a low-latency language server supporting multi-million line C++ code-bases
116 stars 14 forks source link

`cquery--publish-semantic-highlighting' is very slow #7

Closed yyjjl closed 6 years ago

yyjjl commented 6 years ago

cquery--publish-semantic-highlighting is very slow because cquery--read-range calls lsp--position-to-point. It needs to walk from beginning of buffer to the given point on every call. If we can collect all ranges first and sort them by line number, only one pass is required to publish semantic highlighting.

I wrote a simple example as below:

(defun cquery--publish-semantic-highlighting (_workspace params)
  "Publish semantic highlighting information according to PARAMS."
  (when cquery-sem-highlight-method
    (let* ((file (lsp--uri-to-path (gethash "uri" params)))
           (buffer (find-buffer-visiting file))
           (symbols (gethash "symbols" params)))
      (when buffer
        (with-current-buffer buffer
          (save-excursion
            (with-silent-modifications
              (cquery--clear-sem-highlights)
              (let ((last-line 0)
                    ranges point1 point2)
                (dolist (symbol symbols)
                  (-when-let (face (funcall cquery-sem-face-function symbol))
                    (setq ranges
                          (nconc (--map (let ((start (gethash "start" it))
                                              (end (gethash "end" it)))
                                          (list (cons (gethash "line" start)
                                                      (gethash "character" start))
                                                (cons (gethash "line" end)
                                                      (gethash "character" end))
                                                face))
                                        (gethash "ranges" symbol))
                                 ranges))))
                ;; sort ranges by line number
                (setq ranges (sort ranges (lambda (x y) (< (caar x) (caar y)))))
                (save-excursion
                  (goto-char (point-min))
                  (loop for (start end face) in ranges
                        do (progn
                             (forward-line (- (car start) last-line))
                             (forward-char (cdr start))
                             ;; start of range
                             (setq point1 (point))
                             (forward-line (- (car end) (car start)))
                             (forward-char (cdr end))
                             ;; end of range
                             (setq point2 (point))
                             (cquery--make-sem-highlight (cons point1 point2) buffer face)
                             (setq last-line (car end)))))))))))))
MaskRay commented 6 years ago

Can you make a PR for the snippet?

MaskRay commented 6 years ago

The display seems weird...

yyjjl commented 6 years ago

Sometimes, the display is weird. But I have no time to find the reason recently.

MaskRay commented 6 years ago

Yes sometimes. It's very annoying

MaskRay commented 6 years ago

This hack seems to work

                     (forward-line (- (car start) last-line-number))
                     (forward-char (cdr start))
                     ;; start of range
                     (setq range-start (point))
                     (setq last-line-number (car start))
                     (save-excursion
                       (forward-line (- (car end) (car start)))
                       (forward-char (cdr end))
                       ;; end of range
                       (setq range-end (point)))
MaskRay commented 6 years ago

The hack does not work for this file https://github.com/cquery-project/cquery/blob/master/src/indexer.h

Are you using https://github.com/emacs-mirror/emacs/tree/feature/noverlay ?

yyjjl commented 6 years ago

No, I am using Emacs 25.3. The hack seems to work for me on cquery v2018-01-23.

yyjjl commented 6 years ago

Sometimes, the semantic highlighting on indexer.h, line 476 seems wired.

First time to open indexer.h (.cquery_cached_index removed), I got (part of) message from cquery server as below. ( ) , . are recognized as a type. But next time, the display was right.

{
    "stableId" : 22,
    "ranges" : [
        {
            "start" : {
                "line" : 475,
                "character" : 13
            },
            "end" : {
                "character" : 14,
                "line" : 475
            }
        },
        {
            "end" : {
                "line" : 475,
                "character" : 24
            },
            "start" : {
                "line" : 475,
                "character" : 22
            }
        },
        {
            "start" : {
                "line" : 475,
                "character" : 25
            },
            "end" : {
                "character" : 26,
                "line" : 475
            }
        },
        {
            "end" : {
                "line" : 475,
                "character" : 29
            },
            "start" : {
                "character" : 28,
                "line" : 475
            }
        }
    ],
    "storage" : 0,
    "kind" : 6,
    "parentKind" : 0
}
MaskRay commented 6 years ago

line 476 of https://github.com/cquery-project/cquery/blob/master/src/indexer.h is indeed weird. The weirdness seems to start on line 135

I have reverted the sorting.

yyjjl commented 6 years ago

Line 135 -150 are display right in my Emacs. Is it possible that file content in cquery server doesn't match with buffer content in Emacs ?

MaskRay commented 6 years ago

No. If I use the reverted version (goto-char 1 + full forward-line), semantic highlighting looks correct. But that sorting + incremental forward-line approach sometimes makes bad rendering.

yyjjl commented 6 years ago

I will try to figure out why sorting is not working later.