diff options
| author | João Távora | 2025-04-18 23:08:37 +0100 |
|---|---|---|
| committer | João Távora | 2025-04-20 23:20:33 +0100 |
| commit | 53d732d775fb416f6a412c2a87f12beed682c96c (patch) | |
| tree | 56074be7fe275b0da0c1a3f7e3e7f8038695e897 | |
| parent | ed1311a62a4e02817e548b0873ab21114047c076 (diff) | |
| download | emacs-53d732d775fb416f6a412c2a87f12beed682c96c.tar.gz emacs-53d732d775fb416f6a412c2a87f12beed682c96c.zip | |
Flymake: dynamically resize and layout diagnostic listings
Since 'origin' and 'code' are new separate optional attributes of each
diagnostic, it becomes important to not waste space in these listings
when these are absent. When a specific column isn't used by any line,
omit it. Also spare just enough horizontal space to hold the largest
element in each column.
* lisp/progmodes/flymake.el
(flymake--tabulated-setup): New helper.
(flymake-diagnostics-buffer-mode)
(flymake-project-diagnostics-mode): Use flymake--setup-tabulated-listing.
(flymake--fit-diagnostics-window): New helper.
(flymake--tabulated-list-format-base): Rename from
flymake--diagnostics-base-tabulated-list-format.
(flymake--tabulated-setup-1): Rename and rework from
flymake--tabulated-entries-1.
(flymake--diagnostics-buffer-entries): Remove.
(flymake-diagnostics-buffer-mode)
(flymake-project-diagnostics-mode): Simplify.
(flymake--project-diagnostics-entries): Remove.
| -rw-r--r-- | lisp/progmodes/flymake.el | 210 |
1 files changed, 118 insertions, 92 deletions
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f2f59f05131..2ea91ccb15c 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el | |||
| @@ -1948,8 +1948,9 @@ TYPE is usually keyword `:error', `:warning' or `:note'." | |||
| 1948 | (flymake--mode-line-counter-1 type)) | 1948 | (flymake--mode-line-counter-1 type)) |
| 1949 | probe))) | 1949 | probe))) |
| 1950 | 1950 | ||
| 1951 | ;;; Per-buffer diagnostic listing | 1951 | |
| 1952 | 1952 | ;;; Per-buffer diagnostic listings | |
| 1953 | ;;; | ||
| 1953 | (defvar-local flymake--diagnostics-buffer-source nil) | 1954 | (defvar-local flymake--diagnostics-buffer-source nil) |
| 1954 | 1955 | ||
| 1955 | (defvar flymake-diagnostics-buffer-mode-map | 1956 | (defvar flymake-diagnostics-buffer-mode-map |
| @@ -2005,21 +2006,27 @@ POS can be a buffer position or a button" | |||
| 2005 | (pop-to-buffer | 2006 | (pop-to-buffer |
| 2006 | (flymake-show-diagnostic (if (button-type pos) (button-start pos) pos)))) | 2007 | (flymake-show-diagnostic (if (button-type pos) (button-start pos) pos)))) |
| 2007 | 2008 | ||
| 2008 | (defun flymake--tabulated-diagnostic-origin (diag) | 2009 | (defvar flymake--tabulated-list-format-base |
| 2009 | (or (flymake-diagnostic-origin diag) | 2010 | `[("File" 15) |
| 2010 | (let* ((backend (flymake-diagnostic-backend diag)) | 2011 | ("Line" 5 ,(lambda (l1 l2) |
| 2011 | (bname (or (ignore-errors (symbol-name backend)) | 2012 | (< (plist-get (car l1) :line) |
| 2012 | "(anonymous function)"))) | 2013 | (plist-get (car l2) :line))) |
| 2013 | (propertize | 2014 | :right-align t) |
| 2014 | (replace-regexp-in-string "\\(.\\)[^-]+\\(-\\|$\\)" | 2015 | ("Col" 3 nil :right-align t) |
| 2015 | "\\1\\2" bname) | 2016 | ("Type" 8 ,(lambda (l1 l2) |
| 2016 | 'help-echo (format "From `%s' backend" backend))))) | 2017 | (< (plist-get (car l1) :severity) |
| 2017 | 2018 | (plist-get (car l2) :severity)))) | |
| 2018 | (defun flymake--tabulated-entries-1 (diags project-root) | 2019 | ("Origin" 8 t) |
| 2019 | "Helper for `flymake--diagnostics-buffer-entries'. | 2020 | ("Code" 8 t) |
| 2020 | PROJECT-ROOT indicates that each entry should be preceded by the | 2021 | ("Message" 0 t)]) |
| 2021 | filename of the diagnostic relative to that directory." | 2022 | |
| 2023 | (defun flymake--tabulated-setup-1 (diags project-root) | ||
| 2024 | "Helper for `flymake--tabulated-setup'. | ||
| 2025 | Sets `tabulated-list-format' and `tabulated-list-entries', dinamically | ||
| 2026 | resizing columns and ommiting redudant columns." | ||
| 2022 | (cl-loop | 2027 | (cl-loop |
| 2028 | with fields = (copy-tree flymake--tabulated-list-format-base t) | ||
| 2029 | initially (cl-loop for y across fields do (setf (cadr y) nil)) | ||
| 2023 | for diag in diags | 2030 | for diag in diags |
| 2024 | for locus = (flymake-diagnostic-buffer diag) | 2031 | for locus = (flymake-diagnostic-buffer diag) |
| 2025 | for file = (if (bufferp locus) | 2032 | for file = (if (bufferp locus) |
| @@ -2044,72 +2051,99 @@ filename of the diagnostic relative to that directory." | |||
| 2044 | (;; somehow dead annotated diagnostic, ignore/give up | 2051 | (;; somehow dead annotated diagnostic, ignore/give up |
| 2045 | t nil)) | 2052 | t nil)) |
| 2046 | for type = (flymake-diagnostic-type diag) | 2053 | for type = (flymake-diagnostic-type diag) |
| 2047 | for origin = (flymake--tabulated-diagnostic-origin diag) | 2054 | for data-line = `[,(and project-root |
| 2048 | for data-vec = `[,(format "%s" line) | 2055 | `(,(file-name-nondirectory file) |
| 2049 | ,(format "%s" col) | 2056 | help-echo ,(file-relative-name file project-root) |
| 2050 | ,(propertize (format "%s" | 2057 | face nil |
| 2051 | (flymake--lookup-type-property | 2058 | mouse-face highlight |
| 2052 | type 'flymake-type-name type)) | 2059 | action flymake-goto-diagnostic |
| 2053 | 'face (flymake--lookup-type-property | 2060 | mouse-action flymake-goto-diagnostic )) |
| 2054 | type 'mode-line-face 'flymake-error)) | 2061 | ,(format "%s" line) |
| 2055 | ,origin | 2062 | ,(format "%s" col) |
| 2056 | (,(flymake-diagnostic-text diag '(oneliner)) | 2063 | ,(propertize (format "%s" |
| 2057 | mouse-face highlight | 2064 | (flymake--lookup-type-property |
| 2058 | help-echo "mouse-2: visit this diagnostic" | 2065 | type 'flymake-type-name type)) |
| 2059 | face nil | 2066 | 'face (flymake--lookup-type-property |
| 2060 | action flymake-goto-diagnostic | 2067 | type 'mode-line-face 'flymake-error)) |
| 2061 | mouse-action flymake-goto-diagnostic)] | 2068 | ,(flymake-diagnostic-origin diag) |
| 2062 | when (and line col) collect | 2069 | ,(flymake-diagnostic-code diag) |
| 2063 | (list (list :diagnostic diag | 2070 | (,(flymake-diagnostic-text diag '(oneliner)) |
| 2064 | :line line | 2071 | mouse-face highlight |
| 2065 | :severity (flymake--lookup-type-property | 2072 | help-echo "mouse-2: visit this diagnostic" |
| 2066 | type | 2073 | face nil |
| 2067 | 'severity (warning-numeric-level :error))) | 2074 | action flymake-goto-diagnostic |
| 2068 | (if project-root | 2075 | mouse-action flymake-goto-diagnostic)] |
| 2069 | (vconcat `[(,(file-name-nondirectory file) | 2076 | for meta = (and line col |
| 2070 | help-echo ,(file-relative-name file project-root) | 2077 | (list :diagnostic diag |
| 2071 | face nil | 2078 | :line line |
| 2072 | mouse-face highlight | 2079 | :severity (flymake--lookup-type-property |
| 2073 | action flymake-goto-diagnostic | 2080 | type |
| 2074 | mouse-action flymake-goto-diagnostic )] | 2081 | 'severity (warning-numeric-level :error)))) |
| 2075 | data-vec) | 2082 | when meta |
| 2076 | data-vec)))) | 2083 | do (cl-loop for x across data-line |
| 2077 | 2084 | for y across fields | |
| 2078 | (defun flymake--diagnostics-buffer-entries () | 2085 | for z across flymake--tabulated-list-format-base |
| 2079 | "Get tabulated list entries for current tabulated list buffer. | 2086 | for xlen = (cond ((stringp x) (length x)) |
| 2080 | Expects `flymake--diagnostics-buffer-entries' to be bound to a | 2087 | (t (length (car x)))) |
| 2081 | buffer." | 2088 | when (cl-plusp xlen) |
| 2082 | ;; Do nothing if 'flymake--diagnostics-buffer-source' has not yet | 2089 | do (setf (cadr y) |
| 2083 | ;; been set to a valid buffer. This could happen when this function | 2090 | (max xlen |
| 2084 | ;; is called too early. For example 'global-display-line-numbers-mode' | 2091 | (or (cadr y) (cadr z))))) |
| 2085 | ;; calls us from its mode hook, when the diagnostic buffer has just | 2092 | collect (list meta data-line) into data |
| 2086 | ;; been created by 'flymake-show-buffer-diagnostics', but is not yet | 2093 | finally |
| 2087 | ;; set up properly (Bug#40529). | 2094 | ;; `data' and `fields' now hold more or less suitable values for |
| 2088 | (when (bufferp flymake--diagnostics-buffer-source) | 2095 | ;; `tabulated-list-entries' and `tabulated-list-format' respectively, |
| 2089 | (with-current-buffer flymake--diagnostics-buffer-source | 2096 | ;; but we need to trim them, first removing the columns of data where |
| 2090 | (when flymake-mode | 2097 | ;; the corresponding field is known to be nil for every line, and |
| 2091 | (flymake--tabulated-entries-1 (flymake-diagnostics) nil))))) | 2098 | ;; then removing the field description itself. |
| 2092 | 2099 | (cl-loop | |
| 2093 | (defvar flymake--diagnostics-base-tabulated-list-format | 2100 | for entry in data |
| 2094 | `[("Line" 5 ,(lambda (l1 l2) | 2101 | do (setf (cadr entry) (cl-loop for x across (cadr entry) |
| 2095 | (< (plist-get (car l1) :line) | 2102 | for y across fields |
| 2096 | (plist-get (car l2) :line))) | 2103 | when (cadr y) |
| 2097 | :right-align t) | 2104 | vconcat (vector (or x "-"))))) |
| 2098 | ("Col" 3 nil :right-align t) | 2105 | (setq tabulated-list-entries data |
| 2099 | ("Type" 8 ,(lambda (l1 l2) | 2106 | tabulated-list-format |
| 2100 | (< (plist-get (car l1) :severity) | 2107 | (cl-loop for y across fields |
| 2101 | (plist-get (car l2) :severity)))) | 2108 | when (cadr y) vconcat (vector y))))) |
| 2102 | ("Origin" 8 t) | 2109 | |
| 2103 | ("Message" 0 t)]) | 2110 | (defun flymake--tabulated-setup (use-project) |
| 2111 | "Helper for `flymake-diagnostics-buffer-mode'. | ||
| 2112 | And also `flymake-project-diagnostics-mode'." | ||
| 2113 | (let ((saved-r-b-f revert-buffer-function) | ||
| 2114 | (refresh | ||
| 2115 | (lambda () | ||
| 2116 | (cond | ||
| 2117 | (use-project | ||
| 2118 | (let ((p (project-current))) | ||
| 2119 | (flymake--tabulated-setup-1 | ||
| 2120 | (flymake--project-diagnostics p) | ||
| 2121 | (project-root p)))) | ||
| 2122 | (t | ||
| 2123 | ;; Do nothing if 'flymake--diagnostics-buffer-source' has | ||
| 2124 | ;; not yet been set to a valid buffer. This could happen | ||
| 2125 | ;; when this function is called too early. For example | ||
| 2126 | ;; 'global-display-line-numbers-mode' calls us from its | ||
| 2127 | ;; mode hook, when the diagnostic buffer has just been | ||
| 2128 | ;; created by 'flymake-show-buffer-diagnostics', but is not | ||
| 2129 | ;; yet set up properly (Bug#40529). | ||
| 2130 | (flymake--tabulated-setup-1 | ||
| 2131 | (and (bufferp flymake--diagnostics-buffer-source) | ||
| 2132 | (with-current-buffer flymake--diagnostics-buffer-source | ||
| 2133 | (and flymake-mode | ||
| 2134 | (flymake-diagnostics)))) | ||
| 2135 | nil))) | ||
| 2136 | (tabulated-list-init-header)))) | ||
| 2137 | (setq revert-buffer-function | ||
| 2138 | (lambda (&rest args) | ||
| 2139 | (funcall refresh) | ||
| 2140 | (apply saved-r-b-f args))))) | ||
| 2104 | 2141 | ||
| 2105 | (define-derived-mode flymake-diagnostics-buffer-mode tabulated-list-mode | 2142 | (define-derived-mode flymake-diagnostics-buffer-mode tabulated-list-mode |
| 2106 | "Flymake diagnostics" | 2143 | "Flymake diagnostics" |
| 2107 | "A mode for listing Flymake diagnostics." | 2144 | "A mode for listing Flymake diagnostics." |
| 2108 | :interactive nil | 2145 | :interactive nil |
| 2109 | (setq tabulated-list-format flymake--diagnostics-base-tabulated-list-format) | 2146 | (flymake--tabulated-setup nil)) |
| 2110 | (setq tabulated-list-entries | ||
| 2111 | 'flymake--diagnostics-buffer-entries) | ||
| 2112 | (tabulated-list-init-header)) | ||
| 2113 | 2147 | ||
| 2114 | (defun flymake--diagnostics-buffer-name () | 2148 | (defun flymake--diagnostics-buffer-name () |
| 2115 | (format "*Flymake diagnostics for `%s'*" (current-buffer))) | 2149 | (format "*Flymake diagnostics for `%s'*" (current-buffer))) |
| @@ -2117,6 +2151,9 @@ buffer." | |||
| 2117 | (define-obsolete-function-alias 'flymake-show-diagnostics-buffer | 2151 | (define-obsolete-function-alias 'flymake-show-diagnostics-buffer |
| 2118 | 'flymake-show-buffer-diagnostics "1.2.1") | 2152 | 'flymake-show-buffer-diagnostics "1.2.1") |
| 2119 | 2153 | ||
| 2154 | (defun flymake--fit-diagnostics-window (window) | ||
| 2155 | (fit-window-to-buffer window 15 8)) | ||
| 2156 | |||
| 2120 | (defun flymake-show-buffer-diagnostics (&optional diagnostic) | 2157 | (defun flymake-show-buffer-diagnostics (&optional diagnostic) |
| 2121 | "Show a list of Flymake diagnostics for current buffer." | 2158 | "Show a list of Flymake diagnostics for current buffer." |
| 2122 | (interactive) | 2159 | (interactive) |
| @@ -2134,8 +2171,7 @@ buffer." | |||
| 2134 | (display-buffer (current-buffer) | 2171 | (display-buffer (current-buffer) |
| 2135 | `((display-buffer-reuse-window | 2172 | `((display-buffer-reuse-window |
| 2136 | display-buffer-below-selected) | 2173 | display-buffer-below-selected) |
| 2137 | (window-height . (lambda (window) | 2174 | (window-height . flymake--fit-diagnostics-window))) |
| 2138 | (fit-window-to-buffer window 10))))) | ||
| 2139 | (when (and diagnostic flymake-after-show-buffer-diagnostics-hook) | 2175 | (when (and diagnostic flymake-after-show-buffer-diagnostics-hook) |
| 2140 | (goto-char (point-min)) | 2176 | (goto-char (point-min)) |
| 2141 | (catch 'done | 2177 | (catch 'done |
| @@ -2174,14 +2210,9 @@ some of this variable's contents the diagnostic listings.") | |||
| 2174 | 2210 | ||
| 2175 | (define-derived-mode flymake-project-diagnostics-mode tabulated-list-mode | 2211 | (define-derived-mode flymake-project-diagnostics-mode tabulated-list-mode |
| 2176 | "Flymake diagnostics" | 2212 | "Flymake diagnostics" |
| 2177 | "A mode for listing Flymake diagnostics." | 2213 | "A mode for listing Flymake diagnostics in a project." |
| 2178 | :interactive nil | 2214 | :interactive nil |
| 2179 | (setq tabulated-list-format | 2215 | (flymake--tabulated-setup t)) |
| 2180 | (vconcat [("File" 25 t)] | ||
| 2181 | flymake--diagnostics-base-tabulated-list-format)) | ||
| 2182 | (setq tabulated-list-entries | ||
| 2183 | 'flymake--project-diagnostics-entries) | ||
| 2184 | (tabulated-list-init-header)) | ||
| 2185 | 2216 | ||
| 2186 | (cl-defun flymake--project-diagnostics (&optional (project (project-current))) | 2217 | (cl-defun flymake--project-diagnostics (&optional (project (project-current))) |
| 2187 | "Get all known relevant diagnostics for PROJECT." | 2218 | "Get all known relevant diagnostics for PROJECT." |
| @@ -2226,11 +2257,6 @@ some of this variable's contents the diagnostic listings.") | |||
| 2226 | append diags)) | 2257 | append diags)) |
| 2227 | (append buffer-annotated-diags relevant-foreign-diags list-only-diags))) | 2258 | (append buffer-annotated-diags relevant-foreign-diags list-only-diags))) |
| 2228 | 2259 | ||
| 2229 | (defun flymake--project-diagnostics-entries () | ||
| 2230 | (let ((p (project-current))) | ||
| 2231 | (flymake--tabulated-entries-1 (flymake--project-diagnostics p) | ||
| 2232 | (project-root p)))) | ||
| 2233 | |||
| 2234 | (defun flymake--project-diagnostics-buffer (root) | 2260 | (defun flymake--project-diagnostics-buffer (root) |
| 2235 | (get-buffer-create (format "*Flymake diagnostics for `%s'*" root))) | 2261 | (get-buffer-create (format "*Flymake diagnostics for `%s'*" root))) |
| 2236 | 2262 | ||
| @@ -2247,7 +2273,7 @@ some of this variable's contents the diagnostic listings.") | |||
| 2247 | (display-buffer (current-buffer) | 2273 | (display-buffer (current-buffer) |
| 2248 | `((display-buffer-reuse-window | 2274 | `((display-buffer-reuse-window |
| 2249 | display-buffer-at-bottom) | 2275 | display-buffer-at-bottom) |
| 2250 | (window-height . fit-window-to-buffer)))))) | 2276 | (window-height . flymake--fit-diagnostics-window)))))) |
| 2251 | 2277 | ||
| 2252 | (defun flymake--update-diagnostics-listings (buffer) | 2278 | (defun flymake--update-diagnostics-listings (buffer) |
| 2253 | "Update diagnostics listings somehow relevant to BUFFER." | 2279 | "Update diagnostics listings somehow relevant to BUFFER." |