diff options
Diffstat (limited to 'elpa/lsp-mode-20220505.630/lsp-headerline.el')
-rw-r--r-- | elpa/lsp-mode-20220505.630/lsp-headerline.el | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/elpa/lsp-mode-20220505.630/lsp-headerline.el b/elpa/lsp-mode-20220505.630/lsp-headerline.el new file mode 100644 index 0000000..7d42654 --- /dev/null +++ b/elpa/lsp-mode-20220505.630/lsp-headerline.el @@ -0,0 +1,478 @@ +;;; lsp-headerline.el --- LSP headerline features -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2020 emacs-lsp maintainers +;; +;; 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 of the License, 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. If not, see <https://www.gnu.org/licenses/>. +;; +;;; Commentary: +;; +;; LSP headerline features +;; +;;; Code: + +(require 'lsp-icons) +(require 'lsp-mode) + +(defgroup lsp-headerline nil + "LSP support for headerline" + :prefix "lsp-headerline-" + :group 'lsp-mode + :tag "LSP Headerline") + +(defcustom lsp-headerline-breadcrumb-segments '(path-up-to-project file symbols) + "Segments used in breadcrumb text on headerline." + :type '(repeat + (choice (const :tag "Include the project name." project) + (const :tag "Include the open file name." file) + (const :tag "Include the directories up to project." path-up-to-project) + (const :tag "Include document symbols if server supports it." symbols))) + :group 'lsp-headerline) + +(defcustom lsp-headerline-breadcrumb-enable-symbol-numbers nil + "Whether to label symbols with numbers on the breadcrumb." + :type 'boolean + :group 'lsp-headerline) + +(defcustom lsp-headerline-breadcrumb-enable-diagnostics t + "If non-nil, apply different face on the breadcrumb based on the errors." + :type 'boolean + :group 'lsp-headerline + :package-version '(lsp-mode . "8.0.0")) + +(defface lsp-headerline-breadcrumb-separator-face '((t :inherit shadow :height 0.8)) + "Face used for breadcrumb separator on headerline." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-path-face '((t :inherit font-lock-string-face)) + "Face used for breadcrumb paths on headerline." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-path-error-face + '((t :underline (:style wave :color "Red1") + :inherit lsp-headerline-breadcrumb-path-face)) + "Face used for breadcrumb paths on headerline when there is an error under that path" + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-path-warning-face + '((t :underline (:style wave :color "Yellow") + :inherit lsp-headerline-breadcrumb-path-face)) + "Face used for breadcrumb paths on headerline when there is an warning under that path" + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-path-info-face + '((t :underline (:style wave :color "Green") + :inherit lsp-headerline-breadcrumb-path-face)) + "Face used for breadcrumb paths on headerline when there is an info under that path" + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-path-hint-face + '((t :underline (:style wave :color "Green") + :inherit lsp-headerline-breadcrumb-path-face)) + "Face used for breadcrumb paths on headerline when there is an hint under that path" + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-project-prefix-face + '((t :inherit font-lock-string-face :weight bold)) + "Face used for breadcrumb prefix on headerline. +Only if `lsp-headerline-breadcrumb-prefix` is `project-name-only`." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-unknown-project-prefix-face + '((t :inherit shadow :weight bold)) + "Face used for breadcrumb prefix on headerline. +Only if `lsp-headerline-breadcrumb-prefix` is `project-name-only`." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-symbols-face + '((t :inherit font-lock-doc-face :weight bold)) + "Face used for breadcrumb symbols text on headerline." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-symbols-error-face + '((t :inherit lsp-headerline-breadcrumb-symbols-face + :underline (:style wave :color "Red1"))) + "Face used for breadcrumb symbols text on headerline when there +is an error in symbols range." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-symbols-warning-face + '((t :inherit lsp-headerline-breadcrumb-symbols-face + :underline (:style wave :color "Yellow"))) + "Face used for breadcrumb symbols text on headerline when there +is an warning in symbols range." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-symbols-info-face + '((t :inherit lsp-headerline-breadcrumb-symbols-face + :underline (:style wave :color "Green"))) + "Face used for breadcrumb symbols text on headerline when there +is an info in symbols range." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-symbols-hint-face + '((t :inherit lsp-headerline-breadcrumb-symbols-face + :underline (:style wave :color "Green"))) + "Face used for breadcrumb symbols text on headerline when there +is an hints in symbols range." + :group 'lsp-headerline) + +(defface lsp-headerline-breadcrumb-deprecated-face + '((t :inherit lsp-headerline-breadcrumb-symbols-face + :strike-through t)) + "Face used on breadcrumb deprecated text on modeline." + :group 'lsp-headerline) + +(defvar-local lsp-headerline--string nil + "Holds the current breadcrumb string on headerline.") + +(defvar lsp-headerline-arrow nil + "Holds the current breadcrumb string on headerline.") + +(defvar-local lsp-headerline--path-up-to-project-segments nil + "Holds the current breadcrumb path-up-to-project segments for +caching purposes.") + +(defun lsp-headerline--arrow-icon () + "Build the arrow icon for headerline breadcrumb." + (or + lsp-headerline-arrow + (setq lsp-headerline-arrow (lsp-icons-all-the-icons-material-icon + "chevron_right" + 'lsp-headerline-breadcrumb-separator-face + ">" + 'headerline-breadcrumb)))) + +(lsp-defun lsp-headerline--symbol-icon ((&DocumentSymbol :kind)) + "Build the SYMBOL icon for headerline breadcrumb." + (concat (lsp-icons-get-by-symbol-kind kind 'headerline-breadcrumb) + " ")) + +(lsp-defun lsp-headerline--go-to-symbol ((&DocumentSymbol + :selection-range (&RangeToPoint :start selection-start) + :range (&RangeToPoint :start narrowing-start + :end narrowing-end))) + "Go to breadcrumb symbol. +If the buffer is narrowed and the target symbol lies before the +minimum reachable point in the narrowed buffer, then widen and +narrow to the outer symbol." + (when (buffer-narrowed-p) + (narrow-to-region + (min (point-min) narrowing-start) + (max (point-max) narrowing-end))) + (goto-char selection-start)) + +(lsp-defun lsp-headerline--narrow-to-symbol ((&DocumentSymbol :range (&RangeToPoint :start :end))) + "Narrow to breadcrumb symbol range." + (narrow-to-region start end)) + +(defun lsp-headerline--with-action (local-map help-echo-string display-string) + "Assign LOCAL-MAP and HELP-ECHO-STRING to the region around the DISPLAY-STRING." + (propertize display-string + 'mouse-face 'header-line-highlight + 'help-echo help-echo-string + 'local-map local-map)) + +(defmacro lsp-headerline--make-mouse-handler (&rest body) + "Making mouse event handler. +Switch to current mouse interacting window before doing BODY." + (declare (debug t) (indent 0)) + `(lambda (event) + (interactive "e") + (select-window (posn-window (elt event 1))) + ,@body)) + +(defun lsp-headerline--directory-with-action (full-path directory-display-string) + "Build action for FULL-PATH and DIRECTORY-DISPLAY-STRING." + (lsp-headerline--with-action (let ((map (make-sparse-keymap))) + (define-key map [header-line mouse-1] + (lsp-headerline--make-mouse-handler + (dired full-path))) + (define-key map [header-line mouse-2] + (lsp-headerline--make-mouse-handler + (dired-other-window full-path))) + map) + (format "mouse-1: browse '%s' with Dired\nmouse-2: browse '%s' with Dired in other window" + directory-display-string + directory-display-string) + (propertize directory-display-string + 'lsp-full-path full-path))) + +(declare-function evil-set-jump "ext:evil-jumps") + +(lsp-defun lsp-headerline--symbol-with-action ((symbol &as &DocumentSymbol :name) symbol-display-string) + "Build action for SYMBOL and SYMBOL-STRING." + (lsp-headerline--with-action (let ((map (make-sparse-keymap))) + (define-key map [header-line mouse-1] + (lsp-headerline--make-mouse-handler + (when (bound-and-true-p evil-mode) + (evil-set-jump)) + (lsp-headerline--go-to-symbol symbol))) + (define-key map [header-line mouse-2] + (lsp-headerline--make-mouse-handler + (-let (((&DocumentSymbol :range (&RangeToPoint :start :end)) symbol)) + (if (and (eq (point-min) start) (eq (point-max) end)) + (widen) + (lsp-headerline--narrow-to-symbol symbol))))) + map) + (format "mouse-1: go to '%s' symbol\nmouse-2: %s" + name + (-let (((&DocumentSymbol :range (&RangeToPoint :start :end)) symbol)) + (if (and (eq (point-min) start) (eq (point-max) end)) + "widen" + (format "narrow to '%s' range" name)))) + symbol-display-string)) + +(defun lsp-headerline--path-up-to-project-root (root-path path) + "Find recursively the folders until the project ROOT-PATH. +PATH is the current folder to be checked." + (let ((current-path path) + headerline-path-components) + (while (not (lsp-f-same? root-path current-path)) + (push (lsp-headerline--directory-with-action current-path + (f-filename current-path)) + headerline-path-components) + (setq current-path (lsp-f-parent current-path))) + headerline-path-components)) + +(defun lsp-headerline--build-project-string () + "Build the project-segment string for the breadcrumb." + (-if-let (root (lsp-workspace-root)) + (propertize (lsp-headerline--directory-with-action + root + (f-filename root)) + 'font-lock-face + 'lsp-headerline-breadcrumb-project-prefix-face) + (propertize "<unknown>" + 'font-lock-face + 'lsp-headerline-breadcrumb-unknown-project-prefix-face))) + +(defun lsp-headerline--build-file-string () + "Build the file-segment string for the breadcrumb." + (let* ((file-path (or (buffer-file-name) "")) + (filename (f-filename file-path))) + (if-let ((file-ext (f-ext file-path))) + (concat (lsp-icons-get-by-file-ext file-ext 'headerline-breadcrumb) + " " + (propertize filename + 'font-lock-face + (lsp-headerline--face-for-path file-path))) + filename))) + + +(defun lsp-headerline--face-for-path (dir) + "Calculate the face for DIR." + (if-let ((diags (lsp-diagnostics-stats-for (directory-file-name dir)))) + (cl-labels ((check-severity + (severity) + (not (zerop (aref diags severity))))) + (cond + ((not lsp-headerline-breadcrumb-enable-diagnostics) + 'lsp-headerline-breadcrumb-path-face) + ((check-severity lsp/diagnostic-severity-error) + 'lsp-headerline-breadcrumb-path-error-face) + ((check-severity lsp/diagnostic-severity-warning) + 'lsp-headerline-breadcrumb-path-warning-face) + ((check-severity lsp/diagnostic-severity-information) + 'lsp-headerline-breadcrumb-path-info-face) + ((check-severity lsp/diagnostic-severity-hint) + 'lsp-headerline-breadcrumb-path-hint-face) + (t 'lsp-headerline-breadcrumb-path-face))) + 'lsp-headerline-breadcrumb-path-face)) + +(defun lsp-headerline--severity-level-for-range (range) + "Get the severiy level for RANGE." + (let ((range-severity 10)) + (mapc (-lambda ((&Diagnostic :range (&Range :start) :severity?)) + (when (lsp-point-in-range? start range) + (setq range-severity (min range-severity severity?)))) + (lsp--get-buffer-diagnostics)) + range-severity)) + +(defun lsp-headerline--build-path-up-to-project-string () + "Build the path-up-to-project segment for the breadcrumb." + (if-let ((root (lsp-workspace-root))) + (let ((segments (or + lsp-headerline--path-up-to-project-segments + (setq lsp-headerline--path-up-to-project-segments + (lsp-headerline--path-up-to-project-root + root + (lsp-f-parent (buffer-file-name))))))) + (mapconcat (lambda (next-dir) + (propertize next-dir + 'font-lock-face + (lsp-headerline--face-for-path + (get-text-property + 0 'lsp-full-path next-dir)))) + segments + (concat " " (lsp-headerline--arrow-icon) " "))) + "")) + +(lsp-defun lsp-headerline--face-for-symbol ((&DocumentSymbol :deprecated? + :range)) + "Get the face for SYMBOL." + (let ((range-severity (lsp-headerline--severity-level-for-range range))) + (cond + (deprecated? 'lsp-headerline-breadcrumb-deprecated-face) + ((not lsp-headerline-breadcrumb-enable-diagnostics) + 'lsp-headerline-breadcrumb-symbols-face) + ((= range-severity lsp/diagnostic-severity-error) + 'lsp-headerline-breadcrumb-symbols-error-face) + ((= range-severity lsp/diagnostic-severity-warning) + 'lsp-headerline-breadcrumb-symbols-warning-face) + ((= range-severity lsp/diagnostic-severity-information) + 'lsp-headerline-breadcrumb-symbols-info-face) + ((= range-severity lsp/diagnostic-severity-hint) + 'lsp-headerline-breadcrumb-symbols-hint-face) + (t 'lsp-headerline-breadcrumb-symbols-face)))) + +(defun lsp-headerline--build-symbol-string () + "Build the symbol segment for the breadcrumb." + (if (lsp-feature? "textDocument/documentSymbol") + (-if-let* ((lsp--document-symbols-request-async t) + (symbols (lsp--get-document-symbols)) + (symbols-hierarchy (lsp--symbols->document-symbols-hierarchy symbols)) + (enumerated-symbols-hierarchy + (-map-indexed (lambda (index elt) + (cons elt (1+ index))) + symbols-hierarchy))) + (mapconcat + (-lambda (((symbol &as &DocumentSymbol :name) + . index)) + (let* ((symbol2-name + (propertize name + 'font-lock-face + (lsp-headerline--face-for-symbol symbol))) + (symbol2-icon (lsp-headerline--symbol-icon symbol)) + (full-symbol-2 + (concat + (if lsp-headerline-breadcrumb-enable-symbol-numbers + (concat + (propertize (number-to-string index) + 'face + 'lsp-headerline-breadcrumb-symbols-face) + " ") + "") + (if symbol2-icon + (concat symbol2-icon symbol2-name) + symbol2-name)))) + (lsp-headerline--symbol-with-action symbol full-symbol-2))) + enumerated-symbols-hierarchy + (concat " " (lsp-headerline--arrow-icon) " ")) + "") + "")) + +(defun lsp-headerline--build-string () + "Build the header-line string." + (string-trim-right + (mapconcat + (lambda (segment) + (let ((segment-string + (pcase segment + ('project (lsp-headerline--build-project-string)) + ('file (lsp-headerline--build-file-string)) + ('path-up-to-project (lsp-headerline--build-path-up-to-project-string)) + ('symbols (lsp-headerline--build-symbol-string)) + (_ (lsp-log "'%s' is not a valid entry for `lsp-headerline-breadcrumb-segments'" + (symbol-name segment)) + "")))) + (if (eq segment-string "") + "" + (concat (lsp-headerline--arrow-icon) + " " + segment-string + " ")))) + lsp-headerline-breadcrumb-segments + ""))) + +(defun lsp-headerline--check-breadcrumb (&rest _) + "Request for document symbols to build the breadcrumb." + (setq lsp-headerline--string (lsp-headerline--build-string)) + (force-mode-line-update)) + +(defun lsp-headerline--enable-breadcrumb () + "Enable headerline breadcrumb mode." + (when (and lsp-headerline-breadcrumb-enable + (lsp-feature? "textDocument/documentSymbol")) + (lsp-headerline-breadcrumb-mode 1))) + +(defun lsp-headerline--disable-breadcrumb () + "Disable headerline breadcrumb mode." + (lsp-headerline-breadcrumb-mode -1)) + +;;;###autoload +(define-minor-mode lsp-headerline-breadcrumb-mode + "Toggle breadcrumb on headerline." + :group 'lsp-headerline + :global nil + (cond + (lsp-headerline-breadcrumb-mode + ;; make sure header-line-format, if non-nil, is a list. as + ;; mode-line-format says: "The value may be nil, a string, a + ;; symbol or a list." + (unless (listp header-line-format) + (setq header-line-format (list header-line-format))) + (add-to-list 'header-line-format '(t (:eval lsp-headerline--string))) + + (add-hook 'xref-after-jump-hook #'lsp-headerline--check-breadcrumb nil t) + + (add-hook 'lsp-on-idle-hook #'lsp-headerline--check-breadcrumb nil t) + (add-hook 'lsp-configure-hook #'lsp-headerline--enable-breadcrumb nil t) + (add-hook 'lsp-unconfigure-hook #'lsp-headerline--disable-breadcrumb nil t)) + (t + (remove-hook 'lsp-on-idle-hook #'lsp-headerline--check-breadcrumb t) + (remove-hook 'lsp-configure-hook #'lsp-headerline--enable-breadcrumb t) + (remove-hook 'lsp-unconfigure-hook #'lsp-headerline--disable-breadcrumb t) + + (remove-hook 'xref-after-jump-hook #'lsp-headerline--check-breadcrumb t) + + (setq lsp-headerline--path-up-to-project-segments nil) + (setq header-line-format (remove '(t (:eval lsp-headerline--string)) header-line-format))))) + +;;;###autoload +(defun lsp-breadcrumb-go-to-symbol (symbol-position) + "Go to the symbol on breadcrumb at SYMBOL-POSITION." + (interactive "P") + (if (numberp symbol-position) + (if (lsp-feature? "textDocument/documentSymbol") + (-if-let* ((lsp--document-symbols-request-async t) + (symbols (lsp--get-document-symbols)) + (symbols-hierarchy (lsp--symbols->document-symbols-hierarchy symbols))) + (lsp-headerline--go-to-symbol (nth (1- symbol-position) symbols-hierarchy)) + (lsp--info "Symbol not found for position %s" symbol-position)) + (lsp--info "Server does not support breadcrumb.")) + (lsp--info "Call this function with a number representing the symbol position on breadcrumb"))) + +(declare-function evil-set-command-property "ext:evil-common") + +(with-eval-after-load 'evil + (evil-set-command-property 'lsp-breadcrumb-go-to-symbol :jump t)) + +;;;###autoload +(defun lsp-breadcrumb-narrow-to-symbol (symbol-position) + "Narrow to the symbol range on breadcrumb at SYMBOL-POSITION." + (interactive "P") + (if (numberp symbol-position) + (if (lsp-feature? "textDocument/documentSymbol") + (-if-let* ((lsp--document-symbols-request-async t) + (symbols (lsp--get-document-symbols)) + (symbols-hierarchy (lsp--symbols->document-symbols-hierarchy symbols))) + (lsp-headerline--narrow-to-symbol (nth (1- symbol-position) symbols-hierarchy)) + (lsp--info "Symbol not found for position %s" symbol-position)) + (lsp--info "Server does not support breadcrumb.")) + (lsp--info "Call this function with a number representing the symbol position on breadcrumb"))) + +(lsp-consistency-check lsp-headerline) + +(provide 'lsp-headerline) +;;; lsp-headerline.el ends here |