|
47 | 47 | :group 'lsp-ivy
|
48 | 48 | :type 'boolean)
|
49 | 49 |
|
| 50 | +(defcustom lsp-ivy-show-symbol-filename |
| 51 | + t |
| 52 | + "Whether to show the project-relative path to a symbol's point of definition." |
| 53 | + :group 'lsp-ivy |
| 54 | + :type 'boolean) |
| 55 | + |
50 | 56 | (defcustom lsp-ivy-filter-symbol-kind
|
51 | 57 | nil
|
52 | 58 | "A list of LSP SymbolKind's to filter out."
|
|
112 | 118 | (cons string face)
|
113 | 119 | (cons string face)))
|
114 | 120 |
|
115 |
| - |
116 |
| -(defun lsp-ivy--format-symbol-match (symbol-information-match) |
117 |
| - "Convert the match returned by `lsp-mode` into a candidate string. |
118 |
| -SYMBOL-INFORMATION-MATCH is a cons cell whose cdr is the SymbolInformation interface from `lsp-mode`." |
119 |
| - (-let* (((&SymbolInformation :name :kind :container-name?) (cdr symbol-information-match)) |
120 |
| - (type (elt lsp-ivy-symbol-kind-to-face kind)) |
121 |
| - (typestr (if lsp-ivy-show-symbol-kind |
122 |
| - (propertize (format "[%s] " (car type)) 'face (cdr type)) |
123 |
| - ""))) |
| 121 | +(eval-when-compile |
| 122 | + (lsp-interface |
| 123 | + (lsp-ivy:FormattedSymbolInformation |
| 124 | + (:kind :name :location :textualRepresentation) |
| 125 | + (:containerName :deprecated)))) |
| 126 | + |
| 127 | +(lsp-defun lsp-ivy--workspace-symbol-action |
| 128 | + ((&SymbolInformation |
| 129 | + :location (&Location :uri :range (&Range :start (&Position :line :character))))) |
| 130 | + "Jump to selected candidate." |
| 131 | + (find-file (lsp--uri-to-path uri)) |
| 132 | + (goto-char (point-min)) |
| 133 | + (forward-line line) |
| 134 | + (forward-char character)) |
| 135 | + |
| 136 | +(lsp-defun lsp-ivy--format-symbol-match |
| 137 | + ((&SymbolInformation :name :kind :container-name? :location (&Location :uri)) |
| 138 | + project-root) |
| 139 | + "Convert the match returned by `lsp-mode` into a candidate string." |
| 140 | + (let* ((type (elt lsp-ivy-symbol-kind-to-face kind)) |
| 141 | + (typestr (if lsp-ivy-show-symbol-kind |
| 142 | + (propertize (format "[%s] " (car type)) 'face (cdr type)) |
| 143 | + "")) |
| 144 | + (pathstr (if lsp-ivy-show-symbol-filename |
| 145 | + (propertize (format " · %s" (file-relative-name (lsp--uri-to-path uri) project-root)) |
| 146 | + 'face font-lock-comment-face) ""))) |
124 | 147 | (concat typestr (if (or (null container-name?) (string-empty-p container-name?))
|
125 | 148 | (format "%s" name)
|
126 |
| - (format "%s.%s" container-name? name))))) |
127 |
| - |
128 |
| -(defun lsp-ivy--workspace-symbol-action (symbol-information-candidate) |
129 |
| - "Jump to selected SYMBOL-INFORMATION-CANDIDATE, a cons cell whose cdr is a a SymbolInformation." |
130 |
| - (-let (((&SymbolInformation :location |
131 |
| - (&Location :uri |
132 |
| - :range |
133 |
| - (&Range :start |
134 |
| - (&Position :line :character)))) |
135 |
| - (cdr symbol-information-candidate))) |
136 |
| - (find-file (lsp--uri-to-path uri)) |
137 |
| - (goto-char (point-min)) |
138 |
| - (forward-line line) |
139 |
| - (forward-char character))) |
140 |
| - |
141 |
| -(lsp-defun lsp-ivy--filter-func ((&SymbolInformation :kind)) |
142 |
| - "Filter candidate kind from symbol kinds." |
143 |
| - (member kind lsp-ivy-filter-symbol-kind)) |
| 149 | + (format "%s.%s" container-name? name)) pathstr))) |
| 150 | + |
| 151 | +(lsp-defun lsp-ivy--transform-candidate ((symbol-information &as &SymbolInformation :kind) |
| 152 | + filter-regexps? workspace-root) |
| 153 | + "Map candidate to nil if it should be excluded based on `lsp-ivy-filter-symbol-kind' or |
| 154 | +FILTER-REGEXPS?, otherwise convert it to an `lsp-ivy:FormattedSymbolInformation' object." |
| 155 | + (unless (member kind lsp-ivy-filter-symbol-kind) |
| 156 | + (let ((textual-representation |
| 157 | + (lsp-ivy--format-symbol-match symbol-information workspace-root))) |
| 158 | + (when (--all? (string-match-p it textual-representation) filter-regexps?) |
| 159 | + (lsp-put symbol-information :textualRepresentation textual-representation) |
| 160 | + symbol-information)))) |
144 | 161 |
|
145 | 162 | (defun lsp-ivy--workspace-symbol (workspaces prompt initial-input)
|
146 | 163 | "Search against WORKSPACES with PROMPT and INITIAL-INPUT."
|
147 |
| - (ivy-read |
148 |
| - prompt |
149 |
| - (lambda (user-input) |
150 |
| - (with-lsp-workspaces workspaces |
151 |
| - (lsp-request-async |
152 |
| - "workspace/symbol" |
153 |
| - (lsp-make-workspace-symbol-params :query user-input) |
154 |
| - (lambda (result) |
155 |
| - (ivy-update-candidates |
156 |
| - (mapcar |
157 |
| - (-lambda ((symbol-information &as &SymbolInformation :name)) |
158 |
| - (cons name symbol-information)) |
159 |
| - (-remove #'lsp-ivy--filter-func result)))) |
160 |
| - :mode 'detached |
161 |
| - :cancel-token :workspace-symbol)) |
162 |
| - 0) |
163 |
| - :dynamic-collection t |
164 |
| - :require-match t |
165 |
| - :initial-input initial-input |
166 |
| - :action #'lsp-ivy--workspace-symbol-action |
167 |
| - :caller 'lsp-ivy-workspace-symbol)) |
| 164 | + (let* ((prev-query nil) |
| 165 | + (unfiltered-candidates '()) |
| 166 | + (filtered-candidates nil) |
| 167 | + (workspace-root (lsp-workspace-root)) |
| 168 | + (update-candidates |
| 169 | + (lambda (all-candidates filter-regexps?) |
| 170 | + (setq filtered-candidates |
| 171 | + (--keep (lsp-ivy--transform-candidate it filter-regexps? workspace-root) |
| 172 | + all-candidates)) |
| 173 | + (ivy-update-candidates filtered-candidates)))) |
| 174 | + (ivy-read |
| 175 | + prompt |
| 176 | + (lambda (user-input) |
| 177 | + (let* ((parts (split-string user-input)) |
| 178 | + (query (or (car parts) "")) |
| 179 | + (filter-regexps? (mapcar #'regexp-quote (cdr parts)))) |
| 180 | + (when query |
| 181 | + (if (string-equal prev-query query) |
| 182 | + (funcall update-candidates unfiltered-candidates filter-regexps?) |
| 183 | + (with-lsp-workspaces workspaces |
| 184 | + (lsp-request-async |
| 185 | + "workspace/symbol" |
| 186 | + (lsp-make-workspace-symbol-params :query query) |
| 187 | + (lambda (result) |
| 188 | + (setq unfiltered-candidates result) |
| 189 | + (funcall update-candidates unfiltered-candidates filter-regexps?)) |
| 190 | + :mode 'detached |
| 191 | + :cancel-token :workspace-symbol)))) |
| 192 | + (setq prev-query query)) |
| 193 | + (or filtered-candidates 0)) |
| 194 | + :dynamic-collection t |
| 195 | + :require-match t |
| 196 | + :initial-input initial-input |
| 197 | + :action #'lsp-ivy--workspace-symbol-action |
| 198 | + :caller 'lsp-ivy-workspace-symbol))) |
168 | 199 |
|
169 | 200 | (ivy-configure 'lsp-ivy-workspace-symbol
|
170 |
| - :display-transformer-fn #'lsp-ivy--format-symbol-match) |
| 201 | + :display-transformer-fn |
| 202 | + (-lambda ((&lsp-ivy:FormattedSymbolInformation :textual-representation)) |
| 203 | + textual-representation)) |
171 | 204 |
|
172 | 205 | ;;;###autoload
|
173 | 206 | (defun lsp-ivy-workspace-symbol (arg)
|
|
0 commit comments