Using Emacs as a C++ IDE

您所在的位置:网站首页 极融逾期会起诉吗 Using Emacs as a C++ IDE

Using Emacs as a C++ IDE

2022-12-17 10:55| 来源: 网络整理| 查看: 265

Using Emacs as a C++ IDE

By on July 17, 2016

An updated version of this post is available here. Last year I wrote a post about using Flymake with Emacs to get on-the-fly syntax checking. Recently I watched a cppcon lightning talk by Atila Neves on his setup for using Emacs as a C++ IDE and was inspired to adapt this to my own needs. In this post I will guide you through the setup process, which will carry over to Aquamacs for the most part too. I’ll try to note any differences as they come up. This guide will also use Homebrew for installing some additional software, but on Ubuntu you should be able to get it from apt. Please note that I take no credit for this setup at all. It is all thanks to the many developers and blog authors whose content I was able to use and piece together. I will do my best to cite any resources as I go. I apologize in advance for anything I missed.

Additional Software Installation

First we will install the necessary backends using brew and then move on to the actual Emacs setup. All of the packages listed here should be installed by running brew install package. I recommend that at this stage you run brew update to make sure you are getting the latest version of all the packages. Let us first install the package Bear (Build EAR) for recording how your project is compiled. It won’t be necessary for all projects, but definitely for some. To install it, run brew install bear. If you use LaTeX I recommend you install the package chktex using brew as well.

In order to be able to use RTags to navigate your project you should install LLVM using brew install llvm --with-libcxx --with-clang --without-assertions --with-rtti. I suggest you follow the RTags website for the most up-to-date installation instructions. It may look long, but it is rather straight forward to do. You can actually install RTags from brew by running brew install rtags, but note that you still need LLVM. Note that we will be changing the way we launch rdm later (if you don’t know what rdm is, see here). One really cool thing about RTags is that it is not specific to Emacs. You can use it with vim, sublime, Atom, and possibly other editors too.

The last package I suggest you install is clang-format, which allows you to format parts or the entirety of your code to a a certain style. This is extremely useful for ensuring uniform style across a project and for maintaining readability. ClangFormat also is not tied to Emacs and can be used stand alone or in other editors.

As a side note, if you want to try the clang-complete part of the IDE (I use Flycheck’s clang-mode), you will probably have to install the emacs-clang-complete-async package from brew or from the project’s GitHub page.

Emacs Configuration

Below I’ll go over my Emacs init file (~/.emacs) in a sort of section by section approach.

General Settings

What often annoys me in Emacs is that I have to type “yes” or “no”. I’d much prefer to just type “y” and “n”. This can be done (thanks to the post here) by adding the following to your init (~/.emacs) file:

;; We don't want to type yes and no all the time so, do y and n (defalias 'yes-or-no-p 'y-or-n-p)

I also find the #...# auto-save files annoying, so let’s disable that by adding

(setq auto-save-default nil)

On OSX you will have to set additional paths in Emacs so that it can find the packages you install from brew. Add the following to the start of your init file:

;; Add these to the PATH so that proper executables are found (setenv "PATH" (concat (getenv "PATH") ":/usr/texbin")) (setenv "PATH" (concat (getenv "PATH") ":/usr/bin")) (setenv "PATH" (concat (getenv "PATH") ":/usr/local/bin")) (setq exec-path (append exec-path '("/usr/texbin"))) (setq exec-path (append exec-path '("/usr/bin"))) (setq exec-path (append exec-path '("/usr/local/bin")))

Now, the easiest way to install all the packages we will use is to add the following to your init file:

(require 'package) ;; You might already have this line (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) (when ( .clang-format

I use the keyboard shortcut C-M-tab but you may change this if you wish.

Helm

Helm is a framework for incremental completions that allows fuzzy matching and a variety of other powerful features. For example, integrating helm with git allows you to search your project for files no matter which directory you are currently in. Helm’s search is also case-insensitive meaning you don’t have to deal with pesky CamelCase spelling of filenames. Helm also offers similar features for the command-prefix (Meta-x), the buffer list, mini buffer list, finding files, ctest (which you are using, right?) and many others that I have not yet explored. You can also integrate it with Flycheck and Flyspell to get nicer windowing and navigation with them. I’ll discuss Flyspell in more detail below, but for now, here is my helm configuration:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set up helm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load helm and set M-x to helm, buffer to helm, and find files to herm (require 'helm-config) (require 'helm) (require 'helm-ls-git) (require 'helm-ctest) ;; Use C-c h for helm instead of C-x c (global-set-key (kbd "C-c h") 'helm-command-prefix) (global-unset-key (kbd "C-x c")) (global-set-key (kbd "M-x") 'helm-M-x) (global-set-key (kbd "C-x b") 'helm-mini) (global-set-key (kbd "C-x C-b") 'helm-buffers-list) (global-set-key (kbd "C-x C-f") 'helm-find-files) (global-set-key (kbd "C-c t") 'helm-ctest) (setq helm-split-window-in-side-p t ; open helm buffer inside current window, ; not occupy whole other window helm-move-to-line-cycle-in-source t ; move to end or beginning of source when ; reaching top or bottom of source. helm-ff-search-library-in-sexp t ; search for library in `require' and `declare-function' sexp. helm-scroll-amount 8 ; scroll 8 lines other window using M-/M- helm-ff-file-name-history-use-recentf t ;; Allow fuzzy matches in helm semantic helm-semantic-fuzzy-match t helm-imenu-fuzzy-match t) ;; Have helm automaticaly resize the window (helm-autoresize-mode 1) (setq rtags-use-helm t) (require 'helm-flycheck) ;; Not necessary if using ELPA package (eval-after-load 'flycheck '(define-key flycheck-mode-map (kbd "C-c ! h") 'helm-flycheck))

To activate helm-flycheck use the keyboard shortcut C-c ! h and to use helm-ctest C-c t. When in helm-ctest you can type the name or partial name of a test to limit the tests disabled. To run a test press enter (I use C-m) and to select several tests to run use press C-space on each test you want to execute, then press enter. The tests will run and you will get immediate feedback on whether or not they passed.

Code Completion: Company, Irony and Semantic

Note: This part of the post is still somewhat in progress. I’m happy with the current behavior but may find bugs and improvements as time goes on. If there are any, I’ll post updates here.

First thing, company stands for “Complete Anything” and so you can probably guess we will leverage it as our backend for code completion. It is also useful for all around completion. The other nice thing is we can use company to get STL and C library header completion, query RTags for completions, ask clang to give completions for the entire STL, and still use Yasnippet for custom completions to save us typing out mundane things like for loops.

Let’s start with getting Yasnippet set up. Yasnippet comes with several useful snippets for code completion, but to really get the most out of it you will want to use the snippets from here

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Package: yasnippet ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'yasnippet) ;; To get a bunch of extra snippets that come in super handy see: ;; https://github.com/AndreaCrotti/yasnippet-snippets ;; or use: ;; git clone https://github.com/AndreaCrotti/yasnippet-snippets.git ~/.emacs.d/yassnippet-snippets/ (add-to-list 'yas-snippet-dirs "~/.emacs.d/yasnippet-snippets/") (yas-global-mode 1) (yas-reload-all)

I’ve found that there are some missing that I will add and also that some seem to add incorrect code or don’t work.

Now let’s load up company and the backends we will want to use. I’ve generally found that semantic completion is quite slow and gives irrelevant results. However, semantic is really nice for navigating around a file using helm. The shortcut I’ve set is C-c h i. For navigating between files I still recommend RTags. I also provide functions my-enable-semantic and my-disable-semantic to enable and disable semantic completion so you can easily change it if you wish. The code in my init file is:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set up code completion with company and irony ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'company) (require 'company-rtags) (global-company-mode) ;; Enable semantics mode for auto-completion (require 'cc-mode) (require 'semantic) (global-semanticdb-minor-mode 1) (global-semantic-idle-scheduler-mode 1) (semantic-mode 1) ;; Setup irony-mode to load in c-modes (require 'irony) (require 'company-irony-c-headers) (require 'cl) (add-hook 'c++-mode-hook 'irony-mode) (add-hook 'c-mode-hook 'irony-mode) (add-hook 'objc-mode-hook 'irony-mode) ;; irony-mode hook that is called when irony is triggered (defun my-irony-mode-hook () "Custom irony mode hook to remap keys." (define-key irony-mode-map [remap completion-at-point] 'irony-completion-at-point-async) (define-key irony-mode-map [remap complete-symbol] 'irony-completion-at-point-async)) (add-hook 'irony-mode-hook 'my-irony-mode-hook) (add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options) ;; company-irony setup, c-header completions (add-hook 'irony-mode-hook 'company-irony-setup-begin-commands) ;; Remove company-semantic because it has higher precedance than company-clang ;; Using RTags completion is also faster than semantic, it seems. Semantic ;; also provides a bunch of technically irrelevant completions sometimes. ;; All in all, RTags just seems to do a better job. (setq company-backends (delete 'company-semantic company-backends)) ;; Enable company-irony and several other useful auto-completion modes ;; We don't use rtags since we've found that for large projects this can cause ;; async timeouts. company-semantic (after company-clang!) works quite well ;; but some knowledge some knowledge of when best to trigger is still necessary. (eval-after-load 'company '(add-to-list 'company-backends '(company-irony-c-headers company-irony company-yasnippet company-clang company-rtags) ) ) (defun my-disable-semantic () "Disable the company-semantic backend." (interactive) (setq company-backends (delete '(company-irony-c-headers company-irony company-yasnippet company-clang company-rtags company-semantic) company-backends)) (add-to-list 'company-backends '(company-irony-c-headers company-irony company-yasnippet company-clang company-rtags)) ) (defun my-enable-semantic () "Enable the company-semantic backend." (interactive) (setq company-backends (delete '(company-irony-c-headers company-irony company-yasnippet company-clang) company-backends)) (add-to-list 'company-backends '(company-irony-c-headers company-irony company-yasnippet company-clang)) ) ;; Zero delay when pressing tab (setq company-idle-delay 0) (define-key c-mode-map [(tab)] 'company-complete) (define-key c++-mode-map [(tab)] 'company-complete) ;; Delay when idle because I want to be able to think (setq company-idle-delay 0.2) ;; Prohibit semantic from searching through system headers. We want ;; company-clang to do that for us. (setq-mode-local c-mode semanticdb-find-default-throttle '(local project unloaded recursive)) (setq-mode-local c++-mode semanticdb-find-default-throttle '(local project unloaded recursive)) (semantic-remove-system-include "/usr/include/" 'c++-mode) (semantic-remove-system-include "/usr/local/include/" 'c++-mode) (add-hook 'semantic-init-hooks 'semantic-reset-system-include)

To start company completion just press tab. I’ve found that sometimes it will timeout and not offer any completions. When this happens I find that most of the time pressing tab again gives the completions. Sometimes it may appear to hang for a brief period of time. This I believe occurs when RTags is being queried for completions. You can navigate the completions menu using M-n and M-p then pressing enter to select the desired completion. Note that return types in C++ are denoted using the postfix syntax --> type.

Directory local variables can be very useful for setting behaviors for individual projects. For example, setting the build path, CMake flags, or compiler flags is best done by using directory local variables. These are set in a file called .dir-locals.el and Emacs searches up the directory tree until it finds one so you only need to add it to the root directory of your project. Here is an example of how to set the directory local variables for a project

((nil . ((cmake-ide-build-dir . "../build/")))) ((nil . ((setq helm-ctest-dir "..//build/Tests/")))) ((nil . ((cmake-compile-command . "-DCMAKE_VARIABLE=blah")))) ((nil . ((cmake-ide-clang-flags-c++ . "-I/usr/local/include -I/usr/include -I/path/to/my/include/")))) ((nil . ((company-clang-arguments . ("-I/usr/local/include -I/usr/include -I/path/to/my/include/")))))

Without setting directory local variables the code completion and syntax checking tools may not know how to properly compile your code, which means they also cannot function properly since they compile your code to see if what you are changing is functional. What I have noticed is that sometimes you need to restart Emacs (Aquamacs) in order for the directory local variables to properly take effect. This seems to be true if you open a source file of your project before adding the .dir-locals.el file.

Before moving on I want to show you some of the useful things that you can auto-complete using this infrastructure. If you know the first few letters of a function you can quickly complete the entire member function name with the correct arguments: If you cannot remember the member functions then you can get a full list too. This is a bit finicky sometimes, but here is an example of what the list looks like: Finally, you can use the snippets from Yasnippet to get an entire class template complete with move and copy constructors and assignment operators. This is just an example. You can add any snippets and shortcuts you want, which is what makes Yasnippet so great.

Spell Check: Flyspell

Something that I find to be quite useful is on-the-fly spell checking. Flyspell offers this for all your buffers and restricts itself to comments when checking your code. Nobody wants an embarrassing spelling mistake in their comment and this fixes that. The code I use to set up Flyspell is:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Flyspell Mode for Spelling Corrections ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'flyspell) ;; The welcome message is useless and can cause problems (setq flyspell-issue-welcome-flag nil) ;; Fly spell keyboard shortcuts so no mouse is needed ;; Use helm with flyspell (define-key flyspell-mode-map (kbd "") 'helm-flyspell-correct) ;; (global-set-key (kbd "") 'ispell-word) (global-set-key (kbd "C-S-") 'flyspell-mode) (global-set-key (kbd "C-M-") 'flyspell-buffer) (global-set-key (kbd "C-") 'flyspell-check-previous-highlighted-word) (global-set-key (kbd "M-") 'flyspell-check-next-highlighted-word) ;; Set the way word highlighting is done (defun flyspell-check-next-highlighted-word () "Custom function to spell check next highlighted word." (interactive) (flyspell-goto-next-error) (ispell-word) ) ;; Spell check comments in c++ and c common (add-hook 'c++-mode-hook 'flyspell-prog-mode) (add-hook 'c-mode-common-hook 'flyspell-prog-mode) ;; Enable flyspell in text mode (if (fboundp 'prog-mode) (add-hook 'prog-mode-hook 'flyspell-prog-mode) (dolist (hook '(lisp-mode-hook emacs-lisp-mode-hook scheme-mode-hook clojure-mode-hook ruby-mode-hook yaml-mode python-mode-hook shell-mode-hook php-mode-hook css-mode-hook haskell-mode-hook caml-mode-hook nxml-mode-hook crontab-mode-hook perl-mode-hook tcl-mode-hook javascript-mode-hook)) (add-hook hook 'flyspell-prog-mode))) (dolist (hook '(text-mode-hook)) (add-hook hook (lambda () (flyspell-mode 1)))) (dolist (hook '(change-log-mode-hook log-edit-mode-hook)) (add-hook hook (lambda () (flyspell-mode -1))))

For a list of corrections I again use helm (helm-flyspell). To pull up the options move the cursor over the word you want to correct and then type F8 (on a Mac you must use the fn key). Here is an example of what this setup looks like in action:

Miscellaneous

In order to keep this post to a somewhat reasonable length I’ll end here with Magit and cmake-mode. My Magit configuration is

(global-set-key (kbd "M-g M-s") 'magit-status) (global-set-key (kbd "M-g M-c") 'magit-checkout)

and to load cmake-mode I use

(require 'cmake-mode)

I have customized my Emacs configuration a great deal more. My current init file is ~650 lines long, whatever that’s worth. I have a header that shows the current function and the path to the current file, but this required customizing the font a great deal to make it legible and nice. I’ve uploaded a complete version of my Emacs file here and will eventually make a public copy on either GitHub or BitBucket. If you want you can simply copy the entire contents of init.el into your ~/.emacs file and you should automatically have the entire configuration set the way I do.

I hope you found this guide useful and please email me with any questions, feedback and/or suggestions.



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3