Skip to content

Commit 7ae33f6

Browse files
committed
Compactify the in-line messages
1 parent 5579642 commit 7ae33f6

File tree

1 file changed

+114
-21
lines changed

1 file changed

+114
-21
lines changed

lsp-sonarlint.el

Lines changed: 114 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -383,31 +383,27 @@ BEGIN-END-POSITIONS is a plist with :begin and :end positions."
383383
(propertize (number-to-string num)
384384
'face 'lsp-sonarlint--step-marker)))
385385

386-
(defun lsp-sonarlint--make-full-line-overlay (range)
387-
"Create an overlay covering entire line(s) rather than the precise RANGE."
386+
(defun lsp-sonarlint--make-full-line-overlay (line)
387+
"Create an overlay covering entire LINE."
388388
(save-excursion
389-
(goto-char (plist-get range :begin))
390-
(let ((begin (line-beginning-position)))
391-
(goto-char (plist-get range :end))
392-
(let ((end (line-end-position)))
393-
(lsp-sonarlint--make-overlay-between `(:begin ,begin :end ,end))))))
389+
(goto-char (point-min))
390+
(forward-line (- line 1))
391+
(lsp-sonarlint--make-overlay-between `(:begin ,(line-beginning-position)
392+
:end ,(line-end-position)))))
394393

395394
(defun lsp-sonarlint--get-column (pos)
396395
"Get the column of the point position POS."
397396
(save-excursion
398397
(goto-char pos)
399398
(current-column)))
400399

401-
(defun lsp-sonarlint--secondary-msg-lens-offset (range)
402-
"Compute and return white-space string to align message with RANGE."
400+
(defun lsp-sonarlint--secondary-msg-lens-offset (position)
401+
"Compute and return number of characters to align message with POSITION."
403402
(let* ((msg-height (face-attribute 'lsp-sonarlint-embedded-msg-face :height nil 'default))
404-
(default-height (face-attribute 'default :height))
405-
(msg-offset-in-chars
406-
(1+ (/ (* (lsp-sonarlint--get-column
407-
(plist-get range :begin))
408-
default-height)
409-
msg-height))))
410-
(make-string msg-offset-in-chars ?\s)))
403+
(default-height (face-attribute 'default :height)))
404+
(1+ (/ (* (lsp-sonarlint--get-column position)
405+
default-height)
406+
msg-height))))
411407

412408
(defun lsp-sonarlint--procure-overlays-for-secondary-locations (flows)
413409
"Create overlays for secondary locations in FLOWS.
@@ -428,13 +424,8 @@ Returns a list of plists with the overlay, step number, and message."
428424
(let* ((range-ht (ht-get location "textRange"))
429425
(range (lsp-sonarlint--get-range-positions range-ht))
430426
(overlay (lsp-sonarlint--make-overlay-between range))
431-
(fl-ovl (lsp-sonarlint--make-full-line-overlay range))
432-
(message-offset (lsp-sonarlint--secondary-msg-lens-offset range))
433427
(message (ht-get location "message")))
434428
(overlay-put overlay 'face 'lsp-sonarlint-secondary-location-face)
435-
(overlay-put fl-ovl 'before-string
436-
(propertize (concat message-offset message "\n")
437-
'face 'lsp-sonarlint-embedded-msg-face))
438429
(lsp-sonarlint--add-number-marker overlay step-num)
439430
`(:overlay ,overlay :step-num ,step-num :message ,message)))
440431
locations)))
@@ -516,13 +507,113 @@ pointing to the `:overlay' from LOC-MESSAGE."
516507
(overlay-put overlay 'focus-location (plist-get loc-message :overlay))
517508
overlay))
518509

510+
(defun lsp-sonarlint--extract-located-messages (locations)
511+
"Group messages from LOCATIONS by their coordinates."
512+
(let ((line-to-msg (make-hash-table :test #'equal)))
513+
(mapc (lambda (location)
514+
(let* ((precise-overlay (plist-get location :overlay))
515+
(message-offset (lsp-sonarlint--secondary-msg-lens-offset
516+
(overlay-start precise-overlay)))
517+
(message (plist-get location :message))
518+
(line (line-number-at-pos (overlay-start precise-overlay))))
519+
(push `(:message ,message :offset ,message-offset)
520+
(gethash line line-to-msg))))
521+
locations)
522+
line-to-msg))
523+
524+
(defun lsp-sonarlint--deduplicate (sorted-list)
525+
"Remove duplicate elements from sorted SORTED-LIST."
526+
(let ((result '())
527+
(last-element nil))
528+
(dolist (element sorted-list (nreverse result))
529+
(unless (equal element last-element)
530+
(push element result)
531+
(setq last-element element)))))
532+
533+
(defun lsp-sonarlint--combine (messages-with-offsets)
534+
"Combine MESSAGES-WITH-OFFSETS that don't overlap into single line.
535+
536+
MESSAGES-WITH-OFFSETS must be sorted by offset."
537+
(let ((result '())
538+
(reversed (reverse messages-with-offsets)))
539+
(dolist (msg-off messages-with-offsets (nreverse result))
540+
(when-let* ((right-most (car reversed))
541+
(right-offset (plist-get right-most :offset))
542+
(my-offset (plist-get msg-off :offset))
543+
(my-message (plist-get msg-off :message))
544+
(gap (- right-offset (+ my-offset (length my-message)))))
545+
(unless (< right-offset my-offset) ; this element is already combined
546+
(if (<= gap 3) ;; too close or overlap
547+
(push msg-off result)
548+
(setf (plist-get msg-off :message)
549+
(concat my-message
550+
(make-string gap ?\s)
551+
(plist-get right-most :message)))
552+
(pop reversed)
553+
(push msg-off result)))))))
554+
555+
(defun lsp-sonarlint--process-offsets (messages-with-offsets)
556+
"Sort, deduplicate, adjust, and combine MESSAGES-WITH-OFFSETS.
557+
558+
Sort them in increasing order, remove duplicate messages with identical offsets,
559+
adjust offsets to account for the number labels prepended to each location."
560+
(let* ((sorted (sort messages-with-offsets (lambda (msg-off1 msg-off2)
561+
(< (plist-get msg-off1 :offset)
562+
(plist-get msg-off2 :offset)))))
563+
(deduplicated (lsp-sonarlint--deduplicate sorted))
564+
(accumulated-adjustment 0)
565+
(adjusted (mapcar (lambda (msg-with-offset)
566+
(setf (plist-get msg-with-offset :offset)
567+
(+ accumulated-adjustment
568+
(plist-get msg-with-offset :offset)))
569+
(setq accumulated-adjustment 1)
570+
msg-with-offset)
571+
deduplicated)))
572+
(lsp-sonarlint--combine adjusted)))
573+
574+
(defun lsp-sonarlint--concat-msg-lines (msg-offsets)
575+
"Combine the list of MSG-OFFSETS into a single string."
576+
(string-join
577+
(mapcar (lambda (msg-offset)
578+
(concat (make-string (plist-get msg-offset :offset) ?\s)
579+
(plist-get msg-offset :message)))
580+
msg-offsets)
581+
"\n"))
582+
583+
(defun lsp-sonarlint--add-inline-messages (locations)
584+
"Add lens-style in-line messages for LOCATIONS."
585+
(maphash (lambda (line messages)
586+
(let* ((adjusted-messages
587+
(lsp-sonarlint--process-offsets messages))
588+
(overlay (lsp-sonarlint--make-full-line-overlay line))
589+
(prefix-count (/ (1+ (length adjusted-messages)) 2))
590+
(prefix-msgs (seq-take adjusted-messages prefix-count))
591+
(postfix-msgs (seq-drop adjusted-messages prefix-count)))
592+
(when prefix-msgs
593+
(overlay-put overlay 'before-string
594+
(propertize (concat (lsp-sonarlint--concat-msg-lines prefix-msgs)
595+
"\n")
596+
'face 'lsp-sonarlint-embedded-msg-face)))
597+
(when postfix-msgs
598+
(overlay-put overlay 'after-string
599+
(propertize (concat "\n"
600+
(lsp-sonarlint--concat-msg-lines postfix-msgs))
601+
'face 'lsp-sonarlint-embedded-msg-face)))))
602+
(lsp-sonarlint--extract-located-messages locations)))
603+
604+
(defvar lsp-sonarlint--original-buffer nil
605+
"The buffer with code and SonarLint issues.
606+
607+
Useful when exploring secondary locations, which uses an auxiliary buffer.")
608+
519609
(defun lsp-sonarlint--show-all-locations (command)
520610
"Show all secondary locations listed in COMMAND for the focused issue."
521611
(lsp-sonarlint--remove-secondary-loc-highlights)
522612
(let* ((arguments (seq-first (ht-get command "arguments")))
523613
(flows (ht-get arguments "flows")))
524614
(let ((locations (lsp-sonarlint--procure-overlays-for-secondary-locations flows))
525615
(primary (lsp-sonarlint--procure-overlay-for-primary-location arguments)))
616+
(setq lsp-sonarlint--original-buffer (current-buffer))
526617
(switch-to-buffer-other-window lsp-sonarlint--secondary-messages-buffer-name)
527618
(fundamental-mode)
528619
(setq buffer-read-only nil)
@@ -539,6 +630,8 @@ pointing to the `:overlay' from LOC-MESSAGE."
539630
(insert "\n")
540631
(let ((overlay (lsp-sonarlint--add-message-entry location)))
541632
(lsp-sonarlint--add-number-marker overlay (plist-get location :step-num))))
633+
(with-current-buffer lsp-sonarlint--original-buffer
634+
(lsp-sonarlint--add-inline-messages (cons primary locations)))
542635
(goto-char (point-min))
543636
(tabulated-list-mode)
544637
(setq-local cursor-type nil))))

0 commit comments

Comments
 (0)