Skip to content

Commit

Permalink
Add Workspace Symbol completion backend.
Browse files Browse the repository at this point in the history
  • Loading branch information
manateelazycat committed Oct 7, 2024
1 parent 676ef9e commit 58d53c1
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 0 deletions.
147 changes: 147 additions & 0 deletions acm/acm-backend-lsp-workspace-symbol.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
;;; acm-backend-lsp-workspace-symbol.el --- LSP backend for acm -*- lexical-binding: t; no-byte-compile: t; -*-

;; Filename: acm-backend-lsp-workspace-symbol.el
;; Description: LSP backend for acm
;; Author: Andy Stewart <[email protected]>
;; Maintainer: Andy Stewart <[email protected]>
;; Copyright (C) 2022, Andy Stewart, all rights reserved.
;; Created: 2022-06-07 08:56:16
;; Version: 0.1
;; Last-Updated: 2022-10-10 14:09:54 +0800
;; By: Gong Qijian
;; URL: https://www.github.org/manateelazycat/acm-backend-lsp-workspace-symbol
;; Keywords:
;; Compatibility: GNU Emacs 28.1
;;
;; Features that might be required by this library:
;;
;;
;;

;;; This file is NOT part of GNU Emacs

;;; 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:
;;
;; LSP backend for acm
;;

;;; Installation:
;;
;; Put acm-backend-lsp-workspace-symbol.el to your load-path.
;; The load-path is usually ~/elisp/.
;; It's set in your ~/.emacs like this:
;; (add-to-list 'load-path (expand-file-name "~/elisp"))
;;
;; And the following to your ~/.emacs startup file.
;;
;; (require 'acm-backend-lsp-workspace-symbol)
;;
;; No need more.

;;; Customize:
;;
;;
;;
;; All of the above can customize by:
;; M-x customize-group RET acm-backend-lsp-workspace-symbol RET
;;

;;; Change log:
;;
;; 2022/06/07
;; * First released.
;;

;;; Acknowledgements:
;;
;;
;;

;;; TODO
;;
;;
;;

;;; Require


;;; Code:

(defgroup acm-backend-lsp-workspace-symbol nil
"LSP backend for acm."
:group 'acm)

(defcustom acm-backend-lsp-workspace-symbol-candidate-min-length 0
"Minimal length of candidate."
:type 'integer
:group 'acm-backend-lsp-workspace-symbol)

(defcustom acm-backend-lsp-workspace-symbol-frontend-filter-p nil
"Because LSP workspace symbols has filtered at Python backend.
So don't need filter candidates again when show candidates in acm menu.
Anyway, if want use `acm-candidate-fuzzy-search' filter again in acm menu, turn on this option."
:type 'string
:group 'acm-backend-lsp-workspace-symbol)


(defun acm-backend-lsp-workspace-symbol-candidates (keyword)
(let ((match-candidates
(acm-with-cache-candidates
acm-backend-lsp-workspace-symbol-cache-candidates
(let* ((candidates (list)))
(when (and
(>= (length keyword) acm-backend-lsp-workspace-symbol-candidate-min-length)
(boundp 'acm-backend-lsp-workspace-symbol-items)
acm-backend-lsp-workspace-symbol-items
(boundp 'acm-backend-lsp-workspace-symbol-server-names)
acm-backend-lsp-workspace-symbol-server-names
(hash-table-p acm-backend-lsp-workspace-symbol-items))
(dolist (server-name acm-backend-lsp-workspace-symbol-server-names)
(when-let* ((server-items (gethash server-name acm-backend-lsp-workspace-symbol-items)))
(maphash (lambda (k v)
(add-to-list 'candidates v t))
server-items))))

;; NOTE:
;; lsp-bridge has sort candidate at Python side,
;; please do not do secondary sorting here, elisp is very slow.
candidates))))

;; Show candidates
(cond
;; Don't filter candidates is prefix is empty.
((string-equal keyword "")
match-candidates)
;; Fitler candidates when `acm-backend-lsp-workspace-symbol-frontend-filter-p' is non-nil.
(acm-backend-lsp-workspace-symbol-frontend-filter-p
(seq-filter (lambda (c) (acm-candidate-fuzzy-search keyword (plist-get c :label))) match-candidates))
;; Don't filter candidates default, because LSP workspace symbols has filtered at Python backend.
(t
match-candidates))))

(defun acm-backend-lsp-workspace-symbol-clean ()
(setq-local acm-backend-lsp-workspace-symbol-items (make-hash-table :test 'equal))
(setq-local acm-backend-lsp-workspace-symbol-cache-candidates nil))

(provide 'acm-backend-lsp-workspace-symbol)

;;; acm-backend-lsp-workspace-symbol.el ends here
1 change: 1 addition & 0 deletions acm/acm-icon.el
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
("capf" . ("material" "infinity" "#f47b7b"))
("jupyter" . ("material" "exponent" "#f47b7b"))
("copilot" . ("octicons" "copilot" "#808080"))
("workspace-symbol" . ("material" "shark-fin-outline" "#0085c3"))
(t . ("material" "smoking-pipe" "#90cef1"))))

(defvar acm-icon-cache (make-hash-table :test 'equal))
Expand Down
5 changes: 5 additions & 0 deletions acm/acm.el
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
(require 'acm-backend-yas)
(require 'acm-backend-elisp)
(require 'acm-backend-lsp)
(require 'acm-backend-lsp-workspace-symbol)
(require 'acm-backend-path)
(require 'acm-backend-search-file-words)
(require 'acm-backend-search-sdcv-words)
Expand Down Expand Up @@ -196,6 +197,7 @@

(defcustom acm-completion-mode-candidates-merge-order '("elisp-candidates"
"lsp-candidates"
"lsp-workspace-symbol-candidates"
"capf-candidates"
"jupyter-candidates"
"ctags-candidates"
Expand Down Expand Up @@ -467,6 +469,7 @@ Only calculate template candidate when type last character."
(mode-candidates-min-index 2)
(template-candidates-min-index 2)
lsp-candidates
lsp-workspace-symbol-candidates
capf-candidates
path-candidates
yas-candidates
Expand Down Expand Up @@ -518,11 +521,13 @@ Only calculate template candidate when type last character."
(setq ctags-candidates (unless (acm-in-comment-p) (acm-backend-ctags-candidates keyword))))
;; Fetch syntax completion candidates.
(setq lsp-candidates (unless (acm-in-comment-p) (acm-backend-lsp-candidates keyword)))
(setq lsp-workspace-symbol-candidates (unless (acm-in-comment-p) (acm-backend-lsp-workspace-symbol-candidates keyword)))
(setq mode-candidates
(apply #'append (mapcar (lambda (mode-candidate-name)
(pcase mode-candidate-name
("elisp-candidates" (unless (acm-in-comment-p) (acm-backend-elisp-candidates keyword)))
("lsp-candidates" lsp-candidates)
("lsp-workspace-symbol-candidates" lsp-workspace-symbol-candidates)
("capf-candidates" capf-candidates)
("jupyter-candidates" jupyter-candidates)
("ctags-candidates" ctags-candidates)
Expand Down
2 changes: 2 additions & 0 deletions acm/icons/material_shark-fin-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions core/fileaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,18 @@ def try_completion(self, position, before_char, prefix, version=None):
if self.multi_servers:
for lsp_server in self.multi_servers.values():
if lsp_server.server_info["name"] in self.multi_servers_info["completion"]:
# Send code completion request.
self.send_server_request(lsp_server, "completion", lsp_server, position, before_char, prefix, version)

# Send workspace symbol completion request.
self.send_server_request(lsp_server, "completion_workspace_symbol", lsp_server, prefix)
else:
# Send code completion request.
self.send_server_request(self.single_server, "completion", self.single_server, position, before_char, prefix, version)

# Send workspace symbol completion request.
self.send_server_request(self.single_server, "completion_workspace_symbol", self.single_server, prefix)

def try_formatting(self, start, end, *args, **kwargs):
if self.multi_servers:
for lsp_server in self.multi_servers.values():
Expand Down
1 change: 1 addition & 0 deletions core/handler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def handle_response(self, request_id, response):
from core.handler.range_formatting import RangeFormatting # noqa: F401
from core.handler.execute_command import ExecuteCommand # noqa: F401
from core.handler.workspace_symbol import WorkspaceSymbol # noqa: F401
from core.handler.completion_workspace_symbol import CompletionWorkspaceSymbol # noqa: F401
from core.handler.call_hierarchy import PrepareCallHierarchyIncomingCalls, PrepareCallHierarchyOutgoingCalls, CallHierarchyIncomingCalls, CallHierarchyOutgoingCalls # noqa: F401
from core.handler.document_symbol import DocumentSymbol # noqa: F401
from core.handler.imenu import IMenu # noqa: F401
Expand Down
41 changes: 41 additions & 0 deletions core/handler/completion_workspace_symbol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from core.handler import Handler
from core.utils import *


class CompletionWorkspaceSymbol(Handler):
name = "completion_workspace_symbol"
method = "workspace/symbol"
provider = "workspace_symbol_provider"

def process_request(self, lsp_server, query) -> dict:
self.lsp_server = lsp_server
self.lsp_server_name = self.lsp_server.server_info["name"]
query = ''.join(query.split())
return dict(query=query)

def process_response(self, response: dict) -> None:
if response is not None:
completion_symbols = []

for item in response:
symbol_name = item["name"]
symbol_kind = KIND_MAP[item.get("kind", 0)].lower()
symbol_display_name = "{} [{}]".format(symbol_name, symbol_kind)
symbol = {
"key": symbol_name,
"icon": "workspace-symbol",
"label": symbol_name,
"displayLabel": symbol_display_name,
"server": self.lsp_server_name,
"backend": "lsp-workspace-symbol"
}

completion_symbols.append(symbol)

if len(completion_symbols) > 0:
eval_in_emacs("lsp-bridge-completion-workspace-symbol--record-items",
self.file_action.filepath,
get_lsp_file_host(),
completion_symbols,
self.lsp_server_name,
self.file_action.get_lsp_server_names())
28 changes: 28 additions & 0 deletions lsp-bridge.el
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,28 @@ So we build this macro to restore postion after code format."
(setq-local acm-backend-lsp-items lsp-items))
(lsp-bridge-try-completion))))

(defun lsp-bridge-completion-workspace-symbol--record-items (filename
filehost
candidates
server-name
server-names)
;; Adjust `gc-cons-threshold' to maximize temporary,
;; make sure Emacs not do GC
(let ((gc-cons-threshold most-positive-fixnum))
(lsp-bridge--with-file-buffer filename filehost
;; Save completion items.
(setq-local acm-backend-lsp-workspace-symbol-cache-candidates nil)
(setq-local acm-backend-lsp-workspace-symbol-server-names server-names)

(let* ((lsp-items acm-backend-lsp-workspace-symbol-items)
(completion-table (make-hash-table :test 'equal)))
(dolist (item candidates)
(plist-put item :annotation (capitalize (plist-get item :icon)))
(puthash (plist-get item :key) item completion-table))
(puthash server-name completion-table lsp-items)
(setq-local acm-backend-lsp-workspace-symbol-items lsp-items))
(lsp-bridge-try-completion))))

(defun lsp-bridge-check-predicate (pred current-function)
(if (functionp pred)
(let ((result (funcall pred)))
Expand Down Expand Up @@ -2370,6 +2392,12 @@ Default is `bottom-right', you can choose other value: `top-left', `top-right',
(setq-local acm-backend-lsp-filepath (lsp-bridge-get-buffer-truename))
(setq-local acm-backend-lsp-items (make-hash-table :test 'equal))

(setq-local acm-backend-lsp-workspace-symbol-cache-candidates nil)
(setq-local acm-backend-lsp-workspace-symbol-completion-position nil)
(setq-local acm-backend-lsp-workspace-symbol-completion-trigger-characters nil)
(setq-local acm-backend-lsp-workspace-symbol-server-names nil)
(setq-local acm-backend-lsp-workspace-symbol-items (make-hash-table :test 'equal))

(when lsp-bridge-enable-signature-help
(acm-run-idle-func lsp-bridge-signature-help-timer lsp-bridge-signature-help-fetch-idle 'lsp-bridge-signature-help-fetch))
(when lsp-bridge-enable-auto-format-code
Expand Down

0 comments on commit 58d53c1

Please sign in to comment.