diff --git a/_Sidebar.md b/_Sidebar.md index eabcfdd..c4ad61c 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -6,7 +6,9 @@ - [[FAQ]] * Editor configuration - [[Atom]] - - [[Emacs]] + - Emacs + + [[lsp-mode]] (+ emacs-ccls) + + [[eglot]] - Vim/Neovim + [[coc.nvim]] + [[LanguageClient-neovim]] diff --git a/eglot.md b/eglot.md new file mode 100644 index 0000000..a203148 --- /dev/null +++ b/eglot.md @@ -0,0 +1,59 @@ +1. See [[Getting started]] to build the `ccls` executable +2. Install [eglot](https://github.com/joaotavora/eglot) +3. Open a C/C++/ObjC source file. +4. `M-x eglot-ensure` + + +eglot uses builtin `project.el` to detect the project root. If you want to use projectile: + +```elisp +(defun projectile-project-find-function (dir) + (let* ((root (projectile-project-root dir))) + (and root (cons 'transient root)))) + +(with-eval-after-load 'project + (add-to-list 'project-find-functions 'projectile-project-find-function)) +``` + +### Completion + +eglot defines `completion-at-point-functions`, set `company-capf` to the only or the first item of `company-backends`. + +ccls has a fuzzy matching algorithm to order candidates according to your query. You may want to disable client-side sorting: + +```elisp +(setq company-transformers nil) +``` + +### Misc + +Use help window to display hierarchies: + +```elisp +(defun eglot-ccls-inheritance-hierarchy (&optional derived) + "Show inheritance hierarchy for the thing at point. +If DERIVED is non-nil (interactively, with prefix argument), show +the children of class at point." + (interactive "P") + (if-let* ((res (jsonrpc-request + (eglot--current-server-or-lose) + :$ccls/inheritance + (append (eglot--TextDocumentPositionParams) + `(:derived ,(if derived t :json-false)) + '(:levels 100) '(:hierarchy t)))) + (tree (list (cons 0 res)))) + (with-help-window "*ccls inheritance*" + (with-current-buffer standard-output + (while tree + (pcase-let ((`(,depth . ,node) (pop tree))) + (cl-destructuring-bind (&key uri range) (plist-get node :location) + (insert (make-string depth ?\ ) (plist-get node :name) "\n") + (make-text-button (+ (point-at-bol 0) depth) (point-at-eol 0) + 'action (lambda (_arg) + (interactive) + (find-file (eglot--uri-to-path uri)) + (goto-char (car (eglot--range-region range))))) + (cl-loop for child across (plist-get node :children) + do (push (cons (1+ depth) child) tree))))))) + (eglot--error "Hierarchy unavailable"))) +``` diff --git a/Emacs.md b/lsp-mode.md similarity index 83% rename from Emacs.md rename to lsp-mode.md index 338a8a8..eb77544 100644 --- a/Emacs.md +++ b/lsp-mode.md @@ -1,6 +1,6 @@ 1. See [[Getting started]] to build the `ccls` executable 2. Install [lsp-mode](https://github.com/emacs-lsp/lsp-mode) and optionally, [lsp-ui](https://github.com/emacs-lsp/lsp-ui) + [company-lsp](https://github.com/tigersoldier/company-lsp) -3. Install [emacs-ccls](https://github.com/MaskRay/emacs-ccls) (ccls on Melpa) and [configure](#configure) it +3. Install [emacs-ccls](https://github.com/MaskRay/emacs-ccls) (Melpa: https://melpa.org/#/ccls) and [configure](#configure) it 4. Open a source file where either [[.ccls|Getting-started#ccls]] or [[compile_commands.json]] is in the project root (it works without them, if you don't need extra compiler command line options like `-I`) 5. `(require 'ccls)`, `M-x lsp` @@ -10,10 +10,6 @@ Alternatively, you may install https://github.com/joaotavora/eglot -### Install [emacs-ccls](https://github.com/MaskRay/emacs-ccls) - -https://melpa.org/#/ccls - ### Configure #### spacemacs @@ -27,8 +23,12 @@ set `c-c++-backend` to `lsp-ccls` in the `+lang/c-c++` layer :commands lsp :config (require 'lsp-clients)) -(use-package ccls :defer t) -(add-hook 'c++-mode-hook (lambda () (require 'ccls) (lsp))) +(use-package company-lsp + :commands company-lsp) + +(use-package ccls + :hook ((c-mode c++-mode objc-mode) . + (lambda () (cl-pushnew #'company-lsp company-backends) (require 'ccls) (lsp)))) ``` The only required configuration is `ccls-executable`. Others have good defaults. @@ -41,7 +41,7 @@ The only required configuration is `ccls-executable`. Others have good defaults. ;; (setq ccls-executable "/usr/bin/ccls") ;; ;; Log file -;; (setq ccls-extra-args '("--log-file=/tmp/cq.log")) +;; (setq ccls-extra-args '("--log-file=/tmp/ccls.log")) ``` A more flexible way is to leave `ccls-executable` unchanged (default: `ccls`) and create a shell wrapper named `ccls` that is in your `PATH`: @@ -59,36 +59,10 @@ Some common settings to lsp-mode lsp-ui and emacs-ccls: #### Projects with subprojects -When `M-x lsp-ccls-enable` is invoked, projectile is consulted to locate the project root which determines the associated lsp-mode workspace. +When `M-x lsp` is invoked, projectile is consulted to locate the project root which determines the associated lsp-mode workspace. If your project has subprojects, `(projectile-project-root)` may think files in the subproject belong to the child workspace, which is not desired. `touch .ccls-root` in the root directory to override projectile roots. -If you do not mind files in subprojects are treated as part of the whole project in projectile: - -```elisp -(with-eval-after-load 'projectile - (setq projectile-project-root-files-top-down-recurring - (append '("compile_commands.json" - ".ccls") - projectile-project-root-files-top-down-recurring))) -``` - -To turn on ccls for all C/C++ modes: -```elisp -(defun +ccls/enable () - (condition-case nil - (lsp-ccls-enable) - (user-error nil))) - -(use-package ccls - :commands lsp-ccls-enable - :init - (add-hook 'c-mode-hook #'+ccls/enable) - (add-hook 'c++-mode-hook #'+ccls/enable) - (add-hook 'objc-mode-hook #'+ccls/enable) - (add-hook 'cuda-mode-hook #'+ccls/enable) - ) -;; Also see lsp-project-whitelist lsp-project-blacklist -``` +Read `lsp-project-whitelist lsp-project-blacklist` if you don't want to start ccls for some paths. ### Diagnostics @@ -322,36 +296,3 @@ For out-of-band changes to the files in the workspace that are not made in the L `(ccls-reload)` to reload/rebuild indexes for every file. * Performance of lsp-ui-flycheck https://github.com/emacs-lsp/lsp-ui/issues/45 - -### eglot - -Use help window to display hierarchies: - -```elisp -(defun eglot-ccls-inheritance-hierarchy (&optional derived) - "Show inheritance hierarchy for the thing at point. -If DERIVED is non-nil (interactively, with prefix argument), show -the children of class at point." - (interactive "P") - (if-let* ((res (jsonrpc-request - (eglot--current-server-or-lose) - :$ccls/inheritance - (append (eglot--TextDocumentPositionParams) - `(:derived ,(if derived t :json-false)) - '(:levels 100) '(:hierarchy t)))) - (tree (list (cons 0 res)))) - (with-help-window "*ccls inheritance*" - (with-current-buffer standard-output - (while tree - (pcase-let ((`(,depth . ,node) (pop tree))) - (cl-destructuring-bind (&key uri range) (plist-get node :location) - (insert (make-string depth ?\ ) (plist-get node :name) "\n") - (make-text-button (+ (point-at-bol 0) depth) (point-at-eol 0) - 'action (lambda (_arg) - (interactive) - (find-file (eglot--uri-to-path uri)) - (goto-char (car (eglot--range-region range))))) - (cl-loop for child across (plist-get node :children) - do (push (cons (1+ depth) child) tree))))))) - (eglot--error "Hierarchy unavailable"))) -```