Vim helper scripts
Change-Id: Ib1dd467be1ec463b67ff2327b037560a9d882279
diff --git a/vim/README.md b/vim/README.md
new file mode 100644
index 0000000..ad5f3b6
--- /dev/null
+++ b/vim/README.md
@@ -0,0 +1,27 @@
+# Helpful VIM tools for Fuchsia development
+
+## Features
+
+* Configure YouCompleteMe to provide error checking, completion and source navigation within the Fuchsia tree.
+* Set path so that `:find` and `gf` know how to find files.
+
+## Installation
+
+Make sure `env.sh` is being called in your login scripts. This code depends on variables set in `env.sh` and by the
+`fset` command.
+
+Add this to your `vimrc`:
+```
+if $FUCHSIA_DIR != ""
+ source $FUCHSIA_DIR/scripts/vim/fuchsia.vim
+endif
+```
+
+Optionally install [YouCompleteMe](https://github.com/Valloric/YouCompleteMe) for fancy completion, source navigation
+and inline errors.
+
+## TODO
+
+In the future it would be nice to support:
+* Syntax highlighting and indentation for the languages we use
+* Build system integration
diff --git a/vim/fuchsia.vim b/vim/fuchsia.vim
new file mode 100644
index 0000000..4ecb7dc
--- /dev/null
+++ b/vim/fuchsia.vim
@@ -0,0 +1,21 @@
+" Only run if $FUCHSIA_DIR has been set by env.sh
+if $FUCHSIA_DIR != ""
+ let g:ycm_global_ycm_extra_conf = $FUCHSIA_DIR . '/scripts/vim/ycm_extra_conf.py'
+
+ function FuchsiaBuffer()
+ " Set up path so that 'gf' and :find do what we want.
+ let &l:path = $PWD. "/**" . "," . $FUCHSIA_DIR . "," .
+ \ $FUCHSIA_BUILD_DIR . "," .
+ \ $FUCHSIA_BUILD_DIR . "/gen"
+ if g:loaded_youcompleteme
+ " Replace the normal go to tag key with YCM.
+ nnoremap <C-]> :YcmCompleter GoTo<cr>
+ endif
+ endfunction
+
+ augroup fuchsia
+ " Configure buffers that are in the fuchsia tree.
+ autocmd BufRead,BufNewFile $FUCHSIA_DIR/** call FuchsiaBuffer()
+ augroup END
+
+endif
diff --git a/vim/ycm_extra_conf.py b/vim/ycm_extra_conf.py
new file mode 100644
index 0000000..73a1715
--- /dev/null
+++ b/vim/ycm_extra_conf.py
@@ -0,0 +1,111 @@
+# vim: set expandtab:ts=2:sw=2
+# Copyright (c) 2017 The Fuchsia Authors. All rights reserved.
+
+# Autocompletion config for YouCompleteMe in Fuchsia
+
+import os
+import stat
+import subprocess
+
+fuchsia_root = os.environ['FUCHSIA_DIR']
+fuchsia_sysroot = os.environ['FUCHSIA_SYSROOT_DIR']
+fuchsia_build = os.environ['FUCHSIA_BUILD_DIR']
+
+common_flags = [
+ '-std=c++14',
+ '-isystem',
+ fuchsia_sysroot + '/include',
+ '-isystem',
+ fuchsia_sysroot + '/include/c++/v1',
+]
+
+
+def GetClangCommandFromNinjaForFilename(filename):
+ """Returns the command line to build |filename|.
+
+ Asks ninja how it would build the source file. If the specified file is a
+ header, tries to find its companion source file first.
+
+ Args:
+ filename: (String) Path to source file being edited.
+
+ Returns:
+ (List of Strings) Command line arguments for clang.
+ """
+
+ fuchsia_flags = []
+
+ # Header files can't be built. Instead, try to match a header file to its
+ # corresponding source file.
+ if filename.endswith('.h'):
+ alternates = ['.cc', '.cpp']
+ for alt_extension in alternates:
+ alt_name = filename[:-2] + alt_extension
+ if os.path.exists(alt_name):
+ filename = alt_name
+ break
+ else:
+ # If this is a standalone .h file with no source, the best we can do is
+ # try to use the default flags.
+ return fuchsia_flags
+
+ # Ninja needs the path to the source file from the output build directory.
+ # Cut off the common part and /.
+ subdir_filename = filename[len(fuchsia_root) + 1:]
+ rel_filename = os.path.join('..', '..', subdir_filename)
+
+ # Ask ninja how it would build our source file.
+ ninja_command = [
+ 'ninja', '-v', '-C', fuchsia_build, '-t', 'commands', rel_filename + '^'
+ ]
+ p = subprocess.Popen(ninja_command, stdout=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ return fuchsia_flags
+
+ # Ninja might execute several commands to build something. We want the last
+ # clang command.
+ clang_line = None
+ for line in reversed(stdout.split('\n')):
+ if 'clang' in line:
+ clang_line = line
+ break
+ else:
+ return fuchsia_flags
+
+ # Parse out the -I and -D flags. These seem to be the only ones that are
+ # important for YCM's purposes.
+ for flag in clang_line.split(' '):
+ if flag.startswith('-I'):
+ # Relative paths need to be resolved, because they're relative to the
+ # output dir, not the source.
+ if flag[2] == '/':
+ fuchsia_flags.append(flag)
+ else:
+ abs_path = os.path.normpath(os.path.join(fuchsia_build, flag[2:]))
+ fuchsia_flags.append('-I' + abs_path)
+ elif ((flag.startswith('-') and flag[1] in 'DWFfmO') or
+ flag.startswith('-std=') or flag.startswith('--target=') or
+ flag.startswith('--sysroot=')):
+ fuchsia_flags.append(flag)
+ else:
+ print('Ignoring flag: %s' % flag)
+
+ return fuchsia_flags
+
+
+def FlagsForFile(filename):
+ """This is the main entry point for YCM. Its interface is fixed.
+
+ Args:
+ filename: (String) Path to source file being edited.
+
+ Returns:
+ (Dictionary)
+ 'flags': (List of Strings) Command line flags.
+ 'do_cache': (Boolean) True if the result should be cached.
+ """
+ fuchsia_flags = GetClangCommandFromNinjaForFilename(filename)
+ final_flags = common_flags + fuchsia_flags
+
+ return {'flags': final_flags, 'do_cache': True}