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}