From 191d696dd1e52f353b9934efcaef256f80b27021 Mon Sep 17 00:00:00 2001 From: Tristan Hume Date: Sat, 10 Jan 2015 18:57:24 -0500 Subject: [PATCH] Add a emacs-ycmd contrib layer. --- .gitignore | 1 + contrib/ycmd/Readme.md | 26 ++++++++++++ contrib/ycmd/global_conf.py | 84 +++++++++++++++++++++++++++++++++++++ contrib/ycmd/packages.el | 41 ++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 contrib/ycmd/Readme.md create mode 100644 contrib/ycmd/global_conf.py create mode 100644 contrib/ycmd/packages.el diff --git a/.gitignore b/.gitignore index 80bd4b0ff..a5205334e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ tmp/ server/ *~ *.elc +*.pyc /*.zip *.db python-*-docs-html diff --git a/contrib/ycmd/Readme.md b/contrib/ycmd/Readme.md new file mode 100644 index 000000000..e5e46797e --- /dev/null +++ b/contrib/ycmd/Readme.md @@ -0,0 +1,26 @@ +# YCMD + +This contrib layer adds [emacs-ycmd](https://github.com/abingham/emacs-ycmd) support. +In order to use this layer you must have a local [ycmd](https://github.com/Valloric/ycmd#building) installation +and must set the `ycmd-server-command` variable to reflect the path to that installation. See the emacs-ycmd +Readme for more instructions on this. + +This package also requires the `company-mode` layer in order to get actual completion rather than just flychecking and +key bindings. + +## Key Bindings + +Adds the `SPC m g` go to definition binding to `c++-mode` as well as `SPC m G` for the more imprecise but faster version. + +## Configuration + +By default this layer only activates ycmd for `c++-mode` so company can provide completion that is better than mere keyword +completion for all the modes it would otherwise add itself to. If you want ycmd in all sorts of other modes then put this +snippet in your dotspacemacs init function: +```elisp +(setq ycmd/all-the-modes t) +``` +otherwise you might just want to add it for specific languages like: +```elisp +(add-hook 'c++-mode-hook 'ycmd-mode) +``` diff --git a/contrib/ycmd/global_conf.py b/contrib/ycmd/global_conf.py new file mode 100644 index 000000000..929cf9f5e --- /dev/null +++ b/contrib/ycmd/global_conf.py @@ -0,0 +1,84 @@ +import os +import ycm_core + +default_flags = [ +'-Wall', +'-Wextra', +'-Werror', +'-Wc++98-compat', +'-Wno-long-long', +'-Wno-variadic-macros', +'-fexceptions', +'-DNDEBUG', +'-std=c++11', +'-x', +'c++', +# This path will only work on OS X, but extra paths that don't exist are not harmful +'-isystem' +'/System/Library/Frameworks/Python.framework/Headers', +'-isystem', +'/usr/include', +'-isystem', +'/usr/local/include', +'-isystem', +'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1', +'-isystem', +'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include', +] + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + +# Thanks to https://github.com/decrispell/vim-config for this code +def FlagsForFile( filename, **kwargs ): + """ given the source filename, return the compiler flags """ + opt_basename = '.clang_complete' + curr_dir = os.path.dirname(filename) + opt_fname = os.path.join(curr_dir, opt_basename) + # keep traversing up the tree until we find the file, or hit the root + while not os.path.exists(opt_fname): + new_dir = os.path.dirname(curr_dir) + if new_dir == curr_dir: + # we've reached the root of the tree + break + curr_dir = new_dir + opt_fname = os.path.join(curr_dir, opt_basename) + try: + fd = open(opt_fname, 'r') + except IOError: + return {'flags': default_flags, 'do_cache': True} + flags = [line.strip() for line in fd.readlines()] + relative_to = os.path.dirname(opt_fname) + flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) + return { + 'flags': flags, 'do_cache': True + } diff --git a/contrib/ycmd/packages.el b/contrib/ycmd/packages.el new file mode 100644 index 000000000..16babed31 --- /dev/null +++ b/contrib/ycmd/packages.el @@ -0,0 +1,41 @@ +(defvar ycmd-packages + '( + ycmd + flycheck-ycmd + ) + "List of all packages to install and/or initialize. Built-in packages +which require an initialization must be listed explicitly in the list.") + +(unless (boundp 'ycmd-server-command) + (message "YCMD won't work unless you set the ycmd-server-command variable to the path to a ycmd install.")) + +(when (member 'company-mode dotspacemacs-configuration-layers) + (add-to-list 'ycmd-packages 'company-ycmd)) + +(defvar ycmd/all-the-modes nil + "use ycmd for all the modes it supports, most will only use fancy keyword completion.") + +(defun ycmd/init-ycmd () + (use-package ycmd + :init + (progn + (if ycmd/all-the-modes + (ycmd-setup) + (add-hook 'c++-mode-hook 'ycmd-mode)) + (setq-default ycmd-global-config + (expand-file-name "~/.emacs.d/contrib/ycmd/global_conf.py"))) + :config + (evil-leader/set-key-for-mode 'c++-mode + "mg" 'ycmd-goto + "mG" 'ycmd-goto-imprecise))) + +(defun ycmd/init-company-ycmd () + (use-package company-ycmd + :init (company-ycmd-setup))) + +(defun ycmd/init-flycheck-ycmd () + (use-package flycheck-ycmd + :init + (progn + (add-hook 'ycmd-file-parse-result-hook 'flycheck-ycmd--cache-parse-results) + (add-to-list 'flycheck-checkers 'ycmd))))