aboutsummaryrefslogtreecommitdiffstats
path: root/elpa/lsp-ui-20220425.1046/lsp-ui-imenu.el
diff options
context:
space:
mode:
Diffstat (limited to 'elpa/lsp-ui-20220425.1046/lsp-ui-imenu.el')
-rw-r--r--elpa/lsp-ui-20220425.1046/lsp-ui-imenu.el412
1 files changed, 412 insertions, 0 deletions
diff --git a/elpa/lsp-ui-20220425.1046/lsp-ui-imenu.el b/elpa/lsp-ui-20220425.1046/lsp-ui-imenu.el
new file mode 100644
index 0000000..3c628da
--- /dev/null
+++ b/elpa/lsp-ui-20220425.1046/lsp-ui-imenu.el
@@ -0,0 +1,412 @@
+;;; lsp-ui-imenu.el --- Lsp-Ui-Imenu -*- lexical-binding: t -*-
+
+;; Copyright (C) 2018 Sebastien Chapuis
+
+;; Author: Sebastien Chapuis <sebastien@chapu.is>
+;; URL: https://github.com/emacs-lsp/lsp-ui
+;; Keywords: languages, tools
+;; Version: 6.3
+
+;;; License
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+;; Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; Show imenu entries
+;; Call the function `lsp-ui-imenu'
+;;
+;; (define-key lsp-ui-mode-map (kbd "C-c l") 'lsp-ui-imenu)
+
+;;; Code:
+
+(require 'lsp-mode)
+(require 'dash)
+(require 'lsp-ui-util)
+
+(defgroup lsp-ui-imenu nil
+ "Display imenu entries."
+ :group 'tools
+ :group 'convenience
+ :group 'lsp-ui
+ :link '(custom-manual "(lsp-ui-imenu) Top")
+ :link '(info-link "(lsp-ui-imenu) Customizing"))
+
+(defcustom lsp-ui-imenu-enable t
+ "Whether or not to enable ‘lsp-ui-imenu’."
+ :type 'boolean
+ :group 'lsp-ui)
+
+(defcustom lsp-ui-imenu-kind-position 'top
+ "Where to show the entries kind."
+ :type '(choice (const :tag "Top" top)
+ (const :tag "Left" left))
+ :group 'lsp-ui-imenu)
+
+(defcustom lsp-ui-imenu-colors '("deep sky blue" "green3")
+ "Color list to cycle through for entry groups."
+ :type '(repeat color)
+ :group 'lsp-ui-imenu)
+
+(defcustom lsp-ui-imenu-window-width 0
+ "When not 0, don't fit window to buffer and use value as window-width."
+ :type 'number
+ :group 'lsp-ui-imenu)
+
+(defcustom lsp-ui-imenu-auto-refresh nil
+ "Automatically refresh imenu when certain conditions meet."
+ :type '(choice (const :tag "Enable" t)
+ (const :tag "Active only when after save" after-save)
+ (const :tag "Disable" nil))
+ :group 'lsp-ui-imenu)
+
+(defcustom lsp-ui-imenu-auto-refresh-delay 1.0
+ "Delay time to refresh imenu."
+ :type 'float
+ :group 'lsp-ui-imenu)
+
+(defcustom lsp-ui-imenu--custom-mode-line-format nil
+ "Custom mode line format to be used in `lsp-ui-menu-mode'."
+ :type 'sexp
+ :group 'lsp-ui-menu)
+
+(defconst lsp-ui-imenu--max-bars 8)
+
+(declare-function imenu--make-index-alist 'imenu)
+(declare-function imenu--subalist-p 'imenu)
+(defvar imenu--index-alist)
+
+(defvar-local lsp-ui-imenu--refresh-timer nil
+ "Auto refresh timer for imenu.")
+
+(defun lsp-ui-imenu--pad (s len bars depth color-index for-title is-last)
+ (let ((n (- len (length s))))
+ (apply #'concat
+ (make-string n ?\s)
+ (propertize s 'face `(:foreground ,(lsp-ui-imenu--get-color color-index)))
+ (let (bar-strings)
+ (dotimes (i depth)
+ (push
+ (propertize (lsp-ui-imenu--get-bar bars i depth for-title is-last)
+ 'face `(:foreground
+ ,(lsp-ui-imenu--get-color (+ color-index i))))
+ bar-strings))
+ (reverse bar-strings)))))
+
+(defun lsp-ui-imenu--get-bar (bars index depth for-title is-last)
+ (cond
+ ;; Exceeding maximum bars
+ ((>= index lsp-ui-imenu--max-bars) " ")
+ ;; No bar for this level
+ ((not (aref bars index)) " ")
+ ;; For the first level, the title is rendered differently, so leaf items are
+ ;; decorated with the full height bar regardless if it's the last item or
+ ;; not.
+ ((and (= depth 1) (not for-title)) " ┃ ")
+ ;; Full height bar for levels other than the rightmost one.
+ ((< (1+ index) depth) " ┃ ")
+ ;; The rightmost bar for the last item.
+ (is-last " ┗ " )
+ ;; The rightmost bar for the title items other than the last one.
+ (for-title " ┣ ")
+ ;; The rightmost bar for the leaf items other than the last one.
+ (t " ┃ ")))
+
+(defun lsp-ui-imenu--get-color (index)
+ (nth (mod index (length lsp-ui-imenu-colors)) lsp-ui-imenu-colors))
+
+(defun lsp-ui-imenu--make-line (title index entry padding bars depth color-index is-last)
+ (let* ((prefix (if (and (= index 0) (eq lsp-ui-imenu-kind-position 'left)) title " "))
+ (text (concat (lsp-ui-imenu--pad prefix padding bars depth color-index nil is-last)
+ (propertize (car entry) 'face 'default)
+ "\n"))
+ (len (length text)))
+ (add-text-properties 0 len `(index ,index title ,title marker ,(cdr entry)
+ padding ,padding depth, depth)
+ text)
+ text))
+
+(defvar-local lsp-ui-imenu-ov nil
+ "Variable that holds overlay for imenu.")
+
+(defun lsp-ui-imenu--make-ov nil
+ "Make imenu overlay."
+ (or (and (overlayp lsp-ui-imenu-ov) lsp-ui-imenu-ov)
+ (setq lsp-ui-imenu-ov (make-overlay 1 1))))
+
+(defun lsp-ui-imenu--post-command nil
+ "Post command hook for imenu."
+ (when (eobp) (forward-line -1))
+ (lsp-ui-imenu--move-to-name-beginning)
+ (when (eq lsp-ui-imenu-kind-position 'left)
+ (save-excursion
+ (when (overlayp lsp-ui-imenu-ov)
+ (overlay-put lsp-ui-imenu-ov 'display nil))
+ (redisplay)
+ (goto-char (window-start))
+ (if (= (get-text-property (point) 'index) 0)
+ (when (overlayp lsp-ui-imenu-ov) (delete-overlay lsp-ui-imenu-ov))
+ (let* ((ov (lsp-ui-imenu--make-ov))
+ (padding (get-text-property (point) 'padding))
+ (title (get-text-property (point) 'title))
+ (text (buffer-substring (+ (line-beginning-position) padding) (line-end-position))))
+ (move-overlay ov (line-beginning-position) (line-end-position))
+ (overlay-put ov 'display `(string ,(concat (let ((n (- padding (length title))))
+ (propertize (concat (make-string n ?\s) title)))
+ text))))))))
+
+(defun lsp-ui-imenu--move-to-name-beginning ()
+ (-when-let* ((padding (get-char-property (point) 'padding))
+ (depth (get-char-property (point) 'depth)))
+ (goto-char (+ (* depth 3) (line-beginning-position) padding))))
+
+(defvar lsp-ui-imenu--origin nil)
+
+(defun lsp-ui-imenu--put-separator nil
+ (let ((ov (make-overlay (point) (point))))
+ (overlay-put ov 'after-string (propertize "\n" 'face '(:height 0.6)))))
+
+(defun lsp-ui-imenu--put-toplevel-title (title color-index)
+ (if (eq lsp-ui-imenu-kind-position 'top)
+ (let ((ov (make-overlay (point) (point)))
+ (color (lsp-ui-imenu--get-color color-index)))
+ (overlay-put
+ ov 'after-string
+ (concat (propertize "\n" 'face '(:height 0.6))
+ (propertize title 'face `(:foreground ,color))
+ "\n"
+ (propertize "\n" 'face '(:height 0.6)))))
+ ;; Left placement, title is put with the first sub item. Only put a separator here.
+ (lsp-ui-imenu--put-separator)))
+
+(defun lsp-ui-imenu--put-subtitle (title padding bars depth color-index is-last)
+ (let ((ov (make-overlay (point) (point)))
+ (title-color (lsp-ui-imenu--get-color (+ color-index depth))))
+ (overlay-put
+ ov 'after-string
+ (concat (lsp-ui-imenu--pad " " padding bars depth color-index t is-last)
+ (propertize title 'face `(:foreground ,title-color))
+ (propertize "\n" 'face '(:height 1))))))
+
+(defun lsp-ui-imenu--insert-items (title items padding bars depth color-index)
+ "Insert ITEMS for TITLE.
+
+PADDING is the length of whitespaces to the left of the first bar.
+
+BARS is a bool vector of length `lsp-ui-imenu--max-bars'. The ith
+value indicates whether the ith bar from the left is visible.
+
+DEPTH is the depth of the items in the index tree, starting from 0.
+
+COLOR-INDEX is the index of the color of the leftmost bar.
+
+Return the updated COLOR-INDEX."
+ (let ((len (length items)))
+ (--each-indexed items
+ (let ((is-last (= (1+ it-index) len)))
+ (if (imenu--subalist-p it)
+ (-let* (((sub-title . entries) it))
+ (if (= depth 0)
+ (lsp-ui-imenu--put-toplevel-title sub-title color-index)
+ (lsp-ui-imenu--put-subtitle sub-title padding bars depth color-index is-last))
+ (when (and is-last (> depth 0))
+ (aset bars (1- depth) nil))
+ (let ((lsp-ui-imenu-kind-position (if (> depth 0) 'top
+ lsp-ui-imenu-kind-position)))
+ (lsp-ui-imenu--insert-items sub-title
+ entries
+ padding
+ bars
+ (1+ depth)
+ color-index))
+ (when (and is-last (> depth 0))
+ (aset bars (1- depth) t))
+ (when (= depth 0)
+ (setq color-index (1+ color-index))))
+ (insert (lsp-ui-imenu--make-line title it-index it
+ padding bars depth color-index
+ is-last))))))
+ color-index)
+
+(defun lsp-ui-imenu--get-padding (items)
+ "Get imenu padding determined by `lsp-ui-imenu-kind-position'.
+ITEMS are used when the kind position is 'left."
+ (cl-case lsp-ui-imenu-kind-position
+ (top 1)
+ (left (--> (-filter 'imenu--subalist-p items)
+ (--map (length (car it)) it)
+ (-max (or it '(1)))))
+ (t (user-error "Invalid value for imenu's kind position: %s" lsp-ui-imenu-kind-position))))
+
+(defun lsp-ui-imenu--put-bit (bits offset)
+ (logior bits (lsh 1 offset)))
+
+(defun lsp-ui-imenu--clear-bit (bits offset)
+ (logand bits (lognot (lsh 1 offset))))
+
+(defvar lsp-ui-imenu-buffer-name "*lsp-ui-imenu*"
+ "Buffer name for imenu buffers.")
+
+(defun lsp-ui-imenu--refresh-content ()
+ "Refresh imenu content menu"
+ (let ((imenu-auto-rescan t))
+ (setq lsp-ui-imenu--origin (current-buffer))
+ (imenu--make-index-alist)
+ (let ((imenu-buffer (get-buffer-create lsp-ui-imenu-buffer-name))
+ (list imenu--index-alist))
+ (with-current-buffer imenu-buffer
+ (let* ((padding (lsp-ui-imenu--get-padding list))
+ (grouped-by-subs (-partition-by 'imenu--subalist-p list))
+ (color-index 0)
+ (bars (make-bool-vector lsp-ui-imenu--max-bars t))
+ (inhibit-read-only t))
+ (remove-overlays)
+ (erase-buffer)
+ (dolist (group grouped-by-subs)
+ (if (imenu--subalist-p (car group))
+ (setq color-index (lsp-ui-imenu--insert-items "" group padding bars 0 color-index))
+ (lsp-ui-imenu--put-separator)
+ (lsp-ui-imenu--insert-items "" group padding bars 1 color-index)
+ (setq color-index (1+ color-index))))
+ (lsp-ui-imenu-mode)
+ (when lsp-ui-imenu--custom-mode-line-format
+ (setq mode-line-format lsp-ui-imenu--custom-mode-line-format))
+ (goto-char (point-min))
+ (add-hook 'post-command-hook 'lsp-ui-imenu--post-command nil t))))))
+
+(defun lsp-ui-imenu nil
+ "Open ui-imenu in side window."
+ (interactive)
+ (lsp-ui-imenu-buffer-mode 1)
+ (setq lsp-ui-imenu--origin (current-buffer))
+ (imenu--make-index-alist)
+ (let ((imenu-buffer (get-buffer-create lsp-ui-imenu-buffer-name)))
+ (lsp-ui-imenu--refresh-content)
+ (let ((win (display-buffer-in-side-window imenu-buffer '((side . right)))))
+ (set-window-margins win 1)
+ (select-window win)
+ (set-window-start win 1)
+ (lsp-ui-imenu--move-to-name-beginning)
+ (set-window-dedicated-p win t)
+ ;; when `lsp-ui-imenu-window-width' is 0, fit window to buffer
+ (if (= lsp-ui-imenu-window-width 0)
+ (let ((fit-window-to-buffer-horizontally 'only))
+ (fit-window-to-buffer win)
+ (window-resize win 3 t))
+ (let ((x (- lsp-ui-imenu-window-width (window-width))))
+ (window-resize (selected-window) x t))))))
+
+(defun lsp-ui-imenu--kill nil
+ "Kill imenu window."
+ (interactive)
+ (lsp-ui-imenu-buffer-mode -1)
+ (kill-buffer-and-window))
+
+(defun lsp-ui-imenu--jump (direction)
+ (let ((current (get-text-property (point) 'title)))
+ (forward-line direction)
+ (while (and current
+ (not (= (line-number-at-pos) 1))
+ (equal current (get-text-property (point) 'title)))
+ (forward-line direction))))
+
+(defun lsp-ui-imenu--next-kind nil
+ "Jump to next kind of imenu."
+ (interactive)
+ (lsp-ui-imenu--jump 1))
+
+(defun lsp-ui-imenu--prev-kind nil
+ "Jump to previous kind of imenu."
+ (interactive)
+ (lsp-ui-imenu--jump -1)
+ (while (not (= (get-text-property (point) 'index) 0))
+ (forward-line -1)))
+
+(defun lsp-ui-imenu--visit nil
+ (interactive)
+ (let ((marker (get-text-property (point) 'marker)))
+ (select-window (get-buffer-window lsp-ui-imenu--origin))
+ (goto-char marker)
+ (pulse-momentary-highlight-one-line (point) 'next-error)))
+
+(defun lsp-ui-imenu--view nil
+ (interactive)
+ (let ((marker (get-text-property (point) 'marker)))
+ (with-selected-window (get-buffer-window lsp-ui-imenu--origin)
+ (goto-char marker)
+ (recenter)
+ (pulse-momentary-highlight-one-line (point) 'next-error))))
+
+(defvar lsp-ui-imenu-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "q") 'lsp-ui-imenu--kill)
+ (define-key map (kbd "r") 'lsp-ui-imenu--refresh)
+ (define-key map (kbd "<right>") 'lsp-ui-imenu--next-kind)
+ (define-key map (kbd "<left>") 'lsp-ui-imenu--prev-kind)
+ (define-key map (kbd "<return>") 'lsp-ui-imenu--view)
+ (define-key map (kbd "<M-return>") 'lsp-ui-imenu--visit)
+ (define-key map (kbd "RET") 'lsp-ui-imenu--view)
+ (define-key map (kbd "M-RET") 'lsp-ui-imenu--visit)
+ map)
+ "Keymap for ‘lsp-ui-peek-mode’.")
+
+(define-derived-mode lsp-ui-imenu-mode special-mode "lsp-ui-imenu"
+ "Mode showing imenu entries.")
+
+(defun lsp-ui-imenu--refresh ()
+ "Safe refresh imenu content."
+ (interactive)
+ (let ((imenu-buffer (get-buffer lsp-ui-imenu-buffer-name)))
+ (when imenu-buffer
+ (save-selected-window
+ (if (equal (current-buffer) imenu-buffer)
+ (select-window (get-buffer-window lsp-ui-imenu--origin))
+ (setq lsp-ui-imenu--origin (current-buffer)))
+ (lsp-ui-imenu--refresh-content)))))
+
+(defun lsp-ui-imenu--start-refresh (&rest _)
+ "Starts the auto refresh timer."
+ (lsp-ui-util-safe-kill-timer lsp-ui-imenu--refresh-timer)
+ (setq lsp-ui-imenu--refresh-timer
+ (run-with-idle-timer lsp-ui-imenu-auto-refresh-delay nil #'lsp-ui-imenu--refresh)))
+
+(defun lsp-ui-imenu-buffer--enable ()
+ "Enable `lsp-ui-imenu-buffer'."
+ (when lsp-ui-imenu-auto-refresh
+ (cl-case lsp-ui-imenu-auto-refresh
+ (after-save
+ (add-hook 'after-save-hook #'lsp-ui-imenu--start-refresh nil t))
+ (t
+ (add-hook 'after-change-functions #'lsp-ui-imenu--start-refresh nil t)
+ (add-hook 'after-save-hook #'lsp-ui-imenu--start-refresh nil t)))))
+
+(defun lsp-ui-imenu-buffer--disable ()
+ "Disable `lsp-ui-imenu-buffer'."
+ (when lsp-ui-imenu-auto-refresh
+ (cl-case lsp-ui-imenu-auto-refresh
+ (after-save
+ (remove-hook 'after-save-hook #'lsp-ui-imenu--start-refresh t))
+ (t
+ (remove-hook 'after-change-functions #'lsp-ui-imenu--start-refresh t)
+ (remove-hook 'after-save-hook #'lsp-ui-imenu--start-refresh t)))))
+
+(define-minor-mode lsp-ui-imenu-buffer-mode
+ "Minor mode 'lsp-ui-imenu-buffer-mode'."
+ :group lsp-ui-imenu
+ (if lsp-ui-imenu-buffer-mode (lsp-ui-imenu-buffer--enable) (lsp-ui-imenu-buffer--disable)))
+
+(provide 'lsp-ui-imenu)
+;;; lsp-ui-imenu.el ends here